@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
@@ -0,0 +1,294 @@
1
+ import { jsxs as c, jsx as n } from "react/jsx-runtime";
2
+ import { useForm as W, NexusForm as E } from "@xbeeant/form-engine-react";
3
+ import { registerAntdUI as T } from "@xbeeant/form-engine-ui";
4
+ import { Input as A } from "antd";
5
+ import { useMemo as P, useCallback as b, useState as D, useRef as C, useEffect as K } from "react";
6
+ import { useDesigner as B } from "./designer-context.js";
7
+ import { dependencyPropertyFields as I, reactionPropertyFields as M, validationPropertyFields as $, commonPropertyFields as z } from "./property/basic-property.js";
8
+ import { getNodeAtProperties as U, extractFormLevelConfig as q, flattenNodeForPropertyEditor as j, diffPropertyPatch as G } from "./schema-utils.js";
9
+ import { propertyWidgets as H } from "./widgets/index.js";
10
+ function J({
11
+ schema: o,
12
+ initialValues: e,
13
+ onValuesChange: r
14
+ }) {
15
+ const [s] = W(), a = s._getEngine();
16
+ T(a);
17
+ const i = C(/* @__PURE__ */ new Set()), d = b(
18
+ (t, l, u) => {
19
+ u && i.current.add(u), r(
20
+ t,
21
+ i.current
22
+ );
23
+ },
24
+ [r]
25
+ );
26
+ return /* @__PURE__ */ n(
27
+ E,
28
+ {
29
+ footer: !1,
30
+ form: s,
31
+ schema: o,
32
+ initialValues: e,
33
+ watch: { "#": d },
34
+ widgets: H
35
+ }
36
+ );
37
+ }
38
+ const F = {
39
+ displayType: {
40
+ widget: "select",
41
+ type: "string",
42
+ title: "表单布局方向(displayType)",
43
+ props: {
44
+ options: [
45
+ { label: "水平(row)", value: "row" },
46
+ { label: "垂直(column)", value: "column" },
47
+ { label: "行内(inline)", value: "inline" }
48
+ ]
49
+ }
50
+ },
51
+ labelWidth: {
52
+ widget: "input",
53
+ type: "string",
54
+ title: "label 宽度(labelWidth)",
55
+ placeholder: "如 120 或 20%"
56
+ },
57
+ colon: {
58
+ widget: "switch",
59
+ type: "boolean",
60
+ title: "显示冒号(colon)",
61
+ default: !0
62
+ },
63
+ label: {
64
+ widget: "switch",
65
+ type: "boolean",
66
+ title: "显示 label(label)",
67
+ default: !0
68
+ },
69
+ readOnly: {
70
+ widget: "switch",
71
+ type: "boolean",
72
+ title: "整个表单只读(readOnly)",
73
+ default: !1
74
+ },
75
+ column: {
76
+ widget: "number",
77
+ type: "number",
78
+ title: "每行显示列数(column)",
79
+ default: 1
80
+ }
81
+ }, Q = {
82
+ column: { widget: "number", type: "number", title: "列数(column)" },
83
+ gap: { widget: "number", type: "number", title: "间距(gap)" },
84
+ direction: {
85
+ widget: "select",
86
+ type: "string",
87
+ title: "方向(direction)",
88
+ props: {
89
+ options: [
90
+ { label: "水平", value: "row" },
91
+ { label: "垂直", value: "column" }
92
+ ]
93
+ }
94
+ }
95
+ };
96
+ function m(o, e, r) {
97
+ return {
98
+ [o]: {
99
+ type: "collapsePanel",
100
+ title: e,
101
+ properties: r
102
+ }
103
+ };
104
+ }
105
+ function X({ oldKey: o, locked: e, onRename: r }) {
106
+ const [s, a] = D(o), i = C(o);
107
+ if (K(() => {
108
+ o !== i.current && (i.current = o, a(o));
109
+ }, [o]), e)
110
+ return /* @__PURE__ */ c("div", { className: "mb-3 flex items-center justify-between gap-2 rounded-lg border border-[#f0f0f0] bg-[#fafafa] px-3 py-2", children: [
111
+ /* @__PURE__ */ c("div", { className: "min-w-0", children: [
112
+ /* @__PURE__ */ n("div", { className: "mb-0.5 text-[11px] text-[#999]", children: "字段标识(key)" }),
113
+ /* @__PURE__ */ n("code", { className: "break-all font-mono text-[13px] text-[#333]", children: o })
114
+ ] }),
115
+ /* @__PURE__ */ n("span", { className: "shrink-0 rounded bg-[#f0f0f0] px-1.5 py-0.5 text-[11px] text-[#999]", children: "🔒 锁定" })
116
+ ] });
117
+ const d = (t) => {
118
+ i.current = t, a(t), r(t);
119
+ };
120
+ return /* @__PURE__ */ c("div", { className: "mb-3", children: [
121
+ /* @__PURE__ */ n("div", { className: "mb-1.5 text-[11px] text-[#999]", children: "字段标识(key)" }),
122
+ /* @__PURE__ */ n(
123
+ A,
124
+ {
125
+ size: "small",
126
+ value: s,
127
+ onChange: (t) => {
128
+ const l = (t.target.value || "").trim();
129
+ d(l);
130
+ },
131
+ className: "nexus-key-input"
132
+ }
133
+ ),
134
+ /* @__PURE__ */ n("div", { className: "mt-1 text-[11px] text-[#c0c4cc]", children: "用于数据路径与联动引用,修改后自动迁移子节点" })
135
+ ] });
136
+ }
137
+ function ce() {
138
+ const {
139
+ schema: o,
140
+ selectedPath: e,
141
+ updateNode: r,
142
+ renameNode: s,
143
+ selectNode: a,
144
+ setSchema: i,
145
+ propertySchemaMap: d
146
+ } = B(), t = e && e.length > 0 ? U(o.properties, e) : void 0, l = !e || e.length === 0, u = `node_${(e || []).join("_")}_${t?.type || "root"}`, g = e?.[e.length - 1] ?? "", L = t?._lockedKey === !0, x = t ? t.widget || t.type : null, R = P(() => {
147
+ if (l)
148
+ return {
149
+ type: "object",
150
+ properties: F,
151
+ displayType: "column"
152
+ };
153
+ const p = d[x] || Q;
154
+ return {
155
+ type: "object",
156
+ properties: {
157
+ sectionProperty: {
158
+ widget: "collapse",
159
+ props: {
160
+ defaultActiveKey: [
161
+ "sectionCommon",
162
+ "sectionFieldProps",
163
+ "sectionValidation",
164
+ "sectionReaction",
165
+ "sectionDependency"
166
+ ]
167
+ },
168
+ type: "collapse",
169
+ properties: {
170
+ ...m(
171
+ "sectionCommon",
172
+ "通用属性",
173
+ z
174
+ ),
175
+ ...m(
176
+ "sectionFieldProps",
177
+ "组件属性",
178
+ p
179
+ ),
180
+ ...m(
181
+ "sectionValidation",
182
+ "校验配置",
183
+ $
184
+ ),
185
+ ...m(
186
+ "sectionReaction",
187
+ "联动配置",
188
+ M
189
+ ),
190
+ ...m(
191
+ "sectionDependency",
192
+ "依赖配置",
193
+ I
194
+ )
195
+ }
196
+ }
197
+ },
198
+ displayType: "column"
199
+ };
200
+ }, [l, x, d]), f = P(
201
+ () => q(
202
+ o,
203
+ Object.keys(F)
204
+ ),
205
+ [o]
206
+ ), _ = b(
207
+ (p, S) => {
208
+ const V = t ? j(
209
+ t
210
+ ) : f, y = G(V, p, S);
211
+ if (Object.keys(y).length === 0)
212
+ return;
213
+ if (!e || e.length === 0) {
214
+ const h = { ...o };
215
+ for (const [k, N] of Object.entries(y))
216
+ N === void 0 ? delete h[k] : h[k] = N;
217
+ i(h);
218
+ return;
219
+ }
220
+ const { key: Y, ...v } = y;
221
+ Object.keys(v).length > 0 && r(e, v);
222
+ },
223
+ [
224
+ e,
225
+ r,
226
+ i,
227
+ o,
228
+ t,
229
+ f
230
+ ]
231
+ ), O = b(
232
+ (p) => {
233
+ !e || e.length === 0 || s(e, p);
234
+ },
235
+ [e, s]
236
+ ), w = t ? (t.widget || t.type || "").toLowerCase() : "";
237
+ return /* @__PURE__ */ c("div", { className: "flex h-full flex-col border-l border-[#f0f0f0] bg-white", children: [
238
+ /* @__PURE__ */ c("div", { className: "flex items-center justify-between border-b border-[#f0f0f0] px-4 py-2.5", children: [
239
+ /* @__PURE__ */ c("div", { className: "pl-5 flex min-w-0 items-center gap-1 h-6", children: [
240
+ !l && /* @__PURE__ */ n(
241
+ "button",
242
+ {
243
+ type: "button",
244
+ onClick: () => a(null),
245
+ className: "-ml-1 mr-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-[#999] transition-colors hover:bg-[#f0f0f0] hover:text-[#333]",
246
+ title: "返回表单属性",
247
+ children: /* @__PURE__ */ n(
248
+ "svg",
249
+ {
250
+ width: "12",
251
+ height: "12",
252
+ viewBox: "0 0 24 24",
253
+ fill: "none",
254
+ stroke: "currentColor",
255
+ strokeWidth: "2.5",
256
+ strokeLinecap: "round",
257
+ strokeLinejoin: "round",
258
+ "aria-hidden": "true",
259
+ children: /* @__PURE__ */ n("polyline", { points: "15 18 9 12 15 6" })
260
+ }
261
+ )
262
+ }
263
+ ),
264
+ /* @__PURE__ */ n("span", { className: "truncate text-[13px] font-semibold text-[#333]", children: l ? "表单属性" : "节点属性" })
265
+ ] }),
266
+ !l && w && /* @__PURE__ */ n("span", { className: "ml-2 shrink-0 rounded-md bg-[#e8f1ff] px-1.5 py-0.5 text-[11px] text-[#1677ff]", children: w })
267
+ ] }),
268
+ /* @__PURE__ */ c("div", { className: "nexus-property-body flex-1 overflow-y-auto px-4 py-3", children: [
269
+ !l && g && /* @__PURE__ */ n(
270
+ X,
271
+ {
272
+ oldKey: g,
273
+ locked: L,
274
+ onRename: O
275
+ }
276
+ ),
277
+ /* @__PURE__ */ n(
278
+ J,
279
+ {
280
+ schema: R,
281
+ initialValues: t ? j(
282
+ t
283
+ ) : f,
284
+ onValuesChange: _
285
+ },
286
+ u
287
+ )
288
+ ] })
289
+ ] });
290
+ }
291
+ export {
292
+ ce as PropertyPanel
293
+ };
294
+ //# sourceMappingURL=property-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-panel.js","sources":["../../src/property-panel.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 右侧属性配置面板\n// 属性定义采用「描述符驱动」:\n// - 组件(widget)属性来自外部传入的 propertySchemaMap(key 为 widget 名,\n// 值为该组件的属性字段定义,示例见 @xbeeant/form-engine-ui/src/schema/*-schema.ts)\n// - 通用 / 校验 / 依赖分区在 ./property/basic-property.ts 中定义\n// - 布局节点与表单级属性在下方 formLevelProps / layoutBasicProps 内联定义\n//\n// 编辑流程(与 schemaUtils 的读/写通路对应):\n// flattenNodeForPropertyEditor → 节点 → 扁平 initialValues(读)\n// updateNodeWithNesting → 扁平 patch → 节点(写,split schema级/props级)\n// PropertyForm 通过 NexusForm 渲染 propertySchema 描述符驱动的表单\n// ============================================================================\n\nimport type { NexusSchema, SchemaNode } from '@xbeeant/form-engine';\nimport { NexusForm, useForm } from '@xbeeant/form-engine-react';\nimport { registerAntdUI } from '@xbeeant/form-engine-ui';\nimport { Input } from 'antd';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useDesigner } from './designer-context';\nimport {\n commonPropertyFields,\n dependencyPropertyFields,\n reactionPropertyFields,\n validationPropertyFields,\n} from './property/basic-property.ts';\nimport {\n diffPropertyPatch,\n extractFormLevelConfig,\n flattenNodeForPropertyEditor,\n getNodeAtProperties,\n} from './schema-utils';\nimport { propertyWidgets } from './widgets';\n\n// ────────────────────────────────────────────────────────────────────────────\n// PropertyForm — 属性表单容器\n// Designer 的自定义属性编辑器独立于 form-engine 的渲染系统\n// ────────────────────────────────────────────────────────────────────────────\n\ninterface PropertyFormProps {\n schema: NexusSchema;\n initialValues: Record<string, unknown>;\n onValuesChange: (\n allValues: Record<string, unknown>,\n changedFields: ReadonlySet<string>,\n ) => void;\n}\n\nfunction PropertyForm({\n schema,\n initialValues,\n onValuesChange,\n}: PropertyFormProps) {\n const [form] = useForm();\n const engine = form._getEngine();\n\n // 注册 antd 基础 widget(select/input/switch/number/textarea/collapse/collapsePanel)\n registerAntdUI(engine);\n // 记录本表单实例中用户实际改动过的字段(组件随 formKey 重建,节点切换时自动重置)。\n // 用于区分「未触碰的字符串默认值 ''」与「用户主动清空」——\n // 后者需要回写 schema(删除属性),前者写回会污染 schema(如 bind:'' 导致数据 key 丢失)。\n const changedFieldsRef = useRef<Set<string>>(new Set());\n // '#' 全局 watcher 由 FormController._onFieldValueChange 调用,\n // 前两个参数为 (globalData, globalData),第三参为本次实际变更的字段路径\n const handleWatch = useCallback(\n (value: unknown, _allValues: unknown, changedPath?: string) => {\n if (changedPath) {\n changedFieldsRef.current.add(changedPath);\n }\n onValuesChange(\n value as Record<string, unknown>,\n changedFieldsRef.current,\n );\n },\n [onValuesChange],\n );\n\n return (\n <NexusForm\n footer={false}\n form={form}\n schema={schema}\n initialValues={initialValues}\n watch={{ '#': handleWatch }}\n widgets={propertyWidgets}\n />\n );\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// 表单级属性(未选中节点时编辑 NexusSchema 顶层配置)\n// ────────────────────────────────────────────────────────────────────────────\n\nconst formLevelProps: Record<string, SchemaNode> = {\n displayType: {\n widget: 'select',\n type: 'string',\n title: '表单布局方向(displayType)',\n props: {\n options: [\n { label: '水平(row)', value: 'row' },\n { label: '垂直(column)', value: 'column' },\n { label: '行内(inline)', value: 'inline' },\n ],\n },\n },\n labelWidth: {\n widget: 'input',\n type: 'string',\n title: 'label 宽度(labelWidth)',\n placeholder: '如 120 或 20%',\n },\n colon: {\n widget: 'switch',\n type: 'boolean',\n title: '显示冒号(colon)',\n default: true,\n },\n label: {\n widget: 'switch',\n type: 'boolean',\n title: '显示 label(label)',\n default: true,\n },\n readOnly: {\n widget: 'switch',\n type: 'boolean',\n title: '整个表单只读(readOnly)',\n default: false,\n },\n column: {\n widget: 'number',\n type: 'number',\n title: '每行显示列数(column)',\n default: 1,\n },\n};\n\n// ────────────────────────────────────────────────────────────────────────────\n// 布局节点基础属性(layout nodes 不在 propertySchemaMap 中,单独定义)\n// ────────────────────────────────────────────────────────────────────────────\n\nconst layoutBasicProps: Record<string, SchemaNode> = {\n column: { widget: 'number', type: 'number', title: '列数(column)' },\n gap: { widget: 'number', type: 'number', title: '间距(gap)' },\n direction: {\n widget: 'select',\n type: 'string',\n title: '方向(direction)',\n props: {\n options: [\n { label: '水平', value: 'row' },\n { label: '垂直', value: 'column' },\n ],\n },\n },\n};\n\n// ────────────────────────────────────────────────────────────────────────────\n// collapseSection — 将一组扁平字段包裹成单个 collapse 分区\n// collapse / collapsePanel 均为布局节点,key 不进入数据路径,\n// 因此其内部字段(title / validate / bind 等)会被扁平地暴露给属性表单。\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction collapseSection(\n key: string,\n title: string,\n fields: Record<string, SchemaNode>,\n): Record<string, SchemaNode> {\n // @ts-expect-error\n return {\n [key]: {\n type: 'collapsePanel',\n title,\n properties: fields,\n },\n } as unknown as SchemaNode;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// KeyEditor — 单独处理 key 的编辑器\n// key 不是 SchemaNode 的属性,而是 properties 的索引键\n// 修改 key = schema 结构级操作:删除旧 key + 添加新 key + 更新选中路径\n// ────────────────────────────────────────────────────────────────────────────\n\ninterface KeyEditorProps {\n oldKey: string;\n locked?: boolean;\n onRename: (newKey: string) => void;\n}\n\nfunction KeyEditor({ oldKey, locked, onRename }: KeyEditorProps) {\n const [value, setValue] = useState(oldKey);\n // 记录已同步到本地 state 的外部 key:仅当外部 oldKey 发生「非本次编辑产生」的\n // 变化(如切换选中节点)时才重置输入框,避免用 key 重建组件导致失焦\n const syncedKeyRef = useRef(oldKey);\n\n useEffect(() => {\n if (oldKey !== syncedKeyRef.current) {\n syncedKeyRef.current = oldKey;\n setValue(oldKey);\n }\n }, [oldKey]);\n\n if (locked) {\n return (\n <div className='mb-3 flex items-center justify-between gap-2 rounded-lg border border-[#f0f0f0] bg-[#fafafa] px-3 py-2'>\n <div className='min-w-0'>\n <div className='mb-0.5 text-[11px] text-[#999]'>字段标识(key)</div>\n <code className='break-all font-mono text-[13px] text-[#333]'>\n {oldKey}\n </code>\n </div>\n <span className='shrink-0 rounded bg-[#f0f0f0] px-1.5 py-0.5 text-[11px] text-[#999]'>\n 🔒 锁定\n </span>\n </div>\n );\n }\n\n const onChange = (v: string) => {\n syncedKeyRef.current = v;\n setValue(v);\n onRename(v);\n };\n\n return (\n <div className='mb-3'>\n <div className='mb-1.5 text-[11px] text-[#999]'>字段标识(key)</div>\n <Input\n size='small'\n value={value}\n onChange={(e) => {\n const trimmed = (e.target.value || '').trim();\n onChange(trimmed);\n }}\n className='nexus-key-input'\n />\n <div className='mt-1 text-[11px] text-[#c0c4cc]'>\n 用于数据路径与联动引用,修改后自动迁移子节点\n </div>\n </div>\n );\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// PropertyPanel — 主组件\n// ────────────────────────────────────────────────────────────────────────────\n\nexport function PropertyPanel() {\n const {\n schema,\n selectedPath,\n updateNode,\n renameNode,\n selectNode,\n setSchema,\n propertySchemaMap,\n } = useDesigner();\n\n const selectedNode =\n selectedPath && selectedPath.length > 0\n ? getNodeAtProperties(schema.properties, selectedPath)\n : undefined;\n\n const isFormLevel = !selectedPath || selectedPath.length === 0;\n\n // 选择变化时强制重建引擎(nodeVersion bump)\n const formKey = `node_${(selectedPath || []).join('_')}_${(selectedNode as { type?: string } | undefined)?.type || 'root'}`;\n\n // key 的特殊性:从 path 最后一段提取,不通过 NexusForm 管理\n const currentKey = selectedPath?.[selectedPath.length - 1] ?? '';\n const keyLocked =\n (selectedNode as unknown as Record<string, unknown> | undefined)\n ?._lockedKey === true;\n\n // 选中节点的 widget/type:决定属性面板使用哪组组件属性;\n // 仅在切换节点(widget/type 改变)时重新计算,避免每次输入都重建 schema\n // (selectedNode 的 object identity 会随每次 edit 变化,不能直接依赖)\n const selectedWidgetKey = selectedNode\n ? (selectedNode.widget as string | undefined) ||\n (selectedNode.type as string)\n : null;\n\n // 构建属性表单 schema:\n // - 未选中节点(form-level):formLevelProps(NexusSchema 顶层配置)\n // - 选中节点:4 个 collapse 分区(通用属性 / 组件属性 / 校验配置 / 依赖配置)\n // collapse 与 collapsePanel 均为布局节点,key 不进入数据路径,\n // 各分区内部字段会被扁平暴露给属性表单。\n // 注意:不依赖 selectedNode 对象本身(每次 edit 都是新引用),\n // 只依赖 isFormLevel 和 selectedWidgetKey,避免无谓 re-init。\n const formSchema = useMemo<NexusSchema>(() => {\n if (isFormLevel) {\n return {\n type: 'object',\n properties: formLevelProps,\n displayType: 'column',\n } as NexusSchema;\n }\n\n const widgetProps =\n propertySchemaMap[selectedWidgetKey as string] || layoutBasicProps;\n\n return {\n type: 'object',\n properties: {\n sectionProperty: {\n widget: 'collapse',\n props: {\n defaultActiveKey: [\n 'sectionCommon',\n 'sectionFieldProps',\n 'sectionValidation',\n 'sectionReaction',\n 'sectionDependency',\n ],\n },\n type: 'collapse',\n properties: {\n ...collapseSection(\n 'sectionCommon',\n '通用属性',\n commonPropertyFields,\n ),\n ...collapseSection(\n 'sectionFieldProps',\n '组件属性',\n widgetProps as Record<string, SchemaNode>,\n ),\n ...collapseSection(\n 'sectionValidation',\n '校验配置',\n validationPropertyFields,\n ),\n ...collapseSection(\n 'sectionReaction',\n '联动配置',\n reactionPropertyFields,\n ),\n ...collapseSection(\n 'sectionDependency',\n '依赖配置',\n dependencyPropertyFields,\n ),\n },\n },\n },\n displayType: 'column',\n } as NexusSchema;\n }, [isFormLevel, selectedWidgetKey, propertySchemaMap]);\n\n // 表单级属性面板的取值来源:只提取 NexusSchema 顶层的表单配置键,\n // 而非整个 schema 对象(schema 含 properties 与嵌套字段,不是表单级配置)\n const formLevelConfig = useMemo<Record<string, unknown>>(\n () =>\n extractFormLevelConfig(\n schema as unknown as Record<string, unknown>,\n Object.keys(formLevelProps),\n ),\n [schema],\n );\n\n const handleValuesChange = useCallback(\n (\n allValues: Record<string, unknown>,\n changedFields: ReadonlySet<string>,\n ) => {\n // 只写回「真正变化」的值:未触碰的空字符串(''/undefined/null)视为未赋值,\n // 不写回 Schema(否则 bind:'' / hidden:'' / required:'' 会导致 key 丢失 / 空联动);\n // 用户主动清空的字段(changedFields 命中)以 undefined 回写,由\n // updateNodeWithNesting 从节点删除对应属性(详见 diffPropertyPatch)\n const initial = selectedNode\n ? flattenNodeForPropertyEditor(\n selectedNode as unknown as Record<string, any>,\n )\n : formLevelConfig;\n\n const patch = diffPropertyPatch(initial, allValues, changedFields);\n if (Object.keys(patch).length === 0) {\n return;\n }\n\n if (!selectedPath || selectedPath.length === 0) {\n // form-level:修改 NexusSchema 顶层属性(undefined 表示清空,删除该键)\n const next = { ...(schema as unknown as Record<string, unknown>) };\n for (const [key, value] of Object.entries(patch)) {\n if (value === undefined) {\n delete next[key];\n } else {\n next[key] = value;\n }\n }\n setSchema(next as unknown as NexusSchema);\n return;\n }\n\n // 节点级:key 已由 KeyEditor 单独处理,此处跳过\n const { key: _key, ...rest } = patch;\n if (Object.keys(rest).length > 0) {\n updateNode(selectedPath, rest);\n }\n },\n [\n selectedPath,\n updateNode,\n setSchema,\n schema,\n selectedNode,\n formLevelConfig,\n ],\n );\n\n const handleRename = useCallback(\n (newKey: string) => {\n if (!selectedPath || selectedPath.length === 0) {\n return;\n }\n renameNode(selectedPath, newKey);\n },\n [selectedPath, renameNode],\n );\n\n // 节点类型徽标:字段取 widget,布局/对象取 type\n const nodeKind = selectedNode\n ? (\n (selectedNode.widget as string) ||\n (selectedNode.type as string) ||\n ''\n ).toLowerCase()\n : '';\n\n return (\n <div className='flex h-full flex-col border-l border-[#f0f0f0] bg-white'>\n {/* 导航头 */}\n <div className='flex items-center justify-between border-b border-[#f0f0f0] px-4 py-2.5'>\n <div className='pl-5 flex min-w-0 items-center gap-1 h-6'>\n {!isFormLevel && (\n <button\n type='button'\n onClick={() => selectNode(null)}\n className='-ml-1 mr-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-[#999] transition-colors hover:bg-[#f0f0f0] hover:text-[#333]'\n title='返回表单属性'\n >\n <svg\n width='12'\n height='12'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden='true'\n >\n <polyline points='15 18 9 12 15 6' />\n </svg>\n </button>\n )}\n <span className='truncate text-[13px] font-semibold text-[#333]'>\n {isFormLevel ? '表单属性' : '节点属性'}\n </span>\n </div>\n {!isFormLevel && nodeKind && (\n <span className='ml-2 shrink-0 rounded-md bg-[#e8f1ff] px-1.5 py-0.5 text-[11px] text-[#1677ff]'>\n {nodeKind}\n </span>\n )}\n </div>\n\n <div className='nexus-property-body flex-1 overflow-y-auto px-4 py-3'>\n {/* key 特殊性处理:key 不是 SchemaNode 属性,而是 properties 索引键 */}\n {!isFormLevel && currentKey && (\n <KeyEditor\n oldKey={currentKey}\n locked={keyLocked}\n onRename={handleRename}\n />\n )}\n\n {/* 属性表单:完全基于 descriptor 驱动 */}\n <PropertyForm\n key={formKey}\n schema={formSchema}\n initialValues={\n selectedNode\n ? flattenNodeForPropertyEditor(\n selectedNode as unknown as Record<string, any>,\n )\n : formLevelConfig\n }\n onValuesChange={handleValuesChange}\n />\n </div>\n </div>\n );\n}\n"],"names":["PropertyForm","schema","initialValues","onValuesChange","form","useForm","engine","registerAntdUI","changedFieldsRef","useRef","handleWatch","useCallback","value","_allValues","changedPath","jsx","NexusForm","propertyWidgets","formLevelProps","layoutBasicProps","collapseSection","key","title","fields","KeyEditor","oldKey","locked","onRename","setValue","useState","syncedKeyRef","useEffect","jsxs","onChange","v","Input","e","trimmed","PropertyPanel","selectedPath","updateNode","renameNode","selectNode","setSchema","propertySchemaMap","useDesigner","selectedNode","getNodeAtProperties","isFormLevel","formKey","currentKey","keyLocked","selectedWidgetKey","formSchema","useMemo","widgetProps","commonPropertyFields","validationPropertyFields","reactionPropertyFields","dependencyPropertyFields","formLevelConfig","extractFormLevelConfig","handleValuesChange","allValues","changedFields","initial","flattenNodeForPropertyEditor","patch","diffPropertyPatch","next","_key","rest","handleRename","newKey","nodeKind"],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { jsxs as b, Fragment as D, jsx as n } from "react/jsx-runtime";
2
+ import { useRef as w, useState as d, useCallback as k, useEffect as $ } from "react";
3
+ function F({
4
+ side: r,
5
+ defaultWidth: u,
6
+ minWidth: f,
7
+ maxWidth: m,
8
+ children: M,
9
+ collapsedTitle: o
10
+ }) {
11
+ const h = w(null), [l, g] = d(u), [e, L] = d(!1), [s, p] = d(!1), v = w(u), z = k(() => {
12
+ L((t) => (t ? g(v.current) : v.current = l, !t));
13
+ }, [l]), C = k((t) => {
14
+ t.preventDefault(), t.stopPropagation(), p(!0);
15
+ }, []);
16
+ $(() => {
17
+ if (!s)
18
+ return;
19
+ const t = (x) => {
20
+ const c = h.current?.getBoundingClientRect();
21
+ if (!c)
22
+ return;
23
+ const B = r === "left" ? x.clientX - c.left : c.right - x.clientX;
24
+ g(Math.max(f, Math.min(m, Math.round(B))));
25
+ }, y = () => p(!1);
26
+ return document.addEventListener("mousemove", t), document.addEventListener("mouseup", y), document.body.style.userSelect = "none", document.body.style.cursor = "col-resize", () => {
27
+ document.removeEventListener("mousemove", t), document.removeEventListener("mouseup", y), document.body.style.userSelect = "", document.body.style.cursor = "";
28
+ };
29
+ }, [s, f, m, r]);
30
+ const a = r === "left", S = {
31
+ width: e ? 36 : l,
32
+ flexShrink: 0,
33
+ overflow: "hidden",
34
+ transition: s ? "none" : "width 180ms ease",
35
+ position: "relative",
36
+ background: e ? "#fafafa" : void 0
37
+ }, R = [
38
+ "nexus-resize-handle",
39
+ s ? "nexus-resize-active" : ""
40
+ ].filter(Boolean).join(" "), j = a ? { right: -2 } : { left: -2 }, i = {
41
+ position: "absolute",
42
+ top: 8,
43
+ zIndex: 20
44
+ };
45
+ a ? i.right = e ? 7 : 8 : i.left = e ? 7 : 8;
46
+ const E = a ? e ? "rotate(0deg)" : "rotate(180deg)" : e ? "rotate(180deg)" : "rotate(0deg)", N = e && o ? /* @__PURE__ */ n("div", { className: "flex h-full items-center justify-center bg-[#fafafa] select-none", children: /* @__PURE__ */ n(
47
+ "span",
48
+ {
49
+ className: "text-[11px] font-medium text-[#999] whitespace-nowrap tracking-[3px]",
50
+ style: { writingMode: "vertical-lr" },
51
+ children: o
52
+ }
53
+ ) }) : null;
54
+ return /* @__PURE__ */ b(D, { children: [
55
+ s ? /* @__PURE__ */ n("div", { className: "fixed inset-0 z-[9999]", style: { cursor: "col-resize" } }) : null,
56
+ /* @__PURE__ */ b("div", { ref: h, style: S, children: [
57
+ /* @__PURE__ */ n(
58
+ "div",
59
+ {
60
+ className: R,
61
+ style: j,
62
+ onMouseDown: C
63
+ }
64
+ ),
65
+ /* @__PURE__ */ n(
66
+ "button",
67
+ {
68
+ type: "button",
69
+ className: "nexus-panel-toggle",
70
+ style: i,
71
+ onClick: z,
72
+ title: e ? `展开${o}` : `折叠${o}`,
73
+ "aria-expanded": !e,
74
+ "aria-label": e ? `展开${o}` : `折叠${o}`,
75
+ children: /* @__PURE__ */ n(
76
+ "svg",
77
+ {
78
+ width: "10",
79
+ height: "10",
80
+ viewBox: "0 0 24 24",
81
+ fill: "none",
82
+ stroke: "currentColor",
83
+ strokeWidth: "2.5",
84
+ strokeLinecap: "round",
85
+ strokeLinejoin: "round",
86
+ style: {
87
+ transform: E,
88
+ transition: "transform 180ms ease"
89
+ },
90
+ "aria-hidden": "true",
91
+ children: /* @__PURE__ */ n("polyline", { points: "9 18 15 12 9 6" })
92
+ }
93
+ )
94
+ }
95
+ ),
96
+ e ? N : M
97
+ ] })
98
+ ] });
99
+ }
100
+ export {
101
+ F as ResizablePanel
102
+ };
103
+ //# sourceMappingURL=resizable-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resizable-panel.js","sources":["../../src/resizable-panel.tsx"],"sourcesContent":["// ============================================================================\n// @xbeeant/form-engine-designer — 可拖拽调整宽度 / 可折叠面板\n// ============================================================================\n\nimport type { CSSProperties } from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface ResizablePanelProps {\n /** 面板位于左侧还是右侧(决定拖拽手柄位置) */\n side: 'left' | 'right';\n /** 默认展开宽度(px) */\n defaultWidth: number;\n /** 最小宽度 */\n minWidth: number;\n /** 最大宽度 */\n maxWidth: number;\n /** 面板内容 */\n children: React.ReactNode;\n /** 折叠状态时的竖排标题 */\n collapsedTitle: string;\n}\n\n/**\n * 可拖拽调整宽度的侧边面板,支持折叠/展开。\n *\n * - 拖拽手柄位于面板内侧边缘(左面板:右边缘,右面板:左边缘)\n * - 折叠按钮位于手柄附近的一角\n * - 拖拽时全局禁用文本选中,防止误操作\n */\nexport function ResizablePanel({\n side,\n defaultWidth,\n minWidth,\n maxWidth,\n children,\n collapsedTitle,\n}: ResizablePanelProps) {\n const panelRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(defaultWidth);\n const [collapsed, setCollapsed] = useState(false);\n const [dragging, setDragging] = useState(false);\n\n // 折叠前记忆宽度,展开时恢复\n const preCollapseWidthRef = useRef(defaultWidth);\n\n const toggle = useCallback(() => {\n setCollapsed((prev) => {\n if (prev) {\n // 展开:恢复之前宽度\n setWidth(preCollapseWidthRef.current);\n } else {\n // 折叠:记忆当前宽度\n preCollapseWidthRef.current = width;\n }\n return !prev;\n });\n }, [width]);\n\n // ─── 拖拽调整宽度 ─────────────────────────────────────────────\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setDragging(true);\n }, []);\n\n useEffect(() => {\n if (!dragging) {\n return;\n }\n\n const handleMouseMove = (e: MouseEvent) => {\n const rect = panelRef.current?.getBoundingClientRect();\n if (!rect) {\n return;\n }\n\n const next =\n side === 'left'\n ? e.clientX - rect.left // 左面板:拖右边缘 = 距左边界距离\n : rect.right - e.clientX; // 右面板:拖左边缘 = 距右边界距离\n\n setWidth(Math.max(minWidth, Math.min(maxWidth, Math.round(next))));\n };\n\n const handleMouseUp = () => setDragging(false);\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n // 拖拽时禁用文本选中 & 全局 col-resize 光标\n document.body.style.userSelect = 'none';\n document.body.style.cursor = 'col-resize';\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n document.body.style.userSelect = '';\n document.body.style.cursor = '';\n };\n }, [dragging, minWidth, maxWidth, side]);\n\n // ─── 渲染 ─────────────────────────────────────────────────────\n const isLeft = side === 'left';\n const collapsedWidth = 36;\n\n const containerStyle: CSSProperties = {\n width: collapsed ? collapsedWidth : width,\n flexShrink: 0,\n overflow: 'hidden',\n transition: dragging ? 'none' : 'width 180ms ease',\n position: 'relative',\n background: collapsed ? '#fafafa' : undefined,\n };\n\n const handleClass = [\n 'nexus-resize-handle',\n dragging ? 'nexus-resize-active' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const handleStyle: CSSProperties = isLeft ? { right: -2 } : { left: -2 };\n\n // 折叠按钮跟随面板侧\n const toggleBtnStyle: CSSProperties = {\n position: 'absolute',\n top: 8,\n zIndex: 20,\n };\n if (isLeft) {\n toggleBtnStyle.right = collapsed ? 7 : 8;\n } else {\n toggleBtnStyle.left = collapsed ? 7 : 8;\n }\n\n // chevron-right 基准图标,按面板侧/折叠态旋转\n const chevronRotation = isLeft\n ? collapsed\n ? 'rotate(0deg)'\n : 'rotate(180deg)'\n : collapsed\n ? 'rotate(180deg)'\n : 'rotate(0deg)';\n\n // 折叠态竖排标题\n const collapsedLabel =\n collapsed && collapsedTitle ? (\n <div className='flex h-full items-center justify-center bg-[#fafafa] select-none'>\n <span\n className='text-[11px] font-medium text-[#999] whitespace-nowrap tracking-[3px]'\n style={{ writingMode: 'vertical-lr' }}\n >\n {collapsedTitle}\n </span>\n </div>\n ) : null;\n\n // 拖拽蒙层:防止 iframe / canvas 内容吞掉 mousemove 事件\n const dragOverlay = dragging ? (\n <div className='fixed inset-0 z-[9999]' style={{ cursor: 'col-resize' }} />\n ) : null;\n\n return (\n <>\n {dragOverlay}\n <div ref={panelRef} style={containerStyle}>\n {/* 拖拽手柄 */}\n <div\n className={handleClass}\n style={handleStyle}\n onMouseDown={handleMouseDown}\n />\n\n {/* 折叠/展开按钮 */}\n <button\n type='button'\n className='nexus-panel-toggle'\n style={toggleBtnStyle}\n onClick={toggle}\n title={collapsed ? `展开${collapsedTitle}` : `折叠${collapsedTitle}`}\n aria-expanded={!collapsed}\n aria-label={\n collapsed ? `展开${collapsedTitle}` : `折叠${collapsedTitle}`\n }\n >\n <svg\n width='10'\n height='10'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n style={{\n transform: chevronRotation,\n transition: 'transform 180ms ease',\n }}\n aria-hidden='true'\n >\n <polyline points='9 18 15 12 9 6' />\n </svg>\n </button>\n\n {/* 折叠时仅显示竖排标题,展开时渲染完整内容 */}\n {collapsed ? collapsedLabel : children}\n </div>\n </>\n );\n}\n"],"names":["ResizablePanel","side","defaultWidth","minWidth","maxWidth","children","collapsedTitle","panelRef","useRef","width","setWidth","useState","collapsed","setCollapsed","dragging","setDragging","preCollapseWidthRef","toggle","useCallback","prev","handleMouseDown","e","useEffect","handleMouseMove","rect","next","handleMouseUp","isLeft","containerStyle","handleClass","handleStyle","toggleBtnStyle","chevronRotation","collapsedLabel","jsx","jsxs","Fragment"],"mappings":"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"}