@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,314 @@
1
+ import { jsx as i, jsxs as d } from "react/jsx-runtime";
2
+ import { Space as p, Input as h, Select as K, Tooltip as u, Collapse as k, InputNumber as S, Button as y } from "antd";
3
+ import { useState as C, useRef as f, useEffect as P } from "react";
4
+ import { genId as x } from "./reactions-model.js";
5
+ function v(e) {
6
+ return !e || typeof e != "object" ? [] : Object.entries(e).map(([l, t]) => ({
7
+ id: x(),
8
+ key: l,
9
+ value: typeof t == "string" ? t : JSON.stringify(t)
10
+ }));
11
+ }
12
+ function g(e) {
13
+ if (!e)
14
+ return;
15
+ const l = {};
16
+ for (const t of e) {
17
+ const n = t.key.trim();
18
+ if (!n)
19
+ continue;
20
+ const r = t.value.trim();
21
+ if (r)
22
+ try {
23
+ l[n] = JSON.parse(r);
24
+ } catch {
25
+ l[n] = r;
26
+ }
27
+ }
28
+ return Object.keys(l).length > 0 ? l : void 0;
29
+ }
30
+ function b(e) {
31
+ const l = e ?? {}, t = l.responseField ?? {}, n = l.params;
32
+ return {
33
+ url: l.url,
34
+ method: l.method,
35
+ dataPath: t.data,
36
+ valueKey: t.value,
37
+ labelKey: t.label,
38
+ params: n && typeof n == "object" ? v(n) : void 0,
39
+ headers: v(l.headers),
40
+ cacheKey: l.cacheKey,
41
+ timeout: l.timeout
42
+ };
43
+ }
44
+ function T(e) {
45
+ const l = e.url?.trim();
46
+ if (!l)
47
+ return;
48
+ const t = g(e.params), n = g(e.headers), r = {
49
+ url: l,
50
+ method: e.method || "GET",
51
+ responseField: {
52
+ data: e.dataPath?.trim() || "data",
53
+ value: e.valueKey?.trim() || "value",
54
+ label: e.labelKey?.trim() || "label"
55
+ }
56
+ };
57
+ return t && (r.params = t), n && (r.headers = n), e.cacheKey?.trim() && (r.cacheKey = e.cacheKey.trim()), e.timeout != null && (r.timeout = e.timeout), r;
58
+ }
59
+ function z({
60
+ rows: e,
61
+ onChange: l,
62
+ valuePlaceholder: t,
63
+ keyPlaceholder: n
64
+ }) {
65
+ return /* @__PURE__ */ d("div", { style: { width: "100%" }, children: [
66
+ e.map((r) => /* @__PURE__ */ d(
67
+ "div",
68
+ {
69
+ style: {
70
+ display: "flex",
71
+ gap: 6,
72
+ alignItems: "center",
73
+ marginBottom: 6
74
+ },
75
+ children: [
76
+ /* @__PURE__ */ i(
77
+ h,
78
+ {
79
+ size: "small",
80
+ value: r.key,
81
+ placeholder: n,
82
+ onChange: (c) => l(
83
+ e.map(
84
+ (o) => o.id === r.id ? { ...o, key: c.target.value } : o
85
+ )
86
+ )
87
+ }
88
+ ),
89
+ /* @__PURE__ */ i(
90
+ h,
91
+ {
92
+ size: "small",
93
+ value: r.value,
94
+ placeholder: t,
95
+ onChange: (c) => l(
96
+ e.map(
97
+ (o) => o.id === r.id ? { ...o, value: c.target.value } : o
98
+ )
99
+ )
100
+ }
101
+ ),
102
+ /* @__PURE__ */ i(
103
+ y,
104
+ {
105
+ size: "small",
106
+ type: "text",
107
+ danger: !0,
108
+ onClick: () => l(e.filter((c) => c.id !== r.id)),
109
+ children: "✕"
110
+ }
111
+ )
112
+ ]
113
+ },
114
+ r.id
115
+ )),
116
+ /* @__PURE__ */ i(
117
+ y,
118
+ {
119
+ size: "small",
120
+ type: "dashed",
121
+ block: !0,
122
+ onClick: () => l([...e, { id: x(), key: "", value: "" }]),
123
+ children: "+ 添加"
124
+ }
125
+ )
126
+ ] });
127
+ }
128
+ function w({ value: e, onChange: l }) {
129
+ const [t, n] = C(() => b(e)), r = f(e), c = f(e);
130
+ P(() => {
131
+ r.current !== e && (r.current = e, e !== c.current && n(b(e)));
132
+ }, [e]);
133
+ const o = (a) => {
134
+ n(a);
135
+ const m = T(a);
136
+ c.current = m, l(m);
137
+ }, s = (a) => {
138
+ o({ ...t, ...a });
139
+ };
140
+ return /* @__PURE__ */ i("div", { style: { width: "100%" }, children: /* @__PURE__ */ d(p, { orientation: "vertical", style: { width: "100%" }, size: 8, children: [
141
+ /* @__PURE__ */ d("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
142
+ /* @__PURE__ */ i(
143
+ h,
144
+ {
145
+ size: "small",
146
+ placeholder: "接口地址(必填),如 /api/options",
147
+ value: t.url,
148
+ onChange: (a) => s({ url: a.target.value }),
149
+ style: { flex: 1 }
150
+ }
151
+ ),
152
+ /* @__PURE__ */ i(
153
+ K,
154
+ {
155
+ size: "small",
156
+ style: { width: 80 },
157
+ value: t.method,
158
+ onChange: (a) => s({ method: a }),
159
+ options: [
160
+ { value: "GET", label: "GET" },
161
+ { value: "POST", label: "POST" }
162
+ ]
163
+ }
164
+ )
165
+ ] }),
166
+ /* @__PURE__ */ i("div", { style: { fontSize: 11, color: "#999" }, children: "响应字段映射" }),
167
+ /* @__PURE__ */ d("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
168
+ /* @__PURE__ */ i(u, { title: "响应数据数组的路径,如 data.list / data.result.records", children: /* @__PURE__ */ i(
169
+ h,
170
+ {
171
+ size: "small",
172
+ placeholder: "数据路径(默认 data)",
173
+ value: t.dataPath,
174
+ onChange: (a) => s({ dataPath: a.target.value }),
175
+ style: { flex: 1 }
176
+ }
177
+ ) }),
178
+ /* @__PURE__ */ i(u, { title: "作为选项值(value)的字段名", children: /* @__PURE__ */ i(
179
+ h,
180
+ {
181
+ size: "small",
182
+ placeholder: "值字段",
183
+ value: t.valueKey,
184
+ onChange: (a) => s({ valueKey: a.target.value }),
185
+ style: { flex: 1 }
186
+ }
187
+ ) }),
188
+ /* @__PURE__ */ i(u, { title: "作为选项文案(label)的字段名", children: /* @__PURE__ */ i(
189
+ h,
190
+ {
191
+ size: "small",
192
+ placeholder: "文案字段",
193
+ value: t.labelKey,
194
+ onChange: (a) => s({ labelKey: a.target.value }),
195
+ style: { flex: 1 }
196
+ }
197
+ ) })
198
+ ] }),
199
+ /* @__PURE__ */ i(
200
+ k,
201
+ {
202
+ size: "small",
203
+ style: { width: "100%", background: "#fafafa" },
204
+ items: [
205
+ {
206
+ key: "params",
207
+ label: "请求参数(可选)",
208
+ children: /* @__PURE__ */ i(
209
+ z,
210
+ {
211
+ rows: t.params ?? [],
212
+ onChange: (a) => s({ params: a }),
213
+ keyPlaceholder: "参数名",
214
+ valuePlaceholder: "参数值(数字/布尔自动识别)"
215
+ }
216
+ )
217
+ },
218
+ {
219
+ key: "headers",
220
+ label: "请求头(可选)",
221
+ children: /* @__PURE__ */ i(
222
+ z,
223
+ {
224
+ rows: t.headers ?? [],
225
+ onChange: (a) => s({ headers: a }),
226
+ keyPlaceholder: "请求头名",
227
+ valuePlaceholder: "如 Bearer xxxx"
228
+ }
229
+ )
230
+ },
231
+ {
232
+ key: "advanced",
233
+ label: "高级",
234
+ children: /* @__PURE__ */ d(
235
+ p,
236
+ {
237
+ orientation: "vertical",
238
+ style: { width: "100%" },
239
+ size: 8,
240
+ children: [
241
+ /* @__PURE__ */ d(
242
+ "div",
243
+ {
244
+ style: { display: "flex", gap: 6, alignItems: "center" },
245
+ children: [
246
+ /* @__PURE__ */ i(
247
+ "span",
248
+ {
249
+ style: {
250
+ fontSize: 12,
251
+ color: "#666",
252
+ whiteSpace: "nowrap"
253
+ },
254
+ children: "缓存键"
255
+ }
256
+ ),
257
+ /* @__PURE__ */ i(
258
+ h,
259
+ {
260
+ size: "small",
261
+ placeholder: "默认按字段路径缓存",
262
+ value: t.cacheKey,
263
+ onChange: (a) => s({ cacheKey: a.target.value })
264
+ }
265
+ )
266
+ ]
267
+ }
268
+ ),
269
+ /* @__PURE__ */ d(
270
+ "div",
271
+ {
272
+ style: { display: "flex", gap: 6, alignItems: "center" },
273
+ children: [
274
+ /* @__PURE__ */ i(
275
+ "span",
276
+ {
277
+ style: {
278
+ fontSize: 12,
279
+ color: "#666",
280
+ whiteSpace: "nowrap"
281
+ },
282
+ children: "超时(秒)"
283
+ }
284
+ ),
285
+ /* @__PURE__ */ i(
286
+ S,
287
+ {
288
+ size: "small",
289
+ min: 1,
290
+ value: t.timeout,
291
+ onChange: (a) => s({ timeout: a ?? void 0 }),
292
+ placeholder: "默认 10",
293
+ style: { width: 120 }
294
+ }
295
+ )
296
+ ]
297
+ }
298
+ )
299
+ ]
300
+ }
301
+ )
302
+ }
303
+ ]
304
+ }
305
+ ),
306
+ /* @__PURE__ */ i("div", { style: { fontSize: 11, color: "#ccc" }, children: "提示:字段值变化后选项自动刷新;POST 时参数放入请求体" })
307
+ ] }) });
308
+ }
309
+ const j = w;
310
+ export {
311
+ w as RemoteDataEditor,
312
+ j as remoteDataEditorWidget
313
+ };
314
+ //# sourceMappingURL=remote-data-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-data-editor.js","sources":["../../../src/widgets/remote-data-editor.tsx"],"sourcesContent":["// ============================================================================\n// RemoteDataEditor — 可视化编辑 remoteData 远程数据配置\n// 表单化配置代替手写 JSON:\n// - 接口地址 / 请求方式(GET | POST)\n// - 响应字段映射(数据数组路径 / 值字段 / 文案字段)\n// - 请求参数(key-value 行编辑)\n// - 请求头(key-value 行编辑,可选)\n// - 缓存键 / 超时(可选)\n// 值格式:RemoteDataConfig(见 @xbeeant/form-engine-ui 的 _shared.tsx)\n// ============================================================================\n\nimport type { WidgetProps } from '@xbeeant/form-engine-ui';\nimport {\n Button,\n Collapse,\n Input,\n InputNumber,\n Select,\n Space,\n Tooltip,\n} from 'antd';\nimport { useEffect, useRef, useState } from 'react';\nimport { genId } from './reactions-model';\n\ninterface KeyValueRow {\n id: string;\n key: string;\n value: string;\n}\n\ninterface RemoteDataForm {\n url?: string;\n method?: 'GET' | 'POST';\n dataPath?: string;\n valueKey?: string;\n labelKey?: string;\n params?: KeyValueRow[];\n headers?: KeyValueRow[];\n cacheKey?: string;\n timeout?: number;\n}\n\n/** Record → key-value 行 */\nfunction recordToRows(\n value: Record<string, unknown> | undefined,\n): KeyValueRow[] {\n if (!value || typeof value !== 'object') {\n return [];\n }\n return Object.entries(value).map(([key, v]) => ({\n id: genId(),\n key,\n value: typeof v === 'string' ? v : JSON.stringify(v),\n }));\n}\n\n/** key-value 行 → Record(空 key 或空 value 的行丢弃) */\nfunction rowsToRecord(\n rows: KeyValueRow[] | undefined,\n): Record<string, unknown> | undefined {\n if (!rows) {\n return undefined;\n }\n const result: Record<string, unknown> = {};\n for (const row of rows) {\n const key = row.key.trim();\n if (!key) {\n continue;\n }\n const raw = row.value.trim();\n if (!raw) {\n continue;\n }\n // 尝试解析 JSON(数字 / 布尔 / 对象),失败保留字符串\n try {\n result[key] = JSON.parse(raw);\n } catch {\n result[key] = raw;\n }\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\n/** RemoteDataConfig → 表单 */\nfunction configToForm(value: unknown): RemoteDataForm {\n const cfg = (value ?? {}) as Record<string, any>;\n const rf = (cfg.responseField ?? {}) as Record<string, any>;\n const params = cfg.params;\n return {\n url: cfg.url,\n method: cfg.method,\n dataPath: rf.data,\n valueKey: rf.value,\n labelKey: rf.label,\n params:\n params && typeof params === 'object' ? recordToRows(params) : undefined,\n headers: recordToRows(cfg.headers),\n cacheKey: cfg.cacheKey,\n timeout: cfg.timeout,\n };\n}\n\n/** 表单 → RemoteDataConfig */\nfunction formToConfig(\n form: RemoteDataForm,\n): Record<string, unknown> | undefined {\n const url = form.url?.trim();\n if (!url) {\n return undefined;\n }\n const params = rowsToRecord(form.params);\n const headers = rowsToRecord(form.headers);\n const config: Record<string, unknown> = {\n url,\n method: form.method || 'GET',\n responseField: {\n data: form.dataPath?.trim() || 'data',\n value: form.valueKey?.trim() || 'value',\n label: form.labelKey?.trim() || 'label',\n },\n };\n if (params) {\n config.params = params;\n }\n if (headers) {\n config.headers = headers;\n }\n if (form.cacheKey?.trim()) {\n config.cacheKey = form.cacheKey.trim();\n }\n if (form.timeout != null) {\n config.timeout = form.timeout;\n }\n return config;\n}\n\n/** key-value 行编辑器(参数 / 请求头共用) */\nfunction KeyValueRows({\n rows,\n onChange,\n valuePlaceholder,\n keyPlaceholder,\n}: {\n rows: KeyValueRow[];\n onChange: (rows: KeyValueRow[]) => void;\n valuePlaceholder: string;\n keyPlaceholder: string;\n}) {\n return (\n <div style={{ width: '100%' }}>\n {rows.map((row) => (\n <div\n key={row.id}\n style={{\n display: 'flex',\n gap: 6,\n alignItems: 'center',\n marginBottom: 6,\n }}\n >\n <Input\n size='small'\n value={row.key}\n placeholder={keyPlaceholder}\n onChange={(e) =>\n onChange(\n rows.map((r) =>\n r.id === row.id ? { ...r, key: e.target.value } : r,\n ),\n )\n }\n />\n <Input\n size='small'\n value={row.value}\n placeholder={valuePlaceholder}\n onChange={(e) =>\n onChange(\n rows.map((r) =>\n r.id === row.id ? { ...r, value: e.target.value } : r,\n ),\n )\n }\n />\n <Button\n size='small'\n type='text'\n danger\n onClick={() => onChange(rows.filter((r) => r.id !== row.id))}\n >\n ✕\n </Button>\n </div>\n ))}\n <Button\n size='small'\n type='dashed'\n block\n onClick={() => onChange([...rows, { id: genId(), key: '', value: '' }])}\n >\n + 添加\n </Button>\n </div>\n );\n}\n\nexport function RemoteDataEditor({ value, onChange }: WidgetProps) {\n const [form, setForm] = useState<RemoteDataForm>(() => configToForm(value));\n const prevValueRef = useRef(value);\n // 自身 onChange 引发的 value 回传不应触发重建(否则每次输入都被重置)\n const lastEmittedRef = useRef(value);\n\n useEffect(() => {\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (value !== lastEmittedRef.current) {\n setForm(configToForm(value));\n }\n }\n }, [value]);\n\n const commit = (next: RemoteDataForm) => {\n setForm(next);\n const emitted = formToConfig(next);\n lastEmittedRef.current = emitted;\n onChange(emitted);\n };\n\n const patch = (partial: Partial<RemoteDataForm>) => {\n commit({ ...form, ...partial });\n };\n\n return (\n <div style={{ width: '100%' }}>\n <Space orientation='vertical' style={{ width: '100%' }} size={8}>\n {/* 请求地址 + 方式 */}\n <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <Input\n size='small'\n placeholder='接口地址(必填),如 /api/options'\n value={form.url}\n onChange={(e) => patch({ url: e.target.value })}\n style={{ flex: 1 }}\n />\n <Select\n size='small'\n style={{ width: 80 }}\n value={form.method}\n onChange={(v) => patch({ method: v })}\n options={[\n { value: 'GET', label: 'GET' },\n { value: 'POST', label: 'POST' },\n ]}\n />\n </div>\n\n {/* 响应字段映射 */}\n <div style={{ fontSize: 11, color: '#999' }}>响应字段映射</div>\n <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>\n <Tooltip title='响应数据数组的路径,如 data.list / data.result.records'>\n <Input\n size='small'\n placeholder='数据路径(默认 data)'\n value={form.dataPath}\n onChange={(e) => patch({ dataPath: e.target.value })}\n style={{ flex: 1 }}\n />\n </Tooltip>\n <Tooltip title='作为选项值(value)的字段名'>\n <Input\n size='small'\n placeholder='值字段'\n value={form.valueKey}\n onChange={(e) => patch({ valueKey: e.target.value })}\n style={{ flex: 1 }}\n />\n </Tooltip>\n <Tooltip title='作为选项文案(label)的字段名'>\n <Input\n size='small'\n placeholder='文案字段'\n value={form.labelKey}\n onChange={(e) => patch({ labelKey: e.target.value })}\n style={{ flex: 1 }}\n />\n </Tooltip>\n </div>\n\n {/* 请求参数 / 请求头 / 高级 */}\n <Collapse\n size='small'\n style={{ width: '100%', background: '#fafafa' }}\n items={[\n {\n key: 'params',\n label: '请求参数(可选)',\n children: (\n <KeyValueRows\n rows={form.params ?? []}\n onChange={(rows) => patch({ params: rows })}\n keyPlaceholder='参数名'\n valuePlaceholder='参数值(数字/布尔自动识别)'\n />\n ),\n },\n {\n key: 'headers',\n label: '请求头(可选)',\n children: (\n <KeyValueRows\n rows={form.headers ?? []}\n onChange={(rows) => patch({ headers: rows })}\n keyPlaceholder='请求头名'\n valuePlaceholder='如 Bearer xxxx'\n />\n ),\n },\n {\n key: 'advanced',\n label: '高级',\n children: (\n <Space\n orientation='vertical'\n style={{ width: '100%' }}\n size={8}\n >\n <div\n style={{ display: 'flex', gap: 6, alignItems: 'center' }}\n >\n <span\n style={{\n fontSize: 12,\n color: '#666',\n whiteSpace: 'nowrap',\n }}\n >\n 缓存键\n </span>\n <Input\n size='small'\n placeholder='默认按字段路径缓存'\n value={form.cacheKey}\n onChange={(e) => patch({ cacheKey: e.target.value })}\n />\n </div>\n <div\n style={{ display: 'flex', gap: 6, alignItems: 'center' }}\n >\n <span\n style={{\n fontSize: 12,\n color: '#666',\n whiteSpace: 'nowrap',\n }}\n >\n 超时(秒)\n </span>\n <InputNumber\n size='small'\n min={1}\n value={form.timeout}\n onChange={(v) => patch({ timeout: v ?? undefined })}\n placeholder='默认 10'\n style={{ width: 120 }}\n />\n </div>\n </Space>\n ),\n },\n ]}\n />\n\n <div style={{ fontSize: 11, color: '#ccc' }}>\n 提示:字段值变化后选项自动刷新;POST 时参数放入请求体\n </div>\n </Space>\n </div>\n );\n}\n\nexport const remoteDataEditorWidget = RemoteDataEditor;\n"],"names":["recordToRows","value","key","v","genId","rowsToRecord","rows","result","row","raw","configToForm","cfg","rf","params","formToConfig","form","url","headers","config","KeyValueRows","onChange","valuePlaceholder","keyPlaceholder","jsxs","jsx","Input","e","r","Button","RemoteDataEditor","setForm","useState","prevValueRef","useRef","lastEmittedRef","useEffect","commit","next","emitted","patch","partial","Space","Select","Tooltip","Collapse","InputNumber","remoteDataEditorWidget"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { useMemo as e } from "react";
2
+ import { useDesigner as o } from "../designer-context.js";
3
+ import { collectDataFieldPaths as r, collectDataFieldOptions as s } from "../schema-utils.js";
4
+ function n() {
5
+ const { schema: t } = o();
6
+ return e(() => r(t), [t]);
7
+ }
8
+ function c() {
9
+ const { schema: t } = o();
10
+ return e(() => s(t), [t]);
11
+ }
12
+ export {
13
+ c as useFormDataFieldOptions,
14
+ n as useFormDataFields
15
+ };
16
+ //# sourceMappingURL=use-form-data-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-form-data-fields.js","sources":["../../../src/widgets/use-form-data-fields.ts"],"sourcesContent":["// ============================================================================\n// useFormDataFields — 从当前设计器 schema 收集 formData 字段(供变量/依赖选择)\n// ============================================================================\n\nimport { useMemo } from 'react';\nimport { useDesigner } from '../designer-context';\nimport {\n collectDataFieldOptions,\n collectDataFieldPaths,\n} from '../schema-utils';\n\nexport function useFormDataFields(): string[] {\n const { schema } = useDesigner();\n return useMemo(() => collectDataFieldPaths(schema), [schema]);\n}\n\n/** 带字段标题的选项列表:显示 title,值为路径 key(供 dependencies 等选择器) */\nexport function useFormDataFieldOptions(): Array<{\n value: string;\n label: string;\n}> {\n const { schema } = useDesigner();\n return useMemo(() => collectDataFieldOptions(schema), [schema]);\n}\n"],"names":["useFormDataFields","schema","useDesigner","useMemo","collectDataFieldPaths","useFormDataFieldOptions","collectDataFieldOptions"],"mappings":";;;AAWO,SAASA,IAA8B;AAC5C,QAAM,EAAE,QAAAC,EAAA,IAAWC,EAAA;AACnB,SAAOC,EAAQ,MAAMC,EAAsBH,CAAM,GAAG,CAACA,CAAM,CAAC;AAC9D;AAGO,SAASI,IAGb;AACD,QAAM,EAAE,QAAAJ,EAAA,IAAWC,EAAA;AACnB,SAAOC,EAAQ,MAAMG,EAAwBL,CAAM,GAAG,CAACA,CAAM,CAAC;AAChE;"}
@@ -0,0 +1,118 @@
1
+ import { jsxs as c, jsx as r } from "react/jsx-runtime";
2
+ import { Button as u, Input as k } from "antd";
3
+ import { useState as v, useRef as h, useEffect as R } from "react";
4
+ import { ExpressionBuilder as z } from "./expression-builder.js";
5
+ import { genId as y } from "./reactions-model.js";
6
+ import { useFormDataFields as B } from "./use-form-data-fields.js";
7
+ function g(e) {
8
+ return !e || typeof e != "object" ? [] : Object.entries(e).map(
9
+ ([n, s]) => ({ id: y(), key: n, expr: String(s ?? "") })
10
+ );
11
+ }
12
+ function S(e) {
13
+ const n = {};
14
+ for (const s of e) {
15
+ const o = s.key.trim(), l = s.expr.trim();
16
+ !o || !l || l === "{{ }}" || (n[o] = l);
17
+ }
18
+ return Object.keys(n).length > 0 ? n : void 0;
19
+ }
20
+ function j({ value: e, onChange: n }) {
21
+ const s = B(), [o, l] = v(() => g(e)), m = h(e), f = h(e);
22
+ R(() => {
23
+ m.current !== e && (m.current = e, e !== f.current && l(g(e)));
24
+ }, [e]);
25
+ const a = (t) => {
26
+ l(t);
27
+ const i = S(t);
28
+ f.current = i, n(i);
29
+ }, p = (t, i) => {
30
+ a(o.map((d) => d.id === t ? { ...d, ...i } : d));
31
+ }, x = (t) => {
32
+ a(o.filter((i) => i.id !== t));
33
+ }, b = () => {
34
+ a([...o, { id: y(), key: "", expr: "{{ }}" }]);
35
+ };
36
+ return /* @__PURE__ */ c("div", { style: { width: "100%" }, children: [
37
+ o.length === 0 ? /* @__PURE__ */ r(
38
+ "div",
39
+ {
40
+ style: {
41
+ fontSize: 11,
42
+ color: "#999",
43
+ background: "#fafafa",
44
+ border: "1px dashed #e0e0e0",
45
+ borderRadius: 6,
46
+ padding: "6px 8px",
47
+ marginBottom: 4
48
+ },
49
+ children: "点击下方「添加校验规则」,组合 校验名称 + 表达式(变量 / 操作符 / 值)。表达式需返回 boolean,可用变量:$self.value、formData。"
50
+ }
51
+ ) : o.map((t, i) => /* @__PURE__ */ c(
52
+ "div",
53
+ {
54
+ style: {
55
+ border: "1px solid #e8e8e8",
56
+ borderRadius: 8,
57
+ padding: 8,
58
+ marginBottom: 8,
59
+ background: "#fff"
60
+ },
61
+ children: [
62
+ /* @__PURE__ */ c(
63
+ "div",
64
+ {
65
+ style: {
66
+ display: "flex",
67
+ alignItems: "center",
68
+ justifyContent: "space-between",
69
+ marginBottom: 6
70
+ },
71
+ children: [
72
+ /* @__PURE__ */ c("span", { style: { fontSize: 12, fontWeight: 600, color: "#333" }, children: [
73
+ "校验规则 ",
74
+ i + 1
75
+ ] }),
76
+ /* @__PURE__ */ r(u, { size: "small", danger: !0, onClick: () => x(t.id), children: "删除" })
77
+ ]
78
+ }
79
+ ),
80
+ /* @__PURE__ */ c("div", { style: { marginBottom: 6 }, children: [
81
+ /* @__PURE__ */ r("div", { style: { fontSize: 11, color: "#666", marginBottom: 2 }, children: "规则名称(key)" }),
82
+ /* @__PURE__ */ r(
83
+ k,
84
+ {
85
+ size: "small",
86
+ placeholder: "如 match / len",
87
+ value: t.key,
88
+ onChange: (d) => p(t.id, { key: d.target.value })
89
+ }
90
+ )
91
+ ] }),
92
+ /* @__PURE__ */ c("div", { children: [
93
+ /* @__PURE__ */ r("div", { style: { fontSize: 11, color: "#666", marginBottom: 2 }, children: "校验表达式(需返回 boolean)" }),
94
+ /* @__PURE__ */ r(
95
+ z,
96
+ {
97
+ value: t.expr,
98
+ onChange: (d) => p(t.id, { expr: d }),
99
+ fields: s,
100
+ showSelf: !0,
101
+ placeholder: "{{ $self.value.length >= 6 }}"
102
+ }
103
+ )
104
+ ] })
105
+ ]
106
+ },
107
+ t.id
108
+ )),
109
+ /* @__PURE__ */ r(u, { size: "small", type: "dashed", block: !0, onClick: b, children: "+ 添加校验规则" }),
110
+ /* @__PURE__ */ r("div", { style: { fontSize: 11, color: "#999", marginTop: 4 }, children: "提示:依赖字段(formData.xxx)变化时会自动联动重校验。" })
111
+ ] });
112
+ }
113
+ const V = j;
114
+ export {
115
+ j as ValidateEditor,
116
+ V as validateEditorWidget
117
+ };
118
+ //# sourceMappingURL=validate-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-editor.js","sources":["../../../src/widgets/validate-editor.tsx"],"sourcesContent":["// ============================================================================\n// ValidateEditor — 可视化编辑 validate 校验规则\n// 每行一条规则:「规则名称 + 校验表达式」(表达式需返回 boolean)\n// 与 schema 的 ValidateSchema({ key: Expression })互相转换\n// ============================================================================\n\nimport type { WidgetProps } from '@xbeeant/form-engine-ui';\nimport { Button, Input } from 'antd';\nimport { useEffect, useRef, useState } from 'react';\nimport { ExpressionBuilder } from './expression-builder';\nimport { genId } from './reactions-model';\nimport { useFormDataFields } from './use-form-data-fields';\n\ninterface ValidateRow {\n id: string;\n key: string;\n expr: string;\n}\n\n/** ValidateSchema 对象 → 规则行 */\nfunction validateToRows(value: unknown): ValidateRow[] {\n if (!value || typeof value !== 'object') {\n return [];\n }\n return Object.entries(value as Record<string, unknown>).map(\n ([key, expr]) => ({ id: genId(), key, expr: String(expr ?? '') }),\n );\n}\n\n/** 规则行 → ValidateSchema 对象(空表达式 / 空 key 的行丢弃) */\nfunction rowsToValidate(\n rows: ValidateRow[],\n): Record<string, string> | undefined {\n const result: Record<string, string> = {};\n for (const row of rows) {\n const key = row.key.trim();\n const expr = row.expr.trim();\n if (!key || !expr || expr === '{{ }}') {\n continue;\n }\n result[key] = expr;\n }\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\nexport function ValidateEditor({ value, onChange }: WidgetProps) {\n const fields = useFormDataFields();\n const [rows, setRows] = useState<ValidateRow[]>(() => validateToRows(value));\n const prevValueRef = useRef(value);\n // 自身 onChange 引发的 value 回传不应触发重建(否则每次输入都被重置)\n const lastEmittedRef = useRef(value);\n\n useEffect(() => {\n if (prevValueRef.current !== value) {\n prevValueRef.current = value;\n if (value !== lastEmittedRef.current) {\n setRows(validateToRows(value));\n }\n }\n }, [value]);\n\n const commit = (next: ValidateRow[]) => {\n setRows(next);\n const emitted = rowsToValidate(next);\n lastEmittedRef.current = emitted;\n onChange(emitted);\n };\n\n const updateRow = (id: string, patch: Partial<ValidateRow>) => {\n commit(rows.map((r) => (r.id === id ? { ...r, ...patch } : r)));\n };\n\n const removeRow = (id: string) => {\n commit(rows.filter((r) => r.id !== id));\n };\n\n const addRow = () => {\n commit([...rows, { id: genId(), key: '', expr: '{{ }}' }]);\n };\n\n return (\n <div style={{ width: '100%' }}>\n {rows.length === 0 ? (\n <div\n style={{\n fontSize: 11,\n color: '#999',\n background: '#fafafa',\n border: '1px dashed #e0e0e0',\n borderRadius: 6,\n padding: '6px 8px',\n marginBottom: 4,\n }}\n >\n 点击下方「添加校验规则」,组合 校验名称 + 表达式(变量 / 操作符 /\n 值)。表达式需返回 boolean,可用变量:$self.value、formData。\n </div>\n ) : (\n rows.map((row, i) => (\n <div\n key={row.id}\n style={{\n border: '1px solid #e8e8e8',\n borderRadius: 8,\n padding: 8,\n marginBottom: 8,\n background: '#fff',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: 6,\n }}\n >\n <span style={{ fontSize: 12, fontWeight: 600, color: '#333' }}>\n 校验规则 {i + 1}\n </span>\n <Button size='small' danger onClick={() => removeRow(row.id)}>\n 删除\n </Button>\n </div>\n <div style={{ marginBottom: 6 }}>\n <div style={{ fontSize: 11, color: '#666', marginBottom: 2 }}>\n 规则名称(key)\n </div>\n <Input\n size='small'\n placeholder='如 match / len'\n value={row.key}\n onChange={(e) => updateRow(row.id, { key: e.target.value })}\n />\n </div>\n <div>\n <div style={{ fontSize: 11, color: '#666', marginBottom: 2 }}>\n 校验表达式(需返回 boolean)\n </div>\n <ExpressionBuilder\n value={row.expr}\n onChange={(s) => updateRow(row.id, { expr: s })}\n fields={fields}\n showSelf\n placeholder='{{ $self.value.length >= 6 }}'\n />\n </div>\n </div>\n ))\n )}\n\n <Button size='small' type='dashed' block onClick={addRow}>\n + 添加校验规则\n </Button>\n <div style={{ fontSize: 11, color: '#999', marginTop: 4 }}>\n 提示:依赖字段(formData.xxx)变化时会自动联动重校验。\n </div>\n </div>\n );\n}\n\nexport const validateEditorWidget = ValidateEditor;\n"],"names":["validateToRows","value","key","expr","genId","rowsToValidate","rows","result","row","ValidateEditor","onChange","fields","useFormDataFields","setRows","useState","prevValueRef","useRef","lastEmittedRef","useEffect","commit","next","emitted","updateRow","id","patch","r","removeRow","addRow","jsx","jsxs","Button","Input","e","ExpressionBuilder","s","validateEditorWidget"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbeeant/form-engine-designer",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -19,7 +19,6 @@
19
19
  },
20
20
  "files": [
21
21
  "dist",
22
- "src",
23
22
  "README.md"
24
23
  ],
25
24
  "sideEffects": [
@@ -53,11 +52,11 @@
53
52
  }
54
53
  },
55
54
  "dependencies": {
56
- "@xbeeant/form-engine": "^0.0.2",
57
- "@xbeeant/form-engine-ui": "^0.0.2"
55
+ "@xbeeant/form-engine": "^0.0.4",
56
+ "@xbeeant/form-engine-ui": "^0.0.4"
58
57
  },
59
58
  "devDependencies": {
60
59
  "vitest": "^4.1.10"
61
60
  },
62
- "gitHead": "5e640d99e81bec24182061a9f431c39647e562dd"
61
+ "gitHead": "e21b7883779d4623ce1402a1af17b4d35addf4de"
63
62
  }