@xbeeant/form-engine-designer 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/Canvas.js +42 -47
- package/dist/es/Canvas.js.map +1 -1
- package/dist/es/Designer.js +121 -98
- package/dist/es/Designer.js.map +1 -1
- package/dist/es/DesignerContext.js +86 -73
- package/dist/es/DesignerContext.js.map +1 -1
- package/dist/es/Palette.js +3 -3
- package/dist/es/Palette.js.map +1 -1
- package/dist/es/PropertyPanel.js +99 -100
- package/dist/es/PropertyPanel.js.map +1 -1
- package/dist/es/catalog.js +17 -10
- package/dist/es/catalog.js.map +1 -1
- package/dist/es/designer-context.js +110 -0
- package/dist/es/designer-context.js.map +1 -0
- package/dist/es/history.js +73 -0
- package/dist/es/history.js.map +1 -0
- package/dist/es/index.css +1 -1
- package/dist/es/index.js +3 -3
- package/dist/es/property/basic-property.js +6 -0
- package/dist/es/property/basic-property.js.map +1 -1
- package/dist/es/property-panel.js +294 -0
- package/dist/es/property-panel.js.map +1 -0
- package/dist/es/resizable-panel.js +103 -0
- package/dist/es/resizable-panel.js.map +1 -0
- package/dist/es/schema-utils.js +286 -0
- package/dist/es/schema-utils.js.map +1 -0
- package/dist/es/widgets/BindEditor.js +60 -49
- package/dist/es/widgets/BindEditor.js.map +1 -1
- package/dist/es/widgets/CodeEditor.js +41 -49
- package/dist/es/widgets/CodeEditor.js.map +1 -1
- package/dist/es/widgets/DefaultEditor.js +25 -30
- package/dist/es/widgets/DefaultEditor.js.map +1 -1
- package/dist/es/widgets/DependenciesEditor.js +21 -21
- package/dist/es/widgets/DependenciesEditor.js.map +1 -1
- package/dist/es/widgets/EnumEditor.js +25 -31
- package/dist/es/widgets/EnumEditor.js.map +1 -1
- package/dist/es/widgets/ExpressionSwitch.js +32 -30
- package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
- package/dist/es/widgets/ReactionsEditor.js +103 -103
- package/dist/es/widgets/ReactionsEditor.js.map +1 -1
- package/dist/es/widgets/RemoteDataEditor.js +63 -63
- package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
- package/dist/es/widgets/ValidateEditor.js +49 -49
- package/dist/es/widgets/ValidateEditor.js.map +1 -1
- package/dist/es/widgets/bind-editor.js +78 -0
- package/dist/es/widgets/bind-editor.js.map +1 -0
- package/dist/es/widgets/code-editor.js +76 -0
- package/dist/es/widgets/code-editor.js.map +1 -0
- package/dist/es/widgets/default-editor.js +50 -0
- package/dist/es/widgets/default-editor.js.map +1 -0
- package/dist/es/widgets/dependencies-editor.js +36 -0
- package/dist/es/widgets/dependencies-editor.js.map +1 -0
- package/dist/es/widgets/enum-editor.js +43 -0
- package/dist/es/widgets/enum-editor.js.map +1 -0
- package/dist/es/widgets/expression-builder.js +367 -0
- package/dist/es/widgets/expression-builder.js.map +1 -0
- package/dist/es/widgets/expression-model.js +340 -0
- package/dist/es/widgets/expression-model.js.map +1 -0
- package/dist/es/widgets/expression-switch.js +49 -0
- package/dist/es/widgets/expression-switch.js.map +1 -0
- package/dist/es/widgets/index.js +13 -13
- package/dist/es/widgets/index.js.map +1 -1
- package/dist/es/widgets/options-editor.js +102 -0
- package/dist/es/widgets/options-editor.js.map +1 -0
- package/dist/es/widgets/reactions-editor.js +401 -0
- package/dist/es/widgets/reactions-editor.js.map +1 -0
- package/dist/es/widgets/reactions-model.js +107 -0
- package/dist/es/widgets/reactions-model.js.map +1 -0
- package/dist/es/widgets/remote-data-editor.js +314 -0
- package/dist/es/widgets/remote-data-editor.js.map +1 -0
- package/dist/es/widgets/use-form-data-fields.js +16 -0
- package/dist/es/widgets/use-form-data-fields.js.map +1 -0
- package/dist/es/widgets/validate-editor.js +118 -0
- package/dist/es/widgets/validate-editor.js.map +1 -0
- package/package.json +4 -5
- package/dist/es/react/src/components/FormController.js +0 -160
- package/dist/es/react/src/components/FormController.js.map +0 -1
- package/dist/es/react/src/components/NexusField.js +0 -98
- package/dist/es/react/src/components/NexusField.js.map +0 -1
- package/dist/es/react/src/components/NexusForm.js +0 -128
- package/dist/es/react/src/components/NexusForm.js.map +0 -1
- package/dist/es/react/src/components/NexusFormProvider.js +0 -19
- package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
- package/dist/es/react/src/components/NexusLayout.js +0 -49
- package/dist/es/react/src/components/NexusLayout.js.map +0 -1
- package/dist/es/react/src/components/NexusObject.js +0 -65
- package/dist/es/react/src/components/NexusObject.js.map +0 -1
- package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
- package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
- package/dist/es/react/src/contexts/GridContext.js +0 -6
- package/dist/es/react/src/contexts/GridContext.js.map +0 -1
- package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
- package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
- package/dist/es/react/src/contexts/NexusContext.js +0 -13
- package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
- package/dist/es/react/src/hooks/useForm.js +0 -13
- package/dist/es/react/src/hooks/useForm.js.map +0 -1
- package/dist/es/react/src/utils/renderTreeNode.js +0 -18
- package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
- package/dist/es/react/src/utils/resolveColSpan.js +0 -10
- package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
- package/src/Canvas.tsx +0 -1111
- package/src/Designer.css +0 -206
- package/src/Designer.tsx +0 -254
- package/src/DesignerContext.tsx +0 -192
- package/src/Palette.tsx +0 -261
- package/src/PropertyPanel.tsx +0 -486
- package/src/ResizablePanel.tsx +0 -210
- package/src/catalog.ts +0 -527
- package/src/index.ts +0 -25
- package/src/property/basic-property.ts +0 -126
- package/src/schemaUtils.test.ts +0 -261
- package/src/schemaUtils.ts +0 -627
- package/src/types.ts +0 -38
- package/src/vite-env.d.ts +0 -1
- package/src/widgets/BindEditor.tsx +0 -105
- package/src/widgets/CodeEditor.tsx +0 -114
- package/src/widgets/DefaultEditor.tsx +0 -73
- package/src/widgets/DependenciesEditor.tsx +0 -56
- package/src/widgets/EnumEditor.tsx +0 -66
- package/src/widgets/ExpressionBuilder.tsx +0 -509
- package/src/widgets/ExpressionSwitch.tsx +0 -62
- package/src/widgets/OptionsEditor.tsx +0 -141
- package/src/widgets/ReactionsEditor.tsx +0 -532
- package/src/widgets/RemoteDataEditor.tsx +0 -382
- package/src/widgets/ValidateEditor.tsx +0 -162
- package/src/widgets/expressionModel.test.ts +0 -262
- package/src/widgets/expressionModel.ts +0 -573
- package/src/widgets/index.ts +0 -45
- package/src/widgets/reactionsModel.ts +0 -190
- package/src/widgets/useFormDataFields.ts +0 -21
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { NexusEngine as l, AsyncValidatorPlugin as o } from "@xbeeant/form-engine";
|
|
2
|
-
class c {
|
|
3
|
-
engine;
|
|
4
|
-
formElementRef;
|
|
5
|
-
/** 稳定的 getter:每次 submit 执行时从外部 ref 读取最新的 onFinish/onFinishFailed 回调 */
|
|
6
|
-
getOnFinish;
|
|
7
|
-
getOnFinishFailed;
|
|
8
|
-
removeHiddenData = !0;
|
|
9
|
-
watchers = /* @__PURE__ */ new Map();
|
|
10
|
-
globalWatcher = null;
|
|
11
|
-
constructor(e) {
|
|
12
|
-
this.engine = e ?? new l(), this.engine.hasPlugin("async-validator") || this.engine.use(new o(this.engine)), this.formElementRef = {
|
|
13
|
-
current: null
|
|
14
|
-
}, this.getOnFinish = () => () => {
|
|
15
|
-
}, this.getOnFinishFailed = () => () => {
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
/** 内部:首次绑定表单 DOM + 回调 getter(由 NexusForm 在挂载时调用一次) */
|
|
19
|
-
_bind(e, t, i) {
|
|
20
|
-
this.formElementRef.current = e, this.getOnFinish = t, this.getOnFinishFailed = i, this.engine.registerOnFieldValueChange(
|
|
21
|
-
(n, r) => this._onFieldValueChange(n, r)
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
/** 内部:同步 watch / removeHiddenData 配置(由 NexusForm 在它们变化时调用) */
|
|
25
|
-
_syncConfig(e) {
|
|
26
|
-
if (e.removeHiddenData !== void 0 && (this.removeHiddenData = e.removeHiddenData), e.watch) {
|
|
27
|
-
this.watchers.clear(), this.globalWatcher = null;
|
|
28
|
-
for (const [t, i] of Object.entries(e.watch))
|
|
29
|
-
t === "#" ? this.globalWatcher = i : this.watchers.set(t, i);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/** 内部:值变更时调用(由 Engine 通知) */
|
|
33
|
-
_onFieldValueChange(e, t) {
|
|
34
|
-
const i = this.engine.getFormData(), n = this.removeHiddenData ? i : this.engine.getAllFormData();
|
|
35
|
-
this.globalWatcher && this.globalWatcher(n, n, e);
|
|
36
|
-
const r = this.watchers.get(e);
|
|
37
|
-
r && r(t, n);
|
|
38
|
-
}
|
|
39
|
-
/** 内部:获取 Engine 实例 */
|
|
40
|
-
_getEngine() {
|
|
41
|
-
return this.engine;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* 获取底层 Engine 实例(用于跨表单联动:linkForm / setFormId 等)
|
|
45
|
-
*/
|
|
46
|
-
getEngine() {
|
|
47
|
-
return this.engine;
|
|
48
|
-
}
|
|
49
|
-
async submit() {
|
|
50
|
-
const e = await this.engine.validate();
|
|
51
|
-
if (e.size > 0) {
|
|
52
|
-
this.focusFirstError(e), this.getOnFinishFailed()?.(e);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const t = this.removeHiddenData ? this.engine.getFormData() : this.engine.getAllFormData();
|
|
56
|
-
await this.getOnFinish()?.(t);
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 定位到第一个校验失败的字段:按 DOM 渲染顺序查找(保证视觉上的"第一个"),
|
|
60
|
-
* 滚动入视并聚焦其内部可交互控件。
|
|
61
|
-
*/
|
|
62
|
-
focusFirstError(e) {
|
|
63
|
-
const t = this.formElementRef.current;
|
|
64
|
-
if (!t || e.size === 0)
|
|
65
|
-
return;
|
|
66
|
-
const i = new Set(e.keys());
|
|
67
|
-
requestAnimationFrame(() => {
|
|
68
|
-
const r = t.querySelectorAll("[data-nexus-field]");
|
|
69
|
-
for (const s of Array.from(r)) {
|
|
70
|
-
const a = s.getAttribute("data-nexus-field");
|
|
71
|
-
if (!a || !i.has(a))
|
|
72
|
-
continue;
|
|
73
|
-
s.scrollIntoView({ behavior: "smooth", block: "center" }), s.querySelector(
|
|
74
|
-
'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
75
|
-
)?.focus();
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
resetFields() {
|
|
81
|
-
this.engine.reset();
|
|
82
|
-
}
|
|
83
|
-
setErrorFields(e) {
|
|
84
|
-
this.engine.setErrorFields(e);
|
|
85
|
-
}
|
|
86
|
-
setValues(e) {
|
|
87
|
-
this.engine.setFieldValues(e);
|
|
88
|
-
}
|
|
89
|
-
setValueByPath(e, t) {
|
|
90
|
-
this.engine.setFieldValue(e, t);
|
|
91
|
-
}
|
|
92
|
-
setSchemaByPath(e, t) {
|
|
93
|
-
this.engine.setSchemaByPath(e, t);
|
|
94
|
-
}
|
|
95
|
-
setSchema(e) {
|
|
96
|
-
this.engine.setSchema(e);
|
|
97
|
-
}
|
|
98
|
-
getValues(e) {
|
|
99
|
-
return this.engine.getFormData(e);
|
|
100
|
-
}
|
|
101
|
-
getHiddenValues() {
|
|
102
|
-
return this.engine.getHiddenValues();
|
|
103
|
-
}
|
|
104
|
-
/** 获取所有字段值(含 hidden) */
|
|
105
|
-
getAllValues() {
|
|
106
|
-
return this.engine.getAllFormData();
|
|
107
|
-
}
|
|
108
|
-
getValueByPath(e) {
|
|
109
|
-
return this.engine.getFieldValue(e);
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* 注册字段校验逻辑
|
|
113
|
-
* @param path 字段路径(如 'username')
|
|
114
|
-
* @param validator 校验函数,返回错误消息数组(空数组表示通过)
|
|
115
|
-
*/
|
|
116
|
-
registerValidator(e, t) {
|
|
117
|
-
this.engine.registerFieldValidator(e, t);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* 注销字段校验逻辑(按函数引用移除)
|
|
121
|
-
* 与 registerValidator 配对;widget 组件卸载时清理,避免校验器累积
|
|
122
|
-
*/
|
|
123
|
-
unregisterValidator(e, t) {
|
|
124
|
-
this.engine.unregisterFieldValidator(e, t);
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* 实时重校验指定字段(同步)
|
|
128
|
-
* 供 widget 组件内部状态变化(非字段值变化)时主动刷新错误态
|
|
129
|
-
*/
|
|
130
|
-
revalidateField(e) {
|
|
131
|
-
this.engine.validateField(e, { trigger: "change" });
|
|
132
|
-
}
|
|
133
|
-
getSchema() {
|
|
134
|
-
return this.engine.getSchema();
|
|
135
|
-
}
|
|
136
|
-
removeErrorField(e) {
|
|
137
|
-
this.engine.removeErrorField(e);
|
|
138
|
-
}
|
|
139
|
-
scrollToPath(e) {
|
|
140
|
-
this.formElementRef.current?.querySelector(
|
|
141
|
-
`[data-nexus-field="${e}"]`
|
|
142
|
-
)?.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
143
|
-
}
|
|
144
|
-
getFieldError(e) {
|
|
145
|
-
return this.engine.getFieldError(e);
|
|
146
|
-
}
|
|
147
|
-
getFieldsError() {
|
|
148
|
-
return this.engine.getFieldsError();
|
|
149
|
-
}
|
|
150
|
-
validateFields(e) {
|
|
151
|
-
return this.engine.validate(e);
|
|
152
|
-
}
|
|
153
|
-
getFieldState(e) {
|
|
154
|
-
return this.engine.getFieldState(e);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
export {
|
|
158
|
-
c as FormController
|
|
159
|
-
};
|
|
160
|
-
//# sourceMappingURL=FormController.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormController.js","sources":["../../../../../../react/src/components/FormController.ts"],"sourcesContent":["import type {\n FieldState,\n NexusFormInstance,\n NexusSchema,\n} from '@xbeeant/form-engine';\nimport { AsyncValidatorPlugin, NexusEngine } from '@xbeeant/form-engine';\nimport type { RefObject } from 'react';\n\n/**\n * FormController — 包裹 Engine,暴露 Form 实例 API\n */\nexport class FormController implements NexusFormInstance {\n private engine: NexusEngine;\n private formElementRef: RefObject<HTMLFormElement | null>;\n /** 稳定的 getter:每次 submit 执行时从外部 ref 读取最新的 onFinish/onFinishFailed 回调 */\n private getOnFinish: () => (\n data: Record<string, unknown>,\n ) => void | Promise<void>;\n private getOnFinishFailed: () => (errors: Map<string, string[]>) => void;\n private removeHiddenData: boolean = true;\n private watchers: Map<\n string,\n (value: unknown, allValues: Record<string, unknown>) => void\n > = new Map();\n private globalWatcher:\n | ((\n value: Record<string, unknown>,\n allValues: Record<string, unknown>,\n changedPath?: string,\n ) => void)\n | null = null;\n\n constructor(engine?: NexusEngine) {\n this.engine = engine ?? new NexusEngine();\n // 默认注入异步校验器插件:useFieldValidator / registerFieldValidator\n // 注册的异步校验器即可在字段值变化时被触发(防抖调度,与默认 'change' trigger 对齐)。\n // 外部已注入同名插件时不重复注入(hasPlugin 幂等)。\n if (!this.engine.hasPlugin('async-validator')) {\n this.engine.use(new AsyncValidatorPlugin(this.engine));\n }\n this.formElementRef = {\n current: null,\n } as RefObject<HTMLFormElement | null>;\n this.getOnFinish = () => () => {};\n this.getOnFinishFailed = () => () => {};\n }\n\n /** 内部:首次绑定表单 DOM + 回调 getter(由 NexusForm 在挂载时调用一次) */\n _bind(\n formEl: HTMLFormElement | null,\n getOnFinish: () => (data: Record<string, unknown>) => void | Promise<void>,\n getOnFinishFailed: () => (errors: Map<string, string[]>) => void,\n ): void {\n (this.formElementRef as { current: HTMLFormElement | null }).current =\n formEl;\n this.getOnFinish = getOnFinish;\n this.getOnFinishFailed = getOnFinishFailed;\n // 注册值变更回调到 Engine,只需一次(回调闭包引用了稳定的实例)\n this.engine.registerOnFieldValueChange((path, value) =>\n this._onFieldValueChange(path, value),\n );\n }\n\n /** 内部:同步 watch / removeHiddenData 配置(由 NexusForm 在它们变化时调用) */\n _syncConfig(config: {\n removeHiddenData?: boolean;\n watch?: {\n [path: string]: (\n value: unknown,\n allValues: Record<string, unknown>,\n changedPath?: string,\n ) => void;\n };\n }): void {\n if (config.removeHiddenData !== undefined) {\n this.removeHiddenData = config.removeHiddenData;\n }\n if (config.watch) {\n this.watchers.clear();\n this.globalWatcher = null;\n for (const [path, fn] of Object.entries(config.watch)) {\n if (path === '#') {\n this.globalWatcher = fn;\n } else {\n this.watchers.set(path, fn);\n }\n }\n }\n }\n\n /** 内部:值变更时调用(由 Engine 通知) */\n _onFieldValueChange(path: string, value: unknown): void {\n const allValues = this.engine.getFormData();\n const globalData = this.removeHiddenData\n ? allValues\n : this.engine.getAllFormData();\n\n // 全局 watcher(# 监听所有字段变化,value 即为全部表单值;\n // 第三参携带本次实际变更的字段路径,供清空值场景区分「未赋值默认值」与「用户主动清空」)\n if (this.globalWatcher) {\n this.globalWatcher(globalData, globalData, path);\n }\n // 路径匹配的 watcher\n const fn = this.watchers.get(path);\n if (fn) {\n fn(value, globalData);\n }\n }\n\n /** 内部:获取 Engine 实例 */\n _getEngine(): NexusEngine {\n return this.engine;\n }\n\n /**\n * 获取底层 Engine 实例(用于跨表单联动:linkForm / setFormId 等)\n */\n getEngine(): NexusEngine {\n return this.engine;\n }\n\n async submit(): Promise<void> {\n const errors = await this.engine.validate();\n if (errors.size > 0) {\n this.focusFirstError(errors);\n this.getOnFinishFailed()?.(errors);\n return;\n }\n const formData = this.removeHiddenData\n ? this.engine.getFormData()\n : this.engine.getAllFormData();\n await this.getOnFinish()?.(formData);\n }\n\n /**\n * 定位到第一个校验失败的字段:按 DOM 渲染顺序查找(保证视觉上的\"第一个\"),\n * 滚动入视并聚焦其内部可交互控件。\n */\n private focusFirstError(errors: Map<string, string[]>): void {\n const formEl = this.formElementRef.current;\n if (!formEl || errors.size === 0) {\n return;\n }\n\n const errorPaths = new Set(errors.keys());\n\n const focus = () => {\n const fieldEls =\n formEl.querySelectorAll<HTMLElement>('[data-nexus-field]');\n for (const el of Array.from(fieldEls)) {\n const path = el.getAttribute('data-nexus-field');\n if (!path || !errorPaths.has(path)) {\n continue;\n }\n\n el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n const focusable = el.querySelector<HTMLElement>(\n 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n );\n focusable?.focus();\n return;\n }\n };\n\n // 等待 React 将错误态刷到 DOM 后再定位,保证错误提示已可见\n requestAnimationFrame(focus);\n }\n\n resetFields(): void {\n this.engine.reset();\n }\n\n setErrorFields(errors: Array<{ path: string; errors: string[] }>): void {\n this.engine.setErrorFields(errors);\n }\n\n setValues(values: Record<string, unknown>): void {\n this.engine.setFieldValues(values);\n }\n\n setValueByPath(path: string, value: unknown): void {\n this.engine.setFieldValue(path, value);\n }\n\n setSchemaByPath(path: string, patch: Record<string, unknown>): void {\n this.engine.setSchemaByPath(path, patch);\n }\n\n setSchema(schema: NexusSchema): void {\n this.engine.setSchema(schema);\n }\n\n getValues(paths?: string[]): Record<string, unknown> {\n return this.engine.getFormData(paths);\n }\n\n getHiddenValues(): Record<string, unknown> {\n return this.engine.getHiddenValues();\n }\n\n /** 获取所有字段值(含 hidden) */\n getAllValues(): Record<string, unknown> {\n return this.engine.getAllFormData();\n }\n\n getValueByPath(path: string): unknown {\n return this.engine.getFieldValue(path);\n }\n\n /**\n * 注册字段校验逻辑\n * @param path 字段路径(如 'username')\n * @param validator 校验函数,返回错误消息数组(空数组表示通过)\n */\n registerValidator(\n path: string,\n validator: (\n value: unknown,\n formData: Record<string, unknown>,\n ) => string[] | Promise<string[]>,\n ): void {\n // 注册到 Engine(validate 与 实时校验 统一由 Engine 执行)\n this.engine.registerFieldValidator(path, validator);\n }\n\n /**\n * 注销字段校验逻辑(按函数引用移除)\n * 与 registerValidator 配对;widget 组件卸载时清理,避免校验器累积\n */\n unregisterValidator(\n path: string,\n validator: (\n value: unknown,\n formData: Record<string, unknown>,\n ) => string[] | Promise<string[]>,\n ): void {\n this.engine.unregisterFieldValidator(path, validator);\n }\n\n /**\n * 实时重校验指定字段(同步)\n * 供 widget 组件内部状态变化(非字段值变化)时主动刷新错误态\n */\n revalidateField(path: string): void {\n this.engine.validateField(path, { trigger: 'change' });\n }\n\n getSchema(): NexusSchema | null {\n return this.engine.getSchema();\n }\n\n removeErrorField(path: string): void {\n this.engine.removeErrorField(path);\n }\n\n scrollToPath(path: string): void {\n const el = this.formElementRef.current?.querySelector(\n `[data-nexus-field=\"${path}\"]`,\n );\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n getFieldError(path: string): string[] {\n return this.engine.getFieldError(path);\n }\n\n getFieldsError(): Map<string, string[]> {\n return this.engine.getFieldsError();\n }\n\n validateFields(paths?: string[]): Promise<Map<string, string[]>> {\n return this.engine.validate(paths);\n }\n\n getFieldState(path: string): FieldState | undefined {\n return this.engine.getFieldState(path);\n }\n}\n"],"names":["FormController","engine","NexusEngine","AsyncValidatorPlugin","formEl","getOnFinish","getOnFinishFailed","path","value","config","fn","allValues","globalData","errors","formData","errorPaths","fieldEls","el","values","patch","schema","paths","validator"],"mappings":"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"}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { jsx as o, jsxs as L } from "react/jsx-runtime";
|
|
2
|
-
import { useSyncExternalStore as $, useContext as m, useCallback as C, useMemo as W } from "react";
|
|
3
|
-
import { FieldInheritContext as D } from "../contexts/FieldInheritContext.js";
|
|
4
|
-
import { GridContext as E } from "../contexts/GridContext.js";
|
|
5
|
-
import { LayoutConfigContext as G } from "../contexts/LayoutConfigContext.js";
|
|
6
|
-
import { useNexusContext as H } from "../contexts/NexusContext.js";
|
|
7
|
-
import { resolveColSpan as I } from "../utils/resolveColSpan.js";
|
|
8
|
-
function X({ dataPath: t, layoutKey: c }) {
|
|
9
|
-
const { engine: i, config: l, form: h } = H();
|
|
10
|
-
$(
|
|
11
|
-
(n) => i.subscribeField(t, n),
|
|
12
|
-
() => i.getFieldVersion(t),
|
|
13
|
-
() => i.getFieldVersion(t)
|
|
14
|
-
);
|
|
15
|
-
const e = i.getFieldState(t), w = m(E), F = m(G), d = m(D), v = C(
|
|
16
|
-
(n) => {
|
|
17
|
-
i.setFieldValue(t, n);
|
|
18
|
-
},
|
|
19
|
-
[i, t]
|
|
20
|
-
), S = C(
|
|
21
|
-
(n) => {
|
|
22
|
-
n.currentTarget.contains(n.relatedTarget) || i.validateField(t, { trigger: "blur" });
|
|
23
|
-
},
|
|
24
|
-
[i, t]
|
|
25
|
-
), N = W(
|
|
26
|
-
() => e?.meta.enum ? e.meta.enum.map((n, s) => ({
|
|
27
|
-
value: n,
|
|
28
|
-
label: e.meta.enumNames?.[s] ?? String(n)
|
|
29
|
-
})) : e?.props.options,
|
|
30
|
-
[e?.meta.enum, e?.meta.enumNames, e?.props.options]
|
|
31
|
-
), O = W(() => {
|
|
32
|
-
const n = {};
|
|
33
|
-
if (e?.reactions) {
|
|
34
|
-
for (const s of e.reactions)
|
|
35
|
-
if (s.dependencies)
|
|
36
|
-
for (const b of s.dependencies)
|
|
37
|
-
n[b] = i.getFieldValue(b);
|
|
38
|
-
}
|
|
39
|
-
return n;
|
|
40
|
-
}, [e?.reactions, i]);
|
|
41
|
-
if (!e)
|
|
42
|
-
return i.getSnapshot() > 0 && console.warn(`[NexusField] Field not found: ${t}`), null;
|
|
43
|
-
if (d.visible === !1 || !e.visible)
|
|
44
|
-
return F.removeHidden === !0 ? null : /* @__PURE__ */ o("div", { className: "hidden", "data-nexus-hidden": t });
|
|
45
|
-
const p = l.readOnly || d.readOnly === !0 || e.readOnly, T = d.disabled === !0 || e.disabled, u = p && !!e.meta.readOnlyWidget, V = u ? e.meta.readOnlyWidget : e.meta.widget;
|
|
46
|
-
let r = i.getWidget(V);
|
|
47
|
-
if (!r && u && (r = i.getWidget(e.meta.widget)), !r)
|
|
48
|
-
return /* @__PURE__ */ L("div", { className: "text-xs text-red-500", "data-nexus-field": t, children: [
|
|
49
|
-
'⚠️ Widget "',
|
|
50
|
-
e.meta.widget,
|
|
51
|
-
'" 未注册 (path: ',
|
|
52
|
-
t,
|
|
53
|
-
")"
|
|
54
|
-
] });
|
|
55
|
-
const j = e.meta.displayType ?? l.displayType, k = e.meta.labelWidth ?? l.labelWidth, q = e.meta.column ?? l.column, f = I(e.meta.colSpan, w), g = {
|
|
56
|
-
...e.meta.width ? { width: e.meta.width, flexShrink: 0 } : {},
|
|
57
|
-
...f ? { gridColumn: `span ${f}` } : {}
|
|
58
|
-
}, x = i.getFieldWrapper(), B = {
|
|
59
|
-
label: e.meta.label,
|
|
60
|
-
title: e.meta.title,
|
|
61
|
-
description: e.meta.description,
|
|
62
|
-
errors: e.errors,
|
|
63
|
-
required: e.required,
|
|
64
|
-
extra: e.meta.extra,
|
|
65
|
-
width: e.meta.width,
|
|
66
|
-
displayType: j,
|
|
67
|
-
labelWidth: k,
|
|
68
|
-
column: q
|
|
69
|
-
}, y = {
|
|
70
|
-
dataPath: t,
|
|
71
|
-
path: t,
|
|
72
|
-
value: e.value,
|
|
73
|
-
onChange: v,
|
|
74
|
-
disabled: T,
|
|
75
|
-
readOnly: p,
|
|
76
|
-
loading: e.loading,
|
|
77
|
-
placeholder: e.meta.placeholder,
|
|
78
|
-
options: N,
|
|
79
|
-
form: h,
|
|
80
|
-
dependValues: O,
|
|
81
|
-
items: e.meta.items,
|
|
82
|
-
...e.props
|
|
83
|
-
};
|
|
84
|
-
let a;
|
|
85
|
-
return x ? a = /* @__PURE__ */ o(x, { ...B, children: /* @__PURE__ */ o(r, { ...y }) }, c) : a = /* @__PURE__ */ o(r, { ...y }, c), /* @__PURE__ */ o(
|
|
86
|
-
"div",
|
|
87
|
-
{
|
|
88
|
-
"data-nexus-field": t,
|
|
89
|
-
onBlur: S,
|
|
90
|
-
style: Object.keys(g).length > 0 ? g : void 0,
|
|
91
|
-
children: a
|
|
92
|
-
}
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
export {
|
|
96
|
-
X as NexusField
|
|
97
|
-
};
|
|
98
|
-
//# sourceMappingURL=NexusField.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusField.js","sources":["../../../../../../react/src/components/NexusField.tsx"],"sourcesContent":["import type { CSSProperties, FocusEvent, ReactElement } from 'react';\nimport { useCallback, useContext, useMemo, useSyncExternalStore } from 'react';\nimport { FieldInheritContext } from '../contexts/FieldInheritContext';\nimport { GridContext } from '../contexts/GridContext';\nimport { LayoutConfigContext } from '../contexts/LayoutConfigContext';\nimport { useNexusContext } from '../contexts/NexusContext';\nimport { resolveColSpan } from '../utils/resolveColSpan';\n\ninterface NexusFieldProps {\n dataPath: string;\n layoutKey: string;\n}\n\n/**\n * NexusField — 单个字段渲染器\n */\nexport function NexusField({ dataPath, layoutKey }: NexusFieldProps) {\n const { engine, config, form } = useNexusContext();\n // 按路径精准订阅:仅该字段版本变化时重渲染(reaction 影响其他字段不会触发本组件)\n // 第三个参数 getServerSnapshot 与 getSnapshot 一致(引擎状态同步,SSR 必需)\n useSyncExternalStore(\n (onStoreChange) => engine.subscribeField(dataPath, onStoreChange),\n () => engine.getFieldVersion(dataPath),\n () => engine.getFieldVersion(dataPath),\n );\n const state = engine.getFieldState(dataPath);\n // GridContext 必须在所有 early return 之前调用,否则会破坏 Hooks 调用顺序\n const gridCtx = useContext(GridContext);\n const layoutConfig = useContext(LayoutConfigContext);\n // 祖先对象容器(NexusObject)下发的继承属性:visible=false 时子树整体隐藏\n const inherit = useContext(FieldInheritContext);\n\n const handleChange = useCallback(\n (value: unknown) => {\n engine.setFieldValue(dataPath, value);\n },\n [engine, dataPath],\n );\n\n // 失焦触发 blur 规则校验(trigger: 'blur'):\n // React onBlur 冒泡(focusout 语义),包裹层统一处理内部控件失焦;\n // 焦点仍在字段内部(如 dateRange 双输入框间切换)时跳过。\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLDivElement>) => {\n if (e.currentTarget.contains(e.relatedTarget as Node)) {\n return;\n }\n engine.validateField(dataPath, { trigger: 'blur' });\n },\n [engine, dataPath],\n );\n\n // 从 enum + enumNames 构建选项(x-render 对齐)。\n // meta/props 引用在状态更新时保持稳定,useMemo 避免每次渲染重建数组(破坏子组件 memo)。\n // 必须在所有 early return 之前调用(Hooks 顺序规则),state 未定义时安全降级\n const options = useMemo(\n () =>\n state?.meta.enum\n ? state.meta.enum.map((value: any, index: number) => ({\n value,\n label: state.meta.enumNames?.[index] ?? String(value),\n }))\n : (state?.props.options as\n | Array<{ label: string; value: unknown } | string | number>\n | undefined),\n [state?.meta.enum, state?.meta.enumNames, state?.props.options],\n );\n\n // 从 reactions 依赖构建 dependValues,供 widget 获取关联字段值。\n // reactions 引用稳定,值在 memo 执行时读取;避免每次渲染新建对象\n const dependValues = useMemo(() => {\n const values: Record<string, unknown> = {};\n if (state?.reactions) {\n for (const reaction of state.reactions) {\n if (reaction.dependencies) {\n for (const dep of reaction.dependencies) {\n values[dep] = engine.getFieldValue(dep);\n }\n }\n }\n }\n return values;\n }, [state?.reactions, engine]);\n\n if (!state) {\n // 仅当引擎已初始化(version > 0)但字段仍未找到时才发出警告\n // 初始化过程中的短暂空状态不应报警\n if (engine.getSnapshot() > 0) {\n console.warn(`[NexusField] Field not found: ${dataPath}`);\n }\n return null;\n }\n\n // 祖先对象容器隐藏 → 子树整体不可见(与字段自身 visible 合并判断)\n if (inherit.visible === false || !state.visible) {\n // 如果父布局节点配置了 removeHidden,则不渲染占位符(移除以防止栅格塌陷)\n if (layoutConfig.removeHidden === true) {\n return null;\n }\n // 默认行为:渲染 display:none 占位符以保持布局\n return <div className='hidden' data-nexus-hidden={dataPath} />;\n }\n\n // 从 enum + enumNames 构建选项(x-render 对齐)\n const readOnly =\n config.readOnly || inherit.readOnly === true || state.readOnly;\n // 对象容器继承 disabled(父级激活时优先),与字段级 disabled 合并\n const disabled = inherit.disabled === true || state.disabled;\n\n // readOnlyWidget:指定 readOnly 生效时切换使用的渲染 widget(x-render readOnlyWidget 对齐)。\n // - 配置了 readOnlyWidget 且字段为只读时,切换渲染该 widget(readOnly 一并透传,\n // widget 按自身逻辑决定只读展示形态,如 treeSelect 的 readOnly 回显);\n // - 未配置时 readOnly 原样透传给 widget,由 widget 自身决定只读形态\n // (antd 原生 readOnly,或内置 widget 的 ReadOnlyDisplay 文本回退)。\n const wantReadOnlyWidget = readOnly && !!state.meta.readOnlyWidget;\n const widgetName = wantReadOnlyWidget\n ? state.meta.readOnlyWidget!\n : state.meta.widget;\n /** 获取UI组件库 进行渲染 **/\n let Widget = engine.getWidget(widgetName);\n // readOnlyWidget 未注册时优雅降级:退回原 widget,沿用现有只读渲染方式\n if (!Widget && wantReadOnlyWidget) {\n Widget = engine.getWidget(state.meta.widget);\n }\n\n if (!Widget) {\n return (\n <div className='text-xs text-red-500' data-nexus-field={dataPath}>\n ⚠️ Widget \"{state.meta.widget}\" 未注册 (path: {dataPath})\n </div>\n );\n }\n\n // 字段级配置优先于表单级配置\n const fieldDisplayType = state.meta.displayType ?? config.displayType;\n const fieldLabelWidth = state.meta.labelWidth ?? config.labelWidth;\n const fieldColumn = state.meta.column ?? config.column;\n\n // 布局属性作用于 NexusField 包装层而非 DOM 控件\n // - column(fieldColumn):字段内部子元素分列数(如 checkboxes/radio),传给 Widget\n // - colSpan:在父 Grid 中横跨多少列(tailwind 风格:gridColumn: span N)\n // - width:在父 Flex 布局中自身宽度(百分比或固定值),flexShrink:0 防压缩\n const effectiveColSpan = resolveColSpan(state.meta.colSpan, gridCtx);\n const wrapperStyle: CSSProperties = {\n ...(state.meta.width ? { width: state.meta.width, flexShrink: 0 } : {}),\n ...(effectiveColSpan ? { gridColumn: `span ${effectiveColSpan}` } : {}),\n };\n\n // 默认包裹:所有 widget 统一由 FieldWrapper 包裹(引擎注册,UI 层提供),\n // 仅当 label === false(字段级或表单级)时 FieldWrapper 不包裹 Form.Item。\n // 未注册 FieldWrapper(纯 react 无 ui 层)时直接渲染裸 widget。\n const FieldWrapper = engine.getFieldWrapper();\n\n // Form.Item 消费的元数据 props 剥离给 FieldWrapper,避免透传到底层 antd 控件:\n // - required: 会让 <input required> 触发浏览器原生校验\n // - errors/title/description/label/extra/width/displayType/labelWidth/column:\n // 作为未知属性透传到 DOM 会产生 React 警告\n const fieldWrapperProps = {\n label: state.meta.label,\n title: state.meta.title,\n description: state.meta.description,\n errors: state.errors,\n required: state.required,\n extra: state.meta.extra,\n width: state.meta.width,\n displayType: fieldDisplayType,\n labelWidth: fieldLabelWidth,\n column: fieldColumn,\n };\n\n // widget 仅接收控件相关 props(value/onChange/状态/选项/表单引用/自有 props)\n const widgetProps = {\n dataPath,\n path: dataPath,\n value: state.value,\n onChange: handleChange,\n disabled,\n readOnly,\n loading: state.loading,\n placeholder: state.meta.placeholder,\n options,\n form,\n dependValues,\n items: state.meta.items,\n ...state.props,\n };\n\n let control: ReactElement;\n if (FieldWrapper) {\n control = (\n <FieldWrapper key={layoutKey} {...fieldWrapperProps}>\n <Widget {...widgetProps} />\n </FieldWrapper>\n );\n } else {\n control = <Widget key={layoutKey} {...widgetProps} />;\n }\n\n return (\n <div\n data-nexus-field={dataPath}\n onBlur={handleBlur}\n style={Object.keys(wrapperStyle).length > 0 ? wrapperStyle : undefined}\n >\n {control}\n </div>\n );\n}\n"],"names":["NexusField","dataPath","layoutKey","engine","config","form","useNexusContext","useSyncExternalStore","onStoreChange","state","gridCtx","useContext","GridContext","layoutConfig","LayoutConfigContext","inherit","FieldInheritContext","handleChange","useCallback","value","handleBlur","e","options","useMemo","index","dependValues","values","reaction","dep","jsx","readOnly","disabled","wantReadOnlyWidget","widgetName","Widget","jsxs","fieldDisplayType","fieldLabelWidth","fieldColumn","effectiveColSpan","resolveColSpan","wrapperStyle","FieldWrapper","fieldWrapperProps","widgetProps","control"],"mappings":"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"}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { jsx as b, jsxs as k } from "react/jsx-runtime";
|
|
2
|
-
import { useRef as i, useEffect as l, useSyncExternalStore as Z, useMemo as y, useCallback as O } from "react";
|
|
3
|
-
import { renderTreeNode as H } from "../utils/renderTreeNode.js";
|
|
4
|
-
import { NexusFormProvider as ee } from "./NexusFormProvider.js";
|
|
5
|
-
function s() {
|
|
6
|
-
}
|
|
7
|
-
function oe({
|
|
8
|
-
form: t,
|
|
9
|
-
schema: e,
|
|
10
|
-
initialValues: D,
|
|
11
|
-
widgets: u,
|
|
12
|
-
layouts: c,
|
|
13
|
-
onFinish: M,
|
|
14
|
-
onFinishFailed: V,
|
|
15
|
-
onMount: g,
|
|
16
|
-
footer: f = !1,
|
|
17
|
-
className: $,
|
|
18
|
-
style: I,
|
|
19
|
-
children: P,
|
|
20
|
-
labelCol: a,
|
|
21
|
-
labelWidth: q,
|
|
22
|
-
colon: w,
|
|
23
|
-
label: z,
|
|
24
|
-
displayType: A,
|
|
25
|
-
readOnly: R,
|
|
26
|
-
column: B,
|
|
27
|
-
watch: m,
|
|
28
|
-
removeHiddenData: F = !0
|
|
29
|
-
}) {
|
|
30
|
-
const n = t._getEngine(), x = i(null), S = A ?? e?.displayType ?? "row", C = z ?? e?.label ?? !0, T = w ?? e?.colon, r = q ?? e?.labelWidth, E = R ?? e?.readOnly ?? !1, o = B ?? e?.column;
|
|
31
|
-
l(() => {
|
|
32
|
-
u && n.registerWidgets(u), c && n.registerLayouts(c);
|
|
33
|
-
}, [n, u, c]);
|
|
34
|
-
const N = i(!0), G = i(D);
|
|
35
|
-
l(() => {
|
|
36
|
-
e && (N.current ? (n.init(e, G.current), N.current = !1) : n.init(e, n.getFormData()));
|
|
37
|
-
}, [n, e]);
|
|
38
|
-
const h = i(g);
|
|
39
|
-
h.current = g;
|
|
40
|
-
const j = i(!1), v = !e || typeof e == "object" && Object.keys(e).length === 0;
|
|
41
|
-
l(() => {
|
|
42
|
-
j.current || v || (j.current = !0, h.current?.());
|
|
43
|
-
}, [v]);
|
|
44
|
-
const L = i(s), W = i(s);
|
|
45
|
-
L.current = M ?? s, W.current = V ?? s, l(() => {
|
|
46
|
-
t._bind(
|
|
47
|
-
x.current,
|
|
48
|
-
() => L.current,
|
|
49
|
-
() => W.current
|
|
50
|
-
);
|
|
51
|
-
}, [t]), l(() => {
|
|
52
|
-
t._syncConfig({ removeHiddenData: F, watch: m });
|
|
53
|
-
}, [t, F, m]);
|
|
54
|
-
const J = Z(
|
|
55
|
-
n.subscribeStore,
|
|
56
|
-
n.getSnapshot,
|
|
57
|
-
n.getSnapshot
|
|
58
|
-
), K = y(() => n.getRenderTree(), [n, J]), Q = O(
|
|
59
|
-
async (p) => {
|
|
60
|
-
p.preventDefault(), await t.submit();
|
|
61
|
-
},
|
|
62
|
-
[t]
|
|
63
|
-
), U = O(() => {
|
|
64
|
-
t.resetFields();
|
|
65
|
-
}, [t]);
|
|
66
|
-
let d = null;
|
|
67
|
-
f === !0 ? d = /* @__PURE__ */ k("div", { className: "mt-4", children: [
|
|
68
|
-
/* @__PURE__ */ b("button", { type: "submit", children: "提交" }),
|
|
69
|
-
" ",
|
|
70
|
-
/* @__PURE__ */ b("button", { type: "button", onClick: U, children: "重置" })
|
|
71
|
-
] }) : f && (d = f);
|
|
72
|
-
const _ = y(() => {
|
|
73
|
-
if (a)
|
|
74
|
-
return a;
|
|
75
|
-
if (r)
|
|
76
|
-
return {
|
|
77
|
-
style: {
|
|
78
|
-
width: typeof r == "number" ? `${r}px` : r
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}, [a, r]), X = y(
|
|
82
|
-
() => ({
|
|
83
|
-
labelCol: _,
|
|
84
|
-
labelWidth: r,
|
|
85
|
-
colon: T,
|
|
86
|
-
label: C,
|
|
87
|
-
displayType: S,
|
|
88
|
-
readOnly: E,
|
|
89
|
-
column: o
|
|
90
|
-
}),
|
|
91
|
-
[
|
|
92
|
-
_,
|
|
93
|
-
r,
|
|
94
|
-
T,
|
|
95
|
-
C,
|
|
96
|
-
S,
|
|
97
|
-
E,
|
|
98
|
-
o
|
|
99
|
-
]
|
|
100
|
-
);
|
|
101
|
-
return /* @__PURE__ */ b(ee, { engine: n, config: X, form: t, children: /* @__PURE__ */ k(
|
|
102
|
-
"form",
|
|
103
|
-
{
|
|
104
|
-
ref: x,
|
|
105
|
-
onSubmit: Q,
|
|
106
|
-
className: $,
|
|
107
|
-
style: {
|
|
108
|
-
...I,
|
|
109
|
-
// 当配置了 column 时,使用 CSS Grid 布局
|
|
110
|
-
...o && o > 1 ? {
|
|
111
|
-
display: "grid",
|
|
112
|
-
gridTemplateColumns: `repeat(${o}, 1fr)`,
|
|
113
|
-
gap: "0 16px"
|
|
114
|
-
} : {}
|
|
115
|
-
},
|
|
116
|
-
noValidate: !0,
|
|
117
|
-
children: [
|
|
118
|
-
K.map((p, Y) => H(p, Y)),
|
|
119
|
-
!R && d,
|
|
120
|
-
P
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
) });
|
|
124
|
-
}
|
|
125
|
-
export {
|
|
126
|
-
oe as NexusForm
|
|
127
|
-
};
|
|
128
|
-
//# sourceMappingURL=NexusForm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusForm.js","sources":["../../../../../../react/src/components/NexusForm.tsx"],"sourcesContent":["import type { NexusSchema } from '@xbeeant/form-engine';\nimport type { CSSProperties, ReactNode, SubmitEvent } from 'react';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\nimport { renderTreeNode } from '../utils/renderTreeNode';\nimport type { FormController } from './FormController';\nimport { NexusFormProvider } from './NexusFormProvider';\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\nexport interface NexusFormProps {\n /** Form 实例,由 useForm() 创建 */\n form: FormController;\n /** Schema 定义 */\n schema?: NexusSchema;\n /** 初始值 */\n initialValues?: Record<string, unknown>;\n /** 额外注册的 widget(与已注册的合并) */\n widgets?: Record<string, (props: any) => ReactNode>;\n /** 额外注册的 layout */\n layouts?: Record<string, (props: any) => ReactNode>;\n /** 提交成功回调 */\n onFinish?: (formData: Record<string, unknown>) => void | Promise<void>;\n /** 校验失败回调 */\n onFinishFailed?: (errors: Map<string, string[]>) => void;\n /**\n * 表单首次加载回调:非空 schema 首次传入并完成渲染后执行一次\n * - undefined / null / {}(无 properties)均视为「空」schema,不触发\n * - schema 由空变为非空时,于首个非空渲染提交后触发\n * - 后续 schema 变更不重复触发\n */\n onMount?: () => void;\n /** 是否显示默认 footer(提交/重置按钮),或自定义 footer */\n footer?: boolean | ReactNode;\n /** 自定义类名 */\n className?: string;\n /** 自定义样式 */\n style?: CSSProperties;\n /** 子节点 */\n children?: ReactNode;\n /**\n * 表单值变化监听\n * - key 为字段路径(如 'username'),值为回调函数\n * - 使用 '#' 作为 key 可监听所有字段变化\n * - 回调参数为 (value, allValues);全局('#')监听额外提供第三参 changedPath,\n * 即本次实际变更的字段路径(用于区分「未触碰的默认空值」与「用户主动清空」)\n */\n watch?: {\n [path: string]: (\n value: unknown,\n allValues: Record<string, unknown>,\n changedPath?: string,\n ) => void;\n };\n /**\n * 提交时是否移除 hidden 字段数据,默认 true\n * - true: submit/getValues 不包含 hidden 字段\n * - false: submit/getValues 包含所有字段(含 hidden)\n */\n removeHiddenData?: boolean;\n\n // ── 表单布局配置 ──────────────────────────────────────────────────────\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 /** 表单布局方向:'row' = horizontal, 'column' = vertical, 'inline' = inline */\n displayType?: 'row' | 'column' | 'inline';\n /** 整个表单只读,所有字段以文本展示 */\n readOnly?: boolean;\n /** 表单每行显示多少列 */\n column?: number;\n}\n\nfunction noop() {}\n\n/**\n * NexusForm — 顶层表单组件\n */\nexport function NexusForm({\n form,\n schema,\n initialValues,\n widgets,\n layouts,\n onFinish,\n onFinishFailed,\n onMount,\n footer = false,\n className,\n style,\n children,\n labelCol,\n labelWidth,\n colon,\n label,\n displayType,\n readOnly,\n column,\n watch,\n removeHiddenData = true,\n}: NexusFormProps) {\n const engine = form._getEngine();\n const formElRef = useRef<HTMLFormElement | null>(null);\n\n // Schema 顶层配置作为默认值,props 优先级更高\n const finalDisplayType = displayType ?? schema?.displayType ?? 'row';\n const finalLabel = label ?? schema?.label ?? true;\n const finalColon = colon ?? schema?.colon;\n const finalLabelWidth = labelWidth ?? schema?.labelWidth;\n const finalReadOnly = readOnly ?? schema?.readOnly ?? false;\n const finalColumn = column ?? schema?.column;\n\n // 注册额外 widgets / layouts(仅首次或引用变化时)\n useEffect(() => {\n if (widgets) {\n engine.registerWidgets(widgets);\n }\n if (layouts) {\n engine.registerLayouts(layouts);\n }\n }, [engine, widgets, layouts]);\n\n // schema 变化时重新初始化\n // 注意:initialValues 仅在首次挂载时使用,避免每次渲染都 re-init 导致\n // 已有的 errors / 用户输入被重置。后续需要更新值请使用 form.setValues()。\n const isFirstInitRef = useRef(true);\n const initialValuesRef = useRef(initialValues);\n useEffect(() => {\n if (!schema) {\n return;\n }\n if (isFirstInitRef.current) {\n engine.init(schema, initialValuesRef.current);\n isFirstInitRef.current = false;\n } else {\n // schema 变化:保留当前已填数据,而非重置为 initialValues\n engine.init(schema, engine.getFormData());\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [engine, schema]);\n\n // onMount:首次传入「非空」schema 并完成渲染后执行一次\n // undefined / null / {}(无任何键)均视为空 schema,不触发;\n // schema 由空变为非空时,于首个非空渲染提交(useEffect)后触发。\n const onMountRef = useRef(onMount);\n onMountRef.current = onMount;\n const onMountFiredRef = useRef(false);\n const isSchemaEmpty =\n !schema || (typeof schema === 'object' && Object.keys(schema).length === 0);\n useEffect(() => {\n if (onMountFiredRef.current || isSchemaEmpty) {\n return;\n }\n onMountFiredRef.current = true;\n onMountRef.current?.();\n }, [isSchemaEmpty]);\n\n // 绑定 form controller\n // 使用 ref 持有 onFinish / onFinishFailed,避免每次 re-render 都造成绑定逻辑重复执行\n const onFinishRef =\n useRef<(data: Record<string, unknown>) => void | Promise<void>>(noop);\n const onFinishFailedRef =\n useRef<(errors: Map<string, string[]>) => void>(noop);\n onFinishRef.current = onFinish ?? noop;\n onFinishFailedRef.current = onFinishFailed ?? noop;\n\n // 只在挂载时绑定一次:传入「稳定的 getter」,让 FormController 在 submit 时读取最新回调\n useEffect(() => {\n form._bind(\n formElRef.current,\n () => onFinishRef.current,\n () => onFinishFailedRef.current,\n );\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [form]);\n\n // watch / removeHiddenData 变化时单独同步(不重置其他绑定)\n useEffect(() => {\n form._syncConfig({ removeHiddenData, watch });\n }, [form, removeHiddenData, watch]);\n\n const _version = useSyncExternalStore(\n engine.subscribeStore,\n engine.getSnapshot,\n engine.getSnapshot,\n );\n // 依赖 _version:engine.init() / setSchema() 会 bump version,\n // 需要在此后重新读取 renderTree(首次渲染时 engine 尚未 init,renderTree 为空)\n // biome-ignore lint/correctness/useExhaustiveDependencies: _version 是 renderTree 失效信号(engine 内部状态,静态分析不可见)\n const renderTree = useMemo(() => engine.getRenderTree(), [engine, _version]);\n\n const handleSubmit = useCallback(\n async (e: SubmitEvent) => {\n e.preventDefault();\n await form.submit();\n },\n [form],\n );\n\n const handleReset = useCallback(() => {\n form.resetFields();\n }, [form]);\n\n // footer 渲染\n let footerNode: ReactNode = null;\n if (footer === true) {\n footerNode = (\n <div className='mt-4'>\n <button type='submit'>提交</button>{' '}\n <button type='button' onClick={handleReset}>\n 重置\n </button>\n </div>\n );\n } else if (footer) {\n footerNode = footer;\n }\n\n // labelCol: 合并 labelWidth 快捷方式\n const mergedLabelCol = useMemo(() => {\n if (labelCol) {\n return labelCol;\n }\n if (finalLabelWidth) {\n return {\n style: {\n width:\n typeof finalLabelWidth === 'number'\n ? `${finalLabelWidth}px`\n : finalLabelWidth,\n },\n };\n }\n return undefined;\n }, [labelCol, finalLabelWidth]);\n\n // NexusFormConfig 传递给 Context(由 ui 层消费,自行实现布局)\n const formConfig = useMemo<NexusFormConfig>(\n () => ({\n labelCol: mergedLabelCol,\n labelWidth: finalLabelWidth,\n colon: finalColon,\n label: finalLabel,\n displayType: finalDisplayType,\n readOnly: finalReadOnly,\n column: finalColumn,\n }),\n [\n mergedLabelCol,\n finalLabelWidth,\n finalColon,\n finalLabel,\n finalDisplayType,\n finalReadOnly,\n finalColumn,\n ],\n );\n\n return (\n <NexusFormProvider engine={engine} config={formConfig} form={form}>\n <form\n ref={formElRef}\n onSubmit={handleSubmit}\n className={className}\n style={{\n ...style,\n // 当配置了 column 时,使用 CSS Grid 布局\n ...(finalColumn && finalColumn > 1\n ? {\n display: 'grid',\n gridTemplateColumns: `repeat(${finalColumn}, 1fr)`,\n gap: '0 16px',\n }\n : {}),\n }}\n noValidate\n >\n {renderTree.map((node, index) => renderTreeNode(node, index))}\n {!readOnly && footerNode}\n {children}\n </form>\n </NexusFormProvider>\n );\n}\n"],"names":["noop","NexusForm","form","schema","initialValues","widgets","layouts","onFinish","onFinishFailed","onMount","footer","className","style","children","labelCol","labelWidth","colon","label","displayType","readOnly","column","watch","removeHiddenData","engine","formElRef","useRef","finalDisplayType","finalLabel","finalColon","finalLabelWidth","finalReadOnly","finalColumn","useEffect","isFirstInitRef","initialValuesRef","onMountRef","onMountFiredRef","isSchemaEmpty","onFinishRef","onFinishFailedRef","_version","useSyncExternalStore","renderTree","useMemo","handleSubmit","useCallback","e","handleReset","footerNode","jsxs","jsx","mergedLabelCol","formConfig","NexusFormProvider","node","index","renderTreeNode"],"mappings":"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"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { jsx as u } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo as s } from "react";
|
|
3
|
-
import { NexusContext as x } from "../contexts/NexusContext.js";
|
|
4
|
-
function d({
|
|
5
|
-
engine: r,
|
|
6
|
-
config: o,
|
|
7
|
-
form: t,
|
|
8
|
-
children: e
|
|
9
|
-
}) {
|
|
10
|
-
const m = s(
|
|
11
|
-
() => ({ engine: r, config: o, form: t }),
|
|
12
|
-
[r, o, t]
|
|
13
|
-
);
|
|
14
|
-
return /* @__PURE__ */ u(x.Provider, { value: m, children: e });
|
|
15
|
-
}
|
|
16
|
-
export {
|
|
17
|
-
d as NexusFormProvider
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=NexusFormProvider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusFormProvider.js","sources":["../../../../../../react/src/components/NexusFormProvider.tsx"],"sourcesContent":["import type { NexusEngine } from '@xbeeant/form-engine';\nimport type { ReactNode } from 'react';\nimport { useMemo } from 'react';\nimport { NexusContext } from '../contexts/NexusContext';\nimport type { FormController } from './FormController';\nimport type { NexusFormConfig } from './NexusForm';\n\ninterface NexusFormProviderProps {\n engine: NexusEngine;\n config: NexusFormConfig;\n form: FormController;\n children: ReactNode;\n}\n\nexport function NexusFormProvider({\n engine,\n config,\n form,\n children,\n}: NexusFormProviderProps) {\n const value = useMemo(\n () => ({ engine, config, form }),\n [engine, config, form],\n );\n return (\n <NexusContext.Provider value={value}>{children}</NexusContext.Provider>\n );\n}\n"],"names":["NexusFormProvider","engine","config","form","children","value","useMemo","jsx","NexusContext"],"mappings":";;;AAcO,SAASA,EAAkB;AAAA,EAChC,QAAAC;AAAA,EACA,QAAAC;AAAA,EACA,MAAAC;AAAA,EACA,UAAAC;AACF,GAA2B;AACzB,QAAMC,IAAQC;AAAA,IACZ,OAAO,EAAE,QAAAL,GAAQ,QAAAC,GAAQ,MAAAC;IACzB,CAACF,GAAQC,GAAQC,CAAI;AAAA,EAAA;AAEvB,SACE,gBAAAI,EAACC,EAAa,UAAb,EAAsB,OAAAH,GAAe,UAAAD,EAAA,CAAS;AAEnD;"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { jsx as e, jsxs as u } from "react/jsx-runtime";
|
|
2
|
-
import { useContext as y, useMemo as f } from "react";
|
|
3
|
-
import { GridContext as h } from "../contexts/GridContext.js";
|
|
4
|
-
import { LayoutConfigContext as p } from "../contexts/LayoutConfigContext.js";
|
|
5
|
-
import { useNexusContext as v } from "../contexts/NexusContext.js";
|
|
6
|
-
import { renderTreeNode as x } from "../utils/renderTreeNode.js";
|
|
7
|
-
import { resolveColSpan as C } from "../utils/resolveColSpan.js";
|
|
8
|
-
function O({ node: t }) {
|
|
9
|
-
const { engine: n } = v(), r = n.getLayout(t.type), i = t.children.map(
|
|
10
|
-
(c, d) => x(c, d)
|
|
11
|
-
), a = y(h), l = C(t.props.colSpan, a), o = {
|
|
12
|
-
...l ? { gridColumn: `span ${l}` } : {},
|
|
13
|
-
...t.props.width ? { width: t.props.width, flexShrink: 0 } : {}
|
|
14
|
-
}, s = f(
|
|
15
|
-
() => ({ removeHidden: t.props.removeHidden }),
|
|
16
|
-
[t.props.removeHidden]
|
|
17
|
-
);
|
|
18
|
-
if (!r)
|
|
19
|
-
return /* @__PURE__ */ e(p.Provider, { value: s, children: /* @__PURE__ */ u(
|
|
20
|
-
"div",
|
|
21
|
-
{
|
|
22
|
-
"data-nexus-layout": t.type,
|
|
23
|
-
className: "mb-4",
|
|
24
|
-
style: Object.keys(o).length > 0 ? o : void 0,
|
|
25
|
-
children: [
|
|
26
|
-
t.title && /* @__PURE__ */ e("div", { className: "mb-2 font-bold", children: t.title }),
|
|
27
|
-
i
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
) });
|
|
31
|
-
const {
|
|
32
|
-
displayType: g,
|
|
33
|
-
labelWidth: w,
|
|
34
|
-
colSpan: b,
|
|
35
|
-
width: S,
|
|
36
|
-
...m
|
|
37
|
-
} = t.props;
|
|
38
|
-
return /* @__PURE__ */ e(p.Provider, { value: s, children: /* @__PURE__ */ e(
|
|
39
|
-
"div",
|
|
40
|
-
{
|
|
41
|
-
style: Object.keys(o).length > 0 ? o : void 0,
|
|
42
|
-
children: /* @__PURE__ */ e(r, { ...m, node: t, title: t.title, children: i })
|
|
43
|
-
}
|
|
44
|
-
) });
|
|
45
|
-
}
|
|
46
|
-
export {
|
|
47
|
-
O as NexusLayout
|
|
48
|
-
};
|
|
49
|
-
//# sourceMappingURL=NexusLayout.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusLayout.js","sources":["../../../../../../react/src/components/NexusLayout.tsx"],"sourcesContent":["import type { RenderLayoutNode } from '@xbeeant/form-engine';\nimport type { CSSProperties } from 'react';\nimport { useContext, useMemo } from 'react';\n\nimport { GridContext } from '../contexts/GridContext';\nimport {\n LayoutConfigContext,\n type LayoutConfigContextValue,\n} from '../contexts/LayoutConfigContext';\nimport { useNexusContext } from '../contexts/NexusContext';\nimport { renderTreeNode } from '../utils/renderTreeNode';\nimport { resolveColSpan } from '../utils/resolveColSpan';\n\ninterface NexusLayoutProps {\n node: RenderLayoutNode;\n}\n\n/**\n * NexusLayout — 布局容器渲染器\n */\nexport function NexusLayout({ node }: NexusLayoutProps) {\n const { engine } = useNexusContext();\n\n const LayoutComponent = engine.getLayout(node.type);\n\n const children = node.children.map((child, index) =>\n renderTreeNode(child, index),\n );\n\n // 布局容器在父 Grid/Flex 中的跨列/宽度(与 NexusField wrapper 一致)\n const gridCtx = useContext(GridContext);\n const effectiveColSpan = resolveColSpan(node.props.colSpan, gridCtx);\n const wrapperStyle: CSSProperties = {\n ...(effectiveColSpan ? { gridColumn: `span ${effectiveColSpan}` } : {}),\n ...(node.props.width ? { width: node.props.width, flexShrink: 0 } : {}),\n };\n\n const layoutConfigValue = useMemo<LayoutConfigContextValue>(\n () => ({ removeHidden: node.props.removeHidden }),\n [node.props.removeHidden],\n );\n\n if (!LayoutComponent) {\n return (\n <LayoutConfigContext.Provider value={layoutConfigValue}>\n <div\n data-nexus-layout={node.type}\n className='mb-4'\n style={\n Object.keys(wrapperStyle).length > 0 ? wrapperStyle : undefined\n }\n >\n {node.title && <div className='mb-2 font-bold'>{node.title}</div>}\n {children}\n </div>\n </LayoutConfigContext.Provider>\n );\n }\n\n // 剥离布局配置属性(displayType / labelWidth / colSpan / width 是布局配置,\n // 不应透传到布局组件的 DOM 元素,否则触发 React unknown prop 警告)\n const {\n displayType: _dt,\n labelWidth: _lw,\n colSpan: _csp,\n width: _w,\n ...layoutProps\n } = node.props;\n\n return (\n <LayoutConfigContext.Provider value={layoutConfigValue}>\n <div\n style={Object.keys(wrapperStyle).length > 0 ? wrapperStyle : undefined}\n >\n <LayoutComponent {...layoutProps} node={node} title={node.title}>\n {children}\n </LayoutComponent>\n </div>\n </LayoutConfigContext.Provider>\n );\n}\n"],"names":["NexusLayout","node","engine","useNexusContext","LayoutComponent","children","child","index","renderTreeNode","gridCtx","useContext","GridContext","effectiveColSpan","resolveColSpan","wrapperStyle","layoutConfigValue","useMemo","jsx","LayoutConfigContext","jsxs","_dt","_lw","_csp","_w","layoutProps"],"mappings":";;;;;;;AAoBO,SAASA,EAAY,EAAE,MAAAC,KAA0B;AACtD,QAAM,EAAE,QAAAC,EAAA,IAAWC,EAAA,GAEbC,IAAkBF,EAAO,UAAUD,EAAK,IAAI,GAE5CI,IAAWJ,EAAK,SAAS;AAAA,IAAI,CAACK,GAAOC,MACzCC,EAAeF,GAAOC,CAAK;AAAA,EAAA,GAIvBE,IAAUC,EAAWC,CAAW,GAChCC,IAAmBC,EAAeZ,EAAK,MAAM,SAASQ,CAAO,GAC7DK,IAA8B;AAAA,IAClC,GAAIF,IAAmB,EAAE,YAAY,QAAQA,CAAgB,GAAA,IAAO,CAAA;AAAA,IACpE,GAAIX,EAAK,MAAM,QAAQ,EAAE,OAAOA,EAAK,MAAM,OAAO,YAAY,MAAM,CAAA;AAAA,EAAC,GAGjEc,IAAoBC;AAAA,IACxB,OAAO,EAAE,cAAcf,EAAK,MAAM,aAAA;AAAA,IAClC,CAACA,EAAK,MAAM,YAAY;AAAA,EAAA;AAG1B,MAAI,CAACG;AACH,WACE,gBAAAa,EAACC,EAAoB,UAApB,EAA6B,OAAOH,GACnC,UAAA,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,qBAAmBlB,EAAK;AAAA,QACxB,WAAU;AAAA,QACV,OACE,OAAO,KAAKa,CAAY,EAAE,SAAS,IAAIA,IAAe;AAAA,QAGvD,UAAA;AAAA,UAAAb,EAAK,SAAS,gBAAAgB,EAAC,OAAA,EAAI,WAAU,kBAAkB,YAAK,OAAM;AAAA,UAC1DZ;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAEL;AAMJ,QAAM;AAAA,IACJ,aAAae;AAAA,IACb,YAAYC;AAAA,IACZ,SAASC;AAAA,IACT,OAAOC;AAAA,IACP,GAAGC;AAAA,EAAA,IACDvB,EAAK;AAET,SACE,gBAAAgB,EAACC,EAAoB,UAApB,EAA6B,OAAOH,GACnC,UAAA,gBAAAE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAO,OAAO,KAAKH,CAAY,EAAE,SAAS,IAAIA,IAAe;AAAA,MAE7D,UAAA,gBAAAG,EAACb,KAAiB,GAAGoB,GAAa,MAAAvB,GAAY,OAAOA,EAAK,OACvD,UAAAI,EAAA,CACH;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { jsx as r, jsxs as u } from "react/jsx-runtime";
|
|
2
|
-
import { useContext as g, useSyncExternalStore as v, useState as x } from "react";
|
|
3
|
-
import { FieldInheritContext as h } from "../contexts/FieldInheritContext.js";
|
|
4
|
-
import { useNexusContext as y } from "../contexts/NexusContext.js";
|
|
5
|
-
import { renderTreeNode as C } from "../utils/renderTreeNode.js";
|
|
6
|
-
function O({ node: e }) {
|
|
7
|
-
const { engine: i, config: a } = y(), s = g(h);
|
|
8
|
-
v(
|
|
9
|
-
(t) => i.subscribeField(e.dataPath, t),
|
|
10
|
-
() => i.getFieldVersion(e.dataPath),
|
|
11
|
-
() => i.getFieldVersion(e.dataPath)
|
|
12
|
-
);
|
|
13
|
-
const n = i.getFieldState(e.dataPath), [o, m] = x(!1), l = (a.column ?? 1) > 1, d = l ? { gridTemplateColumns: `repeat(${a.column}, 1fr)` } : {}, c = {
|
|
14
|
-
disabled: s.disabled ?? (n?.disabled === !0 ? !0 : void 0),
|
|
15
|
-
readOnly: s.readOnly ?? (n?.readOnly === !0 ? !0 : void 0),
|
|
16
|
-
visible: s.visible === !1 || n?.visible === !1 ? !1 : void 0
|
|
17
|
-
}, p = c.visible === !1, b = () => m((t) => !t);
|
|
18
|
-
return /* @__PURE__ */ r(h.Provider, { value: c, children: /* @__PURE__ */ u(
|
|
19
|
-
"div",
|
|
20
|
-
{
|
|
21
|
-
"data-nexus-object": e.dataPath,
|
|
22
|
-
className: `mb-4 ${p ? "hidden" : ""}`,
|
|
23
|
-
children: [
|
|
24
|
-
e.title && /* @__PURE__ */ u(
|
|
25
|
-
"div",
|
|
26
|
-
{
|
|
27
|
-
onClick: b,
|
|
28
|
-
className: "flex gap-1 mb-2 cursor-pointer select-none border-none bg-transparent p-0 font-bold",
|
|
29
|
-
children: [
|
|
30
|
-
/* @__PURE__ */ r(
|
|
31
|
-
"svg",
|
|
32
|
-
{
|
|
33
|
-
width: "12",
|
|
34
|
-
height: "12",
|
|
35
|
-
viewBox: "0 0 24 24",
|
|
36
|
-
fill: "none",
|
|
37
|
-
stroke: "currentColor",
|
|
38
|
-
strokeWidth: "2",
|
|
39
|
-
strokeLinecap: "round",
|
|
40
|
-
strokeLinejoin: "round",
|
|
41
|
-
className: `align-middle transition-transform duration-200 ease-in-out ${o ? "rotate-0" : "rotate-90"}`,
|
|
42
|
-
"aria-hidden": "true",
|
|
43
|
-
children: /* @__PURE__ */ r("polyline", { points: "9 18 15 12 9 6" })
|
|
44
|
-
}
|
|
45
|
-
),
|
|
46
|
-
e.title
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
),
|
|
50
|
-
/* @__PURE__ */ r(
|
|
51
|
-
"div",
|
|
52
|
-
{
|
|
53
|
-
className: `${l ? "grid gap-x-4" : ""} ${o ? "hidden" : ""}`,
|
|
54
|
-
style: Object.keys(d).length > 0 ? d : void 0,
|
|
55
|
-
children: e.children.map((t, f) => C(t, f))
|
|
56
|
-
}
|
|
57
|
-
)
|
|
58
|
-
]
|
|
59
|
-
}
|
|
60
|
-
) });
|
|
61
|
-
}
|
|
62
|
-
export {
|
|
63
|
-
O as NexusObject
|
|
64
|
-
};
|
|
65
|
-
//# sourceMappingURL=NexusObject.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NexusObject.js","sources":["../../../../../../react/src/components/NexusObject.tsx"],"sourcesContent":["import type { RenderObjectNode } from '@xbeeant/form-engine';\nimport type { CSSProperties } from 'react';\nimport { useContext, useState, useSyncExternalStore } from 'react';\n\nimport { FieldInheritContext } from '../contexts/FieldInheritContext';\nimport { useNexusContext } from '../contexts/NexusContext';\nimport { renderTreeNode } from '../utils/renderTreeNode';\n\ninterface NexusObjectProps {\n node: RenderObjectNode;\n}\n\n/**\n * NexusObject — 数据对象容器(可折叠,支持 disabled/hidden/readOnly 属性配置)\n *\n * 点击 title 折叠/展开 children。\n * 折叠时仅以 display:none 隐藏 children,并不销毁(字段状态与订阅保持存活)。\n *\n * 属性继承:容器自身(Schema 布尔值 / 表达式 / setFieldState)配置的\n * disabled / readOnly / hidden 会经 FieldInheritContext 下发给子树,\n * 子树中的字段继承对应状态(disabled/readOnly 仅父级激活时生效)。\n */\nexport function NexusObject({ node }: NexusObjectProps) {\n const { engine, config } = useNexusContext();\n const parentInherit = useContext(FieldInheritContext);\n // 按路径精准订阅:自身 disabled/readOnly/visible 变化时重渲染并下发新上下文\n useSyncExternalStore(\n (onStoreChange) => engine.subscribeField(node.dataPath, onStoreChange),\n () => engine.getFieldVersion(node.dataPath),\n () => engine.getFieldVersion(node.dataPath),\n );\n const state = engine.getFieldState(node.dataPath);\n const [collapsed, setCollapsed] = useState(false);\n const isGrid = (config.column ?? 1) > 1;\n const gridStyle: CSSProperties = isGrid\n ? { gridTemplateColumns: `repeat(${config.column}, 1fr)` }\n : {};\n\n // 合并继承属性:父级已激活的状态不可被当前容器覆盖;\n // disabled/readOnly 仅携带 true(父级关闭状态不压制子级显式启用)\n const inherit = {\n disabled:\n parentInherit.disabled ?? (state?.disabled === true ? true : undefined),\n readOnly:\n parentInherit.readOnly ?? (state?.readOnly === true ? true : undefined),\n visible:\n parentInherit.visible === false || state?.visible === false\n ? false\n : undefined,\n };\n const hidden = inherit.visible === false;\n\n const toggleCollapsed = () => setCollapsed((prev) => !prev);\n\n return (\n <FieldInheritContext.Provider value={inherit}>\n <div\n data-nexus-object={node.dataPath}\n className={`mb-4 ${hidden ? 'hidden' : ''}`}\n >\n {node.title && (\n <div\n onClick={toggleCollapsed}\n className='flex gap-1 mb-2 cursor-pointer select-none border-none bg-transparent p-0 font-bold'\n >\n <svg\n width='12'\n height='12'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n className={`align-middle transition-transform duration-200 ease-in-out ${collapsed ? 'rotate-0' : 'rotate-90'}`}\n aria-hidden='true'\n >\n <polyline points='9 18 15 12 9 6' />\n </svg>\n {node.title}\n </div>\n )}\n {/* 折叠时隐藏但不卸载 children,保持字段状态与校验订阅 */}\n <div\n className={`${isGrid ? 'grid gap-x-4' : ''} ${collapsed ? 'hidden' : ''}`}\n style={Object.keys(gridStyle).length > 0 ? gridStyle : undefined}\n >\n {node.children.map((child, index) => renderTreeNode(child, index))}\n </div>\n </div>\n </FieldInheritContext.Provider>\n );\n}\n"],"names":["NexusObject","node","engine","config","useNexusContext","parentInherit","useContext","FieldInheritContext","useSyncExternalStore","onStoreChange","state","collapsed","setCollapsed","useState","isGrid","gridStyle","inherit","hidden","toggleCollapsed","prev","jsx","jsxs","child","index","renderTreeNode"],"mappings":";;;;;AAsBO,SAASA,EAAY,EAAE,MAAAC,KAA0B;AACtD,QAAM,EAAE,QAAAC,GAAQ,QAAAC,EAAA,IAAWC,EAAA,GACrBC,IAAgBC,EAAWC,CAAmB;AAEpD,EAAAC;AAAA,IACE,CAACC,MAAkBP,EAAO,eAAeD,EAAK,UAAUQ,CAAa;AAAA,IACrE,MAAMP,EAAO,gBAAgBD,EAAK,QAAQ;AAAA,IAC1C,MAAMC,EAAO,gBAAgBD,EAAK,QAAQ;AAAA,EAAA;AAE5C,QAAMS,IAAQR,EAAO,cAAcD,EAAK,QAAQ,GAC1C,CAACU,GAAWC,CAAY,IAAIC,EAAS,EAAK,GAC1CC,KAAUX,EAAO,UAAU,KAAK,GAChCY,IAA2BD,IAC7B,EAAE,qBAAqB,UAAUX,EAAO,MAAM,SAAA,IAC9C,CAAA,GAIEa,IAAU;AAAA,IACd,UACEX,EAAc,aAAaK,GAAO,aAAa,KAAO,KAAO;AAAA,IAC/D,UACEL,EAAc,aAAaK,GAAO,aAAa,KAAO,KAAO;AAAA,IAC/D,SACEL,EAAc,YAAY,MAASK,GAAO,YAAY,KAClD,KACA;AAAA,EAAA,GAEFO,IAASD,EAAQ,YAAY,IAE7BE,IAAkB,MAAMN,EAAa,CAACO,MAAS,CAACA,CAAI;AAE1D,SACE,gBAAAC,EAACb,EAAoB,UAApB,EAA6B,OAAOS,GACnC,UAAA,gBAAAK;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,qBAAmBpB,EAAK;AAAA,MACxB,WAAW,QAAQgB,IAAS,WAAW,EAAE;AAAA,MAExC,UAAA;AAAA,QAAAhB,EAAK,SACJ,gBAAAoB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,SAASH;AAAA,YACT,WAAU;AAAA,YAEV,UAAA;AAAA,cAAA,gBAAAE;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAM;AAAA,kBACN,QAAO;AAAA,kBACP,SAAQ;AAAA,kBACR,MAAK;AAAA,kBACL,QAAO;AAAA,kBACP,aAAY;AAAA,kBACZ,eAAc;AAAA,kBACd,gBAAe;AAAA,kBACf,WAAW,8DAA8DT,IAAY,aAAa,WAAW;AAAA,kBAC7G,eAAY;AAAA,kBAEZ,UAAA,gBAAAS,EAAC,YAAA,EAAS,QAAO,iBAAA,CAAiB;AAAA,gBAAA;AAAA,cAAA;AAAA,cAEnCnB,EAAK;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAIV,gBAAAmB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW,GAAGN,IAAS,iBAAiB,EAAE,IAAIH,IAAY,WAAW,EAAE;AAAA,YACvE,OAAO,OAAO,KAAKI,CAAS,EAAE,SAAS,IAAIA,IAAY;AAAA,YAEtD,UAAAd,EAAK,SAAS,IAAI,CAACqB,GAAOC,MAAUC,EAAeF,GAAOC,CAAK,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MACnE;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
|