@flowgram.ai/form-materials 0.4.0 → 0.4.2

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 (41) hide show
  1. package/dist/esm/index.js +792 -423
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/index.d.mts +136 -30
  4. package/dist/index.d.ts +136 -30
  5. package/dist/index.js +902 -525
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -5
  8. package/src/components/batch-outputs/index.tsx +3 -2
  9. package/src/components/display-flow-value/index.tsx +2 -12
  10. package/src/components/display-inputs-values/index.tsx +44 -6
  11. package/src/components/dynamic-value-input/hooks.ts +24 -3
  12. package/src/components/dynamic-value-input/index.tsx +2 -2
  13. package/src/components/dynamic-value-input/styles.tsx +14 -4
  14. package/src/components/index.ts +2 -0
  15. package/src/components/inputs-values/index.tsx +3 -3
  16. package/src/components/inputs-values/styles.tsx +1 -1
  17. package/src/components/inputs-values-tree/hooks/use-child-list.tsx +76 -0
  18. package/src/components/inputs-values-tree/index.tsx +56 -0
  19. package/src/components/inputs-values-tree/row.tsx +173 -0
  20. package/src/components/inputs-values-tree/styles.tsx +128 -0
  21. package/src/components/inputs-values-tree/types.ts +21 -0
  22. package/src/components/json-schema-editor/default-value.tsx +1 -3
  23. package/src/components/json-schema-editor/hooks.tsx +13 -2
  24. package/src/components/json-schema-editor/index.tsx +17 -57
  25. package/src/components/json-schema-editor/styles.tsx +12 -55
  26. package/src/components/json-schema-editor/types.ts +0 -1
  27. package/src/components/prompt-editor/index.tsx +10 -3
  28. package/src/effects/auto-rename-ref/index.ts +7 -54
  29. package/src/form-plugins/infer-inputs-plugin/index.ts +3 -75
  30. package/src/hooks/use-object-list/index.tsx +34 -6
  31. package/src/plugins/json-schema-preset/manager.ts +1 -0
  32. package/src/plugins/json-schema-preset/type-definition/string.tsx +18 -9
  33. package/src/shared/flow-value/index.ts +6 -0
  34. package/src/shared/flow-value/schema.ts +38 -0
  35. package/src/shared/flow-value/utils.ts +201 -0
  36. package/src/shared/index.ts +1 -0
  37. package/src/typings/flow-value/index.ts +2 -0
  38. package/src/components/json-schema-editor/components/blur-input.tsx +0 -27
  39. package/src/plugins/disable-declaration-plugin/config.json +0 -5
  40. package/src/plugins/json-schema-preset/config.json +0 -9
  41. /package/src/components/{inputs-values/components/blur-input.tsx → blur-input/index.tsx} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowgram.ai/form-materials",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "homepage": "https://flowgram.ai/",
5
5
  "repository": "https://github.com/bytedance/flowgram.ai",
6
6
  "license": "MIT",
@@ -34,8 +34,9 @@
34
34
  "@codemirror/view": "~6.38.0",
35
35
  "@codemirror/state": "~6.5.2",
36
36
  "typescript": "^5.8.3",
37
- "@flowgram.ai/editor": "0.4.0",
38
- "@flowgram.ai/json-schema": "0.4.0"
37
+ "zod": "^3.24.4",
38
+ "@flowgram.ai/editor": "0.4.2",
39
+ "@flowgram.ai/json-schema": "0.4.2"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/lodash": "^4.14.137",
@@ -51,8 +52,8 @@
51
52
  "tsup": "^8.0.1",
52
53
  "typescript": "^5.8.3",
53
54
  "vitest": "^0.34.6",
54
- "@flowgram.ai/eslint-config": "0.4.0",
55
- "@flowgram.ai/ts-config": "0.4.0"
55
+ "@flowgram.ai/ts-config": "0.4.2",
56
+ "@flowgram.ai/eslint-config": "0.4.2"
56
57
  },
57
58
  "peerDependencies": {
58
59
  "react": ">=16.8",
@@ -5,6 +5,7 @@
5
5
 
6
6
  import React from 'react';
7
7
 
8
+ import { I18n } from '@flowgram.ai/editor';
8
9
  import { Button, Input } from '@douyinfe/semi-ui';
9
10
  import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
10
11
 
@@ -46,8 +47,8 @@ export function BatchOutputs(props: PropsType) {
46
47
  </UIRow>
47
48
  ))}
48
49
  </UIRows>
49
- <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={add}>
50
- Add
50
+ <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={() => add()}>
51
+ {I18n.t('Add')}
51
52
  </Button>
52
53
  </div>
53
54
  );
@@ -9,6 +9,7 @@ import { JsonSchemaTypeManager, JsonSchemaUtils } from '@flowgram.ai/json-schema
9
9
  import { useScopeAvailable } from '@flowgram.ai/editor';
10
10
 
11
11
  import { IFlowValue } from '@/typings';
12
+ import { FlowValueUtils } from '@/shared';
12
13
  import { DisplaySchemaTag } from '@/components/display-schema-tag';
13
14
 
14
15
  interface PropsType {
@@ -31,18 +32,7 @@ export function DisplayFlowValue({ value, title, showIconInTree }: PropsType) {
31
32
  return { type: 'string' };
32
33
  }
33
34
  if (value?.type === 'constant') {
34
- if (value?.schema) {
35
- return value?.schema;
36
- }
37
- if (typeof value?.content === 'string') {
38
- return { type: 'string' };
39
- }
40
- if (typeof value?.content === 'number') {
41
- return { type: 'number' };
42
- }
43
- if (typeof value?.content === 'boolean') {
44
- return { type: 'boolean' };
45
- }
35
+ return FlowValueUtils.inferConstantJsonSchema(value);
46
36
  }
47
37
 
48
38
  return { type: 'unknown' };
@@ -3,15 +3,19 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import React from 'react';
6
+ import React, { useMemo } from 'react';
7
7
 
8
- import { IFlowValue } from '@/typings';
8
+ import { isPlainObject } from 'lodash';
9
+ import { useScopeAvailable } from '@flowgram.ai/editor';
10
+
11
+ import { FlowValueUtils } from '@/shared';
9
12
  import { DisplayFlowValue } from '@/components/display-flow-value';
10
13
 
11
14
  import { DisplayInputsWrapper } from './styles';
15
+ import { DisplaySchemaTag } from '../display-schema-tag';
12
16
 
13
17
  interface PropsType {
14
- value?: Record<string, IFlowValue | undefined>;
18
+ value?: any;
15
19
  showIconInTree?: boolean;
16
20
  }
17
21
 
@@ -20,9 +24,43 @@ export function DisplayInputsValues({ value, showIconInTree }: PropsType) {
20
24
 
21
25
  return (
22
26
  <DisplayInputsWrapper>
23
- {childEntries.map(([key, value]) => (
24
- <DisplayFlowValue key={key} title={key} value={value} showIconInTree={showIconInTree} />
25
- ))}
27
+ {childEntries.map(([key, value]) => {
28
+ if (FlowValueUtils.isFlowValue(value)) {
29
+ return (
30
+ <DisplayFlowValue key={key} title={key} value={value} showIconInTree={showIconInTree} />
31
+ );
32
+ }
33
+
34
+ if (isPlainObject(value)) {
35
+ return (
36
+ <DisplayInputsValueAllInTag
37
+ key={key}
38
+ title={key}
39
+ value={value}
40
+ showIconInTree={showIconInTree}
41
+ />
42
+ );
43
+ }
44
+
45
+ return null;
46
+ })}
26
47
  </DisplayInputsWrapper>
27
48
  );
28
49
  }
50
+
51
+ export function DisplayInputsValueAllInTag({
52
+ value,
53
+ title,
54
+ showIconInTree,
55
+ }: PropsType & {
56
+ title: string;
57
+ }) {
58
+ const available = useScopeAvailable();
59
+
60
+ const schema = useMemo(
61
+ () => FlowValueUtils.inferJsonSchema(value, available.scope),
62
+ [available.version, value]
63
+ );
64
+
65
+ return <DisplaySchemaTag title={title} value={schema} showIconInTree={showIconInTree} />;
66
+ }
@@ -3,12 +3,12 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { useMemo, useState } from 'react';
6
+ import { useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
8
  import { IJsonSchema } from '@flowgram.ai/json-schema';
9
9
  import { useScopeAvailable } from '@flowgram.ai/editor';
10
10
 
11
- import { IFlowConstantRefValue } from '@/typings/flow-value';
11
+ import { IFlowConstantRefValue } from '@/typings';
12
12
 
13
13
  export function useRefVariable(value?: IFlowConstantRefValue) {
14
14
  const available = useScopeAvailable();
@@ -33,9 +33,30 @@ export function useSelectSchema(
33
33
  defaultSelectSchema = value?.schema || defaultSelectSchema;
34
34
  }
35
35
 
36
+ const changeVersion = useRef(0);
37
+ const effectVersion = useRef(0);
38
+
36
39
  const [selectSchema, setSelectSchema] = useState(defaultSelectSchema);
37
40
 
38
- return [selectSchema, setSelectSchema] as const;
41
+ useEffect(() => {
42
+ effectVersion.current += 1;
43
+ if (changeVersion.current === effectVersion.current) {
44
+ return;
45
+ }
46
+ effectVersion.current = changeVersion.current;
47
+
48
+ if (value?.type === 'constant' && value?.schema) {
49
+ setSelectSchema(value?.schema);
50
+ return;
51
+ }
52
+ }, [value]);
53
+
54
+ const setSelectSchemaWithVersionUpdate = (schema: IJsonSchema) => {
55
+ setSelectSchema(schema);
56
+ changeVersion.current += 1;
57
+ };
58
+
59
+ return [selectSchema, setSelectSchemaWithVersionUpdate] as const;
39
60
  }
40
61
 
41
62
  export function useIncludeSchema(schemaFromProps?: IJsonSchema) {
@@ -9,7 +9,7 @@ import { JsonSchemaUtils, IJsonSchema } from '@flowgram.ai/json-schema';
9
9
  import { IconButton } from '@douyinfe/semi-ui';
10
10
  import { IconSetting } from '@douyinfe/semi-icons';
11
11
 
12
- import { IFlowConstantRefValue } from '@/typings/flow-value';
12
+ import { IFlowConstantRefValue } from '@/typings';
13
13
  import { createInjectMaterial } from '@/shared';
14
14
  import { InjectVariableSelector } from '@/components/variable-selector';
15
15
  import { TypeSelector } from '@/components/type-selector';
@@ -107,7 +107,6 @@ export function DynamicValueInput({
107
107
  onChange={(_v) => onChange({ type: 'constant', content: _v, schema: constantSchema })}
108
108
  schema={constantSchema || { type: 'string' }}
109
109
  readonly={readonly}
110
- strategies={[...(constantProps?.strategies || [])]}
111
110
  fallbackRenderer={() => (
112
111
  <InjectVariableSelector
113
112
  style={{ width: '100%' }}
@@ -117,6 +116,7 @@ export function DynamicValueInput({
117
116
  />
118
117
  )}
119
118
  {...constantProps}
119
+ strategies={[...(constantProps?.strategies || [])]}
120
120
  />
121
121
  );
122
122
  };
@@ -20,6 +20,8 @@ export const UIMain = styled.div`
20
20
  flex-grow: 1;
21
21
  overflow: hidden;
22
22
  min-width: 0;
23
+ border-left: 1px solid var(--semi-color-border);
24
+ border-right: 1px solid var(--semi-color-border);
23
25
 
24
26
  & .semi-tree-select,
25
27
  & .semi-input-number,
@@ -33,19 +35,27 @@ export const UIMain = styled.div`
33
35
  border: none;
34
36
  border-radius: 0;
35
37
  }
38
+
39
+ & .semi-input-textarea-wrapper {
40
+ border: none;
41
+ border-radius: 0;
42
+ }
43
+
44
+ & .semi-input-textarea {
45
+ padding: 2px 6px;
46
+ border: none;
47
+ border-radius: 0;
48
+ word-break: break-all;
49
+ }
36
50
  `;
37
51
 
38
52
  export const UIType = styled.div`
39
- border-right: 1px solid #e5e5e5;
40
-
41
53
  & .semi-button {
42
54
  border-radius: 0;
43
55
  }
44
56
  `;
45
57
 
46
58
  export const UITrigger = styled.div`
47
- border-left: 1px solid #e5e5e5;
48
-
49
59
  & .semi-button {
50
60
  border-radius: 0;
51
61
  }
@@ -25,3 +25,5 @@ export * from './display-flow-value';
25
25
  export * from './display-inputs-values';
26
26
  export * from './assign-rows';
27
27
  export * from './assign-row';
28
+ export * from './blur-input';
29
+ export * from './inputs-values-tree';
@@ -12,10 +12,10 @@ import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
12
12
  import { IFlowConstantRefValue, IFlowValue } from '@/typings';
13
13
  import { useObjectList } from '@/hooks';
14
14
  import { InjectDynamicValueInput } from '@/components/dynamic-value-input';
15
+ import { BlurInput } from '@/components/blur-input';
15
16
 
16
17
  import { PropsType } from './types';
17
18
  import { UIRow, UIRows } from './styles';
18
- import { BlurInput } from './components/blur-input';
19
19
 
20
20
  export function InputsValues({
21
21
  value,
@@ -67,8 +67,8 @@ export function InputsValues({
67
67
  </UIRow>
68
68
  ))}
69
69
  </UIRows>
70
- <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={add}>
71
- Add
70
+ <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={() => add()}>
71
+ {I18n.t('Add')}
72
72
  </Button>
73
73
  </div>
74
74
  );
@@ -14,6 +14,6 @@ export const UIRows = styled.div`
14
14
 
15
15
  export const UIRow = styled.div`
16
16
  display: flex;
17
- align-items: center;
17
+ align-items: flex-start;
18
18
  gap: 5px;
19
19
  `;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { useMemo } from 'react';
7
+
8
+ import { isPlainObject } from 'lodash';
9
+
10
+ import { FlowValueUtils } from '@/shared';
11
+ import { useObjectList } from '@/hooks';
12
+
13
+ interface ListItem {
14
+ id: string;
15
+ key?: string;
16
+ value?: any;
17
+ }
18
+
19
+ export function useChildList(
20
+ value?: any,
21
+ onChange?: (value: any) => void
22
+ ): {
23
+ canAddField: boolean;
24
+ hasChildren: boolean;
25
+ list: ListItem[];
26
+ add: (key?: string) => void;
27
+ updateKey: (id: string, key: string) => void;
28
+ updateValue: (id: string, value: any) => void;
29
+ remove: (id: string) => void;
30
+ } {
31
+ const canAddField = useMemo(() => {
32
+ if (!isPlainObject(value)) {
33
+ return false;
34
+ }
35
+
36
+ if (FlowValueUtils.isFlowValue(value)) {
37
+ // Constant Object Value Can Add child fields
38
+ return FlowValueUtils.isConstant(value) && value?.schema?.type === 'object';
39
+ }
40
+
41
+ return true;
42
+ }, [value]);
43
+
44
+ const objectListValue = useMemo(() => {
45
+ if (isPlainObject(value)) {
46
+ if (FlowValueUtils.isFlowValue(value)) {
47
+ return undefined;
48
+ }
49
+ return value;
50
+ }
51
+ return undefined;
52
+ }, [value]);
53
+
54
+ const { list, add, updateKey, updateValue, remove } = useObjectList<any>({
55
+ value: objectListValue,
56
+ onChange: (value) => {
57
+ onChange?.(value);
58
+ },
59
+ sortIndexKey: (value) => (FlowValueUtils.isFlowValue(value) ? 'extra.index' : ''),
60
+ });
61
+
62
+ const hasChildren = useMemo(
63
+ () => canAddField && (list.length > 0 || Object.keys(objectListValue || {}).length > 0),
64
+ [canAddField, list.length, Object.keys(objectListValue || {}).length]
65
+ );
66
+
67
+ return {
68
+ canAddField,
69
+ hasChildren,
70
+ list,
71
+ add,
72
+ updateKey,
73
+ updateValue,
74
+ remove,
75
+ };
76
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import React from 'react';
7
+
8
+ import { I18n } from '@flowgram.ai/editor';
9
+ import { Button } from '@douyinfe/semi-ui';
10
+ import { IconPlus } from '@douyinfe/semi-icons';
11
+
12
+ import { FlowValueUtils } from '@/shared';
13
+ import { useObjectList } from '@/hooks';
14
+
15
+ import { PropsType } from './types';
16
+ import { UITreeItems } from './styles';
17
+ import { InputValueRow } from './row';
18
+
19
+ export function InputsValuesTree(props: PropsType) {
20
+ const { value, onChange, readonly, hasError, constantProps } = props;
21
+
22
+ const { list, updateKey, updateValue, remove, add } = useObjectList({
23
+ value,
24
+ onChange,
25
+ sortIndexKey: (value) => (FlowValueUtils.isFlowValue(value) ? 'extra.index' : ''),
26
+ });
27
+
28
+ return (
29
+ <div>
30
+ <UITreeItems>
31
+ {list.map((item) => (
32
+ <InputValueRow
33
+ key={item.id}
34
+ keyName={item.key}
35
+ value={item.value}
36
+ onUpdateKey={(key) => updateKey(item.id, key)}
37
+ onUpdateValue={(value) => updateValue(item.id, value)}
38
+ onRemove={() => remove(item.id)}
39
+ readonly={readonly}
40
+ hasError={hasError}
41
+ constantProps={constantProps}
42
+ />
43
+ ))}
44
+ </UITreeItems>
45
+ <Button
46
+ style={{ marginTop: 10, marginLeft: 16 }}
47
+ disabled={readonly}
48
+ icon={<IconPlus />}
49
+ size="small"
50
+ onClick={add}
51
+ >
52
+ {I18n.t('Add')}
53
+ </Button>
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import React, { useMemo, useState } from 'react';
7
+
8
+ import { I18n } from '@flowgram.ai/editor';
9
+ import { IconButton, Input } from '@douyinfe/semi-ui';
10
+ import { IconChevronDown, IconChevronRight, IconDelete } from '@douyinfe/semi-icons';
11
+
12
+ import { IFlowConstantValue } from '@/typings';
13
+ import { ConstantInputStrategy } from '@/components/constant-input';
14
+
15
+ import { PropsType } from './types';
16
+ import {
17
+ IconAddChildren,
18
+ UIActions,
19
+ UICollapseTrigger,
20
+ UICollapsible,
21
+ UIRow,
22
+ UITreeItemLeft,
23
+ UITreeItemMain,
24
+ UITreeItemRight,
25
+ UITreeItems,
26
+ } from './styles';
27
+ import { useChildList } from './hooks/use-child-list';
28
+ import { InjectDynamicValueInput } from '../dynamic-value-input';
29
+ import { BlurInput } from '../blur-input';
30
+
31
+ const AddObjectChildStrategy: ConstantInputStrategy = {
32
+ hit: (schema) => schema.type === 'object',
33
+ Renderer: () => (
34
+ <Input
35
+ size="small"
36
+ disabled
37
+ style={{ pointerEvents: 'none' }}
38
+ value={I18n.t('Configure via child fields')}
39
+ />
40
+ ),
41
+ };
42
+
43
+ export function InputValueRow(
44
+ props: {
45
+ keyName?: string;
46
+ value?: any;
47
+ onUpdateKey: (key: string) => void;
48
+ onUpdateValue: (value: any) => void;
49
+ onRemove?: () => void;
50
+ $isLast?: boolean;
51
+ $level?: number;
52
+ } & Pick<PropsType, 'constantProps' | 'hasError' | 'readonly'>
53
+ ) {
54
+ const {
55
+ keyName,
56
+ value,
57
+ $level = 0,
58
+ onUpdateKey,
59
+ onUpdateValue,
60
+ $isLast,
61
+ onRemove,
62
+ constantProps,
63
+ hasError,
64
+ readonly,
65
+ } = props;
66
+ const [collapse, setCollapse] = useState(false);
67
+
68
+ const { canAddField, hasChildren, list, add, updateKey, updateValue, remove } = useChildList(
69
+ value,
70
+ onUpdateValue
71
+ );
72
+
73
+ const strategies = useMemo(
74
+ () => [...(hasChildren ? [AddObjectChildStrategy] : []), ...(constantProps?.strategies || [])],
75
+ [hasChildren, constantProps?.strategies]
76
+ );
77
+
78
+ const flowDisplayValue = useMemo(
79
+ () =>
80
+ hasChildren
81
+ ? ({
82
+ type: 'constant',
83
+ schema: { type: 'object' },
84
+ } as IFlowConstantValue)
85
+ : value,
86
+ [hasChildren, value]
87
+ );
88
+
89
+ return (
90
+ <>
91
+ <UITreeItemLeft $isLast={$isLast} $showLine={$level > 0} $showCollapse={hasChildren}>
92
+ {hasChildren && (
93
+ <UICollapseTrigger onClick={() => setCollapse((_collapse) => !_collapse)}>
94
+ {collapse ? <IconChevronDown size="small" /> : <IconChevronRight size="small" />}
95
+ </UICollapseTrigger>
96
+ )}
97
+ </UITreeItemLeft>
98
+ <UITreeItemRight>
99
+ <UITreeItemMain>
100
+ <UIRow>
101
+ <BlurInput
102
+ style={{ width: 100, minWidth: 100, maxWidth: 100 }}
103
+ disabled={readonly}
104
+ size="small"
105
+ value={keyName}
106
+ onChange={(v) => onUpdateKey?.(v)}
107
+ placeholder={I18n.t('Input Key')}
108
+ />
109
+ <InjectDynamicValueInput
110
+ style={{ flexGrow: 1 }}
111
+ readonly={readonly}
112
+ value={flowDisplayValue}
113
+ onChange={(v) => onUpdateValue(v)}
114
+ hasError={hasError}
115
+ constantProps={{
116
+ ...constantProps,
117
+ strategies,
118
+ }}
119
+ />
120
+ <UIActions>
121
+ {canAddField && (
122
+ <IconButton
123
+ disabled={readonly}
124
+ size="small"
125
+ theme="borderless"
126
+ icon={<IconAddChildren />}
127
+ onClick={() => {
128
+ add();
129
+ setCollapse(true);
130
+ }}
131
+ />
132
+ )}
133
+ <IconButton
134
+ disabled={readonly}
135
+ theme="borderless"
136
+ icon={<IconDelete size="small" />}
137
+ size="small"
138
+ onClick={() => onRemove?.()}
139
+ />
140
+ </UIActions>
141
+ </UIRow>
142
+ </UITreeItemMain>
143
+ {hasChildren && (
144
+ <UICollapsible $collapse={collapse}>
145
+ <UITreeItems $shrink={true}>
146
+ {list.map((_item, index) => (
147
+ <InputValueRow
148
+ readonly={readonly}
149
+ hasError={hasError}
150
+ constantProps={constantProps}
151
+ key={_item.id}
152
+ keyName={_item.key}
153
+ value={_item.value}
154
+ $level={$level + 1} // 传递递增的层级
155
+ onUpdateValue={(_v) => {
156
+ updateValue(_item.id, _v);
157
+ }}
158
+ onUpdateKey={(k) => {
159
+ updateKey(_item.id, k);
160
+ }}
161
+ onRemove={() => {
162
+ remove(_item.id);
163
+ }}
164
+ $isLast={index === list.length - 1}
165
+ />
166
+ ))}
167
+ </UITreeItems>
168
+ </UICollapsible>
169
+ )}
170
+ </UITreeItemRight>
171
+ </>
172
+ );
173
+ }