@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.
- package/dist/es/Canvas.js +42 -46
- package/dist/es/Canvas.js.map +1 -1
- package/dist/es/Designer.js +121 -98
- package/dist/es/Designer.js.map +1 -1
- package/dist/es/DesignerContext.js +86 -73
- package/dist/es/DesignerContext.js.map +1 -1
- package/dist/es/Palette.js +3 -3
- package/dist/es/Palette.js.map +1 -1
- package/dist/es/PropertyPanel.js +97 -97
- package/dist/es/PropertyPanel.js.map +1 -1
- package/dist/es/catalog.js +19 -11
- package/dist/es/catalog.js.map +1 -1
- package/dist/es/designer-context.js +110 -0
- package/dist/es/designer-context.js.map +1 -0
- package/dist/es/history.js +73 -0
- package/dist/es/history.js.map +1 -0
- package/dist/es/index.js +3 -3
- package/dist/es/property/basic-property.js +6 -0
- package/dist/es/property/basic-property.js.map +1 -1
- package/dist/es/property-panel.js +294 -0
- package/dist/es/property-panel.js.map +1 -0
- package/dist/es/resizable-panel.js +103 -0
- package/dist/es/resizable-panel.js.map +1 -0
- package/dist/es/schema-utils.js +286 -0
- package/dist/es/schema-utils.js.map +1 -0
- package/dist/es/widgets/BindEditor.js +60 -49
- package/dist/es/widgets/BindEditor.js.map +1 -1
- package/dist/es/widgets/CodeEditor.js +41 -49
- package/dist/es/widgets/CodeEditor.js.map +1 -1
- package/dist/es/widgets/DefaultEditor.js +25 -30
- package/dist/es/widgets/DefaultEditor.js.map +1 -1
- package/dist/es/widgets/DependenciesEditor.js +21 -21
- package/dist/es/widgets/DependenciesEditor.js.map +1 -1
- package/dist/es/widgets/EnumEditor.js +25 -31
- package/dist/es/widgets/EnumEditor.js.map +1 -1
- package/dist/es/widgets/ExpressionSwitch.js +32 -30
- package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
- package/dist/es/widgets/ReactionsEditor.js +103 -103
- package/dist/es/widgets/ReactionsEditor.js.map +1 -1
- package/dist/es/widgets/RemoteDataEditor.js +63 -63
- package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
- package/dist/es/widgets/ValidateEditor.js +49 -49
- package/dist/es/widgets/ValidateEditor.js.map +1 -1
- package/dist/es/widgets/bind-editor.js +78 -0
- package/dist/es/widgets/bind-editor.js.map +1 -0
- package/dist/es/widgets/code-editor.js +76 -0
- package/dist/es/widgets/code-editor.js.map +1 -0
- package/dist/es/widgets/default-editor.js +50 -0
- package/dist/es/widgets/default-editor.js.map +1 -0
- package/dist/es/widgets/dependencies-editor.js +36 -0
- package/dist/es/widgets/dependencies-editor.js.map +1 -0
- package/dist/es/widgets/enum-editor.js +43 -0
- package/dist/es/widgets/enum-editor.js.map +1 -0
- package/dist/es/widgets/expression-builder.js +367 -0
- package/dist/es/widgets/expression-builder.js.map +1 -0
- package/dist/es/widgets/expression-model.js +340 -0
- package/dist/es/widgets/expression-model.js.map +1 -0
- package/dist/es/widgets/expression-switch.js +49 -0
- package/dist/es/widgets/expression-switch.js.map +1 -0
- package/dist/es/widgets/index.js +13 -13
- package/dist/es/widgets/index.js.map +1 -1
- package/dist/es/widgets/options-editor.js +102 -0
- package/dist/es/widgets/options-editor.js.map +1 -0
- package/dist/es/widgets/reactions-editor.js +401 -0
- package/dist/es/widgets/reactions-editor.js.map +1 -0
- package/dist/es/widgets/reactions-model.js +107 -0
- package/dist/es/widgets/reactions-model.js.map +1 -0
- package/dist/es/widgets/remote-data-editor.js +314 -0
- package/dist/es/widgets/remote-data-editor.js.map +1 -0
- package/dist/es/widgets/use-form-data-fields.js +16 -0
- package/dist/es/widgets/use-form-data-fields.js.map +1 -0
- package/dist/es/widgets/validate-editor.js +118 -0
- package/dist/es/widgets/validate-editor.js.map +1 -0
- package/package.json +4 -5
- package/dist/es/react/src/components/FormController.js +0 -154
- package/dist/es/react/src/components/FormController.js.map +0 -1
- package/dist/es/react/src/components/NexusField.js +0 -99
- package/dist/es/react/src/components/NexusField.js.map +0 -1
- package/dist/es/react/src/components/NexusForm.js +0 -121
- package/dist/es/react/src/components/NexusForm.js.map +0 -1
- package/dist/es/react/src/components/NexusFormProvider.js +0 -19
- package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
- package/dist/es/react/src/components/NexusLayout.js +0 -49
- package/dist/es/react/src/components/NexusLayout.js.map +0 -1
- package/dist/es/react/src/components/NexusObject.js +0 -65
- package/dist/es/react/src/components/NexusObject.js.map +0 -1
- package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
- package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
- package/dist/es/react/src/contexts/GridContext.js +0 -6
- package/dist/es/react/src/contexts/GridContext.js.map +0 -1
- package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
- package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
- package/dist/es/react/src/contexts/NexusContext.js +0 -13
- package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
- package/dist/es/react/src/hooks/useForm.js +0 -10
- package/dist/es/react/src/hooks/useForm.js.map +0 -1
- package/dist/es/react/src/utils/renderTreeNode.js +0 -18
- package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
- package/dist/es/react/src/utils/resolveColSpan.js +0 -10
- package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
- package/dist/es/schemaUtils.test.js +0 -188
- package/dist/es/schemaUtils.test.js.map +0 -1
- package/dist/es/vite-env.d.js +0 -2
- package/dist/es/vite-env.d.js.map +0 -1
- package/dist/es/widgets/expressionModel.test.js +0 -193
- package/dist/es/widgets/expressionModel.test.js.map +0 -1
- package/src/Canvas.tsx +0 -1111
- package/src/Designer.css +0 -206
- package/src/Designer.tsx +0 -254
- package/src/DesignerContext.tsx +0 -192
- package/src/Palette.tsx +0 -261
- package/src/PropertyPanel.tsx +0 -486
- package/src/ResizablePanel.tsx +0 -210
- package/src/catalog.ts +0 -526
- package/src/index.ts +0 -25
- package/src/property/basic-property.ts +0 -126
- package/src/schemaUtils.test.ts +0 -261
- package/src/schemaUtils.ts +0 -627
- package/src/types.ts +0 -38
- package/src/vite-env.d.ts +0 -1
- package/src/widgets/BindEditor.tsx +0 -105
- package/src/widgets/CodeEditor.tsx +0 -114
- package/src/widgets/DefaultEditor.tsx +0 -73
- package/src/widgets/DependenciesEditor.tsx +0 -56
- package/src/widgets/EnumEditor.tsx +0 -66
- package/src/widgets/ExpressionBuilder.tsx +0 -509
- package/src/widgets/ExpressionSwitch.tsx +0 -62
- package/src/widgets/OptionsEditor.tsx +0 -141
- package/src/widgets/ReactionsEditor.tsx +0 -532
- package/src/widgets/RemoteDataEditor.tsx +0 -382
- package/src/widgets/ValidateEditor.tsx +0 -162
- package/src/widgets/expressionModel.test.ts +0 -262
- package/src/widgets/expressionModel.ts +0 -573
- package/src/widgets/index.ts +0 -45
- package/src/widgets/reactionsModel.ts +0 -190
- package/src/widgets/useFormDataFields.ts +0 -21
- /package/dist/es/{form-engine-designer.css → index.css} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GridContext.js","sources":["../../../../../../react/src/contexts/GridContext.ts"],"sourcesContent":["import { createContext } from 'react';\n\n// 子项未显式设置 colSpan 时使用此默认值\nexport interface GridContextValue {\n /** 当前 grid 的列数(tailwind colSpan 基准) */\n column: number;\n}\nexport const GridContext = createContext<GridContextValue | null>(null);\n"],"names":["GridContext","createContext"],"mappings":";AAOO,MAAMA,IAAcC,EAAuC,IAAI;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LayoutConfigContext.js","sources":["../../../../../../react/src/contexts/LayoutConfigContext.ts"],"sourcesContent":["import { createContext } from 'react';\n\nexport interface LayoutConfigContextValue {\n removeHidden?: boolean;\n}\nexport const LayoutConfigContext = createContext<LayoutConfigContextValue>({});\n"],"names":["LayoutConfigContext","createContext"],"mappings":";AAKO,MAAMA,IAAsBC,EAAwC,CAAA,CAAE;"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { useContext as t, createContext as o } from "react";
|
|
2
|
-
const r = o(null);
|
|
3
|
-
function u() {
|
|
4
|
-
const e = t(r);
|
|
5
|
-
if (!e)
|
|
6
|
-
throw new Error("[NexusField] Must be used within <NexusFormProvider>");
|
|
7
|
-
return e;
|
|
8
|
-
}
|
|
9
|
-
export {
|
|
10
|
-
r as NexusContext,
|
|
11
|
-
u as useNexusContext
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=NexusContext.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusContext.js","sources":["../../../../../../react/src/contexts/NexusContext.ts"],"sourcesContent":["import type { NexusEngine } from '@xbeeant/form-engine';\nimport { createContext, type ReactNode, useContext } from 'react';\n\nimport type { FormController } from '../components/FormController';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Form 布局配置\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface NexusFormConfig {\n /** label 列配置(由 ui 层 Form.Item 消费) */\n labelCol?: Record<string, unknown>;\n /** label 宽度(px 或 %),快捷方式 — 映射到 labelCol.style.width */\n labelWidth?: number | string;\n /** 是否显示冒号 */\n colon?: boolean | ReactNode;\n /** 是否显示 label(默认 true) */\n label?: boolean;\n /** 表单布局方向 */\n displayType?: 'row' | 'column' | 'inline';\n /** 整个表单只读,所有字段以文本展示 */\n readOnly?: boolean;\n /** 表单每行显示多少列 */\n column?: number;\n}\n\ninterface NexusContextValue {\n engine: NexusEngine;\n config: NexusFormConfig;\n form: FormController;\n}\n\nexport const NexusContext = createContext<NexusContextValue | null>(null);\n\nexport function useNexusContext(): NexusContextValue {\n const ctx = useContext(NexusContext);\n if (!ctx) {\n throw new Error('[NexusField] Must be used within <NexusFormProvider>');\n }\n return ctx;\n}\n"],"names":["NexusContext","createContext","useNexusContext","ctx","useContext"],"mappings":";AAgCO,MAAMA,IAAeC,EAAwC,IAAI;AAEjE,SAASC,IAAqC;AACnD,QAAMC,IAAMC,EAAWJ,CAAY;AACnC,MAAI,CAACG;AACH,UAAM,IAAI,MAAM,sDAAsD;AAExE,SAAOA;AACT;"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { useRef as e } from "react";
|
|
2
|
-
import { FormController as n } from "../components/FormController.js";
|
|
3
|
-
function f(o) {
|
|
4
|
-
const r = e(null);
|
|
5
|
-
return r.current || (r.current = new n(o)), [r.current];
|
|
6
|
-
}
|
|
7
|
-
export {
|
|
8
|
-
f as useForm
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=useForm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useForm.js","sources":["../../../../../../react/src/hooks/useForm.ts"],"sourcesContent":["import type { NexusEngine } from '@xbeeant/form-engine';\nimport { useRef } from 'react';\n\nimport { FormController } from '../components/FormController';\n\n/**\n * useForm — 创建 Form 实例\n */\nexport function useForm(engine?: NexusEngine): [FormController] {\n const formRef = useRef<FormController | null>(null);\n if (!formRef.current) {\n formRef.current = new FormController(engine);\n }\n return [formRef.current];\n}\n"],"names":["useForm","engine","formRef","useRef","FormController"],"mappings":";;AAQO,SAASA,EAAQC,GAAwC;AAC9D,QAAMC,IAAUC,EAA8B,IAAI;AAClD,SAAKD,EAAQ,YACXA,EAAQ,UAAU,IAAIE,EAAeH,CAAM,IAEtC,CAACC,EAAQ,OAAO;AACzB;"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { jsx as r } from "react/jsx-runtime";
|
|
2
|
-
import { NexusField as e } from "../components/NexusField.js";
|
|
3
|
-
import { NexusLayout as u } from "../components/NexusLayout.js";
|
|
4
|
-
import { NexusObject as y } from "../components/NexusObject.js";
|
|
5
|
-
function p(t, a) {
|
|
6
|
-
return t.type === "field" ? /* @__PURE__ */ r(
|
|
7
|
-
e,
|
|
8
|
-
{
|
|
9
|
-
dataPath: t.dataPath,
|
|
10
|
-
layoutKey: t.layoutKey
|
|
11
|
-
},
|
|
12
|
-
t.layoutKey || t.dataPath
|
|
13
|
-
) : t.type === "object" ? /* @__PURE__ */ r(y, { node: t }, `object-${t.layoutKey}-${a}`) : /* @__PURE__ */ r(u, { node: t }, `layout-${t.type}-${a}`);
|
|
14
|
-
}
|
|
15
|
-
export {
|
|
16
|
-
p as renderTreeNode
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=renderTreeNode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renderTreeNode.js","sources":["../../../../../../react/src/utils/renderTreeNode.tsx"],"sourcesContent":["import type { RenderTreeNode } from '@xbeeant/form-engine';\nimport type { ReactElement } from 'react';\n\nimport { NexusField } from '../components/NexusField';\nimport { NexusLayout } from '../components/NexusLayout';\nimport { NexusObject } from '../components/NexusObject';\n\n/**\n * renderTreeNode — 递归渲染\n */\nexport function renderTreeNode(\n node: RenderTreeNode,\n index: number,\n): ReactElement {\n if (node.type === 'field') {\n return (\n <NexusField\n key={node.layoutKey || node.dataPath}\n dataPath={node.dataPath}\n layoutKey={node.layoutKey}\n />\n );\n }\n if (node.type === 'object') {\n return (\n <NexusObject key={`object-${node.layoutKey}-${index}`} node={node} />\n );\n }\n return <NexusLayout key={`layout-${node.type}-${index}`} node={node} />;\n}\n"],"names":["renderTreeNode","node","index","jsx","NexusField","NexusObject","NexusLayout"],"mappings":";;;;AAUO,SAASA,EACdC,GACAC,GACc;AACd,SAAID,EAAK,SAAS,UAEd,gBAAAE;AAAA,IAACC;AAAA,IAAA;AAAA,MAEC,UAAUH,EAAK;AAAA,MACf,WAAWA,EAAK;AAAA,IAAA;AAAA,IAFXA,EAAK,aAAaA,EAAK;AAAA,EAAA,IAM9BA,EAAK,SAAS,WAEd,gBAAAE,EAACE,KAAsD,MAAAJ,KAArC,UAAUA,EAAK,SAAS,IAAIC,CAAK,EAAgB,IAGhE,gBAAAC,EAACG,KAAiD,MAAAL,KAAhC,UAAUA,EAAK,IAAI,IAAIC,CAAK,EAAgB;AACvE;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolveColSpan.js","sources":["../../../../../../react/src/utils/resolveColSpan.ts"],"sourcesContent":["import type { GridContextValue } from '../contexts/GridContext';\n\n/**\n * resolveColSpan — 统一解析子项在父 Grid 中的跨列数\n */\nexport function resolveColSpan(\n colSpan: number | undefined,\n gridCtx: GridContextValue | null,\n): number | undefined {\n if (colSpan !== undefined) {\n return colSpan;\n }\n\n // 在 grid 容器内:把 24 栅格语义缩放到当前 column\n if (gridCtx && gridCtx.column > 0) {\n return Math.max(1, Math.round(gridCtx.column / 24));\n }\n}\n"],"names":["resolveColSpan","colSpan","gridCtx"],"mappings":"AAKO,SAASA,EACdC,GACAC,GACoB;AACpB,MAAID,MAAY;AACd,WAAOA;AAIT,MAAIC,KAAWA,EAAQ,SAAS;AAC9B,WAAO,KAAK,IAAI,GAAG,KAAK,MAAMA,EAAQ,SAAS,EAAE,CAAC;AAEtD;"}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { NexusEngine as h } from "@xbeeant/form-engine";
|
|
2
|
-
import { describe as s, it as n, expect as t } from "/Users/amybee/Codes/xbeeant/nexus-form-engine/node_modules/.bun/vitest@4.1.10+0a9a94c82e2ce81a/node_modules/vitest/dist/index.js";
|
|
3
|
-
import { diffPropertyPatch as l, updateNodeWithNesting as r, addChildToSchema as m, flattenNodeForPropertyEditor as y, extractFormLevelConfig as c, collectDataFieldOptions as u } from "./schemaUtils.js";
|
|
4
|
-
const p = {
|
|
5
|
-
type: "string",
|
|
6
|
-
widget: "radio",
|
|
7
|
-
title: "单选",
|
|
8
|
-
enum: ["a", "b"],
|
|
9
|
-
enumNames: ["选项A", "选项B"]
|
|
10
|
-
};
|
|
11
|
-
s("diffPropertyPatch", () => {
|
|
12
|
-
n("filters out empty-string defaults for untouched fields", () => {
|
|
13
|
-
const e = l({
|
|
14
|
-
title: "单选",
|
|
15
|
-
options: [
|
|
16
|
-
{ value: "a", label: "选项A" },
|
|
17
|
-
{ value: "b", label: "选项B" }
|
|
18
|
-
]
|
|
19
|
-
}, {
|
|
20
|
-
title: "单选",
|
|
21
|
-
description: "",
|
|
22
|
-
placeholder: "",
|
|
23
|
-
default: "",
|
|
24
|
-
disabled: "",
|
|
25
|
-
readOnly: "",
|
|
26
|
-
hidden: "",
|
|
27
|
-
required: "",
|
|
28
|
-
pattern: "",
|
|
29
|
-
bind: "",
|
|
30
|
-
dependencies: "",
|
|
31
|
-
options: [
|
|
32
|
-
{ value: "a", label: "选项A" },
|
|
33
|
-
{ value: "b", label: "选项B" }
|
|
34
|
-
]
|
|
35
|
-
});
|
|
36
|
-
t(e).toEqual({});
|
|
37
|
-
}), n("keeps genuinely changed values", () => {
|
|
38
|
-
const a = { title: "单选", options: [{ value: "a", label: "A" }] }, i = {
|
|
39
|
-
title: "新标题",
|
|
40
|
-
options: [
|
|
41
|
-
{ value: "a", label: "A" },
|
|
42
|
-
{ value: "b", label: "B" }
|
|
43
|
-
]
|
|
44
|
-
}, e = l(a, i);
|
|
45
|
-
t(e).toEqual(i);
|
|
46
|
-
}), n("empty string is treated as unassigned and never written to schema", () => {
|
|
47
|
-
const e = l({ title: "单选", description: "说明", bind: "user.name" }, { title: "单选", description: "", bind: "" });
|
|
48
|
-
t(e).toEqual({});
|
|
49
|
-
}), n("user-cleared field is written back as undefined for deletion", () => {
|
|
50
|
-
const o = l({ title: "单选", variant: "outlined", description: "说明" }, { title: "单选", variant: "", description: "" }, /* @__PURE__ */ new Set(["variant"]));
|
|
51
|
-
t(o).toEqual({ variant: void 0 });
|
|
52
|
-
}), n("untouched empty fields stay skipped even with changedFields set", () => {
|
|
53
|
-
const o = l({ title: "单选" }, { title: "单选", description: "", placeholder: "" }, /* @__PURE__ */ new Set(["title"]));
|
|
54
|
-
t(o).toEqual({});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
s("updateNodeWithNesting deletion semantics", () => {
|
|
58
|
-
n("cleared UI prop is removed from node.props instead of written as undefined", () => {
|
|
59
|
-
const e = r({
|
|
60
|
-
type: "object",
|
|
61
|
-
properties: {
|
|
62
|
-
card: {
|
|
63
|
-
type: "card",
|
|
64
|
-
title: "卡片",
|
|
65
|
-
props: { variant: "outlined", hoverable: !0 },
|
|
66
|
-
properties: {}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}, ["card"], {
|
|
70
|
-
variant: void 0
|
|
71
|
-
}).properties.card;
|
|
72
|
-
t(e.props.variant).toBeUndefined(), t("variant" in e.props).toBe(!1), t(e.props.hoverable).toBe(!0);
|
|
73
|
-
}), n("cleared schema-level key is removed from the node", () => {
|
|
74
|
-
const a = {
|
|
75
|
-
type: "object",
|
|
76
|
-
properties: {
|
|
77
|
-
gender: { ...p, bind: "user.gender" }
|
|
78
|
-
}
|
|
79
|
-
}, e = r(a, ["gender"], { bind: void 0 }).properties.gender;
|
|
80
|
-
t(e.bind).toBeUndefined(), t("bind" in e).toBe(!1), t(e.title).toBe("单选");
|
|
81
|
-
}), n("widget display format is written into node.props (antd passthrough)", () => {
|
|
82
|
-
const e = r({
|
|
83
|
-
type: "object",
|
|
84
|
-
properties: {
|
|
85
|
-
birthday: { type: "string", widget: "date", title: "出生日期" }
|
|
86
|
-
}
|
|
87
|
-
}, ["birthday"], {
|
|
88
|
-
format: "YYYY/MM/DD"
|
|
89
|
-
}).properties.birthday;
|
|
90
|
-
t(e.format).toBeUndefined(), t("format" in e).toBe(!1), t(e.props.format).toBe("YYYY/MM/DD");
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
s("designer full property-panel patch", () => {
|
|
94
|
-
n("empty defaults are not written, radio key preserved in formData", () => {
|
|
95
|
-
let a = { type: "object", properties: {} };
|
|
96
|
-
a = m(a, [], "gender", p);
|
|
97
|
-
const i = y(
|
|
98
|
-
a.properties.gender
|
|
99
|
-
), o = l(i, {
|
|
100
|
-
title: "单选",
|
|
101
|
-
description: "",
|
|
102
|
-
placeholder: "",
|
|
103
|
-
default: "",
|
|
104
|
-
extra: "",
|
|
105
|
-
disabled: "",
|
|
106
|
-
readOnly: "",
|
|
107
|
-
readOnlyWidget: "",
|
|
108
|
-
hidden: "",
|
|
109
|
-
width: "",
|
|
110
|
-
options: [
|
|
111
|
-
{ value: "a", label: "选项A" },
|
|
112
|
-
{ value: "b", label: "选项B" }
|
|
113
|
-
],
|
|
114
|
-
optionType: "",
|
|
115
|
-
buttonStyle: "",
|
|
116
|
-
required: "",
|
|
117
|
-
pattern: "",
|
|
118
|
-
validate: "",
|
|
119
|
-
bind: "",
|
|
120
|
-
dependencies: ""
|
|
121
|
-
});
|
|
122
|
-
t(o.bind).toBeUndefined(), a = r(a, ["gender"], o);
|
|
123
|
-
const d = new h();
|
|
124
|
-
d.init(a), d.setFieldValue("gender", "a"), t(d.getFormData()).toEqual({ gender: "a" });
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
s("extractFormLevelConfig", () => {
|
|
128
|
-
n("only extracts top-level form config keys, not the whole schema", () => {
|
|
129
|
-
const e = c(
|
|
130
|
-
{
|
|
131
|
-
type: "object",
|
|
132
|
-
displayType: "row",
|
|
133
|
-
colon: !0,
|
|
134
|
-
properties: { gender: p }
|
|
135
|
-
},
|
|
136
|
-
[
|
|
137
|
-
"displayType",
|
|
138
|
-
"labelWidth",
|
|
139
|
-
"colon",
|
|
140
|
-
"label",
|
|
141
|
-
"readOnly",
|
|
142
|
-
"column"
|
|
143
|
-
]
|
|
144
|
-
);
|
|
145
|
-
t(e).toEqual({ displayType: "row", colon: !0 }), t("properties" in e).toBe(!1), t("type" in e).toBe(!1);
|
|
146
|
-
}), n("omits form-level keys that are not set on the schema", () => {
|
|
147
|
-
const i = c(
|
|
148
|
-
{ type: "object", properties: {} },
|
|
149
|
-
["labelWidth", "column"]
|
|
150
|
-
);
|
|
151
|
-
t(i).toEqual({});
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
s("collectDataFieldOptions", () => {
|
|
155
|
-
n("labels options with field title, values with path key", () => {
|
|
156
|
-
const i = u({
|
|
157
|
-
properties: {
|
|
158
|
-
user: {
|
|
159
|
-
type: "object",
|
|
160
|
-
properties: {
|
|
161
|
-
name: { type: "string", title: "姓名" },
|
|
162
|
-
gender: { type: "string", widget: "radio", title: "性别" }
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
tags: {
|
|
166
|
-
type: "array",
|
|
167
|
-
widget: "list",
|
|
168
|
-
items: { type: "string", widget: "input", title: "标签项" }
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
t(i).toEqual([
|
|
173
|
-
{ value: "user.name", label: "姓名" },
|
|
174
|
-
{ value: "user.gender", label: "性别" },
|
|
175
|
-
{ value: "tags", label: "tags" },
|
|
176
|
-
{ value: "tags[0]", label: "标签项" }
|
|
177
|
-
]);
|
|
178
|
-
}), n("falls back to key when title is missing", () => {
|
|
179
|
-
t(u({
|
|
180
|
-
properties: {
|
|
181
|
-
birthday: { type: "string", widget: "date" }
|
|
182
|
-
}
|
|
183
|
-
})).toEqual([
|
|
184
|
-
{ value: "birthday", label: "birthday" }
|
|
185
|
-
]);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
//# sourceMappingURL=schemaUtils.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schemaUtils.test.js","sources":["../../src/schemaUtils.test.ts"],"sourcesContent":["import type { NexusSchema, SchemaNode } from '@xbeeant/form-engine';\nimport { NexusEngine } from '@xbeeant/form-engine';\nimport { describe, expect, it } from 'vitest';\nimport {\n addChildToSchema,\n collectDataFieldOptions,\n diffPropertyPatch,\n extractFormLevelConfig,\n flattenNodeForPropertyEditor,\n updateNodeWithNesting,\n} from './schemaUtils';\n\nconst radioNode: SchemaNode = {\n type: 'string',\n widget: 'radio',\n title: '单选',\n enum: ['a', 'b'],\n enumNames: ['选项A', '选项B'],\n};\n\ndescribe('diffPropertyPatch', () => {\n it('filters out empty-string defaults for untouched fields', () => {\n const initial = {\n title: '单选',\n options: [\n { value: 'a', label: '选项A' },\n { value: 'b', label: '选项B' },\n ],\n };\n const allValues = {\n title: '单选',\n description: '',\n placeholder: '',\n default: '',\n disabled: '',\n readOnly: '',\n hidden: '',\n required: '',\n pattern: '',\n bind: '',\n dependencies: '',\n options: [\n { value: 'a', label: '选项A' },\n { value: 'b', label: '选项B' },\n ],\n };\n const patch = diffPropertyPatch(initial, allValues);\n expect(patch).toEqual({});\n });\n\n it('keeps genuinely changed values', () => {\n const initial = { title: '单选', options: [{ value: 'a', label: 'A' }] };\n const allValues = {\n title: '新标题',\n options: [\n { value: 'a', label: 'A' },\n { value: 'b', label: 'B' },\n ],\n };\n const patch = diffPropertyPatch(initial, allValues);\n expect(patch).toEqual(allValues);\n });\n\n it('empty string is treated as unassigned and never written to schema', () => {\n const initial = { title: '单选', description: '说明', bind: 'user.name' };\n const allValues = { title: '单选', description: '', bind: '' };\n const patch = diffPropertyPatch(initial, allValues);\n expect(patch).toEqual({});\n });\n\n it('user-cleared field is written back as undefined for deletion', () => {\n const initial = { title: '单选', variant: 'outlined', description: '说明' };\n const allValues = { title: '单选', variant: '', description: '' };\n const changed = new Set(['variant']);\n const patch = diffPropertyPatch(initial, allValues, changed);\n expect(patch).toEqual({ variant: undefined });\n });\n\n it('untouched empty fields stay skipped even with changedFields set', () => {\n const initial = { title: '单选' };\n const allValues = { title: '单选', description: '', placeholder: '' };\n const changed = new Set(['title']);\n const patch = diffPropertyPatch(initial, allValues, changed);\n expect(patch).toEqual({});\n });\n});\n\ndescribe('updateNodeWithNesting deletion semantics', () => {\n it('cleared UI prop is removed from node.props instead of written as undefined', () => {\n const schema: NexusSchema = {\n type: 'object',\n properties: {\n card: {\n type: 'card',\n title: '卡片',\n props: { variant: 'outlined', hoverable: true },\n properties: {},\n },\n },\n };\n const next = updateNodeWithNesting(schema, ['card'], {\n variant: undefined,\n });\n const node = next.properties.card as Record<string, unknown>;\n expect((node.props as Record<string, unknown>).variant).toBeUndefined();\n expect('variant' in (node.props as Record<string, unknown>)).toBe(false);\n expect((node.props as Record<string, unknown>).hoverable).toBe(true);\n });\n\n it('cleared schema-level key is removed from the node', () => {\n const schema: NexusSchema = {\n type: 'object',\n properties: {\n gender: { ...radioNode, bind: 'user.gender' },\n },\n };\n const next = updateNodeWithNesting(schema, ['gender'], { bind: undefined });\n const node = next.properties.gender as Record<string, unknown>;\n expect(node.bind).toBeUndefined();\n expect('bind' in node).toBe(false);\n expect(node.title).toBe('单选');\n });\n\n it('widget display format is written into node.props (antd passthrough)', () => {\n // date 等控件的 format 是显示格式,必须走 props 通道才能被 widget 消费;\n // 顶层 node.format 保留给 JSON Schema 校验语义(email/url)\n const schema: NexusSchema = {\n type: 'object',\n properties: {\n birthday: { type: 'string', widget: 'date', title: '出生日期' },\n },\n };\n const next = updateNodeWithNesting(schema, ['birthday'], {\n format: 'YYYY/MM/DD',\n });\n const node = next.properties.birthday as Record<string, unknown>;\n expect(node.format).toBeUndefined();\n expect('format' in node).toBe(false);\n expect((node.props as Record<string, unknown>).format).toBe('YYYY/MM/DD');\n });\n});\n\ndescribe('designer full property-panel patch', () => {\n it('empty defaults are not written, radio key preserved in formData', () => {\n let schema: NexusSchema = { type: 'object', properties: {} };\n schema = addChildToSchema(schema, [], 'gender', radioNode);\n\n // simulate property form allValues (includes empty-string defaults) + diff filter\n const initial = flattenNodeForPropertyEditor(\n schema.properties.gender as never,\n );\n const allValues = {\n title: '单选',\n description: '',\n placeholder: '',\n default: '',\n extra: '',\n disabled: '',\n readOnly: '',\n readOnlyWidget: '',\n hidden: '',\n width: '',\n options: [\n { value: 'a', label: '选项A' },\n { value: 'b', label: '选项B' },\n ],\n optionType: '',\n buttonStyle: '',\n required: '',\n pattern: '',\n validate: '',\n bind: '',\n dependencies: '',\n };\n const patch = diffPropertyPatch(initial, allValues);\n expect(patch.bind).toBeUndefined();\n\n schema = updateNodeWithNesting(schema, ['gender'], patch);\n\n const engine = new NexusEngine();\n engine.init(schema);\n engine.setFieldValue('gender', 'a');\n expect(engine.getFormData()).toEqual({ gender: 'a' });\n });\n});\n\ndescribe('extractFormLevelConfig', () => {\n it('only extracts top-level form config keys, not the whole schema', () => {\n const schema: NexusSchema = {\n type: 'object',\n displayType: 'row',\n colon: true,\n properties: { gender: radioNode },\n };\n const keys = [\n 'displayType',\n 'labelWidth',\n 'colon',\n 'label',\n 'readOnly',\n 'column',\n ];\n const config = extractFormLevelConfig(\n schema as unknown as Record<string, unknown>,\n keys,\n );\n expect(config).toEqual({ displayType: 'row', colon: true });\n expect('properties' in config).toBe(false);\n expect('type' in config).toBe(false);\n });\n\n it('omits form-level keys that are not set on the schema', () => {\n const schema: NexusSchema = { type: 'object', properties: {} };\n const config = extractFormLevelConfig(\n schema as unknown as Record<string, unknown>,\n ['labelWidth', 'column'],\n );\n expect(config).toEqual({});\n });\n});\n\ndescribe('collectDataFieldOptions', () => {\n it('labels options with field title, values with path key', () => {\n const schema: NexusSchema = {\n type: 'object',\n properties: {\n user: {\n type: 'object',\n properties: {\n name: { type: 'string', title: '姓名' },\n gender: { type: 'string', widget: 'radio', title: '性别' },\n },\n },\n tags: {\n type: 'array',\n widget: 'list',\n items: { type: 'string', widget: 'input', title: '标签项' },\n },\n },\n };\n const options = collectDataFieldOptions(schema);\n expect(options).toEqual([\n { value: 'user.name', label: '姓名' },\n { value: 'user.gender', label: '性别' },\n { value: 'tags', label: 'tags' },\n { value: 'tags[0]', label: '标签项' },\n ]);\n });\n\n it('falls back to key when title is missing', () => {\n const schema: NexusSchema = {\n type: 'object',\n properties: {\n birthday: { type: 'string', widget: 'date' },\n },\n };\n expect(collectDataFieldOptions(schema)).toEqual([\n { value: 'birthday', label: 'birthday' },\n ]);\n });\n});\n"],"names":["radioNode","describe","it","patch","diffPropertyPatch","expect","initial","allValues","node","updateNodeWithNesting","schema","addChildToSchema","flattenNodeForPropertyEditor","engine","NexusEngine","config","extractFormLevelConfig","options","collectDataFieldOptions"],"mappings":"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"}
|
package/dist/es/vite-env.d.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vite-env.d.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { describe as c, it as o, expect as t } from "/Users/amybee/Codes/xbeeant/nexus-form-engine/node_modules/.bun/vitest@4.1.10+0a9a94c82e2ce81a/node_modules/vitest/dist/index.js";
|
|
2
|
-
import { collectDataFieldPaths as n } from "../schemaUtils.js";
|
|
3
|
-
import { buildExpression as e, parseExpression as i, stripBraces as p, variableToString as s, parseVariable as d } from "./expressionModel.js";
|
|
4
|
-
c("buildExpression", () => {
|
|
5
|
-
o("构建基本比较表达式(文本 / 数字 / 布尔 / 变量右值)", () => {
|
|
6
|
-
t(
|
|
7
|
-
e([
|
|
8
|
-
{
|
|
9
|
-
id: "1",
|
|
10
|
-
logic: "and",
|
|
11
|
-
variable: { source: "formData", path: "usePromo" },
|
|
12
|
-
operator: "eq",
|
|
13
|
-
rightMode: "boolean",
|
|
14
|
-
right: "true"
|
|
15
|
-
}
|
|
16
|
-
])
|
|
17
|
-
).toBe("{{ formData.usePromo === true }}"), t(
|
|
18
|
-
e([
|
|
19
|
-
{
|
|
20
|
-
id: "1",
|
|
21
|
-
logic: "and",
|
|
22
|
-
variable: { source: "formData", path: "amount" },
|
|
23
|
-
operator: "gte",
|
|
24
|
-
rightMode: "number",
|
|
25
|
-
right: "100"
|
|
26
|
-
}
|
|
27
|
-
])
|
|
28
|
-
).toBe("{{ formData.amount >= 100 }}"), t(
|
|
29
|
-
e([
|
|
30
|
-
{
|
|
31
|
-
id: "1",
|
|
32
|
-
logic: "and",
|
|
33
|
-
variable: { source: "self", path: "" },
|
|
34
|
-
operator: "neq",
|
|
35
|
-
rightMode: "string",
|
|
36
|
-
right: "special"
|
|
37
|
-
}
|
|
38
|
-
])
|
|
39
|
-
).toBe("{{ $self.value !== 'special' }}"), t(
|
|
40
|
-
e([
|
|
41
|
-
{
|
|
42
|
-
id: "1",
|
|
43
|
-
logic: "and",
|
|
44
|
-
variable: { source: "formData", path: "amount" },
|
|
45
|
-
operator: "eq",
|
|
46
|
-
rightMode: "variable",
|
|
47
|
-
right: "formData.limit"
|
|
48
|
-
}
|
|
49
|
-
])
|
|
50
|
-
).toBe("{{ formData.amount === formData.limit }}");
|
|
51
|
-
}), o("空右值 / 未选变量视为不完整条件,返回空模板", () => {
|
|
52
|
-
t(
|
|
53
|
-
e([
|
|
54
|
-
{
|
|
55
|
-
id: "1",
|
|
56
|
-
logic: "and",
|
|
57
|
-
variable: { source: "formData", path: "" },
|
|
58
|
-
operator: "eq",
|
|
59
|
-
rightMode: "string",
|
|
60
|
-
right: "x"
|
|
61
|
-
}
|
|
62
|
-
])
|
|
63
|
-
).toBe("{{ }}"), t(
|
|
64
|
-
e([
|
|
65
|
-
{
|
|
66
|
-
id: "1",
|
|
67
|
-
logic: "and",
|
|
68
|
-
variable: { source: "formData", path: "a" },
|
|
69
|
-
operator: "eq",
|
|
70
|
-
rightMode: "string",
|
|
71
|
-
right: ""
|
|
72
|
-
}
|
|
73
|
-
])
|
|
74
|
-
).toBe("{{ }}");
|
|
75
|
-
}), o("无条件时返回空模板", () => {
|
|
76
|
-
t(e([])).toBe("{{ }}");
|
|
77
|
-
}), o("按 and/or 连接多条条件", () => {
|
|
78
|
-
const a = (u, m) => ({
|
|
79
|
-
id: Math.random().toString(),
|
|
80
|
-
logic: "and",
|
|
81
|
-
variable: { source: "formData", path: "a" },
|
|
82
|
-
operator: u,
|
|
83
|
-
rightMode: "string",
|
|
84
|
-
right: m
|
|
85
|
-
}), r = { ...a("eq", "x"), id: "1" }, l = { ...a("neq", "y"), id: "2", logic: "or" };
|
|
86
|
-
t(e([r, l])).toBe(
|
|
87
|
-
"{{ formData.a === 'x' || formData.a !== 'y' }}"
|
|
88
|
-
);
|
|
89
|
-
}), o("contains / startsWith / empty / truthy 等操作符", () => {
|
|
90
|
-
const a = (r, l = "") => ({
|
|
91
|
-
id: "1",
|
|
92
|
-
logic: "and",
|
|
93
|
-
variable: { source: "formData", path: "code" },
|
|
94
|
-
operator: r,
|
|
95
|
-
rightMode: "string",
|
|
96
|
-
right: l
|
|
97
|
-
});
|
|
98
|
-
t(e([a("contains", "CN")])).toBe(
|
|
99
|
-
"{{ String(formData.code).includes('CN') }}"
|
|
100
|
-
), t(e([a("notEmpty")])).toBe(
|
|
101
|
-
"{{ (formData.code != null && String(formData.code).trim() !== '') }}"
|
|
102
|
-
), t(e([a("truthy")])).toBe("{{ !!(formData.code) }}");
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
c("parseExpression", () => {
|
|
106
|
-
o("解析基本表达式为行模型", () => {
|
|
107
|
-
const a = i("{{ formData.usePromo === true }}");
|
|
108
|
-
t(a).not.toBeNull(), t(a[0]).toMatchObject({
|
|
109
|
-
variable: { source: "formData", path: "usePromo" },
|
|
110
|
-
operator: "eq",
|
|
111
|
-
rightMode: "boolean",
|
|
112
|
-
right: "true"
|
|
113
|
-
});
|
|
114
|
-
}), o("解析 $deps / $self / 文本字面量", () => {
|
|
115
|
-
const a = i("{{ $deps[0] === true }}");
|
|
116
|
-
t(a[0].variable).toEqual({ source: "deps", path: "0" });
|
|
117
|
-
const r = i("{{ $self.value === 'x' }}");
|
|
118
|
-
t(r[0].variable).toEqual({ source: "self", path: "" }), t(r[0].rightMode).toBe("string"), t(r[0].right).toBe("x");
|
|
119
|
-
}), o("解析 and/or 连接", () => {
|
|
120
|
-
const a = i(
|
|
121
|
-
"{{ formData.a === 'x' && formData.b > 3 || formData.c == 'y' }}"
|
|
122
|
-
);
|
|
123
|
-
t(a.length).toBe(3), t(a[0].logic).toBe("and"), t(a[1].logic).toBe("and"), t(a[2].logic).toBe("or");
|
|
124
|
-
}), o("解析空模板 / 空串为 []", () => {
|
|
125
|
-
t(i("{{ }}")).toEqual([]), t(i("")).toEqual([]), t(i(void 0)).toEqual([]);
|
|
126
|
-
}), o("无法解析的表达式返回 null(回退高级模式)", () => {
|
|
127
|
-
t(i("{{ formData.a.toUpperCase() }}")).toBeNull(), t(i("{{ $deps[0] + 1 > 2 }}")).toBeNull();
|
|
128
|
-
}), o("round-trip:build → parse → build 保持一致", () => {
|
|
129
|
-
const a = "{{ formData.a === 'x' || formData.b >= 3 }}", r = i(a);
|
|
130
|
-
t(r.length).toBe(2);
|
|
131
|
-
const l = e(r);
|
|
132
|
-
t(l).toBe(a);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
c("变量工具", () => {
|
|
136
|
-
o("stripBraces 去除包裹", () => {
|
|
137
|
-
t(p("{{ a === 1 }}")).toBe("a === 1"), t(p("a === 1")).toBe("a === 1");
|
|
138
|
-
}), o("variableToString 各来源", () => {
|
|
139
|
-
t(s({ source: "formData", path: "user.name" })).toBe(
|
|
140
|
-
"formData.user.name"
|
|
141
|
-
), t(s({ source: "deps", path: "2" })).toBe("$deps[2]"), t(s({ source: "self", path: "" })).toBe("$self.value"), t(s({ source: "index", path: "" })).toBe("$index"), t(s({ source: "root", path: "" })).toBe("rootValue");
|
|
142
|
-
}), o("parseVariable 非标识符字段用括号访问", () => {
|
|
143
|
-
t(d("formData['a-b']")).toEqual({
|
|
144
|
-
source: "formData",
|
|
145
|
-
path: "a-b"
|
|
146
|
-
}), t(s(d("formData['a-b']"))).toBe(
|
|
147
|
-
"formData['a-b']"
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
c("collectDataFieldPaths", () => {
|
|
152
|
-
o("布局节点 Key 不进路径,对象 / 数组透传", () => {
|
|
153
|
-
t(n({
|
|
154
|
-
properties: {
|
|
155
|
-
card: {
|
|
156
|
-
type: "card",
|
|
157
|
-
title: "卡片",
|
|
158
|
-
properties: {
|
|
159
|
-
name: { type: "string", widget: "input", title: "姓名" },
|
|
160
|
-
address: {
|
|
161
|
-
type: "object",
|
|
162
|
-
properties: {
|
|
163
|
-
city: { type: "string", widget: "input" }
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
tags: {
|
|
167
|
-
type: "array",
|
|
168
|
-
widget: "list",
|
|
169
|
-
items: {
|
|
170
|
-
type: "object",
|
|
171
|
-
properties: {
|
|
172
|
-
label: { type: "string", widget: "input" }
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
})).toEqual([
|
|
180
|
-
"name",
|
|
181
|
-
"address.city",
|
|
182
|
-
"tags",
|
|
183
|
-
"tags[0].label"
|
|
184
|
-
]);
|
|
185
|
-
}), o("子表单(带 widget 的 object)视为叶子字段", () => {
|
|
186
|
-
t(n({
|
|
187
|
-
properties: {
|
|
188
|
-
sub: { type: "object", widget: "subForm", properties: {} }
|
|
189
|
-
}
|
|
190
|
-
})).toEqual(["sub"]);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
//# sourceMappingURL=expressionModel.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expressionModel.test.js","sources":["../../../src/widgets/expressionModel.test.ts"],"sourcesContent":["import type { NexusSchema } from '@xbeeant/form-engine';\nimport { describe, expect, it } from 'vitest';\nimport { collectDataFieldPaths } from '../schemaUtils';\nimport {\n buildExpression,\n type ConditionOperator,\n parseExpression,\n parseVariable,\n stripBraces,\n variableToString,\n} from './expressionModel';\n\ndescribe('buildExpression', () => {\n it('构建基本比较表达式(文本 / 数字 / 布尔 / 变量右值)', () => {\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'formData', path: 'usePromo' },\n operator: 'eq',\n rightMode: 'boolean',\n right: 'true',\n },\n ]),\n ).toBe('{{ formData.usePromo === true }}');\n\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'formData', path: 'amount' },\n operator: 'gte',\n rightMode: 'number',\n right: '100',\n },\n ]),\n ).toBe('{{ formData.amount >= 100 }}');\n\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'self', path: '' },\n operator: 'neq',\n rightMode: 'string',\n right: 'special',\n },\n ]),\n ).toBe(\"{{ $self.value !== 'special' }}\");\n\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'formData', path: 'amount' },\n operator: 'eq',\n rightMode: 'variable',\n right: 'formData.limit',\n },\n ]),\n ).toBe('{{ formData.amount === formData.limit }}');\n });\n\n it('空右值 / 未选变量视为不完整条件,返回空模板', () => {\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'formData', path: '' },\n operator: 'eq',\n rightMode: 'string',\n right: 'x',\n },\n ]),\n ).toBe('{{ }}');\n\n expect(\n buildExpression([\n {\n id: '1',\n logic: 'and',\n variable: { source: 'formData', path: 'a' },\n operator: 'eq',\n rightMode: 'string',\n right: '',\n },\n ]),\n ).toBe('{{ }}');\n });\n\n it('无条件时返回空模板', () => {\n expect(buildExpression([])).toBe('{{ }}');\n });\n\n it('按 and/or 连接多条条件', () => {\n const cond = (op: 'eq' | 'neq', right: string) => ({\n id: Math.random().toString(),\n logic: 'and' as const,\n variable: { source: 'formData' as const, path: 'a' },\n operator: op,\n rightMode: 'string' as const,\n right,\n });\n const c1 = { ...cond('eq', 'x'), id: '1' };\n const c2 = { ...cond('neq', 'y'), id: '2', logic: 'or' as const };\n expect(buildExpression([c1, c2])).toBe(\n \"{{ formData.a === 'x' || formData.a !== 'y' }}\",\n );\n });\n\n it('contains / startsWith / empty / truthy 等操作符', () => {\n const base = (operator: ConditionOperator, right = '') => ({\n id: '1',\n logic: 'and' as const,\n variable: { source: 'formData' as const, path: 'code' },\n operator,\n rightMode: 'string' as const,\n right,\n });\n expect(buildExpression([base('contains', 'CN')])).toBe(\n \"{{ String(formData.code).includes('CN') }}\",\n );\n expect(buildExpression([base('notEmpty')])).toBe(\n \"{{ (formData.code != null && String(formData.code).trim() !== '') }}\",\n );\n expect(buildExpression([base('truthy')])).toBe('{{ !!(formData.code) }}');\n });\n});\n\ndescribe('parseExpression', () => {\n it('解析基本表达式为行模型', () => {\n const conds = parseExpression('{{ formData.usePromo === true }}');\n expect(conds).not.toBeNull();\n expect(conds![0]).toMatchObject({\n variable: { source: 'formData', path: 'usePromo' },\n operator: 'eq',\n rightMode: 'boolean',\n right: 'true',\n });\n });\n\n it('解析 $deps / $self / 文本字面量', () => {\n const conds = parseExpression('{{ $deps[0] === true }}');\n expect(conds![0].variable).toEqual({ source: 'deps', path: '0' });\n\n const self = parseExpression(\"{{ $self.value === 'x' }}\");\n expect(self![0].variable).toEqual({ source: 'self', path: '' });\n expect(self![0].rightMode).toBe('string');\n expect(self![0].right).toBe('x');\n });\n\n it('解析 and/or 连接', () => {\n const conds = parseExpression(\n \"{{ formData.a === 'x' && formData.b > 3 || formData.c == 'y' }}\",\n );\n expect(conds!.length).toBe(3);\n expect(conds![0].logic).toBe('and');\n expect(conds![1].logic).toBe('and');\n expect(conds![2].logic).toBe('or');\n });\n\n it('解析空模板 / 空串为 []', () => {\n expect(parseExpression('{{ }}')).toEqual([]);\n expect(parseExpression('')).toEqual([]);\n expect(parseExpression(undefined)).toEqual([]);\n });\n\n it('无法解析的表达式返回 null(回退高级模式)', () => {\n expect(parseExpression('{{ formData.a.toUpperCase() }}')).toBeNull();\n expect(parseExpression('{{ $deps[0] + 1 > 2 }}')).toBeNull();\n });\n\n it('round-trip:build → parse → build 保持一致', () => {\n const src = \"{{ formData.a === 'x' || formData.b >= 3 }}\";\n const conds = parseExpression(src)!;\n expect(conds.length).toBe(2);\n const rebuilt = buildExpression(conds);\n expect(rebuilt).toBe(src);\n });\n});\n\ndescribe('变量工具', () => {\n it('stripBraces 去除包裹', () => {\n expect(stripBraces('{{ a === 1 }}')).toBe('a === 1');\n expect(stripBraces('a === 1')).toBe('a === 1');\n });\n\n it('variableToString 各来源', () => {\n expect(variableToString({ source: 'formData', path: 'user.name' })).toBe(\n 'formData.user.name',\n );\n expect(variableToString({ source: 'deps', path: '2' })).toBe('$deps[2]');\n expect(variableToString({ source: 'self', path: '' })).toBe('$self.value');\n expect(variableToString({ source: 'index', path: '' })).toBe('$index');\n expect(variableToString({ source: 'root', path: '' })).toBe('rootValue');\n });\n\n it('parseVariable 非标识符字段用括号访问', () => {\n expect(parseVariable(\"formData['a-b']\")).toEqual({\n source: 'formData',\n path: 'a-b',\n });\n expect(variableToString(parseVariable(\"formData['a-b']\"))).toBe(\n \"formData['a-b']\",\n );\n });\n});\n\ndescribe('collectDataFieldPaths', () => {\n it('布局节点 Key 不进路径,对象 / 数组透传', () => {\n const schema = {\n type: 'object',\n properties: {\n card: {\n type: 'card',\n title: '卡片',\n properties: {\n name: { type: 'string', widget: 'input', title: '姓名' },\n address: {\n type: 'object',\n properties: {\n city: { type: 'string', widget: 'input' },\n },\n },\n tags: {\n type: 'array',\n widget: 'list',\n items: {\n type: 'object',\n properties: {\n label: { type: 'string', widget: 'input' },\n },\n },\n },\n },\n },\n },\n } as unknown as NexusSchema;\n\n expect(collectDataFieldPaths(schema)).toEqual([\n 'name',\n 'address.city',\n 'tags',\n 'tags[0].label',\n ]);\n });\n\n it('子表单(带 widget 的 object)视为叶子字段', () => {\n const schema = {\n type: 'object',\n properties: {\n sub: { type: 'object', widget: 'subForm', properties: {} },\n },\n } as unknown as NexusSchema;\n expect(collectDataFieldPaths(schema)).toEqual(['sub']);\n });\n});\n"],"names":["describe","it","expect","buildExpression","cond","op","right","c1","c2","base","operator","conds","parseExpression","self","src","rebuilt","stripBraces","variableToString","parseVariable","collectDataFieldPaths"],"mappings":";;;AAYAA,EAAS,mBAAmB,MAAM;AAChC,EAAAC,EAAG,kCAAkC,MAAM;AACzC,IAAAC;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,YAAY,MAAM,WAAA;AAAA,UACtC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,kCAAkC,GAEzCD;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,YAAY,MAAM,SAAA;AAAA,UACtC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,8BAA8B,GAErCD;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,QAAQ,MAAM,GAAA;AAAA,UAClC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,iCAAiC,GAExCD;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,YAAY,MAAM,SAAA;AAAA,UACtC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,0CAA0C;AAAA,EACnD,CAAC,GAEDF,EAAG,2BAA2B,MAAM;AAClC,IAAAC;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,YAAY,MAAM,GAAA;AAAA,UACtC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,QAAQ,GAEfD;AAAA,MACEC,EAAgB;AAAA,QACd;AAAA,UACE,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,UAAU,EAAE,QAAQ,YAAY,MAAM,IAAA;AAAA,UACtC,UAAU;AAAA,UACV,WAAW;AAAA,UACX,OAAO;AAAA,QAAA;AAAA,MACT,CACD;AAAA,IAAA,EACD,KAAK,QAAQ;AAAA,EACjB,CAAC,GAEDF,EAAG,aAAa,MAAM;AACpB,IAAAC,EAAOC,EAAgB,CAAA,CAAE,CAAC,EAAE,KAAK,QAAQ;AAAA,EAC3C,CAAC,GAEDF,EAAG,mBAAmB,MAAM;AAC1B,UAAMG,IAAO,CAACC,GAAkBC,OAAmB;AAAA,MACjD,IAAI,KAAK,OAAA,EAAS,SAAA;AAAA,MAClB,OAAO;AAAA,MACP,UAAU,EAAE,QAAQ,YAAqB,MAAM,IAAA;AAAA,MAC/C,UAAUD;AAAA,MACV,WAAW;AAAA,MACX,OAAAC;AAAA,IAAA,IAEIC,IAAK,EAAE,GAAGH,EAAK,MAAM,GAAG,GAAG,IAAI,IAAA,GAC/BI,IAAK,EAAE,GAAGJ,EAAK,OAAO,GAAG,GAAG,IAAI,KAAK,OAAO,KAAA;AAClD,IAAAF,EAAOC,EAAgB,CAACI,GAAIC,CAAE,CAAC,CAAC,EAAE;AAAA,MAChC;AAAA,IAAA;AAAA,EAEJ,CAAC,GAEDP,EAAG,+CAA+C,MAAM;AACtD,UAAMQ,IAAO,CAACC,GAA6BJ,IAAQ,QAAQ;AAAA,MACzD,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,UAAU,EAAE,QAAQ,YAAqB,MAAM,OAAA;AAAA,MAC/C,UAAAI;AAAA,MACA,WAAW;AAAA,MACX,OAAAJ;AAAA,IAAA;AAEF,IAAAJ,EAAOC,EAAgB,CAACM,EAAK,YAAY,IAAI,CAAC,CAAC,CAAC,EAAE;AAAA,MAChD;AAAA,IAAA,GAEFP,EAAOC,EAAgB,CAACM,EAAK,UAAU,CAAC,CAAC,CAAC,EAAE;AAAA,MAC1C;AAAA,IAAA,GAEFP,EAAOC,EAAgB,CAACM,EAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,yBAAyB;AAAA,EAC1E,CAAC;AACH,CAAC;AAEDT,EAAS,mBAAmB,MAAM;AAChC,EAAAC,EAAG,eAAe,MAAM;AACtB,UAAMU,IAAQC,EAAgB,kCAAkC;AAChE,IAAAV,EAAOS,CAAK,EAAE,IAAI,SAAA,GAClBT,EAAOS,EAAO,CAAC,CAAC,EAAE,cAAc;AAAA,MAC9B,UAAU,EAAE,QAAQ,YAAY,MAAM,WAAA;AAAA,MACtC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,CACR;AAAA,EACH,CAAC,GAEDV,EAAG,4BAA4B,MAAM;AACnC,UAAMU,IAAQC,EAAgB,yBAAyB;AACvD,IAAAV,EAAOS,EAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,QAAQ,MAAM,IAAA,CAAK;AAEhE,UAAME,IAAOD,EAAgB,2BAA2B;AACxD,IAAAV,EAAOW,EAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,QAAQ,MAAM,GAAA,CAAI,GAC9DX,EAAOW,EAAM,CAAC,EAAE,SAAS,EAAE,KAAK,QAAQ,GACxCX,EAAOW,EAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,EACjC,CAAC,GAEDZ,EAAG,gBAAgB,MAAM;AACvB,UAAMU,IAAQC;AAAA,MACZ;AAAA,IAAA;AAEF,IAAAV,EAAOS,EAAO,MAAM,EAAE,KAAK,CAAC,GAC5BT,EAAOS,EAAO,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,GAClCT,EAAOS,EAAO,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,GAClCT,EAAOS,EAAO,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI;AAAA,EACnC,CAAC,GAEDV,EAAG,kBAAkB,MAAM;AACzB,IAAAC,EAAOU,EAAgB,QAAQ,CAAC,EAAE,QAAQ,CAAA,CAAE,GAC5CV,EAAOU,EAAgB,EAAE,CAAC,EAAE,QAAQ,CAAA,CAAE,GACtCV,EAAOU,EAAgB,MAAS,CAAC,EAAE,QAAQ,CAAA,CAAE;AAAA,EAC/C,CAAC,GAEDX,EAAG,2BAA2B,MAAM;AAClC,IAAAC,EAAOU,EAAgB,gCAAgC,CAAC,EAAE,SAAA,GAC1DV,EAAOU,EAAgB,wBAAwB,CAAC,EAAE,SAAA;AAAA,EACpD,CAAC,GAEDX,EAAG,yCAAyC,MAAM;AAChD,UAAMa,IAAM,+CACNH,IAAQC,EAAgBE,CAAG;AACjC,IAAAZ,EAAOS,EAAM,MAAM,EAAE,KAAK,CAAC;AAC3B,UAAMI,IAAUZ,EAAgBQ,CAAK;AACrC,IAAAT,EAAOa,CAAO,EAAE,KAAKD,CAAG;AAAA,EAC1B,CAAC;AACH,CAAC;AAEDd,EAAS,QAAQ,MAAM;AACrB,EAAAC,EAAG,oBAAoB,MAAM;AAC3B,IAAAC,EAAOc,EAAY,eAAe,CAAC,EAAE,KAAK,SAAS,GACnDd,EAAOc,EAAY,SAAS,CAAC,EAAE,KAAK,SAAS;AAAA,EAC/C,CAAC,GAEDf,EAAG,wBAAwB,MAAM;AAC/B,IAAAC,EAAOe,EAAiB,EAAE,QAAQ,YAAY,MAAM,YAAA,CAAa,CAAC,EAAE;AAAA,MAClE;AAAA,IAAA,GAEFf,EAAOe,EAAiB,EAAE,QAAQ,QAAQ,MAAM,KAAK,CAAC,EAAE,KAAK,UAAU,GACvEf,EAAOe,EAAiB,EAAE,QAAQ,QAAQ,MAAM,IAAI,CAAC,EAAE,KAAK,aAAa,GACzEf,EAAOe,EAAiB,EAAE,QAAQ,SAAS,MAAM,IAAI,CAAC,EAAE,KAAK,QAAQ,GACrEf,EAAOe,EAAiB,EAAE,QAAQ,QAAQ,MAAM,IAAI,CAAC,EAAE,KAAK,WAAW;AAAA,EACzE,CAAC,GAEDhB,EAAG,6BAA6B,MAAM;AACpC,IAAAC,EAAOgB,EAAc,iBAAiB,CAAC,EAAE,QAAQ;AAAA,MAC/C,QAAQ;AAAA,MACR,MAAM;AAAA,IAAA,CACP,GACDhB,EAAOe,EAAiBC,EAAc,iBAAiB,CAAC,CAAC,EAAE;AAAA,MACzD;AAAA,IAAA;AAAA,EAEJ,CAAC;AACH,CAAC;AAEDlB,EAAS,yBAAyB,MAAM;AACtC,EAAAC,EAAG,2BAA2B,MAAM;AA8BlC,IAAAC,EAAOiB,EA7BQ;AAAA,MAEb,YAAY;AAAA,QACV,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,OAAO;AAAA,UACP,YAAY;AAAA,YACV,MAAM,EAAE,MAAM,UAAU,QAAQ,SAAS,OAAO,KAAA;AAAA,YAChD,SAAS;AAAA,cACP,MAAM;AAAA,cACN,YAAY;AAAA,gBACV,MAAM,EAAE,MAAM,UAAU,QAAQ,QAAA;AAAA,cAAQ;AAAA,YAC1C;AAAA,YAEF,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,QAAQ;AAAA,cACR,OAAO;AAAA,gBACL,MAAM;AAAA,gBACN,YAAY;AAAA,kBACV,OAAO,EAAE,MAAM,UAAU,QAAQ,QAAA;AAAA,gBAAQ;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAGiC,CAAC,EAAE,QAAQ;AAAA,MAC5C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH,CAAC,GAEDlB,EAAG,gCAAgC,MAAM;AAOvC,IAAAC,EAAOiB,EANQ;AAAA,MAEb,YAAY;AAAA,QACV,KAAK,EAAE,MAAM,UAAU,QAAQ,WAAW,YAAY,CAAA,EAAC;AAAA,MAAE;AAAA,IAC3D,CAEiC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;AAAA,EACvD,CAAC;AACH,CAAC;"}
|