This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
Reference in New Issue
Block a user