@schemx/vue 1.0.0-next.3 → 1.0.0-next.5

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 (99) hide show
  1. package/README.md +288 -283
  2. package/dist/analyze.html +1 -1
  3. package/dist/bridge/fieldBridge.d.ts +5 -18
  4. package/dist/bridge/fieldBridge.d.ts.map +1 -1
  5. package/dist/bridge/formBridge.d.ts +8 -76
  6. package/dist/bridge/formBridge.d.ts.map +1 -1
  7. package/dist/bridge/formInstance.d.ts +18 -0
  8. package/dist/bridge/formInstance.d.ts.map +1 -0
  9. package/dist/bridge/helpers.d.ts +9 -1
  10. package/dist/bridge/helpers.d.ts.map +1 -1
  11. package/dist/bridge/index.d.ts +7 -5
  12. package/dist/bridge/index.d.ts.map +1 -1
  13. package/dist/bridge/types.d.ts +37 -77
  14. package/dist/bridge/types.d.ts.map +1 -1
  15. package/dist/bridge/viewSchemaBridge.d.ts +5 -0
  16. package/dist/bridge/viewSchemaBridge.d.ts.map +1 -0
  17. package/dist/components/Button/index.vue.d.ts +1 -1
  18. package/dist/components/Button/index.vue.d.ts.map +1 -1
  19. package/dist/components/Button/types.d.ts.map +1 -1
  20. package/dist/components/Col/index.d.ts +31 -0
  21. package/dist/components/Col/index.d.ts.map +1 -0
  22. package/dist/components/ConfigProvider/index.d.ts +78 -0
  23. package/dist/components/ConfigProvider/index.d.ts.map +1 -0
  24. package/dist/components/Dynamic/index.d.ts +46 -0
  25. package/dist/components/Dynamic/index.d.ts.map +1 -0
  26. package/dist/components/Field/index.d.ts +47 -0
  27. package/dist/components/Field/index.d.ts.map +1 -0
  28. package/dist/components/Field/slot.d.ts +40 -0
  29. package/dist/components/Field/slot.d.ts.map +1 -0
  30. package/dist/components/Group/index.d.ts +61 -0
  31. package/dist/components/Group/index.d.ts.map +1 -0
  32. package/dist/components/Group/slot.d.ts +34 -0
  33. package/dist/components/Group/slot.d.ts.map +1 -0
  34. package/dist/components/SchemaList/index.d.ts +58 -0
  35. package/dist/components/SchemaList/index.d.ts.map +1 -0
  36. package/dist/components/Wrapper/index.vue.d.ts +29 -0
  37. package/dist/components/Wrapper/index.vue.d.ts.map +1 -0
  38. package/dist/config/appConfig.d.ts +3 -3
  39. package/dist/config/appConfig.d.ts.map +1 -1
  40. package/dist/config/index.d.ts +2 -0
  41. package/dist/config/index.d.ts.map +1 -1
  42. package/dist/config/providerConfig.d.ts +22 -0
  43. package/dist/config/providerConfig.d.ts.map +1 -0
  44. package/dist/config/vueConfig.d.ts +12 -0
  45. package/dist/config/vueConfig.d.ts.map +1 -0
  46. package/dist/form.d.ts +4 -4
  47. package/dist/form.d.ts.map +1 -1
  48. package/dist/hocs/withRemoteOptions.d.ts.map +1 -1
  49. package/dist/hooks/index.d.ts +3 -3
  50. package/dist/hooks/index.d.ts.map +1 -1
  51. package/dist/hooks/provideFieldContext.d.ts +2 -2
  52. package/dist/hooks/provideFormContext.d.ts +13 -46
  53. package/dist/hooks/provideFormContext.d.ts.map +1 -1
  54. package/dist/hooks/useDictionary.d.ts.map +1 -1
  55. package/dist/hooks/useField.d.ts.map +1 -1
  56. package/dist/hooks/useForm.d.ts +2 -2
  57. package/dist/hooks/useForm.d.ts.map +1 -1
  58. package/dist/hooks/useFormSelector.d.ts +4 -0
  59. package/dist/hooks/useFormSelector.d.ts.map +1 -1
  60. package/dist/hooks/useViewSchemas.d.ts +2 -34
  61. package/dist/hooks/useViewSchemas.d.ts.map +1 -1
  62. package/dist/hooks/useWatch.d.ts.map +1 -1
  63. package/dist/index.cjs +1 -1
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.d.ts +9 -4
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.mjs +1125 -521
  68. package/dist/index.mjs.map +1 -1
  69. package/dist/style.css +1 -1
  70. package/dist/types/dictionary.d.ts +1 -1
  71. package/dist/types/dictionary.d.ts.map +1 -1
  72. package/dist/types/field.d.ts +38 -2
  73. package/dist/types/field.d.ts.map +1 -1
  74. package/dist/types/form.d.ts +12 -7
  75. package/dist/types/form.d.ts.map +1 -1
  76. package/dist/types/index.d.ts +5 -4
  77. package/dist/types/index.d.ts.map +1 -1
  78. package/dist/types/layout.d.ts +26 -0
  79. package/dist/types/layout.d.ts.map +1 -0
  80. package/dist/utils/colProvider.d.ts +20 -0
  81. package/dist/utils/colProvider.d.ts.map +1 -0
  82. package/dist/utils/helpers.d.ts +4 -0
  83. package/dist/utils/helpers.d.ts.map +1 -1
  84. package/dist/utils/index.d.ts +2 -0
  85. package/dist/utils/index.d.ts.map +1 -1
  86. package/dist/utils/presetRuleProvider.d.ts +16 -0
  87. package/dist/utils/presetRuleProvider.d.ts.map +1 -0
  88. package/dist/utils/rendererProvider.d.ts.map +1 -1
  89. package/package.json +5 -6
  90. package/dist/bridge/formFacade.d.ts +0 -65
  91. package/dist/bridge/formFacade.d.ts.map +0 -1
  92. package/dist/components/FormGroup/index.d.ts +0 -16
  93. package/dist/components/FormGroup/index.d.ts.map +0 -1
  94. package/dist/components/FormItem/index.d.ts +0 -22
  95. package/dist/components/FormItem/index.d.ts.map +0 -1
  96. package/dist/components/FormItem/slot.d.ts +0 -48
  97. package/dist/components/FormItem/slot.d.ts.map +0 -1
  98. package/dist/utils/rulesProvider.d.ts +0 -16
  99. package/dist/utils/rulesProvider.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import "./style.css";
2
- import { shallowRef, getCurrentInstance, inject, onScopeDispose, provide, computed, onUnmounted, ref, onMounted, watchEffect, watch, readonly, defineComponent, createVNode, h, createTextVNode, mergeProps, useAttrs, openBlock, createElementBlock, unref, renderSlot, createCommentVNode, createStaticVNode, Fragment, toDisplayString, useSlots, reactive, normalizeClass, normalizeStyle, renderList, createBlock, createSlots, withCtx } from "vue";
3
- import { createValidationRuleRegistry, mergeSchemxConfig, createForm, createField, createWatch, isViewGroupSchema, isSchemxViewFieldSchema, isSchemxSchemas, defaultSchemxConfigKeys } from "@schemx/core";
2
+ import { shallowRef, computed, getCurrentInstance, inject, provide, onScopeDispose, onUnmounted, ref, onMounted, watchEffect, watch, readonly, createVNode, h, createTextVNode, defineComponent, mergeProps, useAttrs, openBlock, createElementBlock, unref, renderSlot, createCommentVNode, createStaticVNode, Fragment, toDisplayString, markRaw, toRaw, isVNode, useSlots, reactive, normalizeClass, normalizeStyle, createSlots, renderList, withCtx, normalizeProps, guardReactiveProps, createBlock, nextTick } from "vue";
3
+ import { isViewGroupSchema, isViewDynamicSchema, mergeSchemxConfig, createPresetRuleRegistry, createForm, createField, createWatch, isSchemxViewFieldSchema, getGlobalSchemxConfig, mergeAndResolveSchemxConfig, isSchemxSchemas, defaultSchemxConfigKeys } from "@schemx/core";
4
4
  export * from "@schemx/core";
5
5
  import classnames from "classnames";
6
6
  import { createFormStateAdapter, createRendererRegistry } from "@schemx/core/adapter";
@@ -8,14 +8,21 @@ import { pick } from "es-toolkit";
8
8
  function createVueShallowRef(value) {
9
9
  return shallowRef(value);
10
10
  }
11
+ function bindSnapshotSource(source) {
12
+ const value = createVueShallowRef(source.getSnapshot());
13
+ source.subscribe(() => {
14
+ value.value = source.getSnapshot();
15
+ });
16
+ return value;
17
+ }
11
18
  function areStringListsEqual(previous, next) {
12
19
  return previous.length === next.length && previous.every((value, index) => value === next[index]);
13
20
  }
14
- function getVueFieldBridge(bridge, name) {
15
- const source = bridge.stateAdapter.field(name);
16
- const cachedBridge = bridge.fieldBridges.get(source);
17
- if (cachedBridge) {
18
- return cachedBridge;
21
+ function getVueFieldState(resources, name) {
22
+ const source = resources.stateAdapter.field(name);
23
+ const cachedState = resources.fieldStates.get(source);
24
+ if (cachedState) {
25
+ return cachedState;
19
26
  }
20
27
  const snapshot = source.getSnapshot();
21
28
  const value = createVueShallowRef(
@@ -24,7 +31,7 @@ function getVueFieldBridge(bridge, name) {
24
31
  const errors = createVueShallowRef(snapshot.errors);
25
32
  const touched = createVueShallowRef(snapshot.touched);
26
33
  const pending = createVueShallowRef(snapshot.pending);
27
- const updateFieldRefs = () => {
34
+ const updateFieldState = () => {
28
35
  const nextSnapshot = source.getSnapshot();
29
36
  if (!Object.is(value.value, nextSnapshot.value)) {
30
37
  value.value = nextSnapshot.value;
@@ -39,82 +46,63 @@ function getVueFieldBridge(bridge, name) {
39
46
  pending.value = nextSnapshot.pending;
40
47
  }
41
48
  };
42
- const unsubscribe = source.subscribe(updateFieldRefs);
43
- const dispose = () => {
44
- unsubscribe();
45
- };
46
- const fieldBridge = {
47
- source,
49
+ source.subscribe(updateFieldState);
50
+ const fieldState = {
48
51
  value,
49
52
  errors,
50
53
  touched,
51
- pending,
52
- dispose
54
+ pending
53
55
  };
54
- bridge.fieldBridges.set(source, fieldBridge);
55
- return fieldBridge;
56
+ resources.fieldStates.set(source, fieldState);
57
+ return fieldState;
56
58
  }
57
- function createVueFormFacade(form, dependencies) {
59
+ function createVueFormInstance(form, dependencies) {
58
60
  let destroyed = false;
59
61
  const getFieldValue = (name) => {
60
62
  if (!destroyed) {
61
- const bridge = dependencies.getFormBridge(form);
62
- void dependencies.getFieldBridge(bridge, name).value.value;
63
+ dependencies.trackField(name, "value");
63
64
  }
64
65
  return form.getFieldValue(name);
65
66
  };
66
67
  const getFieldErrors = (name) => {
67
68
  if (!destroyed) {
68
- const bridge = dependencies.getFormBridge(form);
69
- void dependencies.getFieldBridge(bridge, name).errors.value;
69
+ dependencies.trackField(name, "errors");
70
70
  }
71
71
  return form.getFieldErrors(name);
72
72
  };
73
73
  const isFieldTouched = (name) => {
74
74
  if (!destroyed) {
75
- const bridge = dependencies.getFormBridge(form);
76
- void dependencies.getFieldBridge(bridge, name).touched.value;
75
+ dependencies.trackField(name, "touched");
77
76
  }
78
77
  return form.isFieldTouched(name);
79
78
  };
80
79
  const isFieldPending = (name) => {
81
80
  if (!destroyed) {
82
- const bridge = dependencies.getFormBridge(form);
83
- void dependencies.getFieldBridge(bridge, name).pending.value;
81
+ dependencies.trackField(name, "pending");
84
82
  }
85
83
  return form.isFieldPending(name);
86
84
  };
87
85
  const getFieldsValue = (names) => {
88
86
  if (!destroyed) {
89
- const bridge = dependencies.getFormBridge(form);
90
- if (names === void 0) {
91
- void bridge.values.value;
92
- } else {
93
- for (const name of names) {
94
- void dependencies.getFieldBridge(bridge, name).value.value;
95
- }
96
- }
87
+ dependencies.trackFieldsValue(names);
97
88
  }
98
89
  return form.getFieldsValue(names);
99
90
  };
100
91
  const getTouchedFields = () => {
101
92
  if (!destroyed) {
102
- const bridge = dependencies.getFormBridge(form);
103
- void bridge.touchedFields.value;
93
+ dependencies.trackForm("touchedFields");
104
94
  }
105
95
  return form.getTouchedFields();
106
96
  };
107
97
  const getPendingFields = () => {
108
98
  if (!destroyed) {
109
- const bridge = dependencies.getFormBridge(form);
110
- void bridge.pendingFields.value;
99
+ dependencies.trackForm("pendingFields");
111
100
  }
112
101
  return form.getPendingFields();
113
102
  };
114
103
  const isLoading = () => {
115
104
  if (!destroyed) {
116
- const bridge = dependencies.getFormBridge(form);
117
- void bridge.loading.value;
105
+ dependencies.trackForm("loading");
118
106
  }
119
107
  return form.isLoading();
120
108
  };
@@ -123,10 +111,7 @@ function createVueFormFacade(form, dependencies) {
123
111
  return;
124
112
  }
125
113
  destroyed = true;
126
- const bridge = dependencies.getCachedFormBridge(form);
127
- if (bridge) {
128
- dependencies.disposeFormBridge(bridge);
129
- }
114
+ dependencies.destroy();
130
115
  form.destroy();
131
116
  };
132
117
  return {
@@ -142,122 +127,213 @@ function createVueFormFacade(form, dependencies) {
142
127
  destroy
143
128
  };
144
129
  }
145
- const formBridgeCache = /* @__PURE__ */ new WeakMap();
146
- const formFacadeCache = /* @__PURE__ */ new WeakMap();
147
- const facadeCoreFormCache = /* @__PURE__ */ new WeakMap();
148
- function getCoreForm(form) {
149
- const coreForm = facadeCoreFormCache.get(form);
150
- return coreForm ?? form;
130
+ function createVueViewSchemaState(form) {
131
+ const viewSchemas = shallowRef(
132
+ form.getViewSchemas()
133
+ );
134
+ const schemasByKey = computed(() => {
135
+ return createViewSchemaIndex(viewSchemas.value);
136
+ });
137
+ const unsubscribe = form.subscribeViewSchemas((nextSchemas) => {
138
+ viewSchemas.value = nextSchemas;
139
+ });
140
+ let disposed = false;
141
+ const dispose = () => {
142
+ if (disposed) {
143
+ return;
144
+ }
145
+ disposed = true;
146
+ unsubscribe();
147
+ };
148
+ return {
149
+ schemasByKey,
150
+ viewSchemas,
151
+ dispose
152
+ };
151
153
  }
152
- function getVueFormBridge(form) {
153
- const coreForm = getCoreForm(form);
154
- const cachedBridge = formBridgeCache.get(coreForm);
155
- if (cachedBridge) {
156
- return cachedBridge;
157
- }
158
- const bridge = createVueFormBridge(coreForm);
159
- formBridgeCache.set(coreForm, bridge);
160
- return bridge;
154
+ function createViewSchemaIndex(viewSchemas) {
155
+ const schemasByKey = /* @__PURE__ */ new Map();
156
+ const appendSchemas = (schemas) => {
157
+ for (const schema of schemas) {
158
+ schemasByKey.set(schema.key, schema);
159
+ if (isViewGroupSchema(schema)) {
160
+ appendSchemas(schema.children);
161
+ } else if (isViewDynamicSchema(schema)) {
162
+ for (const item of schema.items) {
163
+ appendSchemas(item.children);
164
+ }
165
+ }
166
+ }
167
+ };
168
+ appendSchemas(viewSchemas);
169
+ return schemasByKey;
170
+ }
171
+ const runtimeCache = /* @__PURE__ */ new WeakMap();
172
+ const instanceRuntimeCache = /* @__PURE__ */ new WeakMap();
173
+ function acquireVueFormRuntime(form) {
174
+ const runtime = getOrCreateVueFormRuntime(form);
175
+ const release = runtime.retain();
176
+ return {
177
+ runtime,
178
+ release
179
+ };
161
180
  }
162
- function getVueFormFacade(form) {
163
- const coreForm = getCoreForm(form);
164
- const cachedFacade = formFacadeCache.get(coreForm);
165
- if (cachedFacade) {
166
- return cachedFacade;
181
+ function getOrCreateVueFormRuntime(form) {
182
+ const cachedByInstance = instanceRuntimeCache.get(form);
183
+ if (cachedByInstance) {
184
+ return cachedByInstance;
167
185
  }
168
- const facade = createVueFormFacade(coreForm, {
169
- getFormBridge: (bridgeForm) => getVueFormBridge(bridgeForm),
170
- getFieldBridge: (bridge, name) => getVueFieldBridge(bridge, name),
171
- getCachedFormBridge: (bridgeForm) => {
172
- const bridge = formBridgeCache.get(bridgeForm);
173
- return bridge;
174
- },
175
- disposeFormBridge: (bridge) => disposeVueFormBridge(bridge)
176
- });
177
- formFacadeCache.set(coreForm, facade);
178
- facadeCoreFormCache.set(facade, coreForm);
179
- return facade;
186
+ const cachedByCore = runtimeCache.get(form);
187
+ if (cachedByCore) {
188
+ return cachedByCore;
189
+ }
190
+ const runtime = createVueFormRuntime(form);
191
+ runtimeCache.set(form, runtime);
192
+ instanceRuntimeCache.set(runtime.instance, runtime);
193
+ return runtime;
180
194
  }
181
- function retainVueFormBridge(bridge) {
182
- if (bridge.destroyed) {
195
+ function createVueFormRuntime(core) {
196
+ let resources;
197
+ let ownerCount = 0;
198
+ let destroyed = false;
199
+ const createResources = () => {
200
+ const stateAdapter = createFormStateAdapter(core);
201
+ const values = bindSnapshotSource(stateAdapter.values);
202
+ const touchedFields = bindSnapshotSource(stateAdapter.touchedFields);
203
+ const pendingFields = bindSnapshotSource(stateAdapter.pendingFields);
204
+ const loading = bindSnapshotSource(stateAdapter.loading);
205
+ const fieldStates = /* @__PURE__ */ new Map();
206
+ let viewSchemaState;
207
+ let disposed = false;
208
+ const dispose = () => {
209
+ if (disposed) {
210
+ return;
211
+ }
212
+ disposed = true;
213
+ fieldStates.clear();
214
+ stateAdapter.dispose();
215
+ };
216
+ return {
217
+ stateAdapter,
218
+ values,
219
+ touchedFields,
220
+ pendingFields,
221
+ loading,
222
+ fieldStates,
223
+ viewSchemaState,
224
+ dispose
225
+ };
226
+ };
227
+ const ensureResources = () => {
228
+ if (destroyed) {
229
+ throw new Error("[schemx] Vue Form Runtime has been destroyed.");
230
+ }
231
+ resources ?? (resources = createResources());
232
+ return resources;
233
+ };
234
+ const disposeResources = () => {
235
+ const currentResources = resources;
236
+ if (!currentResources) {
237
+ return;
238
+ }
239
+ resources = void 0;
240
+ currentResources.dispose();
241
+ };
242
+ const retain = () => {
243
+ if (destroyed) {
244
+ return () => {
245
+ };
246
+ }
247
+ ensureResources();
248
+ ownerCount++;
249
+ let released = false;
183
250
  return () => {
251
+ if (released || destroyed) {
252
+ return;
253
+ }
254
+ released = true;
255
+ ownerCount--;
256
+ if (ownerCount === 0) {
257
+ disposeResources();
258
+ }
184
259
  };
185
- }
186
- bridge.refCount++;
187
- let released = false;
188
- return () => {
189
- if (released || bridge.destroyed) {
260
+ };
261
+ const trackField = (name, dependency) => {
262
+ if (!resources) {
263
+ return;
264
+ }
265
+ const fieldState = getVueFieldState(resources, name);
266
+ void fieldState[dependency].value;
267
+ };
268
+ const trackFieldsValue = (names) => {
269
+ if (!resources) {
270
+ return;
271
+ }
272
+ if (names === void 0) {
273
+ void resources.values.value;
190
274
  return;
191
275
  }
192
- released = true;
193
- bridge.refCount--;
194
- if (bridge.refCount === 0) {
195
- disposeVueFormBridge(bridge);
276
+ for (const name of names) {
277
+ void getVueFieldState(resources, name).value.value;
196
278
  }
197
279
  };
198
- }
199
- function disposeVueFormBridge(bridge) {
200
- if (bridge.destroyed) {
201
- return;
202
- }
203
- bridge.destroyed = true;
204
- bridge.refCount = 0;
205
- bridge.unsubscribe();
206
- for (const fieldBridge of bridge.fieldBridges.values()) {
207
- fieldBridge.dispose();
208
- }
209
- bridge.fieldBridges.clear();
210
- bridge.stateAdapter.dispose();
211
- formBridgeCache.delete(bridge.form);
212
- }
213
- function createVueFormBridge(form) {
214
- const stateAdapter = createFormStateAdapter(form);
215
- const values = createVueShallowRef(stateAdapter.values.getSnapshot());
216
- const touchedFields = createVueShallowRef(
217
- stateAdapter.touchedFields.getSnapshot()
218
- );
219
- const pendingFields = createVueShallowRef(
220
- stateAdapter.pendingFields.getSnapshot()
221
- );
222
- const loading = createVueShallowRef(stateAdapter.loading.getSnapshot());
223
- const fieldBridges = /* @__PURE__ */ new Map();
224
- const syncValues = () => {
225
- values.value = stateAdapter.values.getSnapshot();
280
+ const trackForm = (dependency) => {
281
+ if (!resources) {
282
+ return;
283
+ }
284
+ switch (dependency) {
285
+ case "values":
286
+ void resources.values.value;
287
+ break;
288
+ case "touchedFields":
289
+ void resources.touchedFields.value;
290
+ break;
291
+ case "pendingFields":
292
+ void resources.pendingFields.value;
293
+ break;
294
+ case "loading":
295
+ void resources.loading.value;
296
+ break;
297
+ }
226
298
  };
227
- const syncTouchedFields = () => {
228
- touchedFields.value = stateAdapter.touchedFields.getSnapshot();
299
+ const getValuesRef = () => {
300
+ return ensureResources().values;
229
301
  };
230
- const syncPendingFields = () => {
231
- pendingFields.value = stateAdapter.pendingFields.getSnapshot();
302
+ const getFieldState = (name) => {
303
+ return getVueFieldState(ensureResources(), name);
232
304
  };
233
- const syncLoading = () => {
234
- loading.value = stateAdapter.loading.getSnapshot();
305
+ const getViewSchemaState = () => {
306
+ const currentResources = ensureResources();
307
+ const viewSchemaState = currentResources.viewSchemaState ?? (currentResources.viewSchemaState = createVueViewSchemaState(core));
308
+ return viewSchemaState;
235
309
  };
236
- const unsubscribeValues = stateAdapter.values.subscribe(syncValues);
237
- const unsubscribeTouchedFields = stateAdapter.touchedFields.subscribe(syncTouchedFields);
238
- const unsubscribePendingFields = stateAdapter.pendingFields.subscribe(syncPendingFields);
239
- const unsubscribeLoading = stateAdapter.loading.subscribe(syncLoading);
240
- const unsubscribe = () => {
241
- unsubscribeValues();
242
- unsubscribeTouchedFields();
243
- unsubscribePendingFields();
244
- unsubscribeLoading();
310
+ const destroy = () => {
311
+ if (destroyed) {
312
+ return;
313
+ }
314
+ destroyed = true;
315
+ ownerCount = 0;
316
+ disposeResources();
245
317
  };
246
- const facade = getVueFormFacade(form);
247
- const bridge = {
248
- form,
249
- stateAdapter,
250
- values,
251
- touchedFields,
252
- pendingFields,
253
- loading,
254
- fieldBridges,
255
- refCount: 0,
256
- destroyed: false,
257
- unsubscribe,
258
- facade
318
+ const instance = createVueFormInstance(core, {
319
+ trackField,
320
+ trackFieldsValue,
321
+ trackForm,
322
+ destroy
323
+ });
324
+ const runtime = {
325
+ core,
326
+ instance,
327
+ retain,
328
+ trackField,
329
+ trackFieldsValue,
330
+ trackForm,
331
+ getValuesRef,
332
+ getFieldState,
333
+ getViewSchemaState,
334
+ destroy
259
335
  };
260
- return bridge;
336
+ return runtime;
261
337
  }
262
338
  const SCHEMX_APP_CONFIG_KEY = Symbol("schemx:app-config");
263
339
  const EMPTY_SCHEMX_CONFIG = Object.freeze({
@@ -273,7 +349,8 @@ function normalizeSchemxAppConfig(config) {
273
349
  validatorAdapters,
274
350
  defaultRendererType: config.defaultRendererType,
275
351
  rendererRegistry: config.rendererRegistry,
276
- validationRuleRegistry: config.validationRuleRegistry
352
+ presetRuleRegistry: config.presetRuleRegistry,
353
+ colComponent: config.colComponent
277
354
  });
278
355
  }
279
356
  function normalizeRendererProps(source) {
@@ -295,27 +372,56 @@ function getSchemxAppConfig() {
295
372
  }
296
373
  return inject(SCHEMX_APP_CONFIG_KEY, EMPTY_SCHEMX_CONFIG);
297
374
  }
375
+ const SCHEMX_CONFIG_PROVIDER_KEY = Symbol(
376
+ "schemx:config-provider"
377
+ );
378
+ function provideSchemxConfigProvider(config) {
379
+ const parentConfig = inject(SCHEMX_CONFIG_PROVIDER_KEY, void 0);
380
+ const mergedConfig = computed(() => {
381
+ const mergedCoreConfig = mergeSchemxConfig(config.value, (parentConfig == null ? void 0 : parentConfig.value) ?? {});
382
+ return {
383
+ ...mergedCoreConfig,
384
+ colComponent: config.value.colComponent ?? (parentConfig == null ? void 0 : parentConfig.value.colComponent)
385
+ };
386
+ });
387
+ provide(SCHEMX_CONFIG_PROVIDER_KEY, mergedConfig);
388
+ }
389
+ function getSchemxConfigProvider() {
390
+ var _a;
391
+ return (_a = getSchemxConfigProviderRef()) == null ? void 0 : _a.value;
392
+ }
393
+ function getSchemxConfigProviderRef() {
394
+ if (getCurrentInstance() === null) {
395
+ return void 0;
396
+ }
397
+ return inject(SCHEMX_CONFIG_PROVIDER_KEY, void 0);
398
+ }
399
+ const presetRuleRegistry = createPresetRuleRegistry();
298
400
  const rendererRegistry = createRendererRegistry("input");
299
- const validationRuleRegistry = createValidationRuleRegistry();
300
- function useForm(options = {}) {
301
- const configuredOptions = mergeSchemxConfig(
302
- getUseFormSchemxConfig(options),
303
- // App 安装配置以 Values 存储;在 useForm 边界关联到当前 TValues。
401
+ function mergeVueSchemxConfig(localConfig, fallbackConfig = {}) {
402
+ const providerConfig = getSchemxConfigProvider();
403
+ return mergeSchemxConfig(
404
+ localConfig,
405
+ providerConfig ?? {},
304
406
  getSchemxAppConfig(),
407
+ fallbackConfig,
305
408
  {
306
409
  rendererRegistry,
307
- validationRuleRegistry
410
+ presetRuleRegistry
308
411
  }
309
412
  );
413
+ }
414
+ function useForm(options = {}) {
415
+ const configuredOptions = mergeVueSchemxConfig(getUseFormSchemxConfig(options));
310
416
  const mergedOptions = {
311
417
  ...options,
312
418
  ...configuredOptions
313
419
  };
314
420
  const instance = createForm(mergedOptions);
315
- const form = getVueFormFacade(instance);
316
- const releaseBridge = retainVueFormBridge(getVueFormBridge(instance));
421
+ const acquired = acquireVueFormRuntime(instance);
422
+ const form = acquired.runtime.instance;
317
423
  onScopeDispose(() => {
318
- releaseBridge();
424
+ acquired.release();
319
425
  form.destroy();
320
426
  });
321
427
  return form;
@@ -327,24 +433,32 @@ function getUseFormSchemxConfig(options) {
327
433
  validatorAdapters = [],
328
434
  defaultRendererType = void 0,
329
435
  rendererRegistry: rendererRegistry2 = void 0,
330
- validationRuleRegistry: validationRuleRegistry2 = void 0
436
+ presetRuleRegistry: presetRuleRegistry2 = void 0
331
437
  } = options;
332
438
  return {
333
439
  schemaConfig,
334
440
  rendererProps,
335
441
  defaultRendererType,
336
442
  rendererRegistry: rendererRegistry2,
337
- validationRuleRegistry: validationRuleRegistry2,
443
+ presetRuleRegistry: presetRuleRegistry2,
338
444
  validatorAdapters
339
445
  };
340
446
  }
341
- const SCHEMX_FORM_INSTANCE_KEY = Symbol("schemx:instance");
447
+ const SCHEMX_FORM_INSTANCE_KEY = Symbol(
448
+ "schemx:instance"
449
+ );
450
+ const SCHEMX_FORM_RUNTIME_KEY = Symbol(
451
+ "schemx:runtime"
452
+ );
342
453
  function createFormContext(instance) {
343
- const form = getVueFormFacade(instance);
344
- const releaseBridge = retainVueFormBridge(getVueFormBridge(form));
345
- provide(SCHEMX_FORM_INSTANCE_KEY, form);
346
- onScopeDispose(releaseBridge);
347
- return form;
454
+ const { runtime, release } = acquireVueFormRuntime(instance);
455
+ provide(
456
+ SCHEMX_FORM_INSTANCE_KEY,
457
+ runtime.instance
458
+ );
459
+ provide(SCHEMX_FORM_RUNTIME_KEY, runtime);
460
+ onScopeDispose(release);
461
+ return runtime.instance;
348
462
  }
349
463
  function useFormContext() {
350
464
  const instance = inject(SCHEMX_FORM_INSTANCE_KEY, null);
@@ -355,21 +469,30 @@ function useFormContext() {
355
469
  }
356
470
  return instance;
357
471
  }
358
- function createFieldHook(form, name, fieldBridge) {
359
- const coreForm = getCoreForm(form);
472
+ function useFormRuntimeContext() {
473
+ const providedRuntime = inject(SCHEMX_FORM_RUNTIME_KEY, null);
474
+ if (providedRuntime) {
475
+ return providedRuntime;
476
+ }
477
+ const instance = useFormContext();
478
+ const acquired = acquireVueFormRuntime(instance);
479
+ onScopeDispose(acquired.release);
480
+ return acquired.runtime;
481
+ }
482
+ function createFieldHook(form, coreForm, name, fieldState) {
360
483
  const coreField = createField(coreForm, name);
361
- const errors = computed(() => fieldBridge.errors.value);
362
- const dirty = computed(() => fieldBridge.touched.value);
363
- const pending = computed(() => fieldBridge.pending.value);
364
- const getValue = () => fieldBridge.value.value;
365
- const getErrors = () => fieldBridge.errors.value;
366
- const isTouched = () => fieldBridge.touched.value;
367
- const isPending = () => fieldBridge.pending.value;
484
+ const errors = computed(() => fieldState.errors.value);
485
+ const dirty = computed(() => fieldState.touched.value);
486
+ const pending = computed(() => fieldState.pending.value);
487
+ const getValue = () => fieldState.value.value;
488
+ const getErrors = () => fieldState.errors.value;
489
+ const isTouched = () => fieldState.touched.value;
490
+ const isPending = () => fieldState.pending.value;
368
491
  const getValues = () => form.getFieldsValue();
369
492
  return {
370
493
  ...coreField,
371
494
  name,
372
- value: fieldBridge.value,
495
+ value: fieldState.value,
373
496
  errors,
374
497
  dirty,
375
498
  pending,
@@ -382,10 +505,9 @@ function createFieldHook(form, name, fieldBridge) {
382
505
  }
383
506
  const useField = (name) => {
384
507
  const form = useFormContext();
385
- const coreForm = getCoreForm(form);
386
- const formBridge = getVueFormBridge(coreForm);
387
- const fieldBridge = getVueFieldBridge(formBridge, name);
388
- return createFieldHook(form, name, fieldBridge);
508
+ const runtime = useFormRuntimeContext();
509
+ const fieldState = runtime.getFieldState(name);
510
+ return createFieldHook(form, runtime.core, name, fieldState);
389
511
  };
390
512
  const SCHEMX_FORM_FIELD_KEY = Symbol(
391
513
  "schemx:field"
@@ -403,9 +525,9 @@ function useFieldContext() {
403
525
  return field;
404
526
  }
405
527
  function useWatch(nameOrNamesOrCallback, callbackOrOptions, maybeOptions) {
406
- const form = useFormContext();
528
+ const runtime = useFormRuntimeContext();
407
529
  const dispose = createWatch(
408
- getCoreForm(form),
530
+ runtime.core,
409
531
  nameOrNamesOrCallback,
410
532
  callbackOrOptions,
411
533
  maybeOptions
@@ -434,6 +556,7 @@ const useDictionary = (options, fieldName) => {
434
556
  const loading = ref(false);
435
557
  const error = ref(void 0);
436
558
  let requestCount = 0;
559
+ let activeController = null;
437
560
  const format = async (res) => {
438
561
  if (typeof (options == null ? void 0 : options.formatter) === "function") {
439
562
  return await options.formatter(res, instance);
@@ -445,25 +568,32 @@ const useDictionary = (options, fieldName) => {
445
568
  }
446
569
  return res;
447
570
  };
448
- const executeWithRetry = async (formValues) => {
571
+ const executeWithRetry = async (formValues, signal) => {
449
572
  const maxRetries = options.retryCount ?? 0;
450
573
  const retryDelay = options.retryInterval ?? 1e3;
451
574
  let lastError = new Error("Unknown error");
452
575
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
453
576
  try {
454
- return await options.api(formValues, instance);
577
+ if (signal.aborted) {
578
+ throw new Error("Dictionary request aborted");
579
+ }
580
+ return await callDictionaryApi(formValues, instance, options.api, signal);
455
581
  } catch (err) {
456
582
  lastError = normalizeError(err);
457
583
  if (attempt < maxRetries) {
458
- await new Promise((r) => setTimeout(r, retryDelay));
584
+ await waitForRetry(retryDelay, signal);
459
585
  }
460
586
  }
461
587
  }
462
588
  throw lastError;
463
589
  };
464
590
  const loadDict = async () => {
591
+ const currentCount = ++requestCount;
592
+ activeController == null ? void 0 : activeController.abort();
593
+ const controller = new AbortController();
594
+ activeController = controller;
465
595
  try {
466
- const formValues = instance.getFieldsValue();
596
+ const formValues = instance.getFieldsSnapshot();
467
597
  if (typeof options.shouldFetch === "function" && !options.shouldFetch(formValues)) {
468
598
  list.value = [];
469
599
  loading.value = false;
@@ -471,12 +601,11 @@ const useDictionary = (options, fieldName) => {
471
601
  }
472
602
  loading.value = true;
473
603
  error.value = void 0;
474
- const currentCount = ++requestCount;
475
- const res = await executeWithRetry(formValues);
476
- if (currentCount !== requestCount)
604
+ const res = await executeWithRetry(formValues, controller.signal);
605
+ if (currentCount !== requestCount || controller.signal.aborted)
477
606
  return;
478
607
  const formatted = await format(res);
479
- if (currentCount !== requestCount)
608
+ if (currentCount !== requestCount || controller.signal.aborted)
480
609
  return;
481
610
  list.value = formatted;
482
611
  error.value = void 0;
@@ -485,6 +614,9 @@ const useDictionary = (options, fieldName) => {
485
614
  }
486
615
  loading.value = false;
487
616
  } catch (err) {
617
+ if (controller.signal.aborted || currentCount !== requestCount) {
618
+ return;
619
+ }
488
620
  const normalized = normalizeError(err);
489
621
  error.value = normalized;
490
622
  list.value = [];
@@ -492,6 +624,10 @@ const useDictionary = (options, fieldName) => {
492
624
  if (typeof options.onError === "function") {
493
625
  options.onError(normalized, instance);
494
626
  }
627
+ } finally {
628
+ if (activeController === controller) {
629
+ activeController = null;
630
+ }
495
631
  }
496
632
  };
497
633
  const refresh = () => loadDict();
@@ -499,15 +635,19 @@ const useDictionary = (options, fieldName) => {
499
635
  list.value = data;
500
636
  };
501
637
  if ((_a = options.dependsOn) == null ? void 0 : _a.length) {
502
- useWatchFields(options.dependsOn, (latestSnapshot, _payload) => {
503
- if (typeof options.onDepsChange === "function") {
504
- options.onDepsChange(latestSnapshot, instance);
505
- }
506
- if (options.resetOnDepsChange && fieldName) {
507
- instance.setFieldValue(fieldName, void 0);
508
- }
509
- void loadDict();
510
- });
638
+ useWatchFields(
639
+ options.dependsOn,
640
+ (latestSnapshot, _payload) => {
641
+ if (typeof options.onDepsChange === "function") {
642
+ options.onDepsChange(latestSnapshot, instance);
643
+ }
644
+ if (options.resetOnDepsChange && fieldName) {
645
+ instance.setFieldValue(fieldName, void 0);
646
+ }
647
+ void loadDict();
648
+ },
649
+ { inequality: true }
650
+ );
511
651
  }
512
652
  const immediate = options.immediate ?? true;
513
653
  onMounted(() => {
@@ -515,8 +655,43 @@ const useDictionary = (options, fieldName) => {
515
655
  void loadDict();
516
656
  }
517
657
  });
658
+ onScopeDispose(() => {
659
+ requestCount += 1;
660
+ activeController == null ? void 0 : activeController.abort();
661
+ activeController = null;
662
+ });
518
663
  return { list, loading, error, loadDict, refresh, mutate };
519
664
  };
665
+ function waitForRetry(delay, signal) {
666
+ return new Promise((resolve, reject) => {
667
+ let abort = () => {
668
+ };
669
+ const cleanup = () => {
670
+ clearTimeout(timeoutId);
671
+ signal.removeEventListener("abort", abort);
672
+ };
673
+ const complete = () => {
674
+ cleanup();
675
+ resolve();
676
+ };
677
+ const timeoutId = setTimeout(complete, delay);
678
+ abort = () => {
679
+ cleanup();
680
+ reject(new Error("Dictionary request aborted"));
681
+ };
682
+ if (signal.aborted) {
683
+ abort();
684
+ return;
685
+ }
686
+ signal.addEventListener("abort", abort, { once: true });
687
+ });
688
+ }
689
+ function callDictionaryApi(values, form, api, signal) {
690
+ if (api.length >= 3) {
691
+ return api(values, form, signal);
692
+ }
693
+ return api(values, form);
694
+ }
520
695
  const SCHEMX_FORM_CONFIG_KEY = Symbol(
521
696
  "schemx:form-config"
522
697
  );
@@ -549,87 +724,37 @@ function useStableRef(factory) {
549
724
  });
550
725
  return stableRef;
551
726
  }
552
- const viewSchemaBridgeCache = /* @__PURE__ */ new WeakMap();
553
727
  function useViewSchemas(form) {
554
- const bridge = acquireViewSchemaBridge(getCoreForm(form));
555
- return bridge.viewSchemas;
728
+ const acquired = acquireVueFormRuntime(form);
729
+ const state = acquired.runtime.getViewSchemaState();
730
+ onScopeDispose(acquired.release);
731
+ return state.viewSchemas;
556
732
  }
557
733
  function useViewSchema(form, getKey) {
558
- const bridge = acquireViewSchemaBridge(getCoreForm(form));
559
- return computed(() => bridge.schemasByKey.value.get(getKey()));
560
- }
561
- function createViewSchemaBridge(form) {
562
- const viewSchemas = shallowRef(
563
- form.getViewSchemas()
564
- );
565
- const schemasByKey = computed(() => {
566
- return createViewSchemaIndex(viewSchemas.value);
567
- });
568
- const unsubscribe = form.subscribeViewSchemas(
569
- /**
570
- * 接收 Core 发布的完整列表并替换共享引用,以保留更新边界。
571
- *
572
- * @param nextSchemas - Core 计算出的最新 ViewSchema 列表。
573
- */
574
- (nextSchemas) => {
575
- viewSchemas.value = nextSchemas;
576
- }
577
- );
578
- return {
579
- refCount: 0,
580
- schemasByKey,
581
- unsubscribe,
582
- viewSchemas
583
- };
584
- }
585
- function acquireViewSchemaBridge(form) {
586
- const cachedBridge = viewSchemaBridgeCache.get(form);
587
- const bridge = cachedBridge ?? createViewSchemaBridge(form);
588
- if (!cachedBridge) {
589
- viewSchemaBridgeCache.set(form, bridge);
590
- }
591
- bridge.refCount++;
592
- onScopeDispose(() => {
593
- bridge.refCount--;
594
- if (bridge.refCount > 0) {
595
- return;
596
- }
597
- bridge.unsubscribe();
598
- viewSchemaBridgeCache.delete(form);
599
- });
600
- return bridge;
601
- }
602
- function createViewSchemaIndex(viewSchemas) {
603
- const schemasByKey = /* @__PURE__ */ new Map();
604
- const appendSchemas = (schemas) => {
605
- for (const schema of schemas) {
606
- schemasByKey.set(schema.key, schema);
607
- if (isViewGroupSchema(schema)) {
608
- appendSchemas(schema.children);
609
- }
610
- }
611
- };
612
- appendSchemas(viewSchemas);
613
- return schemasByKey;
734
+ const acquired = acquireVueFormRuntime(form);
735
+ const state = acquired.runtime.getViewSchemaState();
736
+ const schema = computed(() => state.schemasByKey.value.get(getKey()));
737
+ onScopeDispose(acquired.release);
738
+ return schema;
614
739
  }
615
740
  function useFormSelector(form, selector, options = {}) {
616
- const bridge = getVueFormBridge(getCoreForm(form));
617
- const releaseBridge = retainVueFormBridge(bridge);
618
- const selected = shallowRef(selector(bridge.values.value));
741
+ const acquired = acquireVueFormRuntime(form);
742
+ const values = acquired.runtime.getValuesRef();
743
+ const selected = shallowRef(selector(values.value));
619
744
  const equals = options.equals ?? Object.is;
620
745
  const stop = watch(
621
- bridge.values,
622
- (values) => {
623
- const next = selector(values);
746
+ values,
747
+ (values2) => {
748
+ const next = selector(values2);
624
749
  if (!equals(selected.value, next)) {
625
750
  selected.value = next;
626
751
  }
627
752
  },
628
- { flush: "sync" }
753
+ { flush: options.flush ?? "sync" }
629
754
  );
630
755
  onScopeDispose(() => {
631
756
  stop();
632
- releaseBridge();
757
+ acquired.release();
633
758
  });
634
759
  return readonly(selected);
635
760
  }
@@ -703,105 +828,57 @@ const extractChildSlots = (fieldName, allSlots) => {
703
828
  }
704
829
  return result;
705
830
  };
706
- const FormGroup = /* @__PURE__ */ defineComponent((props, {
707
- slots
708
- }) => {
709
- var _a;
710
- const internalCollapsed = ref(Boolean(props.schema.defaultCollapsed));
711
- const collapsed = computed(() => props.schema.collapsed ?? internalCollapsed.value);
712
- const componentId = ((_a = getCurrentInstance()) == null ? void 0 : _a.uid) ?? 0;
713
- watch(() => props.schema.collapsed, (nextCollapsed, previousCollapsed) => {
714
- if (nextCollapsed !== void 0) {
715
- internalCollapsed.value = nextCollapsed;
716
- } else if (previousCollapsed !== void 0) {
717
- internalCollapsed.value = previousCollapsed;
718
- }
719
- });
720
- const toggle = () => {
721
- var _a2, _b;
722
- if (!props.schema.collapsible || props.schema.disabled) {
723
- return;
724
- }
725
- const nextCollapsed = !collapsed.value;
726
- if (props.schema.collapsed === void 0) {
727
- internalCollapsed.value = nextCollapsed;
728
- }
729
- (_b = (_a2 = props.schema).onCollapsedChange) == null ? void 0 : _b.call(_a2, nextCollapsed);
831
+ function getSectionPosition(list, currentKey) {
832
+ const currentIndex = list.findIndex((item) => item.key === currentKey);
833
+ if (currentIndex === -1) {
834
+ return {
835
+ found: false,
836
+ isFirst: false,
837
+ isLast: false
838
+ };
839
+ }
840
+ const currentItem = list[currentIndex];
841
+ if (!isPositionItem(currentItem)) {
842
+ return {
843
+ found: true,
844
+ isFirst: false,
845
+ isLast: false
846
+ };
847
+ }
848
+ return {
849
+ found: true,
850
+ isFirst: !hasPositionItemInSection(list, currentIndex, -1),
851
+ isLast: !hasPositionItemInSection(list, currentIndex, 1)
730
852
  };
731
- return () => {
732
- var _a2;
733
- const schema = props.schema;
734
- if (schema.visible === false) {
735
- return null;
853
+ }
854
+ function normalizeNameKey(name) {
855
+ if (Array.isArray(name)) {
856
+ return name.map((part) => String(part)).join(".");
857
+ }
858
+ return String(name);
859
+ }
860
+ function normalizeId(key) {
861
+ return String(key).replace(/[^a-zA-Z0-9_-]/g, "-");
862
+ }
863
+ function isPositionItem(item) {
864
+ return !!item && !isViewGroupSchema(item) && item.visible !== false;
865
+ }
866
+ function hasPositionItemInSection(list, startIndex, step) {
867
+ for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {
868
+ const item = list[index];
869
+ if (item.visible === false) {
870
+ continue;
736
871
  }
737
- const collapsible = Boolean(schema.collapsible);
738
- const isCollapsed = collapsed.value;
739
- const destroyOnCollapse = schema.destroyOnCollapse ?? true;
740
- const uniqueId = ((_a2 = schema.debug) == null ? void 0 : _a2.runtimeNodeId) ?? `local-${componentId}`;
741
- const idBase = `schemx-group-${uniqueId}-${normalizeId(schema.key)}`;
742
- const headerId = `${idBase}-header`;
743
- const bodyId = `${idBase}-body`;
744
- const body = createVNode("div", {
745
- "id": bodyId,
746
- "role": "group",
747
- "aria-labelledby": schema.label ? headerId : void 0,
748
- "aria-hidden": isCollapsed || void 0,
749
- "class": "schemx-group__body",
750
- "style": !destroyOnCollapse && isCollapsed ? {
751
- display: "none"
752
- } : void 0
753
- }, [schema.children.map((child) => createVNode(FormItem$1, {
754
- "key": child.key,
755
- "schema": child
756
- }, slots))]);
757
- return createVNode("div", {
758
- "class": classnames("schemx-group", {
759
- "schemx-group--collapsed": isCollapsed,
760
- "is-readonly": schema.readonly,
761
- "is-disabled": schema.disabled
762
- }, schema.class),
763
- "style": schema.style,
764
- "data-key": schema.key,
765
- "aria-disabled": schema.disabled || void 0
766
- }, [schema.label && createVNode("div", {
767
- "id": headerId,
768
- "role": collapsible ? "button" : void 0,
769
- "tabindex": collapsible ? schema.disabled ? -1 : 0 : void 0,
770
- "aria-expanded": collapsible ? !isCollapsed : void 0,
771
- "aria-controls": collapsible ? bodyId : void 0,
772
- "aria-disabled": collapsible ? schema.disabled || void 0 : void 0,
773
- "class": classnames("schemx-group__header", {
774
- "schemx-group__header--clickable": collapsible && !schema.disabled
775
- }),
776
- "onClick": toggle,
777
- "onKeydown": (e) => {
778
- if (!schema.disabled && (e.key === "Enter" || e.key === " ")) {
779
- e.preventDefault();
780
- toggle();
781
- }
782
- }
783
- }, [createVNode("span", {
784
- "class": "schemx-group__title"
785
- }, [schema.label]), collapsible && createVNode("span", {
786
- "class": classnames("schemx-group__arrow", {
787
- "schemx-group__arrow--down": !isCollapsed
788
- })
789
- }, null)]), destroyOnCollapse ? !isCollapsed && body : body]);
790
- };
791
- }, {
792
- name: "SchemxGroup",
793
- props: {
794
- schema: {
795
- type: Object,
796
- required: true
872
+ if (isViewGroupSchema(item)) {
873
+ return false;
874
+ }
875
+ if (isPositionItem(item)) {
876
+ return true;
797
877
  }
798
878
  }
799
- });
800
- const FormGroup$1 = FormGroup;
801
- const normalizeId = (key) => {
802
- return String(key).replace(/[^a-zA-Z0-9_-]/g, "-");
803
- };
804
- function createFormItemSlotRenderers(options) {
879
+ return false;
880
+ }
881
+ function createFieldSlotRenderers(options) {
805
882
  const {
806
883
  schemaRef,
807
884
  field,
@@ -816,36 +893,39 @@ function createFormItemSlotRenderers(options) {
816
893
  return null;
817
894
  }
818
895
  return createVNode("span", {
819
- "class": "schemx-item__required"
896
+ "class": "schemx-field__required"
820
897
  }, [createTextVNode("*")]);
821
898
  };
822
899
  const createSlotProps = (additionalProps = {}) => {
823
900
  return {
824
- ...componentProps.value,
901
+ schema: schemaRef.value,
902
+ componentProps: componentProps.value,
825
903
  value: field.value.value,
904
+ field,
905
+ form,
826
906
  ...additionalProps
827
907
  };
828
908
  };
829
909
  const renderFieldSlot = (suffix) => {
830
- const slot = resolveSlot(slots, `${schemaRef.value.name}${suffix}`);
910
+ const slot = resolveSlot(slots, `${normalizeNameKey(schemaRef.value.name)}${suffix}`);
831
911
  return (slot == null ? void 0 : slot(createSlotProps())) ?? null;
832
912
  };
833
913
  const renderLabel = () => {
834
- const labelSlot = resolveSlot(slots, `${schemaRef.value.name}Label`);
914
+ const labelSlot = resolveSlot(slots, `${normalizeNameKey(schemaRef.value.name)}Label`);
835
915
  if (labelSlot) {
836
- return labelSlot(schemaRef.value);
916
+ return labelSlot(createSlotProps());
837
917
  }
838
918
  const labelAlign = schemaRef.value.labelAlign || formContext.schemaConfig.labelAlign;
839
919
  const labelWidth = schemaRef.value.labelWidth || formContext.schemaConfig.labelWidth;
840
920
  const colon = schemaRef.value.colon ?? formContext.schemaConfig.colon;
841
921
  return createVNode("label", {
842
- "class": "schemx-item__label",
922
+ "class": "schemx-field__label",
843
923
  "style": {
844
924
  width: labelWidth,
845
925
  textAlign: labelAlign
846
926
  }
847
927
  }, [renderRequired(), createVNode("span", {
848
- "class": "schemx-item__label-text"
928
+ "class": "schemx-field__label-text"
849
929
  }, [schemaRef.value.label, colon ? ":" : ""])]);
850
930
  };
851
931
  const renderBefore = () => renderFieldSlot("Before");
@@ -856,19 +936,19 @@ function createFormItemSlotRenderers(options) {
856
936
  }
857
937
  const childSlots = extractChildSlots(normalizeNameKey(schemaRef.value.name), slots);
858
938
  const columnElement = h(component, componentProps.value, childSlots);
859
- const contentSlot = resolveSlot(slots, `${schemaRef.value.name}Content`);
939
+ const contentSlot = resolveSlot(slots, `${normalizeNameKey(schemaRef.value.name)}Content`);
860
940
  if (contentSlot) {
861
941
  return contentSlot(createSlotProps({
862
942
  columnElement
863
943
  }));
864
944
  }
865
945
  return createVNode("div", {
866
- "class": "schemx-item__control"
946
+ "class": "schemx-field__control"
867
947
  }, [columnElement]);
868
948
  };
869
949
  const renderAfter = () => renderFieldSlot("After");
870
950
  const renderError = () => {
871
- const errorSlot = resolveSlot(slots, `${schemaRef.value.name}Error`);
951
+ const errorSlot = resolveSlot(slots, `${normalizeNameKey(schemaRef.value.name)}Error`);
872
952
  if (errorSlot) {
873
953
  return errorSlot(createSlotProps({
874
954
  errors: field.errors.value
@@ -878,7 +958,7 @@ function createFormItemSlotRenderers(options) {
878
958
  return null;
879
959
  }
880
960
  return createVNode("div", {
881
- "class": "schemx-item__error"
961
+ "class": "schemx-field__error"
882
962
  }, [field.errors.value[0]]);
883
963
  };
884
964
  return {
@@ -890,49 +970,23 @@ function createFormItemSlotRenderers(options) {
890
970
  renderLabel
891
971
  };
892
972
  }
893
- function normalizeNameKey(name) {
894
- if (Array.isArray(name)) {
895
- return name.map((part) => String(part)).join(".");
896
- }
897
- return String(name);
898
- }
899
- const FormItem = /* @__PURE__ */ defineComponent({
900
- name: "SchemxItem",
901
- props: {
902
- schema: {
903
- type: Object,
904
- required: true
905
- }
906
- },
907
- /**
908
- * 根据 schema 类型分发字段组或字段项渲染。
909
- *
910
- * @param props - 当前组件的 schema 属性。
911
- * @param slots - Vue setup 上下文提供的插槽集合。
912
- */
913
- setup(props, {
914
- slots
915
- }) {
916
- return () => {
917
- const schema = props.schema;
918
- if (isViewGroupSchema(schema)) {
919
- return h(FormGroup$1, {
920
- schema
921
- }, slots);
922
- }
923
- return h(FieldFormItem, {
924
- schema
925
- }, slots);
926
- };
927
- }
928
- });
929
- const FieldFormItem = /* @__PURE__ */ defineComponent({
930
- name: "SchemxFieldItem",
973
+ const Field = /* @__PURE__ */ defineComponent({
974
+ name: "SchemxField",
931
975
  inheritAttrs: false,
932
976
  props: {
933
977
  schema: {
934
978
  type: Object,
935
979
  required: true
980
+ },
981
+ class: {
982
+ type: [String, Object, Array, Boolean],
983
+ required: false,
984
+ default: void 0
985
+ },
986
+ style: {
987
+ type: [String, Object, Array],
988
+ required: false,
989
+ default: void 0
936
990
  }
937
991
  },
938
992
  /**
@@ -957,7 +1011,7 @@ const FieldFormItem = /* @__PURE__ */ defineComponent({
957
1011
  createFieldContext(field);
958
1012
  const trigger = computed(() => mergeTrigger(schemaRef.value.validationTrigger, formContext.schemaConfig.validationTrigger, "onChange"));
959
1013
  const canVerified = computed(() => {
960
- const isOperate = schemaRef.value.visible && !schemaRef.value.readonly && !schemaRef.value.disabled;
1014
+ const isOperate = schemaRef.value.visible !== false && !schemaRef.value.readonly && !schemaRef.value.disabled;
961
1015
  const rules = schemaRef.value.rules;
962
1016
  const hasRules = Array.isArray(rules) ? (rules == null ? void 0 : rules.length) > 0 : !!schemaRef.value.rules;
963
1017
  return isOperate && (Boolean(schemaRef.value.required) || hasRules);
@@ -997,7 +1051,7 @@ const FieldFormItem = /* @__PURE__ */ defineComponent({
997
1051
  renderContent,
998
1052
  renderError,
999
1053
  renderLabel
1000
- } = createFormItemSlotRenderers({
1054
+ } = createFieldSlotRenderers({
1001
1055
  schemaRef,
1002
1056
  field,
1003
1057
  form,
@@ -1006,32 +1060,27 @@ const FieldFormItem = /* @__PURE__ */ defineComponent({
1006
1060
  slots
1007
1061
  });
1008
1062
  return () => {
1009
- if (!schemaRef.value.visible) {
1063
+ if (schemaRef.value.visible === false) {
1010
1064
  return null;
1011
1065
  }
1012
- const itemSlot = resolveSlot(slots, normalizeNameKey(schemaRef.value.name));
1013
- if (itemSlot) {
1014
- return itemSlot(createSlotProps());
1015
- }
1066
+ const fieldSlot = resolveSlot(slots, normalizeNameKey(schemaRef.value.name));
1016
1067
  const labelPosition = schemaRef.value.labelPosition || formContext.schemaConfig.labelPosition;
1017
- return createVNode("div", mergeProps(attrs, {
1018
- "class": classnames("schemx-item-wrapper", attrs.class),
1019
- "style": [attrs.style, schemaRef.value.style]
1020
- }), [createVNode("div", {
1021
- "class": classnames("schemx-item", `schemx-item--label-${labelPosition}`, schemaRef.value.class, {
1068
+ const fieldContent = fieldSlot ? fieldSlot(createSlotProps()) : createVNode("div", {
1069
+ "class": classnames("schemx-field", `schemx-field--label-${labelPosition}`, {
1022
1070
  "is-readonly": schemaRef.value.readonly,
1023
1071
  "is-disabled": schemaRef.value.disabled
1024
- }),
1025
- "style": {
1026
- ...schemaRef.value.style ?? {}
1027
- }
1072
+ })
1028
1073
  }, [renderLabel(), createVNode("div", {
1029
- "class": "schemx-item__content"
1030
- }, [renderBefore(), renderContent(), renderAfter(), renderError()])])]);
1074
+ "class": "schemx-field__content"
1075
+ }, [renderBefore(), renderContent(), renderAfter(), renderError()])]);
1076
+ return createVNode("div", mergeProps(attrs, {
1077
+ "class": classnames("schemx-field-wrapper", props.class, attrs.class, schemaRef.value.class),
1078
+ "style": [attrs.style, props.style, schemaRef.value.style]
1079
+ }), [fieldContent]);
1031
1080
  };
1032
1081
  }
1033
1082
  });
1034
- const FormItem$1 = FormItem;
1083
+ const Field$1 = Field;
1035
1084
  const _hoisted_1$1 = ["aria-busy", "data-loading", "disabled"];
1036
1085
  const _hoisted_2 = {
1037
1086
  key: 0,
@@ -1054,7 +1103,7 @@ const _hoisted_4 = {
1054
1103
  key: 4,
1055
1104
  class: "schemx-button__suffix"
1056
1105
  };
1057
- const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1106
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
1058
1107
  ...{ name: "SchemxButton", inheritAttrs: false },
1059
1108
  __name: "index",
1060
1109
  props: {
@@ -1091,69 +1140,431 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1091
1140
  };
1092
1141
  }
1093
1142
  });
1094
- function getSectionPosition(list, currentKey) {
1095
- const currentIndex = list.findIndex((item) => item.key === currentKey);
1096
- if (currentIndex === -1) {
1097
- return {
1098
- found: false,
1099
- isFirst: false,
1100
- isLast: false
1101
- };
1102
- }
1103
- const currentItem = list[currentIndex];
1104
- if (!isPositionItem(currentItem)) {
1105
- return {
1106
- found: true,
1107
- isFirst: false,
1108
- isLast: false
1143
+ const registeredColComponent = shallowRef();
1144
+ function registerCol(component) {
1145
+ registeredColComponent.value = component;
1146
+ }
1147
+ const Col = /* @__PURE__ */ defineComponent({
1148
+ name: "SchemxCol",
1149
+ inheritAttrs: false,
1150
+ props: {
1151
+ layout: {
1152
+ type: Object,
1153
+ required: false,
1154
+ default: void 0
1155
+ },
1156
+ component: {
1157
+ type: [Object, Function],
1158
+ required: false,
1159
+ default: void 0
1160
+ }
1161
+ },
1162
+ setup(props, {
1163
+ attrs,
1164
+ slots
1165
+ }) {
1166
+ const resolvedComponent = computed(() => {
1167
+ const component = props.component ?? registeredColComponent.value;
1168
+ return component ? markRaw(toRaw(component)) : void 0;
1169
+ });
1170
+ return () => {
1171
+ var _a;
1172
+ const component = resolvedComponent.value;
1173
+ if (!component) {
1174
+ return ((_a = slots.default) == null ? void 0 : _a.call(slots)) ?? null;
1175
+ }
1176
+ const layout = props.layout ?? {};
1177
+ const span = layout.block ? 24 : layout.span ?? 24;
1178
+ const offset = layout.block ? 0 : layout.offset ?? 0;
1179
+ const {
1180
+ class: attrsClass,
1181
+ style: attrsStyle,
1182
+ ...restAttrs
1183
+ } = attrs;
1184
+ return h(component, {
1185
+ ...restAttrs,
1186
+ class: ["schemx-col", attrsClass],
1187
+ style: attrsStyle,
1188
+ span,
1189
+ offset
1190
+ }, slots);
1109
1191
  };
1110
1192
  }
1193
+ });
1194
+ const Col$1 = Col;
1195
+ function createGroupSlotRenderers(options) {
1196
+ const {
1197
+ schema,
1198
+ collapsed,
1199
+ collapsible,
1200
+ disabled,
1201
+ readonly: readonly2,
1202
+ toggle,
1203
+ slots,
1204
+ renderChildren
1205
+ } = options;
1206
+ const slotKey = String(schema.key);
1207
+ const headerSlot = resolveSlot(slots, `${slotKey}Header`);
1208
+ const labelSlot = resolveSlot(slots, `${slotKey}Label`);
1209
+ const contentSlot = resolveSlot(slots, `${slotKey}Content`);
1210
+ const slotProps = {
1211
+ schema,
1212
+ collapsed,
1213
+ collapsible,
1214
+ disabled,
1215
+ readonly: readonly2,
1216
+ toggle
1217
+ };
1218
+ const renderHeaderContent = () => {
1219
+ if (headerSlot) {
1220
+ return headerSlot(slotProps);
1221
+ }
1222
+ const label = labelSlot ? labelSlot(slotProps) : createVNode("span", {
1223
+ "class": "schemx-group__title"
1224
+ }, [schema.label]);
1225
+ if (!collapsible) {
1226
+ return label;
1227
+ }
1228
+ return createVNode(Fragment, null, [label, createVNode("span", {
1229
+ "class": classnames("schemx-group__arrow", {
1230
+ "schemx-group__arrow--down": !collapsed
1231
+ })
1232
+ }, null)]);
1233
+ };
1234
+ const renderBodyContent = () => {
1235
+ if (contentSlot) {
1236
+ return contentSlot(slotProps);
1237
+ }
1238
+ return renderChildren();
1239
+ };
1111
1240
  return {
1112
- found: true,
1113
- isFirst: !hasPositionItemInSection(list, currentIndex, -1),
1114
- isLast: !hasPositionItemInSection(list, currentIndex, 1)
1241
+ hasHeader: Boolean(schema.label || headerSlot || labelSlot),
1242
+ renderBodyContent,
1243
+ renderHeaderContent
1115
1244
  };
1116
1245
  }
1117
- function isPositionItem(item) {
1118
- return !!item && !isViewGroupSchema(item) && item.visible !== false;
1119
- }
1120
- function hasPositionItemInSection(list, startIndex, step) {
1121
- for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {
1122
- const item = list[index];
1123
- if (item.visible === false) {
1124
- continue;
1125
- }
1126
- if (isViewGroupSchema(item)) {
1127
- return false;
1246
+ const Group = /* @__PURE__ */ defineComponent({
1247
+ name: "SchemxGroup",
1248
+ inheritAttrs: false,
1249
+ props: {
1250
+ schema: {
1251
+ type: Object,
1252
+ required: true
1253
+ },
1254
+ class: {
1255
+ type: [String, Object, Array, Boolean],
1256
+ required: false,
1257
+ default: void 0
1258
+ },
1259
+ style: {
1260
+ type: [String, Object, Array],
1261
+ required: false,
1262
+ default: void 0
1263
+ },
1264
+ renderChildren: {
1265
+ type: Function,
1266
+ required: false,
1267
+ default: void 0
1128
1268
  }
1129
- if (isPositionItem(item)) {
1130
- return true;
1269
+ },
1270
+ setup(props, {
1271
+ attrs,
1272
+ slots
1273
+ }) {
1274
+ var _a;
1275
+ const internalCollapsed = ref(Boolean(props.schema.defaultCollapsed));
1276
+ const collapsed = computed(() => props.schema.collapsed ?? internalCollapsed.value);
1277
+ const componentId = ((_a = getCurrentInstance()) == null ? void 0 : _a.uid) ?? 0;
1278
+ watch(() => props.schema.collapsed, (nextCollapsed, previousCollapsed) => {
1279
+ if (nextCollapsed !== void 0) {
1280
+ internalCollapsed.value = nextCollapsed;
1281
+ } else if (previousCollapsed !== void 0) {
1282
+ internalCollapsed.value = previousCollapsed;
1283
+ }
1284
+ });
1285
+ const toggle = () => {
1286
+ var _a2, _b;
1287
+ if (!props.schema.collapsible || props.schema.disabled) {
1288
+ return;
1289
+ }
1290
+ const nextCollapsed = !collapsed.value;
1291
+ if (props.schema.collapsed === void 0) {
1292
+ internalCollapsed.value = nextCollapsed;
1293
+ }
1294
+ (_b = (_a2 = props.schema).onCollapsedChange) == null ? void 0 : _b.call(_a2, nextCollapsed);
1295
+ };
1296
+ return () => {
1297
+ var _a2;
1298
+ const schema = props.schema;
1299
+ if (schema.visible === false) {
1300
+ return null;
1301
+ }
1302
+ const collapsible = Boolean(schema.collapsible);
1303
+ const isCollapsed = collapsed.value;
1304
+ const destroyOnCollapse = schema.destroyOnCollapse ?? true;
1305
+ const uniqueId = ((_a2 = schema.debug) == null ? void 0 : _a2.runtimeNodeId) ?? `local-${componentId}`;
1306
+ const idBase = `schemx-group-${uniqueId}-${normalizeId(schema.key)}`;
1307
+ const headerId = `${idBase}-header`;
1308
+ const bodyId = `${idBase}-body`;
1309
+ const renderChild = (child) => {
1310
+ if (isViewDynamicSchema(child)) {
1311
+ if (child.visible === false) {
1312
+ return null;
1313
+ }
1314
+ return child.items.map((item) => item.children.map((itemChild) => renderChild(itemChild)));
1315
+ }
1316
+ if (isViewGroupSchema(child)) {
1317
+ return createVNode(Group, {
1318
+ "key": child.key,
1319
+ "schema": child,
1320
+ "class": props.class,
1321
+ "style": props.style
1322
+ }, slots);
1323
+ }
1324
+ return createVNode(Field$1, {
1325
+ "key": `${child.key}:${normalizeNameKey(child.name)}`,
1326
+ "schema": child
1327
+ }, slots);
1328
+ };
1329
+ const {
1330
+ hasHeader,
1331
+ renderBodyContent,
1332
+ renderHeaderContent
1333
+ } = createGroupSlotRenderers({
1334
+ schema,
1335
+ collapsed: isCollapsed,
1336
+ collapsible,
1337
+ disabled: Boolean(schema.disabled),
1338
+ readonly: Boolean(schema.readonly),
1339
+ toggle,
1340
+ slots,
1341
+ renderChildren: () => props.renderChildren ? props.renderChildren(schema.children) : schema.children.map(renderChild)
1342
+ });
1343
+ const body = createVNode("div", {
1344
+ "id": bodyId,
1345
+ "role": "group",
1346
+ "aria-labelledby": schema.label ? headerId : void 0,
1347
+ "aria-hidden": isCollapsed || void 0,
1348
+ "class": "schemx-group__body",
1349
+ "style": !destroyOnCollapse && isCollapsed ? {
1350
+ display: "none"
1351
+ } : void 0
1352
+ }, [renderBodyContent()]);
1353
+ return createVNode("div", mergeProps(attrs, {
1354
+ "class": classnames("schemx-group-wrapper", props.class, attrs.class, schema.class),
1355
+ "data-key": schema.key,
1356
+ "aria-disabled": schema.disabled || void 0,
1357
+ "style": [attrs.style, props.style, schema.style]
1358
+ }), [createVNode("div", {
1359
+ "class": classnames("schemx-group", {
1360
+ "schemx-group--collapsed": isCollapsed,
1361
+ "is-readonly": schema.readonly,
1362
+ "is-disabled": schema.disabled
1363
+ })
1364
+ }, [hasHeader && createVNode("div", {
1365
+ "id": headerId,
1366
+ "role": collapsible ? "button" : void 0,
1367
+ "tabindex": collapsible ? schema.disabled ? -1 : 0 : void 0,
1368
+ "aria-expanded": collapsible ? !isCollapsed : void 0,
1369
+ "aria-controls": collapsible ? bodyId : void 0,
1370
+ "aria-disabled": collapsible ? schema.disabled || void 0 : void 0,
1371
+ "class": classnames("schemx-group__header", {
1372
+ "schemx-group__header--clickable": collapsible && !schema.disabled
1373
+ }),
1374
+ "onClick": toggle,
1375
+ "onKeydown": (e) => {
1376
+ if (!schema.disabled && (e.key === "Enter" || e.key === " ")) {
1377
+ e.preventDefault();
1378
+ toggle();
1379
+ }
1380
+ }
1381
+ }, [renderHeaderContent()]), destroyOnCollapse ? !isCollapsed && body : body])]);
1382
+ };
1383
+ }
1384
+ });
1385
+ const Group$1 = Group;
1386
+ const Dynamic = /* @__PURE__ */ defineComponent({
1387
+ name: "SchemxDynamic",
1388
+ inheritAttrs: false,
1389
+ props: {
1390
+ schema: {
1391
+ type: Object,
1392
+ required: true
1393
+ },
1394
+ viewSchemas: {
1395
+ type: Array,
1396
+ required: true
1397
+ },
1398
+ renderChildren: {
1399
+ type: Function,
1400
+ required: false,
1401
+ default: void 0
1131
1402
  }
1403
+ },
1404
+ /**
1405
+ * 初始化 Dynamic 的行模板渲染器。
1406
+ *
1407
+ * @param props - 当前 Dynamic ViewSchema 与 Form 根级 ViewSchema。
1408
+ * @param slots - 父级透传的字段插槽。
1409
+ */
1410
+ setup(props, {
1411
+ slots
1412
+ }) {
1413
+ const getFieldRenderKey = (schema) => `${schema.key}:${normalizeNameKey(schema.name)}`;
1414
+ const getFieldClass = (schema) => {
1415
+ const {
1416
+ isFirst,
1417
+ isLast
1418
+ } = getSectionPosition(props.viewSchemas, schema.key);
1419
+ return {
1420
+ "schemx-field-wrapper--first": isFirst,
1421
+ "schemx-field-wrapper--last": isLast
1422
+ };
1423
+ };
1424
+ const renderItemSchema = (schema) => {
1425
+ if (isViewGroupSchema(schema)) {
1426
+ return createVNode(Group$1, {
1427
+ "key": schema.key,
1428
+ "schema": schema
1429
+ }, slots);
1430
+ }
1431
+ const field = schema;
1432
+ return createVNode(Field$1, {
1433
+ "key": getFieldRenderKey(field),
1434
+ "schema": field,
1435
+ "class": getFieldClass(field)
1436
+ }, slots);
1437
+ };
1438
+ return () => {
1439
+ if (props.schema.visible === false) {
1440
+ return null;
1441
+ }
1442
+ return props.schema.items.map((item) => {
1443
+ const children = props.renderChildren ? props.renderChildren(item.children) : item.children.map(renderItemSchema);
1444
+ return createVNode(Fragment, {
1445
+ "key": item.key
1446
+ }, [children]);
1447
+ });
1448
+ };
1132
1449
  }
1133
- return false;
1450
+ });
1451
+ const Dynamic$1 = Dynamic;
1452
+ function _isSlot(s) {
1453
+ return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
1134
1454
  }
1455
+ const SchemaList = /* @__PURE__ */ defineComponent({
1456
+ name: "SchemxSchemaList",
1457
+ inheritAttrs: false,
1458
+ props: {
1459
+ schemas: {
1460
+ type: Array,
1461
+ required: true
1462
+ },
1463
+ viewSchemas: {
1464
+ type: Array,
1465
+ required: true
1466
+ },
1467
+ colComponent: {
1468
+ type: [Object, Function],
1469
+ required: false,
1470
+ default: void 0
1471
+ },
1472
+ fieldClassResolver: {
1473
+ type: Function,
1474
+ required: false,
1475
+ default: void 0
1476
+ }
1477
+ },
1478
+ setup(props, {
1479
+ slots
1480
+ }) {
1481
+ const getFieldRenderKey = (schema) => `${schema.key}:${normalizeNameKey(schema.name)}`;
1482
+ const getSchemaRenderKey = (schema) => {
1483
+ if (isSchemxViewFieldSchema(schema)) {
1484
+ return getFieldRenderKey(schema);
1485
+ }
1486
+ return schema.key;
1487
+ };
1488
+ const renderChildren = (schemas) => {
1489
+ return h(SchemaList, {
1490
+ schemas,
1491
+ viewSchemas: props.viewSchemas,
1492
+ colComponent: props.colComponent
1493
+ }, slots);
1494
+ };
1495
+ const renderSchema = (schema) => {
1496
+ var _a;
1497
+ if (schema.visible === false) {
1498
+ return null;
1499
+ }
1500
+ let content;
1501
+ if (isViewGroupSchema(schema)) {
1502
+ content = createVNode(Group$1, {
1503
+ "key": schema.key,
1504
+ "schema": schema,
1505
+ "renderChildren": renderChildren
1506
+ }, slots);
1507
+ } else if (isViewDynamicSchema(schema)) {
1508
+ content = createVNode(Dynamic$1, {
1509
+ "key": schema.key,
1510
+ "schema": schema,
1511
+ "viewSchemas": props.viewSchemas,
1512
+ "renderChildren": renderChildren
1513
+ }, slots);
1514
+ } else {
1515
+ content = createVNode(Field$1, {
1516
+ "key": getFieldRenderKey(schema),
1517
+ "schema": schema,
1518
+ "class": (_a = props.fieldClassResolver) == null ? void 0 : _a.call(props, schema)
1519
+ }, slots);
1520
+ }
1521
+ if (!props.colComponent) {
1522
+ return content;
1523
+ }
1524
+ return createVNode(Col$1, {
1525
+ "key": getSchemaRenderKey(schema),
1526
+ "component": props.colComponent,
1527
+ "layout": schema.layout
1528
+ }, _isSlot(content) ? content : {
1529
+ default: () => [content]
1530
+ });
1531
+ };
1532
+ return () => {
1533
+ const children = props.schemas.map(renderSchema);
1534
+ if (!props.colComponent) {
1535
+ return children;
1536
+ }
1537
+ return createVNode("div", {
1538
+ "class": "schemx-row"
1539
+ }, [children]);
1540
+ };
1541
+ }
1542
+ });
1543
+ const SchemaList$1 = SchemaList;
1135
1544
  const _hoisted_1 = {
1136
1545
  key: 0,
1137
1546
  class: "schemx-actions"
1138
1547
  };
1139
- const _sfc_main = /* @__PURE__ */ defineComponent({
1548
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1140
1549
  ...{ name: "SchemxForm" },
1141
1550
  __name: "form",
1142
1551
  props: {
1143
1552
  schemas: { default: () => [] },
1553
+ fieldRules: { default: void 0 },
1144
1554
  initialValues: { default: () => ({}) },
1145
1555
  modelValue: { default: () => ({}) },
1146
1556
  form: { default: void 0 },
1147
1557
  loading: { type: Boolean, default: void 0 },
1148
- submitter: { type: [Boolean, Object], default: void 0 },
1149
- resetter: { type: [Boolean, Object], default: void 0 },
1150
- class: { default: "" },
1558
+ submitter: { type: [Boolean, Object], default: true },
1559
+ resetter: { type: [Boolean, Object], default: true },
1560
+ class: { type: [Boolean, null, String, Object, Array], default: "" },
1151
1561
  style: { type: [Boolean, null, String, Object, Array], default: () => ({}) },
1562
+ colComponent: { default: void 0 },
1152
1563
  rendererProps: { default: void 0 },
1153
1564
  validatorAdapters: {},
1154
1565
  defaultRendererType: {},
1155
1566
  rendererRegistry: { default: void 0 },
1156
- validationRuleRegistry: { default: void 0 },
1567
+ presetRuleRegistry: { default: void 0 },
1157
1568
  onRuleError: {},
1158
1569
  onFinish: { type: Function, default: void 0 },
1159
1570
  onFinishFailed: { type: Function, default: void 0 },
@@ -1162,18 +1573,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1162
1573
  onValuesChange: { type: Function, default: void 0 },
1163
1574
  onFieldsChange: { type: Function, default: void 0 },
1164
1575
  lifecycleHooks: {},
1576
+ schedulerOptions: { default: void 0 },
1577
+ validationConcurrency: { default: void 0 },
1165
1578
  required: { type: [Boolean, Object] },
1166
- readonly: { type: Boolean },
1167
- disabled: { type: Boolean },
1168
- visible: { type: Boolean, default: true },
1579
+ readonly: { type: Boolean, default: void 0 },
1580
+ disabled: { type: Boolean, default: void 0 },
1581
+ visible: { type: Boolean, default: void 0 },
1169
1582
  labelIcon: {},
1170
1583
  labelAlign: {},
1171
1584
  labelPosition: {},
1172
1585
  labelWidth: {},
1173
1586
  contentAlign: {},
1174
- validationTrigger: {},
1175
- colon: { type: Boolean },
1176
- showRequiredMark: { type: Boolean }
1587
+ validationTrigger: { default: void 0 },
1588
+ colon: { type: Boolean, default: void 0 },
1589
+ showRequiredMark: { type: Boolean, default: void 0 }
1177
1590
  },
1178
1591
  emits: ["update:modelValue"],
1179
1592
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -1181,19 +1594,73 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1181
1594
  const emit = __emit;
1182
1595
  const slots = useSlots();
1183
1596
  const pickSchemaConfig = () => {
1184
- return pick(props, defaultSchemxConfigKeys);
1597
+ const schemaConfig = pick(
1598
+ props,
1599
+ defaultSchemxConfigKeys
1600
+ );
1601
+ return Object.fromEntries(
1602
+ Object.entries(schemaConfig).filter(([, value]) => value !== void 0)
1603
+ );
1604
+ };
1605
+ const isExternalForm = props.form !== void 0;
1606
+ const schemaConfigProps = computed(pickSchemaConfig);
1607
+ const appConfig = getSchemxAppConfig();
1608
+ const providerConfig = getSchemxConfigProviderRef();
1609
+ const coreConfig = getGlobalSchemxConfig();
1610
+ const formFallbackConfig = {
1611
+ schemaConfig: {
1612
+ visible: true,
1613
+ validationTrigger: ["blur", "change"]
1614
+ }
1615
+ };
1616
+ const contextSchemaConfig = computed(() => {
1617
+ const mergedConfig = isExternalForm ? mergeSchemxConfig({ schemaConfig: schemaConfigProps.value }, formFallbackConfig) : mergeSchemxConfig(
1618
+ { schemaConfig: schemaConfigProps.value },
1619
+ (providerConfig == null ? void 0 : providerConfig.value) ?? {},
1620
+ appConfig,
1621
+ formFallbackConfig
1622
+ );
1623
+ return mergedConfig.schemaConfig ?? {};
1624
+ });
1625
+ const resolvedSchemaConfig = computed(
1626
+ () => mergeAndResolveSchemxConfig(
1627
+ { schemaConfig: schemaConfigProps.value },
1628
+ (providerConfig == null ? void 0 : providerConfig.value) ?? {},
1629
+ appConfig,
1630
+ formFallbackConfig,
1631
+ coreConfig
1632
+ ).schemaConfig
1633
+ );
1634
+ const initialContextSchemaConfig = contextSchemaConfig.value;
1635
+ const initialCoreSchemaConfig = isExternalForm ? initialContextSchemaConfig : resolvedSchemaConfig.value;
1636
+ const formSchemaConfig = reactive(
1637
+ initialContextSchemaConfig
1638
+ );
1639
+ const hasSchemaConfigKey = (config, key) => Object.prototype.hasOwnProperty.call(config, key);
1640
+ const replaceFormSchemaConfig = (nextSchemaConfig) => {
1641
+ for (const key of defaultSchemxConfigKeys) {
1642
+ if (!hasSchemaConfigKey(nextSchemaConfig, key)) {
1643
+ delete formSchemaConfig[key];
1644
+ }
1645
+ }
1646
+ Object.assign(formSchemaConfig, nextSchemaConfig);
1185
1647
  };
1186
- const formSchemaConfig = reactive(pickSchemaConfig());
1648
+ const resolvedColComponent = computed(
1649
+ () => props.colComponent ?? (providerConfig == null ? void 0 : providerConfig.value.colComponent) ?? appConfig.colComponent ?? registeredColComponent.value
1650
+ );
1187
1651
  createFormConfigContext({ schemaConfig: formSchemaConfig });
1188
1652
  const providedForm = props.form ? props.form : useForm({
1189
1653
  schemas: props.schemas,
1190
- schemaConfig: pickSchemaConfig(),
1654
+ schemaConfig: initialCoreSchemaConfig,
1191
1655
  initialValues: Object.keys(props.modelValue).length > 0 ? props.modelValue : props.initialValues,
1192
1656
  rendererProps: props.rendererProps,
1193
1657
  rendererRegistry: props.rendererRegistry,
1194
1658
  defaultRendererType: props.defaultRendererType,
1195
- validationRuleRegistry: props.validationRuleRegistry,
1659
+ presetRuleRegistry: props.presetRuleRegistry,
1660
+ fieldRules: props.fieldRules,
1196
1661
  validatorAdapters: props.validatorAdapters,
1662
+ schedulerOptions: props.schedulerOptions,
1663
+ validationConcurrency: props.validationConcurrency,
1197
1664
  /**
1198
1665
  * 转发提交成功回调。
1199
1666
  *
@@ -1251,9 +1718,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1251
1718
  (_a = props.onFieldsChange) == null ? void 0 : _a.call(props, changedPaths, allPaths);
1252
1719
  }
1253
1720
  });
1254
- const isExternalForm = props.form !== void 0;
1255
1721
  const formInstance = createFormContext(providedForm);
1256
1722
  const effectiveLoading = computed(() => props.loading ?? formInstance.isLoading());
1723
+ const formRootClass = computed(() => ["schemx", props.class]);
1724
+ const formRootStyle = computed(() => props.style);
1257
1725
  const normalizeActionConfig = (action) => action === true ? {} : action || {};
1258
1726
  const submitterConfig = computed(() => normalizeActionConfig(props.submitter));
1259
1727
  const resetterConfig = computed(() => normalizeActionConfig(props.resetter));
@@ -1290,14 +1758,29 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1290
1758
  () => effectiveLoading.value || Boolean(resetterButtonProps.value.disabled)
1291
1759
  );
1292
1760
  let syncingFromModel = false;
1761
+ const internalModelEchoes = /* @__PURE__ */ new Set();
1762
+ const emitModelValue = (values) => {
1763
+ const rawValues = toRaw(values);
1764
+ internalModelEchoes.add(rawValues);
1765
+ emit("update:modelValue", values);
1766
+ void nextTick(() => {
1767
+ internalModelEchoes.delete(rawValues);
1768
+ });
1769
+ };
1293
1770
  watch(
1294
1771
  () => props.modelValue,
1295
1772
  (values) => {
1773
+ if (internalModelEchoes.delete(toRaw(values))) {
1774
+ return;
1775
+ }
1296
1776
  syncingFromModel = true;
1297
- formInstance.setFieldsValue(values);
1298
- syncingFromModel = false;
1777
+ try {
1778
+ formInstance.setFieldsValue(values);
1779
+ } finally {
1780
+ syncingFromModel = false;
1781
+ }
1299
1782
  },
1300
- { deep: true }
1783
+ { deep: true, immediate: isExternalForm }
1301
1784
  );
1302
1785
  const handleSubmit = () => formInstance.submit();
1303
1786
  const handleReset = () => {
@@ -1307,9 +1790,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1307
1790
  watch(
1308
1791
  formValues,
1309
1792
  (latestSnapshot) => {
1310
- if (syncingFromModel)
1793
+ if (syncingFromModel) {
1311
1794
  return;
1312
- emit("update:modelValue", latestSnapshot);
1795
+ }
1796
+ emitModelValue(latestSnapshot);
1313
1797
  },
1314
1798
  { flush: "sync" }
1315
1799
  );
@@ -1320,27 +1804,45 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1320
1804
  formInstance.setSchemas(schemas);
1321
1805
  }
1322
1806
  },
1323
- { deep: false, immediate: !!props.form }
1807
+ { immediate: isExternalForm }
1324
1808
  );
1325
1809
  const viewSchemas = useViewSchemas(formInstance);
1326
- const getFormItemClass = (schema) => {
1810
+ const getFieldClass = (schema) => {
1327
1811
  const { isFirst, isLast } = getSectionPosition(
1328
1812
  viewSchemas.value,
1329
1813
  schema.key
1330
1814
  );
1331
1815
  return {
1332
- "schemx-item-wrapper--first": isFirst,
1333
- "schemx-item-wrapper--last": isLast
1816
+ "schemx-field-wrapper--first": isFirst,
1817
+ "schemx-field-wrapper--last": isLast
1334
1818
  };
1335
1819
  };
1336
- watch(
1337
- pickSchemaConfig,
1338
- (nextSchemaConfig) => {
1339
- Object.assign(formSchemaConfig, nextSchemaConfig);
1340
- formInstance.updateSchemaConfig(nextSchemaConfig);
1341
- },
1342
- { deep: false, immediate: isExternalForm }
1343
- );
1820
+ const syncInternalSchemaConfig = (nextSchemaConfig) => {
1821
+ replaceFormSchemaConfig(contextSchemaConfig.value);
1822
+ formInstance.updateSchemaConfig(nextSchemaConfig);
1823
+ };
1824
+ const syncExternalSchemaConfig = (nextSchemaConfig, previousSchemaConfig) => {
1825
+ const patch = { ...nextSchemaConfig };
1826
+ if (previousSchemaConfig !== void 0) {
1827
+ for (const key of defaultSchemxConfigKeys) {
1828
+ if (hasSchemaConfigKey(previousSchemaConfig, key) && !hasSchemaConfigKey(nextSchemaConfig, key)) {
1829
+ patch[key] = void 0;
1830
+ }
1831
+ }
1832
+ }
1833
+ replaceFormSchemaConfig(contextSchemaConfig.value);
1834
+ if (Object.keys(patch).length > 0) {
1835
+ formInstance.updateSchemaConfig(patch);
1836
+ }
1837
+ };
1838
+ if (isExternalForm) {
1839
+ watch(schemaConfigProps, syncExternalSchemaConfig, {
1840
+ deep: true,
1841
+ immediate: true
1842
+ });
1843
+ } else {
1844
+ watch(resolvedSchemaConfig, syncInternalSchemaConfig, { deep: true });
1845
+ }
1344
1846
  const exposed = {
1345
1847
  ...formInstance,
1346
1848
  submit: handleSubmit,
@@ -1349,32 +1851,31 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1349
1851
  __expose(exposed);
1350
1852
  return (_ctx, _cache) => {
1351
1853
  return openBlock(), createElementBlock("div", {
1352
- class: normalizeClass(["schemx", props.class]),
1353
- style: normalizeStyle(props.style)
1854
+ class: normalizeClass(formRootClass.value),
1855
+ style: normalizeStyle(formRootStyle.value)
1354
1856
  }, [
1355
- (openBlock(true), createElementBlock(Fragment, null, renderList(unref(viewSchemas), (schema) => {
1356
- return openBlock(), createBlock(unref(FormItem$1), {
1357
- key: schema.key,
1358
- schema,
1359
- class: normalizeClass(getFormItemClass(schema))
1360
- }, createSlots({ _: 2 }, [
1361
- renderList(fieldSlots.value, (_, slotName) => {
1362
- return {
1363
- name: slotName,
1364
- fn: withCtx((slotProps) => [
1365
- renderSlot(_ctx.$slots, slotName, mergeProps({ ref_for: true }, slotProps ?? {}))
1366
- ])
1367
- };
1368
- })
1369
- ]), 1032, ["schema", "class"]);
1370
- }), 128)),
1857
+ createVNode(unref(SchemaList$1), {
1858
+ schemas: unref(viewSchemas),
1859
+ "view-schemas": unref(viewSchemas),
1860
+ "col-component": resolvedColComponent.value,
1861
+ "field-class-resolver": getFieldClass
1862
+ }, createSlots({ _: 2 }, [
1863
+ renderList(fieldSlots.value, (_, slotName) => {
1864
+ return {
1865
+ name: slotName,
1866
+ fn: withCtx((slotProps) => [
1867
+ renderSlot(_ctx.$slots, slotName, normalizeProps(guardReactiveProps(slotProps ?? {})))
1868
+ ])
1869
+ };
1870
+ })
1871
+ ]), 1032, ["schemas", "view-schemas", "col-component"]),
1371
1872
  isActionsVisible.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
1372
1873
  isResetterVisible.value && unref(slots).resetter ? renderSlot(_ctx.$slots, "resetter", {
1373
1874
  form: unref(formInstance),
1374
1875
  loading: effectiveLoading.value,
1375
1876
  disabled: isResetterDisabled.value,
1376
1877
  reset: handleReset
1377
- }, void 0, void 0, 0) : isResetterVisible.value ? (openBlock(), createBlock(unref(_sfc_main$1), mergeProps({ key: 1 }, resetterButtonProps.value, {
1878
+ }, void 0, void 0, 0) : isResetterVisible.value ? (openBlock(), createBlock(unref(_sfc_main$2), mergeProps({ key: 1 }, resetterButtonProps.value, {
1378
1879
  class: "schemx-actions-button schemx-actions-button--reset",
1379
1880
  type: "button",
1380
1881
  disabled: isResetterDisabled.value,
@@ -1390,7 +1891,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1390
1891
  loading: effectiveLoading.value,
1391
1892
  disabled: isSubmitterDisabled.value,
1392
1893
  submit: handleSubmit
1393
- }, void 0, void 0, 2) : isSubmitterVisible.value ? (openBlock(), createBlock(unref(_sfc_main$1), mergeProps({ key: 3 }, submitterButtonProps.value, {
1894
+ }, void 0, void 0, 2) : isSubmitterVisible.value ? (openBlock(), createBlock(unref(_sfc_main$2), mergeProps({ key: 3 }, submitterButtonProps.value, {
1394
1895
  class: "schemx-actions-button schemx-actions-button--submit",
1395
1896
  type: "button",
1396
1897
  loading: effectiveLoading.value,
@@ -1410,14 +1911,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1410
1911
  function withInstall(comp, extra) {
1411
1912
  return Object.assign(comp, extra);
1412
1913
  }
1413
- const SchemxFormExport = withInstall(_sfc_main, {
1914
+ const SchemxFormExport = withInstall(_sfc_main$1, {
1414
1915
  /** Vue 插件安装方法 */
1415
1916
  install(app, options = {}) {
1416
1917
  provideSchemxAppConfig(app, options);
1417
- app.component("SchemxForm", _sfc_main);
1918
+ app.component("SchemxForm", _sfc_main$1);
1418
1919
  },
1419
- /** FormItem 子组件引用 */
1420
- FormItem: FormItem$1
1920
+ /** Field 子组件引用 */
1921
+ Field: Field$1
1421
1922
  });
1422
1923
  const SchemxFormExport$1 = SchemxFormExport;
1423
1924
  function normalizeDictionary(dictionary) {
@@ -1452,15 +1953,119 @@ function WithRemoteOptions(WrappedComponent) {
1452
1953
  }
1453
1954
  });
1454
1955
  }
1956
+ const _sfc_main = /* @__PURE__ */ defineComponent({
1957
+ ...{
1958
+ name: "SchemxWrapper",
1959
+ inheritAttrs: false
1960
+ },
1961
+ __name: "index",
1962
+ props: {
1963
+ readonly: { type: Boolean, default: false },
1964
+ disabled: { type: Boolean, default: false }
1965
+ },
1966
+ setup(__props) {
1967
+ const props = __props;
1968
+ const attrs = useAttrs();
1969
+ const slots = useSlots();
1970
+ const warnedReadonlySlot = ref(false);
1971
+ const rootClass = computed(() => [
1972
+ "schemx-wrapper",
1973
+ attrs.class,
1974
+ props.readonly && "schemx-wrapper--readonly",
1975
+ props.disabled && "schemx-wrapper--disabled"
1976
+ ]);
1977
+ const forwardedAttrs = computed(() => {
1978
+ const { class: _class, ...rest } = attrs;
1979
+ return rest;
1980
+ });
1981
+ const warnIfReadonlySlotIsMissing = (readonly2) => {
1982
+ if (!readonly2 || slots.readonly || warnedReadonlySlot.value || true) {
1983
+ return;
1984
+ }
1985
+ warnedReadonlySlot.value = true;
1986
+ console.warn("[schemx] SchemxWrapper is readonly but no #readonly slot was provided.");
1987
+ };
1988
+ onMounted(() => warnIfReadonlySlotIsMissing(props.readonly));
1989
+ watch(() => props.readonly, warnIfReadonlySlotIsMissing);
1990
+ return (_ctx, _cache) => {
1991
+ return openBlock(), createElementBlock("div", mergeProps(forwardedAttrs.value, { class: rootClass.value }), [
1992
+ props.readonly ? renderSlot(_ctx.$slots, "readonly", {
1993
+ readonly: props.readonly,
1994
+ disabled: props.disabled
1995
+ }, void 0, void 0, 0) : renderSlot(_ctx.$slots, "default", {
1996
+ readonly: props.readonly,
1997
+ disabled: props.disabled
1998
+ }, void 0, void 0, 1)
1999
+ ], 16);
2000
+ };
2001
+ }
2002
+ });
2003
+ const ConfigProvider = /* @__PURE__ */ defineComponent({
2004
+ name: "SchemxConfigProvider",
2005
+ inheritAttrs: false,
2006
+ props: {
2007
+ schemaConfig: {
2008
+ type: Object,
2009
+ default: void 0
2010
+ },
2011
+ rendererProps: {
2012
+ type: Object,
2013
+ default: void 0
2014
+ },
2015
+ validatorAdapters: {
2016
+ type: Array,
2017
+ default: void 0
2018
+ },
2019
+ defaultRendererType: {
2020
+ type: String,
2021
+ default: void 0
2022
+ },
2023
+ rendererRegistry: {
2024
+ type: Object,
2025
+ default: void 0
2026
+ },
2027
+ presetRuleRegistry: {
2028
+ type: Object,
2029
+ default: void 0
2030
+ },
2031
+ colComponent: {
2032
+ type: [Object, Function],
2033
+ default: void 0
2034
+ }
2035
+ },
2036
+ setup(props, {
2037
+ slots
2038
+ }) {
2039
+ const config = computed(() => ({
2040
+ schemaConfig: props.schemaConfig,
2041
+ rendererProps: props.rendererProps,
2042
+ validatorAdapters: props.validatorAdapters,
2043
+ defaultRendererType: props.defaultRendererType,
2044
+ rendererRegistry: props.rendererRegistry,
2045
+ presetRuleRegistry: props.presetRuleRegistry,
2046
+ colComponent: props.colComponent
2047
+ }));
2048
+ provideSchemxConfigProvider(config);
2049
+ return () => {
2050
+ var _a;
2051
+ return (_a = slots.default) == null ? void 0 : _a.call(slots);
2052
+ };
2053
+ }
2054
+ });
2055
+ const ConfigProvider$1 = ConfigProvider;
1455
2056
  export {
1456
- FormGroup$1 as FormGroup,
1457
- FormItem$1 as FormItem,
2057
+ Col$1 as Col,
2058
+ ConfigProvider$1 as ConfigProvider,
2059
+ Field$1 as Field,
2060
+ Group$1 as Group,
1458
2061
  WithRemoteOptions,
2062
+ _sfc_main as Wrapper,
1459
2063
  createFieldContext,
1460
2064
  createFormConfigContext,
1461
2065
  createFormContext,
1462
2066
  SchemxFormExport$1 as default,
1463
- getCoreForm,
2067
+ presetRuleRegistry,
2068
+ registerCol,
1464
2069
  rendererRegistry,
1465
2070
  SchemxFormExport$1 as schemxForm,
1466
2071
  useDictionary,
@@ -1475,7 +2080,6 @@ export {
1475
2080
  useWatch,
1476
2081
  useWatchAll,
1477
2082
  useWatchField,
1478
- useWatchFields,
1479
- validationRuleRegistry
2083
+ useWatchFields
1480
2084
  };
1481
2085
  //# sourceMappingURL=index.mjs.map