preview.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import {
  2. DEFAULT_INITIAL_DATA,
  3. defaultInitialDataTs,
  4. fieldWrapperCss,
  5. fieldWrapperTs,
  6. } from '@flowgram.ai/demo-node-form';
  7. import { Editor } from '../editor.tsx';
  8. import { PreviewEditor } from '../../preview-editor.tsx';
  9. import { nodeRegistry } from './node-registry.tsx';
  10. const nodeRegistryFile = {
  11. code: `import {
  12. DataEvent,
  13. EffectFuncProps,
  14. Field,
  15. FieldRenderProps,
  16. FormMeta,
  17. ValidateTrigger,
  18. WorkflowNodeRegistry,
  19. FieldArray,
  20. FieldArrayRenderProps,
  21. } from '@flowgram.ai/free-layout-editor';
  22. import { FieldWrapper } from '@flowgram.ai/demo-node-form';
  23. import { Input, Button, Popover } from '@douyinfe/semi-ui';
  24. import { IconPlus, IconCrossCircleStroked, IconArrowDown } from '@douyinfe/semi-icons';
  25. import './index.css';
  26. import '../index.css';
  27. export const render = () => (
  28. <div className="demo-node-content">
  29. <div className="demo-node-title">Array Examples</div>
  30. <FieldArray name="array">
  31. {({ field, fieldState }: FieldArrayRenderProps<string>) => (
  32. <FieldWrapper title={'My Array'}>
  33. {field.map((child, index) => (
  34. <Field name={child.name} key={child.key}>
  35. {({ field: childField, fieldState: childState }: FieldRenderProps<string>) => (
  36. <FieldWrapper error={childState.errors?.[0]?.message}>
  37. <div className="array-item-wrapper">
  38. <Input {...childField} size={'small'} />
  39. {index < field.value!.length - 1 ? (
  40. <Popover
  41. content={'swap with next element'}
  42. className={'icon-button-popover'}
  43. showArrow
  44. position={'topLeft'}
  45. >
  46. <Button
  47. theme="borderless"
  48. size={'small'}
  49. icon={<IconArrowDown />}
  50. onClick={() => field.swap(index, index + 1)}
  51. />
  52. </Popover>
  53. ) : null}
  54. <Popover
  55. content={'delete current element'}
  56. className={'icon-button-popover'}
  57. showArrow
  58. position={'topLeft'}
  59. >
  60. <Button
  61. theme="borderless"
  62. size={'small'}
  63. icon={<IconCrossCircleStroked />}
  64. onClick={() => field.delete(index)}
  65. />
  66. </Popover>
  67. </div>
  68. </FieldWrapper>
  69. )}
  70. </Field>
  71. ))}
  72. <div>
  73. <Button
  74. size={'small'}
  75. theme="borderless"
  76. icon={<IconPlus />}
  77. onClick={() => field.append('default')}
  78. >
  79. Add
  80. </Button>
  81. </div>
  82. </FieldWrapper>
  83. )}
  84. </FieldArray>
  85. </div>
  86. );
  87. interface FormData {
  88. array: string[];
  89. }
  90. const formMeta: FormMeta<FormData> = {
  91. render,
  92. validateTrigger: ValidateTrigger.onChange,
  93. defaultValues: {
  94. array: ['default'],
  95. },
  96. validate: {
  97. 'array.*': ({ value }) =>
  98. value.length > 8 ? 'max length exceeded: current length is ' + value.length : undefined,
  99. },
  100. effect: {
  101. 'array.*': [
  102. {
  103. event: DataEvent.onValueInit,
  104. effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
  105. console.log(name + ' value init to ', value);
  106. },
  107. },
  108. {
  109. event: DataEvent.onValueChange,
  110. effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
  111. console.log(name + ' value changed to ', value);
  112. },
  113. },
  114. ],
  115. },
  116. };
  117. export const nodeRegistry: WorkflowNodeRegistry = {
  118. type: 'custom',
  119. meta: {},
  120. defaultPorts: [{ type: 'output' }, { type: 'input' }],
  121. formMeta,
  122. };
  123. `,
  124. active: true,
  125. };
  126. export const NodeFormArrayPreview = () => {
  127. const files = {
  128. 'node-registry.tsx': nodeRegistryFile,
  129. 'initial-data.ts': { code: defaultInitialDataTs, active: true },
  130. 'field-wrapper.tsx': { code: fieldWrapperTs, active: true },
  131. 'field-wrapper.css': { code: fieldWrapperCss, active: true },
  132. };
  133. return (
  134. <PreviewEditor files={files} previewStyle={{ height: 500 }} editorStyle={{ height: 500 }}>
  135. <Editor registry={nodeRegistry} initialData={DEFAULT_INITIAL_DATA} />
  136. </PreviewEditor>
  137. );
  138. };