fix interfere in Menu.tsx
46
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
name: 🐛 Bug report
|
||||
description: Report a reproducible bug or regression
|
||||
title: "[bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
placeholder: A clear and concise description of what the bug is.
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
placeholder: "Steps to reproduce the behavior\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
placeholder: If applicable, add screenshots to help explain your problem.
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
placeholder: A clear and concise description of what you expected to happen.
|
||||
- type: input
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
placeholder: e.g. MacOS, Windows10...
|
||||
- type: input
|
||||
id: browser
|
||||
attributes:
|
||||
label: Browser
|
||||
placeholder: e.g. Chrome, Safari
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Add any other context about the problem here.
|
||||
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💭 Questions and Help - Reddit
|
||||
url: https://www.reddit.com/r/Affine/
|
||||
about: Please ask and answer questions here.
|
||||
- name: 💬 Questions and Help - Telegram
|
||||
url: https://t.me/affineworkos
|
||||
about: Please ask and answer questions here.
|
||||
- name: 🗯 Questions and Help - Discord
|
||||
url: https://discord.gg/yz6tGVsf5p
|
||||
about: Please ask and answer questions here.
|
31
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: ✨ Feature request
|
||||
description: An idea or request for new functionality
|
||||
title: "[feature]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this feature request!
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 1~3 main use cases of the proposed feature
|
||||
description: e.g. As a ..., I have many tasks scattered across documents, and I want to have a unified entry to view these tasks.
|
||||
placeholder: e.g. As a ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Ideas for solution
|
||||
placeholder: e.g. A task view can be added to view all tasks.
|
||||
- type: input
|
||||
id: userType
|
||||
attributes:
|
||||
label: what types of users can benefit from using your proposed feature
|
||||
placeholder: busy student
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Add any other context or screenshots about the feature request here.
|
31
.github/ISSUE_TEMPLATE/improvement-request.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: 🪄 Improvement request
|
||||
description: An improvement to existing functionality
|
||||
title: "[improvement]: "
|
||||
labels: ["improvement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this improvement request!
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 1~3 main use cases of the proposed improvement
|
||||
description: e.g. As a ..., I have many tasks scattered across documents, and I want to have a unified entry to view these tasks.
|
||||
placeholder: e.g. As a ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Ideas for solution
|
||||
placeholder: e.g. A task view can be added to view all tasks.
|
||||
- type: input
|
||||
id: userType
|
||||
attributes:
|
||||
label: what types of users can benefit from using your proposed improvement
|
||||
placeholder: busy student
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Add any other context or screenshots about the improvement request here.
|
66
.github/workflows/venus.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
name: Build Venus
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'apps/venus/**'
|
||||
- '.github/workflows/venus.yml'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'apps/venus/**'
|
||||
- '.github/workflows/venus.yml'
|
||||
|
||||
# Cancels all previous workflow runs for pull requests that have not completed.
|
||||
# See https://docs.github.com/en/actions/using-jobs/using-concurrency
|
||||
concurrency:
|
||||
# The concurrency group contains the workflow name and the branch name for
|
||||
# pull requests or the commit hash for any other events.
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
NAMESPACE: toeverything
|
||||
VENUS_IMAGE_NAME: venus
|
||||
IMAGE_TAG: canary-${{ github.sha }}
|
||||
IMAGE_TAG_LATEST: nightly-latest
|
||||
|
||||
jobs:
|
||||
ligo-virgo:
|
||||
runs-on: self-hosted
|
||||
environment: development
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker (venus)
|
||||
id: meta_venus
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.VENUS_IMAGE_NAME }}
|
||||
tags: |
|
||||
${{ env.IMAGE_TAG }}
|
||||
${{ env.IMAGE_TAG_LATEST }}
|
||||
|
||||
- name: Build and push Docker image (venus)
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile-venus
|
||||
push: ${{ github.ref == 'refs/heads/master' && true || false }}
|
||||
tags: ${{ steps.meta_venus.outputs.tags }}
|
||||
labels: ${{ steps.meta_venus.outputs.labels }}
|
||||
target: venus
|
28
Caddyfile-venus
Normal file
@ -0,0 +1,28 @@
|
||||
:80 {
|
||||
root /* ./dist
|
||||
|
||||
file_server {
|
||||
precompressed br
|
||||
}
|
||||
|
||||
encode {
|
||||
zstd
|
||||
gzip 9
|
||||
}
|
||||
|
||||
@notStatic {
|
||||
not path /*.css
|
||||
not path /*.js
|
||||
not path /*.png
|
||||
not path /*.jpg
|
||||
not path /*.svg
|
||||
not path /*.ttf
|
||||
not path /*.eot
|
||||
not path /*.woff
|
||||
not path /*.woff2
|
||||
}
|
||||
|
||||
handle @notStatic {
|
||||
try_files {path} /index.html
|
||||
}
|
||||
}
|
21
Dockerfile-venus
Normal file
@ -0,0 +1,21 @@
|
||||
FROM node:16-alpine as builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN apk add g++ make python3 git
|
||||
RUN npm i -g pnpm@7 && pnpm i --frozen-lockfile --store=node_modules/.pnpm-store && pnpm run build:venus
|
||||
|
||||
FROM node:16-alpine as relocate
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/dist/apps/venus ./dist
|
||||
COPY --from=builder /app/Caddyfile-venus ./Caddyfile
|
||||
RUN rm ./dist/*.txt
|
||||
|
||||
# =============
|
||||
# venus image
|
||||
# =============
|
||||
FROM caddy:2.4.6-alpine as venus
|
||||
WORKDIR /app
|
||||
COPY --from=relocate /app .
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["caddy", "run"]
|
170
README.md
@ -1,76 +1,150 @@
|
||||
# AFFiNE
|
||||
<h1 align="center" style="border-bottom: none">
|
||||
<b>
|
||||
<a href="https://affine.pro">AFFiNE.PRO</a><br>
|
||||
</b>
|
||||
The Next-Gen Knowledge Base to Replace Notion & Miro.
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
Planning, Sorting and Creating all Together. Open-source, Privacy-First, and Free to use.
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<!--
|
||||
Make New Badge Pattern badges inline
|
||||
See https://github.com/all-contributors/all-contributors/issues/361#issuecomment-637166066
|
||||
-->
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
|
||||
[![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors-)
|
||||
[all-contributors-badge]: https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
Workspace for AFFiNE
|
||||
[![All Contributors][all-contributors-badge]](#contributors)
|
||||
[![Node](https://img.shields.io/badge/node->=16.0-success)](https://www.typescriptlang.org/)
|
||||
[![React](https://img.shields.io/badge/TypeScript-4.7-3178c6)](https://www.typescriptlang.org/)
|
||||
[![React](https://img.shields.io/badge/React-18-61dafb)](https://reactjs.org/)
|
||||
[![Rust](https://img.shields.io/badge/Rust-1.62-dea584)](https://www.rust-lang.org/)
|
||||
|
||||
## Installation
|
||||
</div>
|
||||
|
||||
```sh
|
||||
# Clone the repo
|
||||
git clone git@github.com:toeverything/AFFiNE.git
|
||||
```
|
||||
<p align="center">
|
||||
<a href="http://affine.pro"><b>Website</b></a> •
|
||||
<a href="https://discord.com/invite/yz6tGVsf5p"><b>Discord</b></a> •
|
||||
<a href="https://twitter.com/AffineOfficial"><b>Twitter</b></a> •
|
||||
<a href="https://medium.com/@affineworkos"><b>Medium</b></a> •
|
||||
<a href="https://t.me/affineworkos"><b>Telegram</b></a>
|
||||
</p>
|
||||
|
||||
Once cloned, switch to the master branch and navigate to the folder by typing `cd AFFiNE` and then running the following commands:
|
||||
<p align="center"><img width="1920" alt="affine_screen" src="https://user-images.githubusercontent.com/79301703/182363099-48b479c3-dc26-4fc3-8f9b-45f9cf358f9a.png"><p/>
|
||||
|
||||
```sh
|
||||
# Install all project dependencies
|
||||
npm i -g pnpm
|
||||
pnpm i
|
||||
# Stay Up-to-Date
|
||||
|
||||
# Start the project
|
||||
pnpm start
|
||||
open http://localhost:4200/
|
||||
```
|
||||
![952cd7a5-70fe-48ab-b74f-23981d94d2c5](https://user-images.githubusercontent.com/79301703/182365526-df074c64-cee4-45f6-b8e0-b912f17332c6.gif)
|
||||
|
||||
This project uses pnpm for package management and is built based on nx. It is recommended to install the [nx console](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console) plugin to create dependencies
|
||||
# Table of contents
|
||||
|
||||
**If it is development, you can add environment variables in the project directory .env.local file**
|
||||
- [Stay Up-to-Date](#stay-up-to-date)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Shape your page](#shape-your-page)
|
||||
- [Plan your task](#plan-your-task)
|
||||
- [Sort your knowledge](#sort-your-knowledge)
|
||||
- [Create your story](#create-your-story)
|
||||
- [Getting Started with development](#getting-started-with-development)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Releases](#releases)
|
||||
- [Feature requests](#feature-requests)
|
||||
- [FAQ](#faq)
|
||||
- [The Philosophy of AFFiNE](#the-philosophy-of-affine)
|
||||
- [Community](#community)
|
||||
- [Contributors](#contributors)
|
||||
- [License](#license)
|
||||
|
||||
```
|
||||
NODE_ENV=development
|
||||
```
|
||||
## Shape your page
|
||||
|
||||
## Scripts
|
||||
![546163d6-4c39-4128-ae7f-55d59bc3b76b](https://user-images.githubusercontent.com/79301703/182365611-b0ba3690-21c0-4d9b-bfbc-0bc15da05aeb.gif)
|
||||
|
||||
1. Create react dependency library: `pnpm run add:library`
|
||||
2. Create react components: `pnpm run add:components`
|
||||
3. Create a data source: `pnpm run add:datasource`
|
||||
4. Unit testing: `pnpm test`
|
||||
5. Compile specific components
|
||||
- `pnpm build/test/lint `project name
|
||||
- Project name reference workspace.json
|
||||
6. Create react/node program: use nx console
|
||||
7. If you need to use the git cz function, please install it globally first commitizen `npm install -g commitizen conventional-changelog conventional-changelog-cli`
|
||||
## Plan your task
|
||||
|
||||
## Contributing
|
||||
![41a7b3a4-32f2-4d18-ac6b-57d1e1fda753](https://user-images.githubusercontent.com/79301703/182366553-1f6558a7-f17b-4611-ab95-aea3ec997154.gif)
|
||||
|
||||
- Generic functional components (such as ui components) are placed in `libs/components/common`
|
||||
- components within common are not allowed to reference _components_ except utils and dependencies
|
||||
- Common components can reference each other
|
||||
- Business components are placed in `libs/components`
|
||||
- The data source component is placed in `libs/datasource` - api request code, schema, etc. belong to the data source
|
||||
Please see [CONTRIBUTING](/docs/CONTRIBUTING.md)
|
||||
## Sort your knowledge
|
||||
|
||||
## Documentation
|
||||
![c9e1ff46-cec2-411b-b89d-6727a5e6f6c3](https://user-images.githubusercontent.com/79301703/182366602-08e44d28-a031-4097-9904-52fb9b1e9e17.gif)
|
||||
|
||||
- [how-to-write-css-in-affine.md](/docs/how-to-write-css-in-affine.md)
|
||||
- [how-to-add-ui-component-in-affine.md](/docs/how-to-add-ui-component-in-affine.md)
|
||||
- [how-to-customize-rollup-config.md](docs/how-to-customize-rollup-config.md)
|
||||
- [how-to-auto-download-figma-assets-in-affine.md](docs/how-to-auto-download-figma-assets-in-affine.md)
|
||||
- [affine-icons-user-guide.md](docs/affine-icons-user-guide.md)
|
||||
## Create your story
|
||||
|
||||
## Community
|
||||
We want your data always to be yours, and we don't want to make any sacrifice to your accessibility. Your data is always local-stored first, yet we support real-time collaboration on a peer-to-peer basis. We don't think "privacy-first" is a good excuse for not supporting modern web features.
|
||||
Collaboration isn't only necessary for teams -- you may take and insert pics on your phone, then edit them on your desktop, and share them with your collaborators.
|
||||
Affine is fully built with web technologies so that consistency and accessibility are always guaranteed on Mac, Windows and Linux. The local file system support will be available when version 0.0.1beta is released.
|
||||
|
||||
# Getting Started with development
|
||||
|
||||
Please view the [documentation](https://affine.gitbook.io/affine/) in Contribute-to-AFFiNE/Software-Contributions/Environment-setup.
|
||||
|
||||
# Roadmap
|
||||
|
||||
Coming Soon...
|
||||
|
||||
# Releases
|
||||
|
||||
Get our latest [release notes](https://github.com/toeverything/AFFiNE/wiki) from here.
|
||||
|
||||
# Feature requests
|
||||
|
||||
Please go to [Feature request](https://github.com/toeverything/AFFiNE/issues).
|
||||
|
||||
# FAQ
|
||||
|
||||
Get quick help on [Telegram](https://t.me/affineworkos) and [Discord](https://discord.gg/yz6tGVsf5p) along with other developers and contributors.
|
||||
|
||||
Latest news and technology sharing on [Twitter](https://twitter.com/AffineOfficial), [Medium](https://medium.com/@affineworkos) and [AFFiNE Blog](https://blog.affine.pro/).
|
||||
|
||||
# The Philosophy of AFFiNE
|
||||
|
||||
Timothy Berners-Lee once taught us about the idea of the semantic web, where all the data can be interpreted in any form while the "truth" is kept. This gives our best image of an ideal knowledge base by far, that sorting of information, planning of project and goals as well as creating of knowledge can be all together.
|
||||
We have witnessed waves of paradigm shift so many times. At first, everything was noted on office-like apps or DSL like LaTeX, then we found todo-list apps and WYSIWYG markdown editors better for writing and planning. Finally, here comes Notion and Miro, who take advantage of the idea of blocks to further liberate our creativity.
|
||||
It is all perfect... If there are not so many waste operations and redundant information. And, we insist that privacy first should always be given by default.
|
||||
That's why we are making AFFiNE. Some of the most important features are:
|
||||
|
||||
- Transformable
|
||||
- Every block can be transformed equally as a database
|
||||
- e.g. you can now set up a to-do with MarkDown in text view and edit it in kanban view.
|
||||
- Every doc can be turned into a whiteboard
|
||||
- An always good-to-read, structured docs-form page is the best for your notes, but a boundless doodle surface is better for collaboration and creativity.
|
||||
- Atomic
|
||||
- The basic element of affine are blocks, not pages.
|
||||
- Blocks can be directly reuse and synced between pages.
|
||||
- Pages and blocks are searched and organized on the basis of connected graphs, not tree-like paths.
|
||||
- Dual-link and semantic search are fully supported.
|
||||
- Collaborative and privacy-first
|
||||
- Data is always stored locally by default
|
||||
- CRDTs are applied so that peer-to-peer collaboration is possible.
|
||||
|
||||
We really appreciate the idea of Monday, airtable and notion database. They inspired what we think is right for task management. But we don't like the repeated works -- we don't want to set a todo easily with markdown but end up re-write it again in kanban or other databases.
|
||||
With AFFiNE, every block group has infinite views, for you to keep your single source of truth.
|
||||
|
||||
We would like to give special thanks to the innovators and pioneers who greatly inspired us:
|
||||
|
||||
- Quip & Notion -- that docs can be organized as blocks
|
||||
- Taskade & Monday -- brillant multi-demensional tables
|
||||
- Height & Linear -- beautiful task management tool
|
||||
|
||||
We would also like to give thanks to open-source projects that make affine possible:
|
||||
|
||||
- Yjs & Yrs
|
||||
- React
|
||||
- Rust
|
||||
|
||||
# Community
|
||||
|
||||
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
|
||||
|
||||
[Discuss AFFiNE on GitHub](https://github.com/toeverything/AFFiNE/discussions)
|
||||
|
||||
## Contributors
|
||||
# Contributors
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
@ -98,7 +172,7 @@ For help, discussion about best practices, or any other conversation that would
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
## License
|
||||
# License
|
||||
|
||||
AFFiNE is distributed under the terms of MIT license.
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
/* eslint-disable filename-rules/match */
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
/* eslint-disable filename-rules/match */
|
||||
import { useEffect, useRef, type UIEvent, useState } from 'react';
|
||||
import {
|
||||
useEffect,
|
||||
useRef,
|
||||
type UIEvent,
|
||||
useState,
|
||||
useLayoutEffect,
|
||||
} from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import {
|
||||
MuiBox as Box,
|
||||
@ -17,6 +23,7 @@ import { CollapsibleTitle } from '@toeverything/components/common';
|
||||
import {
|
||||
useShowSpaceSidebar,
|
||||
useUserAndSpaces,
|
||||
usePageClientWidth,
|
||||
} from '@toeverything/datasource/state';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
|
||||
@ -47,11 +54,11 @@ export function Page(props: PageProps) {
|
||||
}
|
||||
);
|
||||
|
||||
await services.api.userConfig.addRecentPage(
|
||||
props.workspace,
|
||||
user.id,
|
||||
page_id
|
||||
);
|
||||
// await services.api.userConfig.addRecentPage(
|
||||
// props.workspace,
|
||||
// user.id,
|
||||
// page_id
|
||||
// );
|
||||
await services.api.editorBlock.clearUndoRedo(props.workspace);
|
||||
};
|
||||
updateRecentPages();
|
||||
@ -113,7 +120,7 @@ const EditorContainer = ({
|
||||
workspace: string;
|
||||
}) => {
|
||||
const [lockScroll, setLockScroll] = useState(false);
|
||||
const scrollContainerRef = useRef();
|
||||
const scrollContainerRef = useRef<HTMLDivElement>();
|
||||
const editorRef = useRef<BlockEditor>();
|
||||
const onScroll = (event: UIEvent) => {
|
||||
editorRef.current.getHooks().onRootNodeScroll(event);
|
||||
@ -130,6 +137,17 @@ const EditorContainer = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { setPageClientWidth } = usePageClientWidth();
|
||||
useEffect(() => {
|
||||
if (scrollContainerRef.current) {
|
||||
const obv = new ResizeObserver(e => {
|
||||
setPageClientWidth(e[0].contentRect.width);
|
||||
});
|
||||
obv.observe(scrollContainerRef.current);
|
||||
return () => obv.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledEditorContainer
|
||||
lockScroll={lockScroll}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
useUserAndSpaces,
|
||||
useShowSpaceSidebar,
|
||||
} from '@toeverything/datasource/state';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
|
||||
const WorkspaceContainer = styled('div')({
|
||||
@ -78,6 +78,7 @@ export const WorkspaceName = () => {
|
||||
const { fixedDisplay, toggleSpaceSidebar } = useShowSpaceSidebar();
|
||||
const [inRename, setInRename] = useState(false);
|
||||
const [workspaceName, setWorkspaceName] = useState('');
|
||||
const [workspaceId, setWorkspaceId] = useState('');
|
||||
|
||||
const fetchWorkspaceName = useCallback(async () => {
|
||||
if (!currentSpaceId) {
|
||||
@ -88,6 +89,11 @@ export const WorkspaceName = () => {
|
||||
currentSpaceId
|
||||
);
|
||||
setWorkspaceName(name);
|
||||
|
||||
const workspaceId = await services.api.userConfig.getWorkspaceId(
|
||||
currentSpaceId
|
||||
);
|
||||
setWorkspaceId(workspaceId);
|
||||
}, [currentSpaceId]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -150,7 +156,7 @@ export const WorkspaceName = () => {
|
||||
) : (
|
||||
<WorkspaceNameContainer>
|
||||
<span onClick={() => setInRename(true)}>
|
||||
{workspaceName}
|
||||
{workspaceName || workspaceId}
|
||||
</span>
|
||||
</WorkspaceNameContainer>
|
||||
)}
|
||||
|
@ -1,53 +0,0 @@
|
||||
// import { FC, useMemo, useState } from 'react';
|
||||
// import { useParams } from 'react-router-dom';
|
||||
// import { TDPage } from '@toeverything/framework/whiteboard';
|
||||
// import {
|
||||
// AffineWhiteboard,
|
||||
// WhiteboardMeta,
|
||||
// AffineEditorShape
|
||||
// } from '@toeverything/components/affine-whiteboard';
|
||||
|
||||
// interface EditorShapeProps {
|
||||
// blockIds: string | string[];
|
||||
// point: [number, number];
|
||||
// }
|
||||
|
||||
// const createEditorShape = (props: EditorShapeProps): AffineEditorShape => {
|
||||
// const block_ids = Array.isArray(props.blockIds)
|
||||
// ? props.blockIds
|
||||
// : [props.blockIds];
|
||||
// return {
|
||||
// id: block_ids.join('_'),
|
||||
// label: '',
|
||||
// childIndex: 1,
|
||||
// name: 'affine_editor',
|
||||
// parentId: 'page',
|
||||
// point: props.point,
|
||||
// rotation: 0,
|
||||
// size: [400, 200],
|
||||
// style: {
|
||||
// color: 'black',
|
||||
// size: 'small',
|
||||
// isFilled: false,
|
||||
// dash: 'draw',
|
||||
// scale: 1
|
||||
// } as any,
|
||||
// type: 'affineEditor',
|
||||
// blockIds: block_ids
|
||||
// };
|
||||
// };
|
||||
|
||||
// const Whiteboard: FC = () => {
|
||||
// const { workspace_id, page_id } = useParams();
|
||||
// const [shapes, set_shapes] = useState<TDPage['shapes']>({});
|
||||
// const meta = useMemo<WhiteboardMeta>(() => {
|
||||
// return {
|
||||
// workspace: workspace_id,
|
||||
// rootBlockId: page_id
|
||||
// };
|
||||
// }, [workspace_id, page_id]);
|
||||
|
||||
// return page_id ? <AffineWhiteboard meta={meta} shapes={shapes} /> : null;
|
||||
// };
|
||||
|
||||
// export default Whiteboard;
|
11
apps/venus/.babelrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
16
apps/venus/.browserslistrc
Normal file
@ -0,0 +1,16 @@
|
||||
# This file is used by:
|
||||
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
||||
# 2. babel preset-env to adjust included polyfills
|
||||
#
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# If you need to support different browsers in production, you may tweak the list below.
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
18
apps/venus/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
19
apps/venus/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "venus",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"description": "",
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "DarkSky <darksky2048@gmail.com>",
|
||||
"dependencies": {
|
||||
"@mui/joy": "^5.0.0-alpha.39",
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"lozad": "^1.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"webpack": "^5.73.0"
|
||||
}
|
||||
}
|
76
apps/venus/project.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/venus/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:webpack",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"compiler": "babel",
|
||||
"outputPath": "dist/apps/venus",
|
||||
"index": "apps/venus/src/index.html",
|
||||
"baseHref": "/",
|
||||
"main": "apps/venus/src/index.tsx",
|
||||
"polyfills": "apps/venus/src/polyfills.ts",
|
||||
"tsConfig": "apps/venus/tsconfig.app.json",
|
||||
"assets": ["apps/venus/src/assets"],
|
||||
"styles": [],
|
||||
"scripts": [],
|
||||
"webpackConfig": "apps/venus/webpack.config.js"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/venus/src/environments/environment.ts",
|
||||
"with": "apps/venus/src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": true,
|
||||
"extractLicenses": false,
|
||||
"vendorChunk": false,
|
||||
"generateIndexHtml": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nrwl/web:dev-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "venus:build",
|
||||
"hmr": true,
|
||||
"open": true
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "venus:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "venus:build:production",
|
||||
"hmr": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/venus/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/apps/venus"],
|
||||
"options": {
|
||||
"jestConfig": "apps/venus/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["app:venus"]
|
||||
}
|
938
apps/venus/src/app/index.tsx
Normal file
@ -0,0 +1,938 @@
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/* eslint-disable filename-rules/match */
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { CssVarsProvider, styled } from '@mui/joy/styles';
|
||||
import { Box, Button, Container, Grid, SvgIcon, Typography } from '@mui/joy';
|
||||
import Card from '@mui/joy/Card';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import RedditIcon from '@mui/icons-material/Reddit';
|
||||
import TelegramIcon from '@mui/icons-material/Telegram';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
|
||||
const DiscordIcon = (props: any) => {
|
||||
return (
|
||||
<SvgIcon
|
||||
{...props}
|
||||
width="71"
|
||||
height="55"
|
||||
viewBox="0 0 71 55"
|
||||
fill="none"
|
||||
>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path
|
||||
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
|
||||
fill="#23272A"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="71" height="55" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</SvgIcon>
|
||||
);
|
||||
};
|
||||
|
||||
const VenusContainer = styled(Container)({
|
||||
margin: '1em auto',
|
||||
});
|
||||
|
||||
const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({
|
||||
position: 'relative',
|
||||
width: '24em',
|
||||
height: '128px',
|
||||
transform: 'translateY(-8px)',
|
||||
overflowY: 'hidden',
|
||||
'@media (max-width: 768px)': {
|
||||
width,
|
||||
height: '48px',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
'& .scroll-element': {
|
||||
width: 'inherit',
|
||||
height: 'inherit',
|
||||
position: 'absolute',
|
||||
left: '0%',
|
||||
top: '0%',
|
||||
lineHeight: '96px',
|
||||
'@media (max-width: 768px)': {
|
||||
lineHeight: '32px',
|
||||
},
|
||||
},
|
||||
'& .scroll-element.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.primary.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.secondary.active': {
|
||||
animation: 'secondary 500ms linear infinite',
|
||||
},
|
||||
'@keyframes primary': {
|
||||
from: {
|
||||
top: '0%',
|
||||
},
|
||||
to: {
|
||||
top: '-100%',
|
||||
},
|
||||
},
|
||||
'@keyframes secondary': {
|
||||
from: {
|
||||
top: '100%',
|
||||
},
|
||||
to: {
|
||||
top: '0%',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const _alternatives = ['Notion', 'Miro', 'Monday'];
|
||||
const _alternativesSize = [8, 6, 10];
|
||||
|
||||
const Product = () => {
|
||||
const [idx, setIdx] = useState(0);
|
||||
const [last, current] = useMemo(
|
||||
() => [
|
||||
_alternatives[idx],
|
||||
_alternatives[idx + 1] ? _alternatives[idx + 1] : _alternatives[0],
|
||||
],
|
||||
[idx]
|
||||
);
|
||||
const maxWidth = useMemo(() => _alternativesSize[idx], [idx]);
|
||||
const [active, setActive] = useState(false);
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
useEffect(() => {
|
||||
const handle = setInterval(() => {
|
||||
setActive(true);
|
||||
setTimeout(
|
||||
() => {
|
||||
setIdx(idx => (_alternatives[idx + 1] ? idx + 1 : 0));
|
||||
setActive(false);
|
||||
},
|
||||
matches ? 450 : 380
|
||||
);
|
||||
}, 2000);
|
||||
return () => clearInterval(handle);
|
||||
}, [matches]);
|
||||
|
||||
return (
|
||||
<Alternatives
|
||||
width={`${maxWidth}em`}
|
||||
sx={{ margin: 'auto', marginRight: '1em', transition: 'width .5s' }}
|
||||
>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 768px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{last}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
sx={{
|
||||
marginTop: '96px',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 768px)': {
|
||||
marginTop: '48px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 768px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{current}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Alternatives>
|
||||
);
|
||||
};
|
||||
|
||||
const AffineImage = styled('img')({
|
||||
maxWidth: '100%',
|
||||
objectFit: 'contain',
|
||||
});
|
||||
|
||||
const GitHub = (props: { center?: boolean; flat?: boolean }) => {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.location.replace(
|
||||
'https://github.com/toeverything/AFFiNE'
|
||||
);
|
||||
}}
|
||||
{...(props.flat ? { variant: 'plain' } : {})}
|
||||
{...{
|
||||
sx: {
|
||||
margin: 'auto 1em',
|
||||
...(props.flat
|
||||
? {
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
}
|
||||
: {}),
|
||||
...(props.center
|
||||
? {
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '2em',
|
||||
':hover': {
|
||||
backgroundColor: '#0c60d9',
|
||||
boxShadow: '2px 2px 20px #08f4',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}}
|
||||
startIcon={<GitHubIcon />}
|
||||
size="lg"
|
||||
>
|
||||
GitHub
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export function App() {
|
||||
const matches = useMediaQuery('(max-width: 768px)');
|
||||
|
||||
return (
|
||||
<CssVarsProvider>
|
||||
<VenusContainer
|
||||
fixed
|
||||
sx={{
|
||||
maxWidth: '1440px !important',
|
||||
'&>div': {
|
||||
marginTop: '1em',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{ maxWidth: '1280px', margin: 'auto' }}
|
||||
>
|
||||
<Grid xs={6}>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="plain"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
}}
|
||||
>
|
||||
AFFiNE
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={6}
|
||||
sx={{ display: 'flex', justifyContent: 'right' }}
|
||||
>
|
||||
<GitHub flat />
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.location.replace(
|
||||
'https://blog.affine.pro'
|
||||
);
|
||||
}}
|
||||
variant="plain"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
}}
|
||||
size="lg"
|
||||
>
|
||||
Blog
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{ display: 'flex', marginTop: '12vh!important' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
marginRight: '0.25em',
|
||||
'@media (max-width: 768px)': {
|
||||
fontSize: '32px',
|
||||
marginRight: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Open Source,
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
'@media (max-width: 768px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Privacy First
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexFlow: 'wrap',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexFlow: 'wrap',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Product />
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
margin: 'auto',
|
||||
'@media (max-width: 768px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Alternative
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h3"
|
||||
fontWeight={'400'}
|
||||
sx={{ color: '#888' }}
|
||||
>
|
||||
Affine is the next-generation collaborative
|
||||
knowledge base for professionals.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginTop: '1.5em',
|
||||
marginBottom: '12vh!important',
|
||||
}}
|
||||
>
|
||||
<Typography level="h2" sx={{ alignSelf: 'center' }}>
|
||||
Try it on
|
||||
</Typography>
|
||||
<GitHub center />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{ display: 'flex', maxWidth: '1200px', margin: 'auto' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
src="/assets/page.png"
|
||||
alt="AFFiNE main ui"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
marginTop: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level={matches ? 'h2' : 'h1'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
It’s not just add-up of Docs, whiteboard, and
|
||||
tables.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography fontSize="1.2em">
|
||||
Transform any building blocks as you like.
|
||||
</Typography>
|
||||
<Typography fontSize="1.2em">
|
||||
Say goodbye to redundancy, and keep all your
|
||||
knowledge minimal, in your way.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 3}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches
|
||||
? {}
|
||||
: { marginLeft: '4em', marginRight: '2em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
Shape Your Page
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
Docs, kanban, and databases are all fully
|
||||
functional at any place. You always keep
|
||||
what-you-see-is-what-you-get.
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
All pages come with docs and whiteboard mode.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 9}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
src="/assets/shape.png"
|
||||
alt="AFFiNE Shape Your Page"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row-reverse',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 4}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches ? {} : { marginRight: '4em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
Plan Your Task
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
No more chaos between so many views.
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
Set a TODO with Markdown, and manage it in
|
||||
Kanban.
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
Managing multi-dimensional tables should be
|
||||
simple as that.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 8}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
src="/assets/task.png"
|
||||
alt="AFFiNE Plan Your Task"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginBottom: '4em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
Privacy-first, and collaborative. No compromises
|
||||
whatsoever.
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
We don’t like cloud lock-in. Your data is always
|
||||
locally stored and secured, while you can still
|
||||
collaborate in real-time with others.
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
Privacy is the foundation of everything we do. But
|
||||
it is no excuse for the bad experience.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '12em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
src="/assets/collaboration.png"
|
||||
alt="AFFiNE Privacy-first, and collaborative"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
}}
|
||||
>
|
||||
<AffineImage src="/assets/logo.png" alt="AFFiNE Logo" />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography fontSize={'1.5em'}>
|
||||
Build for an open and semantic future
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '8em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography level="h3" sx={{ display: 'flex' }}>
|
||||
<span style={{ alignSelf: 'center' }}>
|
||||
Keep Updated on
|
||||
</span>
|
||||
<GitHub />
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '2em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography level="h2" sx={{ display: 'flex' }}>
|
||||
Join Our Community
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
maxWidth: '400px',
|
||||
margin: 'auto',
|
||||
marginBottom: '2em',
|
||||
'--joy-shadow-sm': 0,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<GitHubIcon
|
||||
sx={{ width: '36px', height: '36px' }}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
margin: 'auto',
|
||||
marginTop: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
>
|
||||
GitHub
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<RedditIcon
|
||||
sx={{ width: '36px', height: '36px' }}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
margin: 'auto',
|
||||
marginTop: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
>
|
||||
Reddit
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<TelegramIcon
|
||||
sx={{ width: '36px', height: '36px' }}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
margin: 'auto',
|
||||
marginTop: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
>
|
||||
Telegram
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<DiscordIcon
|
||||
sx={{ width: '36px', height: '36px' }}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
margin: 'auto',
|
||||
marginTop: '1em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
>
|
||||
Discord
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '2em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ display: 'flex', color: '#888' }}>
|
||||
AFFiNE is an
|
||||
<span
|
||||
style={{
|
||||
color: '#5085f6cc',
|
||||
margin: 'auto 0.25em',
|
||||
}}
|
||||
>
|
||||
#OpenSource
|
||||
</span>
|
||||
company
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '2em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ display: 'flex', color: '#888' }}>
|
||||
Copyright © 2022 AFFiNE.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
</VenusContainer>
|
||||
</CssVarsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
0
apps/venus/src/assets/.gitkeep
Normal file
BIN
apps/venus/src/assets/collaboration.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
10
apps/venus/src/assets/discord.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#23272A"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="71" height="55" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
BIN
apps/venus/src/assets/images/favicon.ico
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
apps/venus/src/assets/logo.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
apps/venus/src/assets/page.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
apps/venus/src/assets/shape.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
apps/venus/src/assets/task.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
3
apps/venus/src/environments/environment.prod.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
};
|
6
apps/venus/src/environments/environment.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
};
|
BIN
apps/venus/src/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
14
apps/venus/src/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Venus</title>
|
||||
<base href="/" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
19
apps/venus/src/index.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
/* eslint-disable filename-rules/match */
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import App from './app';
|
||||
|
||||
const container = document.getElementById('root');
|
||||
if (!container) {
|
||||
throw new Error('No root container found');
|
||||
}
|
||||
const root = createRoot(container);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
);
|
7
apps/venus/src/polyfills.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
11
apps/venus/src/template.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
23
apps/venus/tsconfig.app.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
24
apps/venus/tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
24
apps/venus/tsconfig.spec.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||
]
|
||||
}
|
237
apps/venus/webpack.config.js
Normal file
@ -0,0 +1,237 @@
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const webpack = require('webpack');
|
||||
const getNxWebpackConfig = require('@nrwl/react/plugins/webpack');
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const CompressionPlugin = require('compression-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const Style9Plugin = require('style9/webpack');
|
||||
|
||||
const enableBundleAnalyzer = process.env.BUNDLE_ANALYZER;
|
||||
|
||||
module.exports = function (webpackConfig) {
|
||||
const config = getNxWebpackConfig(webpackConfig);
|
||||
|
||||
const isProd = config.mode === 'production';
|
||||
|
||||
const style9 = {
|
||||
test: /\.(tsx|ts|js|mjs|jsx)$/,
|
||||
use: [
|
||||
{
|
||||
loader: Style9Plugin.loader,
|
||||
options: {
|
||||
minifyProperties: isProd,
|
||||
incrementalClassnames: isProd,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
config.experiments.topLevelAwait = true;
|
||||
|
||||
if (isProd) {
|
||||
config.module.rules.unshift(style9);
|
||||
} else {
|
||||
config.module.rules.push(style9);
|
||||
}
|
||||
|
||||
if (isProd) {
|
||||
config.entry = {
|
||||
main: [...config.entry.main, ...config.entry.polyfills],
|
||||
};
|
||||
config.devtool = false;
|
||||
config.output = {
|
||||
...config.output,
|
||||
filename: '[name].[contenthash:8].js',
|
||||
chunkFilename: '[name].[chunkhash:8].js',
|
||||
hashFunction: undefined,
|
||||
};
|
||||
config.optimization = {
|
||||
nodeEnv: 'production',
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
ecma: 2020,
|
||||
},
|
||||
extractComments: true,
|
||||
parallel: true,
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
],
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
cacheGroups: {
|
||||
styles: {
|
||||
name: 'styles',
|
||||
type: 'css/mini-extract',
|
||||
chunks: 'all',
|
||||
enforce: true,
|
||||
},
|
||||
ui: {
|
||||
test: /[\\/]node_modules[\\/](@mui|@emotion|react|katex)/,
|
||||
name: 'ui',
|
||||
priority: -9,
|
||||
chunks: 'all',
|
||||
},
|
||||
vender: {
|
||||
test: /([\\/]node_modules[\\/]|polyfills|@nrwl)/,
|
||||
name: 'vender',
|
||||
priority: -10,
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
config.module.rules.unshift({
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
config.module.rules.unshift({
|
||||
test: /\.scss$/i,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
},
|
||||
],
|
||||
});
|
||||
config.module.rules.splice(6);
|
||||
} else {
|
||||
config.output = {
|
||||
...config.output,
|
||||
publicPath: '/',
|
||||
};
|
||||
|
||||
const babelLoader = config.module.rules.find(
|
||||
rule =>
|
||||
typeof rule !== 'string' &&
|
||||
rule.loader?.toString().includes('babel-loader')
|
||||
);
|
||||
if (babelLoader && typeof babelLoader !== 'string') {
|
||||
babelLoader.options['plugins'] = [
|
||||
...(babelLoader.options['plugins'] || []),
|
||||
[require.resolve('babel-plugin-open-source')],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
addEmotionBabelPlugin(config);
|
||||
|
||||
config.plugins = [
|
||||
...config.plugins.filter(
|
||||
p => !(isProd && p instanceof MiniCssExtractPlugin)
|
||||
),
|
||||
new webpack.DefinePlugin({
|
||||
JWT_DEV: !isProd,
|
||||
global: {},
|
||||
}),
|
||||
isProd &&
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'AFFiNE - All In One Workos',
|
||||
favicon: path.resolve(
|
||||
__dirname,
|
||||
'./src/assets/images/favicon.ico'
|
||||
), //favicon path
|
||||
template: path.resolve(__dirname, './src/template.html'),
|
||||
publicPath: '/',
|
||||
}),
|
||||
new Style9Plugin(),
|
||||
isProd && new MiniCssExtractPlugin(),
|
||||
isProd &&
|
||||
new CompressionPlugin({
|
||||
test: /\.(js|css|html|svg|ttf|woff)$/,
|
||||
algorithm: 'brotliCompress',
|
||||
filename: '[path][base].br',
|
||||
compressionOptions: {
|
||||
params: {
|
||||
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
|
||||
},
|
||||
},
|
||||
}),
|
||||
isProd &&
|
||||
enableBundleAnalyzer &&
|
||||
new BundleAnalyzerPlugin({ analyzerMode: 'static' }),
|
||||
].filter(Boolean);
|
||||
|
||||
// Workaround for webpack infinite recompile errors
|
||||
config.watchOptions = {
|
||||
// followSymlinks: false,
|
||||
ignored: ['**/*.css'],
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
// TODO handle nx issue
|
||||
// see https://github.com/nrwl/nx/issues/8870
|
||||
// see https://github.com/nrwl/nx/issues/4520#issuecomment-787473383
|
||||
const addEmotionBabelPlugin = config => {
|
||||
const babelLoader = config.module.rules.find(
|
||||
rule =>
|
||||
typeof rule !== 'string' &&
|
||||
rule.loader?.toString().includes('babel-loader')
|
||||
);
|
||||
if (!babelLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
babelLoader.options.plugins = [
|
||||
[
|
||||
require.resolve('@emotion/babel-plugin'),
|
||||
{
|
||||
// See https://github.com/mui/material-ui/issues/27380#issuecomment-928973157
|
||||
// See https://github.com/emotion-js/emotion/tree/main/packages/babel-plugin#importmap
|
||||
importMap: {
|
||||
'@toeverything/components/ui': {
|
||||
styled: {
|
||||
canonicalImport: ['@emotion/styled', 'default'],
|
||||
styledBaseImport: [
|
||||
'@toeverything/components/ui',
|
||||
'styled',
|
||||
],
|
||||
},
|
||||
},
|
||||
'@mui/material': {
|
||||
styled: {
|
||||
canonicalImport: ['@emotion/styled', 'default'],
|
||||
styledBaseImport: ['@mui/material', 'styled'],
|
||||
},
|
||||
},
|
||||
'@mui/material/styles': {
|
||||
styled: {
|
||||
canonicalImport: ['@emotion/styled', 'default'],
|
||||
styledBaseImport: [
|
||||
'@mui/material/styles',
|
||||
'styled',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// sourceMap is on by default but source maps are dead code eliminated in production
|
||||
sourceMap: true,
|
||||
autoLabel: 'dev-only',
|
||||
labelFormat: '[filename]-[local]',
|
||||
cssPropOptimization: true,
|
||||
},
|
||||
],
|
||||
...(babelLoader.options.plugins ?? []),
|
||||
];
|
||||
};
|
@ -3,8 +3,12 @@ import { services } from '@toeverything/datasource/db-service';
|
||||
import type { ReturnEditorBlock } from '@toeverything/datasource/db-service';
|
||||
import type { TDShape } from '@toeverything/components/board-types';
|
||||
import { Editor } from '@toeverything/components/board-shapes';
|
||||
import { usePageClientWidth } from '@toeverything/datasource/state';
|
||||
|
||||
export const useShapes = (workspace: string, rootBlockId: string) => {
|
||||
const { pageClientWidth } = usePageClientWidth();
|
||||
// page padding left and right total 300px
|
||||
const editorShapeInitSize = pageClientWidth - 300;
|
||||
const [blocks, setBlocks] = useState<ReturnEditorBlock[]>();
|
||||
useEffect(() => {
|
||||
services.api.editorBlock
|
||||
@ -58,8 +62,9 @@ export const useShapes = (workspace: string, rootBlockId: string) => {
|
||||
acc[block.id] = { ...shapeProps, id: block.id };
|
||||
} else {
|
||||
acc[block.id] = Editor.getShape({
|
||||
point: [groupCount * 740, 200],
|
||||
point: [groupCount * editorShapeInitSize + 200, 200],
|
||||
id: block.id,
|
||||
size: [editorShapeInitSize, 200],
|
||||
...shapeProps,
|
||||
affineId: shapeProps.affineId ?? block.id,
|
||||
workspace: block.workspace,
|
||||
|
@ -64,18 +64,7 @@ export class EditorUtil extends TDShapeUtil<T, E> {
|
||||
};
|
||||
|
||||
Component = TDShapeUtil.Component<T, E, TDMeta>(
|
||||
(
|
||||
{
|
||||
shape,
|
||||
meta: {
|
||||
app: { useStore },
|
||||
},
|
||||
events,
|
||||
isEditing,
|
||||
onShapeChange,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
({ shape, meta: { app }, events, isEditing, onShapeChange }, ref) => {
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const {
|
||||
workspace,
|
||||
@ -83,7 +72,7 @@ export class EditorUtil extends TDShapeUtil<T, E> {
|
||||
size: [width, height],
|
||||
} = shape;
|
||||
|
||||
const state = useStore();
|
||||
const state = app.useStore();
|
||||
const { currentPageId } = state.appState;
|
||||
const { editingId } = state.document.pageStates[currentPageId];
|
||||
const { shapes } = state.document.pages[currentPageId];
|
||||
@ -134,11 +123,19 @@ export class EditorUtil extends TDShapeUtil<T, E> {
|
||||
[isEditing]
|
||||
);
|
||||
|
||||
const activateIfEditing = useCallback(() => {
|
||||
if (editingText && editingId !== shape.id) {
|
||||
app.setEditingText(shape.id);
|
||||
}
|
||||
}, [app, shape.id, editingText, editingId]);
|
||||
|
||||
return (
|
||||
<HTMLContainer ref={ref} {...events}>
|
||||
<Container
|
||||
ref={containerRef}
|
||||
onPointerDown={stopPropagation}
|
||||
onMouseEnter={activateIfEditing}
|
||||
onDragEnter={activateIfEditing}
|
||||
>
|
||||
<MemoAffineEditor
|
||||
workspace={workspace}
|
||||
|
@ -967,6 +967,29 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* used for EditorUtil only
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
setEditingText = (id: string) => {
|
||||
if (this.readOnly) return;
|
||||
|
||||
this.patchState(
|
||||
{
|
||||
document: {
|
||||
pageStates: {
|
||||
[this.currentPageId]: {
|
||||
selectedIds: [id],
|
||||
editingId: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
`set_editing_id`
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or clear the hovered id
|
||||
* @param id [string]
|
||||
|
@ -149,6 +149,7 @@ export interface TDMeta {
|
||||
isDarkMode: boolean;
|
||||
app: {
|
||||
useStore: () => TDSnapshot;
|
||||
setEditingText: (id: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ export const PendantHistoryPanel = ({
|
||||
const { getProperties } = useRecastBlockMeta();
|
||||
const { getProperty } = useRecastBlockMeta();
|
||||
|
||||
const { getAllValue } = getRecastItemValue(block);
|
||||
const recastBlock = useRecastBlock();
|
||||
|
||||
const [history, setHistory] = useState<RecastBlockValue[]>([]);
|
||||
@ -34,7 +33,7 @@ export const PendantHistoryPanel = ({
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
const currentBlockValues = getAllValue();
|
||||
const currentBlockValues = getRecastItemValue(block).getAllValue();
|
||||
const allProperties = getProperties();
|
||||
const missProperties = allProperties.filter(
|
||||
property => !currentBlockValues.find(v => v.id === property.id)
|
||||
@ -52,24 +51,26 @@ export const PendantHistoryPanel = ({
|
||||
return history;
|
||||
}, {});
|
||||
|
||||
const blockHistory = await Promise.all(
|
||||
Object.entries(historyMap).map(
|
||||
async ([propertyId, blockId]) => {
|
||||
const latestValueBlock = (
|
||||
await groupBlock.children()
|
||||
).find((block: AsyncBlock) => block.id === blockId);
|
||||
const blockHistory = (
|
||||
await Promise.all(
|
||||
Object.entries(historyMap).map(
|
||||
async ([propertyId, blockId]) => {
|
||||
const latestValueBlock = (
|
||||
await groupBlock.children()
|
||||
).find((block: AsyncBlock) => block.id === blockId);
|
||||
|
||||
return getRecastItemValue(latestValueBlock).getValue(
|
||||
propertyId as RecastPropertyId
|
||||
);
|
||||
}
|
||||
return getRecastItemValue(
|
||||
latestValueBlock
|
||||
).getValue(propertyId as RecastPropertyId);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
).filter(v => v);
|
||||
setHistory(blockHistory);
|
||||
};
|
||||
|
||||
init();
|
||||
}, [getAllValue, getProperties, groupBlock, recastBlock]);
|
||||
}, [block, getProperties, groupBlock, recastBlock]);
|
||||
|
||||
return (
|
||||
<StyledPendantHistoryPanel>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { CSSProperties, useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { Input, Option, Select, Tooltip } from '@toeverything/components/ui';
|
||||
import { HelpCenterIcon } from '@toeverything/components/icons';
|
||||
import { AsyncBlock } from '../../editor';
|
||||
|
||||
import { IconMap, pendantOptions } from '../config';
|
||||
import { OptionType, PendantOptions, PendantTypes } from '../types';
|
||||
import { PendantOptions } from '../types';
|
||||
import { PendantModifyPanel } from '../pendant-modify-panel';
|
||||
import {
|
||||
StyledDivider,
|
||||
@ -15,23 +15,13 @@ import {
|
||||
StyledPopoverSubTitle,
|
||||
StyledPopoverWrapper,
|
||||
} from '../StyledComponent';
|
||||
import {
|
||||
genSelectOptionId,
|
||||
InformationProperty,
|
||||
useRecastBlock,
|
||||
useRecastBlockMeta,
|
||||
useSelectProperty,
|
||||
} from '../../recast-block';
|
||||
import {
|
||||
genInitialOptions,
|
||||
getOfficialSelected,
|
||||
getPendantConfigByType,
|
||||
} from '../utils';
|
||||
import { usePendant } from '../use-pendant';
|
||||
import { genInitialOptions, getPendantConfigByType } from '../utils';
|
||||
import { useOnCreateSure } from './hooks';
|
||||
|
||||
const upperFirst = (str: string) => {
|
||||
return `${str[0].toUpperCase()}${str.slice(1)}`;
|
||||
};
|
||||
|
||||
export const CreatePendantPanel = ({
|
||||
block,
|
||||
onSure,
|
||||
@ -41,9 +31,7 @@ export const CreatePendantPanel = ({
|
||||
}) => {
|
||||
const [selectedOption, setSelectedOption] = useState<PendantOptions>();
|
||||
const [fieldName, setFieldName] = useState<string>('');
|
||||
const { addProperty, removeProperty } = useRecastBlockMeta();
|
||||
const { createSelect } = useSelectProperty();
|
||||
const { setPendant } = usePendant(block);
|
||||
const onCreateSure = useOnCreateSure({ block });
|
||||
|
||||
useEffect(() => {
|
||||
selectedOption &&
|
||||
@ -110,91 +98,13 @@ export const CreatePendantPanel = ({
|
||||
getPendantConfigByType(selectedOption.type)
|
||||
)}
|
||||
iconConfig={getPendantConfigByType(selectedOption.type)}
|
||||
// isStatusSelect={selectedOption.name === 'Status'}
|
||||
onSure={async (type, newPropertyItem, newValue) => {
|
||||
if (!fieldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
type === PendantTypes.MultiSelect ||
|
||||
type === PendantTypes.Select ||
|
||||
type === PendantTypes.Status
|
||||
) {
|
||||
const newProperty = await createSelect({
|
||||
name: fieldName,
|
||||
options: newPropertyItem,
|
||||
type,
|
||||
});
|
||||
|
||||
const selectedId = getOfficialSelected({
|
||||
isMulti: type === PendantTypes.MultiSelect,
|
||||
options: newProperty.options,
|
||||
tempOptions: newPropertyItem,
|
||||
tempSelectedId: newValue,
|
||||
});
|
||||
|
||||
await setPendant(newProperty, selectedId);
|
||||
} else if (type === PendantTypes.Information) {
|
||||
const emailOptions = genOptionWithId(
|
||||
newPropertyItem.emailOptions
|
||||
);
|
||||
|
||||
const phoneOptions = genOptionWithId(
|
||||
newPropertyItem.phoneOptions
|
||||
);
|
||||
|
||||
const locationOptions = genOptionWithId(
|
||||
newPropertyItem.locationOptions
|
||||
);
|
||||
|
||||
const newProperty = await addProperty({
|
||||
type,
|
||||
name: fieldName,
|
||||
emailOptions,
|
||||
phoneOptions,
|
||||
locationOptions,
|
||||
} as Omit<InformationProperty, 'id'>);
|
||||
|
||||
await setPendant(newProperty, {
|
||||
email: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: emailOptions,
|
||||
tempOptions:
|
||||
newPropertyItem.emailOptions,
|
||||
tempSelectedId: newValue.email,
|
||||
}),
|
||||
phone: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: phoneOptions,
|
||||
tempOptions:
|
||||
newPropertyItem.phoneOptions,
|
||||
tempSelectedId: newValue.phone,
|
||||
}),
|
||||
location: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: locationOptions,
|
||||
tempOptions:
|
||||
newPropertyItem.locationOptions,
|
||||
tempSelectedId: newValue.location,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
// TODO: Color and background should use pendant config, but ui is not design now
|
||||
const iconConfig = getPendantConfigByType(type);
|
||||
// TODO: Color and background should be choose by user in the future
|
||||
const newProperty = await addProperty({
|
||||
type: type,
|
||||
name: fieldName,
|
||||
background:
|
||||
iconConfig.background as CSSProperties['background'],
|
||||
color: iconConfig.color as CSSProperties['color'],
|
||||
iconName: iconConfig.iconName,
|
||||
});
|
||||
|
||||
await setPendant(newProperty, newValue);
|
||||
}
|
||||
|
||||
await onCreateSure({
|
||||
type,
|
||||
newPropertyItem,
|
||||
newValue,
|
||||
fieldName,
|
||||
});
|
||||
onSure?.();
|
||||
}}
|
||||
/>
|
||||
@ -203,10 +113,3 @@ export const CreatePendantPanel = ({
|
||||
</StyledPopoverWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const genOptionWithId = (options: OptionType[] = []) => {
|
||||
return options.map((option: OptionType) => ({
|
||||
...option,
|
||||
id: genSelectOptionId(),
|
||||
}));
|
||||
};
|
||||
|
@ -1,27 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import { Input, Tooltip } from '@toeverything/components/ui';
|
||||
import { HelpCenterIcon } from '@toeverything/components/icons';
|
||||
import { PendantModifyPanel } from '../pendant-modify-panel';
|
||||
import type { AsyncBlock } from '../../editor';
|
||||
import {
|
||||
genSelectOptionId,
|
||||
InformationProperty,
|
||||
type MultiSelectProperty,
|
||||
type RecastBlockValue,
|
||||
type RecastMetaProperty,
|
||||
type SelectOption,
|
||||
type SelectProperty,
|
||||
useRecastBlockMeta,
|
||||
useSelectProperty,
|
||||
} from '../../recast-block';
|
||||
import { OptionType, PendantTypes, TempInformationType } from '../types';
|
||||
import {
|
||||
getOfficialSelected,
|
||||
getPendantConfigByType,
|
||||
// getPendantIconsConfigByNameOrType,
|
||||
} from '../utils';
|
||||
import { getPendantConfigByType } from '../utils';
|
||||
import { usePendant } from '../use-pendant';
|
||||
import {
|
||||
StyledPopoverWrapper,
|
||||
StyledOperationTitle,
|
||||
StyledOperationLabel,
|
||||
StyledInputEndAdornment,
|
||||
StyledDivider,
|
||||
@ -29,10 +18,8 @@ import {
|
||||
StyledPopoverSubTitle,
|
||||
} from '../StyledComponent';
|
||||
import { IconMap, pendantOptions } from '../config';
|
||||
import { Input, Tooltip } from '@toeverything/components/ui';
|
||||
import { HelpCenterIcon } from '@toeverything/components/icons';
|
||||
|
||||
type SelectPropertyType = MultiSelectProperty | SelectProperty;
|
||||
import { useOnUpdateSure } from './hooks';
|
||||
|
||||
type Props = {
|
||||
value: RecastBlockValue;
|
||||
@ -53,13 +40,12 @@ export const UpdatePendantPanel = ({
|
||||
onCancel,
|
||||
titleEditable = false,
|
||||
}: Props) => {
|
||||
const { updateSelect } = useSelectProperty();
|
||||
const { setPendant, removePendant } = usePendant(block);
|
||||
const pendantOption = pendantOptions.find(v => v.type === property.type);
|
||||
const iconConfig = getPendantConfigByType(property.type);
|
||||
const { removePendant } = usePendant(block);
|
||||
const Icon = IconMap[iconConfig.iconName];
|
||||
const { updateProperty } = useRecastBlockMeta();
|
||||
const [fieldTitle, setFieldTitle] = useState(property.name);
|
||||
const [fieldName, setFieldName] = useState(property.name);
|
||||
const onUpdateSure = useOnUpdateSure({ block, property });
|
||||
|
||||
return (
|
||||
<StyledPopoverWrapper>
|
||||
@ -77,10 +63,10 @@ export const UpdatePendantPanel = ({
|
||||
<StyledOperationLabel>Field Title</StyledOperationLabel>
|
||||
{titleEditable ? (
|
||||
<Input
|
||||
value={fieldTitle}
|
||||
value={fieldName}
|
||||
placeholder="Input your field name here"
|
||||
onChange={e => {
|
||||
setFieldTitle(e.target.value);
|
||||
setFieldName(e.target.value);
|
||||
}}
|
||||
endAdornment={
|
||||
<Tooltip content="Help info here">
|
||||
@ -111,114 +97,12 @@ export const UpdatePendantPanel = ({
|
||||
property={property}
|
||||
type={property.type}
|
||||
onSure={async (type, newPropertyItem, newValue) => {
|
||||
if (
|
||||
type === PendantTypes.MultiSelect ||
|
||||
type === PendantTypes.Select ||
|
||||
type === PendantTypes.Status
|
||||
) {
|
||||
const newOptions = newPropertyItem as OptionType[];
|
||||
let selectProperty = property as SelectPropertyType;
|
||||
const deleteOptionIds = selectProperty.options
|
||||
.filter(o => {
|
||||
return !newOptions.find(no => no.id === o.id);
|
||||
})
|
||||
.map(o => o.id);
|
||||
const addOptions = newOptions.filter(
|
||||
o => typeof o.id === 'number'
|
||||
);
|
||||
|
||||
const { addSelectOptions, removeSelectOptions } =
|
||||
updateSelect(selectProperty);
|
||||
|
||||
deleteOptionIds.length &&
|
||||
(selectProperty = (await removeSelectOptions(
|
||||
...deleteOptionIds
|
||||
)) as SelectPropertyType);
|
||||
|
||||
addOptions.length &&
|
||||
(selectProperty = (await addSelectOptions(
|
||||
...(addOptions as unknown as Omit<
|
||||
SelectOption,
|
||||
'id'
|
||||
>[])
|
||||
)) as SelectPropertyType);
|
||||
|
||||
const selectedId = getOfficialSelected({
|
||||
isMulti: type === PendantTypes.MultiSelect,
|
||||
options: selectProperty.options,
|
||||
tempOptions: newPropertyItem,
|
||||
tempSelectedId: newValue,
|
||||
});
|
||||
|
||||
await setPendant(selectProperty, selectedId);
|
||||
} else if (type === PendantTypes.Information) {
|
||||
// const { emailOptions, phoneOptions, locationOptions } =
|
||||
// property as InformationProperty;
|
||||
const optionGroup =
|
||||
newPropertyItem as TempInformationType;
|
||||
|
||||
const emailOptions = optionGroup.emailOptions.map(
|
||||
option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
}
|
||||
);
|
||||
const phoneOptions = optionGroup.phoneOptions.map(
|
||||
option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
}
|
||||
);
|
||||
const locationOptions = optionGroup.locationOptions.map(
|
||||
option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
}
|
||||
);
|
||||
|
||||
const newProperty = await updateProperty({
|
||||
...property,
|
||||
emailOptions,
|
||||
phoneOptions,
|
||||
locationOptions,
|
||||
} as InformationProperty);
|
||||
|
||||
await setPendant(newProperty, {
|
||||
email: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: emailOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.emailOptions,
|
||||
tempSelectedId: newValue.email,
|
||||
}),
|
||||
phone: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: phoneOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.phoneOptions,
|
||||
tempSelectedId: newValue.phone,
|
||||
}),
|
||||
location: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: locationOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.locationOptions,
|
||||
tempSelectedId: newValue.location,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
await setPendant(property, newValue);
|
||||
}
|
||||
|
||||
if (fieldTitle !== property.name) {
|
||||
await updateProperty({
|
||||
...property,
|
||||
name: fieldTitle,
|
||||
});
|
||||
}
|
||||
await onUpdateSure({
|
||||
type,
|
||||
newPropertyItem,
|
||||
newValue,
|
||||
fieldName,
|
||||
});
|
||||
onSure?.();
|
||||
}}
|
||||
onDelete={
|
||||
|
@ -0,0 +1,265 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import {
|
||||
genSelectOptionId,
|
||||
type InformationProperty,
|
||||
type MultiSelectProperty,
|
||||
type RecastMetaProperty,
|
||||
type SelectOption,
|
||||
type SelectProperty,
|
||||
useRecastBlockMeta,
|
||||
useSelectProperty,
|
||||
} from '../../recast-block';
|
||||
import { type AsyncBlock } from '../../editor';
|
||||
import { usePendant } from '../use-pendant';
|
||||
import {
|
||||
type OptionType,
|
||||
PendantTypes,
|
||||
type TempInformationType,
|
||||
} from '../types';
|
||||
import {
|
||||
checkPendantForm,
|
||||
getOfficialSelected,
|
||||
getPendantConfigByType,
|
||||
} from '../utils';
|
||||
import { message } from '@toeverything/components/ui';
|
||||
|
||||
type SelectPropertyType = MultiSelectProperty | SelectProperty;
|
||||
type SureParams = {
|
||||
fieldName: string;
|
||||
type: PendantTypes;
|
||||
newPropertyItem: any;
|
||||
newValue: any;
|
||||
};
|
||||
|
||||
const genOptionWithId = (options: OptionType[] = []) => {
|
||||
return options.map((option: OptionType) => ({
|
||||
...option,
|
||||
id: genSelectOptionId(),
|
||||
}));
|
||||
};
|
||||
// Callback function for pendant create
|
||||
export const useOnCreateSure = ({ block }: { block: AsyncBlock }) => {
|
||||
const { addProperty } = useRecastBlockMeta();
|
||||
const { createSelect } = useSelectProperty();
|
||||
const { setPendant } = usePendant(block);
|
||||
|
||||
return async ({
|
||||
type,
|
||||
fieldName,
|
||||
newPropertyItem,
|
||||
newValue,
|
||||
}: SureParams) => {
|
||||
const checkResult = checkPendantForm(
|
||||
type,
|
||||
fieldName,
|
||||
newPropertyItem,
|
||||
newValue
|
||||
);
|
||||
|
||||
if (!checkResult.passed) {
|
||||
await message.error(checkResult.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
type === PendantTypes.MultiSelect ||
|
||||
type === PendantTypes.Select ||
|
||||
type === PendantTypes.Status
|
||||
) {
|
||||
const newProperty = await createSelect({
|
||||
name: fieldName,
|
||||
options: newPropertyItem,
|
||||
type,
|
||||
});
|
||||
|
||||
const selectedId = getOfficialSelected({
|
||||
isMulti: type === PendantTypes.MultiSelect,
|
||||
options: newProperty.options,
|
||||
tempOptions: newPropertyItem,
|
||||
tempSelectedId: newValue,
|
||||
});
|
||||
|
||||
await setPendant(newProperty, selectedId);
|
||||
} else if (type === PendantTypes.Information) {
|
||||
const emailOptions = genOptionWithId(newPropertyItem.emailOptions);
|
||||
|
||||
const phoneOptions = genOptionWithId(newPropertyItem.phoneOptions);
|
||||
|
||||
const locationOptions = genOptionWithId(
|
||||
newPropertyItem.locationOptions
|
||||
);
|
||||
|
||||
const newProperty = await addProperty({
|
||||
type,
|
||||
name: fieldName,
|
||||
emailOptions,
|
||||
phoneOptions,
|
||||
locationOptions,
|
||||
} as Omit<InformationProperty, 'id'>);
|
||||
|
||||
await setPendant(newProperty, {
|
||||
email: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: emailOptions,
|
||||
tempOptions: newPropertyItem.emailOptions,
|
||||
tempSelectedId: newValue.email,
|
||||
}),
|
||||
phone: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: phoneOptions,
|
||||
tempOptions: newPropertyItem.phoneOptions,
|
||||
tempSelectedId: newValue.phone,
|
||||
}),
|
||||
location: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: locationOptions,
|
||||
tempOptions: newPropertyItem.locationOptions,
|
||||
tempSelectedId: newValue.location,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
// TODO: Color and background should use pendant config, but ui is not design now
|
||||
const iconConfig = getPendantConfigByType(type);
|
||||
// TODO: Color and background should be choose by user in the future
|
||||
const newProperty = await addProperty({
|
||||
type: type,
|
||||
name: fieldName,
|
||||
background:
|
||||
iconConfig.background as CSSProperties['background'],
|
||||
color: iconConfig.color as CSSProperties['color'],
|
||||
iconName: iconConfig.iconName,
|
||||
});
|
||||
|
||||
await setPendant(newProperty, newValue);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Callback function for pendant update
|
||||
export const useOnUpdateSure = ({
|
||||
block,
|
||||
property,
|
||||
}: {
|
||||
block: AsyncBlock;
|
||||
property: RecastMetaProperty;
|
||||
}) => {
|
||||
const { updateSelect } = useSelectProperty();
|
||||
const { setPendant } = usePendant(block);
|
||||
const { updateProperty } = useRecastBlockMeta();
|
||||
|
||||
return async ({
|
||||
type,
|
||||
fieldName,
|
||||
newPropertyItem,
|
||||
newValue,
|
||||
}: SureParams) => {
|
||||
const checkResult = checkPendantForm(
|
||||
type,
|
||||
fieldName,
|
||||
newPropertyItem,
|
||||
newValue
|
||||
);
|
||||
|
||||
if (!checkResult.passed) {
|
||||
await message.error(checkResult.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
type === PendantTypes.MultiSelect ||
|
||||
type === PendantTypes.Select ||
|
||||
type === PendantTypes.Status
|
||||
) {
|
||||
const newOptions = newPropertyItem as OptionType[];
|
||||
let selectProperty = property as SelectPropertyType;
|
||||
const deleteOptionIds = selectProperty.options
|
||||
.filter(o => {
|
||||
return !newOptions.find(no => no.id === o.id);
|
||||
})
|
||||
.map(o => o.id);
|
||||
const addOptions = newOptions.filter(o => typeof o.id === 'number');
|
||||
|
||||
const { addSelectOptions, removeSelectOptions } =
|
||||
updateSelect(selectProperty);
|
||||
|
||||
deleteOptionIds.length &&
|
||||
(selectProperty = (await removeSelectOptions(
|
||||
...deleteOptionIds
|
||||
)) as SelectPropertyType);
|
||||
|
||||
addOptions.length &&
|
||||
(selectProperty = (await addSelectOptions(
|
||||
...(addOptions as unknown as Omit<SelectOption, 'id'>[])
|
||||
)) as SelectPropertyType);
|
||||
|
||||
const selectedId = getOfficialSelected({
|
||||
isMulti: type === PendantTypes.MultiSelect,
|
||||
options: selectProperty.options,
|
||||
tempOptions: newPropertyItem,
|
||||
tempSelectedId: newValue,
|
||||
});
|
||||
|
||||
await setPendant(selectProperty, selectedId);
|
||||
} else if (type === PendantTypes.Information) {
|
||||
// const { emailOptions, phoneOptions, locationOptions } =
|
||||
// property as InformationProperty;
|
||||
const optionGroup = newPropertyItem as TempInformationType;
|
||||
|
||||
const emailOptions = optionGroup.emailOptions.map(option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
});
|
||||
const phoneOptions = optionGroup.phoneOptions.map(option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
});
|
||||
const locationOptions = optionGroup.locationOptions.map(option => {
|
||||
if (typeof option.id === 'number') {
|
||||
option.id = genSelectOptionId();
|
||||
}
|
||||
return option;
|
||||
});
|
||||
|
||||
const newProperty = await updateProperty({
|
||||
...property,
|
||||
emailOptions,
|
||||
phoneOptions,
|
||||
locationOptions,
|
||||
} as InformationProperty);
|
||||
|
||||
await setPendant(newProperty, {
|
||||
email: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: emailOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.emailOptions,
|
||||
tempSelectedId: newValue.email,
|
||||
}),
|
||||
phone: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: phoneOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.phoneOptions,
|
||||
tempSelectedId: newValue.phone,
|
||||
}),
|
||||
location: getOfficialSelected({
|
||||
isMulti: true,
|
||||
options: locationOptions as SelectOption[],
|
||||
tempOptions: newPropertyItem.locationOptions,
|
||||
tempSelectedId: newValue.location,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
await setPendant(property, newValue);
|
||||
}
|
||||
|
||||
if (fieldName !== property.name) {
|
||||
await updateProperty({
|
||||
...property,
|
||||
name: fieldName,
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
PropertyType,
|
||||
RecastBlockValue,
|
||||
RecastPropertyId,
|
||||
SelectOption,
|
||||
@ -175,3 +176,49 @@ export const genInitialOptions = (
|
||||
}
|
||||
return [genBasicOption({ index: 0, iconConfig })];
|
||||
};
|
||||
|
||||
export const checkPendantForm = (
|
||||
type: PropertyType,
|
||||
fieldTitle: string,
|
||||
newProperty: any,
|
||||
newValue: any
|
||||
): { passed: boolean; message: string } => {
|
||||
if (!fieldTitle) {
|
||||
return { passed: false, message: 'The field title cannot be empty !' };
|
||||
}
|
||||
|
||||
if (
|
||||
type === PendantTypes.MultiSelect ||
|
||||
type === PendantTypes.Select ||
|
||||
type === PendantTypes.Status
|
||||
) {
|
||||
if (!newProperty) {
|
||||
return {
|
||||
passed: false,
|
||||
message: 'Ensure at least one non-empty option !',
|
||||
};
|
||||
}
|
||||
}
|
||||
if (type === PendantTypes.Information) {
|
||||
if (!newProperty) {
|
||||
return {
|
||||
passed: false,
|
||||
message: 'Ensure at least one non-empty option !',
|
||||
};
|
||||
}
|
||||
}
|
||||
if (
|
||||
type === PendantTypes.Text ||
|
||||
type === PendantTypes.Date ||
|
||||
type === PendantTypes.Mention
|
||||
) {
|
||||
if (!newValue) {
|
||||
return {
|
||||
passed: false,
|
||||
message: `The content of the input must not be empty !`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { passed: true, message: 'Check passed !' };
|
||||
};
|
||||
|
@ -297,10 +297,10 @@ export class ScrollManager {
|
||||
}
|
||||
|
||||
public lock() {
|
||||
this._scrollController.lockScroll();
|
||||
this._scrollController?.lockScroll();
|
||||
}
|
||||
|
||||
public unLock() {
|
||||
this._scrollController.unLockScroll();
|
||||
this._scrollController?.unLockScroll();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import type { DragDropManager } from './drag-drop';
|
||||
import { MouseManager } from './mouse';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Point } from '@toeverything/utils';
|
||||
import { ScrollManager } from './scroll';
|
||||
|
||||
// import { BrowserClipboard } from './clipboard/browser-clipboard';
|
||||
|
||||
@ -63,6 +64,7 @@ export interface VirgoSelection {
|
||||
// Editor's external API
|
||||
export interface Virgo {
|
||||
selectionManager: SelectionManager;
|
||||
scrollManager: ScrollManager;
|
||||
createBlock: (
|
||||
type: keyof BlockFlavors,
|
||||
parentId?: string
|
||||
|
@ -40,26 +40,30 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
bottom: 0,
|
||||
});
|
||||
|
||||
const [search_text, set_search_text] = useState<string>('');
|
||||
const [search_blocks, set_search_blocks] = useState<QueryResult>([]);
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [searchBlocks, setSearchBlocks] = useState<QueryResult>([]);
|
||||
const commandMenuContentRef = useRef();
|
||||
// TODO: Two-way link to be developed
|
||||
// useEffect(() => {
|
||||
// QueryBlocks(editor, search_text, result => set_search_blocks(result));
|
||||
// }, [editor, search_text]);
|
||||
// QueryBlocks(editor, searchText, result => set_searchBlocks(result));
|
||||
// }, [editor, searchText]);
|
||||
|
||||
const hideMenu = () => {
|
||||
setShow(false);
|
||||
editor.scrollManager.unLock();
|
||||
};
|
||||
const [types, categories] = useMemo(() => {
|
||||
const types: Array<BlockFlavorKeys | string> = [];
|
||||
const categories: Array<CommandMenuCategories> = [];
|
||||
if (search_blocks.length) {
|
||||
Object.values(search_blocks).forEach(({ id }) => types.push(id));
|
||||
if (searchBlocks.length) {
|
||||
Object.values(searchBlocks).forEach(({ id }) => types.push(id));
|
||||
categories.push(CommandMenuCategories.pages);
|
||||
}
|
||||
Object.entries(menuItemsMap).forEach(([category, itemInfoList]) => {
|
||||
itemInfoList.forEach(info => {
|
||||
if (
|
||||
!search_text ||
|
||||
info.text.toLowerCase().includes(search_text.toLowerCase())
|
||||
!searchText ||
|
||||
info.text.toLowerCase().includes(searchText.toLowerCase())
|
||||
) {
|
||||
types.push(info.type);
|
||||
}
|
||||
@ -73,9 +77,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
});
|
||||
});
|
||||
return [types, categories];
|
||||
}, [search_blocks, search_text]);
|
||||
}, [searchBlocks, searchText]);
|
||||
|
||||
const check_if_show_command_menu = useCallback(
|
||||
const checkIfShowCommandMenu = useCallback(
|
||||
async (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
const { type, anchorNode } = editor.selection.currentSelectInfo;
|
||||
if (event.key === '/' && type === 'Range') {
|
||||
@ -101,8 +105,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
set_search_text('');
|
||||
setSearchText('');
|
||||
setShow(true);
|
||||
editor.scrollManager.lock();
|
||||
const rect =
|
||||
editor.selection.currentSelectInfo?.browserSelection
|
||||
?.getRangeAt(0)
|
||||
@ -137,12 +142,12 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
[editor, blockId]
|
||||
);
|
||||
|
||||
const handle_click_others = useCallback(
|
||||
const handleClickOthers = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (show) {
|
||||
const { anchorNode } = editor.selection.currentSelectInfo;
|
||||
if (anchorNode.id !== blockId) {
|
||||
setShow(false);
|
||||
hideMenu();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
@ -150,12 +155,12 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
editor.blockHelper.getSearchSlashText(blockId);
|
||||
// check if has search text
|
||||
if (searchText && searchText.startsWith('/')) {
|
||||
set_search_text(searchText.slice(1));
|
||||
setSearchText(searchText.slice(1));
|
||||
} else {
|
||||
setShow(false);
|
||||
hideMenu();
|
||||
}
|
||||
if (searchText.length > 6 && !types.length) {
|
||||
setShow(false);
|
||||
hideMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -163,18 +168,18 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
[editor, show, blockId, types]
|
||||
);
|
||||
|
||||
const handle_keyup = useCallback(
|
||||
const handleKeyup = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
check_if_show_command_menu(event);
|
||||
handle_click_others(event);
|
||||
checkIfShowCommandMenu(event);
|
||||
handleClickOthers(event);
|
||||
},
|
||||
[check_if_show_command_menu, handle_click_others]
|
||||
[checkIfShowCommandMenu, handleClickOthers]
|
||||
);
|
||||
|
||||
const handle_key_down = useCallback(
|
||||
const handleKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (event.code === 'Escape') {
|
||||
setShow(false);
|
||||
hideMenu();
|
||||
}
|
||||
},
|
||||
[]
|
||||
@ -183,23 +188,23 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
useEffect(() => {
|
||||
const sub = hooks
|
||||
.get(HookType.ON_ROOT_NODE_KEYUP)
|
||||
.subscribe(handle_keyup);
|
||||
.subscribe(handleKeyup);
|
||||
sub.add(
|
||||
hooks
|
||||
.get(HookType.ON_ROOT_NODE_KEYDOWN_CAPTURE)
|
||||
.subscribe(handle_key_down)
|
||||
.subscribe(handleKeyDown)
|
||||
);
|
||||
|
||||
return () => {
|
||||
sub.unsubscribe();
|
||||
};
|
||||
}, [handle_keyup, handle_key_down, hooks]);
|
||||
}, [handleKeyup, handleKeyDown, hooks]);
|
||||
|
||||
const handle_click_away = () => {
|
||||
setShow(false);
|
||||
const handleClickAway = () => {
|
||||
hideMenu();
|
||||
};
|
||||
|
||||
const handle_selected = async (type: BlockFlavorKeys | string) => {
|
||||
const handleSelected = async (type: BlockFlavorKeys | string) => {
|
||||
const text = await editor.commands.textCommands.getBlockText(blockId);
|
||||
editor.blockHelper.removeSearchSlash(blockId, true);
|
||||
if (type.startsWith('Virgo')) {
|
||||
@ -224,21 +229,21 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
block.firstCreateFlag = true;
|
||||
}
|
||||
}
|
||||
setShow(false);
|
||||
hideMenu();
|
||||
};
|
||||
|
||||
const handle_close = () => {
|
||||
const handleClose = () => {
|
||||
editor.blockHelper.removeSearchSlash(blockId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ zIndex: 1 }}
|
||||
onKeyUpCapture={handle_keyup}
|
||||
onKeyUpCapture={handleKeyup}
|
||||
ref={commandMenuContentRef}
|
||||
>
|
||||
{show ? (
|
||||
<MuiClickAwayListener onClickAway={handle_click_away}>
|
||||
<MuiClickAwayListener onClickAway={handleClickAway}>
|
||||
<div>
|
||||
<CommandMenuContainer
|
||||
editor={editor}
|
||||
@ -249,9 +254,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
|
||||
}}
|
||||
isShow={show}
|
||||
blockId={blockId}
|
||||
onSelected={handle_selected}
|
||||
onclose={handle_close}
|
||||
searchBlocks={search_blocks}
|
||||
onSelected={handleSelected}
|
||||
onclose={handleClose}
|
||||
searchBlocks={searchBlocks}
|
||||
types={types}
|
||||
categories={categories}
|
||||
/>
|
||||
|
1
libs/components/icons/src/auto-icons/add/add.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M11.04 12.96V18h1.92v-5.04H18v-1.92h-5.04V6h-1.92v5.04H6v1.92h5.04Z" clip-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 185 B |
21
libs/components/icons/src/auto-icons/add/add.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
import { FC } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { SvgIcon } from '@mui/material';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { SvgIconProps } from '@mui/material';
|
||||
|
||||
export interface AddIconProps extends Omit<SvgIconProps, 'color'> {
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const AddIcon: FC<AddIconProps> = ({ color, style, ...props}) => {
|
||||
const propsStyles = {"color": color};
|
||||
const customStyles = {};
|
||||
const styles = {...propsStyles, ...customStyles, ...style}
|
||||
return (
|
||||
<SvgIcon style={styles} {...props}>
|
||||
<path fillRule="evenodd" d="M11.04 12.96V18h1.92v-5.04H18v-1.92h-5.04V6h-1.92v5.04H6v1.92h5.04Z" clipRule="evenodd" />
|
||||
</SvgIcon>
|
||||
)
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
export const timestamp = 1659000896562;
|
||||
export const timestamp = 1659423582387;
|
||||
export * from './image/image';
|
||||
export * from './format-clear/format-clear';
|
||||
export * from './backward-undo/backward-undo';
|
||||
@ -125,4 +125,8 @@ export * from './eraser/eraser';
|
||||
export * from './group-by/group-by';
|
||||
export * from './layout/layout';
|
||||
export * from './lock/lock';
|
||||
export * from './unlock/unlock';
|
||||
export * from './unlock/unlock';
|
||||
export * from './more-tags-an-subblocks/more-tags-an-subblocks';
|
||||
export * from './page-in-page-tree/page-in-page-tree';
|
||||
export * from './pin/pin';
|
||||
export * from './add/add';
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M2.2 3v13A3.8 3.8 0 0 0 6 19.8h10.316A2.801 2.801 0 0 0 21.8 19a2.8 2.8 0 0 0-5.484-.8H6A2.2 2.2 0 0 1 3.8 16v-6h12.584a2.801 2.801 0 1 0-.12-1.6H3.8V3H2.2ZM19 7.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4ZM17.8 19a1.2 1.2 0 1 1 2.4 0 1.2 1.2 0 0 1-2.4 0Z" clip-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 371 B |
@ -0,0 +1,21 @@
|
||||
|
||||
import { FC } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { SvgIcon } from '@mui/material';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { SvgIconProps } from '@mui/material';
|
||||
|
||||
export interface MoreTagsAnSubblocksIconProps extends Omit<SvgIconProps, 'color'> {
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const MoreTagsAnSubblocksIcon: FC<MoreTagsAnSubblocksIconProps> = ({ color, style, ...props}) => {
|
||||
const propsStyles = {"color": color};
|
||||
const customStyles = {};
|
||||
const styles = {...propsStyles, ...customStyles, ...style}
|
||||
return (
|
||||
<SvgIcon style={styles} {...props}>
|
||||
<path fillRule="evenodd" d="M2.2 3v13A3.8 3.8 0 0 0 6 19.8h10.316A2.801 2.801 0 0 0 21.8 19a2.8 2.8 0 0 0-5.484-.8H6A2.2 2.2 0 0 1 3.8 16v-6h12.584a2.801 2.801 0 1 0-.12-1.6H3.8V3H2.2ZM19 7.8a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4ZM17.8 19a1.2 1.2 0 1 1 2.4 0 1.2 1.2 0 0 1-2.4 0Z" clipRule="evenodd" />
|
||||
</SvgIcon>
|
||||
)
|
||||
};
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="2.5"/></svg>
|
After Width: | Height: | Size: 99 B |
@ -0,0 +1,21 @@
|
||||
|
||||
import { FC } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { SvgIcon } from '@mui/material';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { SvgIconProps } from '@mui/material';
|
||||
|
||||
export interface PageInPageTreeIconProps extends Omit<SvgIconProps, 'color'> {
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const PageInPageTreeIcon: FC<PageInPageTreeIconProps> = ({ color, style, ...props}) => {
|
||||
const propsStyles = {"color": color};
|
||||
const customStyles = {};
|
||||
const styles = {...propsStyles, ...customStyles, ...style}
|
||||
return (
|
||||
<SvgIcon style={styles} {...props}>
|
||||
<circle cx={12} cy={12} r={2.5} />
|
||||
</SvgIcon>
|
||||
)
|
||||
};
|
1
libs/components/icons/src/auto-icons/pin/pin.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g clip-path="url(#a)"><path fill-rule="evenodd" d="M20.403 7.84a2 2 0 0 1 0 2.828l-2.12 2.12-1.414-1.414-2.596 2.595a6.003 6.003 0 0 1-1.172 6.83l-4.243-4.243-3.834 3.834a1 1 0 1 1-1.414-1.414l3.834-3.834L3.2 10.9a6.002 6.002 0 0 1 6.83-1.173l2.595-2.596-1.414-1.414 2.12-2.12a2 2 0 0 1 2.829 0l4.242 4.242Z" clip-rule="evenodd"/></g><defs><clipPath id="a"><path d="M0 0H24V24H0z" transform="matrix(-1 0 0 1 24 0)"/></clipPath></defs></svg>
|
After Width: | Height: | Size: 501 B |
21
libs/components/icons/src/auto-icons/pin/pin.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
import { FC } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { SvgIcon } from '@mui/material';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import type { SvgIconProps } from '@mui/material';
|
||||
|
||||
export interface PinIconProps extends Omit<SvgIconProps, 'color'> {
|
||||
color?: string
|
||||
}
|
||||
|
||||
export const PinIcon: FC<PinIconProps> = ({ color, style, ...props}) => {
|
||||
const propsStyles = {"color": color};
|
||||
const customStyles = {};
|
||||
const styles = {...propsStyles, ...customStyles, ...style}
|
||||
return (
|
||||
<SvgIcon style={styles} {...props}>
|
||||
<g clipPath="url(#a)"><path fillRule="evenodd" d="M20.403 7.84a2 2 0 0 1 0 2.828l-2.12 2.12-1.414-1.414-2.596 2.595a6.003 6.003 0 0 1-1.172 6.83l-4.243-4.243-3.834 3.834a1 1 0 1 1-1.414-1.414l3.834-3.834L3.2 10.9a6.002 6.002 0 0 1 6.83-1.173l2.595-2.596-1.414-1.414 2.12-2.12a2 2 0 0 1 2.829 0l4.242 4.242Z" clipRule="evenodd" /></g><defs><clipPath id="a"><path d="M0 0H24V24H0z" transform="matrix(-1 0 0 1 24 0)" /></clipPath></defs>
|
||||
</SvgIcon>
|
||||
)
|
||||
};
|
@ -3,13 +3,14 @@ import {
|
||||
LogoIcon,
|
||||
SideBarViewIcon,
|
||||
SearchIcon,
|
||||
SideBarViewCloseIcon,
|
||||
} from '@toeverything/components/icons';
|
||||
import { useShowSettingsSidebar } from '@toeverything/datasource/state';
|
||||
import { CurrentPageTitle } from './Title';
|
||||
import { EditorBoardSwitcher } from './EditorBoardSwitcher';
|
||||
|
||||
export const LayoutHeader = () => {
|
||||
const { toggleSettingsSidebar: toggleInfoSidebar } =
|
||||
const { toggleSettingsSidebar: toggleInfoSidebar, showSettingsSidebar } =
|
||||
useShowSettingsSidebar();
|
||||
|
||||
return (
|
||||
@ -31,7 +32,11 @@ export const LayoutHeader = () => {
|
||||
</div>
|
||||
|
||||
<IconButton onClick={toggleInfoSidebar} size="large">
|
||||
<SideBarViewIcon />
|
||||
{showSettingsSidebar ? (
|
||||
<SideBarViewIcon />
|
||||
) : (
|
||||
<SideBarViewCloseIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</StyledHelper>
|
||||
</FlexContainer>
|
||||
|
@ -50,31 +50,35 @@ export const Activities = () => {
|
||||
const [recentPages, setRecentPages] = useState([]);
|
||||
const userId = user?.id;
|
||||
|
||||
const fetchRecentPages = useCallback(async () => {
|
||||
if (!userId || !currentSpaceId) {
|
||||
return;
|
||||
}
|
||||
const recent_pages = await services.api.userConfig.getRecentPages(
|
||||
currentSpaceId,
|
||||
userId
|
||||
);
|
||||
setRecentPages(recent_pages);
|
||||
}, [userId, currentSpaceId]);
|
||||
/* temporarily remove:show recently viewed documents */
|
||||
// const fetchRecentPages = useCallback(async () => {
|
||||
// if (!userId || !currentSpaceId) {
|
||||
// return;
|
||||
// }
|
||||
// const recent_pages = await services.api.userConfig.getRecentPages(
|
||||
// currentSpaceId,
|
||||
// userId
|
||||
// );
|
||||
// setRecentPages(recent_pages);
|
||||
// }, [userId, currentSpaceId]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await fetchRecentPages();
|
||||
})();
|
||||
}, [fetchRecentPages]);
|
||||
// useEffect(() => {
|
||||
// (async () => {
|
||||
// await fetchRecentPages();
|
||||
// })();
|
||||
// }, [fetchRecentPages]);
|
||||
|
||||
/* show recently edit documents */
|
||||
const getRecentEditPages = async (state, block) => {
|
||||
console.log(state, await block.children());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let unobserve: () => void;
|
||||
const observe = async () => {
|
||||
unobserve = await services.api.userConfig.observe(
|
||||
{ workspace: currentSpaceId },
|
||||
() => {
|
||||
fetchRecentPages();
|
||||
}
|
||||
getRecentEditPages
|
||||
);
|
||||
};
|
||||
observe();
|
||||
@ -82,7 +86,7 @@ export const Activities = () => {
|
||||
return () => {
|
||||
unobserve?.();
|
||||
};
|
||||
}, [currentSpaceId, fetchRecentPages]);
|
||||
}, [currentSpaceId, getRecentEditPages]);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
|
@ -95,30 +95,37 @@ export function DndTree(props: DndTreeProps) {
|
||||
>
|
||||
{/* <button onClick={() => handleAdd()}> add top node</button> */}
|
||||
{flattenedItems.map(
|
||||
({ id, title, children, collapsed, depth }) => (
|
||||
<DndTreeItem
|
||||
key={id}
|
||||
id={id}
|
||||
// value={id}
|
||||
value={title}
|
||||
collapsed={Boolean(collapsed && children.length)}
|
||||
depth={
|
||||
id === activeId && projected
|
||||
? projected.depth
|
||||
: depth
|
||||
}
|
||||
indentationWidth={indentationWidth}
|
||||
indicator={showDragIndicator}
|
||||
onCollapse={
|
||||
collapsible && children.length
|
||||
? () => handleCollapse(id)
|
||||
: undefined
|
||||
}
|
||||
onRemove={
|
||||
removable ? () => handleRemove(id) : undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
({ id, title, children, collapsed, depth }) => {
|
||||
return (
|
||||
<DndTreeItem
|
||||
key={id}
|
||||
id={id}
|
||||
// value={id}
|
||||
value={title}
|
||||
collapsed={Boolean(
|
||||
collapsed && children.length
|
||||
)}
|
||||
depth={
|
||||
id === activeId && projected
|
||||
? projected.depth
|
||||
: depth
|
||||
}
|
||||
indentationWidth={indentationWidth}
|
||||
indicator={showDragIndicator}
|
||||
childCount={children.length}
|
||||
onCollapse={
|
||||
collapsible && children.length
|
||||
? () => handleCollapse(id)
|
||||
: undefined
|
||||
}
|
||||
onRemove={
|
||||
removable
|
||||
? () => handleRemove(id)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
)}
|
||||
<DragOverlay
|
||||
dropAnimation={dropAnimation}
|
||||
|
@ -4,6 +4,9 @@ import {
|
||||
Cascader,
|
||||
CascaderItemProps,
|
||||
MuiDivider as Divider,
|
||||
MuiClickAwayListener as ClickAwayListener,
|
||||
IconButton,
|
||||
styled,
|
||||
} from '@toeverything/components/ui';
|
||||
import React from 'react';
|
||||
import { NavLink, useNavigate } from 'react-router-dom';
|
||||
@ -11,6 +14,8 @@ import { copyToClipboard } from '@toeverything/utils';
|
||||
import { services, TemplateFactory } from '@toeverything/datasource/db-service';
|
||||
import { NewFromTemplatePortal } from './NewFromTemplatePortal';
|
||||
import { useFlag } from '@toeverything/datasource/feature-flags';
|
||||
import { MoreIcon, AddIcon } from '@toeverything/components/icons';
|
||||
|
||||
const MESSAGES = {
|
||||
COPY_LINK_SUCCESS: 'Copyed link to clipboard',
|
||||
};
|
||||
@ -19,6 +24,12 @@ interface ActionsProps {
|
||||
pageId: string;
|
||||
onRemove: () => void;
|
||||
}
|
||||
|
||||
const StyledAction = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
function DndTreeItemMoreActions(props: ActionsProps) {
|
||||
const [alert_open, set_alert_open] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLDivElement | null>(null);
|
||||
@ -233,29 +244,42 @@ function DndTreeItemMoreActions(props: ActionsProps) {
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className={styles['TreeItemMoreActions']}
|
||||
onClick={handleClick}
|
||||
>
|
||||
···
|
||||
</span>
|
||||
<Cascader
|
||||
items={menuList}
|
||||
anchorEl={anchorEl}
|
||||
placement="right-start"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
></Cascader>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={alert_open}
|
||||
message={MESSAGES.COPY_LINK_SUCCESS}
|
||||
key={'bottomcenter'}
|
||||
autoHideDuration={2000}
|
||||
onClose={handle_alert_close}
|
||||
/>
|
||||
</>
|
||||
<ClickAwayListener onClickAway={() => handleClose()}>
|
||||
<div>
|
||||
<div className={styles['TreeItemMoreActions']}>
|
||||
<StyledAction>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={handle_new_child_page}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
hoverColor="#E0E6EB"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
</StyledAction>
|
||||
</div>
|
||||
<Cascader
|
||||
items={menuList}
|
||||
anchorEl={anchorEl}
|
||||
placement="right-start"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
></Cascader>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={alert_open}
|
||||
message={MESSAGES.COPY_LINK_SUCCESS}
|
||||
key={'bottomcenter'}
|
||||
autoHideDuration={2000}
|
||||
onClose={handle_alert_close}
|
||||
/>
|
||||
</div>
|
||||
</ClickAwayListener>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
||||
const navigate = useNavigate();
|
||||
const BooleanPageTreeItemMoreActions = useFlag(
|
||||
'BooleanPageTreeItemMoreActions',
|
||||
false
|
||||
true
|
||||
);
|
||||
return (
|
||||
<li
|
||||
@ -94,7 +94,12 @@ export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
||||
title={value}
|
||||
>
|
||||
<Action onClick={onCollapse}>
|
||||
{collapsed ? <ArrowRightIcon /> : <ArrowDropDownIcon />}
|
||||
{childCount !== 0 &&
|
||||
(collapsed ? (
|
||||
<ArrowRightIcon />
|
||||
) : (
|
||||
<ArrowDropDownIcon />
|
||||
))}
|
||||
</Action>
|
||||
{/*<Action>*/}
|
||||
{/* <DocumentIcon />*/}
|
||||
|
@ -83,7 +83,7 @@
|
||||
justify-content: space-around;
|
||||
background-color: #fff;
|
||||
color: #4c6275;
|
||||
padding: 0 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
overflow: hidden;
|
||||
|
||||
.TreeItemMoreActions {
|
||||
|
@ -36,6 +36,7 @@ interface IconButtonProps {
|
||||
style?: CSSProperties;
|
||||
className?: string;
|
||||
size?: SizeType;
|
||||
hoverColor?: string;
|
||||
}
|
||||
|
||||
export const IconButton: FC<PropsWithChildren<IconButtonProps>> = ({
|
||||
@ -57,47 +58,48 @@ export const IconButton: FC<PropsWithChildren<IconButtonProps>> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const Container = styled('button')<{ size?: SizeType }>(
|
||||
({ theme, size = SIZE_MIDDLE }) => {
|
||||
const { iconSize, areaSize } = SIZE_CONFIG[size];
|
||||
const Container = styled('button')<{
|
||||
size?: SizeType;
|
||||
hoverColor?: string;
|
||||
}>(({ theme, size = SIZE_MIDDLE, hoverColor }) => {
|
||||
const { iconSize, areaSize } = SIZE_CONFIG[size];
|
||||
|
||||
return {
|
||||
return {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: areaSize,
|
||||
height: areaSize,
|
||||
backgroundColor: 'transparent',
|
||||
color: theme.affine.palette.icons,
|
||||
padding: theme.affine.spacing.iconPadding,
|
||||
borderRadius: '3px',
|
||||
|
||||
'& svg': {
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: areaSize,
|
||||
height: areaSize,
|
||||
backgroundColor: theme.affine.palette.white,
|
||||
color: theme.affine.palette.icons,
|
||||
padding: theme.affine.spacing.iconPadding,
|
||||
borderRadius: '5px',
|
||||
},
|
||||
|
||||
'& svg': {
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: hoverColor || theme.affine.palette.hover,
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.affine.palette.hover,
|
||||
},
|
||||
[`&${buttonStatus.hover}`]: {
|
||||
backgroundColor: theme.affine.palette.hover,
|
||||
},
|
||||
|
||||
[`&${buttonStatus.hover}`]: {
|
||||
backgroundColor: theme.affine.palette.hover,
|
||||
},
|
||||
'&:focus': {
|
||||
color: theme.affine.palette.primary,
|
||||
},
|
||||
[`&.${buttonStatus.focus}`]: {
|
||||
color: theme.affine.palette.primary,
|
||||
},
|
||||
|
||||
'&:focus': {
|
||||
color: theme.affine.palette.primary,
|
||||
},
|
||||
[`&.${buttonStatus.focus}`]: {
|
||||
color: theme.affine.palette.primary,
|
||||
},
|
||||
|
||||
[`&${buttonStatus.disabled}`]: {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
[`&${buttonStatus.disabled}`]: {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -0,0 +1,237 @@
|
||||
{
|
||||
"type": "group",
|
||||
"properties": {},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "As a collaborative real-time editor, AFFiNE aims to resolve problems in three situations:"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "bullet",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Multi-master replication: the synchronization of data between equipment and applications;"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "bullet",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Eventual consistency: the consistence of data regardless of network latency and outage;"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "bullet",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Conflict resolution: the resolution of conflict between simultaneous edits."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "To achieve these aims, a proper collaborative algorithm should be used. There are hundreds of collaborative algorithms being invented over the past three decades, but they usually fall into two categories: either operational transformation (OT) or conflict-free replicated data type (CRDT). We think CRDT is a better choice."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading1",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "bold": true, "text": "What does CRDT do" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "CRDT is capable of discovering and resolving conflicts while ensuring the effective distribution and merge of date. It may sound like magic, but think about historical study, it is very possible to discover the truth from scattered evidence."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "For CRDT, every piece of data is like a \"historic fragment\". We keep collecting the fragments from other clients and then restore the truth by excluding repeated data and correcting false information."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading1",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "bold": true, "text": "Why CRDT is better" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "In contrast to OT, CRDT possesses three big advantages:"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "bold": true, "text": "Flexibility" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "CRDT supports more data types. For example, Yjs supports Array, Map, and Treelike, and therefore applies to more business scenarios."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "bold": true, "text": "Performance" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "CRDT tolerates higher latency and can wait longer for solving conflicts, whereas the calculation of OT in the same condition may become too overwhelming for a server to sustain."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "bold": true, "text": "Extensibility" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Because CRDT supports more data types and editor elements, it is more extensible."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading1",
|
||||
"properties": {
|
||||
"text": { "value": [{ "text": "Conclusion" }] }
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": { "text": { "value": [{ "text": "" }] } },
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{ "text": "" },
|
||||
{
|
||||
"type": "link",
|
||||
"url": "https://blog.affine.pro/",
|
||||
"id": "link.stubssslo0rq",
|
||||
"children": [{ "text": "← View all posts" }]
|
||||
},
|
||||
{ "text": "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "group",
|
||||
"properties": {},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "text": "" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
{
|
||||
"type": "group",
|
||||
"properties": {},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"bold": true,
|
||||
"text": "Performance"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "CRDT tolerates higher latency and can wait longer for solving conflicts, whereas the calculation of OT in the same condition may become too overwhelming for a server to sustain."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"bold": true,
|
||||
"text": "Extensibility"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Because CRDT supports more data types and editor elements, it is more extensible."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading1",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Conclusion"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "grid",
|
||||
"properties": {},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "gridItem",
|
||||
"properties": {
|
||||
"gridItemWidth": "50%"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "gridItem",
|
||||
"properties": {
|
||||
"gridItemWidth": "50%"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": ""
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "https://blog.affine.pro/",
|
||||
"id": "link.stubssslo0rq",
|
||||
"children": [
|
||||
{
|
||||
"text": "← View all posts"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,592 +0,0 @@
|
||||
//@ts-nocheck
|
||||
import { GroupTemplate } from './types';
|
||||
type GroupTemplateMap = Record<GroupTemplateKeys, GroupTemplate>;
|
||||
const groupTemplateMap: GroupTemplateMap = {
|
||||
empty: {
|
||||
type: 'group',
|
||||
properties: {},
|
||||
blocks: [
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ text: '' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
todolist: {
|
||||
type: 'group',
|
||||
properties: {},
|
||||
blocks: [
|
||||
{
|
||||
type: 'heading1',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ text: '🎓Graduating from the project' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Congratulations! Now you can start create your own projects!',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'To start using workspaces and folders hit the ',
|
||||
},
|
||||
{
|
||||
bold: true,
|
||||
text: 'button at the top left of the screen',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
{
|
||||
text: 'At any time if you feel lost do visit our 📚Blog: ',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'https://blog.affine.pro/',
|
||||
id: 'link.qx4yhw81or54',
|
||||
children: [
|
||||
{
|
||||
text: 'https://blog.affine.pro',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
collapsed: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
{
|
||||
text: 'If you have any suggestions drop a post in our Reddit Channel:',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'https://www.reddit.com/r/Affine/',
|
||||
id: 'link.zeafc4ogfvrb',
|
||||
children: [
|
||||
{
|
||||
text: 'https://www.reddit.com/r/Affine/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: ' ',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: '🎉 The Essentials. Check things off after you tried them!',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{ text: ' ✅ ' },
|
||||
{ bold: true, text: 'Check' },
|
||||
{
|
||||
text: ' the text box here to complete the task!',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{ text: '' },
|
||||
{ text: '' },
|
||||
{ text: ' 👋 ' },
|
||||
{ bold: true, text: 'Drag' },
|
||||
{
|
||||
text: ' the ⠟ button left of the checkbox to reorder tasks',
|
||||
},
|
||||
{ text: '' },
|
||||
{ text: '' },
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'todo',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{ text: '' },
|
||||
{ text: '' },
|
||||
{ text: ' ➡️ ' },
|
||||
{ bold: true, text: 'Fold' },
|
||||
{ text: ' and ' },
|
||||
{ bold: true, text: 'Unfold' },
|
||||
{
|
||||
text: ' a task to simplify your list using the arrow on the right ⤵️',
|
||||
},
|
||||
],
|
||||
},
|
||||
numberType: 'type1',
|
||||
collapsed: { value: false },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
blog: {
|
||||
type: 'group',
|
||||
properties: {},
|
||||
blocks: [
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'As a collaborative real-time editor, AFFiNE aims to resolve problems in three situations:',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'bullet',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Multi-master replication: the synchronization of data between equipment and applications;',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'bullet',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Eventual consistency: the consistence of data regardless of network latency and outage;',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'bullet',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Conflict resolution: the resolution of conflict between simultaneous edits.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'To achieve these aims, a proper collaborative algorithm should be used. There are hundreds of collaborative algorithms being invented over the past three decades, but they usually fall into two categories: either operational transformation (OT) or conflict-free replicated data type (CRDT). We think CRDT is a better choice.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading1',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ bold: true, text: 'What does CRDT do' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'CRDT is capable of discovering and resolving conflicts while ensuring the effective distribution and merge of date. It may sound like magic, but think about historical study, it is very possible to discover the truth from scattered evidence.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'For CRDT, every piece of data is like a "historic fragment". We keep collecting the fragments from other clients and then restore the truth by excluding repeated data and correcting false information.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading1',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ bold: true, text: 'Why CRDT is better' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'In contrast to OT, CRDT possesses three big advantages:',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ bold: true, text: 'Flexibility' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'CRDT supports more data types. For example, Yjs supports Array, Map, and Treelike, and therefore applies to more business scenarios.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ bold: true, text: 'Performance' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'CRDT tolerates higher latency and can wait longer for solving conflicts, whereas the calculation of OT in the same condition may become too overwhelming for a server to sustain.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [{ bold: true, text: 'Extensibility' }],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Because CRDT supports more data types and editor elements, it is more extensible.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading1',
|
||||
properties: {
|
||||
text: { value: [{ text: 'Conclusion' }] },
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: { text: { value: [{ text: '' }] } },
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{ text: '' },
|
||||
{
|
||||
type: 'link',
|
||||
url: 'https://blog.affine.pro/',
|
||||
id: 'link.stubssslo0rq',
|
||||
children: [{ text: '← View all posts' }],
|
||||
},
|
||||
{ text: '' },
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
grid: {
|
||||
type: 'group',
|
||||
properties: {},
|
||||
blocks: [
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
bold: true,
|
||||
text: 'Performance',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'CRDT tolerates higher latency and can wait longer for solving conflicts, whereas the calculation of OT in the same condition may become too overwhelming for a server to sustain.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading2',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
bold: true,
|
||||
text: 'Extensibility',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Because CRDT supports more data types and editor elements, it is more extensible.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'heading1',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Conclusion',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'grid',
|
||||
properties: {},
|
||||
blocks: [
|
||||
{
|
||||
type: 'gridItem',
|
||||
properties: {
|
||||
gridItemWidth: '50%',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'gridItem',
|
||||
properties: {
|
||||
gridItemWidth: '50%',
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: 'Collaborative algorithm is still a foreign concept for many developers. There are some introductions to it, but as to how it shall be used, there is still lack of clear explanation. I hope this article helps. If you also work for a startup company and want some suggestions, CRDT, especially Yjs, should be a better choice.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
blocks: [],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
properties: {
|
||||
text: {
|
||||
value: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
url: 'https://blog.affine.pro/',
|
||||
id: 'link.stubssslo0rq',
|
||||
children: [
|
||||
{
|
||||
text: '← View all posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export type GroupTemplateKeys = 'todolist' | 'blog' | 'empty' | 'grid';
|
||||
export { groupTemplateMap };
|
@ -1,6 +1,19 @@
|
||||
import { groupTemplateMap } from './group-templates';
|
||||
import { Template, TemplateMeta } from './types';
|
||||
const defaultTemplateList: Array<TemplateMeta> = [
|
||||
import { Template, TemplateMeta, GroupTemplate } from './types';
|
||||
import blogTemplate from './blog.json';
|
||||
import emptyTemplate from './empty.json';
|
||||
import gridTemplate from './grid.json';
|
||||
import todoTemplate from './todo.json';
|
||||
|
||||
export type GroupTemplateKeys = 'todolist' | 'blog' | 'empty' | 'grid';
|
||||
type GroupTemplateMap = Record<GroupTemplateKeys, GroupTemplate>;
|
||||
const groupTemplateMap = {
|
||||
empty: emptyTemplate,
|
||||
todolist: todoTemplate,
|
||||
blog: blogTemplate,
|
||||
grid: gridTemplate,
|
||||
} as GroupTemplateMap;
|
||||
|
||||
const defaultTemplateList = [
|
||||
{
|
||||
name: 'New From Quick Start',
|
||||
groupKeys: ['todolist'],
|
||||
@ -9,12 +22,13 @@ const defaultTemplateList: Array<TemplateMeta> = [
|
||||
{ name: 'New From Blog', groupKeys: ['blog'] },
|
||||
{ name: ' New Todolist', groupKeys: ['todolist'] },
|
||||
{ name: ' New Empty Page', groupKeys: ['empty'] },
|
||||
];
|
||||
] as const;
|
||||
|
||||
const TemplateFactory = {
|
||||
defaultTemplateList: defaultTemplateList,
|
||||
generatePageTemplateByGroupKeys(props: TemplateMeta): Template {
|
||||
const newTitle = props.name || 'Get Started with AFFiNE';
|
||||
const keys = props.groupKeys || [];
|
||||
const keys: GroupTemplateKeys[] = props.groupKeys || [];
|
||||
const blankPage: Template = {
|
||||
type: 'page',
|
||||
properties: {
|
||||
|
@ -0,0 +1,173 @@
|
||||
{
|
||||
"type": "group",
|
||||
"properties": {},
|
||||
"blocks": [
|
||||
{
|
||||
"type": "heading1",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [{ "text": "🎓Graduating from the project" }]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "Congratulations! Now you can start create your own projects!"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "To start using workspaces and folders hit the "
|
||||
},
|
||||
{
|
||||
"bold": true,
|
||||
"text": "button at the top left of the screen"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": ""
|
||||
},
|
||||
{
|
||||
"text": "At any time if you feel lost do visit our 📚Blog: "
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "https://blog.affine.pro/",
|
||||
"id": "link.qx4yhw81or54",
|
||||
"children": [
|
||||
{
|
||||
"text": "https://blog.affine.pro"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"collapsed": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": ""
|
||||
},
|
||||
{
|
||||
"text": "If you have any suggestions drop a post in our Reddit Channel:"
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"url": "https://www.reddit.com/r/Affine/",
|
||||
"id": "link.zeafc4ogfvrb",
|
||||
"children": [
|
||||
{
|
||||
"text": "https://www.reddit.com/r/Affine/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": " "
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "heading2",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{
|
||||
"text": "🎉 The Essentials. Check things off after you tried them!"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{ "text": " ✅ " },
|
||||
{ "bold": true, "text": "Check" },
|
||||
{
|
||||
"text": " the text box here to complete the task!"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{ "text": "" },
|
||||
{ "text": "" },
|
||||
{ "text": " 👋 " },
|
||||
{ "bold": true, "text": "Drag" },
|
||||
{
|
||||
"text": " the ⠟ button left of the checkbox to reorder tasks"
|
||||
},
|
||||
{ "text": "" },
|
||||
{ "text": "" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"blocks": []
|
||||
},
|
||||
{
|
||||
"type": "todo",
|
||||
"properties": {
|
||||
"text": {
|
||||
"value": [
|
||||
{ "text": "" },
|
||||
{ "text": "" },
|
||||
{ "text": " ➡️ " },
|
||||
{ "bold": true, "text": "Fold" },
|
||||
{ "text": " and " },
|
||||
{ "bold": true, "text": "Unfold" },
|
||||
{
|
||||
"text": " a task to simplify your list using the arrow on the right ⤵️"
|
||||
}
|
||||
]
|
||||
},
|
||||
"numberType": "type1",
|
||||
"collapsed": { "value": false }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { DefaultColumnsValue, BlockFlavorKeys } from './../index';
|
||||
import { groupTemplateMap, GroupTemplateKeys } from './group-templates';
|
||||
import { BlockFlavorKeys, DefaultColumnsValue } from './../index';
|
||||
import { GroupTemplateKeys } from './template-factory';
|
||||
|
||||
// interface Block {
|
||||
// type: BlockFlavorKeys;
|
||||
|
@ -112,11 +112,15 @@ export class UserConfig extends ServiceBaseClass {
|
||||
async getWorkspaceName(workspace: string): Promise<string> {
|
||||
const workspace_db_block = await this.getWorkspaceDbBlock(workspace);
|
||||
const workspaceName =
|
||||
workspace_db_block.getDecoration<string>(WORKSPACE_CONFIG) ||
|
||||
workspace_db_block.id;
|
||||
workspace_db_block.getDecoration<string>(WORKSPACE_CONFIG) || '';
|
||||
return workspaceName;
|
||||
}
|
||||
|
||||
async getWorkspaceId(workspace: string): Promise<string> {
|
||||
const workspace_db_block = await this.getWorkspaceDbBlock(workspace);
|
||||
return workspace_db_block.id;
|
||||
}
|
||||
|
||||
async setWorkspaceName(workspace: string, workspaceName: string) {
|
||||
const workspace_db_block = await this.getWorkspaceDbBlock(workspace);
|
||||
workspace_db_block.setDecoration(WORKSPACE_CONFIG, workspaceName);
|
||||
|
8
libs/datasource/jwst/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "jwst"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
2
libs/datasource/jwst/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
// Open Source version coming soon
|
||||
// the rust version of jwt
|
@ -26,3 +26,13 @@ export const useCurrentEditors = () => {
|
||||
setCurrentEditors,
|
||||
};
|
||||
};
|
||||
|
||||
// when first time transfer doc to board, need init the editor shape size to page size.
|
||||
const _pageClientWidth = atom<number>(1020);
|
||||
export const usePageClientWidth = () => {
|
||||
const [pageClientWidth, setPageClientWidth] = useAtom(_pageClientWidth);
|
||||
return {
|
||||
pageClientWidth,
|
||||
setPageClientWidth,
|
||||
};
|
||||
};
|
||||
|
@ -7,9 +7,11 @@
|
||||
"start": "env-cmd -f .env.local-dev nx serve ligo-virgo",
|
||||
"start:affine": "nx serve ligo-virgo",
|
||||
"start:keck": "nx serve keck",
|
||||
"start:venus": "nx serve venus",
|
||||
"build": "nx build ligo-virgo",
|
||||
"build:local": "env-cmd -f .env.local nx build ligo-virgo",
|
||||
"build:keck": "nx build keck",
|
||||
"build:venus": "nx build venus",
|
||||
"build:analytic": "cross-env BUNDLE_ANALYZER=true nx build --skip-nx-cache",
|
||||
"test": "nx run-many --target test --all",
|
||||
"check": "nx run-many --target check --all",
|
||||
|
350
pnpm-lock.yaml
@ -213,6 +213,23 @@ importers:
|
||||
mini-css-extract-plugin: 2.6.1_webpack@5.73.0
|
||||
webpack: 5.73.0
|
||||
|
||||
apps/venus:
|
||||
specifiers:
|
||||
'@emotion/react': ^11.10.0
|
||||
'@emotion/styled': ^11.10.0
|
||||
'@mui/joy': ^5.0.0-alpha.39
|
||||
lozad: ^1.16.0
|
||||
mini-css-extract-plugin: ^2.6.1
|
||||
webpack: ^5.73.0
|
||||
dependencies:
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/styled': 11.10.0_@emotion+react@11.10.0
|
||||
'@mui/joy': 5.0.0-alpha.39_72v32ofbtgpmxm7mhvtx474vfu
|
||||
lozad: 1.16.0
|
||||
devDependencies:
|
||||
mini-css-extract-plugin: 2.6.1_webpack@5.73.0
|
||||
webpack: 5.73.0
|
||||
|
||||
libs/components/account:
|
||||
specifiers:
|
||||
'@authing/react-ui-components': ^3.1.23
|
||||
@ -334,7 +351,7 @@ importers:
|
||||
'@emotion/styled': 11.9.3_@emotion+react@11.9.3
|
||||
'@mui/icons-material': 5.8.4_@mui+material@5.8.7
|
||||
'@mui/material': 5.8.7_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/x-data-grid': 5.12.3_lc5g5arqxncq7hlh6olwtqg42u
|
||||
'@mui/x-data-grid': 5.12.3_7ff6pt5vb3e5jymp4h3bl3mztq
|
||||
is-hotkey: 0.2.0
|
||||
is-url: 1.2.4
|
||||
slate: 0.81.1
|
||||
@ -2688,6 +2705,28 @@ packages:
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@emotion/babel-plugin/11.10.0:
|
||||
resolution: {integrity: sha512-xVnpDAAbtxL1dsuSelU5A7BnY/lftws0wUexNJZTPsvX/1tM4GZJbclgODhvW4E+NH7E5VFcH0bBn30NvniPJA==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/helper-module-imports': 7.18.6
|
||||
'@babel/plugin-syntax-jsx': 7.18.6
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/hash': 0.9.0
|
||||
'@emotion/memoize': 0.8.0
|
||||
'@emotion/serialize': 1.1.0
|
||||
babel-plugin-macros: 3.1.0
|
||||
convert-source-map: 1.8.0
|
||||
escape-string-regexp: 4.0.0
|
||||
find-root: 1.1.0
|
||||
source-map: 0.5.7
|
||||
stylis: 4.0.13
|
||||
dev: false
|
||||
|
||||
/@emotion/babel-plugin/11.9.2:
|
||||
resolution: {integrity: sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==}
|
||||
peerDependencies:
|
||||
@ -2733,6 +2772,16 @@ packages:
|
||||
stylis: 4.0.13
|
||||
dev: false
|
||||
|
||||
/@emotion/cache/11.10.1:
|
||||
resolution: {integrity: sha512-uZTj3Yz5D69GE25iFZcIQtibnVCFsc/6+XIozyL3ycgWvEdif2uEw9wlUt6umjLr4Keg9K6xRPHmD8LGi+6p1A==}
|
||||
dependencies:
|
||||
'@emotion/memoize': 0.8.0
|
||||
'@emotion/sheet': 1.2.0
|
||||
'@emotion/utils': 1.2.0
|
||||
'@emotion/weak-memoize': 0.3.0
|
||||
stylis: 4.0.13
|
||||
dev: false
|
||||
|
||||
/@emotion/cache/11.9.3:
|
||||
resolution: {integrity: sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg==}
|
||||
dependencies:
|
||||
@ -2747,16 +2796,53 @@ packages:
|
||||
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
|
||||
dev: false
|
||||
|
||||
/@emotion/hash/0.9.0:
|
||||
resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/is-prop-valid/1.1.3:
|
||||
resolution: {integrity: sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==}
|
||||
dependencies:
|
||||
'@emotion/memoize': 0.7.5
|
||||
dev: false
|
||||
|
||||
/@emotion/is-prop-valid/1.2.0:
|
||||
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
|
||||
dependencies:
|
||||
'@emotion/memoize': 0.8.0
|
||||
dev: false
|
||||
|
||||
/@emotion/memoize/0.7.5:
|
||||
resolution: {integrity: sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/memoize/0.8.0:
|
||||
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
|
||||
dev: false
|
||||
|
||||
/@emotion/react/11.10.0:
|
||||
resolution: {integrity: sha512-K6z9zlHxxBXwN8TcpwBKcEsBsOw4JWCCmR+BeeOWgqp8GIU1yA2Odd41bwdAAr0ssbQrbJbVnndvv7oiv1bZeQ==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
'@types/react': '*'
|
||||
react: '>=16.8.0'
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/babel-plugin': 11.10.0
|
||||
'@emotion/cache': 11.10.1
|
||||
'@emotion/serialize': 1.1.0
|
||||
'@emotion/utils': 1.2.0
|
||||
'@emotion/weak-memoize': 0.3.0
|
||||
hoist-non-react-statics: 3.3.2
|
||||
dev: false
|
||||
|
||||
/@emotion/react/11.9.3:
|
||||
resolution: {integrity: sha512-g9Q1GcTOlzOEjqwuLF/Zd9LC+4FljjPjDfxSM7KmEakm+hsHXk+bYZ2q+/hTJzr0OUNkujo72pXLQvXj6H+GJQ==}
|
||||
peerDependencies:
|
||||
@ -2840,10 +2926,49 @@ packages:
|
||||
csstype: 3.1.0
|
||||
dev: false
|
||||
|
||||
/@emotion/serialize/1.1.0:
|
||||
resolution: {integrity: sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==}
|
||||
dependencies:
|
||||
'@emotion/hash': 0.9.0
|
||||
'@emotion/memoize': 0.8.0
|
||||
'@emotion/unitless': 0.8.0
|
||||
'@emotion/utils': 1.2.0
|
||||
csstype: 3.1.0
|
||||
dev: false
|
||||
|
||||
/@emotion/sheet/1.1.1:
|
||||
resolution: {integrity: sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==}
|
||||
dev: false
|
||||
|
||||
/@emotion/sheet/1.2.0:
|
||||
resolution: {integrity: sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==}
|
||||
dev: false
|
||||
|
||||
/@emotion/styled/11.10.0_@emotion+react@11.10.0:
|
||||
resolution: {integrity: sha512-V9oaEH6V4KePeQpgUE83i8ht+4Ri3E8Djp/ZPJ4DQlqWhSKITvgzlR3/YQE2hdfP4Jw3qVRkANJz01LLqK9/TA==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
'@emotion/react': ^11.0.0-rc.0
|
||||
'@types/react': '*'
|
||||
react: '>=16.8.0'
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/babel-plugin': 11.10.0
|
||||
'@emotion/is-prop-valid': 1.2.0
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/serialize': 1.1.0
|
||||
'@emotion/utils': 1.2.0
|
||||
dev: false
|
||||
|
||||
/@emotion/styled/11.9.3:
|
||||
resolution: {integrity: sha512-o3sBNwbtoVz9v7WB1/Y/AmXl69YHmei2mrVnK7JgyBJ//Rst5yqPZCecEJlMlJrFeWHp+ki/54uN265V2pEcXA==}
|
||||
peerDependencies:
|
||||
@ -2951,14 +3076,26 @@ packages:
|
||||
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
|
||||
dev: false
|
||||
|
||||
/@emotion/unitless/0.8.0:
|
||||
resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==}
|
||||
dev: false
|
||||
|
||||
/@emotion/utils/1.1.0:
|
||||
resolution: {integrity: sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/utils/1.2.0:
|
||||
resolution: {integrity: sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==}
|
||||
dev: false
|
||||
|
||||
/@emotion/weak-memoize/0.2.5:
|
||||
resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==}
|
||||
dev: false
|
||||
|
||||
/@emotion/weak-memoize/0.3.0:
|
||||
resolution: {integrity: sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==}
|
||||
dev: false
|
||||
|
||||
/@eslint/eslintrc/1.3.0:
|
||||
resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@ -4186,6 +4323,31 @@ packages:
|
||||
react-is: 17.0.2
|
||||
dev: false
|
||||
|
||||
/@mui/base/5.0.0-alpha.92:
|
||||
resolution: {integrity: sha512-ZgnSLrTXL4iUdLQhjp01dAOTQPQlnwrqjZRwDT3E6LZXEYn6cMv1MY6LZkWcF/zxrUnyasnsyMAgZ5d8AXS7bA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/is-prop-valid': 1.2.0
|
||||
'@mui/types': 7.1.5
|
||||
'@mui/utils': 5.9.3
|
||||
'@popperjs/core': 2.11.5
|
||||
clsx: 1.2.1
|
||||
prop-types: 15.8.1
|
||||
react-is: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/icons-material/5.8.4:
|
||||
resolution: {integrity: sha512-9Z/vyj2szvEhGWDvb+gG875bOGm8b8rlHBKOD1+nA3PcgC3fV6W1AU6pfOorPeBfH2X4mb9Boe97vHvaSndQvA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -4244,6 +4406,40 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/joy/5.0.0-alpha.39_72v32ofbtgpmxm7mhvtx474vfu:
|
||||
resolution: {integrity: sha512-F/cjEwvH9UFxIRJ30P8fuGOEDtgDBJCd++yTq8JYXARGCSlUMtbpijkPvnYFz69j3BtHCDhSaz3JA0cxcwVjaQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/styled': 11.10.0_@emotion+react@11.10.0
|
||||
'@mui/base': 5.0.0-alpha.92
|
||||
'@mui/system': 5.9.3_72v32ofbtgpmxm7mhvtx474vfu
|
||||
'@mui/types': 7.1.5
|
||||
'@mui/utils': 5.9.3
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.0
|
||||
prop-types: 15.8.1
|
||||
react-is: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/material/5.8.7_d6menda4vqwq6peqnkbe7mkj4i:
|
||||
resolution: {integrity: sha512-Oo62UhrgEi+BMLr3nUEASJgScE2/hhq14CbBUmrVV3GQlEGtqMZsy26Vb0AqEmphFeN3TXlsbM9aeW5yq8ZFlw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -4355,6 +4551,46 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/private-theming/5.9.3:
|
||||
resolution: {integrity: sha512-Ys3WO39WqoGciGX9k5AIi/k2zJhlydv4FzlEEwtw9OqdMaV0ydK/TdZekKzjP9sTI/JcdAP3H5DWtUaPLQJjWg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@mui/utils': 5.9.3
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/@mui/styled-engine/5.8.7_72v32ofbtgpmxm7mhvtx474vfu:
|
||||
resolution: {integrity: sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.4.1
|
||||
'@emotion/styled': ^11.3.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/cache': 11.9.3
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/styled': 11.10.0_@emotion+react@11.10.0
|
||||
csstype: 3.1.0
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/@mui/styled-engine/5.8.7_d6menda4vqwq6peqnkbe7mkj4i:
|
||||
resolution: {integrity: sha512-tVqtowjbYmiRq+qcqXK731L9eWoL9H8xTRhuTgaDGKdch1zlt4I2UwInUe1w2N9N/u3/jHsFbLcl1Un3uOwpQg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -4372,8 +4608,8 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/cache': 11.9.3
|
||||
'@emotion/react': 11.9.3_@babel+core@7.18.6
|
||||
'@emotion/styled': 11.9.3_dc5dh2wp562rsjxvguwi2i3yzq
|
||||
'@emotion/react': 11.9.3
|
||||
'@emotion/styled': 11.9.3_@emotion+react@11.9.3
|
||||
csstype: 3.1.0
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
@ -4453,8 +4689,8 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/react': 11.9.3
|
||||
'@emotion/styled': 11.9.3_@emotion+react@11.9.3
|
||||
'@emotion/react': 11.9.3_@babel+core@7.18.6
|
||||
'@emotion/styled': 11.9.3_dc5dh2wp562rsjxvguwi2i3yzq
|
||||
'@mui/private-theming': 5.8.6
|
||||
'@mui/styled-engine': 5.8.7_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/types': 7.1.4
|
||||
@ -4464,6 +4700,66 @@ packages:
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/@mui/system/5.9.3_72v32ofbtgpmxm7mhvtx474vfu:
|
||||
resolution: {integrity: sha512-EXQV2POwncstHLYII+G4VSYdEFun1TjBbQSBDK76DbIkug8nPjtjAZ+3Kgk3/NoFIigW+vQ9cDVUZtlbRH6YMQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/styled': 11.10.0_@emotion+react@11.10.0
|
||||
'@mui/private-theming': 5.9.3
|
||||
'@mui/styled-engine': 5.8.7_72v32ofbtgpmxm7mhvtx474vfu
|
||||
'@mui/types': 7.1.5
|
||||
'@mui/utils': 5.9.3
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.0
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/@mui/system/5.9.3_d6menda4vqwq6peqnkbe7mkj4i:
|
||||
resolution: {integrity: sha512-EXQV2POwncstHLYII+G4VSYdEFun1TjBbQSBDK76DbIkug8nPjtjAZ+3Kgk3/NoFIigW+vQ9cDVUZtlbRH6YMQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/react': 11.9.3
|
||||
'@emotion/styled': 11.9.3_@emotion+react@11.9.3
|
||||
'@mui/private-theming': 5.9.3
|
||||
'@mui/styled-engine': 5.8.7_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/types': 7.1.5
|
||||
'@mui/utils': 5.9.3
|
||||
clsx: 1.2.1
|
||||
csstype: 3.1.0
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/@mui/types/7.1.4:
|
||||
resolution: {integrity: sha512-uveM3byMbthO+6tXZ1n2zm0W3uJCQYtwt/v5zV5I77v2v18u0ITkb8xwhsDD2i3V2Kye7SaNR6FFJ6lMuY/WqQ==}
|
||||
peerDependencies:
|
||||
@ -4484,6 +4780,15 @@ packages:
|
||||
'@types/react': 18.0.14
|
||||
dev: false
|
||||
|
||||
/@mui/types/7.1.5:
|
||||
resolution: {integrity: sha512-HnRXrxgHJYJcT8ZDdDCQIlqk0s0skOKD7eWs9mJgBUu70hyW4iA6Kiv3yspJR474RFH8hysKR65VVSzUSzkuwA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/@mui/utils/5.8.6:
|
||||
resolution: {integrity: sha512-QM2Sd1xZo2jOt2Vz5Rmro+pi2FLJyiv4+OjxkUwXR3oUM65KSMAMLl/KNYU55s3W3DLRFP5MVwE4FhAbHseHAg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@ -4517,7 +4822,23 @@ packages:
|
||||
react-is: 17.0.2
|
||||
dev: false
|
||||
|
||||
/@mui/x-data-grid/5.12.3_lc5g5arqxncq7hlh6olwtqg42u:
|
||||
/@mui/utils/5.9.3:
|
||||
resolution: {integrity: sha512-l0N5bcrenE9hnwZ/jPecpIRqsDFHkPXoFUcmkgysaJwVZzJ3yQkGXB47eqmXX5yyGrSc6HksbbqXEaUya+siew==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@types/prop-types': 15.7.5
|
||||
'@types/react-is': 17.0.3
|
||||
prop-types: 15.8.1
|
||||
react-is: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/x-data-grid/5.12.3_7ff6pt5vb3e5jymp4h3bl3mztq:
|
||||
resolution: {integrity: sha512-57A2MkRR/uUNC/dECFV0YDJvi1Q+gQgmgw1OHmZ1uSnKh29PcHpswkdapO0LueLpxAy8tfH+fTtnnPDmYgJeUg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
@ -4535,7 +4856,7 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@mui/material': 5.8.7_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/system': 5.8.7_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/system': 5.9.3_d6menda4vqwq6peqnkbe7mkj4i
|
||||
'@mui/utils': 5.8.6
|
||||
clsx: 1.2.0
|
||||
prop-types: 15.8.1
|
||||
@ -7537,6 +7858,15 @@ packages:
|
||||
cosmiconfig: 6.0.0
|
||||
resolve: 1.22.1
|
||||
|
||||
/babel-plugin-macros/3.1.0:
|
||||
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
|
||||
engines: {node: '>=10', npm: '>=6'}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
cosmiconfig: 7.0.1
|
||||
resolve: 1.22.1
|
||||
dev: false
|
||||
|
||||
/babel-plugin-open-source/1.3.4:
|
||||
resolution: {integrity: sha512-7lsfY30y/XYYbK4vYHfPyC/aF2KM0vz2OFaz6+tDuAg6A3223is7vqzFYd0va7n/YTw5osScevR0bQ4hO8gPQg==}
|
||||
dependencies:
|
||||
@ -8362,7 +8692,6 @@ packages:
|
||||
parse-json: 5.2.0
|
||||
path-type: 4.0.0
|
||||
yaml: 1.10.2
|
||||
dev: true
|
||||
|
||||
/create-require/1.1.1:
|
||||
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||
@ -12777,6 +13106,10 @@ packages:
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/lozad/1.16.0:
|
||||
resolution: {integrity: sha512-JBr9WjvEFeKoyim3svo/gsQPTkgG/mOHJmDctZ/+U9H3ymUuvEkqpn8bdQMFsvTMcyRJrdJkLv0bXqGm0sP72w==}
|
||||
dev: false
|
||||
|
||||
/lru-cache/4.0.2:
|
||||
resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==}
|
||||
dependencies:
|
||||
@ -15204,7 +15537,6 @@ packages:
|
||||
|
||||
/react-is/18.2.0:
|
||||
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
|
||||
dev: true
|
||||
|
||||
/react-list/0.8.17:
|
||||
resolution: {integrity: sha512-pgmzGi0G5uGrdHzMhgO7KR1wx5ZXVvI3SsJUmkblSAKtewIhMwbQiMuQiTE83ozo04BQJbe0r3WIWzSO0dR1xg==}
|
||||
|
@ -14,6 +14,7 @@
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"@toeverything/components/account": [
|
||||
"libs/components/account/src/index.ts"
|
||||
|
@ -28,6 +28,7 @@
|
||||
"framework-virgo": "libs/framework/virgo",
|
||||
"keck": "apps/keck",
|
||||
"ligo-virgo": "apps/ligo-virgo",
|
||||
"utils": "libs/utils"
|
||||
"utils": "libs/utils",
|
||||
"venus": "apps/venus"
|
||||
}
|
||||
}
|
||||
|