chore: import upstream snapshot with attribution
CI / build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:09:51 +08:00
commit 0232b4e2bb
3528 changed files with 291404 additions and 0 deletions
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
.array-item-wrapper {
display: flex;
align-items: center;
}
.icon-button-popover {
padding: 6px 8px;
}
@@ -0,0 +1,121 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DataEvent,
EffectFuncProps,
Field,
FieldRenderProps,
FormMeta,
ValidateTrigger,
WorkflowNodeRegistry,
FieldArray,
FieldArrayRenderProps,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input, Button, Popover } from '@douyinfe/semi-ui';
import { IconPlus, IconCrossCircleStroked, IconArrowDown } from '@douyinfe/semi-icons';
import './index.css';
import '../index.css';
export const render = () => (
<div className="demo-node-content">
<div className="demo-node-title">Array Examples</div>
<FieldArray name="array">
{({ field, fieldState }: FieldArrayRenderProps<string>) => (
<FieldWrapper title={'My Array'}>
{field.map((child, index) => (
<Field name={child.name} key={child.key}>
{({ field: childField, fieldState: childState }: FieldRenderProps<string>) => (
<FieldWrapper error={childState.errors?.[0]?.message}>
<div className="array-item-wrapper">
<Input {...childField} size={'small'} />
{index < field.value!.length - 1 ? (
<Popover
content={'swap with next element'}
className={'icon-button-popover'}
showArrow
position={'topLeft'}
>
<Button
theme="borderless"
size={'small'}
icon={<IconArrowDown />}
onClick={() => field.swap(index, index + 1)}
/>
</Popover>
) : null}
<Popover
content={'delete current element'}
className={'icon-button-popover'}
showArrow
position={'topLeft'}
>
<Button
theme="borderless"
size={'small'}
icon={<IconCrossCircleStroked />}
onClick={() => field.delete(index)}
/>
</Popover>
</div>
</FieldWrapper>
)}
</Field>
))}
<div>
<Button
size={'small'}
theme="borderless"
icon={<IconPlus />}
onClick={() => field.append('default')}
>
Add
</Button>
</div>
</FieldWrapper>
)}
</FieldArray>
</div>
);
interface FormData {
array: string[];
}
const formMeta: FormMeta<FormData> = {
render,
validateTrigger: ValidateTrigger.onChange,
defaultValues: {
array: ['default'],
},
validate: {
'array.*': ({ value }) =>
value.length > 8 ? 'max length exceeded: current length is ' + value.length : undefined,
},
effect: {
'array.*': [
{
event: DataEvent.onValueInit,
effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
console.log(name + ' value init to ', value);
},
},
{
event: DataEvent.onValueChange,
effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
console.log(name + ' value changed to ', value);
},
},
],
},
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
@@ -0,0 +1,151 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DEFAULT_INITIAL_DATA,
defaultInitialDataTs,
fieldWrapperCss,
fieldWrapperTs,
} from '@flowgram.ai/demo-node-form';
import { Editor } from '../editor.tsx';
import { PreviewEditor } from '../../preview-editor.tsx';
import { nodeRegistry } from './node-registry.tsx';
const nodeRegistryFile = {
code: `import {
DataEvent,
EffectFuncProps,
Field,
FieldRenderProps,
FormMeta,
ValidateTrigger,
WorkflowNodeRegistry,
FieldArray,
FieldArrayRenderProps,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input, Button, Popover } from '@douyinfe/semi-ui';
import { IconPlus, IconCrossCircleStroked, IconArrowDown } from '@douyinfe/semi-icons';
import './index.css';
import '../index.css';
export const render = () => (
<div className="demo-node-content">
<div className="demo-node-title">Array Examples</div>
<FieldArray name="array">
{({ field, fieldState }: FieldArrayRenderProps<string>) => (
<FieldWrapper title={'My Array'}>
{field.map((child, index) => (
<Field name={child.name} key={child.key}>
{({ field: childField, fieldState: childState }: FieldRenderProps<string>) => (
<FieldWrapper error={childState.errors?.[0]?.message}>
<div className="array-item-wrapper">
<Input {...childField} size={'small'} />
{index < field.value!.length - 1 ? (
<Popover
content={'swap with next element'}
className={'icon-button-popover'}
showArrow
position={'topLeft'}
>
<Button
theme="borderless"
size={'small'}
icon={<IconArrowDown />}
onClick={() => field.swap(index, index + 1)}
/>
</Popover>
) : null}
<Popover
content={'delete current element'}
className={'icon-button-popover'}
showArrow
position={'topLeft'}
>
<Button
theme="borderless"
size={'small'}
icon={<IconCrossCircleStroked />}
onClick={() => field.delete(index)}
/>
</Popover>
</div>
</FieldWrapper>
)}
</Field>
))}
<div>
<Button
size={'small'}
theme="borderless"
icon={<IconPlus />}
onClick={() => field.append('default')}
>
Add
</Button>
</div>
</FieldWrapper>
)}
</FieldArray>
</div>
);
interface FormData {
array: string[];
}
const formMeta: FormMeta<FormData> = {
render,
validateTrigger: ValidateTrigger.onChange,
defaultValues: {
array: ['default'],
},
validate: {
'array.*': ({ value }) =>
value.length > 8 ? 'max length exceeded: current length is ' + value.length : undefined,
},
effect: {
'array.*': [
{
event: DataEvent.onValueInit,
effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
console.log(name + ' value init to ', value);
},
},
{
event: DataEvent.onValueChange,
effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
console.log(name + ' value changed to ', value);
},
},
],
},
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
`,
active: true,
};
export const NodeFormArrayPreview = () => {
const files = {
'node-registry.tsx': nodeRegistryFile,
'initial-data.ts': { code: defaultInitialDataTs, active: true },
'field-wrapper.tsx': { code: fieldWrapperTs, active: true },
'field-wrapper.css': { code: fieldWrapperCss, active: true },
};
return (
<PreviewEditor files={files} previewStyle={{ height: 500 }} editorStyle={{ height: 500 }}>
<Editor registries={[nodeRegistry]} initialData={DEFAULT_INITIAL_DATA} />
</PreviewEditor>
);
};
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DEFAULT_DEMO_REGISTRY,
DEFAULT_INITIAL_DATA,
defaultInitialDataTs,
fieldWrapperCss,
fieldWrapperTs,
} from '@flowgram.ai/demo-node-form';
import { PreviewEditor } from '../preview-editor';
import { Editor } from './editor';
const registryCode = {
code: `import {
Field,
FieldRenderProps,
FormMeta,
ValidateTrigger,
} from '@flowgram.ai/free-layout-editor';
import { Input } from '@douyinfe/semi-ui';
// FieldWrapper is not provided by sdk, it can be customized
import { FieldWrapper } from './components';
const render = () => (
<div className="demo-node-content">
<div className="demo-node-title">Basic Node</div>
<Field name="name">
{({ field, fieldState }: FieldRenderProps<string>) => (
<FieldWrapper required title="Name" error={fieldState.errors?.[0]?.message}>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="city">
{({ field, fieldState }: FieldRenderProps<string>) => (
<FieldWrapper required title="City" error={fieldState.errors?.[0]?.message}>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
</div>
);
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,
};
export const NodeFormBasicPreview = () => {
const files = {
'node-registry.tsx': registryCode,
'initial-data.ts': { code: defaultInitialDataTs, active: true },
'field-wrapper.tsx': { code: fieldWrapperTs, active: true },
'field-wrapper.css': { code: fieldWrapperCss, active: true },
};
return (
<PreviewEditor files={files} previewStyle={{ height: 500 }} editorStyle={{ height: 500 }}>
<Editor registry={DEFAULT_DEMO_REGISTRY} initialData={DEFAULT_INITIAL_DATA} />
</PreviewEditor>
);
};
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
Field,
FieldRenderProps,
FormMeta,
WorkflowNodeRegistry,
FormRenderProps,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input } from '@douyinfe/semi-ui';
import '../index.css';
interface FormData {
country: string;
city: string;
}
const render = ({ form }: FormRenderProps<FormData>) => (
<div className="demo-node-content">
<div className="demo-node-title">Visibility Examples</div>
<Field name="country">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper title="Country">
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="city" deps={['country']}>
{({ field }: FieldRenderProps<string>) =>
form.getValueIn('country') ? (
<FieldWrapper title="City">
<Input size={'small'} {...field} />
</FieldWrapper>
) : (
<></>
)
}
</Field>
</div>
);
const formMeta: FormMeta<FormData> = {
render,
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DEFAULT_INITIAL_DATA,
defaultInitialDataTs,
fieldWrapperCss,
fieldWrapperTs,
} from '@flowgram.ai/demo-node-form';
import { Editor } from '../editor.tsx';
import { PreviewEditor } from '../../preview-editor.tsx';
import { nodeRegistry } from './node-registry.tsx';
const nodeRegistryFile = {
code: `import {
Field,
FieldRenderProps,
FormMeta,
WorkflowNodeRegistry,
FormRenderProps,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input } from '@douyinfe/semi-ui';
import '../index.css';
interface FormData {
country: string;
city: string;
}
const render = ({ form }: FormRenderProps<FormData>) => (
<div className="demo-node-content">
<div className="demo-node-title">Visibility Examples</div>
<Field name="country">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper title="Country">
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="city" deps={['country']}>
{({ field }: FieldRenderProps<string>) =>
form.getValueIn('country') ? (
<FieldWrapper title="City">
<Input size={'small'} {...field} />
</FieldWrapper>
) : (
<></>
)
}
</Field>
</div>
);
const formMeta: FormMeta<FormData> = {
render,
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
`,
active: true,
};
export const NodeFormDynamicPreview = () => {
const files = {
'node-registry.tsx': nodeRegistryFile,
'initial-data.ts': { code: defaultInitialDataTs, active: true },
'field-wrapper.tsx': { code: fieldWrapperTs, active: true },
'field-wrapper.css': { code: fieldWrapperCss, active: true },
};
return (
<PreviewEditor files={files} previewStyle={{ height: 500 }} editorStyle={{ height: 500 }}>
<Editor registries={[nodeRegistry]} initialData={DEFAULT_INITIAL_DATA} />
</PreviewEditor>
);
};
+15
View File
@@ -0,0 +1,15 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import React from 'react';
// https://github.com/web-infra-dev/rspress/issues/553
const Editor = React.lazy(() =>
import('@flowgram.ai/demo-node-form').then((module) => ({
default: module.Editor,
}))
);
export { Editor };
@@ -0,0 +1,87 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DataEvent,
EffectFuncProps,
Field,
FieldRenderProps,
FormMeta,
ValidateTrigger,
WorkflowNodeRegistry,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input } from '@douyinfe/semi-ui';
import '../index.css';
const render = () => (
<div className="demo-node-content">
<div className="demo-node-title">Effect Examples</div>
<Field name="field1">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper
title="Basic effect"
note={'The following field will console.log field value on value change'}
>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="field2">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper
title="Control other fields"
note={'The following field will change Field 3 value on value change'}
>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="field3">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper title="Field 3">
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
</div>
);
interface FormData {
field1: string;
field2: string;
field3: string;
}
const formMeta: FormMeta<FormData> = {
render,
validateTrigger: ValidateTrigger.onChange,
effect: {
field1: [
{
event: DataEvent.onValueChange,
effect: ({ value }: EffectFuncProps<string, FormData>) => {
console.log('field1 value:', value);
},
},
],
field2: [
{
event: DataEvent.onValueChange,
effect: ({ value, form }: EffectFuncProps<string, FormData>) => {
form.setValueIn('field3', 'field2 value is ' + value);
},
},
],
},
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
@@ -0,0 +1,117 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
import {
DEFAULT_INITIAL_DATA,
defaultInitialDataTs,
fieldWrapperCss,
fieldWrapperTs,
} from '@flowgram.ai/demo-node-form';
import { Editor } from '../editor.tsx';
import { PreviewEditor } from '../../preview-editor.tsx';
import { nodeRegistry } from './node-registry.tsx';
const nodeRegistryFile = {
code: `import {
DataEvent,
EffectFuncProps,
Field,
FieldRenderProps,
FormMeta,
ValidateTrigger,
WorkflowNodeRegistry,
} from '@flowgram.ai/free-layout-editor';
import { FieldWrapper } from '@flowgram.ai/demo-node-form';
import { Input } from '@douyinfe/semi-ui';
import '../index.css';
const render = () => (
<div className="demo-node-content">
<div className="demo-node-title">Effect Examples</div>
<Field name="field1">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper
title="Basic effect"
note={'The following field will console.log field value on value change'}
>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="field2">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper
title="Control other fields"
note={'The following field will change Field 3 value on value change'}
>
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
<Field name="field3">
{({ field }: FieldRenderProps<string>) => (
<FieldWrapper title="Field 3">
<Input size={'small'} {...field} />
</FieldWrapper>
)}
</Field>
</div>
);
interface FormData {
field1: string;
field2: string;
field3: string;
}
const formMeta: FormMeta<FormData> = {
render,
validateTrigger: ValidateTrigger.onChange,
effect: {
field1: [
{
event: DataEvent.onValueChange,
effect: ({ value }: EffectFuncProps<string, FormData>) => {
console.log('field1 value:', value);
},
},
],
field2: [
{
event: DataEvent.onValueChange,
effect: ({ value, form }: EffectFuncProps<string, FormData>) => {
form.setValueIn('field3', 'field2 value is ' + value);
},
},
],
},
};
export const nodeRegistry: WorkflowNodeRegistry = {
type: 'custom',
meta: {},
defaultPorts: [{ type: 'output' }, { type: 'input' }],
formMeta,
};
`,
active: true,
};
export const NodeFormEffectPreview = () => {
const files = {
'node-registry.tsx': nodeRegistryFile,
'initial-data.ts': { code: defaultInitialDataTs, active: true },
'field-wrapper.tsx': { code: fieldWrapperTs, active: true },
'field-wrapper.css': { code: fieldWrapperCss, active: true },
};
return (
<PreviewEditor files={files} previewStyle={{ height: 500 }} editorStyle={{ height: 500 }}>
<Editor registries={[nodeRegistry]} initialData={DEFAULT_INITIAL_DATA} />
</PreviewEditor>
);
};
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
.demo-node-content {
padding: 8px 12px;
flex-grow: 1;
min-width: 300px;
}
.demo-node-title {
font-weight: 500;
font-size: 14px;
width: 100%;
margin: 4px 0px 12px 0px;
}
+8
View File
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
* SPDX-License-Identifier: MIT
*/
export { NodeFormBasicPreview } from './basic-preview';
export { NodeFormEffectPreview } from './effect/preview';
export { NodeFormDynamicPreview } from './dynamic/preview';