fix interfere in Menu.tsx

This commit is contained in:
DiamondThree 2022-08-03 15:30:02 +08:00
commit eab4fd2b8f
80 changed files with 3509 additions and 1137 deletions

46
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View 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
View 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.

View 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.

View 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
View 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
View 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
View 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
View File

@ -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.

View File

@ -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';

View File

@ -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}

View File

@ -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>
)}

View File

@ -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
View File

@ -0,0 +1,11 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic"
}
]
],
"plugins": []
}

View 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
View 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
View 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
View 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"]
}

View 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'}
>
Its 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 dont 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;

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -0,0 +1,3 @@
export const environment = {
production: true,
};

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

14
apps/venus/src/index.html Normal file
View 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
View 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>
);

View 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';

View 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>

View 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
View 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"
}
]
}

View 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"
]
}

View 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 ?? []),
];
};

View File

@ -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,

View File

@ -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}

View File

@ -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]

View File

@ -149,6 +149,7 @@ export interface TDMeta {
isDarkMode: boolean;
app: {
useStore: () => TDSnapshot;
setEditingText: (id: string) => void;
};
}

View File

@ -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(
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>

View File

@ -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,
await onCreateSure({
type,
newPropertyItem,
newValue,
fieldName,
});
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);
}
onSure?.();
}}
/>
@ -203,10 +113,3 @@ export const CreatePendantPanel = ({
</StyledPopoverWrapper>
);
};
const genOptionWithId = (options: OptionType[] = []) => {
return options.map((option: OptionType) => ({
...option,
id: genSelectOptionId(),
}));
};

View File

@ -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 onUpdateSure({
type,
newPropertyItem,
newValue,
fieldName,
});
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,
});
}
onSure?.();
}}
onDelete={

View File

@ -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,
});
}
};
};

View File

@ -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 !' };
};

View File

@ -297,10 +297,10 @@ export class ScrollManager {
}
public lock() {
this._scrollController.lockScroll();
this._scrollController?.lockScroll();
}
public unLock() {
this._scrollController.unLockScroll();
this._scrollController?.unLockScroll();
}
}

View File

@ -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

View File

@ -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}
/>

View 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

View 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>
)
};

View File

@ -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';
@ -126,3 +126,7 @@ export * from './group-by/group-by';
export * from './layout/layout';
export * from './lock/lock';
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';

View File

@ -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

View 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 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>
)
};

View File

@ -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

View 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 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>
)
};

View 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

View 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>
)
};

View File

@ -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">
{showSettingsSidebar ? (
<SideBarViewIcon />
) : (
<SideBarViewCloseIcon />
)}
</IconButton>
</StyledHelper>
</FlexContainer>

View File

@ -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>

View File

@ -95,13 +95,16 @@ export function DndTree(props: DndTreeProps) {
>
{/* <button onClick={() => handleAdd()}> add top node</button> */}
{flattenedItems.map(
({ id, title, children, collapsed, depth }) => (
({ id, title, children, collapsed, depth }) => {
return (
<DndTreeItem
key={id}
id={id}
// value={id}
value={title}
collapsed={Boolean(collapsed && children.length)}
collapsed={Boolean(
collapsed && children.length
)}
depth={
id === activeId && projected
? projected.depth
@ -109,16 +112,20 @@ export function DndTree(props: DndTreeProps) {
}
indentationWidth={indentationWidth}
indicator={showDragIndicator}
childCount={children.length}
onCollapse={
collapsible && children.length
? () => handleCollapse(id)
: undefined
}
onRemove={
removable ? () => handleRemove(id) : undefined
removable
? () => handleRemove(id)
: undefined
}
/>
)
);
}
)}
<DragOverlay
dropAnimation={dropAnimation}

View File

@ -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,13 +244,25 @@ function DndTreeItemMoreActions(props: ActionsProps) {
];
return (
<>
<span
className={styles['TreeItemMoreActions']}
<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}
>
···
</span>
<MoreIcon />
</IconButton>
</StyledAction>
</div>
<Cascader
items={menuList}
anchorEl={anchorEl}
@ -255,7 +278,8 @@ function DndTreeItemMoreActions(props: ActionsProps) {
autoHideDuration={2000}
onClose={handle_alert_close}
/>
</>
</div>
</ClickAwayListener>
);
}

View File

@ -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 />*/}

View File

@ -83,7 +83,7 @@
justify-content: space-around;
background-color: #fff;
color: #4c6275;
padding: 0 0.5rem;
padding-right: 0.5rem;
overflow: hidden;
.TreeItemMoreActions {

View File

@ -36,6 +36,7 @@ interface IconButtonProps {
style?: CSSProperties;
className?: string;
size?: SizeType;
hoverColor?: string;
}
export const IconButton: FC<PropsWithChildren<IconButtonProps>> = ({
@ -57,8 +58,10 @@ export const IconButton: FC<PropsWithChildren<IconButtonProps>> = ({
);
};
const Container = styled('button')<{ size?: SizeType }>(
({ theme, size = SIZE_MIDDLE }) => {
const Container = styled('button')<{
size?: SizeType;
hoverColor?: string;
}>(({ theme, size = SIZE_MIDDLE, hoverColor }) => {
const { iconSize, areaSize } = SIZE_CONFIG[size];
return {
@ -67,10 +70,10 @@ const Container = styled('button')<{ size?: SizeType }>(
alignItems: 'center',
width: areaSize,
height: areaSize,
backgroundColor: theme.affine.palette.white,
backgroundColor: 'transparent',
color: theme.affine.palette.icons,
padding: theme.affine.spacing.iconPadding,
borderRadius: '5px',
borderRadius: '3px',
'& svg': {
width: iconSize,
@ -81,7 +84,7 @@ const Container = styled('button')<{ size?: SizeType }>(
},
'&:hover': {
backgroundColor: theme.affine.palette.hover,
backgroundColor: hoverColor || theme.affine.palette.hover,
},
[`&${buttonStatus.hover}`]: {
@ -99,5 +102,4 @@ const Container = styled('button')<{ size?: SizeType }>(
cursor: 'not-allowed',
},
};
}
);
});

View File

@ -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": []
}
]
}

View File

@ -0,0 +1,15 @@
{
"type": "group",
"properties": {},
"blocks": [
{
"type": "text",
"properties": {
"text": {
"value": [{ "text": "" }]
}
},
"blocks": []
}
]
}

View File

@ -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": ""
}
]
}
}
}
]
}

View File

@ -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 };

View File

@ -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: {

View File

@ -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 }
}
}
]
}

View File

@ -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;

View File

@ -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);

View 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]

View File

@ -0,0 +1,2 @@
// Open Source version coming soon
// the rust version of jwt

View File

@ -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,
};
};

View File

@ -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",

View File

@ -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==}

View File

@ -14,6 +14,7 @@
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"resolveJsonModule": true,
"paths": {
"@toeverything/components/account": [
"libs/components/account/src/index.ts"

View File

@ -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"
}
}