mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 18:41:52 +08:00
init: the first public commit for AFFiNE
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# components-affine-board
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test components-affine-board` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'components-affine-board',
|
||||
preset: '../../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../../coverage/libs/components/affine-board',
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@toeverything/components/affine-board",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/components/affine-board/src",
|
||||
"projectType": "library",
|
||||
"tags": ["components"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:rollup",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/components/affine-board",
|
||||
"tsConfig": "libs/components/affine-board/tsconfig.lib.json",
|
||||
"project": "libs/components/affine-board/package.json",
|
||||
"entryFile": "libs/components/affine-board/src/index.ts",
|
||||
"external": ["react/jsx-runtime"],
|
||||
"rollupConfig": "libs/rollup.config.cjs",
|
||||
"compiler": "babel",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "libs/components/affine-board/README.md",
|
||||
"input": ".",
|
||||
"output": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"libs/components/affine-board/**/*.{ts,tsx,js,jsx}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/components/affine-board"],
|
||||
"options": {
|
||||
"jestConfig": "libs/components/affine-board/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Tldraw } from '@toeverything/components/board-draw';
|
||||
import { tools } from '@toeverything/components/board-tools';
|
||||
import { getSession } from '@toeverything/components/board-sessions';
|
||||
import * as commands from '@toeverything/components/board-commands';
|
||||
import { TldrawApp, deepCopy } from '@toeverything/components/board-state';
|
||||
import { TDShapeType } from '@toeverything/components/board-types';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import { useShapes } from './hooks';
|
||||
import { RecastBlockProvider } from '@toeverything/components/editor-core';
|
||||
import { createEditor } from '@toeverything/components/affine-editor';
|
||||
import { AsyncBlock, BlockEditor } from '@toeverything/framework/virgo';
|
||||
|
||||
interface AffineBoardProps {
|
||||
workspace: string;
|
||||
rootBlockId: string;
|
||||
}
|
||||
|
||||
const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
|
||||
const [app, set_app] = useState<TldrawApp>();
|
||||
|
||||
const [document] = useState(() => {
|
||||
return {
|
||||
...deepCopy(TldrawApp.default_document),
|
||||
id: workspace,
|
||||
pages: {
|
||||
[rootBlockId]: {
|
||||
id: rootBlockId,
|
||||
name: `Page ${rootBlockId}`,
|
||||
childIndex: 1,
|
||||
shapes: {},
|
||||
bindings: {},
|
||||
},
|
||||
},
|
||||
pageStates: {
|
||||
[rootBlockId]: {
|
||||
id: rootBlockId,
|
||||
camera: {
|
||||
point: [0, 0],
|
||||
zoom: 1,
|
||||
},
|
||||
selectedIds: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const shapes = useShapes(workspace, rootBlockId);
|
||||
useEffect(() => {
|
||||
if (app) {
|
||||
app.replacePageContent(shapes || {}, {}, {});
|
||||
}
|
||||
}, [app, shapes]);
|
||||
|
||||
return (
|
||||
<Tldraw
|
||||
document={document}
|
||||
commands={commands}
|
||||
tools={tools}
|
||||
getSession={getSession}
|
||||
callbacks={{
|
||||
onMount(app) {
|
||||
set_app(app);
|
||||
},
|
||||
onChangePage(app, shapes, bindings, assets) {
|
||||
Promise.all(
|
||||
Object.entries(shapes).map(async ([id, shape]) => {
|
||||
if (shape === undefined) {
|
||||
return services.api.editorBlock.delete({
|
||||
workspace: workspace,
|
||||
id,
|
||||
});
|
||||
} else {
|
||||
let block = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace: workspace,
|
||||
ids: [shape.affineId],
|
||||
})
|
||||
)?.[0];
|
||||
if (!block) {
|
||||
block =
|
||||
await services.api.editorBlock.create({
|
||||
workspace: workspace,
|
||||
parentId:
|
||||
app.appState.currentPageId,
|
||||
type:
|
||||
shape.type ===
|
||||
TDShapeType.Editor
|
||||
? 'group'
|
||||
: 'shape',
|
||||
});
|
||||
}
|
||||
shape.affineId = block.id;
|
||||
return services.api.editorBlock.update({
|
||||
workspace: shape.workspace,
|
||||
id: block.id,
|
||||
properties: {
|
||||
shapeProps: {
|
||||
value: JSON.stringify(shape),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const AffineBoardWitchContext = ({
|
||||
workspace,
|
||||
rootBlockId,
|
||||
}: AffineBoardProps) => {
|
||||
const [editor, set_editor] = useState<BlockEditor>();
|
||||
useEffect(() => {
|
||||
const inner_editor = createEditor(workspace, true);
|
||||
set_editor(inner_editor);
|
||||
return () => {
|
||||
inner_editor.dispose();
|
||||
};
|
||||
}, [workspace]);
|
||||
|
||||
const [page, set_page] = useState<AsyncBlock>();
|
||||
useEffect(() => {
|
||||
editor?.getBlockById(rootBlockId).then(block => {
|
||||
set_page(block);
|
||||
});
|
||||
}, [editor, rootBlockId]);
|
||||
return page ? (
|
||||
<RecastBlockProvider block={page}>
|
||||
<AffineBoard workspace={workspace} rootBlockId={rootBlockId} />
|
||||
</RecastBlockProvider>
|
||||
) : null;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './use-shapes';
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
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';
|
||||
|
||||
export const useShapes = (workspace: string, rootBlockId: string) => {
|
||||
const [blocks, setBlocks] = useState<ReturnEditorBlock[]>();
|
||||
useEffect(() => {
|
||||
services.api.editorBlock
|
||||
.get({ workspace, ids: [rootBlockId] })
|
||||
.then(async blockData => {
|
||||
const shapes = await Promise.all(
|
||||
(blockData?.[0]?.children || []).map(async childId => {
|
||||
const childBlock = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace,
|
||||
ids: [childId],
|
||||
})
|
||||
)?.[0];
|
||||
return childBlock;
|
||||
})
|
||||
);
|
||||
setBlocks(shapes);
|
||||
});
|
||||
let unobserve: () => void;
|
||||
services.api.editorBlock
|
||||
.observe({ workspace, id: rootBlockId }, async blockData => {
|
||||
const shapes = await Promise.all(
|
||||
(blockData?.children || []).map(async childId => {
|
||||
const childBlock = (
|
||||
await services.api.editorBlock.get({
|
||||
workspace,
|
||||
ids: [childId],
|
||||
})
|
||||
)?.[0];
|
||||
return childBlock;
|
||||
})
|
||||
);
|
||||
setBlocks(shapes);
|
||||
})
|
||||
.then(cb => {
|
||||
unobserve = cb;
|
||||
});
|
||||
|
||||
return () => {
|
||||
unobserve?.();
|
||||
};
|
||||
}, [workspace, rootBlockId]);
|
||||
|
||||
let groupCount = 0;
|
||||
|
||||
return blocks?.reduce((acc, block) => {
|
||||
const shapeProps = block.properties.shapeProps?.value
|
||||
? JSON.parse(block.properties.shapeProps.value)
|
||||
: {};
|
||||
if (block.type === 'shape') {
|
||||
acc[block.id] = { ...shapeProps, id: block.id };
|
||||
} else {
|
||||
acc[block.id] = Editor.getShape({
|
||||
point: [groupCount * 740, 200],
|
||||
id: block.id,
|
||||
...shapeProps,
|
||||
affineId: shapeProps.affineId ?? block.id,
|
||||
workspace: block.workspace,
|
||||
rootBlockId: block.id,
|
||||
});
|
||||
groupCount = groupCount + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, TDShape>);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { AffineBoardWitchContext as AffineBoard } from './Board';
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user