@xbeeant/form-engine-designer 0.0.3 → 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.
- package/dist/es/Canvas.js +42 -47
- package/dist/es/Canvas.js.map +1 -1
- package/dist/es/Designer.js +121 -98
- package/dist/es/Designer.js.map +1 -1
- package/dist/es/DesignerContext.js +86 -73
- package/dist/es/DesignerContext.js.map +1 -1
- package/dist/es/Palette.js +3 -3
- package/dist/es/Palette.js.map +1 -1
- package/dist/es/PropertyPanel.js +99 -100
- package/dist/es/PropertyPanel.js.map +1 -1
- package/dist/es/catalog.js +17 -10
- package/dist/es/catalog.js.map +1 -1
- package/dist/es/designer-context.js +110 -0
- package/dist/es/designer-context.js.map +1 -0
- package/dist/es/history.js +73 -0
- package/dist/es/history.js.map +1 -0
- package/dist/es/index.css +1 -1
- package/dist/es/index.js +3 -3
- package/dist/es/property/basic-property.js +6 -0
- package/dist/es/property/basic-property.js.map +1 -1
- package/dist/es/property-panel.js +294 -0
- package/dist/es/property-panel.js.map +1 -0
- package/dist/es/resizable-panel.js +103 -0
- package/dist/es/resizable-panel.js.map +1 -0
- package/dist/es/schema-utils.js +286 -0
- package/dist/es/schema-utils.js.map +1 -0
- package/dist/es/widgets/BindEditor.js +60 -49
- package/dist/es/widgets/BindEditor.js.map +1 -1
- package/dist/es/widgets/CodeEditor.js +41 -49
- package/dist/es/widgets/CodeEditor.js.map +1 -1
- package/dist/es/widgets/DefaultEditor.js +25 -30
- package/dist/es/widgets/DefaultEditor.js.map +1 -1
- package/dist/es/widgets/DependenciesEditor.js +21 -21
- package/dist/es/widgets/DependenciesEditor.js.map +1 -1
- package/dist/es/widgets/EnumEditor.js +25 -31
- package/dist/es/widgets/EnumEditor.js.map +1 -1
- package/dist/es/widgets/ExpressionSwitch.js +32 -30
- package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
- package/dist/es/widgets/ReactionsEditor.js +103 -103
- package/dist/es/widgets/ReactionsEditor.js.map +1 -1
- package/dist/es/widgets/RemoteDataEditor.js +63 -63
- package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
- package/dist/es/widgets/ValidateEditor.js +49 -49
- package/dist/es/widgets/ValidateEditor.js.map +1 -1
- package/dist/es/widgets/bind-editor.js +78 -0
- package/dist/es/widgets/bind-editor.js.map +1 -0
- package/dist/es/widgets/code-editor.js +76 -0
- package/dist/es/widgets/code-editor.js.map +1 -0
- package/dist/es/widgets/default-editor.js +50 -0
- package/dist/es/widgets/default-editor.js.map +1 -0
- package/dist/es/widgets/dependencies-editor.js +36 -0
- package/dist/es/widgets/dependencies-editor.js.map +1 -0
- package/dist/es/widgets/enum-editor.js +43 -0
- package/dist/es/widgets/enum-editor.js.map +1 -0
- package/dist/es/widgets/expression-builder.js +367 -0
- package/dist/es/widgets/expression-builder.js.map +1 -0
- package/dist/es/widgets/expression-model.js +340 -0
- package/dist/es/widgets/expression-model.js.map +1 -0
- package/dist/es/widgets/expression-switch.js +49 -0
- package/dist/es/widgets/expression-switch.js.map +1 -0
- package/dist/es/widgets/index.js +13 -13
- package/dist/es/widgets/index.js.map +1 -1
- package/dist/es/widgets/options-editor.js +102 -0
- package/dist/es/widgets/options-editor.js.map +1 -0
- package/dist/es/widgets/reactions-editor.js +401 -0
- package/dist/es/widgets/reactions-editor.js.map +1 -0
- package/dist/es/widgets/reactions-model.js +107 -0
- package/dist/es/widgets/reactions-model.js.map +1 -0
- package/dist/es/widgets/remote-data-editor.js +314 -0
- package/dist/es/widgets/remote-data-editor.js.map +1 -0
- package/dist/es/widgets/use-form-data-fields.js +16 -0
- package/dist/es/widgets/use-form-data-fields.js.map +1 -0
- package/dist/es/widgets/validate-editor.js +118 -0
- package/dist/es/widgets/validate-editor.js.map +1 -0
- package/package.json +4 -5
- package/dist/es/react/src/components/FormController.js +0 -160
- package/dist/es/react/src/components/FormController.js.map +0 -1
- package/dist/es/react/src/components/NexusField.js +0 -98
- package/dist/es/react/src/components/NexusField.js.map +0 -1
- package/dist/es/react/src/components/NexusForm.js +0 -128
- package/dist/es/react/src/components/NexusForm.js.map +0 -1
- package/dist/es/react/src/components/NexusFormProvider.js +0 -19
- package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
- package/dist/es/react/src/components/NexusLayout.js +0 -49
- package/dist/es/react/src/components/NexusLayout.js.map +0 -1
- package/dist/es/react/src/components/NexusObject.js +0 -65
- package/dist/es/react/src/components/NexusObject.js.map +0 -1
- package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
- package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
- package/dist/es/react/src/contexts/GridContext.js +0 -6
- package/dist/es/react/src/contexts/GridContext.js.map +0 -1
- package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
- package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
- package/dist/es/react/src/contexts/NexusContext.js +0 -13
- package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
- package/dist/es/react/src/hooks/useForm.js +0 -13
- package/dist/es/react/src/hooks/useForm.js.map +0 -1
- package/dist/es/react/src/utils/renderTreeNode.js +0 -18
- package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
- package/dist/es/react/src/utils/resolveColSpan.js +0 -10
- package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
- package/src/Canvas.tsx +0 -1111
- package/src/Designer.css +0 -206
- package/src/Designer.tsx +0 -254
- package/src/DesignerContext.tsx +0 -192
- package/src/Palette.tsx +0 -261
- package/src/PropertyPanel.tsx +0 -486
- package/src/ResizablePanel.tsx +0 -210
- package/src/catalog.ts +0 -527
- package/src/index.ts +0 -25
- package/src/property/basic-property.ts +0 -126
- package/src/schemaUtils.test.ts +0 -261
- package/src/schemaUtils.ts +0 -627
- package/src/types.ts +0 -38
- package/src/vite-env.d.ts +0 -1
- package/src/widgets/BindEditor.tsx +0 -105
- package/src/widgets/CodeEditor.tsx +0 -114
- package/src/widgets/DefaultEditor.tsx +0 -73
- package/src/widgets/DependenciesEditor.tsx +0 -56
- package/src/widgets/EnumEditor.tsx +0 -66
- package/src/widgets/ExpressionBuilder.tsx +0 -509
- package/src/widgets/ExpressionSwitch.tsx +0 -62
- package/src/widgets/OptionsEditor.tsx +0 -141
- package/src/widgets/ReactionsEditor.tsx +0 -532
- package/src/widgets/RemoteDataEditor.tsx +0 -382
- package/src/widgets/ValidateEditor.tsx +0 -162
- package/src/widgets/expressionModel.test.ts +0 -262
- package/src/widgets/expressionModel.ts +0 -573
- package/src/widgets/index.ts +0 -45
- package/src/widgets/reactionsModel.ts +0 -190
- package/src/widgets/useFormDataFields.ts +0 -21
package/dist/es/Designer.js
CHANGED
|
@@ -1,162 +1,185 @@
|
|
|
1
|
-
import { jsx as t, jsxs as
|
|
2
|
-
import { widgetSchemas as
|
|
3
|
-
import { Typography as
|
|
4
|
-
import { useMemo as
|
|
5
|
-
import { Canvas as
|
|
6
|
-
/* empty css
|
|
7
|
-
import { layoutCatalog as
|
|
8
|
-
import { DesignerProvider as
|
|
9
|
-
import { Palette as
|
|
10
|
-
import { PropertyPanel as
|
|
11
|
-
import { ResizablePanel as
|
|
12
|
-
const
|
|
1
|
+
import { jsx as t, jsxs as m } from "react/jsx-runtime";
|
|
2
|
+
import { widgetSchemas as O, registerAntdUI as P } from "@xbeeant/form-engine-ui";
|
|
3
|
+
import { Typography as U, Space as B, Radio as u, Button as f, Popconfirm as J, Input as W, message as S } from "antd";
|
|
4
|
+
import { useMemo as w, useState as x, useEffect as K } from "react";
|
|
5
|
+
import { Canvas as M } from "./canvas.js";
|
|
6
|
+
/* empty css */
|
|
7
|
+
import { layoutCatalog as z, widgetCatalog as I } from "./catalog.js";
|
|
8
|
+
import { DesignerProvider as F, useDesigner as G } from "./designer-context.js";
|
|
9
|
+
import { Palette as H } from "./palette.js";
|
|
10
|
+
import { PropertyPanel as X } from "./property-panel.js";
|
|
11
|
+
import { ResizablePanel as N } from "./resizable-panel.js";
|
|
12
|
+
const C = {
|
|
13
13
|
type: "object",
|
|
14
14
|
displayType: "row",
|
|
15
15
|
properties: {}
|
|
16
16
|
};
|
|
17
|
-
function
|
|
18
|
-
schema:
|
|
19
|
-
propertySchemaMap:
|
|
20
|
-
onSchemaChange:
|
|
21
|
-
registerUI:
|
|
22
|
-
fields:
|
|
23
|
-
widgetCatalog:
|
|
24
|
-
layoutCatalog:
|
|
17
|
+
function ae({
|
|
18
|
+
schema: i,
|
|
19
|
+
propertySchemaMap: l = O,
|
|
20
|
+
onSchemaChange: c,
|
|
21
|
+
registerUI: s = P,
|
|
22
|
+
fields: h,
|
|
23
|
+
widgetCatalog: a,
|
|
24
|
+
layoutCatalog: d
|
|
25
25
|
}) {
|
|
26
|
-
const
|
|
27
|
-
() =>
|
|
28
|
-
[
|
|
29
|
-
),
|
|
30
|
-
() =>
|
|
31
|
-
[
|
|
32
|
-
),
|
|
33
|
-
() =>
|
|
34
|
-
[
|
|
26
|
+
const g = w(
|
|
27
|
+
() => i ?? C,
|
|
28
|
+
[i]
|
|
29
|
+
), y = w(
|
|
30
|
+
() => T(I, a, "widget"),
|
|
31
|
+
[a]
|
|
32
|
+
), p = w(
|
|
33
|
+
() => T(z, d, "layoutType"),
|
|
34
|
+
[d]
|
|
35
35
|
);
|
|
36
36
|
return /* @__PURE__ */ t(
|
|
37
|
-
|
|
37
|
+
F,
|
|
38
38
|
{
|
|
39
|
-
initialSchema:
|
|
40
|
-
onSchemaChange:
|
|
41
|
-
registerUI:
|
|
42
|
-
fields:
|
|
43
|
-
propertySchemaMap:
|
|
44
|
-
widgetCatalog:
|
|
45
|
-
layoutCatalog:
|
|
46
|
-
children: /* @__PURE__ */ t(
|
|
39
|
+
initialSchema: g,
|
|
40
|
+
onSchemaChange: c,
|
|
41
|
+
registerUI: s,
|
|
42
|
+
fields: h,
|
|
43
|
+
propertySchemaMap: l,
|
|
44
|
+
widgetCatalog: y,
|
|
45
|
+
layoutCatalog: p,
|
|
46
|
+
children: /* @__PURE__ */ t(Z, {})
|
|
47
47
|
}
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
|
-
function
|
|
51
|
-
if (!
|
|
52
|
-
return
|
|
53
|
-
const
|
|
54
|
-
return [...
|
|
50
|
+
function T(i, l, c) {
|
|
51
|
+
if (!l || l.length === 0)
|
|
52
|
+
return i;
|
|
53
|
+
const s = new Set(l.map((a) => a[c]).filter(Boolean));
|
|
54
|
+
return [...i.filter((a) => !s.has(a[c])), ...l];
|
|
55
55
|
}
|
|
56
|
-
function
|
|
57
|
-
const {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
c
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
function Z() {
|
|
57
|
+
const {
|
|
58
|
+
mode: i,
|
|
59
|
+
setMode: l,
|
|
60
|
+
schema: c,
|
|
61
|
+
setSchema: s,
|
|
62
|
+
selectNode: h,
|
|
63
|
+
undo: a,
|
|
64
|
+
redo: d,
|
|
65
|
+
canUndo: g,
|
|
66
|
+
canRedo: y
|
|
67
|
+
} = G(), [p, v] = x(""), [j, b] = x(!1);
|
|
68
|
+
K(() => {
|
|
69
|
+
const e = (n) => {
|
|
70
|
+
const o = n.target;
|
|
71
|
+
if (o && (o.tagName === "INPUT" || o.tagName === "TEXTAREA" || o.isContentEditable) || !(n.metaKey || n.ctrlKey))
|
|
72
|
+
return;
|
|
73
|
+
const r = n.key.toLowerCase();
|
|
74
|
+
r === "z" ? (n.preventDefault(), n.shiftKey ? d() : a()) : r === "y" && (n.preventDefault(), d());
|
|
75
|
+
};
|
|
76
|
+
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
77
|
+
}, [a, d]);
|
|
78
|
+
const k = () => {
|
|
79
|
+
s(C), h(null), i === "schema" && v(JSON.stringify(C, null, 2));
|
|
80
|
+
}, E = () => {
|
|
81
|
+
v(JSON.stringify(c, null, 2)), b(!0);
|
|
82
|
+
}, R = () => {
|
|
83
|
+
if (j) {
|
|
63
84
|
try {
|
|
64
|
-
const e = JSON.parse(
|
|
65
|
-
e && e.type === "object" && e.properties ?
|
|
85
|
+
const e = JSON.parse(p);
|
|
86
|
+
e && e.type === "object" && e.properties ? s(e) : S.warning('Schema 必须包含 type: "object" 和 properties');
|
|
66
87
|
} catch {
|
|
67
|
-
|
|
88
|
+
S.error("JSON 格式错误,无法解析");
|
|
68
89
|
}
|
|
69
|
-
|
|
90
|
+
b(!1);
|
|
70
91
|
}
|
|
71
|
-
},
|
|
72
|
-
e === "schema" ?
|
|
73
|
-
},
|
|
74
|
-
const e = JSON.stringify(
|
|
75
|
-
|
|
76
|
-
},
|
|
92
|
+
}, A = (e) => {
|
|
93
|
+
e === "schema" ? E() : R(), l(e);
|
|
94
|
+
}, L = () => {
|
|
95
|
+
const e = JSON.stringify(c, null, 2), n = new Blob([e], { type: "application/json" }), o = URL.createObjectURL(n), r = document.createElement("a");
|
|
96
|
+
r.href = o, r.download = "schema.json", r.click(), URL.revokeObjectURL(o);
|
|
97
|
+
}, D = () => {
|
|
77
98
|
const e = document.createElement("input");
|
|
78
99
|
e.type = "file", e.accept = "application/json", e.onchange = () => {
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
100
|
+
const n = e.files?.[0];
|
|
101
|
+
if (!n)
|
|
81
102
|
return;
|
|
82
|
-
const
|
|
83
|
-
|
|
103
|
+
const o = new FileReader();
|
|
104
|
+
o.onload = () => {
|
|
84
105
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
106
|
+
const r = JSON.parse(o.result);
|
|
107
|
+
r && r.type === "object" && r.properties && s(r);
|
|
87
108
|
} catch {
|
|
88
109
|
}
|
|
89
|
-
},
|
|
110
|
+
}, o.readAsText(n);
|
|
90
111
|
}, e.click();
|
|
91
112
|
};
|
|
92
|
-
return /* @__PURE__ */
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
-
/* @__PURE__ */ t(
|
|
95
|
-
/* @__PURE__ */
|
|
96
|
-
/* @__PURE__ */
|
|
97
|
-
|
|
113
|
+
return /* @__PURE__ */ m("div", { className: "flex flex-col h-full bg-white", children: [
|
|
114
|
+
/* @__PURE__ */ m("div", { className: "flex items-center justify-between px-4 py-2 border-b border-[#f0f0f0] bg-[#fafafa]", children: [
|
|
115
|
+
/* @__PURE__ */ t(U.Text, { strong: !0, children: "Nexus 表单设计器" }),
|
|
116
|
+
/* @__PURE__ */ m(B, { children: [
|
|
117
|
+
/* @__PURE__ */ m(
|
|
118
|
+
u.Group,
|
|
98
119
|
{
|
|
99
|
-
value:
|
|
100
|
-
onChange: (e) =>
|
|
120
|
+
value: i,
|
|
121
|
+
onChange: (e) => A(e.target.value),
|
|
101
122
|
children: [
|
|
102
|
-
/* @__PURE__ */ t(
|
|
103
|
-
/* @__PURE__ */ t(
|
|
104
|
-
/* @__PURE__ */ t(
|
|
123
|
+
/* @__PURE__ */ t(u.Button, { value: "design", children: "设计" }),
|
|
124
|
+
/* @__PURE__ */ t(u.Button, { value: "preview", children: "预览" }),
|
|
125
|
+
/* @__PURE__ */ t(u.Button, { value: "schema", children: "Schema" })
|
|
105
126
|
]
|
|
106
127
|
}
|
|
107
128
|
),
|
|
108
|
-
/* @__PURE__ */ t(
|
|
109
|
-
/* @__PURE__ */ t(
|
|
129
|
+
/* @__PURE__ */ t(f, { onClick: D, children: "导入" }),
|
|
130
|
+
/* @__PURE__ */ t(f, { onClick: L, children: "导出" }),
|
|
131
|
+
/* @__PURE__ */ t(f, { disabled: !g, onClick: a, children: "撤销" }),
|
|
132
|
+
/* @__PURE__ */ t(f, { disabled: !y, onClick: d, children: "重做" }),
|
|
110
133
|
/* @__PURE__ */ t(
|
|
111
|
-
|
|
134
|
+
J,
|
|
112
135
|
{
|
|
113
136
|
title: "清空画布",
|
|
114
|
-
description: "
|
|
137
|
+
description: "将移除画布上所有组件,可使用 Ctrl/Cmd+Z 撤销",
|
|
115
138
|
okText: "清空",
|
|
116
139
|
cancelText: "取消",
|
|
117
140
|
okButtonProps: { danger: !0 },
|
|
118
|
-
onConfirm:
|
|
119
|
-
children: /* @__PURE__ */ t(
|
|
141
|
+
onConfirm: k,
|
|
142
|
+
children: /* @__PURE__ */ t(f, { danger: !0, children: "清空" })
|
|
120
143
|
}
|
|
121
144
|
)
|
|
122
145
|
] })
|
|
123
146
|
] }),
|
|
124
|
-
|
|
125
|
-
|
|
147
|
+
i === "schema" ? /* @__PURE__ */ t("div", { className: "flex-1 overflow-auto p-4 bg-[#1e1e1e]", children: /* @__PURE__ */ t(
|
|
148
|
+
W.TextArea,
|
|
126
149
|
{
|
|
127
|
-
value:
|
|
128
|
-
onChange: (e) =>
|
|
150
|
+
value: p,
|
|
151
|
+
onChange: (e) => v(e.target.value),
|
|
129
152
|
autoSize: { minRows: 20 },
|
|
130
153
|
className: "font-mono! text-[13px]! leading-[1.6]! bg-[#1e1e1e]! text-[#d4d4d4]! border-[#3e3e42]!"
|
|
131
154
|
}
|
|
132
|
-
) }) : /* @__PURE__ */
|
|
155
|
+
) }) : /* @__PURE__ */ m("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
133
156
|
/* @__PURE__ */ t(
|
|
134
|
-
|
|
157
|
+
N,
|
|
135
158
|
{
|
|
136
159
|
side: "left",
|
|
137
|
-
defaultWidth:
|
|
160
|
+
defaultWidth: 320,
|
|
138
161
|
minWidth: 160,
|
|
139
162
|
maxWidth: 420,
|
|
140
163
|
collapsedTitle: "组件面板",
|
|
141
|
-
children: /* @__PURE__ */ t(
|
|
164
|
+
children: /* @__PURE__ */ t(H, {})
|
|
142
165
|
}
|
|
143
166
|
),
|
|
144
|
-
/* @__PURE__ */ t(
|
|
167
|
+
/* @__PURE__ */ t(M, {}),
|
|
145
168
|
/* @__PURE__ */ t(
|
|
146
|
-
|
|
169
|
+
N,
|
|
147
170
|
{
|
|
148
171
|
side: "right",
|
|
149
172
|
defaultWidth: 320,
|
|
150
173
|
minWidth: 240,
|
|
151
|
-
maxWidth:
|
|
174
|
+
maxWidth: 800,
|
|
152
175
|
collapsedTitle: "属性面板",
|
|
153
|
-
children: /* @__PURE__ */ t(
|
|
176
|
+
children: /* @__PURE__ */ t(X, {})
|
|
154
177
|
}
|
|
155
178
|
)
|
|
156
179
|
] })
|
|
157
180
|
] });
|
|
158
181
|
}
|
|
159
182
|
export {
|
|
160
|
-
|
|
183
|
+
ae as Designer
|
|
161
184
|
};
|
|
162
|
-
//# sourceMappingURL=
|
|
185
|
+
//# sourceMappingURL=designer.js.map
|
package/dist/es/Designer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Designer.js","sources":["../../src/Designer.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 主组件\n// ============================================================================\n\nimport type { NexusEngine, NexusSchema } from '@xbeeant/form-engine';\nimport type { PropertySchemaMap } from '@xbeeant/form-engine-ui';\nimport { registerAntdUI, widgetSchemas } from '@xbeeant/form-engine-ui';\nimport {\n Button,\n Input,\n message,\n Popconfirm,\n Radio,\n Space,\n Typography,\n} from 'antd';\nimport { useMemo, useState } from 'react';\nimport { Canvas } from './Canvas';\nimport './Designer.css';\nimport { layoutCatalog, widgetCatalog } from './catalog';\nimport { DesignerProvider, useDesigner } from './DesignerContext';\nimport { Palette } from './Palette';\nimport { PropertyPanel } from './PropertyPanel';\nimport { ResizablePanel } from './ResizablePanel';\nimport type { CatalogItem, DesignerMode, FieldDef } from './types';\n\nconst DEFAULT_SCHEMA: NexusSchema = {\n type: 'object',\n displayType: 'row',\n properties: {},\n};\n\nexport interface DesignerProps {\n schema?: NexusSchema;\n /**\n * widget 名 → 属性描述符映射,决定属性面板「组件属性」分区展示哪些配置项。\n * 默认使用 @xbeeant/form-engine-ui 的 widgetSchemas(antd 组件全覆盖);\n * 传入自定义映射可整体替换,也可在默认基础上按 widget 名覆盖(如\n * `{ ...widgetSchemas, input: myInputSchema }`)。\n */\n propertySchemaMap?: PropertySchemaMap;\n onSchemaChange?: (schema: NexusSchema) => void;\n /**\n * UI 注册函数,向引擎注册 widgets 和 layouts。\n * 默认使用 @xbeeant/form-engine-ui 的 registerAntdUI;接入其它 UI 库时传入自定义注册函数。\n */\n registerUI?: (engine: NexusEngine) => void;\n /** 外部字段列表,传入后左侧 palette 多一个「字段列表」分组 */\n fields?: FieldDef[];\n /** 额外的 widget 目录项,与内置 widgetCatalog 合并(同 widget 名时外部覆盖内置) */\n widgetCatalog?: CatalogItem[];\n /** 额外的 layout 目录项,与内置 layoutCatalog 合并(同 layoutType 时外部覆盖内置) */\n layoutCatalog?: CatalogItem[];\n}\n\nexport function Designer({\n schema,\n propertySchemaMap = widgetSchemas,\n onSchemaChange,\n registerUI = registerAntdUI,\n fields,\n widgetCatalog: extraWidgetCatalog,\n layoutCatalog: extraLayoutCatalog,\n}: DesignerProps) {\n const initialSchema = useMemo<NexusSchema>(\n () => schema ?? DEFAULT_SCHEMA,\n [schema],\n );\n\n // 合并内置 catalog 与外部传入的 catalog:外部同名项覆盖内置\n const mergedWidgetCatalog = useMemo(\n () => mergeCatalog(widgetCatalog, extraWidgetCatalog, 'widget'),\n [extraWidgetCatalog],\n );\n const mergedLayoutCatalog = useMemo(\n () => mergeCatalog(layoutCatalog, extraLayoutCatalog, 'layoutType'),\n [extraLayoutCatalog],\n );\n\n return (\n <DesignerProvider\n initialSchema={initialSchema}\n onSchemaChange={onSchemaChange}\n registerUI={registerUI}\n fields={fields}\n propertySchemaMap={propertySchemaMap}\n widgetCatalog={mergedWidgetCatalog}\n layoutCatalog={mergedLayoutCatalog}\n >\n <DesignerForm />\n </DesignerProvider>\n );\n}\n\n// 合并内置与外部 catalog:外部项按 key 覆盖内置同名项,其余追加到末尾\nfunction mergeCatalog(\n builtIn: CatalogItem[],\n extra: CatalogItem[] | undefined,\n keyField: 'widget' | 'layoutType',\n): CatalogItem[] {\n if (!extra || extra.length === 0) {\n return builtIn;\n }\n const extraKeys = new Set(extra.map((c) => c[keyField]).filter(Boolean));\n const preserved = builtIn.filter((c) => !extraKeys.has(c[keyField]));\n return [...preserved, ...extra];\n}\n\nfunction DesignerForm() {\n const { mode, setMode, schema, setSchema, selectNode } = useDesigner();\n const [schemaText, setSchemaText] = useState('');\n const [schemaActive, setSchemaActive] = useState(false);\n\n // 清空画布:重置为空 schema 并取消选中(必须新建对象,保证触发重渲染)\n const handleClearSchema = () => {\n setSchema(DEFAULT_SCHEMA);\n selectNode(null);\n if (mode === 'schema') {\n setSchemaText(JSON.stringify(DEFAULT_SCHEMA, null, 2));\n }\n };\n\n // 进入 schema 模式时同步当前 schema 到编辑框\n const handleEnterSchema = () => {\n setSchemaText(JSON.stringify(schema, null, 2));\n setSchemaActive(true);\n };\n\n // 离开 schema 模式时尝试解析并写回\n const handleLeaveSchema = () => {\n if (!schemaActive) {\n return;\n }\n try {\n const parsed = JSON.parse(schemaText) as NexusSchema;\n if (parsed && parsed.type === 'object' && parsed.properties) {\n setSchema(parsed);\n } else {\n message.warning('Schema 必须包含 type: \"object\" 和 properties');\n }\n } catch {\n message.error('JSON 格式错误,无法解析');\n }\n setSchemaActive(false);\n };\n\n // Radio.Group 切换\n const handleModeChange = (value: DesignerMode) => {\n if (value === 'schema') {\n handleEnterSchema();\n } else {\n handleLeaveSchema();\n }\n setMode(value);\n };\n\n const handleExport = () => {\n const json = JSON.stringify(schema, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'schema.json';\n a.click();\n URL.revokeObjectURL(url);\n };\n\n const handleImport = () => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'application/json';\n input.onchange = () => {\n const file = input.files?.[0];\n if (!file) {\n return;\n }\n const reader = new FileReader();\n reader.onload = () => {\n try {\n const parsed = JSON.parse(reader.result as string) as NexusSchema;\n if (parsed && parsed.type === 'object' && parsed.properties) {\n setSchema(parsed);\n }\n } catch {\n // 忽略非法 JSON\n }\n };\n reader.readAsText(file);\n };\n input.click();\n };\n\n return (\n <div className='flex flex-col h-full bg-white'>\n <div className='flex items-center justify-between px-4 py-2 border-b border-[#f0f0f0] bg-[#fafafa]'>\n <Typography.Text strong>Nexus 表单设计器</Typography.Text>\n <Space>\n <Radio.Group\n value={mode}\n onChange={(e) => handleModeChange(e.target.value as DesignerMode)}\n >\n <Radio.Button value='design'>设计</Radio.Button>\n <Radio.Button value='preview'>预览</Radio.Button>\n <Radio.Button value='schema'>Schema</Radio.Button>\n </Radio.Group>\n <Button onClick={handleImport}>导入</Button>\n <Button onClick={handleExport}>导出</Button>\n <Popconfirm\n title='清空画布'\n description='将移除画布上所有组件,此操作不可撤销'\n okText='清空'\n cancelText='取消'\n okButtonProps={{ danger: true }}\n onConfirm={handleClearSchema}\n >\n <Button danger>清空</Button>\n </Popconfirm>\n </Space>\n </div>\n {mode === 'schema' ? (\n <div className='flex-1 overflow-auto p-4 bg-[#1e1e1e]'>\n <Input.TextArea\n value={schemaText}\n onChange={(e) => setSchemaText(e.target.value)}\n autoSize={{ minRows: 20 }}\n className='font-mono! text-[13px]! leading-[1.6]! bg-[#1e1e1e]! text-[#d4d4d4]! border-[#3e3e42]!'\n />\n </div>\n ) : (\n <div className='flex flex-1 overflow-hidden'>\n <ResizablePanel\n side='left'\n defaultWidth={240}\n minWidth={160}\n maxWidth={420}\n collapsedTitle='组件面板'\n >\n <Palette />\n </ResizablePanel>\n <Canvas />\n <ResizablePanel\n side='right'\n defaultWidth={320}\n minWidth={240}\n maxWidth={560}\n collapsedTitle='属性面板'\n >\n <PropertyPanel />\n </ResizablePanel>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_SCHEMA","Designer","schema","propertySchemaMap","widgetSchemas","onSchemaChange","registerUI","registerAntdUI","fields","extraWidgetCatalog","extraLayoutCatalog","initialSchema","useMemo","mergedWidgetCatalog","mergeCatalog","widgetCatalog","mergedLayoutCatalog","layoutCatalog","jsx","DesignerProvider","DesignerForm","builtIn","extra","keyField","extraKeys","c","mode","setMode","setSchema","selectNode","useDesigner","schemaText","setSchemaText","useState","schemaActive","setSchemaActive","handleClearSchema","handleEnterSchema","handleLeaveSchema","parsed","message","handleModeChange","value","handleExport","json","blob","url","handleImport","input","file","reader","jsxs","Typography","Space","Radio","Button","Popconfirm","Input","ResizablePanel","Palette","Canvas","PropertyPanel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"designer.js","sources":["../../src/designer.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 主组件\n// ============================================================================\n\nimport type { NexusEngine, NexusSchema } from '@xbeeant/form-engine';\nimport type { PropertySchemaMap } from '@xbeeant/form-engine-ui';\nimport { registerAntdUI, widgetSchemas } from '@xbeeant/form-engine-ui';\nimport {\n Button,\n Input,\n message,\n Popconfirm,\n Radio,\n Space,\n Typography,\n} from 'antd';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Canvas } from './canvas';\nimport './Designer.css';\nimport { layoutCatalog, widgetCatalog } from './catalog';\nimport { DesignerProvider, useDesigner } from './designer-context';\nimport { Palette } from './palette';\nimport { PropertyPanel } from './property-panel';\nimport { ResizablePanel } from './resizable-panel';\nimport type { CatalogItem, DesignerMode, FieldDef } from './types';\n\nconst DEFAULT_SCHEMA: NexusSchema = {\n type: 'object',\n displayType: 'row',\n properties: {},\n};\n\nexport interface DesignerProps {\n schema?: NexusSchema;\n /**\n * widget 名 → 属性描述符映射,决定属性面板「组件属性」分区展示哪些配置项。\n * 默认使用 @xbeeant/form-engine-ui 的 widgetSchemas(antd 组件全覆盖);\n * 传入自定义映射可整体替换,也可在默认基础上按 widget 名覆盖(如\n * `{ ...widgetSchemas, input: myInputSchema }`)。\n */\n propertySchemaMap?: PropertySchemaMap;\n onSchemaChange?: (schema: NexusSchema) => void;\n /**\n * UI 注册函数,向引擎注册 widgets 和 layouts。\n * 默认使用 @xbeeant/form-engine-ui 的 registerAntdUI;接入其它 UI 库时传入自定义注册函数。\n */\n registerUI?: (engine: NexusEngine) => void;\n /** 外部字段列表,传入后左侧 palette 多一个「字段列表」分组 */\n fields?: FieldDef[];\n /** 额外的 widget 目录项,与内置 widgetCatalog 合并(同 widget 名时外部覆盖内置) */\n widgetCatalog?: CatalogItem[];\n /** 额外的 layout 目录项,与内置 layoutCatalog 合并(同 layoutType 时外部覆盖内置) */\n layoutCatalog?: CatalogItem[];\n}\n\nexport function Designer({\n schema,\n propertySchemaMap = widgetSchemas,\n onSchemaChange,\n registerUI = registerAntdUI,\n fields,\n widgetCatalog: extraWidgetCatalog,\n layoutCatalog: extraLayoutCatalog,\n}: DesignerProps) {\n const initialSchema = useMemo<NexusSchema>(\n () => schema ?? DEFAULT_SCHEMA,\n [schema],\n );\n\n // 合并内置 catalog 与外部传入的 catalog:外部同名项覆盖内置\n const mergedWidgetCatalog = useMemo(\n () => mergeCatalog(widgetCatalog, extraWidgetCatalog, 'widget'),\n [extraWidgetCatalog],\n );\n const mergedLayoutCatalog = useMemo(\n () => mergeCatalog(layoutCatalog, extraLayoutCatalog, 'layoutType'),\n [extraLayoutCatalog],\n );\n\n return (\n <DesignerProvider\n initialSchema={initialSchema}\n onSchemaChange={onSchemaChange}\n registerUI={registerUI}\n fields={fields}\n propertySchemaMap={propertySchemaMap}\n widgetCatalog={mergedWidgetCatalog}\n layoutCatalog={mergedLayoutCatalog}\n >\n <DesignerForm />\n </DesignerProvider>\n );\n}\n\n// 合并内置与外部 catalog:外部项按 key 覆盖内置同名项,其余追加到末尾\nfunction mergeCatalog(\n builtIn: CatalogItem[],\n extra: CatalogItem[] | undefined,\n keyField: 'widget' | 'layoutType',\n): CatalogItem[] {\n if (!extra || extra.length === 0) {\n return builtIn;\n }\n const extraKeys = new Set(extra.map((c) => c[keyField]).filter(Boolean));\n const preserved = builtIn.filter((c) => !extraKeys.has(c[keyField]));\n return [...preserved, ...extra];\n}\n\nfunction DesignerForm() {\n const {\n mode,\n setMode,\n schema,\n setSchema,\n selectNode,\n undo,\n redo,\n canUndo,\n canRedo,\n } = useDesigner();\n const [schemaText, setSchemaText] = useState('');\n const [schemaActive, setSchemaActive] = useState(false);\n\n // 全局快捷键:Cmd/Ctrl+Z 撤销、+Shift+Z 或 Ctrl+Y 重做。\n // 输入框/文本域内不拦截(保留原生文本撤销)\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement | null;\n if (\n target &&\n (target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable)\n ) {\n return;\n }\n if (!(e.metaKey || e.ctrlKey)) {\n return;\n }\n const key = e.key.toLowerCase();\n if (key === 'z') {\n e.preventDefault();\n if (e.shiftKey) {\n redo();\n } else {\n undo();\n }\n } else if (key === 'y') {\n e.preventDefault();\n redo();\n }\n };\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [undo, redo]);\n\n // 清空画布:重置为空 schema 并取消选中(必须新建对象,保证触发重渲染)\n const handleClearSchema = () => {\n setSchema(DEFAULT_SCHEMA);\n selectNode(null);\n if (mode === 'schema') {\n setSchemaText(JSON.stringify(DEFAULT_SCHEMA, null, 2));\n }\n };\n\n // 进入 schema 模式时同步当前 schema 到编辑框\n const handleEnterSchema = () => {\n setSchemaText(JSON.stringify(schema, null, 2));\n setSchemaActive(true);\n };\n\n // 离开 schema 模式时尝试解析并写回\n const handleLeaveSchema = () => {\n if (!schemaActive) {\n return;\n }\n try {\n const parsed = JSON.parse(schemaText) as NexusSchema;\n if (parsed && parsed.type === 'object' && parsed.properties) {\n setSchema(parsed);\n } else {\n message.warning('Schema 必须包含 type: \"object\" 和 properties');\n }\n } catch {\n message.error('JSON 格式错误,无法解析');\n }\n setSchemaActive(false);\n };\n\n // Radio.Group 切换\n const handleModeChange = (value: DesignerMode) => {\n if (value === 'schema') {\n handleEnterSchema();\n } else {\n handleLeaveSchema();\n }\n setMode(value);\n };\n\n const handleExport = () => {\n const json = JSON.stringify(schema, null, 2);\n const blob = new Blob([json], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'schema.json';\n a.click();\n URL.revokeObjectURL(url);\n };\n\n const handleImport = () => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'application/json';\n input.onchange = () => {\n const file = input.files?.[0];\n if (!file) {\n return;\n }\n const reader = new FileReader();\n reader.onload = () => {\n try {\n const parsed = JSON.parse(reader.result as string) as NexusSchema;\n if (parsed && parsed.type === 'object' && parsed.properties) {\n setSchema(parsed);\n }\n } catch {\n // 忽略非法 JSON\n }\n };\n reader.readAsText(file);\n };\n input.click();\n };\n\n return (\n <div className='flex flex-col h-full bg-white'>\n <div className='flex items-center justify-between px-4 py-2 border-b border-[#f0f0f0] bg-[#fafafa]'>\n <Typography.Text strong>Nexus 表单设计器</Typography.Text>\n <Space>\n <Radio.Group\n value={mode}\n onChange={(e) => handleModeChange(e.target.value as DesignerMode)}\n >\n <Radio.Button value='design'>设计</Radio.Button>\n <Radio.Button value='preview'>预览</Radio.Button>\n <Radio.Button value='schema'>Schema</Radio.Button>\n </Radio.Group>\n <Button onClick={handleImport}>导入</Button>\n <Button onClick={handleExport}>导出</Button>\n <Button disabled={!canUndo} onClick={undo}>\n 撤销\n </Button>\n <Button disabled={!canRedo} onClick={redo}>\n 重做\n </Button>\n <Popconfirm\n title='清空画布'\n description='将移除画布上所有组件,可使用 Ctrl/Cmd+Z 撤销'\n okText='清空'\n cancelText='取消'\n okButtonProps={{ danger: true }}\n onConfirm={handleClearSchema}\n >\n <Button danger>清空</Button>\n </Popconfirm>\n </Space>\n </div>\n {mode === 'schema' ? (\n <div className='flex-1 overflow-auto p-4 bg-[#1e1e1e]'>\n <Input.TextArea\n value={schemaText}\n onChange={(e) => setSchemaText(e.target.value)}\n autoSize={{ minRows: 20 }}\n className='font-mono! text-[13px]! leading-[1.6]! bg-[#1e1e1e]! text-[#d4d4d4]! border-[#3e3e42]!'\n />\n </div>\n ) : (\n <div className='flex flex-1 overflow-hidden'>\n <ResizablePanel\n side='left'\n defaultWidth={320}\n minWidth={160}\n maxWidth={420}\n collapsedTitle='组件面板'\n >\n <Palette />\n </ResizablePanel>\n <Canvas />\n <ResizablePanel\n side='right'\n defaultWidth={320}\n minWidth={240}\n maxWidth={800}\n collapsedTitle='属性面板'\n >\n <PropertyPanel />\n </ResizablePanel>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_SCHEMA","Designer","schema","propertySchemaMap","widgetSchemas","onSchemaChange","registerUI","registerAntdUI","fields","extraWidgetCatalog","extraLayoutCatalog","initialSchema","useMemo","mergedWidgetCatalog","mergeCatalog","widgetCatalog","mergedLayoutCatalog","layoutCatalog","jsx","DesignerProvider","DesignerForm","builtIn","extra","keyField","extraKeys","c","mode","setMode","setSchema","selectNode","undo","redo","canUndo","canRedo","useDesigner","schemaText","setSchemaText","useState","schemaActive","setSchemaActive","useEffect","onKeyDown","e","target","key","handleClearSchema","handleEnterSchema","handleLeaveSchema","parsed","message","handleModeChange","value","handleExport","json","blob","url","a","handleImport","input","file","reader","jsxs","Typography","Space","Radio","Button","Popconfirm","Input","ResizablePanel","Palette","Canvas","PropertyPanel"],"mappings":"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"}
|
|
@@ -1,97 +1,110 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { jsx as T } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as U, useState as u, useRef as W, useCallback as n, useMemo as _, useContext as q } from "react";
|
|
3
|
+
import { SchemaHistory as z } from "./history.js";
|
|
4
|
+
import { addChildToSchema as A, removeNodeFromSchema as B, updateNodeWithNesting as G, moveNodeInSchema as J, renameNodeInSchema as K } from "./schemaUtils.js";
|
|
5
|
+
const k = U(null);
|
|
6
|
+
function Z({
|
|
7
|
+
initialSchema: i,
|
|
8
|
+
propertySchemaMap: N,
|
|
9
|
+
onSchemaChange: s,
|
|
10
|
+
registerUI: f,
|
|
11
|
+
fields: v,
|
|
12
|
+
widgetCatalog: h,
|
|
13
|
+
layoutCatalog: P,
|
|
14
|
+
children: E
|
|
14
15
|
}) {
|
|
15
|
-
const [
|
|
16
|
-
(e) => {
|
|
17
|
-
|
|
16
|
+
const [t, m] = u(i), [p, l] = u(null), [x, F] = u("design"), d = W(new z()), [L, a] = u(0), r = n(
|
|
17
|
+
(e, o = "replace", c = null) => {
|
|
18
|
+
d.current.push(t, o, c), a((M) => M + 1), m(e), s?.(e);
|
|
18
19
|
},
|
|
19
|
-
[
|
|
20
|
-
),
|
|
21
|
-
d(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
[t, s]
|
|
21
|
+
), D = n(() => {
|
|
22
|
+
const e = d.current.undo(t);
|
|
23
|
+
e !== null && (a((o) => o + 1), m(e), s?.(e));
|
|
24
|
+
}, [t, s]), R = n(() => {
|
|
25
|
+
const e = d.current.redo();
|
|
26
|
+
e !== null && (a((o) => o + 1), m(e), s?.(e));
|
|
27
|
+
}, [s]), S = n((e) => r(e), [r]), w = n((e) => {
|
|
28
|
+
l(e);
|
|
29
|
+
}, []), j = n(
|
|
30
|
+
(e, o, c) => {
|
|
31
|
+
r(A(t, e, o, c), "edit");
|
|
25
32
|
},
|
|
26
|
-
[
|
|
27
|
-
),
|
|
33
|
+
[t, r]
|
|
34
|
+
), y = n(
|
|
28
35
|
(e) => {
|
|
29
|
-
|
|
36
|
+
r(B(t, e), "edit"), l(null);
|
|
30
37
|
},
|
|
31
|
-
[
|
|
32
|
-
),
|
|
33
|
-
(e,
|
|
34
|
-
|
|
38
|
+
[t, r]
|
|
39
|
+
), H = n(
|
|
40
|
+
(e, o) => {
|
|
41
|
+
r(G(t, e, o), "edit", e.join("."));
|
|
35
42
|
},
|
|
36
|
-
[
|
|
37
|
-
),
|
|
38
|
-
(e,
|
|
39
|
-
|
|
43
|
+
[t, r]
|
|
44
|
+
), V = n(
|
|
45
|
+
(e, o) => {
|
|
46
|
+
r(J(t, e, o), "edit");
|
|
40
47
|
},
|
|
41
|
-
[
|
|
42
|
-
),
|
|
43
|
-
(e,
|
|
44
|
-
const
|
|
45
|
-
|
|
48
|
+
[t, r]
|
|
49
|
+
), b = n(
|
|
50
|
+
(e, o) => {
|
|
51
|
+
const c = K(t, e, o);
|
|
52
|
+
r(c.schema, "edit"), l(c.newPath);
|
|
46
53
|
},
|
|
47
|
-
[
|
|
48
|
-
),
|
|
54
|
+
[t, r]
|
|
55
|
+
), I = _(
|
|
49
56
|
() => ({
|
|
50
|
-
schema:
|
|
51
|
-
selectedPath:
|
|
52
|
-
mode:
|
|
53
|
-
registerUI:
|
|
54
|
-
fields:
|
|
55
|
-
widgetCatalog:
|
|
56
|
-
layoutCatalog:
|
|
57
|
-
propertySchemaMap:
|
|
58
|
-
setSchema:
|
|
59
|
-
selectNode:
|
|
60
|
-
setMode:
|
|
61
|
-
addNode:
|
|
62
|
-
removeNode:
|
|
63
|
-
updateNode:
|
|
64
|
-
moveNode:
|
|
65
|
-
renameNode:
|
|
57
|
+
schema: t,
|
|
58
|
+
selectedPath: p,
|
|
59
|
+
mode: x,
|
|
60
|
+
registerUI: f,
|
|
61
|
+
fields: v,
|
|
62
|
+
widgetCatalog: h,
|
|
63
|
+
layoutCatalog: P,
|
|
64
|
+
propertySchemaMap: N,
|
|
65
|
+
setSchema: S,
|
|
66
|
+
selectNode: w,
|
|
67
|
+
setMode: F,
|
|
68
|
+
addNode: j,
|
|
69
|
+
removeNode: y,
|
|
70
|
+
updateNode: H,
|
|
71
|
+
moveNode: V,
|
|
72
|
+
renameNode: b,
|
|
73
|
+
undo: D,
|
|
74
|
+
redo: R,
|
|
75
|
+
canUndo: d.current.canUndo,
|
|
76
|
+
canRedo: d.current.canRedo
|
|
66
77
|
}),
|
|
67
78
|
[
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
t,
|
|
80
|
+
N,
|
|
81
|
+
p,
|
|
82
|
+
x,
|
|
83
|
+
f,
|
|
70
84
|
v,
|
|
71
|
-
S,
|
|
72
|
-
a,
|
|
73
85
|
h,
|
|
74
|
-
N,
|
|
75
|
-
l,
|
|
76
86
|
P,
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
S,
|
|
88
|
+
j,
|
|
89
|
+
y,
|
|
90
|
+
H,
|
|
91
|
+
V,
|
|
92
|
+
b,
|
|
79
93
|
D,
|
|
80
|
-
|
|
81
|
-
g,
|
|
94
|
+
R,
|
|
82
95
|
w
|
|
83
96
|
]
|
|
84
97
|
);
|
|
85
|
-
return /* @__PURE__ */
|
|
98
|
+
return /* @__PURE__ */ T(k.Provider, { value: I, children: E });
|
|
86
99
|
}
|
|
87
|
-
function
|
|
88
|
-
const
|
|
89
|
-
if (!
|
|
100
|
+
function $() {
|
|
101
|
+
const i = q(k);
|
|
102
|
+
if (!i)
|
|
90
103
|
throw new Error("useDesigner 必须在 <DesignerProvider> 内使用");
|
|
91
|
-
return
|
|
104
|
+
return i;
|
|
92
105
|
}
|
|
93
106
|
export {
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
Z as DesignerProvider,
|
|
108
|
+
$ as useDesigner
|
|
96
109
|
};
|
|
97
110
|
//# sourceMappingURL=DesignerContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesignerContext.js","sources":["../../src/DesignerContext.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 设计器状态上下文\n// ============================================================================\n\nimport type {\n NexusEngine,\n NexusSchema,\n SchemaNode,\n} from '@xbeeant/form-engine';\nimport type { PropertySchemaMap } from '@xbeeant/form-engine-ui';\nimport type { ReactNode } from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from 'react';\nimport {\n addChildToSchema,\n moveNodeInSchema,\n removeNodeFromSchema,\n renameNodeInSchema,\n updateNodeWithNesting,\n} from './schemaUtils';\nimport type { CatalogItem, DesignerMode, FieldDef } from './types';\n\n/** 设计器内部状态 */\nexport interface DesignerState {\n schema: NexusSchema;\n selectedPath: string[] | null;\n mode: DesignerMode;\n}\n\nexport interface DesignerContextValue {\n schema: NexusSchema;\n propertySchemaMap: PropertySchemaMap;\n selectedPath: string[] | null;\n mode: DesignerMode;\n /** UI 注册函数,向引擎注册 widgets/layouts */\n registerUI?: (engine: NexusEngine) => void;\n /** 外部字段列表 */\n fields?: FieldDef[];\n /** 合并后的 widget 目录(内置 + 外部传入) */\n widgetCatalog: CatalogItem[];\n /** 合并后的 layout 目录(内置 + 外部传入) */\n layoutCatalog: CatalogItem[];\n setSchema: (schema: NexusSchema) => void;\n selectNode: (path: string[] | null) => void;\n setMode: (mode: DesignerMode) => void;\n addNode: (parentPath: string[], key: string, node: SchemaNode) => void;\n removeNode: (path: string[]) => void;\n updateNode: (path: string[], patch: Record<string, unknown>) => void;\n moveNode: (fromPath: string[], toParentPath: string[]) => void;\n /** 重命名节点 key(schema 结构级操作,保留节点在父级中的位置) */\n renameNode: (path: string[], newKey: string) => void;\n}\n\ninterface DesignerProviderProps {\n initialSchema: NexusSchema;\n propertySchemaMap: PropertySchemaMap;\n onSchemaChange?: (schema: NexusSchema) => void;\n registerUI?: (engine: NexusEngine) => void;\n fields?: FieldDef[];\n widgetCatalog: CatalogItem[];\n layoutCatalog: CatalogItem[];\n children: ReactNode;\n}\n\nconst DesignerContext = createContext<DesignerContextValue | null>(null);\n\nexport function DesignerProvider({\n initialSchema,\n propertySchemaMap,\n onSchemaChange,\n registerUI,\n fields,\n widgetCatalog,\n layoutCatalog,\n children,\n}: DesignerProviderProps) {\n const [schema, setSchemaState] = useState<NexusSchema>(initialSchema);\n const [selectedPath, setSelectedPath] = useState<string[] | null>(null);\n const [mode, setMode] = useState<DesignerMode>('design');\n\n // 写入 schema
|
|
1
|
+
{"version":3,"file":"DesignerContext.js","sources":["../../src/DesignerContext.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 设计器状态上下文\n// ============================================================================\n\nimport type {\n NexusEngine,\n NexusSchema,\n SchemaNode,\n} from '@xbeeant/form-engine';\nimport type { PropertySchemaMap } from '@xbeeant/form-engine-ui';\nimport type { ReactNode } from 'react';\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { SchemaHistory } from './history';\nimport {\n addChildToSchema,\n moveNodeInSchema,\n removeNodeFromSchema,\n renameNodeInSchema,\n updateNodeWithNesting,\n} from './schemaUtils';\nimport type { CatalogItem, DesignerMode, FieldDef } from './types';\n\n/** 设计器内部状态 */\nexport interface DesignerState {\n schema: NexusSchema;\n selectedPath: string[] | null;\n mode: DesignerMode;\n}\n\nexport interface DesignerContextValue {\n schema: NexusSchema;\n propertySchemaMap: PropertySchemaMap;\n selectedPath: string[] | null;\n mode: DesignerMode;\n /** UI 注册函数,向引擎注册 widgets/layouts */\n registerUI?: (engine: NexusEngine) => void;\n /** 外部字段列表 */\n fields?: FieldDef[];\n /** 合并后的 widget 目录(内置 + 外部传入) */\n widgetCatalog: CatalogItem[];\n /** 合并后的 layout 目录(内置 + 外部传入) */\n layoutCatalog: CatalogItem[];\n setSchema: (schema: NexusSchema) => void;\n selectNode: (path: string[] | null) => void;\n setMode: (mode: DesignerMode) => void;\n addNode: (parentPath: string[], key: string, node: SchemaNode) => void;\n removeNode: (path: string[]) => void;\n updateNode: (path: string[], patch: Record<string, unknown>) => void;\n moveNode: (fromPath: string[], toParentPath: string[]) => void;\n /** 重命名节点 key(schema 结构级操作,保留节点在父级中的位置) */\n renameNode: (path: string[], newKey: string) => void;\n /** 撤销:恢复最近一次结构/属性变更 */\n undo: () => void;\n /** 重做:恢复最近一次撤销 */\n redo: () => void;\n canUndo: boolean;\n canRedo: boolean;\n}\n\ninterface DesignerProviderProps {\n initialSchema: NexusSchema;\n propertySchemaMap: PropertySchemaMap;\n onSchemaChange?: (schema: NexusSchema) => void;\n registerUI?: (engine: NexusEngine) => void;\n fields?: FieldDef[];\n widgetCatalog: CatalogItem[];\n layoutCatalog: CatalogItem[];\n children: ReactNode;\n}\n\nconst DesignerContext = createContext<DesignerContextValue | null>(null);\n\nexport function DesignerProvider({\n initialSchema,\n propertySchemaMap,\n onSchemaChange,\n registerUI,\n fields,\n widgetCatalog,\n layoutCatalog,\n children,\n}: DesignerProviderProps) {\n const [schema, setSchemaState] = useState<NexusSchema>(initialSchema);\n const [selectedPath, setSelectedPath] = useState<string[] | null>(null);\n const [mode, setMode] = useState<DesignerMode>('design');\n const historyRef = useRef(new SchemaHistory());\n // 历史栈版本:驱动 canUndo/canRedo 的消费方重渲染\n const [_historyVersion, setHistoryVersion] = useState(0);\n\n // 写入 schema 并同步通知外部;历史记录由 SchemaHistory 统一管理\n const emit = useCallback(\n (\n next: NexusSchema,\n kind: 'edit' | 'replace' = 'replace',\n path: string | null = null,\n ) => {\n historyRef.current.push(schema, kind, path);\n setHistoryVersion((v) => v + 1);\n setSchemaState(next);\n onSchemaChange?.(next);\n },\n [schema, onSchemaChange],\n );\n\n const undo = useCallback(() => {\n const restored = historyRef.current.undo(schema);\n if (restored === null) {\n return;\n }\n setHistoryVersion((v) => v + 1);\n setSchemaState(restored);\n onSchemaChange?.(restored);\n }, [schema, onSchemaChange]);\n\n const redo = useCallback(() => {\n const restored = historyRef.current.redo();\n if (restored === null) {\n return;\n }\n setHistoryVersion((v) => v + 1);\n setSchemaState(restored);\n onSchemaChange?.(restored);\n }, [onSchemaChange]);\n\n const setSchema = useCallback((next: NexusSchema) => emit(next), [emit]);\n\n const selectNode = useCallback((path: string[] | null) => {\n setSelectedPath(path);\n }, []);\n\n // setMode 直接复用 useState 的 setter(稳定引用,无需额外包裹)\n\n const addNode = useCallback(\n (parentPath: string[], key: string, node: SchemaNode) => {\n emit(addChildToSchema(schema, parentPath, key, node), 'edit');\n },\n [schema, emit],\n );\n\n const removeNode = useCallback(\n (path: string[]) => {\n emit(removeNodeFromSchema(schema, path), 'edit');\n setSelectedPath(null);\n },\n [schema, emit],\n );\n\n const updateNode = useCallback(\n (path: string[], patch: Record<string, unknown>) => {\n // 同路径的连续属性编辑由 SchemaHistory 合并(COALESCE_MS 窗口)\n emit(updateNodeWithNesting(schema, path, patch), 'edit', path.join('.'));\n },\n [schema, emit],\n );\n\n const moveNode = useCallback(\n (fromPath: string[], toParentPath: string[]) => {\n emit(moveNodeInSchema(schema, fromPath, toParentPath), 'edit');\n },\n [schema, emit],\n );\n\n const renameNode = useCallback(\n (path: string[], newKey: string) => {\n const result = renameNodeInSchema(schema, path, newKey);\n emit(result.schema, 'edit');\n setSelectedPath(result.newPath);\n },\n [schema, emit],\n );\n\n const value = useMemo<DesignerContextValue>(\n () => ({\n schema,\n selectedPath,\n mode,\n registerUI,\n fields,\n widgetCatalog,\n layoutCatalog,\n propertySchemaMap,\n setSchema,\n selectNode,\n setMode,\n addNode,\n removeNode,\n updateNode,\n moveNode,\n renameNode,\n undo,\n redo,\n canUndo: historyRef.current.canUndo,\n canRedo: historyRef.current.canRedo,\n }),\n [\n schema,\n propertySchemaMap,\n selectedPath,\n mode,\n registerUI,\n fields,\n widgetCatalog,\n layoutCatalog,\n setSchema,\n addNode,\n removeNode,\n updateNode,\n moveNode,\n renameNode,\n undo,\n redo,\n selectNode,\n ],\n );\n\n return (\n <DesignerContext.Provider value={value}>\n {children}\n </DesignerContext.Provider>\n );\n}\n\nexport function useDesigner(): DesignerContextValue {\n const ctx = useContext(DesignerContext);\n if (!ctx) {\n throw new Error('useDesigner 必须在 <DesignerProvider> 内使用');\n }\n return ctx;\n}\n"],"names":["DesignerContext","createContext","DesignerProvider","initialSchema","propertySchemaMap","onSchemaChange","registerUI","fields","widgetCatalog","layoutCatalog","children","schema","setSchemaState","useState","selectedPath","setSelectedPath","mode","setMode","historyRef","useRef","SchemaHistory","_historyVersion","setHistoryVersion","emit","useCallback","next","kind","path","v","undo","restored","redo","setSchema","selectNode","addNode","parentPath","key","node","addChildToSchema","removeNode","removeNodeFromSchema","updateNode","patch","updateNodeWithNesting","moveNode","fromPath","toParentPath","moveNodeInSchema","renameNode","newKey","result","renameNodeInSchema","value","useMemo","jsx","useDesigner","ctx","useContext"],"mappings":"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"}
|
package/dist/es/Palette.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as t, jsxs as o } from "react/jsx-runtime";
|
|
2
2
|
import { Input as j, Collapse as A } from "antd";
|
|
3
3
|
import { useState as S, useCallback as _, useMemo as g } from "react";
|
|
4
|
-
import { useDesigner as L } from "./
|
|
5
|
-
import { getNodeAtProperties as $, getPropertiesOf as E, generateKey as v } from "./
|
|
4
|
+
import { useDesigner as L } from "./designer-context.js";
|
|
5
|
+
import { getNodeAtProperties as $, getPropertiesOf as E, generateKey as v } from "./schema-utils.js";
|
|
6
6
|
const h = {
|
|
7
7
|
tabPane: "tabs",
|
|
8
8
|
collapsePanel: "collapse",
|
|
@@ -145,4 +145,4 @@ function G() {
|
|
|
145
145
|
export {
|
|
146
146
|
G as Palette
|
|
147
147
|
};
|
|
148
|
-
//# sourceMappingURL=
|
|
148
|
+
//# sourceMappingURL=palette.js.map
|