@flowgram.ai/form-materials 0.1.0-alpha.13 → 0.1.0-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/bin/index.ts +0 -11
  2. package/bin/materials.ts +29 -2
  3. package/dist/esm/chunk-727SU246.js +13 -0
  4. package/dist/esm/chunk-727SU246.js.map +1 -0
  5. package/dist/esm/chunk-DEZUEMUM.js +284 -0
  6. package/dist/esm/chunk-DEZUEMUM.js.map +1 -0
  7. package/dist/esm/chunk-DUOXDOUE.js +477 -0
  8. package/dist/esm/chunk-DUOXDOUE.js.map +1 -0
  9. package/dist/esm/editor-6UMULJYB.js +180 -0
  10. package/dist/esm/editor-6UMULJYB.js.map +1 -0
  11. package/dist/esm/editor-EYOQTGMT.js +282 -0
  12. package/dist/esm/editor-EYOQTGMT.js.map +1 -0
  13. package/dist/esm/editor-OXPGKPF5.js +167 -0
  14. package/dist/esm/editor-OXPGKPF5.js.map +1 -0
  15. package/dist/esm/editor-VO6YAXRC.js +249 -0
  16. package/dist/esm/editor-VO6YAXRC.js.map +1 -0
  17. package/dist/esm/editor-XYLKTB6L.js +365 -0
  18. package/dist/esm/editor-XYLKTB6L.js.map +1 -0
  19. package/dist/esm/index.js +1110 -2306
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/index.d.mts +298 -59
  22. package/dist/index.d.ts +298 -59
  23. package/dist/index.js +3932 -2681
  24. package/dist/index.js.map +1 -1
  25. package/package.json +9 -8
  26. package/src/components/batch-outputs/index.tsx +3 -2
  27. package/src/components/code-editor/editor.tsx +89 -0
  28. package/src/components/code-editor/index.tsx +5 -89
  29. package/src/components/code-editor/language-features.ts +18 -18
  30. package/src/components/code-editor/theme/dark.ts +49 -30
  31. package/src/components/code-editor/theme/light.ts +56 -32
  32. package/src/components/code-editor-mini/index.tsx +2 -2
  33. package/src/components/condition-row/constants.ts +8 -0
  34. package/src/components/condition-row/index.tsx +4 -0
  35. package/src/components/db-condition-row/hooks/use-left.tsx +66 -0
  36. package/src/components/db-condition-row/hooks/use-op.tsx +59 -0
  37. package/src/components/db-condition-row/index.tsx +93 -0
  38. package/src/components/db-condition-row/styles.tsx +43 -0
  39. package/src/components/db-condition-row/types.ts +34 -0
  40. package/src/components/display-flow-value/index.tsx +2 -12
  41. package/src/components/display-inputs-values/index.tsx +44 -6
  42. package/src/components/dynamic-value-input/hooks.ts +25 -4
  43. package/src/components/dynamic-value-input/index.tsx +21 -12
  44. package/src/components/dynamic-value-input/styles.tsx +14 -4
  45. package/src/components/index.ts +3 -0
  46. package/src/components/inputs-values/index.tsx +14 -3
  47. package/src/components/inputs-values/styles.tsx +1 -1
  48. package/src/components/inputs-values-tree/hooks/use-child-list.tsx +76 -0
  49. package/src/components/inputs-values-tree/index.tsx +62 -0
  50. package/src/components/inputs-values-tree/row.tsx +177 -0
  51. package/src/components/inputs-values-tree/styles.tsx +128 -0
  52. package/src/components/inputs-values-tree/types.ts +21 -0
  53. package/src/components/json-editor-with-variables/editor.tsx +69 -0
  54. package/src/components/json-editor-with-variables/extensions/variable-tag.tsx +6 -5
  55. package/src/components/json-editor-with-variables/index.tsx +5 -59
  56. package/src/components/json-schema-editor/default-value.tsx +1 -3
  57. package/src/components/json-schema-editor/hooks.tsx +14 -3
  58. package/src/components/json-schema-editor/index.tsx +18 -58
  59. package/src/components/json-schema-editor/styles.tsx +12 -55
  60. package/src/components/json-schema-editor/types.ts +0 -1
  61. package/src/components/prompt-editor/editor.tsx +81 -0
  62. package/src/components/prompt-editor/index.tsx +5 -62
  63. package/src/components/prompt-editor-with-inputs/editor.tsx +25 -0
  64. package/src/components/prompt-editor-with-inputs/extensions/inputs-tree.tsx +11 -0
  65. package/src/components/prompt-editor-with-inputs/index.tsx +5 -16
  66. package/src/components/prompt-editor-with-inputs/inputs-picker.tsx +34 -17
  67. package/src/components/prompt-editor-with-variables/editor.tsx +22 -0
  68. package/src/components/prompt-editor-with-variables/extensions/variable-tag.tsx +12 -20
  69. package/src/components/prompt-editor-with-variables/extensions/variable-tree.tsx +13 -1
  70. package/src/components/prompt-editor-with-variables/index.tsx +5 -13
  71. package/src/components/type-selector/index.tsx +12 -2
  72. package/src/components/variable-selector/context.tsx +28 -0
  73. package/src/components/variable-selector/index.tsx +10 -1
  74. package/src/components/variable-selector/use-variable-tree.tsx +3 -3
  75. package/src/effects/auto-rename-ref/index.ts +7 -54
  76. package/src/effects/validate-when-variable-sync/index.ts +1 -1
  77. package/src/form-plugins/infer-assign-plugin/index.ts +1 -1
  78. package/src/form-plugins/infer-inputs-plugin/index.ts +4 -76
  79. package/src/hooks/use-object-list/index.tsx +35 -7
  80. package/src/index.ts +1 -0
  81. package/src/plugins/json-schema-preset/manager.ts +1 -0
  82. package/src/plugins/json-schema-preset/type-definition/date-time.tsx +25 -0
  83. package/src/plugins/json-schema-preset/type-definition/index.tsx +2 -0
  84. package/src/plugins/json-schema-preset/type-definition/string.tsx +18 -9
  85. package/src/shared/flow-value/index.ts +6 -0
  86. package/src/shared/flow-value/schema.ts +38 -0
  87. package/src/shared/flow-value/utils.ts +201 -0
  88. package/src/shared/format-legacy-refs/index.ts +1 -1
  89. package/src/shared/index.ts +3 -0
  90. package/src/shared/lazy-suspense/index.tsx +28 -0
  91. package/src/shared/polyfill-create-root/index.tsx +33 -0
  92. package/src/typings/flow-value/index.ts +3 -1
  93. package/src/validate/validate-flow-value/index.tsx +4 -16
  94. package/src/components/json-schema-editor/components/blur-input.tsx +0 -27
  95. package/src/plugins/disable-declaration-plugin/config.json +0 -5
  96. package/src/plugins/json-schema-preset/config.json +0 -9
  97. /package/src/components/{inputs-values/components/blur-input.tsx → blur-input/index.tsx} +0 -0
@@ -3,10 +3,9 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import ReactDOM from 'react-dom';
7
6
  import React, { useLayoutEffect } from 'react';
8
7
 
9
- import { isEqual, last } from 'lodash';
8
+ import { isEqual, last } from 'lodash-es';
10
9
  import {
11
10
  BaseVariableField,
12
11
  Disposable,
@@ -26,6 +25,8 @@ import {
26
25
  WidgetType,
27
26
  } from '@codemirror/view';
28
27
 
28
+ import { IPolyfillRoot, polyfillCreateRoot } from '@/shared';
29
+
29
30
  import { UIPopoverContent, UIRootTitle, UITag, UIVarName } from '../styles';
30
31
 
31
32
  class VariableTagWidget extends WidgetType {
@@ -35,7 +36,7 @@ class VariableTagWidget extends WidgetType {
35
36
 
36
37
  scope: Scope;
37
38
 
38
- rootDOM?: HTMLSpanElement;
39
+ root: IPolyfillRoot;
39
40
 
40
41
  constructor({ keyPath, scope }: { keyPath?: string[]; scope: Scope }) {
41
42
  super();
@@ -52,14 +53,9 @@ class VariableTagWidget extends WidgetType {
52
53
  return icon;
53
54
  };
54
55
 
55
- renderReact(jsx: React.ReactElement) {
56
- // NOTICE: For React 19, upgrade to 'react-dom/client'
57
- ReactDOM.render(jsx, this.rootDOM!);
58
- }
59
-
60
56
  renderVariable(v?: BaseVariableField) {
61
57
  if (!v) {
62
- this.renderReact(
58
+ this.root.render(
63
59
  <UITag prefixIcon={<IconIssueStroked />} color="amber">
64
60
  Unknown
65
61
  </UITag>
@@ -67,17 +63,14 @@ class VariableTagWidget extends WidgetType {
67
63
  return;
68
64
  }
69
65
 
70
- const rootField = last(v.parentFields) || v;
71
- const isRoot = v.parentFields.length === 0;
66
+ const rootField = last(v.parentFields);
72
67
 
73
68
  const rootTitle = (
74
- <UIRootTitle>
75
- {rootField?.meta.title ? `${rootField.meta.title} ${isRoot ? '' : '-'} ` : ''}
76
- </UIRootTitle>
69
+ <UIRootTitle>{rootField?.meta.title ? `${rootField.meta.title} -` : ''}</UIRootTitle>
77
70
  );
78
71
  const rootIcon = this.renderIcon(rootField?.meta.icon);
79
72
 
80
- this.renderReact(
73
+ this.root.render(
81
74
  <Popover
82
75
  content={
83
76
  <UIPopoverContent>
@@ -89,7 +82,7 @@ class VariableTagWidget extends WidgetType {
89
82
  >
90
83
  <UITag prefixIcon={rootIcon}>
91
84
  {rootTitle}
92
- {!isRoot && <UIVarName>{v?.key}</UIVarName>}
85
+ <UIVarName>{v?.key}</UIVarName>
93
86
  </UITag>
94
87
  </Popover>
95
88
  );
@@ -98,12 +91,11 @@ class VariableTagWidget extends WidgetType {
98
91
  toDOM(view: EditorView): HTMLElement {
99
92
  const dom = document.createElement('span');
100
93
 
101
- this.rootDOM = dom;
94
+ this.root = polyfillCreateRoot(dom);
102
95
 
103
96
  this.toDispose.push(
104
97
  Disposable.create(() => {
105
- // NOTICE: For React 19, upgrade to 'react-dom/client'
106
- ReactDOM.unmountComponentAtNode(this.rootDOM!);
98
+ this.root.unmount();
107
99
  })
108
100
  );
109
101
 
@@ -143,7 +135,7 @@ export function VariableTagInject() {
143
135
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
144
136
  useLayoutEffect(() => {
145
137
  const atMatcher = new MatchDecorator({
146
- regexp: /\{\{([^\}]+)\}\}/g,
138
+ regexp: /\{\{([^\}\{]+)\}\}/g,
147
139
  decoration: (match) =>
148
140
  Decoration.replace({
149
141
  widget: new VariableTagWidget({
@@ -30,8 +30,20 @@ export function VariableTree() {
30
30
  return;
31
31
  }
32
32
 
33
+ /**
34
+ * When user input {{xxxx}}, {{{xxx}}}(more brackets if possible), replace all brackets with {{xxxx}}
35
+ */
36
+ let { from, to } = range;
37
+ while (editor.$view.state.doc.sliceString(from - 1, from) === '{') {
38
+ from--;
39
+ }
40
+ while (editor.$view.state.doc.sliceString(to, to + 1) === '}') {
41
+ to++;
42
+ }
43
+
33
44
  editor.replaceText({
34
- ...range,
45
+ from,
46
+ to,
35
47
  text: '{{' + variablePath + '}}',
36
48
  });
37
49
 
@@ -3,18 +3,10 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import React from 'react';
6
+ import { lazySuspense } from '@/shared';
7
7
 
8
- import { PromptEditor, PromptEditorPropsType } from '@/components/prompt-editor';
8
+ export const PromptEditorWithVariables = lazySuspense(() =>
9
+ import('./editor').then((module) => ({ default: module.PromptEditorWithVariables }))
10
+ );
9
11
 
10
- import { VariableTree } from './extensions/variable-tree';
11
- import { VariableTagInject } from './extensions/variable-tag';
12
-
13
- export function PromptEditorWithVariables(props: PromptEditorPropsType) {
14
- return (
15
- <PromptEditor {...props}>
16
- <VariableTree />
17
- <VariableTagInject />
18
- </PromptEditor>
19
- );
20
- }
12
+ export type { PromptEditorWithVariablesProps } from './editor';
@@ -86,12 +86,22 @@ export function TypeSelector(props: TypeSelectorProps) {
86
86
  []
87
87
  );
88
88
 
89
+ const isDisabled = readonly || disabled;
90
+
89
91
  return (
90
92
  <Cascader
91
- disabled={readonly || disabled}
93
+ disabled={isDisabled}
92
94
  size="small"
93
95
  triggerRender={() => (
94
- <IconButton size="small" style={style} disabled={readonly || disabled} icon={icon} />
96
+ <IconButton
97
+ size="small"
98
+ style={{
99
+ ...(isDisabled ? { pointerEvents: 'none' } : {}),
100
+ ...(style || {}),
101
+ }}
102
+ disabled={isDisabled}
103
+ icon={icon}
104
+ />
95
105
  )}
96
106
  treeData={options}
97
107
  value={selectValue}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import React, { createContext, useContext, useMemo } from 'react';
7
+
8
+ import { BaseVariableField } from '@flowgram.ai/editor';
9
+
10
+ export const VariableSelectorContext = createContext<{
11
+ skipVariable?: (variable?: BaseVariableField) => boolean;
12
+ }>({});
13
+
14
+ export const useVariableSelectorContext = () => useContext(VariableSelectorContext);
15
+
16
+ export const VariableSelectorProvider = ({
17
+ children,
18
+ skipVariable,
19
+ }: {
20
+ skipVariable?: (variable?: BaseVariableField) => boolean;
21
+ children: React.ReactNode;
22
+ }) => {
23
+ const context = useMemo(() => ({ skipVariable }), [skipVariable]);
24
+
25
+ return (
26
+ <VariableSelectorContext.Provider value={context}>{children}</VariableSelectorContext.Provider>
27
+ );
28
+ };
@@ -16,6 +16,7 @@ import { createInjectMaterial } from '@/shared';
16
16
 
17
17
  import { useVariableTree } from './use-variable-tree';
18
18
  import { UIPopoverContent, UIRootTitle, UITag, UITreeSelect, UIVarName } from './styles';
19
+ import { useVariableSelectorContext } from './context';
19
20
 
20
21
  export interface VariableSelectorProps {
21
22
  value?: string[];
@@ -45,7 +46,13 @@ export const VariableSelector = ({
45
46
  hasError,
46
47
  triggerRender,
47
48
  }: VariableSelectorProps) => {
48
- const treeData = useVariableTree({ includeSchema, excludeSchema });
49
+ const { skipVariable } = useVariableSelectorContext();
50
+
51
+ const treeData = useVariableTree({
52
+ includeSchema,
53
+ excludeSchema,
54
+ skipVariable,
55
+ });
49
56
 
50
57
  const treeValue = useMemo(() => {
51
58
  if (typeof value === 'string') {
@@ -139,3 +146,5 @@ export const VariableSelector = ({
139
146
 
140
147
  VariableSelector.renderKey = 'variable-selector-render-key';
141
148
  export const InjectVariableSelector = createInjectMaterial(VariableSelector);
149
+
150
+ export { VariableSelectorProvider } from './context';
@@ -21,9 +21,9 @@ type VariableField = BaseVariableField<{
21
21
  export function useVariableTree(params: {
22
22
  includeSchema?: IJsonSchema | IJsonSchema[];
23
23
  excludeSchema?: IJsonSchema | IJsonSchema[];
24
- customSkip?: (variable: VariableField) => boolean;
24
+ skipVariable?: (variable: VariableField) => boolean;
25
25
  }): TreeNodeData[] {
26
- const { includeSchema, excludeSchema, customSkip } = params;
26
+ const { includeSchema, excludeSchema, skipVariable } = params;
27
27
 
28
28
  const typeManager = useTypeManager();
29
29
  const variables = useAvailableVariables();
@@ -69,7 +69,7 @@ export function useVariableTree(params: {
69
69
  const isSchemaExclude = excludeSchema
70
70
  ? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema)
71
71
  : false;
72
- const isCustomSkip = customSkip ? customSkip(variable) : false;
72
+ const isCustomSkip = skipVariable ? skipVariable(variable) : false;
73
73
 
74
74
  // disabled in meta when created
75
75
  const isMetaDisabled = variable.meta?.disabled;
@@ -3,7 +3,6 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { isArray, isObject, uniq } from 'lodash';
7
6
  import {
8
7
  DataEvent,
9
8
  Effect,
@@ -12,6 +11,7 @@ import {
12
11
  } from '@flowgram.ai/editor';
13
12
 
14
13
  import { IFlowRefValue, IFlowTemplateValue } from '@/typings';
14
+ import { FlowValueUtils } from '@/shared';
15
15
 
16
16
  /**
17
17
  * Auto rename ref when form item's key is renamed
@@ -52,7 +52,7 @@ export const autoRenameRefEffect: EffectOptions[] = [
52
52
  }
53
53
  } else if (_v.type === 'template') {
54
54
  // template auto rename
55
- const templateKeyPaths = getTemplateKeyPaths(_v);
55
+ const templateKeyPaths = FlowValueUtils.getTemplateKeyPaths(_v);
56
56
  let hasMatch = false;
57
57
 
58
58
  templateKeyPaths.forEach((_keyPath) => {
@@ -93,34 +93,6 @@ function isKeyPathMatch(keyPath: string[] = [], targetKeyPath: string[]) {
93
93
  return targetKeyPath.every((_key, index) => _key === keyPath[index]);
94
94
  }
95
95
 
96
- /**
97
- * get template key paths
98
- * @param value
99
- * @returns
100
- */
101
- function getTemplateKeyPaths(value: IFlowTemplateValue) {
102
- // find all keyPath wrapped in {{}}
103
- const keyPathReg = /{{(.*?)}}/g;
104
- return uniq(value.content?.match(keyPathReg) || []).map((_keyPath) =>
105
- _keyPath.slice(2, -2).split('.')
106
- );
107
- }
108
-
109
- /**
110
- * If value is ref
111
- * @param value
112
- * @returns
113
- */
114
- function isRef(value: any): value is IFlowRefValue {
115
- return (
116
- value?.type === 'ref' && Array.isArray(value?.content) && typeof value?.content[0] === 'string'
117
- );
118
- }
119
-
120
- function isTemplate(value: any): value is IFlowTemplateValue {
121
- return value?.type === 'template' && typeof value?.content === 'string';
122
- }
123
-
124
96
  /**
125
97
  * Traverse value to find ref
126
98
  * @param value
@@ -132,29 +104,10 @@ function traverseRef(
132
104
  value: any,
133
105
  cb: (name: string, _v: IFlowRefValue | IFlowTemplateValue) => void
134
106
  ) {
135
- if (isObject(value)) {
136
- if (isRef(value)) {
137
- cb(name, value);
138
- return;
139
- }
140
-
141
- if (isTemplate(value)) {
142
- cb(name, value);
143
- return;
144
- }
145
-
146
- Object.entries(value).forEach(([_key, _value]) => {
147
- traverseRef(`${name}.${_key}`, _value, cb);
148
- });
149
- return;
150
- }
151
-
152
- if (isArray(value)) {
153
- value.forEach((_value, idx) => {
154
- traverseRef(`${name}[${idx}]`, _value, cb);
155
- });
156
- return;
107
+ for (const { value: _v, path } of FlowValueUtils.traverse(value, {
108
+ includeTypes: ['ref', 'template'],
109
+ path: name,
110
+ })) {
111
+ cb(path, _v as IFlowRefValue | IFlowTemplateValue);
157
112
  }
158
-
159
- return;
160
113
  }
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { isEmpty } from 'lodash';
6
+ import { isEmpty } from 'lodash-es';
7
7
  import {
8
8
  DataEvent,
9
9
  Effect,
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { set, uniqBy } from 'lodash';
6
+ import { set, uniqBy } from 'lodash-es';
7
7
  import { JsonSchemaUtils } from '@flowgram.ai/json-schema';
8
8
  import {
9
9
  ASTFactory,
@@ -3,16 +3,10 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { get, set } from 'lodash';
7
- import { JsonSchemaUtils, IJsonSchema } from '@flowgram.ai/json-schema';
8
- import {
9
- defineFormPluginCreator,
10
- getNodePrivateScope,
11
- getNodeScope,
12
- Scope,
13
- } from '@flowgram.ai/editor';
6
+ import { get, set } from 'lodash-es';
7
+ import { defineFormPluginCreator, getNodePrivateScope, getNodeScope } from '@flowgram.ai/editor';
14
8
 
15
- import { IFlowConstantValue, IFlowRefValue, IFlowTemplateValue } from '@/typings';
9
+ import { FlowValueUtils } from '@/shared';
16
10
 
17
11
  interface InputConfig {
18
12
  sourceKey: string;
@@ -30,7 +24,7 @@ export const createInferInputsPlugin = defineFormPluginCreator<InputConfig>({
30
24
  set(
31
25
  formData,
32
26
  targetKey,
33
- infer(
27
+ FlowValueUtils.inferJsonSchema(
34
28
  get(formData, sourceKey),
35
29
  scope === 'private' ? getNodePrivateScope(ctx.node) : getNodeScope(ctx.node)
36
30
  )
@@ -40,69 +34,3 @@ export const createInferInputsPlugin = defineFormPluginCreator<InputConfig>({
40
34
  });
41
35
  },
42
36
  });
43
-
44
- function isRef(value: any): value is IFlowRefValue {
45
- return (
46
- value?.type === 'ref' && Array.isArray(value?.content) && typeof value?.content[0] === 'string'
47
- );
48
- }
49
-
50
- function isTemplate(value: any): value is IFlowTemplateValue {
51
- return value?.type === 'template' && typeof value?.content === 'string';
52
- }
53
-
54
- function isConstant(value: any): value is IFlowConstantValue {
55
- return value?.type === 'constant' && typeof value?.content !== 'undefined';
56
- }
57
-
58
- const infer = (values: any, scope: Scope): IJsonSchema | undefined => {
59
- if (typeof values === 'object') {
60
- if (isConstant(values)) {
61
- if (values?.schema) {
62
- return values.schema;
63
- }
64
-
65
- if (typeof values.content === 'string') {
66
- return {
67
- type: 'string',
68
- };
69
- }
70
-
71
- if (typeof values.content === 'number') {
72
- return {
73
- type: 'number',
74
- };
75
- }
76
-
77
- if (typeof values.content === 'boolean') {
78
- return {
79
- type: 'boolean',
80
- };
81
- }
82
- }
83
-
84
- if (isRef(values)) {
85
- const variable = scope.available.getByKeyPath(values?.content);
86
- const schema = variable?.type ? JsonSchemaUtils.astToSchema(variable?.type) : undefined;
87
-
88
- return schema;
89
- }
90
-
91
- if (isTemplate(values)) {
92
- return {
93
- type: 'string',
94
- };
95
- }
96
-
97
- return {
98
- type: 'object',
99
- properties: Object.keys(values).reduce((acc, key) => {
100
- const schema = infer(values[key], scope);
101
- if (schema) {
102
- acc[key] = schema;
103
- }
104
- return acc;
105
- }, {} as Record<string, IJsonSchema>),
106
- };
107
- }
108
- };
@@ -3,10 +3,10 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { useEffect, useState } from 'react';
6
+ import { useEffect, useRef, useState } from 'react';
7
7
 
8
8
  import { nanoid } from 'nanoid';
9
- import { difference, get, isObject, set } from 'lodash';
9
+ import { difference, get, isObject, set } from 'lodash-es';
10
10
 
11
11
  function genId() {
12
12
  return nanoid();
@@ -27,14 +27,30 @@ export function useObjectList<ValueType>({
27
27
  }: {
28
28
  value?: ObjectType<ValueType>;
29
29
  onChange: (value?: ObjectType<ValueType>) => void;
30
- sortIndexKey?: string;
30
+ sortIndexKey?: string | ((item: ValueType | undefined) => string);
31
31
  }) {
32
32
  const [list, setList] = useState<ListItem<ValueType>[]>([]);
33
33
 
34
+ const effectVersion = useRef(0);
35
+ const changeVersion = useRef(0);
36
+
37
+ const getSortIndex = (value?: ValueType) => {
38
+ if (typeof sortIndexKey === 'function') {
39
+ return get(value, sortIndexKey(value)) || 0;
40
+ }
41
+ return get(value, sortIndexKey || '') || 0;
42
+ };
43
+
34
44
  useEffect(() => {
45
+ effectVersion.current = effectVersion.current + 1;
46
+ if (effectVersion.current === changeVersion.current) {
47
+ return;
48
+ }
49
+ effectVersion.current = changeVersion.current;
50
+
35
51
  setList((_prevList) => {
36
52
  const newKeys = Object.entries(value || {})
37
- .sort((a, b) => get(a[1], sortIndexKey || 0) - get(b[1], sortIndexKey || 0))
53
+ .sort((a, b) => getSortIndex(a[1]) - getSortIndex(b[1]))
38
54
  .map(([key]) => key);
39
55
 
40
56
  const oldKeys = _prevList.map((item) => item.key).filter(Boolean) as string[];
@@ -57,16 +73,19 @@ export function useObjectList<ValueType>({
57
73
  });
58
74
  }, [value]);
59
75
 
60
- const add = () => {
76
+ const add = (defaultValue?: ValueType) => {
61
77
  setList((prevList) => [
62
78
  ...prevList,
63
79
  {
64
80
  id: genId(),
81
+ value: defaultValue,
65
82
  },
66
83
  ]);
67
84
  };
68
85
 
69
86
  const updateValue = (itemId: string, value: ValueType) => {
87
+ changeVersion.current = changeVersion.current + 1;
88
+
70
89
  setList((prevList) => {
71
90
  const nextList = prevList.map((_item) => {
72
91
  if (_item.id === itemId) {
@@ -84,8 +103,13 @@ export function useObjectList<ValueType>({
84
103
  .filter((item) => item.key)
85
104
  .map((item) => [item.key!, item.value])
86
105
  .map((_res, idx) => {
87
- if (isObject(_res[1]) && sortIndexKey) {
88
- set(_res[1], sortIndexKey, idx);
106
+ const indexKey =
107
+ typeof sortIndexKey === 'function'
108
+ ? sortIndexKey(_res[1] as ValueType | undefined)
109
+ : sortIndexKey;
110
+
111
+ if (isObject(_res[1]) && indexKey) {
112
+ set(_res[1], indexKey, idx);
89
113
  }
90
114
  return _res;
91
115
  })
@@ -97,6 +121,8 @@ export function useObjectList<ValueType>({
97
121
  };
98
122
 
99
123
  const updateKey = (itemId: string, key: string) => {
124
+ changeVersion.current = changeVersion.current + 1;
125
+
100
126
  setList((prevList) => {
101
127
  const nextList = prevList.map((_item) => {
102
128
  if (_item.id === itemId) {
@@ -119,6 +145,8 @@ export function useObjectList<ValueType>({
119
145
  };
120
146
 
121
147
  const remove = (itemId: string) => {
148
+ changeVersion.current = changeVersion.current + 1;
149
+
122
150
  setList((prevList) => {
123
151
  const nextList = prevList.filter((_item) => _item.id !== itemId);
124
152
 
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from './typings';
10
10
  export * from './form-plugins';
11
11
  export * from './plugins';
12
12
  export * from './validate';
13
+ export * from './hooks';
@@ -9,6 +9,7 @@ export interface ConstantRendererProps<Value = any> {
9
9
  value?: Value;
10
10
  onChange?: (value: Value) => void;
11
11
  readonly?: boolean;
12
+ [key: string]: any;
12
13
  }
13
14
  export interface JsonSchemaTypeRegistry<Value = any> extends OriginJsonSchemaTypeRegistry {
14
15
  /**
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ /* eslint-disable react/prop-types */
7
+ import React from 'react';
8
+
9
+ import { DatePicker } from '@douyinfe/semi-ui';
10
+
11
+ import { type JsonSchemaTypeRegistry } from '../manager';
12
+
13
+ export const dateTimeRegistry: Partial<JsonSchemaTypeRegistry> = {
14
+ type: 'date-time',
15
+ ConstantRenderer: (props) => (
16
+ <DatePicker
17
+ size="small"
18
+ type="dateTime"
19
+ density="compact"
20
+ style={{ width: '100%', ...(props.style || {}) }}
21
+ disabled={props.readonly}
22
+ {...props}
23
+ />
24
+ ),
25
+ };
@@ -9,6 +9,7 @@ import { stringRegistry } from './string';
9
9
  import { objectRegistry } from './object';
10
10
  import { numberRegistry } from './number';
11
11
  import { integerRegistry } from './integer';
12
+ import { dateTimeRegistry } from './date-time';
12
13
  import { booleanRegistry } from './boolean';
13
14
  import { arrayRegistry } from './array';
14
15
 
@@ -19,6 +20,7 @@ export const jsonSchemaTypePreset = [
19
20
  integerRegistry,
20
21
  booleanRegistry,
21
22
  arrayRegistry,
23
+ dateTimeRegistry,
22
24
  ];
23
25
 
24
26
  jsonSchemaTypePreset.forEach((_type) => jsonSchemaTypeManager.register(_type));
@@ -7,18 +7,27 @@
7
7
  import React from 'react';
8
8
 
9
9
  import { I18n } from '@flowgram.ai/editor';
10
- import { Input } from '@douyinfe/semi-ui';
10
+ import { Input, TextArea } from '@douyinfe/semi-ui';
11
11
 
12
12
  import { type JsonSchemaTypeRegistry } from '../manager';
13
13
 
14
14
  export const stringRegistry: Partial<JsonSchemaTypeRegistry> = {
15
15
  type: 'string',
16
- ConstantRenderer: (props) => (
17
- <Input
18
- placeholder={I18n.t('Please Input String')}
19
- size="small"
20
- disabled={props.readonly}
21
- {...props}
22
- />
23
- ),
16
+ ConstantRenderer: (props) =>
17
+ props?.enableMultiLineStr ? (
18
+ <TextArea
19
+ autosize
20
+ rows={1}
21
+ placeholder={I18n.t('Please Input String')}
22
+ disabled={props.readonly}
23
+ {...props}
24
+ />
25
+ ) : (
26
+ <Input
27
+ size="small"
28
+ placeholder={I18n.t('Please Input String')}
29
+ disabled={props.readonly}
30
+ {...props}
31
+ />
32
+ ),
24
33
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './utils';