@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
@@ -0,0 +1,128 @@
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 styled, { css } from 'styled-components';
9
+ import Icon from '@douyinfe/semi-icons';
10
+
11
+ export const UIContainer = styled.div``;
12
+
13
+ export const UIRow = styled.div`
14
+ display: flex;
15
+ align-items: flex-start;
16
+ gap: 5px;
17
+ `;
18
+
19
+ export const UICollapseTrigger = styled.div`
20
+ cursor: pointer;
21
+ margin-right: 5px;
22
+ `;
23
+
24
+ export const UITreeItems = styled.div<{ $shrink?: boolean }>`
25
+ display: grid;
26
+ grid-template-columns: auto 1fr;
27
+
28
+ ${({ $shrink }) =>
29
+ $shrink &&
30
+ css`
31
+ padding-left: 3px;
32
+ margin-top: 10px;
33
+ `}
34
+ `;
35
+
36
+ export const UITreeItemLeft = styled.div<{
37
+ $isLast?: boolean;
38
+ $showLine?: boolean;
39
+ $showCollapse?: boolean;
40
+ }>`
41
+ grid-column: 1;
42
+ position: relative;
43
+ width: 16px;
44
+
45
+ ${({ $showLine, $isLast, $showCollapse }) => {
46
+ let height = $isLast ? '24px' : '100%';
47
+ let width = $showCollapse ? '12px' : '30px';
48
+
49
+ return (
50
+ $showLine &&
51
+ css`
52
+ &::before {
53
+ /* 竖线 */
54
+ content: '';
55
+ height: ${height};
56
+ position: absolute;
57
+ left: -14px;
58
+ top: -16px;
59
+ width: 1px;
60
+ background: #d9d9d9;
61
+ display: block;
62
+ }
63
+
64
+ &::after {
65
+ /* 横线 */
66
+ content: '';
67
+ position: absolute;
68
+ left: -14px; // 横线起点和竖线对齐
69
+ top: 8px; // 跟随你的行高调整
70
+ width: ${width}; // 横线长度
71
+ height: 1px;
72
+ background: #d9d9d9;
73
+ display: block;
74
+ }
75
+ `
76
+ );
77
+ }}
78
+ `;
79
+
80
+ export const UITreeItemRight = styled.div`
81
+ grid-column: 2;
82
+ margin-bottom: 10px;
83
+
84
+ &:last-child {
85
+ margin-bottom: 0px;
86
+ }
87
+ `;
88
+
89
+ export const UITreeItemMain = styled.div<{}>`
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 10px;
93
+ position: relative;
94
+ `;
95
+
96
+ export const UICollapsible = styled.div<{ $collapse?: boolean }>`
97
+ display: none;
98
+
99
+ ${({ $collapse }) =>
100
+ $collapse &&
101
+ css`
102
+ display: block;
103
+ `}
104
+ `;
105
+
106
+ export const UIActions = styled.div`
107
+ white-space: nowrap;
108
+ `;
109
+
110
+ const iconAddChildrenSvg = (
111
+ <svg
112
+ className="icon-icon icon-icon-coz_add_node "
113
+ width="1em"
114
+ height="1em"
115
+ viewBox="0 0 24 24"
116
+ fill="currentColor"
117
+ xmlns="http://www.w3.org/2000/svg"
118
+ >
119
+ <path
120
+ fillRule="evenodd"
121
+ clipRule="evenodd"
122
+ d="M11 6.49988C11 8.64148 9.50397 10.4337 7.49995 10.8884V15.4998C7.49995 16.0521 7.94767 16.4998 8.49995 16.4998H11.208C11.0742 16.8061 11 17.1443 11 17.4998C11 17.8554 11.0742 18.1936 11.208 18.4998H8.49995C6.8431 18.4998 5.49995 17.1567 5.49995 15.4998V10.8884C3.49599 10.4336 2 8.64145 2 6.49988C2 4.0146 4.01472 1.99988 6.5 1.99988C8.98528 1.99988 11 4.0146 11 6.49988ZM6.5 8.99988C7.88071 8.99988 9 7.88059 9 6.49988C9 5.11917 7.88071 3.99988 6.5 3.99988C5.11929 3.99988 4 5.11917 4 6.49988C4 7.88059 5.11929 8.99988 6.5 8.99988Z"
123
+ ></path>
124
+ <path d="M17.5 12.4999C18.0523 12.4999 18.5 12.9476 18.5 13.4999V16.4999H21.5C22.0523 16.4999 22.5 16.9476 22.5 17.4999C22.5 18.0522 22.0523 18.4999 21.5 18.4999H18.5V21.4999C18.5 22.0522 18.0523 22.4999 17.5 22.4999C16.9477 22.4999 16.5 22.0522 16.5 21.4999V18.4999H13.5C12.9477 18.4999 12.5 18.0522 12.5 17.4999C12.5 16.9476 12.9477 16.4999 13.5 16.4999H16.5V13.4999C16.5 12.9476 16.9477 12.4999 17.5 12.4999Z"></path>
125
+ </svg>
126
+ );
127
+
128
+ export const IconAddChildren = () => <Icon size="small" svg={iconAddChildrenSvg} />;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { IJsonSchema } from '@flowgram.ai/json-schema';
7
+
8
+ import { ConstantInputStrategy } from '@/components/constant-input';
9
+
10
+ export interface PropsType {
11
+ value?: any;
12
+ onChange: (value?: any) => void;
13
+ readonly?: boolean;
14
+ hasError?: boolean;
15
+ schema?: IJsonSchema;
16
+ style?: React.CSSProperties;
17
+ constantProps?: {
18
+ strategies?: ConstantInputStrategy[];
19
+ [key: string]: any;
20
+ };
21
+ }
@@ -20,10 +20,7 @@ import { ConstantInputWrapper } from './styles';
20
20
  export function DefaultValue(props: {
21
21
  value: any;
22
22
  schema?: IJsonSchema;
23
- name?: string;
24
- type?: string;
25
23
  placeholder?: string;
26
- jsonFormatText?: string;
27
24
  onChange: (value: any) => void;
28
25
  }) {
29
26
  const { value, schema, onChange, placeholder } = props;
@@ -35,6 +32,7 @@ export function DefaultValue(props: {
35
32
  onChange={(_v) => onChange(_v)}
36
33
  schema={schema || { type: 'string' }}
37
34
  placeholder={placeholder ?? I18n.t('Default value if parameter is not provided')}
35
+ enableMultiLineStr
38
36
  />
39
37
  </ConstantInputWrapper>
40
38
  );
@@ -3,7 +3,7 @@
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 { difference, omit } from 'lodash';
9
9
  import { produce } from 'immer';
@@ -28,7 +28,16 @@ export function usePropertiesEdit(
28
28
 
29
29
  const [propertyList, setPropertyList] = useState<PropertyValueType[]>([]);
30
30
 
31
+ const effectVersion = useRef(0);
32
+ const changeVersion = useRef(0);
33
+
31
34
  useEffect(() => {
35
+ effectVersion.current = effectVersion.current + 1;
36
+ if (effectVersion.current === changeVersion.current) {
37
+ return;
38
+ }
39
+ effectVersion.current = changeVersion.current;
40
+
32
41
  // If the value is changed, update the property list
33
42
  setPropertyList((_list) => {
34
43
  const newNames = Object.entries(drilldownSchema?.properties || {})
@@ -44,7 +53,7 @@ export function usePropertiesEdit(
44
53
  key: item.key,
45
54
  name: item.name,
46
55
  isPropertyRequired: drilldownSchema?.required?.includes(item.name || '') || false,
47
- ...item,
56
+ ...(drilldownSchema?.properties?.[item.name || ''] || item || {}),
48
57
  }))
49
58
  .concat(
50
59
  addNames.map((_name) => ({
@@ -58,6 +67,8 @@ export function usePropertiesEdit(
58
67
  }, [drilldownSchema]);
59
68
 
60
69
  const updatePropertyList = (updater: (list: PropertyValueType[]) => PropertyValueType[]) => {
70
+ changeVersion.current = changeVersion.current + 1;
71
+
61
72
  setPropertyList((_list) => {
62
73
  const next = updater(_list);
63
74
 
@@ -18,6 +18,7 @@ import {
18
18
  } from '@douyinfe/semi-icons';
19
19
 
20
20
  import { InjectTypeSelector } from '@/components/type-selector';
21
+ import { BlurInput } from '@/components/blur-input';
21
22
 
22
23
  import { ConfigType, PropertyValueType } from './types';
23
24
  import {
@@ -28,10 +29,10 @@ import {
28
29
  UIContainer,
29
30
  UIExpandDetail,
30
31
  UILabel,
31
- UIProperties,
32
- UIPropertyLeft,
33
- UIPropertyMain,
34
- UIPropertyRight,
32
+ UITreeItems,
33
+ UITreeItemLeft,
34
+ UITreeItemMain,
35
+ UITreeItemRight,
35
36
  UIRequired,
36
37
  UIType,
37
38
  } from './styles';
@@ -39,7 +40,6 @@ import { UIName } from './styles';
39
40
  import { DefaultValueWrapper, UIRow } from './styles';
40
41
  import { usePropertiesEdit } from './hooks';
41
42
  import { DefaultValue } from './default-value';
42
- import { BlurInput } from './components/blur-input';
43
43
 
44
44
  const DEFAULT = { type: 'object' };
45
45
 
@@ -58,14 +58,13 @@ export function JsonSchemaEditor(props: {
58
58
 
59
59
  return (
60
60
  <UIContainer className={props.className}>
61
- <UIProperties>
62
- {propertyList.map((_property, index) => (
61
+ <UITreeItems>
62
+ {propertyList.map((_property) => (
63
63
  <PropertyEdit
64
64
  readonly={readonly}
65
65
  key={_property.key}
66
66
  value={_property}
67
67
  config={config}
68
- $index={index}
69
68
  onChange={(_v) => {
70
69
  onEditProperty(_property.key!, _v);
71
70
  }}
@@ -74,7 +73,7 @@ export function JsonSchemaEditor(props: {
74
73
  }}
75
74
  />
76
75
  ))}
77
- </UIProperties>
76
+ </UITreeItems>
78
77
  <Button
79
78
  disabled={readonly}
80
79
  size="small"
@@ -95,27 +94,9 @@ function PropertyEdit(props: {
95
94
  onRemove?: () => void;
96
95
  readonly?: boolean;
97
96
  $isLast?: boolean;
98
- $index?: number;
99
- $isFirst?: boolean;
100
- $parentExpand?: boolean;
101
- $parentType?: string;
102
- $showLine?: boolean;
103
97
  $level?: number; // 添加层级属性
104
98
  }) {
105
- const {
106
- value,
107
- config,
108
- readonly,
109
- $level = 0,
110
- onChange: onChangeProps,
111
- onRemove,
112
- $index,
113
- $isFirst,
114
- $isLast,
115
- $parentExpand = false,
116
- $parentType = '',
117
- $showLine,
118
- } = props;
99
+ const { value, config, readonly, $level = 0, onChange: onChangeProps, onRemove, $isLast } = props;
119
100
 
120
101
  const [expand, setExpand] = useState(false);
121
102
  const [collapse, setCollapse] = useState(false);
@@ -138,29 +119,15 @@ function PropertyEdit(props: {
138
119
 
139
120
  return (
140
121
  <>
141
- <UIPropertyLeft
142
- type={type}
143
- $index={$index}
144
- $isFirst={$isFirst}
145
- $isLast={$isLast}
146
- $showLine={$showLine}
147
- $isExpand={expand}
148
- $parentExpand={$parentExpand}
149
- $parentType={$parentType}
150
- >
122
+ <UITreeItemLeft $isLast={$isLast} $showLine={$level > 0} $showCollapse={showCollapse}>
151
123
  {showCollapse && (
152
124
  <UICollapseTrigger onClick={() => setCollapse((_collapse) => !_collapse)}>
153
125
  {collapse ? <IconChevronDown size="small" /> : <IconChevronRight size="small" />}
154
126
  </UICollapseTrigger>
155
127
  )}
156
- </UIPropertyLeft>
157
- <UIPropertyRight>
158
- <UIPropertyMain
159
- $showCollapse={showCollapse}
160
- $collapse={collapse}
161
- $expand={expand}
162
- type={type}
163
- >
128
+ </UITreeItemLeft>
129
+ <UITreeItemRight>
130
+ <UITreeItemMain>
164
131
  <UIRow>
165
132
  <UIName>
166
133
  <BlurInput
@@ -242,9 +209,7 @@ function PropertyEdit(props: {
242
209
  <DefaultValue
243
210
  value={defaultValue}
244
211
  schema={value}
245
- type={type}
246
212
  placeholder={config?.defaultValuePlaceholder ?? I18n.t('Default Value')}
247
- jsonFormatText={config?.jsonFormatText}
248
213
  onChange={(value) => onChange('default', value)}
249
214
  />
250
215
  </DefaultValueWrapper>
@@ -252,10 +217,10 @@ function PropertyEdit(props: {
252
217
  )}
253
218
  </UIExpandDetail>
254
219
  )}
255
- </UIPropertyMain>
220
+ </UITreeItemMain>
256
221
  {showCollapse && (
257
222
  <UICollapsible $collapse={collapse}>
258
- <UIProperties $shrink={true}>
223
+ <UITreeItems $shrink={true}>
259
224
  {propertyList.map((_property, index) => (
260
225
  <PropertyEdit
261
226
  readonly={readonly}
@@ -263,8 +228,6 @@ function PropertyEdit(props: {
263
228
  value={_property}
264
229
  config={config}
265
230
  $level={$level + 1} // 传递递增的层级
266
- $parentExpand={expand}
267
- $parentType={type}
268
231
  onChange={(_v) => {
269
232
  onEditProperty(_property.key!, _v);
270
233
  }}
@@ -272,15 +235,12 @@ function PropertyEdit(props: {
272
235
  onRemoveProperty(_property.key!);
273
236
  }}
274
237
  $isLast={index === propertyList.length - 1}
275
- $isFirst={index === 0}
276
- $index={index}
277
- $showLine={true}
278
238
  />
279
239
  ))}
280
- </UIProperties>
240
+ </UITreeItems>
281
241
  </UICollapsible>
282
242
  )}
283
- </UIPropertyRight>
243
+ </UITreeItemRight>
284
244
  </>
285
245
  );
286
246
  }
@@ -39,37 +39,30 @@ export const UILabel = styled.div`
39
39
  margin-bottom: 2px;
40
40
  `;
41
41
 
42
- export const UIProperties = styled.div<{ $shrink?: boolean }>`
42
+ export const UITreeItems = styled.div<{ $shrink?: boolean }>`
43
43
  display: grid;
44
44
  grid-template-columns: auto 1fr;
45
45
 
46
46
  ${({ $shrink }) =>
47
47
  $shrink &&
48
48
  css`
49
- padding-left: 10px;
49
+ padding-left: 3px;
50
50
  margin-top: 10px;
51
51
  `}
52
52
  `;
53
53
 
54
- export const UIPropertyLeft = styled.div<{
54
+ export const UITreeItemLeft = styled.div<{
55
55
  $isLast?: boolean;
56
56
  $showLine?: boolean;
57
- $isExpand?: boolean;
58
- type?: string;
59
- $isFirst?: boolean;
60
- $index?: number;
61
- $parentExpand?: boolean;
62
- $parentType?: string;
57
+ $showCollapse?: boolean;
63
58
  }>`
64
59
  grid-column: 1;
65
60
  position: relative;
66
61
  width: 16px;
67
62
 
68
- ${({ $showLine, $isLast, $parentType }) => {
69
- let height = '100%';
70
- if ($parentType && $isLast) {
71
- height = '24px';
72
- }
63
+ ${({ $showLine, $isLast, $showCollapse }) => {
64
+ let height = $isLast ? '24px' : '100%';
65
+ let width = $showCollapse ? '12px' : '30px';
73
66
 
74
67
  return (
75
68
  $showLine &&
@@ -79,7 +72,7 @@ export const UIPropertyLeft = styled.div<{
79
72
  content: '';
80
73
  height: ${height};
81
74
  position: absolute;
82
- left: -22px;
75
+ left: -14px;
83
76
  top: -16px;
84
77
  width: 1px;
85
78
  background: #d9d9d9;
@@ -90,9 +83,9 @@ export const UIPropertyLeft = styled.div<{
90
83
  /* 横线 */
91
84
  content: '';
92
85
  position: absolute;
93
- left: -22px; // 横线起点和竖线对齐
86
+ left: -14px; // 横线起点和竖线对齐
94
87
  top: 8px; // 跟随你的行高调整
95
- width: 18px; // 横线长度
88
+ width: ${width}; // 横线长度
96
89
  height: 1px;
97
90
  background: #d9d9d9;
98
91
  display: block;
@@ -102,7 +95,7 @@ export const UIPropertyLeft = styled.div<{
102
95
  }}
103
96
  `;
104
97
 
105
- export const UIPropertyRight = styled.div`
98
+ export const UITreeItemRight = styled.div`
106
99
  grid-column: 2;
107
100
  margin-bottom: 10px;
108
101
 
@@ -111,47 +104,11 @@ export const UIPropertyRight = styled.div`
111
104
  }
112
105
  `;
113
106
 
114
- export const UIPropertyMain = styled.div<{
115
- $expand?: boolean;
116
- type?: string;
117
- $collapse?: boolean;
118
- $showCollapse?: boolean;
119
- }>`
107
+ export const UITreeItemMain = styled.div<{}>`
120
108
  display: flex;
121
109
  flex-direction: column;
122
110
  gap: 10px;
123
111
  position: relative;
124
-
125
- ${({ $expand, type, $collapse, $showCollapse }) => {
126
- const beforeElement = `
127
- &::before {
128
- /* 竖线 */
129
- content: '';
130
- height: 100%;
131
- position: absolute;
132
- left: -12px;
133
- top: 18px;
134
- width: 1px;
135
- background: #d9d9d9;
136
- display: block;
137
- }`;
138
-
139
- return (
140
- $expand &&
141
- css`
142
- background-color: #f5f5f5;
143
- padding: 10px;
144
- border-radius: 4px;
145
-
146
- ${$showCollapse &&
147
- $collapse &&
148
- (type === 'array' || type === 'object') &&
149
- css`
150
- ${beforeElement}
151
- `}
152
- `
153
- );
154
- }}
155
112
  `;
156
113
 
157
114
  export const UICollapsible = styled.div<{ $collapse?: boolean }>`
@@ -22,5 +22,4 @@ export interface ConfigType {
22
22
  defaultValueTitle?: string;
23
23
  defaultValuePlaceholder?: string;
24
24
  addButtonText?: string;
25
- jsonFormatText?: string;
26
25
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { useEffect, useRef } from 'react';
7
7
 
8
- import { Renderer, EditorProvider, ActiveLinePlaceholder } from '@coze-editor/editor/react';
8
+ import { Renderer, EditorProvider, ActiveLinePlaceholder, InferValues } from '@coze-editor/editor/react';
9
9
  import preset, { EditorAPI } from '@coze-editor/editor/preset-prompt';
10
10
 
11
11
  import { PropsType } from './types';
@@ -14,9 +14,14 @@ import MarkdownHighlight from './extensions/markdown';
14
14
  import LanguageSupport from './extensions/language-support';
15
15
  import JinjaHighlight from './extensions/jinja';
16
16
 
17
- export type PromptEditorPropsType = PropsType;
17
+ type Preset = typeof preset;
18
+ type Options = Partial<InferValues<Preset[number]>>;
18
19
 
19
- export function PromptEditor(props: PropsType) {
20
+ export interface PromptEditorPropsType extends PropsType {
21
+ options?: Options;
22
+ }
23
+
24
+ export function PromptEditor(props: PromptEditorPropsType) {
20
25
  const {
21
26
  value,
22
27
  onChange,
@@ -27,6 +32,7 @@ export function PromptEditor(props: PropsType) {
27
32
  hasError,
28
33
  children,
29
34
  disableMarkdownHighlight,
35
+ options,
30
36
  } = props || {};
31
37
 
32
38
  const editorRef = useRef<EditorAPI | null>(null);
@@ -51,6 +57,7 @@ export function PromptEditor(props: PropsType) {
51
57
  readOnly: readonly,
52
58
  editable: !readonly,
53
59
  placeholder,
60
+ ...options
54
61
  }}
55
62
  onChange={(e) => {
56
63
  onChange({ type: 'template', content: e.value });
@@ -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
  }