node-registry.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import {
  6. DataEvent,
  7. EffectFuncProps,
  8. Field,
  9. FieldRenderProps,
  10. FormMeta,
  11. ValidateTrigger,
  12. WorkflowNodeRegistry,
  13. FieldArray,
  14. FieldArrayRenderProps,
  15. } from '@flowgram.ai/free-layout-editor';
  16. import { FieldWrapper } from '@flowgram.ai/demo-node-form';
  17. import { Input, Button, Popover } from '@douyinfe/semi-ui';
  18. import { IconPlus, IconCrossCircleStroked, IconArrowDown } from '@douyinfe/semi-icons';
  19. import './index.css';
  20. import '../index.css';
  21. export const render = () => (
  22. <div className="demo-node-content">
  23. <div className="demo-node-title">Array Examples</div>
  24. <FieldArray name="array">
  25. {({ field, fieldState }: FieldArrayRenderProps<string>) => (
  26. <FieldWrapper title={'My Array'}>
  27. {field.map((child, index) => (
  28. <Field name={child.name} key={child.key}>
  29. {({ field: childField, fieldState: childState }: FieldRenderProps<string>) => (
  30. <FieldWrapper error={childState.errors?.[0]?.message}>
  31. <div className="array-item-wrapper">
  32. <Input {...childField} size={'small'} />
  33. {index < field.value!.length - 1 ? (
  34. <Popover
  35. content={'swap with next element'}
  36. className={'icon-button-popover'}
  37. showArrow
  38. position={'topLeft'}
  39. >
  40. <Button
  41. theme="borderless"
  42. size={'small'}
  43. icon={<IconArrowDown />}
  44. onClick={() => field.swap(index, index + 1)}
  45. />
  46. </Popover>
  47. ) : null}
  48. <Popover
  49. content={'delete current element'}
  50. className={'icon-button-popover'}
  51. showArrow
  52. position={'topLeft'}
  53. >
  54. <Button
  55. theme="borderless"
  56. size={'small'}
  57. icon={<IconCrossCircleStroked />}
  58. onClick={() => field.delete(index)}
  59. />
  60. </Popover>
  61. </div>
  62. </FieldWrapper>
  63. )}
  64. </Field>
  65. ))}
  66. <div>
  67. <Button
  68. size={'small'}
  69. theme="borderless"
  70. icon={<IconPlus />}
  71. onClick={() => field.append('default')}
  72. >
  73. Add
  74. </Button>
  75. </div>
  76. </FieldWrapper>
  77. )}
  78. </FieldArray>
  79. </div>
  80. );
  81. interface FormData {
  82. array: string[];
  83. }
  84. const formMeta: FormMeta<FormData> = {
  85. render,
  86. validateTrigger: ValidateTrigger.onChange,
  87. defaultValues: {
  88. array: ['default'],
  89. },
  90. validate: {
  91. 'array.*': ({ value }) =>
  92. value.length > 8 ? 'max length exceeded: current length is ' + value.length : undefined,
  93. },
  94. effect: {
  95. 'array.*': [
  96. {
  97. event: DataEvent.onValueInit,
  98. effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
  99. console.log(name + ' value init to ', value);
  100. },
  101. },
  102. {
  103. event: DataEvent.onValueChange,
  104. effect: ({ value, name }: EffectFuncProps<string, FormData>) => {
  105. console.log(name + ' value changed to ', value);
  106. },
  107. },
  108. ],
  109. },
  110. };
  111. export const nodeRegistry: WorkflowNodeRegistry = {
  112. type: 'custom',
  113. meta: {},
  114. defaultPorts: [{ type: 'output' }, { type: 'input' }],
  115. formMeta,
  116. };