@xbeeant/form-engine-react 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.
Files changed (142) hide show
  1. package/dist/cjs/components/FormController.cjs +1 -1
  2. package/dist/cjs/components/NexusField.cjs +1 -1
  3. package/dist/cjs/components/NexusForm.cjs +1 -1
  4. package/dist/cjs/components/form-controller.cjs +1 -0
  5. package/dist/cjs/components/nexus-field.cjs +1 -0
  6. package/dist/cjs/components/nexus-form-provider.cjs +1 -0
  7. package/dist/cjs/components/nexus-form.cjs +1 -0
  8. package/dist/cjs/components/nexus-layout.cjs +1 -0
  9. package/dist/cjs/components/nexus-object.cjs +1 -0
  10. package/dist/cjs/contexts/field-inherit-context.cjs +1 -0
  11. package/dist/cjs/contexts/grid-context.cjs +1 -0
  12. package/dist/cjs/contexts/layout-config-context.cjs +1 -0
  13. package/dist/cjs/contexts/nexus-context.cjs +1 -0
  14. package/dist/cjs/hooks/index.cjs +1 -1
  15. package/dist/cjs/hooks/use-engine.cjs +1 -0
  16. package/dist/cjs/hooks/use-field-state.cjs +1 -0
  17. package/dist/cjs/hooks/use-field-validator.cjs +1 -0
  18. package/dist/cjs/hooks/use-field-value.cjs +1 -0
  19. package/dist/cjs/hooks/use-form-config.cjs +1 -0
  20. package/dist/cjs/hooks/use-form-data.cjs +1 -0
  21. package/dist/cjs/hooks/use-form-submitting.cjs +1 -0
  22. package/dist/cjs/hooks/use-form.cjs +1 -0
  23. package/dist/cjs/hooks/use-nexus-context.cjs +1 -0
  24. package/dist/cjs/hooks/use-watch-all.cjs +1 -0
  25. package/dist/cjs/hooks/use-watch-multiple.cjs +1 -0
  26. package/dist/cjs/hooks/use-watch-state.cjs +1 -0
  27. package/dist/cjs/hooks/use-watch.cjs +1 -0
  28. package/dist/cjs/hooks/useFormData.cjs +1 -1
  29. package/dist/cjs/hooks/useFormSubmitting.cjs +1 -0
  30. package/dist/cjs/index.cjs +1 -1
  31. package/dist/cjs/styles.css +1 -1
  32. package/dist/cjs/utils/omit-nil.cjs +1 -0
  33. package/dist/cjs/utils/omitNil.cjs +1 -0
  34. package/dist/cjs/utils/persist.cjs +1 -0
  35. package/dist/cjs/utils/render-tree-node.cjs +1 -0
  36. package/dist/cjs/utils/resolve-col-span.cjs +1 -0
  37. package/dist/es/components/FormController.d.ts +73 -19
  38. package/dist/es/components/FormController.js +222 -71
  39. package/dist/es/components/NexusField.js +25 -21
  40. package/dist/es/components/NexusForm.d.ts +33 -3
  41. package/dist/es/components/NexusForm.js +143 -96
  42. package/dist/es/components/form-controller.d.ts +129 -0
  43. package/dist/es/components/form-controller.js +310 -0
  44. package/dist/es/components/nexus-field.d.ts +9 -0
  45. package/dist/es/components/nexus-field.js +131 -0
  46. package/dist/es/components/nexus-form-provider.d.ts +12 -0
  47. package/dist/es/components/nexus-form-provider.js +18 -0
  48. package/dist/es/components/nexus-form.d.ts +114 -0
  49. package/dist/es/components/nexus-form.js +174 -0
  50. package/dist/es/components/nexus-layout.d.ts +9 -0
  51. package/dist/es/components/nexus-layout.js +48 -0
  52. package/dist/es/components/nexus-object.d.ts +16 -0
  53. package/dist/es/components/nexus-object.js +64 -0
  54. package/dist/es/contexts/NexusContext.d.ts +2 -0
  55. package/dist/es/contexts/field-inherit-context.d.ts +18 -0
  56. package/dist/es/contexts/field-inherit-context.js +5 -0
  57. package/dist/es/contexts/grid-context.d.ts +5 -0
  58. package/dist/es/contexts/grid-context.js +5 -0
  59. package/dist/es/contexts/layout-config-context.d.ts +4 -0
  60. package/dist/es/contexts/layout-config-context.js +5 -0
  61. package/dist/es/contexts/nexus-context.d.ts +29 -0
  62. package/dist/es/contexts/nexus-context.js +12 -0
  63. package/dist/es/hooks/index.d.ts +12 -11
  64. package/dist/es/hooks/index.js +20 -18
  65. package/dist/es/hooks/use-engine.d.ts +2 -0
  66. package/dist/es/hooks/use-engine.js +8 -0
  67. package/dist/es/hooks/use-field-state.d.ts +7 -0
  68. package/dist/es/hooks/use-field-state.js +13 -0
  69. package/dist/es/hooks/use-field-validator.d.ts +40 -0
  70. package/dist/es/hooks/use-field-validator.js +35 -0
  71. package/dist/es/hooks/use-field-value.d.ts +6 -0
  72. package/dist/es/hooks/use-field-value.js +13 -0
  73. package/dist/es/hooks/use-form-config.d.ts +5 -0
  74. package/dist/es/hooks/use-form-config.js +8 -0
  75. package/dist/es/hooks/use-form-data.d.ts +6 -0
  76. package/dist/es/hooks/use-form-data.js +13 -0
  77. package/dist/es/hooks/use-form-submitting.d.ts +7 -0
  78. package/dist/es/hooks/use-form-submitting.js +11 -0
  79. package/dist/es/hooks/use-form.d.ts +22 -0
  80. package/dist/es/hooks/use-form.js +12 -0
  81. package/dist/es/hooks/use-nexus-context.d.ts +11 -0
  82. package/dist/es/hooks/use-nexus-context.js +12 -0
  83. package/dist/es/hooks/use-watch-all.d.ts +19 -0
  84. package/dist/es/hooks/use-watch-all.js +29 -0
  85. package/dist/es/hooks/use-watch-multiple.d.ts +20 -0
  86. package/dist/es/hooks/use-watch-multiple.js +29 -0
  87. package/dist/es/hooks/use-watch-state.d.ts +18 -0
  88. package/dist/es/hooks/use-watch-state.js +19 -0
  89. package/dist/es/hooks/use-watch.d.ts +25 -0
  90. package/dist/es/hooks/use-watch.js +20 -0
  91. package/dist/es/hooks/useForm.d.ts +11 -4
  92. package/dist/es/hooks/useFormData.js +9 -9
  93. package/dist/es/hooks/useFormSubmitting.d.ts +7 -0
  94. package/dist/es/hooks/useFormSubmitting.js +11 -0
  95. package/dist/es/index.d.ts +26 -24
  96. package/dist/es/index.js +36 -32
  97. package/dist/es/styles.css +1 -1
  98. package/dist/es/utils/omit-nil.d.ts +9 -0
  99. package/dist/es/utils/omit-nil.js +14 -0
  100. package/dist/es/utils/omitNil.d.ts +9 -0
  101. package/dist/es/utils/omitNil.js +14 -0
  102. package/dist/es/utils/persist.d.ts +24 -0
  103. package/dist/es/utils/persist.js +43 -0
  104. package/dist/es/utils/render-tree-node.d.ts +6 -0
  105. package/dist/es/utils/render-tree-node.js +17 -0
  106. package/dist/es/utils/resolve-col-span.d.ts +5 -0
  107. package/dist/es/utils/resolve-col-span.js +9 -0
  108. package/dist/umd/index.css +1 -1
  109. package/dist/umd/index.umd.cjs +1 -1
  110. package/package.json +5 -4
  111. package/src/components/form-controller.ts +584 -0
  112. package/src/components/{NexusField.tsx → nexus-field.tsx} +57 -6
  113. package/src/components/{NexusFormProvider.tsx → nexus-form-provider.tsx} +3 -3
  114. package/src/components/{NexusForm.tsx → nexus-form.tsx} +135 -18
  115. package/src/components/{NexusLayout.tsx → nexus-layout.tsx} +5 -5
  116. package/src/components/{NexusObject.tsx → nexus-object.tsx} +3 -3
  117. package/src/contexts/{NexusContext.ts → nexus-context.ts} +3 -1
  118. package/src/hooks/index.ts +12 -11
  119. package/src/hooks/{useEngine.ts → use-engine.ts} +1 -1
  120. package/src/hooks/{useFieldState.ts → use-field-state.ts} +1 -1
  121. package/src/hooks/{useFieldValidator.ts → use-field-validator.ts} +1 -1
  122. package/src/hooks/{useFieldValue.ts → use-field-value.ts} +1 -1
  123. package/src/hooks/{useFormConfig.ts → use-form-config.ts} +2 -2
  124. package/src/hooks/{useFormData.ts → use-form-data.ts} +4 -4
  125. package/src/hooks/use-form-submitting.ts +16 -0
  126. package/src/hooks/{useForm.ts → use-form.ts} +12 -5
  127. package/src/hooks/{useNexusContext.ts → use-nexus-context.ts} +2 -2
  128. package/src/index.ts +26 -24
  129. package/src/utils/omit-nil.ts +24 -0
  130. package/src/utils/persist.ts +88 -0
  131. package/src/utils/{renderTreeNode.tsx → render-tree-node.tsx} +3 -3
  132. package/src/utils/{resolveColSpan.ts → resolve-col-span.ts} +1 -1
  133. package/dist/cjs/tailwind.css +0 -1
  134. package/dist/es/tailwind.css +0 -1
  135. package/src/components/FormController.ts +0 -278
  136. /package/src/contexts/{FieldInheritContext.ts → field-inherit-context.ts} +0 -0
  137. /package/src/contexts/{GridContext.ts → grid-context.ts} +0 -0
  138. /package/src/contexts/{LayoutConfigContext.ts → layout-config-context.ts} +0 -0
  139. /package/src/hooks/{useWatchAll.ts → use-watch-all.ts} +0 -0
  140. /package/src/hooks/{useWatchMultiple.ts → use-watch-multiple.ts} +0 -0
  141. /package/src/hooks/{useWatchState.ts → use-watch-state.ts} +0 -0
  142. /package/src/hooks/{useWatch.ts → use-watch.ts} +0 -0
@@ -0,0 +1,310 @@
1
+ import { NexusEngine as g, AsyncValidatorPlugin as h } from "@xbeeant/form-engine";
2
+ import { omitNilDeep as c } from "../utils/omit-nil.js";
3
+ const f = /* @__PURE__ */ new WeakSet();
4
+ class V {
5
+ engine;
6
+ /** 提交中状态(校验 + onFinish 全流程,formily submitting 对齐) */
7
+ submitting = !1;
8
+ /** 提交状态监听器(供 useSyncExternalStore 消费) */
9
+ submittingListeners = /* @__PURE__ */ new Set();
10
+ /** 实例标识(内部)→ 实例视图引擎(同一 form 挂载的多个 NexusForm 各自独立) */
11
+ views = /* @__PURE__ */ new Map();
12
+ /** 实例标识 → 该实例的 DOM / 回调绑定(onFinish/onFinishFailed 按实例独立) */
13
+ instanceBindings = /* @__PURE__ */ new Map();
14
+ /** 实例标识 → watch / removeHiddenData / onValuesChange / omitNil 配置(按实例独立) */
15
+ instanceConfigs = /* @__PURE__ */ new Map();
16
+ constructor(e) {
17
+ this.engine = e ?? new g(), this.engine.hasPlugin("async-validator") || this.engine.use(new h(this.engine));
18
+ }
19
+ /**
20
+ * 获取(或创建)指定实例的引擎视图
21
+ *
22
+ * 同一 form 挂载多个 NexusForm 时各自独立:schema/值/订阅互不影响。
23
+ * 实例标识由 NexusForm 内部分配(React useId),用户不感知。
24
+ *
25
+ * @param instanceId - 实例标识(内部)
26
+ * @returns 定向到该实例的引擎视图
27
+ */
28
+ _useInstance(e) {
29
+ let t = this.views.get(e);
30
+ return t || (this.views.size === 0 && !f.has(this.engine) ? (f.add(this.engine), t = this.engine) : t = this.engine.instance(e), this.views.set(e, t), this.instanceConfigs.set(e, {
31
+ removeHiddenData: !0,
32
+ omitNil: !1,
33
+ watchers: /* @__PURE__ */ new Map(),
34
+ globalWatcher: null,
35
+ onValuesChange: null
36
+ })), t;
37
+ }
38
+ /**
39
+ * 内部:解析方法作用的目标实例视图(聚合全部实例,无实例时回退宿主引擎)
40
+ */
41
+ resolveViews() {
42
+ const e = Array.from(this.views.values());
43
+ return e.length > 0 ? e : [this.engine];
44
+ }
45
+ /** 内部:首次绑定的实例 DOM(兼容既有 scrollToPath/focusFirstError 单实例行为) */
46
+ getPrimaryFormEl() {
47
+ return this.instanceBindings.values().next().value?.formEl ?? null;
48
+ }
49
+ /**
50
+ * 内部:绑定实例 DOM + 回调 getter(由 NexusForm 在挂载时按实例调用)
51
+ * 回调以 getter 传入:submit 时读取最新闭包,而非绑定时刻的快照
52
+ */
53
+ _bind(e, t, s, n) {
54
+ this.instanceBindings.set(e, {
55
+ formEl: t,
56
+ getOnFinish: s,
57
+ getOnFinishFailed: n
58
+ }), this._useInstance(e).registerOnFieldValueChange(
59
+ (o, r) => this._onFieldValueChange(e, o, r)
60
+ );
61
+ }
62
+ /** 内部:同步 watch / removeHiddenData / onValuesChange 配置(由 NexusForm 在它们变化时按实例调用) */
63
+ _syncConfig(e, t) {
64
+ const s = this.instanceConfigs.get(e) ?? {
65
+ removeHiddenData: !0,
66
+ omitNil: !1,
67
+ watchers: /* @__PURE__ */ new Map(),
68
+ globalWatcher: null,
69
+ onValuesChange: null
70
+ };
71
+ if (t.removeHiddenData !== void 0 && (s.removeHiddenData = t.removeHiddenData), t.omitNil !== void 0 && (s.omitNil = t.omitNil), t.onValuesChange !== void 0 && (s.onValuesChange = t.onValuesChange), t.watch) {
72
+ s.watchers.clear(), s.globalWatcher = null;
73
+ for (const [n, i] of Object.entries(t.watch))
74
+ n === "#" ? s.globalWatcher = i : s.watchers.set(n, i);
75
+ }
76
+ this.instanceConfigs.set(e, s);
77
+ }
78
+ /** 内部:值变更时调用(由 Engine 通知,携带实例标识路由到对应实例的 watcher) */
79
+ _onFieldValueChange(e, t, s) {
80
+ const n = this.views.get(e), i = this.instanceConfigs.get(e);
81
+ if (!n || !i)
82
+ return;
83
+ const o = n.getFormData(), r = i.removeHiddenData ? o : n.getAllFormData();
84
+ i.globalWatcher && i.globalWatcher(r, r, t), i.onValuesChange && i.onValuesChange(s, r, t);
85
+ const a = i.watchers.get(t);
86
+ a && a(s, r);
87
+ }
88
+ /** 内部:获取引擎宿主实例 */
89
+ _getEngine() {
90
+ return this.engine;
91
+ }
92
+ /**
93
+ * 获取底层引擎宿主实例(用于跨表单联动:linkForm / setFormId / registerAntdUI 等;
94
+ * 宿主上的组件/插件注册对全部实例生效)
95
+ */
96
+ getEngine() {
97
+ return this.engine;
98
+ }
99
+ async submit(e) {
100
+ this.setSubmitting(!0);
101
+ try {
102
+ await this.runSubmit(e);
103
+ } finally {
104
+ this.setSubmitting(!1);
105
+ }
106
+ }
107
+ /** 提交中状态(formily submitting 对齐,供提交按钮 loading) */
108
+ getSubmitting() {
109
+ return this.submitting;
110
+ }
111
+ /** 订阅提交状态变化(返回取消订阅函数,供 useSyncExternalStore 使用) */
112
+ onSubmittingChange(e) {
113
+ return this.submittingListeners.add(e), () => {
114
+ this.submittingListeners.delete(e);
115
+ };
116
+ }
117
+ setSubmitting(e) {
118
+ if (this.submitting !== e) {
119
+ this.submitting = e;
120
+ for (const t of this.submittingListeners)
121
+ t();
122
+ }
123
+ }
124
+ async runSubmit(e) {
125
+ const t = this.resolveViews(), s = /* @__PURE__ */ new Map();
126
+ let n;
127
+ for (const i of t) {
128
+ const o = await i.validate(void 0, {
129
+ validateFirst: e?.validateFirst
130
+ });
131
+ if (o.size > 0) {
132
+ for (const [r, a] of o)
133
+ s.set(r, a);
134
+ n ??= this.instanceBindings.get(this.findInstanceId(i));
135
+ }
136
+ }
137
+ if (s.size > 0) {
138
+ this.focusFirstError(s, n?.formEl ?? null), n?.getOnFinishFailed()?.(s);
139
+ return;
140
+ }
141
+ for (const i of t) {
142
+ const o = this.findInstanceId(i), r = this.instanceConfigs.get(o) ?? {
143
+ removeHiddenData: !0,
144
+ omitNil: !1
145
+ }, a = r.removeHiddenData ? i.getFormData() : i.getAllFormData(), l = e?.omitNil ?? r.omitNil ?? !1 ? c(a) : a;
146
+ await i.submit(l) && await this.instanceBindings.get(o)?.getOnFinish()?.(l);
147
+ }
148
+ }
149
+ /** 内部:反查实例视图对应的实例标识 */
150
+ findInstanceId(e) {
151
+ for (const [t, s] of this.views)
152
+ if (s === e)
153
+ return t;
154
+ return "default";
155
+ }
156
+ /**
157
+ * 定位到第一个校验失败的字段:按 DOM 渲染顺序查找(保证视觉上的"第一个"),
158
+ * 滚动入视并聚焦其内部可交互控件。
159
+ */
160
+ focusFirstError(e, t) {
161
+ if (!t || e.size === 0)
162
+ return;
163
+ const s = new Set(e.keys());
164
+ requestAnimationFrame(() => {
165
+ const i = t.querySelectorAll("[data-nexus-field]");
166
+ for (const o of Array.from(i)) {
167
+ const r = o.getAttribute("data-nexus-field");
168
+ if (!r || !s.has(r))
169
+ continue;
170
+ typeof o.scrollIntoView == "function" && o.scrollIntoView({ behavior: "smooth", block: "center" }), o.querySelector(
171
+ 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
172
+ )?.focus();
173
+ return;
174
+ }
175
+ });
176
+ }
177
+ resetFields() {
178
+ for (const e of this.resolveViews())
179
+ e.reset();
180
+ }
181
+ setErrorFields(e) {
182
+ for (const t of this.resolveViews())
183
+ t.setErrorFields(e);
184
+ }
185
+ setValues(e) {
186
+ for (const t of this.resolveViews())
187
+ t.setFieldValues(e);
188
+ }
189
+ setValueByPath(e, t) {
190
+ for (const s of this.resolveViews())
191
+ s.setFieldValue(e, t);
192
+ }
193
+ setSchemaByPath(e, t) {
194
+ for (const s of this.resolveViews())
195
+ s.setSchemaByPath(e, t);
196
+ }
197
+ setSchema(e) {
198
+ for (const t of this.resolveViews())
199
+ t.setSchema(e);
200
+ }
201
+ getValues(e, t) {
202
+ const s = {};
203
+ for (const n of this.resolveViews()) {
204
+ const i = n.getFormData(e);
205
+ t?.omitNil ? Object.assign(s, c(i)) : Object.assign(s, i);
206
+ }
207
+ return s;
208
+ }
209
+ getHiddenValues() {
210
+ const e = {};
211
+ for (const t of this.resolveViews())
212
+ Object.assign(e, t.getHiddenValues());
213
+ return e;
214
+ }
215
+ /** 获取所有字段值(含 hidden) */
216
+ getAllValues() {
217
+ const e = {};
218
+ for (const t of this.resolveViews())
219
+ Object.assign(e, t.getAllFormData());
220
+ return e;
221
+ }
222
+ getValueByPath(e) {
223
+ for (const t of this.resolveViews()) {
224
+ const s = t.getFieldValue(e);
225
+ if (s !== void 0)
226
+ return s;
227
+ }
228
+ }
229
+ /**
230
+ * 注册字段校验逻辑
231
+ * @param path 字段路径(如 'username')
232
+ * @param validator 校验函数,返回错误消息数组(空数组表示通过)
233
+ */
234
+ registerValidator(e, t) {
235
+ for (const s of this.resolveViews())
236
+ s.registerFieldValidator(e, t);
237
+ }
238
+ /**
239
+ * 注销字段校验逻辑(按函数引用移除)
240
+ * 与 registerValidator 配对;widget 组件卸载时清理,避免校验器累积
241
+ */
242
+ unregisterValidator(e, t) {
243
+ for (const s of this.resolveViews())
244
+ s.unregisterFieldValidator(e, t);
245
+ }
246
+ /**
247
+ * 实时重校验指定字段(同步)
248
+ * 供 widget 组件内部状态变化(非字段值变化)时主动刷新错误态
249
+ */
250
+ revalidateField(e) {
251
+ for (const t of this.resolveViews())
252
+ t.validateField(e, { trigger: "change" });
253
+ }
254
+ getSchema() {
255
+ return this.resolveViews()[0]?.getSchema() ?? null;
256
+ }
257
+ removeErrorField(e) {
258
+ for (const t of this.resolveViews())
259
+ t.removeErrorField(e);
260
+ }
261
+ scrollToPath(e) {
262
+ this.getPrimaryFormEl()?.querySelector(`[data-nexus-field="${e}"]`)?.scrollIntoView({ behavior: "smooth", block: "center" });
263
+ }
264
+ getFieldError(e) {
265
+ for (const t of this.resolveViews()) {
266
+ const s = t.getFieldError(e);
267
+ if (s.length > 0)
268
+ return s;
269
+ }
270
+ return [];
271
+ }
272
+ getFieldsError() {
273
+ const e = /* @__PURE__ */ new Map();
274
+ for (const t of this.resolveViews())
275
+ for (const [s, n] of t.getFieldsError())
276
+ e.set(s, n);
277
+ return e;
278
+ }
279
+ validateFields(e, t) {
280
+ const s = this.resolveViews();
281
+ return s.length === 1 ? s[0].validate(e, t) : Promise.all(s.map((n) => n.validate(e, t))).then(
282
+ (n) => {
283
+ const i = /* @__PURE__ */ new Map();
284
+ for (const o of n)
285
+ for (const [r, a] of o)
286
+ i.set(r, a);
287
+ return i;
288
+ }
289
+ );
290
+ }
291
+ getFieldState(e) {
292
+ for (const t of this.resolveViews()) {
293
+ const s = t.getFieldState(e);
294
+ if (s)
295
+ return s;
296
+ }
297
+ }
298
+ /**
299
+ * 重载远程选项数据(x-render reloadRemoteData 对齐)
300
+ * 传入 path 仅重载该字段的远程数据;缺省时重载全部远程数据字段。
301
+ * 作用于全部实例视图。
302
+ */
303
+ reloadRemoteData(e) {
304
+ for (const t of this.resolveViews())
305
+ t.reloadRemoteData(e);
306
+ }
307
+ }
308
+ export {
309
+ V as FormController
310
+ };
@@ -0,0 +1,9 @@
1
+ interface NexusFieldProps {
2
+ dataPath: string;
3
+ layoutKey: string;
4
+ }
5
+ /**
6
+ * NexusField — 单个字段渲染器
7
+ */
8
+ export declare function NexusField({ dataPath, layoutKey }: NexusFieldProps): import("react").JSX.Element | null;
9
+ export {};
@@ -0,0 +1,131 @@
1
+ import { jsx as m, jsxs as R } from "react/jsx-runtime";
2
+ import { useSyncExternalStore as $, useContext as f, useCallback as C, useMemo as y } from "react";
3
+ import { FieldInheritContext as E } from "../contexts/field-inherit-context.js";
4
+ import { GridContext as G } from "../contexts/grid-context.js";
5
+ import { LayoutConfigContext as H } from "../contexts/layout-config-context.js";
6
+ import { useNexusContext as z } from "../contexts/nexus-context.js";
7
+ import { resolveColSpan as A } from "../utils/resolve-col-span.js";
8
+ function K({ dataPath: t, layoutKey: x }) {
9
+ const { engine: l, config: c, form: i } = z();
10
+ $(
11
+ (n) => l.subscribeField(t, n),
12
+ () => l.getFieldVersion(t),
13
+ () => l.getFieldVersion(t)
14
+ );
15
+ const e = l.getFieldState(t), S = f(G), w = f(H), u = f(E), W = C(
16
+ (n) => {
17
+ l.setFieldValue(t, n);
18
+ },
19
+ [l, t]
20
+ ), N = C(
21
+ (n) => {
22
+ n.currentTarget.contains(n.relatedTarget) || l.validateField(t, { trigger: "blur" });
23
+ },
24
+ [l, t]
25
+ ), O = y(
26
+ () => e?.meta.enum ? e.meta.enum.map((n, r) => ({
27
+ value: n,
28
+ label: e.meta.enumNames?.[r] ?? String(n)
29
+ })) : e?.props.options,
30
+ [e?.meta.enum, e?.meta.enumNames, e?.props.options]
31
+ ), B = y(() => {
32
+ const n = {};
33
+ if (e?.reactions) {
34
+ for (const r of e.reactions)
35
+ if (r.dependencies)
36
+ for (const a of r.dependencies)
37
+ n[a] = l.getFieldValue(a);
38
+ }
39
+ return n;
40
+ }, [e?.reactions, l]), T = y(() => {
41
+ const n = e?.meta.itemOf, a = !!n ? t.match(/\[(\d+)\]/) : void 0, M = a ? Number(a[1]) : void 0;
42
+ return {
43
+ get formData() {
44
+ return i.getValues();
45
+ },
46
+ get rootValue() {
47
+ return i.getValues();
48
+ },
49
+ value: e?.value,
50
+ dataPath: t,
51
+ path: t,
52
+ index: M,
53
+ parentValues: n ? i.getValueByPath(n) : void 0,
54
+ getValue: (s) => i.getValueByPath(s),
55
+ setValue: (s, d) => i.setValueByPath(s, d),
56
+ onItemChange: (s, d) => i.setValueByPath(s, d),
57
+ validate: async (s) => {
58
+ s ? await i.validateFields([s]) : await i.validateFields([t]);
59
+ },
60
+ validateFields: async (s) => {
61
+ await i.validateFields(s);
62
+ },
63
+ submit: () => i.submit(),
64
+ resetFields: () => i.resetFields(),
65
+ setSchema: (s) => i.setSchema(s),
66
+ setSchemaByPath: (s, d) => i.setSchemaByPath(s, d),
67
+ getSchema: () => i.getSchema()
68
+ };
69
+ }, [i, t, e?.value, e?.meta.itemOf]);
70
+ if (!e)
71
+ return l.getSnapshot() > 0 && console.warn(`[NexusField] Field not found: ${t}`), null;
72
+ if (u.visible === !1 || !e.visible)
73
+ return w.removeHidden === !0 ? null : /* @__PURE__ */ m("div", { className: "hidden", "data-nexus-hidden": t });
74
+ const p = c.readOnly || u.readOnly === !0 || e.readOnly, I = u.disabled === !0 || e.disabled, h = p && !!e.meta.readOnlyWidget, j = h ? e.meta.readOnlyWidget : e.meta.widget;
75
+ let o = l.getWidget(j);
76
+ if (!o && h && (o = l.getWidget(e.meta.widget)), !o)
77
+ return /* @__PURE__ */ R("div", { className: "text-xs text-red-500", "data-nexus-field": t, children: [
78
+ '⚠️ Widget "',
79
+ e.meta.widget,
80
+ '" 未注册 (path: ',
81
+ t,
82
+ ")"
83
+ ] });
84
+ const k = e.meta.displayType ?? c.displayType, D = e.meta.labelWidth ?? c.labelWidth, q = e.meta.column ?? c.column, v = A(e.meta.colSpan, S), F = {
85
+ ...e.meta.width ? { width: e.meta.width, flexShrink: 0 } : {},
86
+ ...v ? { gridColumn: `span ${v}` } : {}
87
+ }, b = l.getFieldWrapper(), L = {
88
+ label: e.meta.label,
89
+ title: e.meta.title,
90
+ description: e.meta.description,
91
+ tooltip: e.meta.tooltip,
92
+ errors: e.errors,
93
+ required: e.required,
94
+ extra: e.meta.extra,
95
+ displayType: k,
96
+ labelWidth: D,
97
+ column: q
98
+ }, V = {
99
+ dataPath: t,
100
+ path: t,
101
+ value: e.value,
102
+ onChange: W,
103
+ disabled: I,
104
+ readOnly: p,
105
+ loading: e.loading,
106
+ placeholder: e.meta.placeholder,
107
+ options: O,
108
+ form: i,
109
+ addons: T,
110
+ dependValues: B,
111
+ items: e.meta.items,
112
+ schema: e.meta.schema,
113
+ // 远程选项数据版本:reloadRemoteData 后变化,widget 据此跳过缓存重新请求
114
+ remoteVersion: l.getRemoteDataVersion(t),
115
+ ...e.props
116
+ };
117
+ let g;
118
+ return b ? g = /* @__PURE__ */ m(b, { ...L, children: /* @__PURE__ */ m(o, { ...V }) }, x) : g = /* @__PURE__ */ m(o, { ...V }, x), /* @__PURE__ */ m(
119
+ "div",
120
+ {
121
+ "data-nexus-field": t,
122
+ className: p ? "nexus-field-readonly" : void 0,
123
+ onBlur: N,
124
+ style: Object.keys(F).length > 0 ? F : void 0,
125
+ children: g
126
+ }
127
+ );
128
+ }
129
+ export {
130
+ K as NexusField
131
+ };
@@ -0,0 +1,12 @@
1
+ import { NexusEngine } from '@xbeeant/form-engine';
2
+ import { ReactNode } from 'react';
3
+ import { FormController } from './form-controller';
4
+ import { NexusFormConfig } from './nexus-form';
5
+ interface NexusFormProviderProps {
6
+ engine: NexusEngine;
7
+ config: NexusFormConfig;
8
+ form: FormController;
9
+ children: ReactNode;
10
+ }
11
+ export declare function NexusFormProvider({ engine, config, form, children, }: NexusFormProviderProps): import("react").JSX.Element;
12
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsx as u } from "react/jsx-runtime";
2
+ import { useMemo as s } from "react";
3
+ import { NexusContext as x } from "../contexts/nexus-context.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
+ };
@@ -0,0 +1,114 @@
1
+ import { NexusComponent, NexusSchema } from '@xbeeant/form-engine';
2
+ import { CSSProperties, ReactNode } from 'react';
3
+ import { PersistOptions } from '../utils/persist';
4
+ import { FormController } from './form-controller';
5
+ export interface NexusFormConfig {
6
+ /** label 列配置(由 ui 层 Form.Item 消费) */
7
+ labelCol?: Record<string, unknown>;
8
+ /** label 宽度(px 或 %),快捷方式 — 映射到 labelCol.style.width */
9
+ labelWidth?: number | string;
10
+ /** 是否显示冒号 */
11
+ colon?: boolean | ReactNode;
12
+ /** 是否显示 label(默认 true) */
13
+ label?: boolean;
14
+ /** 表单布局方向 */
15
+ displayType?: 'row' | 'column' | 'inline';
16
+ /** 整个表单只读,所有字段以文本展示 */
17
+ readOnly?: boolean;
18
+ /** 表单每行显示多少列 */
19
+ column?: number;
20
+ /** 表单语言标识(如 'zh-CN' / 'en-US',ui 层消费:antd locale + 内置文案) */
21
+ locale?: string;
22
+ }
23
+ export interface NexusFormProps {
24
+ /**
25
+ * Form 实例,由 useForm() 创建。同一 form 引用 = 同一引擎宿主(组件/插件注册共享);
26
+ * 每个 NexusForm 挂载自动获得独立实例(schema/值/订阅互不影响),
27
+ * form 的 API(getValues/setValues/submit...)聚合作用于全部实例。
28
+ */
29
+ form: FormController;
30
+ /** Schema 定义 */
31
+ schema?: NexusSchema;
32
+ /** 初始值 */
33
+ initialValues?: Record<string, unknown>;
34
+ /** 额外注册的 widget(与已注册的合并) */
35
+ widgets?: Record<string, (props: any) => ReactNode>;
36
+ /** 额外注册的 layout */
37
+ layouts?: Record<string, (props: any) => ReactNode>;
38
+ /** 字段包裹组件(UI 无关,Renderer 层注入;渲染层默认包裹所有 widget,label=false 时跳过) */
39
+ fieldWrapper?: NexusComponent;
40
+ /** 提交成功回调 */
41
+ onFinish?: (formData: Record<string, unknown>) => void | Promise<void>;
42
+ /** 校验失败回调 */
43
+ onFinishFailed?: (errors: Map<string, string[]>) => void;
44
+ /**
45
+ * 表单首次加载回调:非空 schema 首次传入并完成渲染后执行一次
46
+ * - undefined / null / {}(无 properties)均视为「空」schema,不触发
47
+ * - schema 由空变为非空时,于首个非空渲染提交后触发
48
+ * - 后续 schema 变更不重复触发
49
+ */
50
+ onMount?: () => void;
51
+ /** 是否显示默认 footer(提交/重置按钮),或自定义 footer */
52
+ footer?: boolean | ReactNode;
53
+ /** 自定义类名 */
54
+ className?: string;
55
+ /** 自定义样式 */
56
+ style?: CSSProperties;
57
+ /** 子节点 */
58
+ children?: ReactNode;
59
+ /**
60
+ * 表单值变化监听
61
+ * - key 为字段路径(如 'username'),值为回调函数
62
+ * - 使用 '#' 作为 key 可监听所有字段变化
63
+ * - 回调参数为 (value, allValues);全局('#')监听额外提供第三参 changedPath,
64
+ * 即本次实际变更的字段路径(用于区分「未触碰的默认空值」与「用户主动清空」)
65
+ */
66
+ watch?: {
67
+ [path: string]: (value: unknown, allValues: Record<string, unknown>, changedPath?: string) => void;
68
+ };
69
+ /**
70
+ * 表单值变化回调(x-render onValuesChange 对齐)
71
+ * 任意字段值变化时触发,参数为 (changedValue, allValues, changedPath)
72
+ * 与 watch: { '#': fn } 语义等价,但作为标准回调 API 提供
73
+ */
74
+ onValuesChange?: (changedValue: unknown, allValues: Record<string, unknown>, changedPath: string) => void;
75
+ /**
76
+ * 提交时是否移除 hidden 字段数据,默认 true
77
+ * - true: submit/getValues 不包含 hidden 字段
78
+ * - false: submit/getValues 包含所有字段(含 hidden)
79
+ */
80
+ removeHiddenData?: boolean;
81
+ /**
82
+ * 提交时递归移除空值(undefined/null/'',ProForm omitNil 对齐)
83
+ * 与 form.submit({ omitNil: true }) / form.getValues(paths, { omitNil: true }) 等价
84
+ */
85
+ omitNil?: boolean;
86
+ /** 表单语言标识(如 'zh-CN' / 'en-US',ui 层消费:antd locale + 内置文案) */
87
+ locale?: string;
88
+ /**
89
+ * 表单草稿持久化:值变化时自动保存到 Web Storage,下次挂载自动恢复
90
+ * @example
91
+ * ```tsx
92
+ * <NexusForm persist={{ key: 'apply-form', storage: 'localStorage' }} />
93
+ * ```
94
+ */
95
+ persist?: PersistOptions;
96
+ /** label 列配置(由 ui 层 Form.Item 消费) */
97
+ labelCol?: Record<string, unknown>;
98
+ /** label 宽度(px 或 %),快捷方式 — 映射到 labelCol.style.width */
99
+ labelWidth?: number | string;
100
+ /** 是否显示冒号 */
101
+ colon?: boolean | ReactNode;
102
+ /** 是否显示 label(默认 true) */
103
+ label?: boolean;
104
+ /** 表单布局方向:'row' = horizontal, 'column' = vertical, 'inline' = inline */
105
+ displayType?: 'row' | 'column' | 'inline';
106
+ /** 整个表单只读,所有字段以文本展示 */
107
+ readOnly?: boolean;
108
+ /** 表单每行显示多少列 */
109
+ column?: number;
110
+ }
111
+ /**
112
+ * NexusForm — 顶层表单组件
113
+ */
114
+ export declare function NexusForm({ form, schema, initialValues, widgets, layouts, fieldWrapper, onFinish, onFinishFailed, onMount, footer, className, style, children, labelCol, labelWidth, colon, label, displayType, readOnly, column, watch, onValuesChange, removeHiddenData, omitNil, locale, persist, }: NexusFormProps): import("react").JSX.Element;