@flowgram-vue/type-editor 0.2.0
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/LICENSE +22 -0
- package/dist/index.cjs +0 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/components/index.ts +8 -0
- package/src/components/type-editor/columns/index.ts +404 -0
- package/src/components/type-editor/common.ts +44 -0
- package/src/components/type-editor/formatter/index.ts +44 -0
- package/src/components/type-editor/hooks/active-pos.ts +20 -0
- package/src/components/type-editor/hooks/disabled.ts +22 -0
- package/src/components/type-editor/hooks/formatter-value.ts +53 -0
- package/src/components/type-editor/hooks/index.ts +8 -0
- package/src/components/type-editor/index.ts +9 -0
- package/src/components/type-editor/mode/declare-assign.ts +102 -0
- package/src/components/type-editor/mode/index.ts +15 -0
- package/src/components/type-editor/mode/type-definition.ts +46 -0
- package/src/components/type-editor/table.vue +293 -0
- package/src/components/type-editor/type-editor.vue +107 -0
- package/src/components/type-editor/type.ts +142 -0
- package/src/components/type-editor/utils.ts +173 -0
- package/src/components/type-selector/index.ts +51 -0
- package/src/components/type-selector/type-selector.vue +100 -0
- package/src/contexts/index.ts +106 -0
- package/src/env.d.ts +10 -0
- package/src/index.ts +15 -0
- package/src/json-schema-exports.ts +18 -0
- package/src/preset/index.ts +6 -0
- package/src/preset/object-type-editor.vue +91 -0
- package/src/services/clipboard-service.ts +93 -0
- package/src/services/index.ts +11 -0
- package/src/services/shortcut-service.ts +9 -0
- package/src/services/type-editor-service.ts +396 -0
- package/src/services/type-operation-service.ts +99 -0
- package/src/services/type-registry-manager.ts +14 -0
- package/src/services/utils.ts +28 -0
- package/src/styles.css +235 -0
- package/src/type-registry/array.ts +18 -0
- package/src/type-registry/boolean.ts +30 -0
- package/src/type-registry/index.ts +22 -0
- package/src/type-registry/integer.ts +24 -0
- package/src/type-registry/number.ts +23 -0
- package/src/type-registry/object.ts +13 -0
- package/src/type-registry/string.ts +21 -0
- package/src/types/index.ts +7 -0
- package/src/types/registry.ts +52 -0
- package/src/types/type-editor.ts +150 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/monitor-data/index.ts +7 -0
- package/src/utils/monitor-data/monitor-data.ts +43 -0
- package/src/utils/monitor-data/use-monitor-data.ts +29 -0
- package/src/utils/registry-adapter.ts +83 -0
- package/src/utils/toast.ts +16 -0
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flowgram-vue/type-editor",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://flowgram.ai/",
|
|
7
|
+
"repository": "https://github.com/Crayon-hua/flowgram-vue",
|
|
8
|
+
"description": "JSON Schema type editor with Vue + native controls. Semi UI dropped.",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./index.css": "./index.css"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"classnames": "^2.5.1",
|
|
26
|
+
"inversify": "^6.0.1",
|
|
27
|
+
"lodash-es": "^4.17.21",
|
|
28
|
+
"nanoid": "^5.0.9",
|
|
29
|
+
"reflect-metadata": "~0.2.2",
|
|
30
|
+
"@flowgram-vue/json-schema": "0.2.0",
|
|
31
|
+
"@flowgram-vue/utils": "0.2.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"vue": "^3.3.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/lodash-es": "^4.17.12",
|
|
38
|
+
"@types/node": "^20",
|
|
39
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
40
|
+
"@vue/test-utils": "^2.4.6",
|
|
41
|
+
"eslint": "^9.0.0",
|
|
42
|
+
"jsdom": "^26.1.0",
|
|
43
|
+
"typescript": "^5.8.3",
|
|
44
|
+
"vitest": "^3.2.4",
|
|
45
|
+
"vue": "^3.5.18",
|
|
46
|
+
"@flowgram-vue/build-config": "0.2.0",
|
|
47
|
+
"@flowgram-vue/eslint-config": "0.2.0",
|
|
48
|
+
"@flowgram-vue/ts-config": "0.2.0"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public",
|
|
52
|
+
"registry": "https://registry.npmjs.org/"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "flowgram-build",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"lint": "eslint .",
|
|
58
|
+
"ts-check": "tsc --noEmit",
|
|
59
|
+
"type-check": "tsc --noEmit",
|
|
60
|
+
"build:fast": "flowgram-build --fast",
|
|
61
|
+
"build:watch": "flowgram-build --watch"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineComponent, h, type PropType } from 'vue';
|
|
7
|
+
import { IJsonSchema } from '@flowgram-vue/json-schema';
|
|
8
|
+
|
|
9
|
+
import { TypeEditorColumnConfig, TypeEditorColumnType, type RenderProps } from '../../../types';
|
|
10
|
+
import { useTypeDefinitionManager } from '../../../contexts';
|
|
11
|
+
import { typeEditorUtils, getComponentId } from '../utils';
|
|
12
|
+
import { useDisabled } from '../hooks/disabled';
|
|
13
|
+
import { toast } from '../../../utils/toast';
|
|
14
|
+
|
|
15
|
+
const cellProps = {
|
|
16
|
+
rowData: { type: Object as PropType<RenderProps<IJsonSchema>['rowData']>, required: true },
|
|
17
|
+
readonly: Boolean,
|
|
18
|
+
onViewMode: { type: Function as PropType<() => void>, required: true },
|
|
19
|
+
typeEditor: { type: Object as PropType<RenderProps<IJsonSchema>['typeEditor']>, required: true },
|
|
20
|
+
onChildrenVisibleChange: {
|
|
21
|
+
type: Function as PropType<(id: string, val: boolean) => void>,
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
onChange: { type: Function as PropType<() => void>, required: true },
|
|
25
|
+
onEditMode: { type: Function as PropType<() => void>, required: true },
|
|
26
|
+
unOpenKeys: { type: Object as PropType<Record<string, boolean>>, required: true },
|
|
27
|
+
error: Boolean,
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
export const KeyView = defineComponent({
|
|
31
|
+
name: 'KeyView',
|
|
32
|
+
props: cellProps,
|
|
33
|
+
setup(props) {
|
|
34
|
+
const typeService = useTypeDefinitionManager();
|
|
35
|
+
return () => {
|
|
36
|
+
const rowData = props.rowData;
|
|
37
|
+
const disabled = useDisabled(TypeEditorColumnType.Key, rowData);
|
|
38
|
+
const config = typeService.getTypeBySchema(rowData.self);
|
|
39
|
+
const indent = (rowData.level + (config?.canAddField?.(rowData) ? 0 : 1)) * 16;
|
|
40
|
+
return h(
|
|
41
|
+
'div',
|
|
42
|
+
{
|
|
43
|
+
class: 'fg-type-key',
|
|
44
|
+
id: getComponentId('key-text'),
|
|
45
|
+
onClick: disabled ? undefined : () => props.onEditMode(),
|
|
46
|
+
},
|
|
47
|
+
[
|
|
48
|
+
h('span', { class: 'fg-type-indent', style: { width: `${indent}px` } }),
|
|
49
|
+
rowData.childrenCount
|
|
50
|
+
? h(
|
|
51
|
+
'button',
|
|
52
|
+
{
|
|
53
|
+
class: 'fg-type-icon-btn',
|
|
54
|
+
type: 'button',
|
|
55
|
+
onClick: (e: Event) => {
|
|
56
|
+
props.onChildrenVisibleChange(rowData.id, !props.unOpenKeys[rowData.id]);
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
props.unOpenKeys[rowData.id] ? '▸' : '▾'
|
|
61
|
+
)
|
|
62
|
+
: null,
|
|
63
|
+
h(
|
|
64
|
+
'span',
|
|
65
|
+
{ class: 'fg-type-key-text', title: disabled },
|
|
66
|
+
typeEditorUtils.formateKey(rowData.key)
|
|
67
|
+
),
|
|
68
|
+
!props.readonly && config?.canAddField?.(rowData)
|
|
69
|
+
? h(
|
|
70
|
+
'button',
|
|
71
|
+
{
|
|
72
|
+
class: 'fg-type-icon-btn',
|
|
73
|
+
type: 'button',
|
|
74
|
+
id: getComponentId('add-field'),
|
|
75
|
+
onClick: (e: Event) => {
|
|
76
|
+
e.stopPropagation();
|
|
77
|
+
const parent = config.getPropertiesParent?.(rowData.self);
|
|
78
|
+
if (!parent) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!parent.properties) {
|
|
82
|
+
parent.properties = {};
|
|
83
|
+
}
|
|
84
|
+
let index = -1;
|
|
85
|
+
Object.values(parent.properties).forEach((val) => {
|
|
86
|
+
index = Math.max(index, val.extra?.index || 0);
|
|
87
|
+
});
|
|
88
|
+
const [key, schema] = typeEditorUtils.genNewTypeSchema(index + 1);
|
|
89
|
+
parent.properties[key] = schema as IJsonSchema;
|
|
90
|
+
props.onChange();
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
'+'
|
|
94
|
+
)
|
|
95
|
+
: null,
|
|
96
|
+
]
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export const KeyEdit = defineComponent({
|
|
103
|
+
name: 'KeyEdit',
|
|
104
|
+
props: cellProps,
|
|
105
|
+
setup(props) {
|
|
106
|
+
return () =>
|
|
107
|
+
h('input', {
|
|
108
|
+
class: 'fg-type-input',
|
|
109
|
+
id: getComponentId('key-edit'),
|
|
110
|
+
autofocus: true,
|
|
111
|
+
value: typeEditorUtils.formateKey(props.rowData.key),
|
|
112
|
+
onBlur: (e: Event) => {
|
|
113
|
+
const value = (e.target as HTMLInputElement).value;
|
|
114
|
+
const newVal = typeEditorUtils.deFormateKey(value, props.rowData.key);
|
|
115
|
+
if (newVal !== props.rowData.key && props.rowData.parent?.properties) {
|
|
116
|
+
if (props.rowData.parent.properties[newVal]) {
|
|
117
|
+
toast('The same key exists at the current level.', 'error');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
props.rowData.parent.properties[newVal] =
|
|
121
|
+
props.rowData.parent.properties[props.rowData.key];
|
|
122
|
+
delete props.rowData.parent.properties[props.rowData.key];
|
|
123
|
+
}
|
|
124
|
+
props.onChange();
|
|
125
|
+
props.onViewMode();
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
export const TypeView = defineComponent({
|
|
132
|
+
name: 'TypeView',
|
|
133
|
+
props: cellProps,
|
|
134
|
+
setup(props) {
|
|
135
|
+
const typeService = useTypeDefinitionManager();
|
|
136
|
+
return () => {
|
|
137
|
+
const disabled = useDisabled(TypeEditorColumnType.Type, props.rowData);
|
|
138
|
+
const typeConfig = typeService.getTypeBySchema(props.rowData.self);
|
|
139
|
+
return h(
|
|
140
|
+
'div',
|
|
141
|
+
{
|
|
142
|
+
class: 'fg-type-key',
|
|
143
|
+
title: disabled,
|
|
144
|
+
onClick: disabled ? undefined : () => props.onEditMode(),
|
|
145
|
+
},
|
|
146
|
+
[typeConfig?.getDisplayLabel?.(props.rowData) || 'Unknown']
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const RequiredView = defineComponent({
|
|
153
|
+
name: 'RequiredView',
|
|
154
|
+
props: cellProps,
|
|
155
|
+
setup(props) {
|
|
156
|
+
return () => {
|
|
157
|
+
const disabled = useDisabled(TypeEditorColumnType.Required, props.rowData);
|
|
158
|
+
return h('div', { class: 'fg-type-center' }, [
|
|
159
|
+
h('input', {
|
|
160
|
+
type: 'checkbox',
|
|
161
|
+
disabled: !!disabled,
|
|
162
|
+
checked: !!props.rowData.isRequired,
|
|
163
|
+
onChange: () => {
|
|
164
|
+
const { parent } = props.rowData;
|
|
165
|
+
if (!parent) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!parent.required) {
|
|
169
|
+
parent.required = [];
|
|
170
|
+
}
|
|
171
|
+
const idx = parent.required.findIndex((key) => key === props.rowData.key);
|
|
172
|
+
if (idx !== -1) {
|
|
173
|
+
parent.required.splice(idx, 1);
|
|
174
|
+
} else {
|
|
175
|
+
parent.required.push(props.rowData.key);
|
|
176
|
+
}
|
|
177
|
+
props.onChange();
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
]);
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const DescriptionView = defineComponent({
|
|
186
|
+
name: 'DescriptionView',
|
|
187
|
+
props: cellProps,
|
|
188
|
+
setup(props) {
|
|
189
|
+
return () => {
|
|
190
|
+
const disabled = useDisabled(TypeEditorColumnType.Description, props.rowData);
|
|
191
|
+
return h(
|
|
192
|
+
'div',
|
|
193
|
+
{
|
|
194
|
+
class: 'fg-type-key',
|
|
195
|
+
title: disabled,
|
|
196
|
+
onClick: disabled ? undefined : () => props.onEditMode(),
|
|
197
|
+
},
|
|
198
|
+
props.rowData.description || ''
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export const DescriptionEdit = defineComponent({
|
|
205
|
+
name: 'DescriptionEdit',
|
|
206
|
+
props: cellProps,
|
|
207
|
+
setup(props) {
|
|
208
|
+
return () =>
|
|
209
|
+
h('input', {
|
|
210
|
+
class: 'fg-type-input',
|
|
211
|
+
autofocus: true,
|
|
212
|
+
value: props.rowData.description,
|
|
213
|
+
onBlur: (e: Event) => {
|
|
214
|
+
props.rowData.self.description = (e.target as HTMLInputElement).value;
|
|
215
|
+
props.onChange();
|
|
216
|
+
props.onViewMode();
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
export const DefaultView = defineComponent({
|
|
223
|
+
name: 'DefaultView',
|
|
224
|
+
props: cellProps,
|
|
225
|
+
setup(props) {
|
|
226
|
+
const typeService = useTypeDefinitionManager();
|
|
227
|
+
return () => {
|
|
228
|
+
const disabled = useDisabled(TypeEditorColumnType.Default, props.rowData);
|
|
229
|
+
const config = typeService.getTypeBySchema(props.rowData.self);
|
|
230
|
+
const text = config?.getValueText
|
|
231
|
+
? config.getValueText(props.rowData.self.default)
|
|
232
|
+
: String(props.rowData.self.default ?? '');
|
|
233
|
+
return h(
|
|
234
|
+
'div',
|
|
235
|
+
{
|
|
236
|
+
class: 'fg-type-key',
|
|
237
|
+
title: disabled,
|
|
238
|
+
onClick: disabled ? undefined : () => props.onEditMode(),
|
|
239
|
+
},
|
|
240
|
+
text
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
export const DefaultEdit = defineComponent({
|
|
247
|
+
name: 'DefaultEdit',
|
|
248
|
+
props: cellProps,
|
|
249
|
+
setup(props) {
|
|
250
|
+
const typeService = useTypeDefinitionManager();
|
|
251
|
+
return () => {
|
|
252
|
+
const config = typeService.getTypeBySchema(props.rowData.self);
|
|
253
|
+
return (
|
|
254
|
+
config?.getInputNode?.({
|
|
255
|
+
value: props.rowData.self.default,
|
|
256
|
+
type: props.rowData.self,
|
|
257
|
+
onChange: (v) => {
|
|
258
|
+
props.rowData.self.default = v;
|
|
259
|
+
},
|
|
260
|
+
onSubmit: () => {
|
|
261
|
+
props.onChange();
|
|
262
|
+
props.onViewMode();
|
|
263
|
+
},
|
|
264
|
+
}) || null
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
export const ValueView = defineComponent({
|
|
271
|
+
name: 'ValueView',
|
|
272
|
+
props: cellProps,
|
|
273
|
+
setup(props) {
|
|
274
|
+
return () =>
|
|
275
|
+
h(
|
|
276
|
+
'div',
|
|
277
|
+
{ class: 'fg-type-key', onClick: () => props.onEditMode() },
|
|
278
|
+
String(props.rowData.self?.extra?.value ?? '')
|
|
279
|
+
);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
export const OperateView = defineComponent({
|
|
284
|
+
name: 'OperateView',
|
|
285
|
+
props: cellProps,
|
|
286
|
+
setup(props) {
|
|
287
|
+
return () => {
|
|
288
|
+
const disabled = useDisabled(TypeEditorColumnType.Operate, props.rowData);
|
|
289
|
+
return h('div', { class: 'fg-type-center' }, [
|
|
290
|
+
h(
|
|
291
|
+
'button',
|
|
292
|
+
{
|
|
293
|
+
class: 'fg-type-icon-btn',
|
|
294
|
+
type: 'button',
|
|
295
|
+
id: getComponentId('remove-field'),
|
|
296
|
+
disabled: !!disabled,
|
|
297
|
+
onClick: () => {
|
|
298
|
+
if (!props.rowData.parent?.properties) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
delete props.rowData.parent.properties[props.rowData.key];
|
|
302
|
+
typeEditorUtils.sortProperties(props.rowData.parent);
|
|
303
|
+
props.onChange();
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
'✕'
|
|
307
|
+
),
|
|
308
|
+
]);
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
export const PrivateView = defineComponent({
|
|
314
|
+
name: 'PrivateView',
|
|
315
|
+
props: cellProps,
|
|
316
|
+
setup(props) {
|
|
317
|
+
return () =>
|
|
318
|
+
h('div', { class: 'fg-type-center' }, [
|
|
319
|
+
h('input', {
|
|
320
|
+
type: 'checkbox',
|
|
321
|
+
checked: !!props.rowData.extra?.private,
|
|
322
|
+
onChange: () => {
|
|
323
|
+
if (!props.rowData.extra) {
|
|
324
|
+
props.rowData.extra = {};
|
|
325
|
+
}
|
|
326
|
+
props.rowData.extra.private = !props.rowData.extra.private;
|
|
327
|
+
props.onChange();
|
|
328
|
+
},
|
|
329
|
+
}),
|
|
330
|
+
]);
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
export const keyColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
335
|
+
type: TypeEditorColumnType.Key,
|
|
336
|
+
label: 'Key',
|
|
337
|
+
width: 43,
|
|
338
|
+
viewRender: KeyView,
|
|
339
|
+
editRender: KeyEdit,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const typeColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
343
|
+
type: TypeEditorColumnType.Type,
|
|
344
|
+
label: 'Type',
|
|
345
|
+
width: 20,
|
|
346
|
+
viewRender: TypeView,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
export const requiredColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
350
|
+
type: TypeEditorColumnType.Required,
|
|
351
|
+
label: 'Required',
|
|
352
|
+
width: 11,
|
|
353
|
+
viewRender: RequiredView,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
export const descriptionColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
357
|
+
type: TypeEditorColumnType.Description,
|
|
358
|
+
width: 15,
|
|
359
|
+
label: 'Description',
|
|
360
|
+
viewRender: DescriptionView,
|
|
361
|
+
editRender: DescriptionEdit,
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
export const defaultColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
365
|
+
type: TypeEditorColumnType.Default,
|
|
366
|
+
width: 15,
|
|
367
|
+
label: 'Default',
|
|
368
|
+
viewRender: DefaultView,
|
|
369
|
+
editRender: DefaultEdit,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export const valueColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
373
|
+
type: TypeEditorColumnType.Value,
|
|
374
|
+
width: 15,
|
|
375
|
+
label: 'Value',
|
|
376
|
+
viewRender: ValueView,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
export const operateColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
380
|
+
type: TypeEditorColumnType.Operate,
|
|
381
|
+
label: 'Operate',
|
|
382
|
+
width: 11,
|
|
383
|
+
focusable: false,
|
|
384
|
+
viewRender: OperateView,
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
export const privateColumnConfig: TypeEditorColumnConfig<IJsonSchema> = {
|
|
388
|
+
type: TypeEditorColumnType.Private,
|
|
389
|
+
label: 'Private',
|
|
390
|
+
width: 11,
|
|
391
|
+
viewRender: PrivateView,
|
|
392
|
+
info: () => 'Private under the current project.',
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
export const columnConfigs = [
|
|
396
|
+
keyColumnConfig,
|
|
397
|
+
typeColumnConfig,
|
|
398
|
+
requiredColumnConfig,
|
|
399
|
+
descriptionColumnConfig,
|
|
400
|
+
privateColumnConfig,
|
|
401
|
+
valueColumnConfig,
|
|
402
|
+
defaultColumnConfig,
|
|
403
|
+
operateColumnConfig,
|
|
404
|
+
];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* empty key 前缀
|
|
8
|
+
*/
|
|
9
|
+
export const SUFFIX = '___empty___';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 表格 header 高度
|
|
13
|
+
*/
|
|
14
|
+
export const HEADER_HIGHT = 36;
|
|
15
|
+
/**
|
|
16
|
+
* 表格 cell 高度
|
|
17
|
+
* 36 高度 + 1px border
|
|
18
|
+
*/
|
|
19
|
+
export const CELL_HIGHT = 36 + 1;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* cell padding 宽度
|
|
23
|
+
*/
|
|
24
|
+
export const CELL_PADDING = 8;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 单个缩进的宽度
|
|
28
|
+
*/
|
|
29
|
+
export const INDENT_WIDTH = 16;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 操作栏高度
|
|
33
|
+
*/
|
|
34
|
+
export const TAB_BRA_HIGHT = 0;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* test id 前缀
|
|
38
|
+
*/
|
|
39
|
+
export const COMPONENT_ID_PREFIX = 'type-editor-component-id';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* root 字段 id
|
|
43
|
+
*/
|
|
44
|
+
export const ROOT_FIELD_ID = 'root';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IJsonSchema } from '@flowgram-vue/json-schema';
|
|
7
|
+
|
|
8
|
+
import { SUFFIX } from '../common';
|
|
9
|
+
export type Formatter = (typeSchema: Partial<IJsonSchema>) => void;
|
|
10
|
+
|
|
11
|
+
export const extraFormatter: Formatter = (type) => {
|
|
12
|
+
if (type.extra !== undefined) {
|
|
13
|
+
type.extra = type.extra;
|
|
14
|
+
|
|
15
|
+
delete type.extra;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const extraDeFormatter: Formatter = (type) => {
|
|
20
|
+
if (type.extra !== undefined) {
|
|
21
|
+
type.extra = type.extra;
|
|
22
|
+
delete type.extra;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const emptyKeyFormatter: Formatter = (type) => {
|
|
27
|
+
if (type.properties) {
|
|
28
|
+
Object.keys(type.properties).forEach((k) => {
|
|
29
|
+
if (k.startsWith(SUFFIX)) {
|
|
30
|
+
delete type.properties![k];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const disableFixIndexFormatter: Formatter = (type) => {
|
|
37
|
+
if (type.extra) {
|
|
38
|
+
delete type.extra.index;
|
|
39
|
+
|
|
40
|
+
if (Object.keys(type.extra).length === 0) {
|
|
41
|
+
delete type.extra;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { onBeforeUnmount, ref } from 'vue';
|
|
7
|
+
import { IJsonSchema } from '@flowgram-vue/json-schema';
|
|
8
|
+
|
|
9
|
+
import { TypeEditorService } from '../../../services';
|
|
10
|
+
import { useService } from '../../../contexts';
|
|
11
|
+
|
|
12
|
+
export const useActivePos = (): { x: number; y: number } => {
|
|
13
|
+
const typeEditorService = useService<TypeEditorService<IJsonSchema>>(TypeEditorService);
|
|
14
|
+
const activePos = ref(typeEditorService.activePos);
|
|
15
|
+
const dispose = typeEditorService.onActivePosChange.event((v) => {
|
|
16
|
+
activePos.value = v;
|
|
17
|
+
});
|
|
18
|
+
onBeforeUnmount(() => dispose.dispose());
|
|
19
|
+
return activePos.value;
|
|
20
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IJsonSchema } from '@flowgram-vue/json-schema';
|
|
7
|
+
|
|
8
|
+
import { TypeEditorColumnType, TypeEditorRowData } from '../../../types';
|
|
9
|
+
|
|
10
|
+
export const useDisabled = <TypeSchema extends Partial<IJsonSchema>>(
|
|
11
|
+
type: TypeEditorColumnType,
|
|
12
|
+
rowData: TypeEditorRowData<TypeSchema>
|
|
13
|
+
): string | undefined => {
|
|
14
|
+
const disabled = (rowData.disableEditColumn || []).find((r) => r.column === type);
|
|
15
|
+
if (disabled?.reason) {
|
|
16
|
+
return disabled.reason;
|
|
17
|
+
}
|
|
18
|
+
if (typeof rowData.extra?.editable === 'string') {
|
|
19
|
+
return rowData.extra.editable;
|
|
20
|
+
}
|
|
21
|
+
return rowData.extra?.editable === false ? `${type} is not editable.` : undefined;
|
|
22
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IJsonSchema } from '@flowgram-vue/json-schema';
|
|
7
|
+
|
|
8
|
+
import { ModeValueConfig, TypeEditorMode, TypeEditorProp, TypeEditorValue } from '../type';
|
|
9
|
+
import { modeValueConfig } from '../mode';
|
|
10
|
+
import { extraDeFormatter, extraFormatter, Formatter } from '../formatter';
|
|
11
|
+
import { traverseIJsonSchema } from '../../../services/utils';
|
|
12
|
+
|
|
13
|
+
export const useFormatter = <Mode extends TypeEditorMode, TypeSchema extends Partial<IJsonSchema>>({
|
|
14
|
+
extraConfig = {},
|
|
15
|
+
mode,
|
|
16
|
+
}: Pick<TypeEditorProp<Mode, TypeSchema>, 'mode' | 'extraConfig'>) => {
|
|
17
|
+
const { useExtra } = extraConfig;
|
|
18
|
+
const modeConfig = modeValueConfig.find((v) => v.mode === mode)! as unknown as ModeValueConfig<
|
|
19
|
+
Mode,
|
|
20
|
+
TypeSchema
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
const formatter = (value: TypeEditorValue<Mode, TypeSchema> | undefined) => {
|
|
24
|
+
const originSchema = value && modeConfig.convertValueToSchema(value as never);
|
|
25
|
+
let res = originSchema ? JSON.parse(JSON.stringify(originSchema)) : originSchema;
|
|
26
|
+
const formatters: Formatter[] = [];
|
|
27
|
+
if (useExtra) {
|
|
28
|
+
formatters.push(extraFormatter);
|
|
29
|
+
}
|
|
30
|
+
if (formatters.length !== 0 && res) {
|
|
31
|
+
traverseIJsonSchema(res, (type) => {
|
|
32
|
+
formatters.forEach((f) => f(type));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return res;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const deFormatter = (originSchema: TypeSchema | undefined) => {
|
|
39
|
+
let schema = originSchema ? JSON.parse(JSON.stringify(originSchema)) : originSchema;
|
|
40
|
+
const formatters: Formatter[] = [];
|
|
41
|
+
if (useExtra) {
|
|
42
|
+
formatters.push(extraDeFormatter);
|
|
43
|
+
}
|
|
44
|
+
if (formatters.length !== 0 && schema) {
|
|
45
|
+
traverseIJsonSchema(schema, (type) => {
|
|
46
|
+
formatters.forEach((f) => f(type));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return schema && modeConfig.convertSchemaToValue(schema as TypeSchema);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return { formatter, deFormatter, modeConfig };
|
|
53
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from './type';
|
|
7
|
+
export { columnConfigs as TypeEditorColumnConfigs } from './columns';
|
|
8
|
+
export { default as TypeEditor } from './type-editor.vue';
|
|
9
|
+
export { default as TypeEditorTable } from './table.vue';
|