Просмотр исходного кода

feat(docs): add demo-playground example to docs (#537)

* feat(docs): add demo-playground example to docs

* chore: update license header

* refactor(docs): infinite-canvas -> playground
xiamidaxia 5 месяцев назад
Родитель
Сommit
c8618ccea5

+ 3 - 2
apps/create-app/src/index.ts

@@ -44,14 +44,15 @@ const main = async () => {
           { name: 'Fixed Layout Demo Simple', value: 'demo-fixed-layout-simple' },
           { name: 'Fixed Layout Demo Simple', value: 'demo-fixed-layout-simple' },
           { name: 'Free Layout Demo Simple', value: 'demo-free-layout-simple' },
           { name: 'Free Layout Demo Simple', value: 'demo-free-layout-simple' },
           { name: 'Free Layout Nextjs Demo', value: 'demo-nextjs' },
           { name: 'Free Layout Nextjs Demo', value: 'demo-nextjs' },
-          { name: 'Free Layout Vite Demo Simple', value: 'demo-vite' }
+          { name: 'Free Layout Vite Demo Simple', value: 'demo-vite' },
+          { name: 'Demo Playground for infinite canvas', value: 'demo-playground' }
         ],
         ],
       },
       },
     ]);
     ]);
 
 
     folderName = repo;
     folderName = repo;
   } else {
   } else {
-    if (['fixed-layout', 'free-layout', 'fixed-layout-simple', 'free-layout-simple', 'nextjs'].includes(args[0])) {
+    if (['fixed-layout', 'free-layout', 'fixed-layout-simple', 'free-layout-simple', 'playground', 'nextjs'].includes(args[0])) {
       folderName = `demo-${args[0]}`;
       folderName = `demo-${args[0]}`;
     } else {
     } else {
       console.error('Invalid argument. Please run "npx create-app" to choose demo.');
       console.error('Invalid argument. Please run "npx create-app" to choose demo.');

+ 1 - 1
apps/demo-playground/rsbuild.config.ts

@@ -10,7 +10,7 @@ export default defineConfig({
   plugins: [pluginReact()],
   plugins: [pluginReact()],
   source: {
   source: {
     entry: {
     entry: {
-      index: './src/index.tsx',
+      index: './src/app.tsx',
     },
     },
   },
   },
   html: {
   html: {

+ 12 - 0
apps/demo-playground/src/app.tsx

@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { createRoot } from 'react-dom/client';
+
+import { PlaygroundEditor } from './editor';
+
+const app = createRoot(document.getElementById('root')!);
+
+app.render(<PlaygroundEditor />);

+ 23 - 11
apps/demo-playground/src/components/card.tsx

@@ -7,7 +7,7 @@ import React, { useCallback, useState } from 'react';
 
 
 import { usePlayground, usePlaygroundDrag } from '@flowgram.ai/playground-react';
 import { usePlayground, usePlaygroundDrag } from '@flowgram.ai/playground-react';
 
 
-export function Card() {
+export function StaticCard() {
   return (
   return (
     <div
     <div
       style={{
       style={{
@@ -15,16 +15,23 @@ export function Card() {
         height: 100,
         height: 100,
         position: 'absolute',
         position: 'absolute',
         color: 'white',
         color: 'white',
-        backgroundColor: 'red',
-        left: 500,
-        top: 500,
+        backgroundColor: 'gray',
+        left: 200,
+        top: 200,
+        alignItems: 'center',
+        justifyContent: 'center',
+        display: 'flex',
       }}
       }}
-    ></div>
+    >
+      {' '}
+      Static Card
+    </div>
   );
   );
 }
 }
 
 
 export function DragableCard() {
 export function DragableCard() {
-  const [pos, setPos] = useState({ x: 200, y: 100 });
+  const [pos, setPos] = useState({ x: 100, y: 50 });
+  // Used for dragging, the canvas will automatically scroll when dragged to the edge
   // 用于拖拽,拖拽到边缘时候会自动滚动画布
   // 用于拖拽,拖拽到边缘时候会自动滚动画布
   const dragger = usePlaygroundDrag();
   const dragger = usePlaygroundDrag();
   const playground = usePlayground();
   const playground = usePlayground();
@@ -32,20 +39,19 @@ export function DragableCard() {
     (e: React.MouseEvent) => {
     (e: React.MouseEvent) => {
       const startPos = { x: pos.x, y: pos.y };
       const startPos = { x: pos.x, y: pos.y };
       dragger.start(e, {
       dragger.start(e, {
+        // start Drag
         onDragStart() {
         onDragStart() {
           playground.config.grabDisable = true;
           playground.config.grabDisable = true;
-          // start drag
         },
         },
         onDrag(dragEvent) {
         onDrag(dragEvent) {
-          // 需要 除去当前的缩放比例
           setPos({
           setPos({
             x: startPos.x + (dragEvent.endPos.x - dragEvent.startPos.x) / dragEvent.scale,
             x: startPos.x + (dragEvent.endPos.x - dragEvent.startPos.x) / dragEvent.scale,
             y: startPos.y + (dragEvent.endPos.y - dragEvent.startPos.y) / dragEvent.scale,
             y: startPos.y + (dragEvent.endPos.y - dragEvent.startPos.y) / dragEvent.scale,
           });
           });
         },
         },
+        // end drag
         onDragEnd() {
         onDragEnd() {
           playground.config.grabDisable = false;
           playground.config.grabDisable = false;
-          // end drag
         },
         },
       });
       });
       // e.stopPropagation();
       // e.stopPropagation();
@@ -62,10 +68,16 @@ export function DragableCard() {
         height: 100,
         height: 100,
         position: 'absolute',
         position: 'absolute',
         color: 'white',
         color: 'white',
-        backgroundColor: 'blue',
+        backgroundColor: '#0089ff',
         left: pos.x,
         left: pos.x,
         top: pos.y,
         top: pos.y,
+        alignItems: 'center',
+        justifyContent: 'center',
+        display: 'flex',
       }}
       }}
-    ></div>
+    >
+      {' '}
+      Draggable Card
+    </div>
   );
   );
 }
 }

+ 6 - 3
apps/demo-playground/src/components/playground-tools.tsx

@@ -14,8 +14,8 @@ export const PlaygroundTools: React.FC<{ minZoom?: number; maxZoom?: number }> =
       style={{
       style={{
         position: 'absolute',
         position: 'absolute',
         zIndex: 100,
         zIndex: 100,
-        right: 100,
-        bottom: 100,
+        right: 40,
+        bottom: 40,
         padding: 13,
         padding: 13,
         border: '1px solid #ccc',
         border: '1px solid #ccc',
         backgroundColor: 'white',
         backgroundColor: 'white',
@@ -24,9 +24,12 @@ export const PlaygroundTools: React.FC<{ minZoom?: number; maxZoom?: number }> =
         cursor: 'pointer',
         cursor: 'pointer',
       }}
       }}
     >
     >
-      <button onClick={() => tools.toggleIneractiveType()}>{tools.interactiveType}</button>
+      <button onClick={() => tools.toggleIneractiveType()}>{tools.interactiveType} Mode</button>
+      &nbsp;
       <button onClick={() => tools.zoomout()}>Zoom Out</button>
       <button onClick={() => tools.zoomout()}>Zoom Out</button>
+      &nbsp;
       <button onClick={() => tools.zoomin()}>Zoom In</button>
       <button onClick={() => tools.zoomin()}>Zoom In</button>
+      &nbsp;
       <span>{Math.floor(tools.zoom * 100)}%</span>
       <span>{Math.floor(tools.zoom * 100)}%</span>
     </div>
     </div>
   );
   );

+ 74 - 0
apps/demo-playground/src/editor.tsx

@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import { useMemo } from 'react';
+
+import {
+  Command,
+  PlaygroundReact,
+  PlaygroundReactContent,
+  PlaygroundReactProps,
+} from '@flowgram.ai/playground-react';
+
+import { PlaygroundTools } from './components/playground-tools';
+import { StaticCard, DragableCard } from './components/card';
+
+// Load style
+import '@flowgram.ai/playground-react/index.css';
+
+/**
+ * The ability to zoom to provide an infinite canvas
+ */
+export function PlaygroundEditor(props: { className?: string }) {
+  const playgroundProps = useMemo<PlaygroundReactProps>(
+    () => ({
+      background: true, // Background available
+      playground: {
+        ineractiveType: 'PAD', // MOUSE | PAD
+      },
+      // 自定义快捷键
+      shortcuts(registry, ctx) {
+        registry.addHandlers(
+          /**
+           * Zoom In
+           */
+          {
+            commandId: Command.Default.ZOOM_IN,
+            shortcuts: ['meta =', 'ctrl ='],
+            execute: () => {
+              ctx.playground.config.zoomin();
+            },
+          },
+          /**
+           * Zoom Out
+           */
+          {
+            commandId: Command.Default.ZOOM_OUT,
+            shortcuts: ['meta -', 'ctrl -'],
+            execute: () => {
+              ctx.playground.config.zoomout();
+            },
+          }
+        );
+      },
+    }),
+    []
+  );
+  /*
+   * PlaygroundReact: Canvas React containers 画布 react 容器
+   * PlaygroundReactContent: The canvas content will be scaled accordingly 画布内容,会跟着缩放
+   */
+  return (
+    <div className={props.className}>
+      <PlaygroundReact {...playgroundProps}>
+        <PlaygroundReactContent>
+          <StaticCard />
+          <DragableCard />
+        </PlaygroundReactContent>
+        <PlaygroundTools />
+      </PlaygroundReact>
+    </div>
+  );
+}

+ 1 - 73
apps/demo-playground/src/index.tsx

@@ -3,76 +3,4 @@
  * SPDX-License-Identifier: MIT
  * SPDX-License-Identifier: MIT
  */
  */
 
 
-import { useMemo } from 'react';
-
-import { createRoot } from 'react-dom/client';
-import {
-  Command,
-  PlaygroundReact,
-  PlaygroundReactContent,
-  PlaygroundReactProps,
-} from '@flowgram.ai/playground-react';
-
-import { PlaygroundTools } from './components/playground-tools';
-import { Card, DragableCard } from './components/card';
-
-// 加载画布样式
-import '@flowgram.ai/playground-react/index.css';
-
-/**
- * 用于提供纯画布缩放能力
- */
-export function App() {
-  const playgroundProps = useMemo<PlaygroundReactProps>(
-    () => ({
-      // 是否增加背景
-      background: true,
-      playground: {
-        ineractiveType: 'MOUSE', // 鼠标模式, MOUSE | PAD
-      },
-      // 自定义快捷键
-      shortcuts(registry, ctx) {
-        registry.addHandlers(
-          /**
-           * 放大
-           */
-          {
-            commandId: Command.Default.ZOOM_IN,
-            shortcuts: ['meta =', 'ctrl ='],
-            execute: () => {
-              ctx.playground.config.zoomin();
-            },
-          },
-          /**
-           * 缩小
-           */
-          {
-            commandId: Command.Default.ZOOM_OUT,
-            shortcuts: ['meta -', 'ctrl -'],
-            execute: () => {
-              ctx.playground.config.zoomout();
-            },
-          }
-        );
-      },
-    }),
-    []
-  );
-  /*
-   * PlaygroundReact 画布 react 容器, background 属性可以关闭背景的点点点
-   * PlaygroundReactContent 画布内容,会跟着缩放
-   */
-  return (
-    <PlaygroundReact {...playgroundProps}>
-      <PlaygroundReactContent>
-        <Card />
-        <DragableCard />
-      </PlaygroundReactContent>
-      <PlaygroundTools />
-    </PlaygroundReact>
-  );
-}
-
-const app = createRoot(document.getElementById('root')!);
-
-app.render(<App />);
+export { PlaygroundEditor } from './editor';

+ 1 - 0
apps/docs/components/index.ts

@@ -11,3 +11,4 @@ export { FreeLayoutSimplePreview } from './free-layout-simple/preview';
 export { FixedLayoutSimple, CompositeNodesPreview } from './fixed-layout-simple';
 export { FixedLayoutSimple, CompositeNodesPreview } from './fixed-layout-simple';
 export { FixedLayoutSimplePreview } from './fixed-layout-simple/preview';
 export { FixedLayoutSimplePreview } from './fixed-layout-simple/preview';
 export { NodeFormBasicPreview, NodeFormEffectPreview, NodeFormDynamicPreview } from './node-form';
 export { NodeFormBasicPreview, NodeFormEffectPreview, NodeFormDynamicPreview } from './node-form';
+export { InfiniteCanvasPreview } from './infinite-canvas';

+ 15 - 0
apps/docs/components/infinite-canvas/index.less

@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+.doc-infinite-canvas-preview {
+  position: absolute;
+  width: 100%;
+  height: 500px;
+  button {
+    border: 1px solid #ccc;
+    padding: 4px 8px;
+    border-radius: 4px;
+  }
+}

+ 6 - 0
apps/docs/components/infinite-canvas/index.tsx

@@ -0,0 +1,6 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+export { InfiniteCanvasPreview } from './preview';

+ 15 - 0
apps/docs/components/infinite-canvas/infinite-canvas.tsx

@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React from 'react';
+import './index.less';
+
+const InfiniteCanvas = React.lazy(() =>
+  import('@flowgram.ai/demo-playground').then((module) => ({
+    default: module.PlaygroundEditor,
+  }))
+);
+
+export { InfiniteCanvas };

+ 36 - 0
apps/docs/components/infinite-canvas/preview.tsx

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+/* eslint-disable import/no-unresolved */
+import { PreviewEditor } from '../preview-editor';
+import { InfiniteCanvas } from './infinite-canvas.tsx';
+
+import editorCode from '!!raw-loader!@flowgram.ai/demo-playground/src/editor.tsx';
+import toolCode from '!!raw-loader!@flowgram.ai/demo-playground/src/components/playground-tools.tsx';
+import cardCode from '!!raw-loader!@flowgram.ai/demo-playground/src/components/card.tsx';
+
+export const InfiniteCanvasPreview = () => {
+  const files = {
+    'editor.tsx': {
+      active: true,
+      code: editorCode,
+    },
+    'playground-tools.tsx': {
+      code: toolCode,
+    },
+    'card.tsx': {
+      code: cardCode,
+    },
+  };
+  return (
+    <PreviewEditor
+      files={files}
+      previewStyle={{ height: 500, position: 'relative' }}
+      editorStyle={{ height: 500 }}
+    >
+      <InfiniteCanvas className="doc-infinite-canvas-preview" />
+    </PreviewEditor>
+  );
+};

+ 1 - 0
apps/docs/package.json

@@ -22,6 +22,7 @@
     "@flowgram.ai/demo-free-layout-simple": "workspace:*",
     "@flowgram.ai/demo-free-layout-simple": "workspace:*",
     "@flowgram.ai/demo-fixed-layout-simple": "workspace:*",
     "@flowgram.ai/demo-fixed-layout-simple": "workspace:*",
     "@flowgram.ai/demo-node-form": "workspace:*",
     "@flowgram.ai/demo-node-form": "workspace:*",
+    "@flowgram.ai/demo-playground": "workspace:*",
     "@flowgram.ai/fixed-layout-editor": "workspace:*",
     "@flowgram.ai/fixed-layout-editor": "workspace:*",
     "@flowgram.ai/fixed-semi-materials": "workspace:*",
     "@flowgram.ai/fixed-semi-materials": "workspace:*",
     "@flowgram.ai/free-layout-editor": "workspace:*",
     "@flowgram.ai/free-layout-editor": "workspace:*",

+ 1 - 0
apps/docs/src/en/examples/_meta.json

@@ -4,6 +4,7 @@
     "name": "index",
     "name": "index",
     "label": "Experience Environment"
     "label": "Experience Environment"
   },
   },
+  "playground",
   {
   {
     "type": "dir",
     "type": "dir",
     "name": "fixed-layout",
     "name": "fixed-layout",

+ 29 - 0
apps/docs/src/en/examples/playground.mdx

@@ -0,0 +1,29 @@
+---
+outline: false
+---
+
+
+# PlaygroundReact
+
+
+PlaygroundReact is the underlying module of fixed-layout and free-layout, which can be used separately, Features:
+
+- Infinite drag on the canvas
+- Mouse or touchpad gesture zooming
+- Built-in background plugin
+- Built-in shortcut plugin
+
+import { InfiniteCanvasPreview } from '../../../components';
+
+<InfiniteCanvasPreview />
+
+## Install
+
+```bash
+npx @flowgram.ai/create-app@latest playground
+```
+
+## Source Code
+
+https://github.com/bytedance/flowgram.ai/tree/main/apps/demo-playground
+

+ 1 - 0
apps/docs/src/zh/examples/_meta.json

@@ -4,6 +4,7 @@
     "name": "index",
     "name": "index",
     "label": "体验环境"
     "label": "体验环境"
   },
   },
+  "playground",
   {
   {
     "type": "dir",
     "type": "dir",
     "name": "fixed-layout",
     "name": "fixed-layout",

+ 28 - 0
apps/docs/src/zh/examples/playground.mdx

@@ -0,0 +1,28 @@
+---
+outline: false
+---
+
+
+# 无限画布
+
+PlaygroundReact 是固定布局和自由布局的底层模块,可以单独使用, 功能:
+
+- 无限拖动画布
+- 鼠标/触摸板手势缩放
+- 内置背景插件
+- 内置快捷键插件
+
+import { InfiniteCanvasPreview } from '../../../components';
+
+<InfiniteCanvasPreview />
+
+## 安装
+
+```bash
+npx @flowgram.ai/create-app@latest playground
+```
+
+## 源码
+
+https://github.com/bytedance/flowgram.ai/tree/main/apps/demo-playground
+

+ 3 - 0
common/config/rush/pnpm-lock.yaml

@@ -783,6 +783,9 @@ importers:
       '@flowgram.ai/demo-node-form':
       '@flowgram.ai/demo-node-form':
         specifier: workspace:*
         specifier: workspace:*
         version: link:../demo-node-form
         version: link:../demo-node-form
+      '@flowgram.ai/demo-playground':
+        specifier: workspace:*
+        version: link:../demo-playground
       '@flowgram.ai/fixed-layout-editor':
       '@flowgram.ai/fixed-layout-editor':
         specifier: workspace:*
         specifier: workspace:*
         version: link:../../packages/client/fixed-layout-editor
         version: link:../../packages/client/fixed-layout-editor