@flowgram.ai/form-materials 0.4.2 → 0.4.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowgram.ai/form-materials",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "homepage": "https://flowgram.ai/",
5
5
  "repository": "https://github.com/bytedance/flowgram.ai",
6
6
  "license": "MIT",
@@ -35,8 +35,8 @@
35
35
  "@codemirror/state": "~6.5.2",
36
36
  "typescript": "^5.8.3",
37
37
  "zod": "^3.24.4",
38
- "@flowgram.ai/editor": "0.4.2",
39
- "@flowgram.ai/json-schema": "0.4.2"
38
+ "@flowgram.ai/editor": "0.4.3",
39
+ "@flowgram.ai/json-schema": "0.4.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/lodash": "^4.14.137",
@@ -52,8 +52,8 @@
52
52
  "tsup": "^8.0.1",
53
53
  "typescript": "^5.8.3",
54
54
  "vitest": "^0.34.6",
55
- "@flowgram.ai/ts-config": "0.4.2",
56
- "@flowgram.ai/eslint-config": "0.4.2"
55
+ "@flowgram.ai/eslint-config": "0.4.3",
56
+ "@flowgram.ai/ts-config": "0.4.3"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "react": ">=16.8",
@@ -5,11 +5,16 @@
5
5
 
6
6
  import React from 'react';
7
7
 
8
- import { JsonSchemaUtils, IJsonSchema } from '@flowgram.ai/json-schema';
8
+ import {
9
+ JsonSchemaUtils,
10
+ IJsonSchema,
11
+ useTypeManager,
12
+ type JsonSchemaTypeManager,
13
+ } from '@flowgram.ai/json-schema';
9
14
  import { IconButton } from '@douyinfe/semi-ui';
10
15
  import { IconSetting } from '@douyinfe/semi-icons';
11
16
 
12
- import { IFlowConstantRefValue } from '@/typings';
17
+ import { IFlowConstantRefValue, IFlowConstantValue } from '@/typings';
13
18
  import { createInjectMaterial } from '@/shared';
14
19
  import { InjectVariableSelector } from '@/components/variable-selector';
15
20
  import { TypeSelector } from '@/components/type-selector';
@@ -32,6 +37,12 @@ interface PropsType {
32
37
  };
33
38
  }
34
39
 
40
+ const DEFAULT_VALUE: IFlowConstantValue = {
41
+ type: 'constant',
42
+ content: '',
43
+ schema: { type: 'string' },
44
+ };
45
+
35
46
  export function DynamicValueInput({
36
47
  value,
37
48
  onChange,
@@ -44,6 +55,8 @@ export function DynamicValueInput({
44
55
  const [selectSchema, setSelectSchema] = useSelectSchema(schemaFromProps, constantProps, value);
45
56
  const includeSchema = useIncludeSchema(schemaFromProps);
46
57
 
58
+ const typeManager = useTypeManager() as JsonSchemaTypeManager;
59
+
47
60
  const renderTypeSelector = () => {
48
61
  if (schemaFromProps) {
49
62
  return <TypeSelector value={schemaFromProps} readonly={true} />;
@@ -60,24 +73,20 @@ export function DynamicValueInput({
60
73
  value={selectSchema}
61
74
  onChange={(_v) => {
62
75
  setSelectSchema(_v || { type: 'string' });
63
- let content;
64
76
 
77
+ const schema = _v || { type: 'string' };
78
+ let content = typeManager.getDefaultValue(schema);
65
79
  if (_v?.type === 'object') {
66
80
  content = '{}';
67
81
  }
68
-
69
82
  if (_v?.type === 'array') {
70
83
  content = '[]';
71
84
  }
72
85
 
73
- if (_v?.type === 'boolean') {
74
- content = false;
75
- }
76
-
77
86
  onChange({
78
87
  type: 'constant',
79
88
  content,
80
- schema: _v || { type: 'string' },
89
+ schema,
81
90
  });
82
91
  }}
83
92
  readonly={readonly}
@@ -92,7 +101,7 @@ export function DynamicValueInput({
92
101
  <InjectVariableSelector
93
102
  style={{ width: '100%' }}
94
103
  value={value?.content}
95
- onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
104
+ onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : DEFAULT_VALUE)}
96
105
  includeSchema={includeSchema}
97
106
  readonly={readonly}
98
107
  />
@@ -110,7 +119,7 @@ export function DynamicValueInput({
110
119
  fallbackRenderer={() => (
111
120
  <InjectVariableSelector
112
121
  style={{ width: '100%' }}
113
- onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
122
+ onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : DEFAULT_VALUE)}
114
123
  includeSchema={includeSchema}
115
124
  readonly={readonly}
116
125
  />
@@ -67,7 +67,18 @@ export function InputsValues({
67
67
  </UIRow>
68
68
  ))}
69
69
  </UIRows>
70
- <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={() => add()}>
70
+ <Button
71
+ disabled={readonly}
72
+ icon={<IconPlus />}
73
+ size="small"
74
+ onClick={() =>
75
+ add({
76
+ type: 'constant',
77
+ content: '',
78
+ schema: { type: 'string' },
79
+ })
80
+ }
81
+ >
71
82
  {I18n.t('Add')}
72
83
  </Button>
73
84
  </div>
@@ -23,7 +23,7 @@ export function useChildList(
23
23
  canAddField: boolean;
24
24
  hasChildren: boolean;
25
25
  list: ListItem[];
26
- add: (key?: string) => void;
26
+ add: (defaultValue?: any) => void;
27
27
  updateKey: (id: string, key: string) => void;
28
28
  updateValue: (id: string, value: any) => void;
29
29
  remove: (id: string) => void;
@@ -47,7 +47,13 @@ export function InputsValuesTree(props: PropsType) {
47
47
  disabled={readonly}
48
48
  icon={<IconPlus />}
49
49
  size="small"
50
- onClick={add}
50
+ onClick={() => {
51
+ add({
52
+ type: 'constant',
53
+ content: '',
54
+ schema: { type: 'string' },
55
+ });
56
+ }}
51
57
  >
52
58
  {I18n.t('Add')}
53
59
  </Button>
@@ -125,7 +125,11 @@ export function InputValueRow(
125
125
  theme="borderless"
126
126
  icon={<IconAddChildren />}
127
127
  onClick={() => {
128
- add();
128
+ add({
129
+ type: 'constant',
130
+ content: '',
131
+ schema: { type: 'string' },
132
+ });
129
133
  setCollapse(true);
130
134
  }}
131
135
  />
@@ -134,7 +134,7 @@ export function VariableTagInject() {
134
134
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
135
135
  useLayoutEffect(() => {
136
136
  const atMatcher = new MatchDecorator({
137
- regexp: /\{\{([^\}]+)\}\}/g,
137
+ regexp: /\{\{([^\}\{]+)\}\}/g,
138
138
  decoration: (match) =>
139
139
  Decoration.replace({
140
140
  widget: new VariableTagWidget({
@@ -32,6 +32,17 @@ export function InputsTree({ inputsValues }: { inputsValues: Record<string, IFlo
32
32
  return;
33
33
  }
34
34
 
35
+ /**
36
+ * When user input {{xxxx}}, {{{xxx}}}(more brackets if possible), replace all brackets with {{xxxx}}
37
+ */
38
+ let { from, to } = range;
39
+ while (editor.$view.state.doc.sliceString(from - 1, from) === '{') {
40
+ from--;
41
+ }
42
+ while (editor.$view.state.doc.sliceString(to, to + 1) === '}') {
43
+ to++;
44
+ }
45
+
35
46
  editor.replaceText({
36
47
  ...range,
37
48
  text: '{{' + variablePath + '}}',
@@ -5,13 +5,12 @@
5
5
 
6
6
  import React from 'react';
7
7
 
8
- import { IFlowValue } from '@/typings';
9
8
  import { PromptEditor, PromptEditorPropsType } from '@/components/prompt-editor';
10
9
 
11
10
  import { InputsTree } from './extensions/inputs-tree';
12
11
 
13
12
  interface PropsType extends PromptEditorPropsType {
14
- inputsValues: Record<string, IFlowValue>;
13
+ inputsValues: any;
15
14
  }
16
15
 
17
16
  export function PromptEditorWithInputs({ inputsValues, ...restProps }: PropsType) {
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { useMemo } from 'react';
7
7
 
8
- import { last } from 'lodash';
8
+ import { isPlainObject, last } from 'lodash';
9
9
  import {
10
10
  type ArrayType,
11
11
  ASTMatch,
@@ -16,7 +16,7 @@ import {
16
16
  import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
17
17
  import { Tree } from '@douyinfe/semi-ui';
18
18
 
19
- import { IFlowValue } from '@/typings';
19
+ import { FlowValueUtils } from '@/shared';
20
20
 
21
21
  type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
22
22
 
@@ -24,7 +24,7 @@ export function InputsPicker({
24
24
  inputsValues,
25
25
  onSelect,
26
26
  }: {
27
- inputsValues: Record<string, IFlowValue>;
27
+ inputsValues: any;
28
28
  onSelect: (v: string) => void;
29
29
  }) {
30
30
  const available = useScopeAvailable();
@@ -76,23 +76,40 @@ export function InputsPicker({
76
76
  };
77
77
  };
78
78
 
79
- const treeData: TreeNodeData[] = useMemo(
80
- () =>
81
- Object.entries(inputsValues).map(([key, value]) => {
82
- if (value?.type === 'ref') {
83
- const variable = available.getByKeyPath(value.content || []);
79
+ const getTreeData = (value: any, keyPath: string[]): TreeNodeData | undefined => {
80
+ const currKey = keyPath.join('.');
84
81
 
85
- if (variable) {
86
- return renderVariable(variable, [key]);
87
- }
82
+ if (FlowValueUtils.isFlowValue(value)) {
83
+ if (FlowValueUtils.isRef(value)) {
84
+ const variable = available.getByKeyPath(value.content || []);
85
+ if (variable) {
86
+ return renderVariable(variable, keyPath);
88
87
  }
88
+ }
89
+ return {
90
+ key: currKey,
91
+ value: currKey,
92
+ label: last(keyPath),
93
+ };
94
+ }
89
95
 
90
- return {
91
- key,
92
- value: key,
93
- label: key,
94
- };
95
- }),
96
+ if (isPlainObject(value)) {
97
+ return {
98
+ key: currKey,
99
+ value: currKey,
100
+ label: last(keyPath),
101
+ children: Object.entries(value)
102
+ .map(([key, value]) => getTreeData(value, [...keyPath, key])!)
103
+ .filter(Boolean),
104
+ };
105
+ }
106
+ };
107
+
108
+ const treeData: TreeNodeData[] = useMemo(
109
+ () =>
110
+ Object.entries(inputsValues)
111
+ .map(([key, value]) => getTreeData(value, [key])!)
112
+ .filter(Boolean),
96
113
  []
97
114
  );
98
115
 
@@ -143,7 +143,7 @@ export function VariableTagInject() {
143
143
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
144
144
  useLayoutEffect(() => {
145
145
  const atMatcher = new MatchDecorator({
146
- regexp: /\{\{([^\}]+)\}\}/g,
146
+ regexp: /\{\{([^\}\{]+)\}\}/g,
147
147
  decoration: (match) =>
148
148
  Decoration.replace({
149
149
  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
 
@@ -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}
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';
@@ -121,7 +121,7 @@ export namespace FlowValueUtils {
121
121
  */
122
122
  export function getTemplateKeyPaths(value: IFlowTemplateValue) {
123
123
  // find all keyPath wrapped in {{}}
124
- const keyPathReg = /{{(.*?)}}/g;
124
+ const keyPathReg = /\{\{([^\}\{]+)\}\}/g;
125
125
  return uniq(value.content?.match(keyPathReg) || []).map((_keyPath) =>
126
126
  _keyPath.slice(2, -2).split('.')
127
127
  );
@@ -13,7 +13,7 @@ export type FlowValueType = 'constant' | 'ref' | 'expression' | 'template';
13
13
 
14
14
  export interface IFlowConstantValue {
15
15
  type: 'constant';
16
- content?: string | number | boolean;
16
+ content?: any;
17
17
  schema?: IJsonSchema;
18
18
  extra?: IFlowValueExtra;
19
19
  }
@@ -3,10 +3,11 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { isNil, uniq } from 'lodash';
6
+ import { isNil } from 'lodash';
7
7
  import { FeedbackLevel, FlowNodeEntity, getNodeScope } from '@flowgram.ai/editor';
8
8
 
9
- import { IFlowTemplateValue, IFlowValue } from '@/typings';
9
+ import { IFlowValue } from '@/typings';
10
+ import { FlowValueUtils } from '@/shared';
10
11
 
11
12
  interface Context {
12
13
  node: FlowNodeEntity;
@@ -43,7 +44,7 @@ export function validateFlowValue(value: IFlowValue | undefined, ctx: Context) {
43
44
  }
44
45
 
45
46
  if (value?.type === 'template') {
46
- const allRefs = getTemplateKeyPaths(value);
47
+ const allRefs = FlowValueUtils.getTemplateKeyPaths(value);
47
48
 
48
49
  for (const ref of allRefs) {
49
50
  const variable = getNodeScope(node).available.getByKeyPath(ref);
@@ -58,16 +59,3 @@ export function validateFlowValue(value: IFlowValue | undefined, ctx: Context) {
58
59
 
59
60
  return undefined;
60
61
  }
61
-
62
- /**
63
- * get template key paths
64
- * @param value
65
- * @returns
66
- */
67
- function getTemplateKeyPaths(value: IFlowTemplateValue) {
68
- // find all keyPath wrapped in {{}}
69
- const keyPathReg = /{{(.*?)}}/g;
70
- return uniq(value.content?.match(keyPathReg) || []).map((_keyPath) =>
71
- _keyPath.slice(2, -2).split('.')
72
- );
73
- }