diff --git a/apps/demo-free-layout/package.json b/apps/demo-free-layout/package.json index de73eb02..d02ffcfd 100644 --- a/apps/demo-free-layout/package.json +++ b/apps/demo-free-layout/package.json @@ -49,7 +49,7 @@ "@types/node": "^18", "@types/react": "^18", "@types/react-dom": "^18", - "@types/styled-components": "^5", + "styled-components": "^5", "@typescript-eslint/parser": "^6.10.0", "eslint": "^8.54.0", "less": "^4.1.2", diff --git a/apps/demo-node-form/package.json b/apps/demo-node-form/package.json index b582fb1b..f4e53eb2 100644 --- a/apps/demo-node-form/package.json +++ b/apps/demo-node-form/package.json @@ -34,7 +34,8 @@ "@flowgram.ai/free-snap-plugin": "workspace:*", "@flowgram.ai/minimap-plugin": "workspace:*", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "styled-components": "^5" }, "devDependencies": { "@flowgram.ai/ts-config": "workspace:*", diff --git a/apps/demo-node-form/src/components/field-title.tsx b/apps/demo-node-form/src/components/field-title.tsx new file mode 100644 index 00000000..3bc680d9 --- /dev/null +++ b/apps/demo-node-form/src/components/field-title.tsx @@ -0,0 +1,5 @@ +import styled from 'styled-components'; + +export const FieldTitle = styled.div` + padding-bottom: 4px; +`; diff --git a/apps/demo-node-form/src/components/field-wrapper.css b/apps/demo-node-form/src/components/field-wrapper.css new file mode 100644 index 00000000..57de555c --- /dev/null +++ b/apps/demo-node-form/src/components/field-wrapper.css @@ -0,0 +1,18 @@ +.error-message { + color: #f5222d !important; +} + +.required { + color: #f5222d !important; + padding-left: 4px +} + +.field-wrapper { + width: 100%; + margin-bottom: 12px; +} + +.field-title { + margin-bottom: 6px; +} + diff --git a/apps/demo-node-form/src/components/field-wrapper.tsx b/apps/demo-node-form/src/components/field-wrapper.tsx new file mode 100644 index 00000000..7e26c9e7 --- /dev/null +++ b/apps/demo-node-form/src/components/field-wrapper.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import './field-wrapper.css'; + +interface FieldWrapperProps { + required?: boolean; + title: string; + children?: React.ReactNode; + error?: string; +} + +export const FieldWrapper = ({ required, title, children, error }: FieldWrapperProps) => ( +
+
+ {title} + {required ? * : null} +
+ {children} +

{error}

+
+); diff --git a/apps/demo-node-form/src/components/index.ts b/apps/demo-node-form/src/components/index.ts new file mode 100644 index 00000000..f04829e6 --- /dev/null +++ b/apps/demo-node-form/src/components/index.ts @@ -0,0 +1,2 @@ +export { FieldTitle } from './field-title'; +export { FieldWrapper } from './field-wrapper'; diff --git a/apps/demo-node-form/src/constant.ts b/apps/demo-node-form/src/constant.ts new file mode 100644 index 00000000..7cf14596 --- /dev/null +++ b/apps/demo-node-form/src/constant.ts @@ -0,0 +1,41 @@ +export const fieldWrapperTs = `import React from 'react'; + +import './index.css'; + +interface FieldWrapperProps { + required?: boolean; + title: string; + children?: React.ReactNode; + error?: string; +} + +export const FieldWrapper = ({ required, title, children, error }: FieldWrapperProps) => ( +
+
+ {title} + {required ? * : null} +
+ {children} +

{error}

+
+); +`; + +export const fieldWrapperCss = `.error-message { + color: #f5222d !important; +} + +.required { + color: #f5222d !important; + padding-left: 4px +} + +.field-wrapper { + width: 100%; + margin-bottom: 12px; +} + +.field-title { + margin-bottom: 6px; +} +`; diff --git a/apps/demo-node-form/src/form-meta.tsx b/apps/demo-node-form/src/form-meta.tsx new file mode 100644 index 00000000..8cf213e0 --- /dev/null +++ b/apps/demo-node-form/src/form-meta.tsx @@ -0,0 +1,51 @@ +import { + Field, + FieldRenderProps, + FormMeta, + ValidateTrigger, +} from '@flowgram.ai/free-layout-editor'; +import { Input } from '@douyinfe/semi-ui'; + +// FieldWrapper is not provided by sdk, and can be customized +import { FieldWrapper } from './components'; + +const render = () => ( +
+
Basic Node
+ + {({ field, fieldState }: FieldRenderProps) => ( + + + + )} + + + + {({ field, fieldState }: FieldRenderProps) => ( + + + + )} + +
+); + +const formMeta: FormMeta = { + render, + defaultValues: { name: 'Tina', city: 'Hangzhou' }, + validateTrigger: ValidateTrigger.onChange, + validate: { + name: ({ value }) => { + if (!value) { + return 'Name is required'; + } + }, + city: ({ value }) => { + if (!value) { + return 'City is required'; + } + }, + }, +}; + +export const DEFAULT_FORM_META = formMeta; diff --git a/apps/demo-node-form/src/index.css b/apps/demo-node-form/src/index.css index c76cebfb..15728111 100644 --- a/apps/demo-node-form/src/index.css +++ b/apps/demo-node-form/src/index.css @@ -11,11 +11,17 @@ box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1); } -.demo-free-node-title { - background-color: #93bfe2; +.demo-node-content { + padding: 8px 12px; + flex-grow: 1; width: 100%; - border-radius: 8px 8px 0 0; - padding: 4px 12px; +} + +.demo-node-title { + font-weight: 500; + font-size: 14px; + width: 100%; + margin: 4px 0px 12px 0px; } .demo-free-node-content { padding: 4px 12px; diff --git a/apps/demo-node-form/src/index.tsx b/apps/demo-node-form/src/index.tsx index 2f6832bf..f99f9f87 100644 --- a/apps/demo-node-form/src/index.tsx +++ b/apps/demo-node-form/src/index.tsx @@ -1 +1,6 @@ export { Editor } from './editor'; +export { FieldTitle, FieldWrapper } from './components'; +export { DEFAULT_FORM_META } from './form-meta'; +export { DEFAULT_DEMO_REGISTRY } from './node-registries'; +export { DEFAULT_INITIAL_DATA } from './initial-data'; +export { fieldWrapperTs, fieldWrapperCss } from './constant'; diff --git a/apps/demo-node-form/src/initial-data.ts b/apps/demo-node-form/src/initial-data.ts index b2e76742..0a0c8cdb 100644 --- a/apps/demo-node-form/src/initial-data.ts +++ b/apps/demo-node-form/src/initial-data.ts @@ -8,10 +8,6 @@ export const DEFAULT_INITIAL_DATA: WorkflowJSON = { meta: { position: { x: 400, y: 0 }, }, - data: { - title: 'Custom', - content: 'Custom node content', - }, }, ], edges: [], diff --git a/apps/demo-node-form/src/node-registries.tsx b/apps/demo-node-form/src/node-registries.tsx index 21edbcbd..1afa4cab 100644 --- a/apps/demo-node-form/src/node-registries.tsx +++ b/apps/demo-node-form/src/node-registries.tsx @@ -1,23 +1,10 @@ -import { WorkflowNodeRegistry, Field } from '@flowgram.ai/free-layout-editor'; -import { Input, TextArea } from '@douyinfe/semi-ui'; +import { WorkflowNodeRegistry } from '@flowgram.ai/free-layout-editor'; + +import { DEFAULT_FORM_META } from './form-meta'; export const DEFAULT_DEMO_REGISTRY: WorkflowNodeRegistry = { type: 'custom', meta: {}, defaultPorts: [{ type: 'output' }, { type: 'input' }], - formMeta: { - render: () => ( -
-
Basic Node
-

name

- - - -

city

- - - -
- ), - }, + formMeta: DEFAULT_FORM_META, }; diff --git a/apps/docs/components/form-basic/preview.tsx b/apps/docs/components/form-basic/preview.tsx index aa21182d..97742096 100644 --- a/apps/docs/components/form-basic/preview.tsx +++ b/apps/docs/components/form-basic/preview.tsx @@ -1,42 +1,104 @@ +import { + DEFAULT_DEMO_REGISTRY, + DEFAULT_INITIAL_DATA, + fieldWrapperCss, + fieldWrapperTs, +} from '@flowgram.ai/demo-node-form'; + import { PreviewEditor } from '../preview-editor'; import { Editor } from '.'; -const indexCode = { +const registryCode = { code: `import { - EditorRenderer, - FreeLayoutEditorProvider, + Field, + FieldRenderProps, + FormMeta, + ValidateTrigger, } from '@flowgram.ai/free-layout-editor'; +import { Input } from '@douyinfe/semi-ui'; -import { useEditorProps } from './hooks/use-editor-props' -import '@flowgram.ai/free-layout-editor/index.css'; -import './index.css'; +// FieldWrapper is not provided by sdk, it can be customized +import { FieldWrapper } from './components'; -export const App = () => { - const editorProps = useEditorProps() - return ( - -
-
- - -
- - -
-
- ) +const render = () => ( +
+
Basic Node
+ + {({ field, fieldState }: FieldRenderProps) => ( + + + + )} + + + + {({ field, fieldState }: FieldRenderProps) => ( + + + + )} + +
+); + +const formMeta: FormMeta = { + render, + defaultValues: { name: 'Tina', city: 'Hangzhou' }, + validateTrigger: ValidateTrigger.onChange, + validate: { + name: ({ value }) => { + if (!value) { + return 'Name is required'; + } + }, + city: ({ value }) => { + if (!value) { + return 'City is required'; + } + } + } }; - `, + + + +export const nodeRegistry: WorkflowNodeRegistry = { + type: 'custom', + meta: {}, + defaultPorts: [{ type: 'output' }, { type: 'input' }], + formMeta +}; +`, + active: true, +}; + +const initialDataCode = { + code: `import { WorkflowJSON } from '@flowgram.ai/free-layout-editor'; + +export const DEFAULT_INITIAL_DATA: WorkflowJSON = { + nodes: [ + { + id: 'node_0', + type: 'custom', + meta: { + position: { x: 400, y: 0 }, + }, + }, + ], + edges: [], +};`, active: true, }; export const NodeFormBasicPreview = () => { const files = { - 'index.tsx': indexCode, + 'node-registry.tsx': registryCode, + 'initial-data.ts': initialDataCode, + 'field-wrapper.tsx': { code: fieldWrapperTs, active: true }, + 'field-wrapper.css': { code: fieldWrapperCss, active: true }, }; return ( - + ); }; diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 51f69125..b612c91b 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -245,9 +245,6 @@ importers: '@types/react-dom': specifier: ^18 version: 18.3.5(@types/react@18.3.16) - '@types/styled-components': - specifier: ^5 - version: 5.1.34 '@typescript-eslint/parser': specifier: ^6.10.0 version: 6.21.0(eslint@8.57.1)(typescript@5.0.4) @@ -333,6 +330,9 @@ importers: react-dom: specifier: ^18 version: 18.3.1(react@18.3.1) + styled-components: + specifier: ^5 + version: 5.3.11(@babel/core@7.26.0)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1) devDependencies: '@flowgram.ai/eslint-config': specifier: workspace:*