@xbeeant/form-engine-designer 0.0.2 → 0.0.4

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 (137) hide show
  1. package/dist/es/Canvas.js +42 -46
  2. package/dist/es/Canvas.js.map +1 -1
  3. package/dist/es/Designer.js +121 -98
  4. package/dist/es/Designer.js.map +1 -1
  5. package/dist/es/DesignerContext.js +86 -73
  6. package/dist/es/DesignerContext.js.map +1 -1
  7. package/dist/es/Palette.js +3 -3
  8. package/dist/es/Palette.js.map +1 -1
  9. package/dist/es/PropertyPanel.js +97 -97
  10. package/dist/es/PropertyPanel.js.map +1 -1
  11. package/dist/es/catalog.js +19 -11
  12. package/dist/es/catalog.js.map +1 -1
  13. package/dist/es/designer-context.js +110 -0
  14. package/dist/es/designer-context.js.map +1 -0
  15. package/dist/es/history.js +73 -0
  16. package/dist/es/history.js.map +1 -0
  17. package/dist/es/index.js +3 -3
  18. package/dist/es/property/basic-property.js +6 -0
  19. package/dist/es/property/basic-property.js.map +1 -1
  20. package/dist/es/property-panel.js +294 -0
  21. package/dist/es/property-panel.js.map +1 -0
  22. package/dist/es/resizable-panel.js +103 -0
  23. package/dist/es/resizable-panel.js.map +1 -0
  24. package/dist/es/schema-utils.js +286 -0
  25. package/dist/es/schema-utils.js.map +1 -0
  26. package/dist/es/widgets/BindEditor.js +60 -49
  27. package/dist/es/widgets/BindEditor.js.map +1 -1
  28. package/dist/es/widgets/CodeEditor.js +41 -49
  29. package/dist/es/widgets/CodeEditor.js.map +1 -1
  30. package/dist/es/widgets/DefaultEditor.js +25 -30
  31. package/dist/es/widgets/DefaultEditor.js.map +1 -1
  32. package/dist/es/widgets/DependenciesEditor.js +21 -21
  33. package/dist/es/widgets/DependenciesEditor.js.map +1 -1
  34. package/dist/es/widgets/EnumEditor.js +25 -31
  35. package/dist/es/widgets/EnumEditor.js.map +1 -1
  36. package/dist/es/widgets/ExpressionSwitch.js +32 -30
  37. package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
  38. package/dist/es/widgets/ReactionsEditor.js +103 -103
  39. package/dist/es/widgets/ReactionsEditor.js.map +1 -1
  40. package/dist/es/widgets/RemoteDataEditor.js +63 -63
  41. package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
  42. package/dist/es/widgets/ValidateEditor.js +49 -49
  43. package/dist/es/widgets/ValidateEditor.js.map +1 -1
  44. package/dist/es/widgets/bind-editor.js +78 -0
  45. package/dist/es/widgets/bind-editor.js.map +1 -0
  46. package/dist/es/widgets/code-editor.js +76 -0
  47. package/dist/es/widgets/code-editor.js.map +1 -0
  48. package/dist/es/widgets/default-editor.js +50 -0
  49. package/dist/es/widgets/default-editor.js.map +1 -0
  50. package/dist/es/widgets/dependencies-editor.js +36 -0
  51. package/dist/es/widgets/dependencies-editor.js.map +1 -0
  52. package/dist/es/widgets/enum-editor.js +43 -0
  53. package/dist/es/widgets/enum-editor.js.map +1 -0
  54. package/dist/es/widgets/expression-builder.js +367 -0
  55. package/dist/es/widgets/expression-builder.js.map +1 -0
  56. package/dist/es/widgets/expression-model.js +340 -0
  57. package/dist/es/widgets/expression-model.js.map +1 -0
  58. package/dist/es/widgets/expression-switch.js +49 -0
  59. package/dist/es/widgets/expression-switch.js.map +1 -0
  60. package/dist/es/widgets/index.js +13 -13
  61. package/dist/es/widgets/index.js.map +1 -1
  62. package/dist/es/widgets/options-editor.js +102 -0
  63. package/dist/es/widgets/options-editor.js.map +1 -0
  64. package/dist/es/widgets/reactions-editor.js +401 -0
  65. package/dist/es/widgets/reactions-editor.js.map +1 -0
  66. package/dist/es/widgets/reactions-model.js +107 -0
  67. package/dist/es/widgets/reactions-model.js.map +1 -0
  68. package/dist/es/widgets/remote-data-editor.js +314 -0
  69. package/dist/es/widgets/remote-data-editor.js.map +1 -0
  70. package/dist/es/widgets/use-form-data-fields.js +16 -0
  71. package/dist/es/widgets/use-form-data-fields.js.map +1 -0
  72. package/dist/es/widgets/validate-editor.js +118 -0
  73. package/dist/es/widgets/validate-editor.js.map +1 -0
  74. package/package.json +4 -5
  75. package/dist/es/react/src/components/FormController.js +0 -154
  76. package/dist/es/react/src/components/FormController.js.map +0 -1
  77. package/dist/es/react/src/components/NexusField.js +0 -99
  78. package/dist/es/react/src/components/NexusField.js.map +0 -1
  79. package/dist/es/react/src/components/NexusForm.js +0 -121
  80. package/dist/es/react/src/components/NexusForm.js.map +0 -1
  81. package/dist/es/react/src/components/NexusFormProvider.js +0 -19
  82. package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
  83. package/dist/es/react/src/components/NexusLayout.js +0 -49
  84. package/dist/es/react/src/components/NexusLayout.js.map +0 -1
  85. package/dist/es/react/src/components/NexusObject.js +0 -65
  86. package/dist/es/react/src/components/NexusObject.js.map +0 -1
  87. package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
  88. package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
  89. package/dist/es/react/src/contexts/GridContext.js +0 -6
  90. package/dist/es/react/src/contexts/GridContext.js.map +0 -1
  91. package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
  92. package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
  93. package/dist/es/react/src/contexts/NexusContext.js +0 -13
  94. package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
  95. package/dist/es/react/src/hooks/useForm.js +0 -10
  96. package/dist/es/react/src/hooks/useForm.js.map +0 -1
  97. package/dist/es/react/src/utils/renderTreeNode.js +0 -18
  98. package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
  99. package/dist/es/react/src/utils/resolveColSpan.js +0 -10
  100. package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
  101. package/dist/es/schemaUtils.test.js +0 -188
  102. package/dist/es/schemaUtils.test.js.map +0 -1
  103. package/dist/es/vite-env.d.js +0 -2
  104. package/dist/es/vite-env.d.js.map +0 -1
  105. package/dist/es/widgets/expressionModel.test.js +0 -193
  106. package/dist/es/widgets/expressionModel.test.js.map +0 -1
  107. package/src/Canvas.tsx +0 -1111
  108. package/src/Designer.css +0 -206
  109. package/src/Designer.tsx +0 -254
  110. package/src/DesignerContext.tsx +0 -192
  111. package/src/Palette.tsx +0 -261
  112. package/src/PropertyPanel.tsx +0 -486
  113. package/src/ResizablePanel.tsx +0 -210
  114. package/src/catalog.ts +0 -526
  115. package/src/index.ts +0 -25
  116. package/src/property/basic-property.ts +0 -126
  117. package/src/schemaUtils.test.ts +0 -261
  118. package/src/schemaUtils.ts +0 -627
  119. package/src/types.ts +0 -38
  120. package/src/vite-env.d.ts +0 -1
  121. package/src/widgets/BindEditor.tsx +0 -105
  122. package/src/widgets/CodeEditor.tsx +0 -114
  123. package/src/widgets/DefaultEditor.tsx +0 -73
  124. package/src/widgets/DependenciesEditor.tsx +0 -56
  125. package/src/widgets/EnumEditor.tsx +0 -66
  126. package/src/widgets/ExpressionBuilder.tsx +0 -509
  127. package/src/widgets/ExpressionSwitch.tsx +0 -62
  128. package/src/widgets/OptionsEditor.tsx +0 -141
  129. package/src/widgets/ReactionsEditor.tsx +0 -532
  130. package/src/widgets/RemoteDataEditor.tsx +0 -382
  131. package/src/widgets/ValidateEditor.tsx +0 -162
  132. package/src/widgets/expressionModel.test.ts +0 -262
  133. package/src/widgets/expressionModel.ts +0 -573
  134. package/src/widgets/index.ts +0 -45
  135. package/src/widgets/reactionsModel.ts +0 -190
  136. package/src/widgets/useFormDataFields.ts +0 -21
  137. /package/dist/es/{form-engine-designer.css → index.css} +0 -0
@@ -1,105 +0,0 @@
1
- // ============================================================================
2
- // BindEditor — 编辑 bind 属性(路径映射)
3
- // 支持四种模式:无/字符串/数组/false,不同模式显示不同的子控件
4
- // ============================================================================
5
-
6
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
7
- import { Form, Input, Select } from 'antd';
8
- import { useState } from 'react';
9
-
10
- function getBindMode(value: unknown): string {
11
- if (value === false || value === 'false') {
12
- return 'false';
13
- }
14
- if (Array.isArray(value)) {
15
- return 'array';
16
- }
17
- if (typeof value === 'string' && value.length > 0) {
18
- return 'string';
19
- }
20
- return 'none';
21
- }
22
-
23
- const BIND_MODE_OPTIONS = [
24
- { value: 'none', label: '无(使用字段路径)' },
25
- { value: 'string', label: '字符串路径' },
26
- { value: 'array', label: '字符串数组(多路径拆分)' },
27
- { value: 'false', label: '不绑定(不提交)' },
28
- ];
29
-
30
- export function BindEditor({ value, onChange, title }: WidgetProps) {
31
- const mode = getBindMode(value);
32
- // 本地状态缓存文本值,防止每次按键都触发 onChange
33
- const [textValue, setTextValue] = useState<string>(
34
- Array.isArray(value)
35
- ? (value as string[]).join('\n')
36
- : typeof value === 'string'
37
- ? value
38
- : '',
39
- );
40
-
41
- const handleModeChange = (newMode: string) => {
42
- if (newMode === 'none') {
43
- onChange(undefined);
44
- setTextValue('');
45
- } else if (newMode === 'string') {
46
- onChange('');
47
- setTextValue('');
48
- } else if (newMode === 'array') {
49
- onChange([]);
50
- setTextValue('');
51
- } else if (newMode === 'false') {
52
- onChange(false);
53
- setTextValue('');
54
- }
55
- };
56
-
57
- const handleTextCommit = () => {
58
- if (mode === 'string') {
59
- onChange(textValue || undefined);
60
- } else if (mode === 'array') {
61
- const lines = textValue
62
- .split('\n')
63
- .map((s) => s.trim())
64
- .filter((s) => s.length > 0);
65
- onChange(lines.length > 0 ? lines : undefined);
66
- }
67
- };
68
-
69
- return (
70
- <Form.Item layout={'vertical'} label={title} style={{ width: '100%' }}>
71
- <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
72
- <Select
73
- value={mode}
74
- options={BIND_MODE_OPTIONS}
75
- onChange={handleModeChange}
76
- style={{ width: '100%' }}
77
- />
78
- {mode === 'string' && (
79
- <Input
80
- placeholder='如: user.name'
81
- value={textValue}
82
- onChange={(e) => setTextValue(e.target.value)}
83
- onBlur={handleTextCommit}
84
- onPressEnter={handleTextCommit}
85
- />
86
- )}
87
- {mode === 'array' && (
88
- <Input.TextArea
89
- rows={3}
90
- placeholder={'如: a.b\nc.d'}
91
- value={textValue}
92
- onChange={(e) => setTextValue(e.target.value)}
93
- onBlur={handleTextCommit}
94
- />
95
- )}
96
- <div style={{ fontSize: 11, color: '#999' }}>
97
- 提示:bind 将字段值映射到不同数据路径。string[]
98
- 表示字段值数组按顺序拆分到多个路径;false 表示字段不参与提交。
99
- </div>
100
- </div>
101
- </Form.Item>
102
- );
103
- }
104
-
105
- export const bindEditorWidget = BindEditor;
@@ -1,114 +0,0 @@
1
- // ============================================================================
2
- // CodeEditor — 属性面板专用 JSON 编辑器
3
- // 供 remoteData 等对象配置使用:支持语法校验、格式化与占位符示例
4
- // 值格式:object | array(非法 JSON 不写回,光标处自动提示)
5
- // ============================================================================
6
-
7
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
8
- import { Button, Form, Input, Space, Typography } from 'antd';
9
- import { useMemo, useState } from 'react';
10
-
11
- export function CodeEditor({
12
- value,
13
- onChange,
14
- title,
15
- description,
16
- placeholder,
17
- ...rest
18
- }: WidgetProps) {
19
- const fieldProps = rest as Record<string, unknown>;
20
- const lines = (fieldProps?.lines as number) ?? 10;
21
-
22
- const [error, setError] = useState<string | null>(null);
23
-
24
- // 编辑态文案:初始为 JSON 字符串;解析失败时保留原文(不打断输入)
25
- const [draft, setDraft] = useState<string | undefined>(undefined);
26
-
27
- const text = useMemo(() => {
28
- if (draft !== undefined) {
29
- return draft;
30
- }
31
- if (value === undefined || value === null) {
32
- return '';
33
- }
34
- try {
35
- return JSON.stringify(value, null, 2);
36
- } catch {
37
- return String(value);
38
- }
39
- }, [draft, value]);
40
-
41
- const handleChange = (raw: string) => {
42
- setDraft(raw);
43
- const trimmed = raw.trim();
44
- if (!trimmed) {
45
- setError(null);
46
- onChange(undefined);
47
- return;
48
- }
49
- try {
50
- const parsed = JSON.parse(trimmed);
51
- setError(null);
52
- onChange(parsed);
53
- } catch (e) {
54
- setError(e instanceof Error ? e.message : 'JSON 解析失败');
55
- }
56
- };
57
-
58
- const handleFormat = () => {
59
- try {
60
- const formatted = JSON.stringify(JSON.parse(text), null, 2);
61
- setDraft(formatted);
62
- setError(null);
63
- } catch {
64
- // 语法非法时静默,保留原文
65
- }
66
- };
67
-
68
- return (
69
- <Form.Item
70
- label={title}
71
- layout={'vertical'}
72
- style={{ width: '100%' }}
73
- help={
74
- error ? (
75
- <Typography.Text type='danger' style={{ fontSize: 11 }}>
76
- {error}
77
- </Typography.Text>
78
- ) : description ? (
79
- <span style={{ fontSize: 11, color: '#999' }}>{description}</span>
80
- ) : undefined
81
- }
82
- validateStatus={error ? 'error' : undefined}
83
- >
84
- <Input.TextArea
85
- value={text}
86
- rows={lines}
87
- onChange={(e) => handleChange(e.target.value)}
88
- placeholder={
89
- placeholder
90
- ? `示例:\n${typeof placeholder === 'string' ? placeholder : JSON.stringify(placeholder, null, 2)}`
91
- : '{}'
92
- }
93
- style={{ fontFamily: 'monospace', fontSize: 12 }}
94
- />
95
- <Space.Compact style={{ marginTop: 6, width: '100%' }}>
96
- <Button size='small' onClick={handleFormat} disabled={!!error}>
97
- 格式化
98
- </Button>
99
- <Button
100
- size='small'
101
- onClick={() => {
102
- setDraft('');
103
- setError(null);
104
- onChange(undefined);
105
- }}
106
- >
107
- 清空
108
- </Button>
109
- </Space.Compact>
110
- </Form.Item>
111
- );
112
- }
113
-
114
- export const codeEditorWidget = CodeEditor;
@@ -1,73 +0,0 @@
1
- // ============================================================================
2
- // DefaultEditor — 按字段类型编辑默认值
3
- // string → Input, number/integer → InputNumber, boolean → Switch, object/array → JSON TextArea
4
- // ============================================================================
5
-
6
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
7
- import { Form, Input, InputNumber, Switch } from 'antd';
8
-
9
- export function DefaultEditor({
10
- value,
11
- onChange,
12
- title,
13
- ...rest
14
- }: WidgetProps) {
15
- const fieldProps = rest as Record<string, unknown>;
16
- const dataType = (fieldProps?.dataType as string) ?? 'string';
17
-
18
- if (dataType === 'boolean') {
19
- return (
20
- <Form.Item label={title} style={{ width: '100%' }}>
21
- <Switch checked={!!value} onChange={(v) => onChange(v)} />
22
- </Form.Item>
23
- );
24
- }
25
-
26
- if (dataType === 'number' || dataType === 'integer') {
27
- return (
28
- <Form.Item label={title} style={{ width: '100%' }}>
29
- <InputNumber
30
- value={value as number}
31
- onChange={(v) => onChange(v ?? undefined)}
32
- style={{ width: '100%' }}
33
- />
34
- </Form.Item>
35
- );
36
- }
37
-
38
- if (dataType === 'object' || dataType === 'array') {
39
- const text = value !== undefined ? JSON.stringify(value, null, 2) : '';
40
- return (
41
- <Form.Item label={`${title}(JSON)`} style={{ width: '100%' }}>
42
- <Input.TextArea
43
- rows={3}
44
- value={text}
45
- onChange={(e) => {
46
- const t = e.target.value.trim();
47
- if (!t) {
48
- onChange(undefined);
49
- return;
50
- }
51
- try {
52
- onChange(JSON.parse(t));
53
- } catch {
54
- // JSON 解析失败时不更新
55
- }
56
- }}
57
- />
58
- </Form.Item>
59
- );
60
- }
61
-
62
- // string
63
- return (
64
- <Form.Item label={title} style={{ width: '100%' }}>
65
- <Input
66
- value={(value as string) ?? ''}
67
- onChange={(e) => onChange(e.target.value || undefined)}
68
- />
69
- </Form.Item>
70
- );
71
- }
72
-
73
- export const defaultEditorWidget = DefaultEditor;
@@ -1,56 +0,0 @@
1
- // ============================================================================
2
- // DependenciesEditor — 编辑 dependencies 依赖字段路径(string[])
3
- // 通过标签选择器从表单字段中选择依赖路径,也支持手动输入自定义路径
4
- // ============================================================================
5
-
6
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
7
- import { Form, Select } from 'antd';
8
- import { useEffect, useRef, useState } from 'react';
9
- import { useFormDataFieldOptions } from './useFormDataFields';
10
-
11
- export function DependenciesEditor({ value, onChange, title }: WidgetProps) {
12
- const fieldOptions = useFormDataFieldOptions();
13
- const [tags, setTags] = useState<string[]>(() =>
14
- Array.isArray(value) ? [...value] : [],
15
- );
16
- const prevValueRef = useRef(value);
17
- const lastEmittedRef = useRef(value);
18
-
19
- // 外部 value 变化(切换节点等)时重置,自身 onChange 回传不重置
20
- useEffect(() => {
21
- if (prevValueRef.current !== value) {
22
- prevValueRef.current = value;
23
- if (value !== lastEmittedRef.current) {
24
- setTags(Array.isArray(value) ? [...value] : []);
25
- }
26
- }
27
- }, [value]);
28
-
29
- const commit = (next: string[]) => {
30
- setTags(next);
31
- lastEmittedRef.current = next;
32
- onChange(next.length > 0 ? next : undefined);
33
- };
34
-
35
- return (
36
- <Form.Item layout={'vertical'} label={title} style={{ width: '100%' }}>
37
- <Select
38
- size='small'
39
- mode='tags'
40
- value={tags}
41
- onChange={(v) =>
42
- commit((v as string[]).map((s) => s.trim()).filter(Boolean))
43
- }
44
- options={fieldOptions}
45
- placeholder='选择或输入依赖字段路径'
46
- style={{ width: '100%' }}
47
- />
48
- <div style={{ fontSize: 11, color: '#999', marginTop: 4 }}>
49
- 提示:依赖字段值变化时,触发该字段重新求值与渲染。支持相对路径(如 处于
50
- address.city 时填 province 表示 address.province)。
51
- </div>
52
- </Form.Item>
53
- );
54
- }
55
-
56
- export const dependenciesEditorWidget = DependenciesEditor;
@@ -1,66 +0,0 @@
1
- // ============================================================================
2
- // EnumEditor — 编辑 enum 枚举值及 enumNames 枚举文案
3
- // 使用两个 TextArea,值按行分隔
4
- // ============================================================================
5
-
6
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
7
- import { Form, Input } from 'antd';
8
-
9
- export function EnumEditor({ value, onChange }: WidgetProps) {
10
- // value 是一个包含 _enum 和 _enumNames 的对象(由 engine 传入)
11
- // 实际上 engine 传入的 value 是字段存储的原始值
12
- // 这里我们使用单独的 _enum 和 _enumNames key
13
- // 实际上这个 widget 对应 schema 中的 _enum 字段
14
- // 但 engine 只传单个 value
15
-
16
- // 这里简化处理:value 是 _enum 的序列化字符串
17
- // _enumNames 通过 props 或 dependValues 传递
18
- // 但更简洁的方式是让 schema 中有两个独立字段
19
-
20
- // 这个 widget 实际上应该对应两个字段。让我们重新考虑...
21
- // 在 schema 中,我们只定义了一个 _enum 字段,这个 widget 渲染时
22
- // value 是 _enum 的值(序列化字符串)
23
-
24
- return (
25
- <>
26
- <Form.Item label='枚举值(enum,每行一个)' style={{ width: '100%' }}>
27
- <Input.TextArea
28
- rows={3}
29
- value={typeof value === 'string' ? value : ''}
30
- onChange={(e) => onChange(e.target.value)}
31
- placeholder='每行一个枚举值'
32
- />
33
- </Form.Item>
34
- {/* _enumNames 由调用方通过额外逻辑处理 */}
35
- <div
36
- style={{ fontSize: 11, color: '#999', marginTop: -12, marginBottom: 8 }}
37
- >
38
- 提示:枚举值用于选项的值,枚举文案见下方字段
39
- </div>
40
- </>
41
- );
42
- }
43
-
44
- export const enumEditorWidget = EnumEditor;
45
-
46
- // ────────────────────────────────────────────────────────────────────────────
47
- // EnumNamesEditor — 编辑 enumNames(与 _enum 配对使用)
48
- // ────────────────────────────────────────────────────────────────────────────
49
-
50
- export function EnumNamesEditor({ value, onChange }: WidgetProps) {
51
- return (
52
- <Form.Item
53
- label='枚举文案(enumNames,每行一个)'
54
- style={{ width: '100%' }}
55
- >
56
- <Input.TextArea
57
- rows={3}
58
- value={typeof value === 'string' ? value : ''}
59
- onChange={(e) => onChange(e.target.value)}
60
- placeholder='每行一个枚举文案,与枚举值一一对应'
61
- />
62
- </Form.Item>
63
- );
64
- }
65
-
66
- export const enumNamesEditorWidget = EnumNamesEditor;