@flowgram.ai/form-materials 0.2.26 → 0.2.28
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/bin/materials.ts +1 -1
- package/dist/esm/index.js +506 -224
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +53 -15
- package/dist/index.d.ts +53 -15
- package/dist/index.js +514 -230
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/components/batch-outputs/config.json +2 -1
- package/src/components/batch-outputs/index.tsx +4 -12
- package/src/components/code-editor/config.json +2 -1
- package/src/components/code-editor/language-features.ts +3 -4
- package/src/components/constant-input/index.tsx +19 -2
- package/src/components/constant-input/types.ts +1 -0
- package/src/components/dynamic-value-input/index.tsx +58 -9
- package/src/components/dynamic-value-input/styles.tsx +28 -2
- package/src/components/index.ts +1 -0
- package/src/components/inputs-values/config.json +12 -0
- package/src/components/inputs-values/index.tsx +60 -0
- package/src/components/inputs-values/styles.tsx +19 -0
- package/src/components/inputs-values/types.ts +19 -0
- package/src/components/json-schema-editor/index.tsx +14 -1
- package/src/components/prompt-editor-with-inputs/inputs-picker.tsx +1 -1
- package/src/components/type-selector/index.tsx +15 -8
- package/src/components/variable-selector/index.tsx +30 -11
- package/src/components/variable-selector/styles.tsx +18 -8
- package/src/effects/auto-rename-ref/index.ts +59 -8
- package/src/effects/index.ts +0 -1
- package/src/form-plugins/index.ts +2 -1
- package/src/form-plugins/infer-inputs-plugin/config.json +7 -0
- package/src/form-plugins/infer-inputs-plugin/index.ts +108 -0
- package/src/hooks/index.tsx +6 -0
- package/src/hooks/use-object-list/config.json +8 -0
- package/src/{components/batch-outputs/use-list.ts → hooks/use-object-list/index.tsx} +49 -12
- package/src/typings/flow-value/config.json +3 -1
- package/src/typings/flow-value/index.ts +3 -0
- package/src/effects/provide-batch-outputs/config.json +0 -5
- package/src/effects/provide-batch-outputs/index.ts +0 -38
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import styled from 'styled-components';
|
|
6
|
+
import styled, { css } from 'styled-components';
|
|
7
7
|
import { Tag, TreeSelect } from '@douyinfe/semi-ui';
|
|
8
8
|
|
|
9
9
|
export const UIRootTitle = styled.div`
|
|
@@ -15,11 +15,16 @@ export const UIRootTitle = styled.div`
|
|
|
15
15
|
color: var(--semi-color-text-2);
|
|
16
16
|
`;
|
|
17
17
|
|
|
18
|
-
export const UIVarName = styled.div
|
|
18
|
+
export const UIVarName = styled.div<{ $inSelector?: boolean }>`
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
text-overflow: ellipsis;
|
|
21
21
|
white-space: nowrap;
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
${({ $inSelector }) =>
|
|
24
|
+
$inSelector &&
|
|
25
|
+
css`
|
|
26
|
+
min-width: 50%;
|
|
27
|
+
`}
|
|
23
28
|
`;
|
|
24
29
|
|
|
25
30
|
export const UITag = styled(Tag)`
|
|
@@ -34,18 +39,15 @@ export const UITag = styled(Tag)`
|
|
|
34
39
|
|
|
35
40
|
&.semi-tag {
|
|
36
41
|
margin: 0;
|
|
42
|
+
height: 22px;
|
|
37
43
|
}
|
|
38
44
|
`;
|
|
39
45
|
|
|
40
46
|
export const UITreeSelect = styled(TreeSelect)<{ $error?: boolean }>`
|
|
41
47
|
outline: ${({ $error }) => ($error ? '1px solid red' : 'none')};
|
|
42
48
|
|
|
43
|
-
height: 22px;
|
|
44
|
-
min-height: 22px;
|
|
45
|
-
line-height: 22px;
|
|
46
|
-
|
|
47
49
|
& .semi-tree-select-selection {
|
|
48
|
-
padding:
|
|
50
|
+
padding: 0px;
|
|
49
51
|
height: 22px;
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -57,3 +59,11 @@ export const UITreeSelect = styled(TreeSelect)<{ $error?: boolean }>`
|
|
|
57
59
|
padding-left: 10px;
|
|
58
60
|
}
|
|
59
61
|
`;
|
|
62
|
+
|
|
63
|
+
export const UIPopoverContent = styled.div`
|
|
64
|
+
padding: 10px;
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: flex-start;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
`;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { isArray, isObject } from 'lodash';
|
|
6
|
+
import { isArray, isObject, uniq } from 'lodash';
|
|
7
7
|
import {
|
|
8
8
|
DataEvent,
|
|
9
9
|
Effect,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
VariableFieldKeyRenameService,
|
|
12
12
|
} from '@flowgram.ai/editor';
|
|
13
13
|
|
|
14
|
-
import { IFlowRefValue } from '../../typings';
|
|
14
|
+
import { IFlowRefValue, IFlowTemplateValue } from '../../typings';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Auto rename ref when form item's key is renamed
|
|
@@ -44,9 +44,34 @@ export const autoRenameRefEffect: EffectOptions[] = [
|
|
|
44
44
|
|
|
45
45
|
// traverse rename refs inside form item 'name'
|
|
46
46
|
traverseRef(name, form.getValueIn(name), (_drilldownName, _v) => {
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (_v.type === 'ref') {
|
|
48
|
+
// ref auto rename
|
|
49
|
+
if (isKeyPathMatch(_v.content, beforeKeyPath)) {
|
|
50
|
+
_v.content = [...afterKeyPath, ...(_v.content || [])?.slice(beforeKeyPath.length)];
|
|
51
|
+
form.setValueIn(_drilldownName, _v);
|
|
52
|
+
}
|
|
53
|
+
} else if (_v.type === 'template') {
|
|
54
|
+
// template auto rename
|
|
55
|
+
const templateKeyPaths = getTemplateKeyPaths(_v);
|
|
56
|
+
let hasMatch = false;
|
|
57
|
+
|
|
58
|
+
templateKeyPaths.forEach((_keyPath) => {
|
|
59
|
+
if (isKeyPathMatch(_keyPath, beforeKeyPath)) {
|
|
60
|
+
hasMatch = true;
|
|
61
|
+
const nextKeyPath = [
|
|
62
|
+
...afterKeyPath,
|
|
63
|
+
...(_keyPath || [])?.slice(beforeKeyPath.length),
|
|
64
|
+
];
|
|
65
|
+
_v.content = _v.content?.replace(
|
|
66
|
+
`{{${_keyPath.join('.')}}`,
|
|
67
|
+
`{{${nextKeyPath.join('.')}}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (hasMatch) {
|
|
73
|
+
form.setValueIn(_drilldownName, { ..._v });
|
|
74
|
+
}
|
|
50
75
|
}
|
|
51
76
|
});
|
|
52
77
|
});
|
|
@@ -64,8 +89,21 @@ export const autoRenameRefEffect: EffectOptions[] = [
|
|
|
64
89
|
* @param targetKeyPath
|
|
65
90
|
* @returns
|
|
66
91
|
*/
|
|
67
|
-
function
|
|
68
|
-
return targetKeyPath.every((_key, index) => _key ===
|
|
92
|
+
function isKeyPathMatch(keyPath: string[] = [], targetKeyPath: string[]) {
|
|
93
|
+
return targetKeyPath.every((_key, index) => _key === keyPath[index]);
|
|
94
|
+
}
|
|
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
|
+
);
|
|
69
107
|
}
|
|
70
108
|
|
|
71
109
|
/**
|
|
@@ -79,19 +117,32 @@ function isRef(value: any): value is IFlowRefValue {
|
|
|
79
117
|
);
|
|
80
118
|
}
|
|
81
119
|
|
|
120
|
+
function isTemplate(value: any): value is IFlowTemplateValue {
|
|
121
|
+
return value?.type === 'template' && typeof value?.content === 'string';
|
|
122
|
+
}
|
|
123
|
+
|
|
82
124
|
/**
|
|
83
125
|
* Traverse value to find ref
|
|
84
126
|
* @param value
|
|
85
127
|
* @param options
|
|
86
128
|
* @returns
|
|
87
129
|
*/
|
|
88
|
-
function traverseRef(
|
|
130
|
+
function traverseRef(
|
|
131
|
+
name: string,
|
|
132
|
+
value: any,
|
|
133
|
+
cb: (name: string, _v: IFlowRefValue | IFlowTemplateValue) => void
|
|
134
|
+
) {
|
|
89
135
|
if (isObject(value)) {
|
|
90
136
|
if (isRef(value)) {
|
|
91
137
|
cb(name, value);
|
|
92
138
|
return;
|
|
93
139
|
}
|
|
94
140
|
|
|
141
|
+
if (isTemplate(value)) {
|
|
142
|
+
cb(name, value);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
95
146
|
Object.entries(value).forEach(([_key, _value]) => {
|
|
96
147
|
traverseRef(`${name}.${_key}`, _value, cb);
|
|
97
148
|
});
|
package/src/effects/index.ts
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { get, set } from 'lodash';
|
|
7
|
+
import {
|
|
8
|
+
defineFormPluginCreator,
|
|
9
|
+
getNodePrivateScope,
|
|
10
|
+
getNodeScope,
|
|
11
|
+
Scope,
|
|
12
|
+
} from '@flowgram.ai/editor';
|
|
13
|
+
|
|
14
|
+
import { JsonSchemaUtils } from '../../utils';
|
|
15
|
+
import { IFlowConstantValue, IFlowRefValue, IFlowTemplateValue, IJsonSchema } from '../../typings';
|
|
16
|
+
|
|
17
|
+
interface InputConfig {
|
|
18
|
+
sourceKey: string;
|
|
19
|
+
targetKey: string;
|
|
20
|
+
scope?: 'private' | 'public';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const createInferInputsPlugin = defineFormPluginCreator<InputConfig>({
|
|
24
|
+
onSetupFormMeta({ addFormatOnSubmit }, { sourceKey, targetKey, scope }) {
|
|
25
|
+
if (!sourceKey || !targetKey) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
addFormatOnSubmit((formData, ctx) => {
|
|
30
|
+
set(
|
|
31
|
+
formData,
|
|
32
|
+
targetKey,
|
|
33
|
+
infer(
|
|
34
|
+
get(formData, sourceKey),
|
|
35
|
+
scope === 'private' ? getNodePrivateScope(ctx.node) : getNodeScope(ctx.node)
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return formData;
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
});
|
|
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
|
+
};
|
|
@@ -5,17 +5,29 @@
|
|
|
5
5
|
|
|
6
6
|
import { useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
+
import { nanoid } from 'nanoid';
|
|
8
9
|
import { difference } from 'lodash';
|
|
9
10
|
|
|
10
|
-
import { OutputItem, PropsType } from './types';
|
|
11
|
-
|
|
12
|
-
let _id = 0;
|
|
13
11
|
function genId() {
|
|
14
|
-
return
|
|
12
|
+
return nanoid();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ListItem<ValueType> {
|
|
16
|
+
id: string;
|
|
17
|
+
key?: string;
|
|
18
|
+
value?: ValueType;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
type ObjectType<ValueType> = Record<string, ValueType | undefined>;
|
|
22
|
+
|
|
23
|
+
export function useObjectList<ValueType>({
|
|
24
|
+
value,
|
|
25
|
+
onChange,
|
|
26
|
+
}: {
|
|
27
|
+
value?: ObjectType<ValueType>;
|
|
28
|
+
onChange: (value?: ObjectType<ValueType>) => void;
|
|
29
|
+
}) {
|
|
30
|
+
const [list, setList] = useState<ListItem<ValueType>[]>([]);
|
|
19
31
|
|
|
20
32
|
useEffect(() => {
|
|
21
33
|
setList((_prevList) => {
|
|
@@ -28,7 +40,7 @@ export function useList({ value, onChange }: PropsType) {
|
|
|
28
40
|
.map((item) => ({
|
|
29
41
|
id: item.id,
|
|
30
42
|
key: item.key,
|
|
31
|
-
value: item.key ? value?.[item.key!] :
|
|
43
|
+
value: item.key ? value?.[item.key!] : item.value,
|
|
32
44
|
}))
|
|
33
45
|
.concat(
|
|
34
46
|
addKeys.map((_key) => ({
|
|
@@ -49,11 +61,36 @@ export function useList({ value, onChange }: PropsType) {
|
|
|
49
61
|
]);
|
|
50
62
|
};
|
|
51
63
|
|
|
52
|
-
const
|
|
64
|
+
const updateValue = (itemId: string, value: ValueType) => {
|
|
65
|
+
setList((prevList) => {
|
|
66
|
+
const nextList = prevList.map((_item) => {
|
|
67
|
+
if (_item.id === itemId) {
|
|
68
|
+
return {
|
|
69
|
+
..._item,
|
|
70
|
+
value,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return _item;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
onChange(
|
|
77
|
+
Object.fromEntries(
|
|
78
|
+
nextList.filter((item) => item.key).map((item) => [item.key!, item.value])
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return nextList;
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const updateKey = (itemId: string, key: string) => {
|
|
53
87
|
setList((prevList) => {
|
|
54
88
|
const nextList = prevList.map((_item) => {
|
|
55
|
-
if (_item.id ===
|
|
56
|
-
return
|
|
89
|
+
if (_item.id === itemId) {
|
|
90
|
+
return {
|
|
91
|
+
..._item,
|
|
92
|
+
key,
|
|
93
|
+
};
|
|
57
94
|
}
|
|
58
95
|
return _item;
|
|
59
96
|
});
|
|
@@ -68,7 +105,7 @@ export function useList({ value, onChange }: PropsType) {
|
|
|
68
105
|
});
|
|
69
106
|
};
|
|
70
107
|
|
|
71
|
-
const remove = (itemId:
|
|
108
|
+
const remove = (itemId: string) => {
|
|
72
109
|
setList((prevList) => {
|
|
73
110
|
const nextList = prevList.filter((_item) => _item.id !== itemId);
|
|
74
111
|
|
|
@@ -82,5 +119,5 @@ export function useList({ value, onChange }: PropsType) {
|
|
|
82
119
|
});
|
|
83
120
|
};
|
|
84
121
|
|
|
85
|
-
return { list, add,
|
|
122
|
+
return { list, add, updateKey, updateValue, remove };
|
|
86
123
|
}
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IJsonSchema } from '../json-schema';
|
|
7
|
+
|
|
6
8
|
export interface IFlowConstantValue {
|
|
7
9
|
type: 'constant';
|
|
8
10
|
content?: string | number | boolean;
|
|
11
|
+
schema?: IJsonSchema;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
export interface IFlowRefValue {
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
-
* SPDX-License-Identifier: MIT
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
ASTFactory,
|
|
8
|
-
EffectOptions,
|
|
9
|
-
FlowNodeRegistry,
|
|
10
|
-
createEffectFromVariableProvider,
|
|
11
|
-
getNodeForm,
|
|
12
|
-
} from '@flowgram.ai/editor';
|
|
13
|
-
|
|
14
|
-
import { IFlowRefValue } from '../../typings';
|
|
15
|
-
|
|
16
|
-
export const provideBatchOutputsEffect: EffectOptions[] = createEffectFromVariableProvider({
|
|
17
|
-
parse: (value: Record<string, IFlowRefValue>, ctx) => [
|
|
18
|
-
ASTFactory.createVariableDeclaration({
|
|
19
|
-
key: `${ctx.node.id}`,
|
|
20
|
-
meta: {
|
|
21
|
-
title: getNodeForm(ctx.node)?.getValueIn('title'),
|
|
22
|
-
icon: ctx.node.getNodeRegistry<FlowNodeRegistry>().info?.icon,
|
|
23
|
-
},
|
|
24
|
-
type: ASTFactory.createObject({
|
|
25
|
-
properties: Object.entries(value).map(([_key, value]) =>
|
|
26
|
-
ASTFactory.createProperty({
|
|
27
|
-
key: _key,
|
|
28
|
-
initializer: ASTFactory.createWrapArrayExpression({
|
|
29
|
-
wrapFor: ASTFactory.createKeyPathExpression({
|
|
30
|
-
keyPath: value.content || [],
|
|
31
|
-
}),
|
|
32
|
-
}),
|
|
33
|
-
})
|
|
34
|
-
),
|
|
35
|
-
}),
|
|
36
|
-
}),
|
|
37
|
-
],
|
|
38
|
-
});
|