chore: read source code directly in example docs (#77)

This commit is contained in:
tecvan 2025-03-21 16:33:28 +08:00 committed by GitHub
parent 5dbb2f4b55
commit d90fdad677
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 111 additions and 393 deletions

View File

@ -47,6 +47,7 @@
"stylelintrc": "jsonc",
"*.json": "jsonc",
"package.json": "json",
"*.mdc": "markdown",
".htmlhintrc": "jsonc",
"htmlhintrc": "jsonc",
"Procfile*": "shellscript",

View File

@ -1,377 +1,14 @@
import { PreviewEditor } from '../preview-editor';
import { FreeLayoutSimple } from '.';
const indexCode = {
code: `import {
EditorRenderer,
FreeLayoutEditorProvider,
} from '@flowgram.ai/free-layout-editor';
import nodeRegistriesCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/node-registries.ts';
import dataCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/initial-data.ts';
import useEditorPropsCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/hooks/use-editor-props.tsx';
import indexCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/editor.tsx';
import toolsCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/components/tools.tsx';
import nodeAddPanelCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/components/node-add-panel.tsx';
import minimapCode from '!!raw-loader!@flowgram.ai/demo-free-layout-simple/src/components/minimap.tsx';
import { NodeAddPanel } from './components/node-add-panel';
import { Tools } from './components/tools'
import { Minimap } from './components/minimap'
import { useEditorProps } from './hooks/use-editor-props'
import '@flowgram.ai/free-layout-editor/index.css';
import './index.css';
export const Editor = () => {
const editorProps = useEditorProps()
return (
<FreeLayoutEditorProvider {...editorProps}>
<div className="demo-free-container">
<div className="demo-free-layout">
<NodeAddPanel />
<EditorRenderer className="demo-free-editor" />
</div>
<Tools />
<Minimap />
</div>
</FreeLayoutEditorProvider>
)
};
`,
active: true,
};
const dataCode = `import { WorkflowJSON } from '@flowgram.ai/free-layout-editor';
export const initialData: WorkflowJSON = {
nodes: [
{
id: 'start_0',
type: 'start',
meta: {
position: { x: 0, y: 0 },
},
data: {
title: 'Start',
content: 'Start content'
},
},
{
id: 'node_0',
type: 'custom',
meta: {
position: { x: 400, y: 0 },
},
data: {
title: 'Custom',
content: 'Custom node content'
},
},
{
id: 'end_0',
type: 'end',
meta: {
position: { x: 800, y: 0 },
},
data: {
title: 'End',
content: 'End content'
},
},
],
edges: [
{
sourceNodeID: 'start_0',
targetNodeID: 'node_0',
},
{
sourceNodeID: 'node_0',
targetNodeID: 'end_0',
},
],
};
`;
const nodeAddPanelCode = `import React from 'react';
import { WorkflowDragService, useService } from '@flowgram.ai/free-layout-editor';
const cardkeys = ['Node1', 'Node2'];
export const NodeAddPanel: React.FC = props => {
const startDragSerivce = useService<WorkflowDragService>(WorkflowDragService);
return (
<div className="demo-free-sidebar">
{cardkeys.map(nodeType => (
<div
key={nodeType}
className="demo-free-card"
onMouseDown={e => startDragSerivce.startDragCard(nodeType, e, {
data: {
title: \`New \${nodeType}\`,
content: 'xxxx'
}
})}
>
{nodeType}
</div>
))}
</div>
);
};
`;
const useEditorPropsCode = `import { useMemo } from 'react';
import {
FreeLayoutProps,
WorkflowNodeProps,
WorkflowNodeRenderer,
Field,
useNodeRender
} from '@flowgram.ai/free-layout-editor';
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
import { initialData } from '../initial-data';
import { nodeRegistries } from '../node-registries'
export const useEditorProps = () => useMemo<FreeLayoutProps>(
() => ({
/**
* Whether to enable the background
*/
background: true,
/**
* Whether it is read-only or not, the node cannot be dragged in read-only mode
*/
readonly: false,
/**
* Initial data
*
*/
initialData,
/**
* Node registries
*
*/
nodeRegistries,
/**
* Get the default node registry, which will be merged with the 'nodeRegistries'
* nodeRegistries
*/
getNodeDefaultRegistry(type) {
return {
type,
meta: {
defaultExpanded: true,
},
formMeta: {
/**
* Render form
*/
render: () => <>
<Field<string> name="title">
{({ field }) => <div className="demo-free-node-title">{field.value}</div>}
</Field>
<div className="demo-free-node-content">
<Field<string> name="content">
<input />
</Field>
</div>
</>
}
};
},
materials: {
/**
* Render Node
*/
renderDefaultNode: (props: WorkflowNodeProps) => {
const { form } = useNodeRender()
return (
<WorkflowNodeRenderer className="demo-free-node" node={props.node}>
{form?.render()}
</WorkflowNodeRenderer>
)
},
},
/**
* Content change
*/
onContentChange(ctx, event) {
// console.log('Auto Save: ', event, ctx.document.toJSON());
},
// /**
// * Node engine enable, you can configure formMeta in the FlowNodeRegistry
// */
nodeEngine: {
enable: true,
},
/**
* Redo/Undo enable
*/
history: {
enable: true,
enableChangeNode: true, // Listen Node engine data change
},
/**
* Playground init
*/
onInit: ctx => {},
/**
* Playground render
*/
onAllLayersRendered(ctx) {
// Fitview
ctx.document.fitView(false);
},
/**
* Playground dispose
*/
onDispose() {
console.log('---- Playground Dispose ----');
},
plugins: () => [
/**
* Minimap plugin
*
*/
createMinimapPlugin({
disableLayer: true,
canvasStyle: {
canvasWidth: 182,
canvasHeight: 102,
canvasPadding: 50,
canvasBackground: 'rgba(245, 245, 245, 1)',
canvasBorderRadius: 10,
viewportBackground: 'rgba(235, 235, 235, 1)',
viewportBorderRadius: 4,
viewportBorderColor: 'rgba(201, 201, 201, 1)',
viewportBorderWidth: 1,
viewportBorderDashLength: 2,
nodeColor: 'rgba(255, 255, 255, 1)',
nodeBorderRadius: 2,
nodeBorderWidth: 0.145,
nodeBorderColor: 'rgba(6, 7, 9, 0.10)',
overlayColor: 'rgba(255, 255, 255, 0)',
},
inactiveDebounceTime: 1,
}),
/**
* Snap plugin
* 线
*/
createFreeSnapPlugin({
edgeColor: '#00B2B2',
alignColor: '#00B2B2',
edgeLineWidth: 1,
alignLineWidth: 1,
alignCrossWidth: 8,
}),
]
}),
[],
);
`;
const nodeRegistriesCode = `import { WorkflowNodeRegistry } from '@flowgram.ai/free-layout-editor';
/**
* You can customize your own node registry
*
*/
export const nodeRegistries: WorkflowNodeRegistry[] = [
{
type: 'start',
meta: {
isStart: true, // Mark as start
deleteDisable: true, // The start node cannot be deleted
copyDisable: true, // The start node cannot be copied
defaultPorts: [{ type: 'output' }], // Used to define the input and output ports, the start node only has the output port
},
},
{
type: 'end',
meta: {
deleteDisable: true,
copyDisable: true,
defaultPorts: [{ type: 'input' }],
},
},
{
type: 'custom',
meta: {
},
defaultPorts: [{ type: 'output' }, { type: 'input' }], // A normal node has two ports
},
];
`;
const toolsCode = `import { useEffect, useState } from 'react'
import { usePlaygroundTools, useClientContext } from '@flowgram.ai/free-layout-editor';
export function Tools() {
const { history } = useClientContext();
const tools = usePlaygroundTools();
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
useEffect(() => {
const disposable = history.undoRedoService.onChange(() => {
setCanUndo(history.canUndo());
setCanRedo(history.canRedo());
});
return () => disposable.dispose();
}, [history]);
return <div style={{ position: 'absolute', zIndex: 10, bottom: 16, left: 226, display: 'flex', gap: 8 }}>
<button onClick={() => tools.zoomin()}>ZoomIn</button>
<button onClick={() => tools.zoomout()}>ZoomOut</button>
<button onClick={() => tools.fitView()}>Fitview</button>
<button onClick={() => tools.autoLayout()}>AutoLayout</button>
<button onClick={() => history.undo()} disabled={!canUndo}>Undo</button>
<button onClick={() => history.redo()} disabled={!canRedo}>Redo</button>
<span>{Math.floor(tools.zoom * 100)}%</span>
</div>
}
`;
const minimapCode = `import { FlowMinimapService, MinimapRender } from '@flowgram.ai/minimap-plugin';
import { useService } from '@flowgram.ai/free-layout-editor';
export const Minimap = () => {
const minimapService = useService(FlowMinimapService);
return (
<div
style={{
position: 'absolute',
left: 226,
bottom: 51,
zIndex: 100,
width: 182,
}}
>
<MinimapRender
service={minimapService}
containerStyles={{
pointerEvents: 'auto',
position: 'relative',
top: 'unset',
right: 'unset',
bottom: 'unset',
left: 'unset',
}}
inactiveStyle={{
opacity: 1,
scale: 1,
translateX: 0,
translateY: 0,
}}
/>
</div>
);
};
`;
export const FreeLayoutSimplePreview = () => {
const files = {
'index.tsx': indexCode,

View File

@ -47,7 +47,9 @@
"devDependencies": {
"@flowgram.ai/ts-config": "workspace:*",
"@flowgram.ai/eslint-config": "workspace:*",
"webpack-merge": "^5.9.0",
"@eslint/js": "^9.12.0",
"raw-loader": "^4.0.2",
"@rspress/plugin-typedoc": "^1.38.0",
"@types/node": "^18",
"@types/lodash-es": "^4.17.12",

View File

@ -1,5 +1,6 @@
import * as path from 'node:path';
import { merge } from 'webpack-merge';
import { defineConfig } from 'rspress/config';
export default defineConfig({
@ -9,32 +10,46 @@ export default defineConfig({
globalStyles: path.join(__dirname, './global.less'),
builderConfig: {
tools: {
rspack: {
optimization: {
splitChunks: {
chunks: 'all', // 拆分所有模块,包括异步和同步
minSize: 30 * 1024, // 30KB 以下不拆分
maxSize: 500 * 1024, // 500KB 以上强制拆分
minChunks: 1, // 最少被引用 1 次就可以拆分
automaticNameDelimiter: '-',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: -10, // 优先级
rspack(options) {
return merge(options, {
module: {
rules: [
{
test: /\.mdc$/,
type: 'asset/source',
},
{
resourceQuery: /raw/,
type: 'asset/source',
},
],
},
optimization: {
splitChunks: {
chunks: 'all', // 拆分所有模块,包括异步和同步
minSize: 30 * 1024, // 30KB 以下不拆分
maxSize: 500 * 1024, // 500KB 以上强制拆分
minChunks: 1, // 最少被引用 1 次就可以拆分
automaticNameDelimiter: '-',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: -10, // 优先级
},
},
},
},
},
// 禁用 ES 模块输出(启用 CommonJS
experiments: {
outputModule: false,
},
// 允许省略文件扩展名
resolve: {
fullySpecified: false,
},
// 禁用 ES 模块输出(启用 CommonJS
experiments: {
outputModule: false,
},
// 允许省略文件扩展名
resolve: {
fullySpecified: false,
},
});
},
},
},

View File

@ -494,12 +494,18 @@ importers:
globals:
specifier: ^15.11.0
version: 15.13.0
raw-loader:
specifier: ^4.0.2
version: 4.0.2(webpack@5.76.0)
sucrase:
specifier: 3.35.0
version: 3.35.0
typescript-eslint:
specifier: ^8.8.1
version: 8.18.0(eslint@8.57.1)(typescript@5.0.4)
webpack-merge:
specifier: ^5.9.0
version: 5.10.0
../../config/eslint-config:
dependencies:
@ -8001,6 +8007,15 @@ packages:
engines: {node: '>= 12'}
dev: false
/clone-deep@4.0.1:
resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==}
engines: {node: '>=6'}
dependencies:
is-plain-object: 2.0.4
kind-of: 6.0.3
shallow-clone: 3.0.1
dev: true
/clone-response@1.0.2:
resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==}
dependencies:
@ -9479,6 +9494,11 @@ packages:
keyv: 4.5.4
rimraf: 3.0.2
/flat@5.0.2:
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
hasBin: true
dev: true
/flatted@3.3.2:
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
@ -10356,6 +10376,13 @@ packages:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
/is-plain-object@2.0.4:
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
engines: {node: '>=0.10.0'}
dependencies:
isobject: 3.0.1
dev: true
/is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
dev: true
@ -10456,6 +10483,11 @@ packages:
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
/isobject@3.0.1:
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
engines: {node: '>=0.10.0'}
dev: true
/isomorphic-rslog@0.0.6:
resolution: {integrity: sha512-HM0q6XqQ93psDlqvuViNs/Ea3hAyGDkIdVAHlrEocjjAwGrs1fZ+EdQjS9eUPacnYB7Y8SoDdSY3H8p3ce205A==}
engines: {node: '>=14.17.6'}
@ -12585,6 +12617,17 @@ packages:
dependencies:
safe-buffer: 5.2.1
/raw-loader@4.0.2(webpack@5.76.0):
resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==}
engines: {node: '>= 10.13.0'}
peerDependencies:
webpack: ^4.0.0 || ^5.0.0
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.76.0
dev: true
/react-devtools-inline@4.4.0:
resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==}
dependencies:
@ -13524,6 +13567,13 @@ packages:
functions-have-names: 1.2.3
has-property-descriptors: 1.0.2
/shallow-clone@3.0.1:
resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==}
engines: {node: '>=8'}
dependencies:
kind-of: 6.0.3
dev: true
/shallowequal@1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
@ -14801,6 +14851,15 @@ packages:
engines: {node: '>=12'}
dev: true
/webpack-merge@5.10.0:
resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==}
engines: {node: '>=10.0.0'}
dependencies:
clone-deep: 4.0.1
flat: 5.0.2
wildcard: 2.0.1
dev: true
/webpack-sources@3.2.3:
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
engines: {node: '>=10.13.0'}
@ -14936,6 +14995,10 @@ packages:
stackback: 0.0.2
dev: true
/wildcard@2.0.1:
resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
dev: true
/word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}