@xbeeant/form-engine-designer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/es/Canvas.js +42 -46
  2. package/dist/es/Canvas.js.map +1 -1
  3. package/dist/es/Designer.js +121 -98
  4. package/dist/es/Designer.js.map +1 -1
  5. package/dist/es/DesignerContext.js +86 -73
  6. package/dist/es/DesignerContext.js.map +1 -1
  7. package/dist/es/Palette.js +3 -3
  8. package/dist/es/Palette.js.map +1 -1
  9. package/dist/es/PropertyPanel.js +97 -97
  10. package/dist/es/PropertyPanel.js.map +1 -1
  11. package/dist/es/catalog.js +19 -11
  12. package/dist/es/catalog.js.map +1 -1
  13. package/dist/es/designer-context.js +110 -0
  14. package/dist/es/designer-context.js.map +1 -0
  15. package/dist/es/history.js +73 -0
  16. package/dist/es/history.js.map +1 -0
  17. package/dist/es/index.js +3 -3
  18. package/dist/es/property/basic-property.js +6 -0
  19. package/dist/es/property/basic-property.js.map +1 -1
  20. package/dist/es/property-panel.js +294 -0
  21. package/dist/es/property-panel.js.map +1 -0
  22. package/dist/es/resizable-panel.js +103 -0
  23. package/dist/es/resizable-panel.js.map +1 -0
  24. package/dist/es/schema-utils.js +286 -0
  25. package/dist/es/schema-utils.js.map +1 -0
  26. package/dist/es/widgets/BindEditor.js +60 -49
  27. package/dist/es/widgets/BindEditor.js.map +1 -1
  28. package/dist/es/widgets/CodeEditor.js +41 -49
  29. package/dist/es/widgets/CodeEditor.js.map +1 -1
  30. package/dist/es/widgets/DefaultEditor.js +25 -30
  31. package/dist/es/widgets/DefaultEditor.js.map +1 -1
  32. package/dist/es/widgets/DependenciesEditor.js +21 -21
  33. package/dist/es/widgets/DependenciesEditor.js.map +1 -1
  34. package/dist/es/widgets/EnumEditor.js +25 -31
  35. package/dist/es/widgets/EnumEditor.js.map +1 -1
  36. package/dist/es/widgets/ExpressionSwitch.js +32 -30
  37. package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
  38. package/dist/es/widgets/ReactionsEditor.js +103 -103
  39. package/dist/es/widgets/ReactionsEditor.js.map +1 -1
  40. package/dist/es/widgets/RemoteDataEditor.js +63 -63
  41. package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
  42. package/dist/es/widgets/ValidateEditor.js +49 -49
  43. package/dist/es/widgets/ValidateEditor.js.map +1 -1
  44. package/dist/es/widgets/bind-editor.js +78 -0
  45. package/dist/es/widgets/bind-editor.js.map +1 -0
  46. package/dist/es/widgets/code-editor.js +76 -0
  47. package/dist/es/widgets/code-editor.js.map +1 -0
  48. package/dist/es/widgets/default-editor.js +50 -0
  49. package/dist/es/widgets/default-editor.js.map +1 -0
  50. package/dist/es/widgets/dependencies-editor.js +36 -0
  51. package/dist/es/widgets/dependencies-editor.js.map +1 -0
  52. package/dist/es/widgets/enum-editor.js +43 -0
  53. package/dist/es/widgets/enum-editor.js.map +1 -0
  54. package/dist/es/widgets/expression-builder.js +367 -0
  55. package/dist/es/widgets/expression-builder.js.map +1 -0
  56. package/dist/es/widgets/expression-model.js +340 -0
  57. package/dist/es/widgets/expression-model.js.map +1 -0
  58. package/dist/es/widgets/expression-switch.js +49 -0
  59. package/dist/es/widgets/expression-switch.js.map +1 -0
  60. package/dist/es/widgets/index.js +13 -13
  61. package/dist/es/widgets/index.js.map +1 -1
  62. package/dist/es/widgets/options-editor.js +102 -0
  63. package/dist/es/widgets/options-editor.js.map +1 -0
  64. package/dist/es/widgets/reactions-editor.js +401 -0
  65. package/dist/es/widgets/reactions-editor.js.map +1 -0
  66. package/dist/es/widgets/reactions-model.js +107 -0
  67. package/dist/es/widgets/reactions-model.js.map +1 -0
  68. package/dist/es/widgets/remote-data-editor.js +314 -0
  69. package/dist/es/widgets/remote-data-editor.js.map +1 -0
  70. package/dist/es/widgets/use-form-data-fields.js +16 -0
  71. package/dist/es/widgets/use-form-data-fields.js.map +1 -0
  72. package/dist/es/widgets/validate-editor.js +118 -0
  73. package/dist/es/widgets/validate-editor.js.map +1 -0
  74. package/package.json +4 -5
  75. package/dist/es/react/src/components/FormController.js +0 -154
  76. package/dist/es/react/src/components/FormController.js.map +0 -1
  77. package/dist/es/react/src/components/NexusField.js +0 -99
  78. package/dist/es/react/src/components/NexusField.js.map +0 -1
  79. package/dist/es/react/src/components/NexusForm.js +0 -121
  80. package/dist/es/react/src/components/NexusForm.js.map +0 -1
  81. package/dist/es/react/src/components/NexusFormProvider.js +0 -19
  82. package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
  83. package/dist/es/react/src/components/NexusLayout.js +0 -49
  84. package/dist/es/react/src/components/NexusLayout.js.map +0 -1
  85. package/dist/es/react/src/components/NexusObject.js +0 -65
  86. package/dist/es/react/src/components/NexusObject.js.map +0 -1
  87. package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
  88. package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
  89. package/dist/es/react/src/contexts/GridContext.js +0 -6
  90. package/dist/es/react/src/contexts/GridContext.js.map +0 -1
  91. package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
  92. package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
  93. package/dist/es/react/src/contexts/NexusContext.js +0 -13
  94. package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
  95. package/dist/es/react/src/hooks/useForm.js +0 -10
  96. package/dist/es/react/src/hooks/useForm.js.map +0 -1
  97. package/dist/es/react/src/utils/renderTreeNode.js +0 -18
  98. package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
  99. package/dist/es/react/src/utils/resolveColSpan.js +0 -10
  100. package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
  101. package/dist/es/schemaUtils.test.js +0 -188
  102. package/dist/es/schemaUtils.test.js.map +0 -1
  103. package/dist/es/vite-env.d.js +0 -2
  104. package/dist/es/vite-env.d.js.map +0 -1
  105. package/dist/es/widgets/expressionModel.test.js +0 -193
  106. package/dist/es/widgets/expressionModel.test.js.map +0 -1
  107. package/src/Canvas.tsx +0 -1111
  108. package/src/Designer.css +0 -206
  109. package/src/Designer.tsx +0 -254
  110. package/src/DesignerContext.tsx +0 -192
  111. package/src/Palette.tsx +0 -261
  112. package/src/PropertyPanel.tsx +0 -486
  113. package/src/ResizablePanel.tsx +0 -210
  114. package/src/catalog.ts +0 -526
  115. package/src/index.ts +0 -25
  116. package/src/property/basic-property.ts +0 -126
  117. package/src/schemaUtils.test.ts +0 -261
  118. package/src/schemaUtils.ts +0 -627
  119. package/src/types.ts +0 -38
  120. package/src/vite-env.d.ts +0 -1
  121. package/src/widgets/BindEditor.tsx +0 -105
  122. package/src/widgets/CodeEditor.tsx +0 -114
  123. package/src/widgets/DefaultEditor.tsx +0 -73
  124. package/src/widgets/DependenciesEditor.tsx +0 -56
  125. package/src/widgets/EnumEditor.tsx +0 -66
  126. package/src/widgets/ExpressionBuilder.tsx +0 -509
  127. package/src/widgets/ExpressionSwitch.tsx +0 -62
  128. package/src/widgets/OptionsEditor.tsx +0 -141
  129. package/src/widgets/ReactionsEditor.tsx +0 -532
  130. package/src/widgets/RemoteDataEditor.tsx +0 -382
  131. package/src/widgets/ValidateEditor.tsx +0 -162
  132. package/src/widgets/expressionModel.test.ts +0 -262
  133. package/src/widgets/expressionModel.ts +0 -573
  134. package/src/widgets/index.ts +0 -45
  135. package/src/widgets/reactionsModel.ts +0 -190
  136. package/src/widgets/useFormDataFields.ts +0 -21
  137. /package/dist/es/{form-engine-designer.css → index.css} +0 -0
@@ -1,573 +0,0 @@
1
- // ============================================================================
2
- // expressionModel — 可视化表达式构建器的纯数据模型(无 React / DOM 依赖)
3
- // 负责 ExprCondition 行模型 ⇄ `{{ ... }}` 表达式字符串的双向转换
4
- // ============================================================================
5
- //
6
- // 构建:行模型 → 表达式(buildExpression)
7
- // 解析:表达式 → 行模型(parseExpression,尽力而为)
8
- // 解析失败时返回 null,由调用方回退到「高级模式」原始表达式编辑,保证不丢数据。
9
-
10
- // ────────────────────────────────────────────────────────────────────────────
11
- // 类型定义
12
- // ────────────────────────────────────────────────────────────────────────────
13
-
14
- /** 变量来源 */
15
- export type VariableSource =
16
- | 'formData'
17
- | 'self'
18
- | 'deps'
19
- | 'index'
20
- | 'root'
21
- | 'raw';
22
-
23
- export interface ExprVariable {
24
- source: VariableSource;
25
- /** formData: 点分字段路径;deps: 下标字符串;raw: 自定义表达式片段 */
26
- path: string;
27
- }
28
-
29
- /** 右侧值模式 */
30
- export type RightMode = 'string' | 'number' | 'boolean' | 'variable';
31
-
32
- /** 操作符 */
33
- export type ConditionOperator =
34
- | 'eq'
35
- | 'neq'
36
- | 'gt'
37
- | 'gte'
38
- | 'lt'
39
- | 'lte'
40
- | 'contains'
41
- | 'notContains'
42
- | 'startsWith'
43
- | 'endsWith'
44
- | 'empty'
45
- | 'notEmpty'
46
- | 'truthy'
47
- | 'falsy';
48
-
49
- export interface ExprCondition {
50
- id: string;
51
- /** 与上一条条件的连接关系(首条忽略) */
52
- logic: 'and' | 'or';
53
- variable: ExprVariable;
54
- operator: ConditionOperator;
55
- rightMode: RightMode;
56
- /** 右侧值:string/number 为字面量文本,boolean 为 'true'/'false',variable 为变量表达式 */
57
- right: string;
58
- }
59
-
60
- /** 操作符文案选项 */
61
- export const OPERATOR_OPTIONS: Array<{
62
- label: string;
63
- value: ConditionOperator;
64
- }> = [
65
- { label: '等于', value: 'eq' },
66
- { label: '不等于', value: 'neq' },
67
- { label: '大于', value: 'gt' },
68
- { label: '大于等于', value: 'gte' },
69
- { label: '小于', value: 'lt' },
70
- { label: '小于等于', value: 'lte' },
71
- { label: '包含', value: 'contains' },
72
- { label: '不包含', value: 'notContains' },
73
- { label: '以…开头', value: 'startsWith' },
74
- { label: '以…结尾', value: 'endsWith' },
75
- { label: '为空', value: 'empty' },
76
- { label: '不为空', value: 'notEmpty' },
77
- { label: '为真', value: 'truthy' },
78
- { label: '为假', value: 'falsy' },
79
- ];
80
-
81
- /** 无需右侧值的操作符 */
82
- export const NO_RIGHT_OPERATORS = new Set<ConditionOperator>([
83
- 'empty',
84
- 'notEmpty',
85
- 'truthy',
86
- 'falsy',
87
- ]);
88
-
89
- export const RIGHT_MODE_OPTIONS: Array<{ label: string; value: RightMode }> = [
90
- { label: '文本', value: 'string' },
91
- { label: '数字', value: 'number' },
92
- { label: '布尔', value: 'boolean' },
93
- { label: '变量', value: 'variable' },
94
- ];
95
-
96
- export function genConditionId(): string {
97
- return Math.random().toString(36).slice(2, 8);
98
- }
99
-
100
- /** 创建一个空条件行 */
101
- export function createCondition(): ExprCondition {
102
- return {
103
- id: genConditionId(),
104
- logic: 'and',
105
- variable: { source: 'formData', path: '' },
106
- operator: 'eq',
107
- rightMode: 'string',
108
- right: '',
109
- };
110
- }
111
-
112
- // ────────────────────────────────────────────────────────────────────────────
113
- // 变量文本化 / 解析
114
- // ────────────────────────────────────────────────────────────────────────────
115
-
116
- const IDENT_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
117
-
118
- /** 点分路径 → 安全访问器(非标识符片段用 ['...'] 包裹) */
119
- function toAccessor(path: string): string {
120
- if (!path) {
121
- return '';
122
- }
123
- return path
124
- .split('.')
125
- .map((seg) => (IDENT_RE.test(seg) ? `.${seg}` : `['${seg}']`))
126
- .join('');
127
- }
128
-
129
- /** ExprVariable → 表达式文本 */
130
- export function variableToString(v: ExprVariable): string {
131
- switch (v.source) {
132
- case 'formData':
133
- return `formData${toAccessor(v.path)}`;
134
- case 'self':
135
- return '$self.value';
136
- case 'deps':
137
- return `$deps[${v.path}]`;
138
- case 'index':
139
- return '$index';
140
- case 'root':
141
- return 'rootValue';
142
- case 'raw':
143
- return v.path;
144
- }
145
- }
146
-
147
- /** 从访问器后缀解析出路径段('.a' / "['b']" 混合形式) */
148
- function parseAccessorSuffix(suffix: string): string {
149
- const segments: string[] = [];
150
- const re = /\.([a-zA-Z_$][a-zA-Z0-9_$]*)|\[\s*'([^']*)'\s*\]/g;
151
- let m = re.exec(suffix);
152
- while (m !== null) {
153
- segments.push(m[1] ?? m[2]);
154
- m = re.exec(suffix);
155
- }
156
- return segments.join('.');
157
- }
158
-
159
- /** 表达式文本 → ExprVariable;无法识别时回退为 raw */
160
- export function parseVariable(text: string): ExprVariable {
161
- const t = text.trim();
162
- if (t === '$self.value' || t === '$self') {
163
- return { source: 'self', path: '' };
164
- }
165
- const depsMatch = /^\$deps\[\s*(\d+)\s*\]$/.exec(t);
166
- if (depsMatch) {
167
- return { source: 'deps', path: depsMatch[1] };
168
- }
169
- if (t === '$index') {
170
- return { source: 'index', path: '' };
171
- }
172
- if (t === 'rootValue') {
173
- return { source: 'root', path: '' };
174
- }
175
- if (t.startsWith('formData')) {
176
- return {
177
- source: 'formData',
178
- path: parseAccessorSuffix(t.slice('formData'.length)),
179
- };
180
- }
181
- return { source: 'raw', path: t };
182
- }
183
-
184
- /**
185
- * 严格解析「简单变量」(仅白名单变量源,不含任意表达式片段)
186
- * 用于解析已有表达式时约束左右操作数,无法识别返回 null(→ 回退高级模式)
187
- */
188
- export function parseSimpleVariable(text: string): ExprVariable | null {
189
- const t = text.trim();
190
- if (t === '$self.value') {
191
- return { source: 'self', path: '' };
192
- }
193
- const depsMatch = /^\$deps\[\s*(\d+)\s*\]$/.exec(t);
194
- if (depsMatch) {
195
- return { source: 'deps', path: depsMatch[1] };
196
- }
197
- if (t === '$index') {
198
- return { source: 'index', path: '' };
199
- }
200
- if (t === 'rootValue') {
201
- return { source: 'root', path: '' };
202
- }
203
- if (t.startsWith('formData') && t !== 'formData') {
204
- const v = parseVariable(t);
205
- if (v.source === 'formData' && v.path) {
206
- return v;
207
- }
208
- }
209
- return null;
210
- }
211
-
212
- // ────────────────────────────────────────────────────────────────────────────
213
- // 右侧值格式化
214
- // ────────────────────────────────────────────────────────────────────────────
215
-
216
- function escapeString(s: string): string {
217
- return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
218
- }
219
-
220
- function unescapeString(s: string): string {
221
- return s.replace(/\\(['\\])/g, '$1');
222
- }
223
-
224
- /** 根据 rightMode 生成右侧 JS 值文本 */
225
- export function formatRightValue(mode: RightMode, raw: string): string {
226
- switch (mode) {
227
- case 'string':
228
- return `'${escapeString(raw)}'`;
229
- case 'number': {
230
- const t = raw.trim();
231
- return /^-?\d+(\.\d+)?$/.test(t) ? t : '0';
232
- }
233
- case 'boolean':
234
- return raw === 'true' ? 'true' : 'false';
235
- case 'variable':
236
- return raw.trim();
237
- }
238
- }
239
-
240
- /** 解析右侧 JS 值 → { mode, raw } */
241
- export function parseRightValue(text: string): {
242
- mode: RightMode;
243
- raw: string;
244
- } {
245
- const t = text.trim();
246
- if (t.length >= 2 && t[0] === "'" && t[t.length - 1] === "'") {
247
- return { mode: 'string', raw: unescapeString(t.slice(1, -1)) };
248
- }
249
- if (t.length >= 2 && t[0] === '"' && t[t.length - 1] === '"') {
250
- return { mode: 'string', raw: unescapeString(t.slice(1, -1)) };
251
- }
252
- if (/^-?\d+(\.\d+)?$/.test(t)) {
253
- return { mode: 'number', raw: t };
254
- }
255
- if (t === 'true' || t === 'false') {
256
- return { mode: 'boolean', raw: t };
257
- }
258
- return { mode: 'variable', raw: t };
259
- }
260
-
261
- // ────────────────────────────────────────────────────────────────────────────
262
- // 构建表达式
263
- // ────────────────────────────────────────────────────────────────────────────
264
-
265
- const JS_OP: Record<string, string> = {
266
- eq: '===',
267
- neq: '!==',
268
- gt: '>',
269
- gte: '>=',
270
- lt: '<',
271
- lte: '<=',
272
- };
273
-
274
- const LOGIC_JS: Record<'and' | 'or', string> = { and: '&&', or: '||' };
275
-
276
- function conditionToExpression(c: ExprCondition): string {
277
- const left = variableToString(c.variable);
278
- const op = c.operator;
279
- if (op === 'truthy') {
280
- return `!!(${left})`;
281
- }
282
- if (op === 'falsy') {
283
- return `!(${left})`;
284
- }
285
- if (op === 'empty') {
286
- return `(${left} == null || String(${left}).trim() === '')`;
287
- }
288
- if (op === 'notEmpty') {
289
- return `(${left} != null && String(${left}).trim() !== '')`;
290
- }
291
- const right = formatRightValue(c.rightMode, c.right);
292
- switch (op) {
293
- case 'contains':
294
- return `String(${left}).includes(${right})`;
295
- case 'notContains':
296
- return `!String(${left}).includes(${right})`;
297
- case 'startsWith':
298
- return `String(${left}).startsWith(${right})`;
299
- case 'endsWith':
300
- return `String(${left}).endsWith(${right})`;
301
- default:
302
- return `${left} ${JS_OP[op] ?? '==='} ${right}`;
303
- }
304
- }
305
-
306
- /** 条件行是否完整(变量已填,需要右值时右值已填) */
307
- export function isConditionComplete(c: ExprCondition): boolean {
308
- if (c.variable.source === 'raw') {
309
- if (!c.variable.path.trim()) {
310
- return false;
311
- }
312
- } else if (c.variable.source === 'formData' && !c.variable.path.trim()) {
313
- return false;
314
- }
315
- if (NO_RIGHT_OPERATORS.has(c.operator)) {
316
- return true;
317
- }
318
- if (c.rightMode === 'variable') {
319
- return c.right.trim().length > 0;
320
- }
321
- return c.rightMode === 'boolean' || c.right.trim().length > 0;
322
- }
323
-
324
- /**
325
- * 行模型 → `{{ ... }}` 表达式
326
- * - 无完整条件时返回空模板 `{{ }}`(保持表达式模式)
327
- */
328
- export function buildExpression(conditions: ExprCondition[]): string {
329
- const valid = conditions.filter(isConditionComplete);
330
- if (valid.length === 0) {
331
- return '{{ }}';
332
- }
333
- let expr = conditionToExpression(valid[0]);
334
- for (let i = 1; i < valid.length; i++) {
335
- expr = `${expr} ${LOGIC_JS[valid[i].logic]} ${conditionToExpression(valid[i])}`;
336
- }
337
- return `{{ ${expr} }}`;
338
- }
339
-
340
- // ────────────────────────────────────────────────────────────────────────────
341
- // 解析表达式
342
- // ────────────────────────────────────────────────────────────────────────────
343
-
344
- /** 去掉 `{{ }}` 包裹 */
345
- export function stripBraces(expr: string): string {
346
- const t = expr.trim();
347
- if (t.startsWith('{{') && t.endsWith('}}')) {
348
- return t.slice(2, -2).trim();
349
- }
350
- return t;
351
- }
352
-
353
- /** 按顶层 `&&` / `||` 切分(忽略引号与括号内的连接符) */
354
- function splitTopLevel(
355
- text: string,
356
- ): Array<{ logic: 'and' | 'or'; text: string }> {
357
- const parts: Array<{ logic: 'and' | 'or'; text: string }> = [];
358
- let depth = 0;
359
- let quote: "'" | '"' | null = null;
360
- let buffer = '';
361
- let nextLogic: 'and' | 'or' = 'and';
362
-
363
- for (let i = 0; i < text.length; i++) {
364
- const ch = text[i];
365
- if (quote) {
366
- buffer += ch;
367
- if (ch === '\\') {
368
- buffer += text[i + 1] ?? '';
369
- i++;
370
- } else if (ch === quote) {
371
- quote = null;
372
- }
373
- continue;
374
- }
375
- if (ch === "'" || ch === '"') {
376
- quote = ch;
377
- buffer += ch;
378
- continue;
379
- }
380
- if (ch === '(') {
381
- depth++;
382
- buffer += ch;
383
- continue;
384
- }
385
- if (ch === ')') {
386
- depth = Math.max(0, depth - 1);
387
- buffer += ch;
388
- continue;
389
- }
390
- if (depth === 0 && (ch === '&' || ch === '|')) {
391
- const op = text.slice(i, i + 2);
392
- if (op === '&&' || op === '||') {
393
- const trimmed = buffer.trim();
394
- if (trimmed) {
395
- parts.push({ logic: nextLogic, text: trimmed });
396
- }
397
- nextLogic = op === '&&' ? 'and' : 'or';
398
- buffer = '';
399
- i++;
400
- continue;
401
- }
402
- }
403
- buffer += ch;
404
- }
405
- const trimmed = buffer.trim();
406
- if (trimmed) {
407
- parts.push({ logic: nextLogic, text: trimmed });
408
- }
409
- return parts;
410
- }
411
-
412
- function parseAtomic(text: string): ExprCondition | null {
413
- const t = text.trim();
414
-
415
- const truthy = /^!!\(\s*(.+)\s*\)$/.exec(t);
416
- if (truthy) {
417
- const variable = parseSimpleVariable(truthy[1]);
418
- if (!variable) {
419
- return null;
420
- }
421
- return {
422
- id: genConditionId(),
423
- logic: 'and',
424
- variable,
425
- operator: 'truthy',
426
- rightMode: 'string',
427
- right: '',
428
- };
429
- }
430
- const falsy = /^!\(\s*(.+)\s*\)$/.exec(t);
431
- if (falsy) {
432
- const variable = parseSimpleVariable(falsy[1]);
433
- if (!variable) {
434
- return null;
435
- }
436
- return {
437
- id: genConditionId(),
438
- logic: 'and',
439
- variable,
440
- operator: 'falsy',
441
- rightMode: 'string',
442
- right: '',
443
- };
444
- }
445
-
446
- const empty =
447
- /^\(\s*(.+?)\s*== null \|\| String\(\s*(.+?)\s*\)\.trim\(\) === ''\)$/.exec(
448
- t,
449
- );
450
- if (empty) {
451
- const variable = parseSimpleVariable(empty[1]);
452
- if (!variable) {
453
- return null;
454
- }
455
- return {
456
- id: genConditionId(),
457
- logic: 'and',
458
- variable,
459
- operator: 'empty',
460
- rightMode: 'string',
461
- right: '',
462
- };
463
- }
464
- const notEmpty =
465
- /^\(\s*(.+?)\s*!= null && String\(\s*(.+?)\s*\)\.trim\(\) !== ''\)$/.exec(
466
- t,
467
- );
468
- if (notEmpty) {
469
- const variable = parseSimpleVariable(notEmpty[1]);
470
- if (!variable) {
471
- return null;
472
- }
473
- return {
474
- id: genConditionId(),
475
- logic: 'and',
476
- variable,
477
- operator: 'notEmpty',
478
- rightMode: 'string',
479
- right: '',
480
- };
481
- }
482
-
483
- const stringCall =
484
- /^(!?)String\(\s*(.+?)\s*\)\.(includes|startsWith|endsWith)\(\s*(.+?)\s*\)$/.exec(
485
- t,
486
- );
487
- if (stringCall) {
488
- const variable = parseSimpleVariable(stringCall[2]);
489
- if (!variable) {
490
- return null;
491
- }
492
- const negated = stringCall[1] === '!';
493
- const rawOp = stringCall[3] as 'includes' | 'startsWith' | 'endsWith';
494
- const opMap: Record<string, ConditionOperator> = {
495
- includes: negated ? 'notContains' : 'contains',
496
- startsWith: 'startsWith',
497
- endsWith: 'endsWith',
498
- };
499
- const { mode, raw } = parseRightValue(stringCall[4]);
500
- if (mode === 'variable' && !parseSimpleVariable(raw)) {
501
- return null;
502
- }
503
- return {
504
- id: genConditionId(),
505
- logic: 'and',
506
- variable,
507
- operator: opMap[rawOp],
508
- rightMode: mode,
509
- right: raw,
510
- };
511
- }
512
-
513
- const cmp = /^\s*(.+?)\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+?)\s*$/.exec(t);
514
- if (cmp) {
515
- const variable = parseSimpleVariable(cmp[1]);
516
- if (!variable) {
517
- return null;
518
- }
519
- const opMap: Record<string, ConditionOperator> = {
520
- '===': 'eq',
521
- '==': 'eq',
522
- '!==': 'neq',
523
- '!=': 'neq',
524
- '>': 'gt',
525
- '>=': 'gte',
526
- '<': 'lt',
527
- '<=': 'lte',
528
- };
529
- const { mode, raw } = parseRightValue(cmp[3]);
530
- if (mode === 'variable' && !parseSimpleVariable(raw)) {
531
- return null;
532
- }
533
- return {
534
- id: genConditionId(),
535
- logic: 'and',
536
- variable,
537
- operator: opMap[cmp[2]],
538
- rightMode: mode,
539
- right: raw,
540
- };
541
- }
542
-
543
- return null;
544
- }
545
-
546
- /**
547
- * 表达式字符串 → 行模型
548
- * - 空 / 空模板 → []
549
- * - 无法解析 → null(调用方回退「高级模式」)
550
- */
551
- export function parseExpression(expr: unknown): ExprCondition[] | null {
552
- if (typeof expr !== 'string') {
553
- return [];
554
- }
555
- const inner = stripBraces(expr);
556
- if (!inner) {
557
- return [];
558
- }
559
- const parts = splitTopLevel(inner);
560
- if (parts.length === 0) {
561
- return [];
562
- }
563
- const conditions: ExprCondition[] = [];
564
- for (const part of parts) {
565
- const parsed = parseAtomic(part.text);
566
- if (!parsed) {
567
- return null;
568
- }
569
- parsed.logic = part.logic;
570
- conditions.push(parsed);
571
- }
572
- return conditions;
573
- }
@@ -1,45 +0,0 @@
1
- // ============================================================================
2
- // @xbeeant/form-engine-designer — 属性面板专用 Widget 注册
3
- // ============================================================================
4
-
5
- import type { ReactNode } from 'react';
6
- import { bindEditorWidget } from './BindEditor';
7
- import { codeEditorWidget } from './CodeEditor';
8
- import { defaultEditorWidget } from './DefaultEditor';
9
- import { dependenciesEditorWidget } from './DependenciesEditor';
10
- import { enumEditorWidget, enumNamesEditorWidget } from './EnumEditor';
11
- import { expressionSwitchWidget } from './ExpressionSwitch';
12
- import { optionsEditorWidget } from './OptionsEditor';
13
- import { reactionsEditorWidget } from './ReactionsEditor';
14
- import { remoteDataEditorWidget } from './RemoteDataEditor';
15
- import { validateEditorWidget } from './ValidateEditor';
16
-
17
- /** 属性面板专用的 widget 集合,注册到属性表单的引擎实例中 */
18
- export const propertyWidgets: Record<string, (props: any) => ReactNode> = {
19
- propertyExpr: expressionSwitchWidget,
20
- propertyBind: bindEditorWidget,
21
- propertyValidate: validateEditorWidget,
22
- propertyDependencies: dependenciesEditorWidget,
23
- propertyReactions: reactionsEditorWidget,
24
- propertyEnum: enumEditorWidget,
25
- propertyEnumNames: enumNamesEditorWidget,
26
- propertyOptions: optionsEditorWidget,
27
- propertyRemoteData: remoteDataEditorWidget,
28
- propertyCodeEditor: codeEditorWidget,
29
- propertyDefault: defaultEditorWidget,
30
- };
31
-
32
- export { bindEditorWidget } from './BindEditor';
33
- export { codeEditorWidget } from './CodeEditor';
34
- export { defaultEditorWidget } from './DefaultEditor';
35
- export { dependenciesEditorWidget } from './DependenciesEditor';
36
- export { enumEditorWidget, enumNamesEditorWidget } from './EnumEditor';
37
- export type { ExpressionBuilderProps } from './ExpressionBuilder';
38
- export { ExpressionBuilder } from './ExpressionBuilder';
39
- export { expressionSwitchWidget } from './ExpressionSwitch';
40
- export * from './expressionModel';
41
- export { optionsEditorWidget } from './OptionsEditor';
42
- export { reactionsEditorWidget } from './ReactionsEditor';
43
- export { remoteDataEditorWidget } from './RemoteDataEditor';
44
- export { useFormDataFields } from './useFormDataFields';
45
- export { validateEditorWidget } from './ValidateEditor';