@schemx/vue 1.0.0-next.2 → 1.0.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -62
- package/dist/analyze.html +1 -1
- package/dist/bridge/fieldBridge.d.ts +23 -0
- package/dist/bridge/fieldBridge.d.ts.map +1 -0
- package/dist/bridge/formBridge.d.ts +81 -0
- package/dist/bridge/formBridge.d.ts.map +1 -0
- package/dist/bridge/formFacade.d.ts +65 -0
- package/dist/bridge/formFacade.d.ts.map +1 -0
- package/dist/bridge/helpers.d.ts +31 -0
- package/dist/bridge/helpers.d.ts.map +1 -0
- package/dist/bridge/index.d.ts +11 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/types.d.ts +109 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/components/Button/index.vue.d.ts +30 -0
- package/dist/components/Button/index.vue.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/provideFormContext.d.ts +1 -1
- package/dist/hooks/provideFormContext.d.ts.map +1 -1
- package/dist/hooks/useField.d.ts +1 -1
- package/dist/hooks/useField.d.ts.map +1 -1
- package/dist/hooks/useForm.d.ts +1 -1
- package/dist/hooks/useForm.d.ts.map +1 -1
- package/dist/hooks/useFormSelector.d.ts.map +1 -1
- package/dist/hooks/useWatch.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +184 -176
- package/dist/index.mjs.map +1 -1
- package/dist/types/form.d.ts +1 -1
- package/dist/types/form.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/formBridge.d.ts +0 -188
- package/dist/formBridge.d.ts.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/config/appConfig.ts","../src/formBridge.ts","../src/utils/rendererProvider.ts","../src/utils/rulesProvider.ts","../src/hooks/useForm.ts","../src/hooks/provideFormContext.ts","../src/hooks/useField.ts","../src/hooks/provideFieldContext.ts","../src/hooks/useWatch.ts","../src/hooks/useDictionary.ts","../src/hooks/provideFormConfigContext.ts","../src/hooks/useStableRef.ts","../src/utils/equal.ts","../src/hooks/useViewSchemas.ts","../src/hooks/useFormSelector.ts","../src/utils/validation.ts","../src/utils/slot.ts","../src/components/FormGroup/index.tsx","../src/components/FormItem/slot.tsx","../src/components/FormItem/index.tsx","../src/components/Button/index.vue","../src/utils/helpers.ts","../src/form.vue","../src/form.ts","../src/hocs/withRemoteOptions.ts"],"sourcesContent":["/**\n * Vue App 级 Schemx 配置的 provide/inject 适配。\n *\n * 安装配置属于当前 Vue App,而不是 Core 模块级共享状态;该模块只在 Vue\n * 适配层内部保存和读取安装配置,不将注入 key 暴露为公共 API。\n *\n * @module utils/appConfig\n */\n\nimport { type App, getCurrentInstance, inject, type InjectionKey } from \"vue\"\n\nimport type { SchemxConfig, SchemxRendererPropsMap } from \"@schemx/core\"\n\n// Vue App 级配置在组件树中的私有注入 key。\nconst SCHEMX_APP_CONFIG_KEY: InjectionKey<SchemxConfig> = Symbol(\"schemx:app-config\")\n\n// 没有安装配置时使用的不可变空配置。\nconst EMPTY_SCHEMX_CONFIG: SchemxConfig = Object.freeze({\n schemaConfig: Object.freeze({}),\n validatorAdapters: Object.freeze([]),\n})\n\n/**\n * 将插件安装选项复制为可安全注入的 App 配置快照。\n *\n * Registry 保留其可变实例语义,使同一 App 内创建的多个 Form 继续共享注册结果;\n * Schema 配置和 adapter 列表则复制并冻结,避免安装后修改原对象影响 App 配置。\n *\n * @param config - 插件安装时传入的配置。\n * @returns 与调用方输入隔离的 App 配置快照。\n */\nfunction normalizeSchemxAppConfig(config: SchemxConfig): SchemxConfig {\n // 复制并冻结 App 级字段默认值,避免外部对象后续变更配置。\n const schemaConfig = Object.freeze({ ...(config.schemaConfig ?? {}) })\n\n // 复制并冻结 App 级 adapter 列表,保持列表与调用方数组相互独立。\n const validatorAdapters = Object.freeze([...(config.validatorAdapters ?? [])])\n\n return Object.freeze({\n schemaConfig,\n rendererProps: normalizeRendererProps(config.rendererProps),\n validatorAdapters,\n defaultRendererType: config.defaultRendererType,\n rendererRegistry: config.rendererRegistry,\n validationRuleRegistry: config.validationRuleRegistry,\n })\n}\n\n/**\n * 复制并冻结 Renderer Props Map 外层及每个 Renderer 的 Props 对象。\n *\n * @param source - 待标准化的 Renderer Props Map。\n * @returns 与输入隔离一层的只读 App 配置;任意嵌套 Prop 值仍保留原引用。\n */\nfunction normalizeRendererProps(\n source: SchemxRendererPropsMap | undefined\n): SchemxRendererPropsMap | undefined {\n if (source === undefined) {\n return undefined\n }\n\n // 每个 Renderer 的 Props 对象单独复制,避免安装后修改源对象影响当前 App。\n const entries = Object.entries(source).map(([type, props]) => {\n return [type, props === undefined ? undefined : Object.freeze({ ...props })]\n })\n\n return Object.freeze(Object.fromEntries(entries)) as SchemxRendererPropsMap\n}\n\n/**\n * 将 Schemx 安装配置绑定到指定 Vue App。\n *\n * @param app - 要安装 Schemx 的 Vue App 实例。\n * @param config - 当前 App 的默认配置。\n */\nexport function provideSchemxAppConfig(app: App, config: SchemxConfig = {}): void {\n // 当前 App 后续组件实例应读取的配置快照。\n const normalizedConfig = normalizeSchemxAppConfig(config)\n\n app.provide(SCHEMX_APP_CONFIG_KEY, normalizedConfig)\n}\n\n/**\n * 读取当前组件所属 Vue App 的 Schemx 安装配置。\n *\n * 在组件 setup() 之外调用时,Vue 没有可用的注入上下文;此时返回空配置,\n * 使 `useForm()` 仍可在独立 effect scope 中使用 Vue 模块级 Registry。\n *\n * @returns 当前 App 的安装配置,或无 App 上下文时的空配置。\n */\nexport function getSchemxAppConfig(): SchemxConfig {\n // 只有存在当前组件实例时才读取 Vue 注入上下文,避免 setup 外触发 Vue 警告。\n if (getCurrentInstance() === null) {\n return EMPTY_SCHEMX_CONFIG\n }\n\n // 当前组件树中最近的 App 级配置,未安装插件时回退到空配置。\n return inject(SCHEMX_APP_CONFIG_KEY, EMPTY_SCHEMX_CONFIG)\n}\n","/**\n * Core Form 到 Vue 响应式状态的共享桥接。\n *\n * @module vue/formBridge\n */\n\nimport { shallowRef } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\nimport { createFormExternalStore } from \"@schemx/core/adapter\"\n\nimport type { FieldExternalStore, FormExternalStore } from \"@schemx/core/adapter\"\nimport type { FieldValue, NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * 可在 Vue effect 中直接读取 Form 方法的结构兼容实例类型。\n *\n * Facade 与 Core Form 不同一引用,但保留完整的 `SchemxInstance` API。\n */\nexport type VueSchemxInstance<TValues extends Values = Values> = SchemxInstance<TValues>\n\n/**\n * External Store 中 pending 字段的只读聚合快照。\n */\ntype PendingFieldsSnapshot<TValues extends Values> = readonly ReturnType<\n SchemxInstance<TValues>[\"getPendingFields\"]\n>[number][]\n\n/**\n * Vue 中单个字段状态的 Ref 投影。\n */\nexport interface VueFieldBridge<\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n> {\n /**\n * 对应的 Core 字段 External Store。\n */\n readonly store: FieldExternalStore<TValues, TName>\n /**\n * 当前字段值。\n */\n readonly value: ShallowRef<FieldValue<TValues, TName> | undefined>\n /**\n * 当前字段错误消息。\n */\n readonly errors: ShallowRef<readonly string[]>\n /**\n * 当前字段 touched 状态。\n */\n readonly touched: ShallowRef<boolean>\n /**\n * 当前字段 pending 状态。\n */\n readonly pending: ShallowRef<boolean>\n}\n\n/**\n * 包含唯一 Vue Facade 的共享 Form Bridge。\n */\nexport interface VueFormBridge<TValues extends Values = Values> {\n /**\n * 原始 Core Form。\n */\n readonly form: SchemxInstance<TValues>\n /**\n * 该 Form 的 Core External Store owner。\n */\n readonly externalStore: FormExternalStore<TValues>\n /**\n * 全表值 Ref。\n */\n readonly values: ShallowRef<TValues>\n /**\n * 已 touched 字段 Ref。\n */\n readonly touchedFields: ShallowRef<readonly NamePath<TValues>[]>\n /**\n * pending 字段 Ref。\n */\n readonly pendingFields: ShallowRef<PendingFieldsSnapshot<TValues>>\n /**\n * 当前表单提交状态的 Vue Ref 投影。\n */\n readonly loading: ShallowRef<boolean>\n /**\n * 按 Field External Store 身份缓存的字段 Ref 投影。\n */\n readonly fieldBridges: Map<object, ManagedVueFieldBridge<TValues>>\n /**\n * 正在使用当前 Bridge 的 Vue owner 数量。\n */\n refCount: number\n /**\n * Bridge 是否已被手动或自动释放。\n */\n destroyed: boolean\n /**\n * 取消 Form 级 External Store 订阅。\n */\n readonly unsubscribe: () => void\n /**\n * 与原始 Core Form 对应的唯一 Vue Facade。\n */\n readonly facade: VueSchemxInstance<TValues>\n}\n\n/**\n * 内部使用的、可停止订阅的字段 Ref 投影。\n */\ninterface ManagedVueFieldBridge<TValues extends Values> extends VueFieldBridge<TValues> {\n dispose(): void\n}\n\n/**\n * 同一 Core Form 只创建一个共享 Vue Bridge。\n */\nconst formBridgeCache = new WeakMap<object, VueFormBridge<Values>>()\n\n/**\n * 同一 Core Form 始终复用同一个 Vue Facade。\n */\nconst formFacadeCache = new WeakMap<object, VueSchemxInstance<Values>>()\n\n/**\n * 用于从公开 Facade 回到原始 Core Form。\n */\nconst facadeCoreFormCache = new WeakMap<object, SchemxInstance<Values>>()\n\n/**\n * 将 Vue 的条件 Ref 返回类型收窄为当前桥接所需的 shallow Ref。\n *\n * @param value - 要包装为 shallow Ref 的当前值。\n * @returns 包装后的 shallow Ref。\n */\nfunction createVueShallowRef<TValue>(value: TValue): ShallowRef<TValue> {\n return shallowRef(value) as ShallowRef<TValue>\n}\n\n/**\n * 返回原始 Core Form;普通 Core Form 输入时保持引用不变。\n *\n * @param form - Core Form 或 Vue Facade。\n * @returns 原始 Core Form。\n *\n * @example\n * ```ts\n * const coreForm = getCoreForm(form)\n * const snapshot = coreForm.getFieldsSnapshot()\n * ```\n */\nexport function getCoreForm<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): SchemxInstance<TValues> {\n const coreForm = facadeCoreFormCache.get(form as object)\n\n return (coreForm ?? form) as SchemxInstance<TValues>\n}\n\n/**\n * 获取指定 Form 的共享 Vue Bridge。\n *\n * @param form - Core Form 或 Vue Facade。\n * @returns 对应的共享 Bridge。\n *\n * @remarks\n * 首次调用会创建 External Store 订阅;调用方应使用\n * `retainVueFormBridge()` 持有 Bridge,并在 owner 销毁时释放。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const release = retainVueFormBridge(bridge)\n * onScopeDispose(release)\n * ```\n */\nexport function getVueFormBridge<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): VueFormBridge<TValues> {\n const coreForm = getCoreForm(form)\n const cachedBridge = formBridgeCache.get(coreForm)\n\n if (cachedBridge) {\n return cachedBridge as VueFormBridge<TValues>\n }\n\n const bridge = createVueFormBridge(coreForm)\n\n formBridgeCache.set(coreForm, bridge as VueFormBridge<Values>)\n\n return bridge\n}\n\n/**\n * 获取指定 Form 的唯一 Vue Facade。\n *\n * @param form - Core Form 或已有 Vue Facade。\n * @returns 可被 Vue effect 追踪的 Facade。\n *\n * @example\n * ```ts\n * const reactiveForm = getVueFormFacade(form)\n * watchEffect(() => {\n * console.log(reactiveForm.getFieldsValue())\n * })\n * ```\n */\nexport function getVueFormFacade<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): VueSchemxInstance<TValues> {\n const coreForm = getCoreForm(form)\n const cachedFacade = formFacadeCache.get(coreForm)\n\n if (cachedFacade) {\n return cachedFacade as VueSchemxInstance<TValues>\n }\n\n const facade = createVueFormFacade(coreForm)\n\n formFacadeCache.set(coreForm, facade as VueSchemxInstance<Values>)\n facadeCoreFormCache.set(facade as object, coreForm as SchemxInstance<Values>)\n\n return facade\n}\n\n/**\n * 为一个 Vue owner 保留 Bridge,并返回对应的幂等释放函数。\n *\n * @param bridge - 要保留的共享 Bridge。\n * @returns 释放当前 owner 的函数。\n *\n * @remarks\n * 返回的释放函数可安全重复调用;最后一个 owner 释放后,Bridge 会自动销毁。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const release = retainVueFormBridge(bridge)\n * onScopeDispose(release)\n * ```\n */\nexport function retainVueFormBridge<TValues extends Values>(\n bridge: VueFormBridge<TValues>\n): () => void {\n if (bridge.destroyed) {\n return () => {}\n }\n\n bridge.refCount++\n let released = false\n\n return () => {\n if (released || bridge.destroyed) {\n return\n }\n\n released = true\n bridge.refCount--\n\n if (bridge.refCount === 0) {\n disposeVueFormBridge(bridge)\n }\n }\n}\n\n/**\n * 获取一个字段的共享 Vue Ref 投影。\n *\n * @param bridge - 所属 Form Bridge。\n * @param name - 字段路径。\n * @returns 对应字段的共享 Vue Bridge。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const field = getVueFieldBridge(bridge, \"email\")\n * watchEffect(() => console.log(field.value.value))\n * ```\n */\nexport function getVueFieldBridge<\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(bridge: VueFormBridge<TValues>, name: TName): VueFieldBridge<TValues, TName> {\n const store = bridge.externalStore.field(name)\n const cachedBridge = bridge.fieldBridges.get(store as object)\n\n if (cachedBridge) {\n return cachedBridge as VueFieldBridge<TValues, TName>\n }\n\n const snapshot = store.getSnapshot()\n const value = createVueShallowRef<FieldValue<TValues, TName> | undefined>(\n snapshot.value\n )\n const errors = createVueShallowRef<readonly string[]>(snapshot.errors)\n const touched = createVueShallowRef<boolean>(snapshot.touched)\n const pending = createVueShallowRef<boolean>(snapshot.pending)\n\n const updateFieldRefs = (): void => {\n const nextSnapshot = store.getSnapshot()\n\n if (!Object.is(value.value, nextSnapshot.value)) {\n value.value = nextSnapshot.value\n }\n\n if (!areStringListsEqual(errors.value, nextSnapshot.errors)) {\n errors.value = nextSnapshot.errors\n }\n\n if (touched.value !== nextSnapshot.touched) {\n touched.value = nextSnapshot.touched\n }\n\n if (pending.value !== nextSnapshot.pending) {\n pending.value = nextSnapshot.pending\n }\n }\n\n const unsubscribe = store.subscribe(updateFieldRefs)\n const dispose = (): void => {\n unsubscribe()\n }\n\n const fieldBridge: ManagedVueFieldBridge<TValues> = {\n store,\n value,\n errors,\n touched,\n pending,\n dispose,\n }\n\n bridge.fieldBridges.set(store as object, fieldBridge)\n\n return fieldBridge as VueFieldBridge<TValues, TName>\n}\n\n/**\n * 销毁共享 Bridge、全部字段订阅与 Core External Store。\n *\n * @param bridge - 要销毁的共享 Form Bridge。\n *\n * @remarks\n * 销毁后 Bridge、字段 Ref 投影和 External Store 均不可继续使用;通常由\n * `retainVueFormBridge()` 返回的最后一个释放函数自动触发。\n *\n * @example\n * ```ts\n * disposeVueFormBridge(bridge)\n * ```\n */\nexport function disposeVueFormBridge<TValues extends Values>(\n bridge: VueFormBridge<TValues>\n): void {\n if (bridge.destroyed) {\n return\n }\n\n bridge.destroyed = true\n bridge.refCount = 0\n bridge.unsubscribe()\n\n for (const fieldBridge of bridge.fieldBridges.values()) {\n fieldBridge.dispose()\n }\n\n bridge.fieldBridges.clear()\n bridge.externalStore.dispose()\n formBridgeCache.delete(bridge.form)\n}\n\n/**\n * 创建单个 Core Form 的 Vue Bridge 和唯一 Facade。\n *\n * @param form - 要桥接的原始 Core Form。\n * @returns 新建的共享 Vue Form Bridge。\n */\nfunction createVueFormBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): VueFormBridge<TValues> {\n const externalStore = createFormExternalStore(form)\n const values = createVueShallowRef<TValues>(externalStore.values.getSnapshot())\n const touchedFields = createVueShallowRef<readonly NamePath<TValues>[]>(\n externalStore.touchedFields.getSnapshot()\n )\n const pendingFields = createVueShallowRef<PendingFieldsSnapshot<TValues>>(\n externalStore.pendingFields.getSnapshot()\n )\n const loading = createVueShallowRef<boolean>(externalStore.loading.getSnapshot())\n const fieldBridges = new Map<object, ManagedVueFieldBridge<TValues>>()\n\n const syncValues = (): void => {\n values.value = externalStore.values.getSnapshot()\n }\n\n const syncTouchedFields = (): void => {\n touchedFields.value = externalStore.touchedFields.getSnapshot()\n }\n\n const syncPendingFields = (): void => {\n pendingFields.value = externalStore.pendingFields.getSnapshot()\n }\n\n /**\n * 将 Core 提交流程状态同步到 Vue Ref。\n */\n const syncLoading = (): void => {\n loading.value = externalStore.loading.getSnapshot()\n }\n\n const unsubscribeValues = externalStore.values.subscribe(syncValues)\n const unsubscribeTouchedFields =\n externalStore.touchedFields.subscribe(syncTouchedFields)\n const unsubscribePendingFields =\n externalStore.pendingFields.subscribe(syncPendingFields)\n const unsubscribeLoading = externalStore.loading.subscribe(syncLoading)\n\n const unsubscribe = (): void => {\n unsubscribeValues()\n unsubscribeTouchedFields()\n unsubscribePendingFields()\n unsubscribeLoading()\n }\n\n const facade = getVueFormFacade(form)\n const bridge: VueFormBridge<TValues> = {\n form,\n externalStore,\n values,\n touchedFields,\n pendingFields,\n loading,\n fieldBridges,\n refCount: 0,\n destroyed: false,\n unsubscribe,\n facade,\n }\n\n return bridge\n}\n\n/**\n * 为 Core Form 创建保持完整 API 的 Vue Facade。\n *\n * @param form - 要包装的原始 Core Form。\n * @returns 可被 Vue effect 追踪的 Facade。\n */\nfunction createVueFormFacade<TValues extends Values>(\n form: SchemxInstance<TValues>\n): VueSchemxInstance<TValues> {\n let destroyed = false\n\n /**\n * 读取字段值,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取的字段路径。\n */\n const getFieldValue: SchemxInstance<TValues>[\"getFieldValue\"] = (name) => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void getVueFieldBridge(bridge, name).value.value\n }\n\n return form.getFieldValue(name)\n }\n\n /**\n * 读取字段错误,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取错误的字段路径。\n */\n const getFieldErrors: SchemxInstance<TValues>[\"getFieldErrors\"] = (name) => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void getVueFieldBridge(bridge, name).errors.value\n }\n\n return form.getFieldErrors(name)\n }\n\n /**\n * 读取字段 touched 状态,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取状态的字段路径。\n */\n const isFieldTouched: SchemxInstance<TValues>[\"isFieldTouched\"] = (name) => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void getVueFieldBridge(bridge, name).touched.value\n }\n\n return form.isFieldTouched(name)\n }\n\n /**\n * 读取字段 pending 状态,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取状态的字段路径。\n */\n const isFieldPending: SchemxInstance<TValues>[\"isFieldPending\"] = (name) => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void getVueFieldBridge(bridge, name).pending.value\n }\n\n return form.isFieldPending(name)\n }\n\n /**\n * 读取全部或指定字段值,并为读取范围收集 Vue 依赖。\n *\n * @param names - 可选的字段路径列表;省略时追踪完整表单值。\n */\n const getFieldsValue = ((names?: NamePath<TValues>[]) => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n if (names === undefined) {\n void bridge.values.value\n } else {\n for (const name of names) {\n void getVueFieldBridge(bridge, name).value.value\n }\n }\n }\n\n return form.getFieldsValue(names)\n }) as SchemxInstance<TValues>[\"getFieldsValue\"]\n\n const getTouchedFields: SchemxInstance<TValues>[\"getTouchedFields\"] = () => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void bridge.touchedFields.value\n }\n\n return form.getTouchedFields()\n }\n\n const getPendingFields: SchemxInstance<TValues>[\"getPendingFields\"] = () => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void bridge.pendingFields.value\n }\n\n return form.getPendingFields()\n }\n\n /**\n * 读取提交状态,并在 Vue effect 中收集对应 Ref 依赖。\n */\n const isLoading: SchemxInstance<TValues>[\"isLoading\"] = () => {\n if (!destroyed) {\n const bridge = getVueFormBridge(form)\n\n void bridge.loading.value\n }\n\n return form.isLoading()\n }\n\n const destroy: SchemxInstance<TValues>[\"destroy\"] = () => {\n if (destroyed) {\n return\n }\n\n destroyed = true\n const bridge = formBridgeCache.get(form) as VueFormBridge<TValues> | undefined\n\n if (bridge) {\n disposeVueFormBridge(bridge)\n }\n\n form.destroy()\n }\n\n return {\n ...form,\n getFieldValue,\n getFieldErrors,\n isFieldTouched,\n isFieldPending,\n getFieldsValue,\n getTouchedFields,\n getPendingFields,\n isLoading,\n destroy,\n }\n}\n\n/**\n * 比较错误消息内容,避免无变化时写入 Vue Ref。\n *\n * @param previous - 当前 Ref 中保存的错误消息列表。\n * @param next - External Store 返回的最新错误消息列表。\n * @returns 两个列表的长度和每一项都相同时返回 true。\n */\nfunction areStringListsEqual(\n previous: readonly string[],\n next: readonly string[]\n): boolean {\n return (\n previous.length === next.length &&\n previous.every((value, index) => value === next[index])\n )\n}\n","/**\n * 全局渲染器注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * vant 等 UI 适配层通过 import 后调用 registerAll 注册渲染器,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rendererProvider\n */\n\nimport {\n createRendererRegistry,\n type RendererRegistry,\n} from \"@schemx/core/adapter\"\n\nimport type { SchemxRendererKey } from \"@schemx/core\"\n\n/**\n * 全局渲染器注册实例\n *\n * @example\n * ```ts\n * // UI 适配层注册渲染器\n * import { rendererRegistry } from '@schemx/vue'\n * rendererRegistry.registerAll({ input: InputRenderer, ... })\n *\n * // useForm 内部自动使用\n * // props 传入的 rendererRegistry 优先级更高\n * ```\n */\nexport const rendererRegistry: RendererRegistry<SchemxRendererKey> =\n createRendererRegistry<SchemxRendererKey>(\"input\")\n","/**\n * 全局校验规则注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * UI 适配层通过 import 后调用 register/registerAll 注册自定义规则,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rulesProvider\n */\n\nimport { createValidationRuleRegistry, type ValidationRuleRegistry } from \"@schemx/core\"\n\n/**\n * 全局校验规则注册实例\n *\n * @example\n * ```ts\n * // 注册自定义规则\n * import { validationRuleRegistry } from '@schemx/vue'\n * validationRuleRegistry.register('phone', phoneRule)\n *\n * // useForm 内部自动使用\n * // props 传入的 validationRuleRegistry 优先级更高\n * ```\n */\nexport const validationRuleRegistry: ValidationRuleRegistry =\n createValidationRuleRegistry()\n","/**\n * useForm - Schemx 表单实例的 Vue 生命周期适配。\n *\n * 负责创建由当前 Vue effect scope 持有的 SchemxInstance,并在 scope 销毁时\n * 自动释放实例。该模块不负责 provide/inject;表单上下文统一由\n * createFormContext() 注册,避免外部传入 form 时跳过上下文注册。\n *\n * @module hooks/useForm\n */\nimport { onScopeDispose } from \"vue\"\n\nimport { createForm, mergeSchemxConfig, type SchemxConfig } from \"@schemx/core\"\n\nimport { getSchemxAppConfig } from \"../config\"\nimport {\n getVueFormBridge,\n getVueFormFacade,\n retainVueFormBridge,\n type VueSchemxInstance,\n} from \"../formBridge\"\nimport { rendererRegistry as globalRendererRegistry } from \"../utils/rendererProvider\"\nimport { validationRuleRegistry as globalValidationRuleRegistry } from \"../utils/rulesProvider\"\n\nimport type { CreateFormOptions, NamePath, Values } from \"@schemx/core\"\n\n/**\n * useForm 配置选项。\n *\n * 当前与 core 层 CreateFormOptions 保持一致,并在 Vue 层自动补充默认的\n * rendererRegistry 和 validationRuleRegistry。保留独立类型用于后续扩展\n * Vue 专属配置,而不污染 core 层接口。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface UseFormOptions<TValues extends Values> extends CreateFormOptions<\n TValues,\n NamePath<TValues>\n> {}\n\n/**\n * 创建由当前 Vue effect scope 持有的表单实例。\n *\n * useForm 只负责以下职责:\n * 1. 合并表单显式、App 安装和 Vue 层默认配置;\n * 2. 同步创建 SchemxInstance;\n * 3. 在当前 effect scope 销毁时调用 instance.destroy()。\n *\n * useForm 不再自动调用 provide()。需要向后代组件暴露实例时,应由\n * <SchemxForm> 或其他 Provider 组件显式调用 createFormContext(form)。\n * 这样无论实例是内部创建还是通过 props.form 外部传入,都能走同一条\n * 上下文注册路径,并保持清晰的实例所有权:谁创建,谁销毁。\n *\n * 该函数应在组件 setup() 或其他有效的 Vue effect scope 中同步调用。\n * 非 Vue 生命周期场景请直接使用 @schemx/core 的 createForm(),并自行销毁实例。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 表单创建配置\n * @returns 当前 scope 持有的 SchemxInstance\n *\n * @example\n * ```ts\n * const form = useForm({\n * initialValues: {\n * name: \"\",\n * email: \"\",\n * },\n * onFinish: async (values) => {\n * await api.submit(values)\n * },\n * })\n *\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n *\n * @example\n * ```ts\n * // Provider 组件中统一注册上下文。\n * const form = props.form ?? useForm(options)\n * createFormContext(form)\n * ```\n */\nexport function useForm<TValues extends Values = Values>(\n options: UseFormOptions<TValues> = {}\n): VueSchemxInstance<TValues> {\n // 按表单、App、Vue 包默认值的优先级解析可继承配置。\n const configuredOptions = mergeSchemxConfig<TValues>(\n getUseFormSchemxConfig(options),\n // App 安装配置以 Values 存储;在 useForm 边界关联到当前 TValues。\n getSchemxAppConfig() as SchemxConfig<TValues>,\n {\n rendererRegistry: globalRendererRegistry,\n validationRuleRegistry: globalValidationRuleRegistry,\n }\n )\n\n // 将已合并配置写入 Form 创建选项,避免 Core 再按较低优先级覆盖 Vue 结果。\n const mergedOptions: CreateFormOptions<TValues> = {\n ...options,\n ...configuredOptions,\n }\n\n // 表单实例是当前 scope 内的一次性资源,不需要使用 computed 包装。\n const instance = createForm<TValues>(mergedOptions)\n const form = getVueFormFacade(instance)\n const releaseBridge = retainVueFormBridge(getVueFormBridge(instance))\n\n // useForm 创建的实例归当前 effect scope 所有,因此由当前 scope 负责销毁。\n onScopeDispose(() => {\n releaseBridge()\n form.destroy()\n })\n\n return form\n}\n\n/**\n * 从 useForm 选项中提取需要参与 Vue 优先级计算的可继承配置。\n *\n * @param options - useForm 的完整创建选项。\n * @returns 仅包含调用方实际提供字段的 SchemxConfig。\n */\nfunction getUseFormSchemxConfig<TValues extends Values>(\n options: UseFormOptions<TValues>\n): SchemxConfig<TValues> {\n const {\n schemaConfig = {},\n rendererProps = undefined,\n validatorAdapters = [],\n defaultRendererType = undefined,\n rendererRegistry = undefined,\n validationRuleRegistry = undefined,\n } = options\n\n return {\n schemaConfig,\n rendererProps,\n defaultRendererType,\n rendererRegistry,\n validationRuleRegistry,\n validatorAdapters,\n }\n}\n","/**\n * Schemx 表单实例的 Vue provide/inject 适配。\n *\n * 负责在组件树中注册和读取 SchemxInstance,不负责创建或销毁实例。\n * 表单实例无论由 <SchemxForm> 内部创建,还是通过 form 属性从外部传入,\n * 都应由 <SchemxForm> 在 setup() 同步阶段调用 createFormContext() 注册。\n *\n * @module hooks/provideFormContext\n */\nimport { inject, onScopeDispose, type InjectionKey, provide } from \"vue\"\n\nimport {\n getVueFormBridge,\n getVueFormFacade,\n retainVueFormBridge,\n type VueSchemxInstance,\n} from \"../formBridge\"\n\nimport type { SchemxInstance, Values } from \"@schemx/core\"\n\n// provide/inject 只传递运行时实例,不在此边界固定表单值泛型;\n// 否则 SchemxInstance 的方法参数会使具体 TValues 与默认 Values 不兼容。\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype FormContextInstance = VueSchemxInstance<any>\n\n/**\n * SchemxInstance 在 Vue provide/inject 中使用的注入 key。\n *\n * 该 key 只应与同一份包代码导出的 createFormContext() 和 useFormContext()\n * 配套使用;重复安装不兼容版本的包不会共享此上下文。\n */\nexport const SCHEMX_FORM_INSTANCE_KEY = Symbol(\"schemx:instance\") as InjectionKey<\n FormContextInstance\n>\n\n/**\n * 旧版表单实例注入 key。\n *\n * @deprecated 请使用 SCHEMX_FORM_INSTANCE_KEY。该别名仅用于兼容旧测试和旧适配代码。\n */\nexport const FORM_INSTANCE_KEY = SCHEMX_FORM_INSTANCE_KEY\n\n/**\n * 向当前组件的后代组件提供表单实例。\n *\n * 必须在组件 setup() 的同步执行阶段调用;不要放入 onMounted、nextTick、\n * Promise 或其他异步回调中,否则 Vue 无法把上下文关联到当前组件实例。\n *\n * @typeParam TValues - 表单值类型\n * @param instance - 要提供给后代组件的表单实例\n *\n * @remarks\n * 该函数不接管实例所有权,也不会在组件卸载时销毁实例。\n *\n * @example\n * ```ts\n * const form = props.form ?? useForm(options)\n *\n * // 无论 form 来自外部还是内部,都统一注册上下文。\n * createFormContext(form)\n * ```\n */\nexport function createFormContext<TValues extends Values = Values>(\n instance: SchemxInstance<TValues>\n): VueSchemxInstance<TValues> {\n const form = getVueFormFacade(instance)\n const releaseBridge = retainVueFormBridge(getVueFormBridge(form))\n\n provide<FormContextInstance>(SCHEMX_FORM_INSTANCE_KEY, form as FormContextInstance)\n onScopeDispose(releaseBridge)\n\n return form\n}\n\n/**\n * 获取最近祖先组件提供的表单实例。\n *\n * 该函数只能读取祖先组件注册的上下文,不能读取当前组件自身或后代组件\n * 调用 createFormContext() 提供的值。通常由 useField()、useWatch() 等其他\n * 表单 hook 间接调用。\n *\n * @typeParam TValues - 表单值类型\n * @returns 当前组件所属表单树中的 SchemxInstance\n *\n * @throws Error 当前组件不在已注册表单实例上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const form = useFormContext<FormValues>()\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n */\nexport function useFormContext<\n TValues extends Values = Values,\n>(): VueSchemxInstance<TValues> {\n const instance = inject<FormContextInstance | null>(SCHEMX_FORM_INSTANCE_KEY, null)\n\n if (!instance) {\n throw new Error(\n \"[schemx] useFormContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormContext(form) is called synchronously during setup().\"\n )\n }\n\n return instance as VueSchemxInstance<TValues>\n}\n","/**\n * useField - 字段控制 Hook。\n *\n * 复用共享 Vue Form Bridge 的字段 Ref,并将 Core 的字段写入、校验和规则操作\n * 保持在原始 `createField()` 实例上。\n *\n * @module hooks/useField\n */\n\nimport { computed } from \"vue\"\n\nimport { createField } from \"@schemx/core\"\n\nimport {\n getCoreForm,\n getVueFieldBridge,\n getVueFormBridge,\n type VueFieldBridge,\n type VueSchemxInstance,\n} from \"../formBridge\"\n\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\nimport type { FieldInstance } from \"../types/field\"\n\n/**\n * 创建一份字段控制器,并将所有读取状态投影到共享 Vue Field Bridge。\n */\nfunction createFieldHook<\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(\n form: VueSchemxInstance<TValues>,\n name: TName,\n fieldBridge: VueFieldBridge<TValues, TName>\n): FieldInstance<TValues> {\n const coreForm = getCoreForm(form)\n const coreField = createField<TValues>(coreForm, name)\n const errors = computed(() => fieldBridge.errors.value)\n const dirty = computed(() => fieldBridge.touched.value)\n const pending = computed(() => fieldBridge.pending.value)\n\n const getValue = (): ReturnType<typeof coreField.getValue> => fieldBridge.value.value\n const getErrors = (): ReturnType<typeof coreField.getErrors> => fieldBridge.errors.value\n const isTouched = (): boolean => fieldBridge.touched.value\n const isPending = (): boolean => fieldBridge.pending.value\n const getValues = (): Readonly<TValues> => form.getFieldsValue()\n\n return {\n ...coreField,\n name,\n value: fieldBridge.value,\n errors,\n dirty,\n pending,\n getValue,\n getErrors,\n isTouched,\n isPending,\n getValues,\n }\n}\n\n/**\n * 获取单个字段的控制能力。\n *\n * @param name - 字段路径(支持嵌套字段)。\n * @returns 包含 Vue Ref 状态和 Core 字段操作的控制器。\n */\nexport const useField = <TValues extends Values = Values>(\n name: NamePath<TValues>\n): FieldInstance<TValues> => {\n const form = useFormContext<TValues>()\n const coreForm = getCoreForm(form)\n const formBridge = getVueFormBridge(coreForm)\n const fieldBridge = getVueFieldBridge(formBridge, name)\n\n return createFieldHook(form, name, fieldBridge)\n}\n\nexport default useField\n","import { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { FieldInstance } from \"../types/field\"\nimport type { Values } from \"@schemx/core\"\n\n/** 当前字段实例在 Vue provide/inject 中使用的注入 key。 */\nconst SCHEMX_FORM_FIELD_KEY: InjectionKey<FieldInstance> = Symbol(\n \"schemx:field\"\n) as InjectionKey<FieldInstance<Values>>\n\n/**\n * 向后代组件提供当前字段实例。\n *\n * 应在创建字段实例的组件 setup() 同步阶段调用,通常由 FormItem\n * 在调用 useField() 后使用,使字段插槽或自定义子组件能够读取同一实例。\n *\n * @param field - useField() 返回的字段实例\n *\n * @remarks\n * 该函数只注册字段上下文,不创建字段实例,也不负责字段实例的生命周期。\n *\n * @example\n * ```typescript\n * // FormItem 中\n * const field = useField('date')\n * createFieldContext(field)\n *\n * ```\n */\nexport function createFieldContext<TValues extends Values = Values>(\n field: FieldInstance<TValues>\n): void {\n provide<FieldInstance<TValues>>(SCHEMX_FORM_FIELD_KEY, field)\n}\n\n/**\n * 获取最近祖先组件提供的当前字段实例。\n *\n * 必须在已调用 createFieldContext() 的组件子树内调用,否则抛出错误。\n *\n * @returns useField() 返回的字段实例\n *\n * @throws Error 当前组件不在字段上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const field = useFieldContext()\n * field.errors.value // readonly string[]\n * field.getValue() // 当前字段值\n * ```\n */\nexport function useFieldContext(): FieldInstance<Values> {\n const field = inject<FieldInstance<Values>>(SCHEMX_FORM_FIELD_KEY)\n\n if (!field) {\n throw new Error(\n \"[schemx] useFieldContext() must be called inside a component tree where \" +\n \"createFieldContext(field) has been called.\"\n )\n }\n\n return field\n}\n","/**\n * useWatch - 监听字段变化(Vue 组合式 API 版本)\n *\n * 基于 core 层的 {@link createWatch} 实现,\n * 自动通过 `useFormContext` 获取 formContext,\n * 并在 `onUnmounted` 时自动取消监听,无需手动管理生命周期。\n *\n * 提供三种监听模式:\n * - 全局监听:`useWatch(callback, options?)` — 监听表单中任意字段变化\n * - 单字段监听:`useWatch(name, callback, options?)` — 监听指定字段变化\n * - 多字段监听:`useWatch(names, callback, options?)` — 监听一组字段变化\n *\n * 同时提供语义化的便捷方法:\n * - {@link useWatchField} — 单字段监听\n * - {@link useWatchFields} — 多字段监听\n * - {@link useWatchAll} — 全局监听\n *\n * @module hooks/useWatch\n *\n * @example\n * ```ts\n * // 全局监听\n * useWatch((snapshot, payload) => {\n * console.log('form changed:', snapshot, payload)\n * })\n *\n * // 单字段监听\n * useWatch('username', (current, prev) => {\n * console.log('username changed:', prev, '->', current)\n * }, { immediate: true })\n *\n * // 多字段监听\n * useWatch(['firstName', 'lastName'], (currentValues, prevValues) => {\n * console.log('name changed:', currentValues)\n * }, { inequality: true })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createWatch } from \"@schemx/core\"\n\nimport { getCoreForm } from \"../formBridge\"\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\nimport type {\n CreateWatchOptions,\n WatchAllCallback,\n WatchFieldCallback,\n WatchFieldsCallback,\n} from \"@schemx/core\"\n\n/**\n * 监听所有字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * const dispose = useWatch((values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues, values)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n callback: WatchAllCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听单个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param name - 字段路径\n * @param callback - 单字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch(\"email\", (_values, payload) => {\n * console.log(payload.prevValue, payload.value)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听多个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 字段路径数组\n * @param callback - 多字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch([\"firstName\", \"lastName\"], (_values, payload) => {\n * console.log(payload.changedPaths, payload.changedValues, payload.prevValues)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * useWatch 实现 — 获取 form 实例后委托给 core 层的 createWatch\n *\n * @typeParam TValues - 表单值类型。\n */\nexport function useWatch<TValues extends Values>(\n nameOrNamesOrCallback:\n NamePath<TValues> | NamePath<TValues>[] | WatchAllCallback<TValues>,\n callbackOrOptions?:\n WatchFieldCallback<TValues> | WatchFieldsCallback<TValues> | CreateWatchOptions,\n maybeOptions?: CreateWatchOptions\n): () => void {\n const form = useFormContext<TValues>()\n\n const dispose = (createWatch as any)(\n getCoreForm(form),\n nameOrNamesOrCallback,\n callbackOrOptions,\n maybeOptions\n )\n\n onUnmounted(dispose)\n\n return dispose\n}\n\n/**\n * 监听单个字段变化的便捷方法\n *\n * 等价于 `useWatch(name, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param name - 要监听的字段路径\n * @param callback - 字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchField(\"email\", (_values, payload) => {\n * const { value, prevValue } = payload\n * console.log(prevValue, value)\n * })\n * ```\n */\nexport function useWatchField<TValues extends Values = Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(name, callback, options)\n}\n\n/**\n * 监听多个字段变化的便捷方法\n *\n * 等价于 `useWatch(names, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 要监听的字段路径数组\n * @param callback - 字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchFields([\"firstName\", \"lastName\"], (_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchFields<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(names, callback, options)\n}\n\n/**\n * 监听所有字段变化的便捷方法\n *\n * 等价于 `useWatch(callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchAll((_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchAll<TValues extends Values = Values>(\n callback: WatchAllCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch(callback, options)\n}\n\nexport default useWatch\n","/**\n * useDictionary - 字典选项 Hook\n *\n * 通过 api 函数加载字典选项,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持通过 dependsOn 声明依赖字段,\n * 当依赖字段变化时自动重新请求。\n *\n * @module hooks/useDictionary\n */\n\nimport { onMounted, Ref, ref, shallowRef } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFormContext } from \"./provideFormContext\"\nimport { useWatchFields } from \"./useWatch\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\n\nexport type { SchemxDictionary, SchemxWithDictionary } from \"@/types/dictionary\"\n\n/**\n * useDictionary 返回值\n */\nexport interface UseDictionaryReturn<TOption = unknown> {\n /** 远程加载的字典选项列表(响应式) */\n list: Ref<TOption[]>\n /** 请求加载状态(响应式) */\n loading: Ref<boolean>\n /** 请求错误信息(响应式) */\n error: Ref<Error | undefined>\n /** 触发字典选项加载 */\n loadDict: () => Promise<void>\n /** 使用当前配置重新执行 api */\n refresh: () => Promise<void>\n /** 直接修改 list 的值,不触发 api 调用 */\n mutate: (data: TOption[]) => void\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **UseDictionaryReturn**\n */\nexport type UseDictOptionsReturn<TOption = unknown> = UseDictionaryReturn<TOption>\n\n/**\n * 将未知抛出值规范化为 `Error` 实例\n *\n * @param err - 捕获的值(可能是任意类型)\n * @returns 包装原始值的 `Error` 对象\n */\nexport function normalizeError(err: unknown): Error {\n if (err instanceof Error) return err\n\n return new Error(String(err))\n}\n\n/**\n * 加载字典选项\n *\n * 通过 api 函数获取数据,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持依赖字段联动、竞态控制、\n * 重试、错误处理等能力。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 字典配置选项\n * @param fieldName - 当前字段名,用于 resetOnDepsChange 时清空字段值\n * @returns `{ list, loading, error, loadDict, refresh, mutate }`:响应式选项、\n * 加载与错误状态,以及加载、刷新和本地更新方法\n *\n * @example\n * ```ts\n * // 基础用法\n * const { list, loading, error, loadDict, refresh, mutate } = useDictionary({\n * api: async () => {\n * const res = await fetch('/api/options')\n * return res.json()\n * },\n * })\n *\n * await loadDict() // 主动加载\n * await refresh() // 使用当前配置重新加载\n * mutate([{ label: \"手动选项\", value: \"manual\" }])\n * console.log(list.value, loading.value, error.value)\n *\n * // 依赖联动(带泛型)\n * const { list } = useDictionary<MyFormValues>({\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * }, 'city')\n * ```\n */\nexport const useDictionary = <\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n TResponse = unknown,\n TOption = unknown,\n>(\n options: SchemxDictionary<TValues, TResponse, TOption>,\n fieldName?: TName\n): UseDictionaryReturn<TOption> => {\n const instance = useFormContext<TValues>()\n\n const list = shallowRef<TOption[]>([])\n\n const loading = ref<boolean>(false)\n\n const error = ref<Error | undefined>(undefined)\n\n // 竞态控制:仅最新请求写入状态\n let requestCount = 0\n\n /**\n * 使用配置的 formatter 格式化原始响应数据\n */\n const format = async (res: Awaited<TResponse>): Promise<TOption[]> => {\n if (typeof options?.formatter === \"function\") {\n return await options.formatter(res, instance)\n }\n\n if (!Array.isArray(res)) {\n throw new Error(\n \"[schemx] Dictionary api must return an array when formatter is not provided.\"\n )\n }\n\n return res as TOption[]\n }\n\n /**\n * 带重试的执行\n */\n const executeWithRetry = async (formValues: TValues): Promise<Awaited<TResponse>> => {\n const maxRetries = options.retryCount ?? 0\n\n const retryDelay = options.retryInterval ?? 1000\n\n let lastError: Error = new Error(\"Unknown error\")\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await options.api(formValues, instance)\n } catch (err) {\n lastError = normalizeError(err)\n\n if (attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, retryDelay))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 执行 api 函数加载字典选项\n */\n const loadDict = async (): Promise<void> => {\n try {\n const formValues = instance.getFieldsValue()\n\n // shouldFetch 检查\n if (typeof options.shouldFetch === \"function\" && !options.shouldFetch(formValues)) {\n list.value = []\n loading.value = false\n\n return\n }\n\n loading.value = true\n error.value = undefined\n\n // 递增请求计数器用于竞态控制\n const currentCount = ++requestCount\n\n const res = await executeWithRetry(formValues)\n\n // 竞态检查:丢弃过期响应\n if (currentCount !== requestCount) return\n\n const formatted = await format(res)\n\n // 格式化后再次竞态检查(格式化可能是异步的)\n if (currentCount !== requestCount) return\n\n list.value = formatted\n error.value = undefined\n\n // onSuccess 回调\n if (typeof options.onSuccess === \"function\") {\n options.onSuccess(formatted, instance)\n }\n\n loading.value = false\n } catch (err) {\n const normalized = normalizeError(err)\n\n error.value = normalized\n list.value = []\n loading.value = false\n\n if (typeof options.onError === \"function\") {\n options.onError(normalized, instance)\n }\n }\n }\n\n const refresh = (): Promise<void> => loadDict()\n\n const mutate = (data: TOption[]): void => {\n list.value = data\n }\n\n // ========== 依赖字段监听 ==========\n if (options.dependsOn?.length) {\n useWatchFields(options.dependsOn, (latestSnapshot, _payload) => {\n // 1. 触发 onDepsChange 回调\n if (typeof options.onDepsChange === \"function\") {\n options.onDepsChange(latestSnapshot as TValues, instance)\n }\n\n // 2. 清空当前字段值\n if (options.resetOnDepsChange && fieldName) {\n instance.setFieldValue(fieldName, undefined)\n }\n\n // 3. 重新执行 api\n void loadDict()\n })\n }\n\n // ========== 挂载行为 ==========\n const immediate = options.immediate ?? true\n\n onMounted(() => {\n if (immediate) {\n void loadDict()\n }\n })\n\n return { list, loading, error, loadDict, refresh, mutate }\n}\n\nexport default useDictionary\n","/**\n * 表单级配置的 Vue provide/inject 适配。\n *\n * `SchemxForm` 通过 createFormConfigContext() 提供默认展示配置,\n * 字段组件通过 useFormConfigContext() 读取这些配置。\n *\n * @module hooks/provideFormConfigContext\n */\n\nimport { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { SchemxSchemaConfig } from \"@schemx/core\"\n\n/** 表单级展示配置在 Vue provide/inject 中使用的注入 key。 */\nexport const SCHEMX_FORM_CONFIG_KEY = Symbol(\n \"schemx:form-config\"\n) as InjectionKey<FormContextProps>\n\n/**\n * 表单级默认配置。\n *\n * 该类型只描述 Schema 通用配置,不包含表单实例、表单数据、回调、样式或 Registry。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface FormContextProps {\n schemaConfig: Partial<SchemxSchemaConfig>\n}\n\n/**\n * 创建并注入表单级配置上下文。\n *\n * 应在 `SchemxForm` 或自定义 Provider 的 setup() 同步阶段调用,\n * 使后代字段组件能够读取 readonly、disabled、labelAlign 等默认配置。\n *\n * @param props - 要提供给后代组件的 Schema 配置\n *\n * @remarks\n * 该函数只注册上下文,不创建或销毁表单实例,也不改变传入配置的所有权。\n *\n * @example\n * ```ts\n * createFormConfigContext({ schemaConfig: { readonly: true, labelAlign: \"right\" } })\n * ```\n */\nexport const createFormConfigContext = (props: FormContextProps): void => {\n provide<FormContextProps>(SCHEMX_FORM_CONFIG_KEY, props)\n}\n\n/**\n * `createFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 createFormConfigContext。\n *\n * @example\n * ```ts\n * createContext({ schemaConfig: { disabled: true } })\n * ```\n */\nexport const createContext = createFormConfigContext\n\n/**\n * 获取最近祖先组件提供的表单级配置。\n *\n * 只能读取祖先组件通过 createFormConfigContext() 注册的配置,\n * 不能读取当前组件或后代组件提供的值。\n *\n * @returns 当前表单树中的表单级默认配置\n *\n * @throws Error 当前组件不在已注册表单配置上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const context = useFormConfigContext()\n * console.log(context.schemaConfig.readonly, context.schemaConfig.disabled)\n * ```\n */\nexport function useFormConfigContext(): FormContextProps {\n const context = inject<FormContextProps>(SCHEMX_FORM_CONFIG_KEY)\n\n if (!context) {\n throw new Error(\n \"[schemx] useFormConfigContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormConfigContext(props) is called synchronously during setup().\"\n )\n }\n\n return context\n}\n\n/**\n * `useFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 useFormConfigContext。\n *\n * @example\n * ```ts\n * const context = useContext()\n * ```\n */\nexport const useContext = useFormConfigContext\n","/**\n * useStableRef - 引用稳定化的 shallowRef\n *\n * 封装 `shallowRef` + `watchEffect` + `isShallowEqual`,\n * 仅在工厂函数返回值与当前值浅不相等时才更新引用,\n * 避免下游组件因对象引用变化而不必要地重渲染。\n *\n * @module hooks/useStableRef\n *\n * @example\n * ```ts\n * import { useStableRef } from '@schemx/vue'\n *\n * const stableProps = useStableRef(() => ({\n * visible: visible.value,\n * disabled: disabled.value,\n * value: field.getValue(),\n * }))\n * ```\n */\n\nimport { type ShallowRef, shallowRef, watchEffect } from \"vue\"\n\nimport { isShallowEqual } from \"../utils/equal\"\n\n/**\n * 创建一个引用稳定的 shallowRef。\n *\n * 在 watchEffect 中执行工厂函数,自动追踪响应式依赖。\n * 仅当新旧值浅比较不相等时才替换 `.value`,保持引用稳定。\n *\n * @typeParam TValue - 需要保持引用稳定的对象类型\n *\n * @param factory - 返回目标对象的工厂函数,在 watchEffect 内执行\n *\n * @returns 只读的 ShallowRef,引用仅在属性值真正变化时更新\n *\n * @example\n * ```ts\n * const props = useStableRef(() => ({\n * value: field.getValue(),\n * onChange: handleChange,\n * }))\n *\n * // props.value 的引用在属性值未变时保持不变\n * h(Component, props.value)\n * ```\n */\nexport function useStableRef<TValue extends Record<string, any>>(\n factory: () => TValue\n): Readonly<ShallowRef<TValue>> {\n const stableRef = shallowRef({} as TValue) as ShallowRef<TValue>\n\n watchEffect(() => {\n const next = factory()\n\n if (!isShallowEqual(stableRef.value, next)) {\n stableRef.value = next\n }\n })\n\n return stableRef\n}\n\nexport default useStableRef\n","/**\n * 对象浅比较工具\n *\n * @module utils/equal\n */\n\n/**\n * 浅比较两个对象的自有属性值是否相同。\n *\n * 用于避免 computed / shallowRef 每次返回新对象引用导致子组件不必要的更新。\n * 仅比较第一层属性,函数引用通过 === 判断。\n *\n * @param a - 旧对象\n * @param b - 新对象\n *\n * @returns 两个对象浅相等时返回 true\n *\n * @example\n * shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // => true\n * shallowEqual({ a: 1 }, { a: 1, b: 2 }) // => false\n */\nexport const isShallowEqual = (\n a: Record<string, any>,\n b: Record<string, any>\n): boolean => {\n const keysA = Object.keys(a)\n\n const keysB = Object.keys(b)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => a[key] === b[key])\n}\n","/**\n * useViewSchemas - Vue ViewSchemas bridge.\n *\n * @module hooks/useViewSchemas\n */\n\nimport { computed, onScopeDispose, shallowRef } from \"vue\"\nimport type { ComputedRef, ShallowRef } from \"vue\"\n\nimport {\n isViewGroupSchema,\n type SchemxInstance,\n type SchemxViewSchema,\n type Values,\n} from \"@schemx/core\"\n\nimport { getCoreForm } from \"../formBridge\"\n\n/**\n * 同一表单内共享的 ViewSchema 响应式桥接。\n */\ninterface ViewSchemaBridge<TValues extends Values = Values> {\n /**\n * 当前桥接的使用者注册数;最后一个使用者释放后才取消订阅。\n */\n refCount: number\n /**\n * 按 schema key 建立的共享索引。\n */\n schemasByKey: ComputedRef<ReadonlyMap<string, SchemxViewSchema<TValues>>>\n /**\n * 取消 Core ViewSchema 订阅。\n */\n unsubscribe: () => void\n /**\n * 最新的 ViewSchema 列表。\n */\n viewSchemas: ShallowRef<readonly SchemxViewSchema<TValues>[]>\n}\n\n/**\n * 按表单实例复用订阅,避免每个 FormItem 重复桥接同一份 ViewSchema。\n */\nconst viewSchemaBridgeCache = new WeakMap<object, ViewSchemaBridge>()\n\n/**\n * 将表单 ViewSchema 映射为共享的 Vue 响应式引用。\n *\n * 当前作用域释放一个桥接使用者;所有使用者都释放后才取消 Core 订阅。\n *\n * @typeParam TValues - 表单值类型。\n * @param form - 要订阅的表单实例。\n * @returns 当前 ViewSchema 列表的共享响应式浅引用;调用方不应直接改写其值。\n *\n * @example\n * ```ts\n * const form = useForm()\n * const viewSchemas = useViewSchemas(form)\n *\n * watchEffect(() => console.log(viewSchemas.value))\n * ```\n */\nexport function useViewSchemas<TValues extends Values = Values>(\n form: SchemxInstance<TValues>\n): ShallowRef<readonly SchemxViewSchema<TValues>[]> {\n const bridge = acquireViewSchemaBridge(getCoreForm(form))\n\n return bridge.viewSchemas\n}\n\n/**\n * 获取指定 key 的响应式 ViewSchema,并复用当前表单的共享订阅。\n *\n * @typeParam TValues - 表单值类型。\n * @param form - 要读取的表单实例。\n * @param getKey - 返回当前 schema key 的响应式 getter。\n * @returns 对应的最新 ViewSchema;不存在时返回 undefined。\n *\n * @example\n * ```ts\n * const schema = useViewSchema(form, () => fieldKey)\n *\n * watchEffect(() => {\n * console.log(schema.value?.label)\n * })\n * ```\n */\nexport function useViewSchema<TValues extends Values = Values>(\n form: SchemxInstance<TValues>,\n getKey: () => string\n): ComputedRef<SchemxViewSchema<TValues> | undefined> {\n const bridge = acquireViewSchemaBridge(getCoreForm(form))\n\n return computed(() => bridge.schemasByKey.value.get(getKey()))\n}\n\n/**\n * 创建一个表单级 ViewSchema 桥接。\n *\n * @param form - 用于提供 ViewSchema 和订阅更新的 Core 表单实例。\n */\nfunction createViewSchemaBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): ViewSchemaBridge<TValues> {\n const viewSchemas = shallowRef<readonly SchemxViewSchema<TValues>[]>(\n form.getViewSchemas()\n )\n\n const schemasByKey = computed<ReadonlyMap<string, SchemxViewSchema<TValues>>>(() => {\n return createViewSchemaIndex(viewSchemas.value)\n })\n\n const unsubscribe = form.subscribeViewSchemas(\n /**\n * 接收 Core 发布的完整列表并替换共享引用,以保留更新边界。\n *\n * @param nextSchemas - Core 计算出的最新 ViewSchema 列表。\n */\n (nextSchemas) => {\n viewSchemas.value = nextSchemas\n }\n )\n\n return {\n refCount: 0,\n schemasByKey,\n unsubscribe,\n viewSchemas,\n }\n}\n\n/**\n * 获取共享桥接,并把使用权绑定到当前 Vue effect scope。\n *\n * @param form - 用于查找或创建共享桥接的 Core 表单实例。\n */\nfunction acquireViewSchemaBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): ViewSchemaBridge<TValues> {\n const cachedBridge = viewSchemaBridgeCache.get(form) as\n ViewSchemaBridge<TValues> | undefined\n\n const bridge = cachedBridge ?? createViewSchemaBridge(form)\n\n if (!cachedBridge) {\n viewSchemaBridgeCache.set(form, bridge as ViewSchemaBridge)\n }\n\n bridge.refCount++\n\n onScopeDispose(() => {\n bridge.refCount--\n\n if (bridge.refCount > 0) {\n return\n }\n\n bridge.unsubscribe()\n viewSchemaBridgeCache.delete(form)\n })\n\n return bridge\n}\n\n/**\n * 递归索引顶层和分组内的 ViewSchema,供任意 FormItem 按 key 查询。\n *\n * @param viewSchemas - 待索引的顶层 ViewSchema 列表。\n * @returns 按 schema key 建立的只读索引。\n */\nfunction createViewSchemaIndex<TValues extends Values>(\n viewSchemas: readonly SchemxViewSchema<TValues>[]\n): ReadonlyMap<string, SchemxViewSchema<TValues>> {\n const schemasByKey = new Map<string, SchemxViewSchema<TValues>>()\n\n /**\n * 将当前层及其分组子节点追加到索引。\n *\n * @param schemas - 当前层待追加的 ViewSchema 列表。\n */\n const appendSchemas = (schemas: readonly SchemxViewSchema<TValues>[]): void => {\n for (const schema of schemas) {\n schemasByKey.set(schema.key, schema)\n\n if (isViewGroupSchema(schema)) {\n appendSchemas(schema.children)\n }\n }\n }\n\n appendSchemas(viewSchemas)\n\n return schemasByKey\n}\n\nexport default useViewSchemas\n","/**\n * useFormSelector - Core 表单值的 Vue 响应式选择器桥接。\n *\n * @module hooks/useFormSelector\n */\n\nimport { onScopeDispose, readonly, shallowRef, watch } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\nimport { getCoreForm, getVueFormBridge, retainVueFormBridge } from \"../formBridge\"\n\nimport type { SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * `useFormSelector` 的选中结果比较配置。\n */\nexport interface UseFormSelectorOptions<TSelected> {\n /**\n * 判断两次 selector 结果是否等价;返回 true 时保持当前 Ref 值。\n * 默认使用 `Object.is`。\n */\n equals?: (previous: TSelected, next: TSelected) => boolean\n}\n\n/**\n * 将 Core 表单值按 selector 映射为 Vue 只读浅引用。\n *\n * selector 只应读取快照,不应通过快照修改表单值或调用表单写入方法。\n * `Readonly<TValues>` 仅表达顶层只读,嵌套字段应按不可变值使用;字段值需通过\n * `setFieldValue()` 或 `setFieldsValue()` 更新,才能触发后续计算。\n *\n * @typeParam TValues - 表单值类型。\n * @typeParam TSelected - selector 返回值类型。\n * @param form - 要订阅的表单实例。\n * @param selector - 从当前表单值快照派生结果的纯函数。\n * @param options - 选中结果的比较配置。\n * @returns selector 当前结果的 Vue 只读浅引用。\n *\n * @example\n * ```ts\n * const selectedName = useFormSelector(form, (values) => values.name)\n *\n * watchEffect(() => {\n * console.log(selectedName.value)\n * })\n * ```\n */\nexport function useFormSelector<\n TValues extends Values = Values,\n TSelected = unknown,\n>(\n form: SchemxInstance<TValues>,\n selector: (values: Readonly<TValues>) => TSelected,\n options: UseFormSelectorOptions<TSelected> = {}\n): Readonly<ShallowRef<TSelected>> {\n const bridge = getVueFormBridge(getCoreForm(form))\n const releaseBridge = retainVueFormBridge(bridge)\n const selected = shallowRef<TSelected>(selector(bridge.values.value))\n const equals = options.equals ?? Object.is\n\n /**\n * 根据完整快照重新计算 selector,并只在结果变化时更新 Ref。\n *\n * @param values - Bridge 发布的最新完整表单快照。\n */\n const stop = watch(\n bridge.values,\n (values) => {\n const next = selector(values)\n\n if (!equals(selected.value, next)) {\n selected.value = next\n }\n },\n { flush: \"sync\" }\n )\n\n onScopeDispose(() => {\n stop()\n releaseBridge()\n })\n\n return readonly(selected) as Readonly<ShallowRef<TSelected>>\n}\n\nexport default useFormSelector\n","/**\n * 校验触发工具\n *\n * 提供校验触发时机的合并、归一化和匹配判断功能。\n *\n * @module utils/validation\n */\n\nimport type { ValidationTrigger } from \"@schemx/core\"\n\n/** 触发时机类型(支持单个或数组) */\nexport type TriggerConfig = ValidationTrigger | ValidationTrigger[]\n\n/** 归一化后的触发类型 */\nexport type NormalizedTrigger = \"blur\" | \"change\" | \"submit\"\n\n/**\n * 判断触发时机配置是否有效。\n *\n * undefined 和空数组视为无效,其余值(包括单个字符串、非空数组)视为有效。\n *\n * @param v - 触发时机配置\n *\n * @returns 配置有效时返回 true\n */\nfunction isValidTrigger(v: TriggerConfig | undefined): v is TriggerConfig {\n if (v === undefined) return false\n if (Array.isArray(v) && v.length === 0) return false\n\n return true\n}\n\n/**\n * 合并校验触发时机配置。\n *\n * 按优先级从高到低取值:列级配置 > 表单上下文配置 > 默认值。\n * 跳过 undefined 和空数组,确保空数组不会意外覆盖后续配置。\n *\n * @param columnTrigger - 列级配置的触发时机\n * @param contextTrigger - 表单上下文配置的触发时机\n * @param defaultTrigger - 兜底默认值\n *\n * @returns 最终生效的触发时机\n *\n * @example\n * mergeTrigger(\"onBlur\", \"onChange\", \"onSubmit\") // => \"onBlur\"\n * mergeTrigger(undefined, \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger([], \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger(undefined, undefined, \"onSubmit\") // => \"onSubmit\"\n */\nexport function mergeTrigger(\n columnTrigger: TriggerConfig | undefined,\n contextTrigger: TriggerConfig | undefined,\n defaultTrigger: TriggerConfig\n): TriggerConfig {\n if (isValidTrigger(columnTrigger)) return columnTrigger\n\n if (isValidTrigger(contextTrigger)) return contextTrigger\n\n return defaultTrigger\n}\n\n/**\n * 归一化触发类型字符串。\n *\n * 将带 `on` 前缀的格式统一为短格式:\n * `\"onBlur\"` → `\"blur\"`、`\"onChange\"` → `\"change\"`、`\"onSubmit\"` → `\"submit\"`。\n *\n * @param t - 原始触发类型\n *\n * @returns 归一化后的触发类型\n */\nfunction normalizeTrigger(t: ValidationTrigger): NormalizedTrigger {\n const map: Record<ValidationTrigger, NormalizedTrigger> = {\n onBlur: \"blur\",\n onChange: \"change\",\n onSubmit: \"submit\",\n blur: \"blur\",\n change: \"change\",\n submit: \"submit\",\n }\n\n return map[t] ?? \"submit\"\n}\n\n/**\n * 判断当前事件是否应该触发校验。\n *\n * 将配置的触发时机归一化后,与当前事件类型进行匹配。\n *\n * @param event - 当前触发的事件类型\n * @param trigger - 配置的触发时机(支持单个或数组)\n *\n * @returns 是否应该触发校验\n *\n * @example\n * shouldValidateOn(\"change\", \"onChange\") // => true\n * shouldValidateOn(\"blur\", [\"onBlur\", \"onChange\"]) // => true\n * shouldValidateOn(\"change\", \"onSubmit\") // => false\n * shouldValidateOn(\"change\", undefined) // => false\n */\nexport function shouldValidateOn(\n event: NormalizedTrigger,\n trigger?: TriggerConfig\n): boolean {\n if (!trigger) return false\n const triggers = Array.isArray(trigger) ? trigger : [trigger]\n\n return triggers.some((t) => normalizeTrigger(t) === event)\n}\n","/**\n * 插槽解析与子插槽提取工具。\n *\n * 提供 camelCase / kebab-case 双向查找插槽,\n * 以及按字段名前缀提取子渲染器插槽。\n *\n * @module utils/slot\n */\n\n/**\n * 检查字符串是否包含大写字母(驼峰格式特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含大写字母\n */\nconst isCamelCase = (str: string): boolean => /[A-Z]/.test(str)\n\n/**\n * 检查字符串是否包含连字符(kebab-case 特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含连字符\n */\nconst isKebabCase = (str: string): boolean => str.includes(\"-\")\n\n/**\n * 将驼峰命名转换为连字符命名(kebab-case)。\n *\n * @param str - 驼峰格式的字符串\n * @returns 转换后的 kebab-case 字符串\n */\nconst camelToKebab = (str: string): string => {\n return str.replace(/([A-Z])/g, \"-$1\").toLowerCase()\n}\n\n/**\n * 将连字符命名转换为驼峰命名(camelCase)。\n *\n * @param str - kebab-case 格式的字符串\n * @returns 转换后的 camelCase 字符串\n */\nconst kebabToCamel = (str: string): string => {\n return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())\n}\n\n/**\n * 将命名格式归一化为 kebab-case。\n *\n * @param str - 任意命名格式的字符串\n * @returns kebab-case 格式的字符串\n */\nconst normalizeToKebab = (str: string): string => {\n if (isCamelCase(str)) return camelToKebab(str)\n\n return str\n}\n\n/**\n * 将命名格式归一化为 camelCase。\n *\n * @param str - 任意命名格式的字符串\n * @returns camelCase 格式的字符串\n */\nconst normalizeToCamel = (str: string): string => {\n if (isKebabCase(str)) return kebabToCamel(str)\n\n return str\n}\n\n/**\n * 从插槽对象中按名称查找插槽,同时支持 camelCase 和 kebab-case。\n *\n * 优先查找原始名称,未找到时尝试另一种命名格式。\n * 例如传入 `userName` 时,先查 `userName`,再查 `user-name`;\n * 传入 `user-name` 时,先查 `user-name`,再查 `userName`。\n *\n * @param slots - Vue 插槽对象\n * @param name - 插槽名称(camelCase 或 kebab-case)\n *\n * @returns 匹配的插槽函数,未找到返回 undefined\n *\n * @example\n * ```typescript\n * // slots 中定义了 'user-name' 插槽\n * resolveSlot(slots, 'userName') // => 找到 'user-name' 插槽\n * resolveSlot(slots, 'user-name') // => 找到 'user-name' 插槽\n * ```\n */\nexport const resolveSlot = (\n slots: Record<string, any>,\n name: string\n): ((...args: any[]) => any) | undefined => {\n if (slots[name]) return slots[name]\n\n const alt = isCamelCase(name)\n ? camelToKebab(name)\n : isKebabCase(name)\n ? kebabToCamel(name)\n : undefined\n\n if (alt && slots[alt]) return slots[alt]\n\n return undefined\n}\n\n/**\n * 从父级插槽中提取子渲染器插槽。\n *\n * 按 `fieldName:slotName` 和 `fieldName:slot-name` 格式匹配,\n * 提取后去掉前缀作为子渲染器的插槽名。\n * 同时支持 camelCase 和 kebab-case 的 fieldName。\n *\n * @param fieldName - 字段名\n * @param allSlots - 父级全部插槽\n *\n * @returns 子渲染器的插槽对象\n *\n * @example\n * ```typescript\n * // 父级插槽: { 'userName:prefix': fn, 'userName:suffix': fn, 'userNameLabel': fn }\n * extractChildSlots('userName', slots)\n * // => { prefix: fn, suffix: fn }\n * ```\n */\nexport const extractChildSlots = (\n fieldName: string,\n allSlots: Record<string, any>\n): Record<string, any> => {\n const result: Record<string, any> = {}\n\n const camelPrefix = normalizeToCamel(String(fieldName)) + \":\"\n\n const kebabPrefix = normalizeToKebab(String(fieldName)) + \":\"\n\n for (const [key, value] of Object.entries(allSlots)) {\n if (key.startsWith(camelPrefix)) {\n result[key.slice(camelPrefix.length)] = value\n } else if (key.startsWith(kebabPrefix) && kebabPrefix !== camelPrefix) {\n result[key.slice(kebabPrefix.length)] = value\n }\n }\n\n return result\n}\n","/**\n * FormGroup - 分组字段组件\n *\n * 用于渲染包含 children 的 Group ViewSchema。\n * 支持可折叠的分组容器,内部渲染子 columns。\n *\n * @module components/FormGroup\n */\n\nimport { computed, defineComponent, getCurrentInstance, PropType, ref, watch } from \"vue\"\nimport type { SetupContext, VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport FormItem from \"../FormItem\"\n\nimport type { SchemxViewGroupSchema, SchemxViewSchema, Values } from \"@schemx/core\"\n\n/**\n * FormGroup Props\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface SchemxGroupProps<TValues extends Values = Values> {\n schema: SchemxViewGroupSchema<TValues>\n}\n\nconst FormGroup = defineComponent(\n <TValues extends Values = Values>(\n props: SchemxGroupProps<TValues>,\n { slots }: SetupContext\n ) => {\n const internalCollapsed = ref(Boolean(props.schema.defaultCollapsed))\n\n const collapsed = computed(() => props.schema.collapsed ?? internalCollapsed.value)\n\n const componentId = getCurrentInstance()?.uid ?? 0\n\n watch(\n () => props.schema.collapsed,\n (nextCollapsed, previousCollapsed) => {\n if (nextCollapsed !== undefined) {\n internalCollapsed.value = nextCollapsed\n } else if (previousCollapsed !== undefined) {\n internalCollapsed.value = previousCollapsed\n }\n }\n )\n\n const toggle = () => {\n if (!props.schema.collapsible || props.schema.disabled) {\n return\n }\n\n const nextCollapsed = !collapsed.value\n\n if (props.schema.collapsed === undefined) {\n internalCollapsed.value = nextCollapsed\n }\n\n props.schema.onCollapsedChange?.(nextCollapsed)\n }\n\n return (): VNodeChild => {\n const schema = props.schema\n\n if (schema.visible === false) {\n return null\n }\n\n const collapsible = Boolean(schema.collapsible)\n\n const isCollapsed = collapsed.value\n\n const destroyOnCollapse = schema.destroyOnCollapse ?? true\n\n const uniqueId = schema.debug?.runtimeNodeId ?? `local-${componentId}`\n\n const idBase = `schemx-group-${uniqueId}-${normalizeId(schema.key)}`\n\n const headerId = `${idBase}-header`\n\n const bodyId = `${idBase}-body`\n\n const body = (\n <div\n id={bodyId}\n role=\"group\"\n aria-labelledby={schema.label ? headerId : undefined}\n aria-hidden={isCollapsed || undefined}\n class=\"schemx-group__body\"\n style={!destroyOnCollapse && isCollapsed ? { display: \"none\" } : undefined}\n >\n {schema.children.map((child) => (\n <FormItem\n key={child.key}\n schema={child as SchemxViewSchema}\n v-slots={slots}\n />\n ))}\n </div>\n )\n\n return (\n <div\n class={classnames(\n \"schemx-group\",\n {\n \"schemx-group--collapsed\": isCollapsed,\n \"is-readonly\": schema.readonly,\n \"is-disabled\": schema.disabled,\n },\n schema.class as string | undefined\n )}\n style={schema.style}\n data-key={schema.key}\n aria-disabled={schema.disabled || undefined}\n >\n {schema.label && (\n <div\n id={headerId}\n role={collapsible ? \"button\" : undefined}\n tabindex={collapsible ? (schema.disabled ? -1 : 0) : undefined}\n aria-expanded={collapsible ? !isCollapsed : undefined}\n aria-controls={collapsible ? bodyId : undefined}\n aria-disabled={collapsible ? schema.disabled || undefined : undefined}\n class={classnames(\"schemx-group__header\", {\n \"schemx-group__header--clickable\": collapsible && !schema.disabled,\n })}\n onClick={toggle}\n onKeydown={(e: KeyboardEvent) => {\n if (!schema.disabled && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault()\n toggle()\n }\n }}\n >\n <span class=\"schemx-group__title\">{schema.label}</span>\n {collapsible && (\n <span\n class={classnames(\"schemx-group__arrow\", {\n \"schemx-group__arrow--down\": !isCollapsed,\n })}\n />\n )}\n </div>\n )}\n {destroyOnCollapse ? !isCollapsed && body : body}\n </div>\n )\n }\n },\n {\n name: \"SchemxGroup\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewGroupSchema>,\n required: true,\n },\n },\n }\n)\n\nexport default FormGroup\n\nconst normalizeId = (key: string): string => {\n return String(key).replace(/[^a-zA-Z0-9_-]/g, \"-\")\n}\n","/**\n * FormItem 插槽渲染器。\n *\n * 集中处理字段各区域的插槽解析,保持 FormItem 仅负责字段状态和布局。\n *\n * @module components/FormItem/slot\n */\n\nimport { h } from \"vue\"\nimport type { ShallowRef, Slots, VNodeChild } from \"vue\"\n\nimport { FormContextProps } from \"../../hooks/provideFormConfigContext\"\nimport { extractChildSlots, resolveSlot } from \"../../utils\"\n\nimport type { FieldInstance } from \"../../types/field\"\nimport type {\n SchemxComponentProps,\n SchemxInstance,\n SchemxViewFieldSchema,\n Values,\n} from \"@schemx/core\"\n\n/** FormItem 插槽渲染所需的状态与依赖。 */\ninterface FormItemSlotRendererOptions<TValues extends Values = Values> {\n /** 当前字段 ViewSchema。 */\n schemaRef: Readonly<ShallowRef<SchemxViewFieldSchema<TValues>>>\n /** 当前字段的响应式控制器。 */\n field: FieldInstance<TValues>\n /** 当前表单实例,用于解析 Renderer。 */\n form: SchemxInstance<TValues>\n /** 表单级展示配置。 */\n formContext: FormContextProps\n /** 传给 Renderer 的稳定属性。 */\n componentProps: Readonly<ShallowRef<SchemxComponentProps<TValues>>>\n /** 父组件传入的所有具名插槽。 */\n slots: Slots\n}\n\n/**\n * 创建 FormItem 各区域的插槽渲染函数。\n *\n * `Before`、`Content`、`After` 依次包裹 Renderer;`Error` 位于控件之后。\n */\nexport function createFormItemSlotRenderers<TValues extends Values = Values>(\n options: FormItemSlotRendererOptions<TValues>\n) {\n const { schemaRef, field, form, formContext, componentProps, slots } = options\n\n /**\n * 渲染 required 星号。\n *\n * `showRequiredMark` 未设置时回退到 `required`;该展示开关不参与校验逻辑。\n * 禁用或只读字段始终不显示星号。\n *\n * @returns 星号 VNode 或空片段\n */\n const renderRequired = (): VNodeChild => {\n const showRequiredMark =\n schemaRef.value.showRequiredMark ?? Boolean(schemaRef.value.required)\n\n if (!showRequiredMark || schemaRef.value.disabled || schemaRef.value.readonly) {\n return null\n }\n\n return <span class=\"schemx-item__required\">*</span>\n }\n\n /** 构造各字段插槽共用的上下文参数。 */\n const createSlotProps = (additionalProps: Record<string, unknown> = {}) => {\n return {\n ...componentProps.value,\n value: field.value.value,\n ...additionalProps,\n }\n }\n\n /** 渲染指定后缀的字段区域插槽。 */\n const renderFieldSlot = (suffix: string): VNodeChild => {\n const slot = resolveSlot(slots, `${schemaRef.value.name}${suffix}`)\n\n return slot?.(createSlotProps()) ?? null\n }\n\n /** 渲染标签区域,优先使用 `{name}Label` 插槽。 */\n const renderLabel = (): VNodeChild => {\n const labelSlot = resolveSlot(slots, `${schemaRef.value.name}Label`)\n\n if (labelSlot) {\n return labelSlot(schemaRef.value)\n }\n\n const labelAlign = schemaRef.value.labelAlign || formContext.schemaConfig.labelAlign\n\n const labelWidth = schemaRef.value.labelWidth || formContext.schemaConfig.labelWidth\n\n const colon = schemaRef.value.colon ?? formContext.schemaConfig.colon\n\n return (\n <label\n class=\"schemx-item__label\"\n style={{ width: labelWidth, textAlign: labelAlign }}\n >\n {renderRequired()}\n <span class=\"schemx-item__label-text\">\n {schemaRef.value.label}\n {colon ? \":\" : \"\"}\n </span>\n </label>\n )\n }\n\n /** 渲染 Renderer 前的 `{name}Before` 插槽。 */\n const renderBefore = (): VNodeChild => renderFieldSlot(\"Before\")\n\n /** 渲染控件区域,优先使用 `{name}Content` 插槽。 */\n const renderContent = (): VNodeChild => {\n const component = form.getRenderer(schemaRef.value.componentType)\n\n if (!component) {\n throw new Error(\n `[schemx] Can not find component renderer of \"${schemaRef.value.componentType}\".`\n )\n }\n\n const childSlots = extractChildSlots(normalizeNameKey(schemaRef.value.name), slots)\n\n const columnElement = h(component, componentProps.value, childSlots)\n\n const contentSlot = resolveSlot(slots, `${schemaRef.value.name}Content`)\n\n if (contentSlot) {\n return contentSlot(\n createSlotProps({\n columnElement,\n })\n )\n }\n\n return <div class=\"schemx-item__control\">{columnElement}</div>\n }\n\n /** 渲染 Renderer 后的 `{name}After` 插槽。 */\n const renderAfter = (): VNodeChild => renderFieldSlot(\"After\")\n\n /** 渲染错误区域,优先使用 `{name}Error` 插槽。 */\n const renderError = (): VNodeChild => {\n const errorSlot = resolveSlot(slots, `${schemaRef.value.name}Error`)\n\n if (errorSlot) {\n return errorSlot(\n createSlotProps({\n errors: field.errors.value,\n })\n )\n }\n\n if (field.errors.value.length === 0) {\n return null\n }\n\n return <div class=\"schemx-item__error\">{field.errors.value[0]}</div>\n }\n\n return {\n createSlotProps,\n renderAfter,\n renderBefore,\n renderContent,\n renderError,\n renderLabel,\n }\n}\n\n/** 将 NamePath 转换为整体字段与 Renderer 子插槽使用的键。 */\nexport function normalizeNameKey(name: unknown): string {\n if (Array.isArray(name)) {\n return name.map((part) => String(part)).join(\".\")\n }\n\n return String(name)\n}\n","/**\n * FormItem\n *\n * schemx 和 FormGroup 实际渲染字段的组件。\n * 动态属性已由 core 解析到 ViewSchema 中;这里只负责消费\n * 已解析 schema、创建字段实例、组装渲染器属性与插槽。\n *\n * @module components/FormItem\n */\n\n/* eslint-disable vue/one-component-per-file */\nimport { computed, defineComponent, h, PropType } from \"vue\"\nimport type { VNodeChild } from \"vue\"\n\nimport { isSchemxViewFieldSchema, isViewGroupSchema } from \"@schemx/core\"\nimport classnames from \"classnames\"\n\nimport type { TriggerConfig } from \"@/utils\"\n\nimport {\n createFieldContext,\n useField,\n useFormConfigContext,\n useFormContext,\n useStableRef,\n} from \"../../hooks\"\nimport { useViewSchema } from \"../../hooks/useViewSchemas\"\nimport { mergeTrigger, resolveSlot, shouldValidateOn } from \"../../utils\"\nimport FormGroup from \"../FormGroup\"\n\nimport { createFormItemSlotRenderers, normalizeNameKey } from \"./slot\"\n\nimport type {\n FieldValue,\n NamePath,\n SchemxComponentProps,\n SchemxViewFieldSchema,\n SchemxViewSchema,\n Values,\n} from \"@schemx/core\"\n\n/**\n * FormItem 属性。\n *\n * 提供待渲染的原始 ViewSchema;具体字段或分组类型由组件内部运行时收窄。\n */\nexport interface SchemxItemProps {\n schema: unknown\n}\n\nconst FormItem = defineComponent({\n name: \"SchemxItem\",\n\n props: {\n schema: {\n type: Object as PropType<unknown>,\n required: true,\n },\n },\n\n /**\n * 根据 schema 类型分发字段组或字段项渲染。\n *\n * @param props - 当前组件的 schema 属性。\n * @param slots - Vue setup 上下文提供的插槽集合。\n */\n setup(props, { slots }) {\n return (): VNodeChild => {\n const schema = props.schema as SchemxViewSchema<Values>\n\n if (isViewGroupSchema(schema)) {\n return h(FormGroup, { schema }, slots)\n }\n\n return h(FieldFormItem, { schema }, slots)\n }\n },\n})\n\nconst FieldFormItem = defineComponent({\n name: \"SchemxFieldItem\",\n inheritAttrs: false,\n\n props: {\n schema: {\n type: Object as PropType<unknown>,\n required: true,\n },\n },\n\n /**\n * 初始化字段上下文,并组合响应式 schema、校验处理器与插槽渲染器。\n *\n * @param props - 当前字段项的 schema 属性。\n * @param attrs - Vue setup 上下文提供的透传属性。\n * @param slots - Vue setup 上下文提供的插槽集合。\n */\n setup(props, { attrs, slots }) {\n const form = useFormContext<Values>()\n\n const inputSchema = computed<SchemxViewFieldSchema<Values>>(\n () => props.schema as SchemxViewFieldSchema<Values>\n )\n\n // 按 key 复用表单级 ViewSchema 订阅,避免每个 FormItem 建立独立 Core effect。\n const latestSchema = useViewSchema(form, () => inputSchema.value.key)\n\n // 优先使用桥接中的最新字段;首帧或字段暂不存在时回退到输入 schema。\n const schemaRef = computed<SchemxViewFieldSchema<Values>>(() => {\n return latestSchema.value && isSchemxViewFieldSchema(latestSchema.value)\n ? latestSchema.value\n : inputSchema.value\n })\n\n const formContext = useFormConfigContext()\n\n const field = useField(schemaRef.value.name)\n\n createFieldContext(field)\n\n const trigger = computed<TriggerConfig>(() =>\n mergeTrigger(\n schemaRef.value.validationTrigger,\n formContext.schemaConfig.validationTrigger,\n \"onChange\"\n )\n )\n\n /**\n * 是否需要进行校验。\n *\n * 当字段不可见、只读或禁用时,无需进行校验。\n */\n const canVerified = computed(() => {\n const isOperate =\n schemaRef.value.visible && !schemaRef.value.readonly && !schemaRef.value.disabled\n\n const rules = schemaRef.value.rules\n\n const hasRules = Array.isArray(rules) ? rules?.length > 0 : !!schemaRef.value.rules\n\n return isOperate && (Boolean(schemaRef.value.required) || hasRules)\n })\n\n /**\n * 处理字段值变化,设置值后根据触发时机决定是否校验。\n *\n * @param v - 渲染器提交的最新字段值。\n */\n const handleChange = (v: FieldValue<Values, NamePath<Values>>) => {\n field.setValue(v)\n schemaRef.value.componentProps?.onChange?.(v)\n\n if (canVerified.value && shouldValidateOn(\"change\", trigger.value)) {\n field.validate()\n }\n }\n\n /**\n * 处理字段失焦,并根据触发时机决定是否校验。\n *\n * @param v - 触发失焦事件时对应的字段值。\n */\n const handleBlur = (v: FieldValue<Values, NamePath<Values>>) => {\n schemaRef.value.componentProps?.onBlur?.(v)\n\n if (canVerified.value && shouldValidateOn(\"blur\", trigger.value)) {\n field.validate()\n }\n }\n\n /**\n * 处理组件的 v-model 更新,只同步字段值,不主动触发校验。\n *\n * @param v - 渲染器提交的最新字段值。\n */\n const handleValueUpdate = (v: FieldValue<Values, NamePath<Values>>) => {\n field.setValue(v)\n }\n\n // 使用 useStableRef 避免每次生成新对象引用\n const componentProps = useStableRef<SchemxComponentProps<Values>>(\n (): SchemxComponentProps<Values> => {\n const currentComponentProps = schemaRef.value.componentProps ?? {}\n\n return {\n ...currentComponentProps,\n value: field.value.value,\n onChange: handleChange,\n onBlur: handleBlur,\n \"onUpdate:value\": handleValueUpdate,\n }\n }\n )\n\n const {\n createSlotProps,\n renderAfter,\n renderBefore,\n renderContent,\n renderError,\n renderLabel,\n } = createFormItemSlotRenderers({\n schemaRef,\n field,\n form,\n formContext,\n componentProps,\n slots,\n })\n\n return (): VNodeChild => {\n if (!schemaRef.value.visible) {\n return null\n }\n\n // 整体插槽:完全接管渲染,不包裹任何默认结构\n const itemSlot = resolveSlot(slots, normalizeNameKey(schemaRef.value.name))\n\n if (itemSlot) {\n return itemSlot(createSlotProps())\n }\n\n const labelPosition =\n schemaRef.value.labelPosition || formContext.schemaConfig.labelPosition\n\n return (\n <div\n {...attrs}\n class={classnames(\"schemx-item-wrapper\", attrs.class)}\n style={[attrs.style, schemaRef.value.style]}\n >\n <div\n class={classnames(\n \"schemx-item\",\n `schemx-item--label-${labelPosition}`,\n schemaRef.value.class,\n {\n \"is-readonly\": schemaRef.value.readonly,\n \"is-disabled\": schemaRef.value.disabled,\n }\n )}\n style={{ ...((schemaRef.value.style ?? {}) as CSSStyleValue) }}\n >\n {renderLabel()}\n\n <div class=\"schemx-item__content\">\n {renderBefore()}\n {renderContent()}\n {renderAfter()}\n {renderError()}\n </div>\n </div>\n </div>\n )\n }\n },\n})\n\nexport default FormItem\n","<script setup lang=\"ts\">\n import { computed, useAttrs } from \"vue\"\n\n import type { SchemxButtonSize } from \"./types\"\n\n interface Props {\n loading?: boolean\n loadingText?: string\n disabled?: boolean\n size?: SchemxButtonSize\n }\n\n defineOptions({ name: \"SchemxButton\", inheritAttrs: false })\n\n const props = withDefaults(defineProps<Props>(), {\n loading: false,\n loadingText: undefined,\n disabled: false,\n size: \"medium\",\n })\n\n const attrs = useAttrs()\n\n const isDisabled = computed(() => props.disabled || props.loading)\n\n const buttonClass = computed(() => [\"schemx-button\", `schemx-button--${props.size}`])\n</script>\n\n<template>\n <button\n v-bind=\"attrs\"\n :class=\"buttonClass\"\n :aria-busy=\"props.loading || undefined\"\n :data-loading=\"props.loading || undefined\"\n :disabled=\"isDisabled\"\n >\n <span v-if=\"$slots.prefix\" class=\"schemx-button__prefix\">\n <slot name=\"prefix\" />\n </span>\n <svg\n v-if=\"props.loading\"\n class=\"schemx-button__loading\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2v4\" />\n <path d=\"m16.2 7.8 2.9-2.9\" />\n <path d=\"M18 12h4\" />\n <path d=\"m16.2 16.2 2.9 2.9\" />\n <path d=\"M12 18v4\" />\n <path d=\"m4.9 19.1 2.9-2.9\" />\n <path d=\"M2 12h4\" />\n <path d=\"m4.9 4.9 2.9 2.9\" />\n </svg>\n\n <template v-if=\"props.loading && props.loadingText\">\n {{ props.loadingText }}\n </template>\n <slot v-else />\n <span v-if=\"$slots.suffix\" class=\"schemx-button__suffix\">\n <slot name=\"suffix\" />\n </span>\n </button>\n</template>\n\n<style>\n .schemx-button {\n box-sizing: content-box;\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--schemx-padding-xs);\n min-width: 72px;\n height: 24px;\n padding: var(--schemx-padding-xs) var(--schemx-padding-md);\n border: 1px solid var(--schemx-border-color);\n border-radius: var(--schemx-border-radius-sm);\n background: var(--schemx-background-color);\n color: var(--schemx-text-color);\n cursor: pointer;\n transition:\n border-color 0.15s ease,\n background-color 0.15s ease,\n color 0.15s ease,\n transform 0.1s ease;\n }\n\n .schemx-button:not(:disabled):hover {\n opacity: 0.8;\n }\n\n .schemx-button:not(:disabled):active {\n transform: scale(0.98);\n }\n\n .schemx-button--small {\n min-width: 56px;\n height: 20px;\n padding: var(--schemx-padding-base) var(--schemx-padding-sm);\n font-size: var(--schemx-font-size-sm);\n }\n\n .schemx-button--large {\n min-width: 88px;\n height: 28px;\n padding: var(--schemx-padding-sm) var(--schemx-padding-lg);\n font-size: var(--schemx-font-size-lg);\n }\n\n .schemx-button__loading {\n display: inline-block;\n border-radius: 50%;\n vertical-align: -0.15em;\n animation: schemx-button-loading 0.8s linear infinite;\n }\n\n .schemx-actions-button--submit {\n border-color: var(--schemx-primary-color);\n background: var(--schemx-primary-color);\n color: #ffffff;\n }\n\n .schemx-actions-button--submit:not(:disabled):hover {\n filter: brightness(0.92);\n color: #ffffff;\n }\n\n .schemx-button:disabled {\n cursor: not-allowed;\n opacity: var(--schemx-disabled-opacity);\n }\n\n @keyframes schemx-button-loading {\n to {\n transform: rotate(360deg);\n }\n }\n</style>\n","import { isViewGroupSchema, SchemxViewSchema } from \"@schemx/core\"\n\n/**\n * 判断当前项是否为顶层可见普通字段所在区段的第一个或最后一个。\n *\n * 可见 Group 是区段边界;不可见字段和不可见 Group 不参与计算。\n *\n * 使用示例:\n *\n * const list = [\n * { key: 'group-1', children: [] },\n * { key: 'a' },\n * { key: 'b' },\n * { key: 'group-2', children: [] },\n * { key: 'c' },\n * ]\n *\n * getSectionPosition(list, 'a')\n * // => { found: true, isFirst: true, isLast: false }\n */\nexport function getSectionPosition<TItem extends SchemxViewSchema>(\n list: TItem[],\n currentKey: string\n) {\n const currentIndex = list.findIndex((item) => item.key === currentKey)\n\n if (currentIndex === -1) {\n return {\n found: false,\n isFirst: false,\n isLast: false,\n }\n }\n\n const currentItem = list[currentIndex]\n\n if (!isPositionItem(currentItem)) {\n return {\n found: true,\n isFirst: false,\n isLast: false,\n }\n }\n\n return {\n found: true,\n isFirst: !hasPositionItemInSection(list, currentIndex, -1),\n isLast: !hasPositionItemInSection(list, currentIndex, 1),\n }\n}\n\nfunction isPositionItem<TItem extends SchemxViewSchema>(item?: TItem) {\n return !!item && !isViewGroupSchema(item) && item.visible !== false\n}\n\nfunction hasPositionItemInSection<TItem extends SchemxViewSchema>(\n list: TItem[],\n startIndex: number,\n step: 1 | -1\n): boolean {\n for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {\n const item = list[index]\n\n if (item.visible === false) {\n continue\n }\n\n if (isViewGroupSchema(item)) {\n return false\n }\n\n if (isPositionItem(item)) {\n return true\n }\n }\n\n return false\n}\n","<!--\n schemx - 主组件\n\n 基于 Schema 配置的表单组件,使用 useForm Hook 管理状态。\n\n @module schemx\n-->\n\n<script lang=\"ts\" setup generic=\"TValues extends Values = Values\">\n import { computed, reactive, useSlots, watch } from \"vue\"\n\n import { defaultSchemxConfigKeys, isSchemxSchemas } from \"@schemx/core\"\n import { pick } from \"es-toolkit\"\n\n import FormItem from \"./components/FormItem\"\n import Button from \"./components/Button\"\n import {\n createFormConfigContext,\n createFormContext,\n useForm,\n useFormSelector,\n useViewSchemas,\n } from \"./hooks\"\n import { getSectionPosition } from \"./utils/helpers\"\n\n import type { SchemxFormActionConfig, SchemxFormProps } from \"./types/index\"\n import type {\n SchemxInstance,\n SchemxSchemaConfig,\n SchemxViewSchema,\n Values,\n } from \"@schemx/core\"\n\n import \"./styles/index.css\"\n\n defineOptions({ name: \"SchemxForm\" })\n\n /**\n * 规范化表单组件的公开输入,并为未提供的可选项补充默认值。\n *\n * 具体字段语义由 `SchemxFormProps<TValues>` 定义;其中 `modelValue`、\n * `schemas` 和 `form` 共同决定表单数据与实例来源。\n */\n const props = withDefaults(defineProps<SchemxFormProps<TValues>>(), {\n modelValue: () => ({}) as TValues,\n initialValues: () => ({}) as TValues,\n form: undefined,\n schemas: () => [],\n class: \"\",\n style: () => ({}),\n onFinish: undefined,\n onFinishFailed: undefined,\n onReset: undefined,\n onLoadingChange: undefined,\n onValuesChange: undefined,\n onFieldsChange: undefined,\n loading: undefined,\n submitter: true,\n resetter: true,\n rendererProps: undefined,\n rendererRegistry: undefined,\n validationRuleRegistry: undefined,\n visible: true,\n })\n\n /**\n * 定义表单 v-model 的同步事件。\n *\n * 仅在内部表单值变化且不是外部 `modelValue` 同步窗口时发出更新。\n */\n const emit = defineEmits<{\n \"update:modelValue\": [value: TValues]\n }>()\n\n /**\n * 读取组件保留操作区插槽,并据此决定是否渲染默认操作区。\n */\n const slots = useSlots()\n\n /**\n * 提取需要同步到 Core 的表单级 schema 配置。\n */\n const pickSchemaConfig = (): Partial<SchemxSchemaConfig> => {\n // Vue 保留 `required` 对当前 TValues 的泛型约束;Core 的表单级配置使用\n // unknown 表示任意字段值。运行时该回调只会接收当前 Form 的字段值,因此在\n // Vue 到 Core 的适配边界收窄为 Core 配置类型。\n return pick(props, defaultSchemxConfigKeys) as Partial<SchemxSchemaConfig>\n }\n\n /**\n * 保存供后代组件读取的响应式表单级 schema 配置。\n */\n const formSchemaConfig = reactive<Partial<SchemxSchemaConfig>>(pickSchemaConfig())\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createFormConfigContext({ schemaConfig: formSchemaConfig })\n\n /**\n * 获取或创建表单实例\n *\n * 优先使用外部传入的 form,否则内部通过 useForm 创建。\n * 必须在 setup 同步阶段调用,确保 provide 正确注入。\n */\n const providedForm = props.form\n ? props.form\n : useForm<TValues>({\n schemas: props.schemas,\n schemaConfig: pickSchemaConfig(),\n initialValues:\n Object.keys(props.modelValue).length > 0\n ? props.modelValue\n : props.initialValues,\n\n rendererProps: props.rendererProps,\n rendererRegistry: props.rendererRegistry,\n defaultRendererType: props.defaultRendererType,\n validationRuleRegistry: props.validationRuleRegistry,\n validatorAdapters: props.validatorAdapters,\n\n /**\n * 转发提交成功回调。\n *\n * @param values - 提交成功时的完整表单快照。\n */\n onFinish: (values) => props.onFinish?.(values),\n /**\n * 转发提交失败回调。\n *\n * @param errors - 提交失败时的字段错误集合。\n */\n onFinishFailed: (errors) => props.onFinishFailed?.(errors),\n /**\n * 转发完整表单重置回调。\n */\n onReset: () => {\n props.onReset?.()\n },\n /**\n * 转发 Core 发出的提交 loading 状态。\n *\n * 外部传入 Form 时不会注入该回调,保持实例的创建期边界。\n *\n * @param loading - 当前是否处于提交流程中。\n */\n onLoadingChange: (loading) => {\n props.onLoadingChange?.(loading)\n },\n /**\n * 转发值变化回调。\n *\n * @param changedValues - 本次变更涉及的字段值。\n * @param latestSnapshot - 变更后的完整表单快照。\n */\n onValuesChange: (changedValues, latestSnapshot) => {\n props.onValuesChange?.(changedValues, latestSnapshot)\n },\n /**\n * 转发字段变化回调。\n *\n * @param changedPaths - 本次发生变化的字段路径。\n * @param allPaths - 当前已变更字段路径集合。\n */\n onFieldsChange: (changedPaths, allPaths) => {\n props.onFieldsChange?.(changedPaths, allPaths)\n },\n })\n\n const isExternalForm = props.form !== undefined\n\n /**\n * 注册表单上下文。\n *\n * 无论实例来自 props.form 还是 useForm,都必须同步注册,\n * 从而保证 FormItem、useField 等后代逻辑能够获取同一个实例。\n */\n const formInstance = createFormContext(providedForm)\n\n /**\n * 组件未受控时直接跟随 Core 提交状态;受控值仅覆盖操作区展示。\n */\n const effectiveLoading = computed(() => props.loading ?? formInstance.isLoading())\n\n /**\n * 归一化启用状态下的操作配置。\n */\n const normalizeActionConfig = (\n action: SchemxFormProps<TValues>[\"submitter\"]\n ): SchemxFormActionConfig => (action === true ? {} : action || {})\n\n /**\n * 提交操作的归一化配置。\n */\n const submitterConfig = computed(() => normalizeActionConfig(props.submitter))\n\n /**\n * 重置操作的归一化配置。\n */\n const resetterConfig = computed(() => normalizeActionConfig(props.resetter))\n\n /**\n * 操作区必须保护内部事件和 button type,不接受运行时透传的同名属性。\n */\n const getButtonProps = (config: SchemxFormActionConfig) => {\n const buttonProps = Object.fromEntries(\n Object.entries(config.buttonProps ?? {}).filter(\n ([name]) => name !== \"type\" && name !== \"onClick\"\n )\n )\n\n return buttonProps\n }\n\n /**\n * 提交按钮可安全透传的原生属性。\n */\n const submitterButtonProps = computed(() => getButtonProps(submitterConfig.value))\n\n /**\n * 重置按钮可安全透传的原生属性。\n */\n const resetterButtonProps = computed(() => getButtonProps(resetterConfig.value))\n\n /**\n * 仅配置或提供对应插槽时展示提交操作。\n */\n const isSubmitterVisible = computed(\n () => props.submitter !== false && (props.submitter !== undefined || slots.submitter)\n )\n\n /**\n * 仅配置或提供对应插槽时展示重置操作。\n */\n const isResetterVisible = computed(\n () => props.resetter !== false && (props.resetter !== undefined || slots.resetter)\n )\n\n /**\n * 操作区的整体显示状态。\n */\n const isActionsVisible = computed(\n () => isSubmitterVisible.value || isResetterVisible.value\n )\n\n /**\n * 操作区保留插槽不能下传给字段,避免与同名字段插槽冲突。\n */\n const fieldSlots = computed(() =>\n Object.fromEntries(\n Object.entries(slots).filter(\n ([slotName]) => slotName !== \"submitter\" && slotName !== \"resetter\"\n )\n )\n )\n\n /**\n * 提交按钮在 loading 或显式 disabled 时不可点击。\n */\n const isSubmitterDisabled = computed(\n () => effectiveLoading.value || Boolean(submitterButtonProps.value.disabled)\n )\n\n /**\n * 重置按钮在 loading 或显式 disabled 时不可点击。\n */\n const isResetterDisabled = computed(\n () => effectiveLoading.value || Boolean(resetterButtonProps.value.disabled)\n )\n\n let syncingFromModel = false\n\n /**\n * 将外部 modelValue 同步到 Core,并标记窗口以阻止 v-model 回写。\n *\n * @param values - 外部传入的最新表单值。\n */\n watch(\n () => props.modelValue,\n (values) => {\n syncingFromModel = true\n formInstance.setFieldsValue(values)\n syncingFromModel = false\n },\n { deep: true }\n )\n\n /**\n * 响应提交按钮点击,并返回 Core Promise 以让 Vue 处理异常。\n */\n const handleSubmit: SchemxInstance<TValues>[\"submit\"] = () => formInstance.submit()\n\n /**\n * 响应重置按钮点击。\n */\n const handleReset = (): void => {\n formInstance.reset()\n }\n\n /**\n * 通过共享 selector bridge 订阅完整快照,避免组件树重复注册 Core 监听。\n *\n * @param values - 当前表单的完整快照。\n */\n const formValues = useFormSelector(formInstance, (values) => values)\n\n /**\n * 将 Core 值同步回 v-model;外部 modelValue 写入期间跳过回写,避免循环。\n *\n * @param latestSnapshot - Core 发布的最新完整表单快照。\n */\n watch(\n formValues,\n (latestSnapshot) => {\n if (syncingFromModel) return\n\n emit(\"update:modelValue\", latestSnapshot)\n },\n { flush: \"sync\" }\n )\n\n /**\n * 将外部传入的原始 Schema 同步更新到 Core。\n *\n * @param schemas - 最新的 props.schemas 配置。\n */\n watch(\n () => props.schemas,\n (schemas) => {\n if (!isSchemxSchemas(schemas)) {\n formInstance.setSchemas(schemas)\n }\n },\n { deep: false, immediate: !!props.form }\n )\n\n const viewSchemas = useViewSchemas(formInstance)\n\n /**\n * 根据 ViewSchema 在当前列表中的位置生成首尾样式类。\n *\n * @param schema - 当前需要计算样式的 ViewSchema。\n * @returns 用于包裹元素的首尾位置 class 映射。\n */\n const getFormItemClass = (schema: SchemxViewSchema<TValues>) => {\n const { isFirst, isLast } = getSectionPosition(\n viewSchemas.value as SchemxViewSchema[],\n schema.key\n )\n\n return {\n \"schemx-item-wrapper--first\": isFirst,\n \"schemx-item-wrapper--last\": isLast,\n }\n }\n\n /**\n * 将公开配置快照写入本地上下文并同步到 Core。\n *\n * @param nextSchemaConfig - 最新的表单级 schema 配置。\n */\n watch(\n pickSchemaConfig,\n (nextSchemaConfig) => {\n Object.assign(formSchemaConfig, nextSchemaConfig)\n formInstance.updateSchemaConfig(nextSchemaConfig)\n },\n { deep: false, immediate: isExternalForm }\n )\n\n /**\n * 暴露当前表单 Facade 的完整控制 API,供模板 ref 调用。\n */\n defineExpose({\n ...formInstance,\n submit: handleSubmit,\n reset: handleReset,\n })\n</script>\n\n<template>\n <div :class=\"['schemx', props.class]\" :style=\"props.style\">\n <FormItem\n v-for=\"schema in viewSchemas\"\n :key=\"schema.key\"\n :schema=\"schema as SchemxViewSchema\"\n :class=\"getFormItemClass(schema as SchemxViewSchema<TValues>)\"\n >\n <template v-for=\"(_, slotName) in fieldSlots\" #[slotName]=\"slotProps\">\n <slot :name=\"slotName\" v-bind=\"slotProps ?? {}\" />\n </template>\n </FormItem>\n <div v-if=\"isActionsVisible\" class=\"schemx-actions\">\n <slot\n v-if=\"isResetterVisible && slots.resetter\"\n name=\"resetter\"\n :form=\"formInstance\"\n :loading=\"effectiveLoading\"\n :disabled=\"isResetterDisabled\"\n :reset=\"handleReset\"\n />\n <Button\n v-else-if=\"isResetterVisible\"\n v-bind=\"resetterButtonProps\"\n class=\"schemx-actions-button schemx-actions-button--reset\"\n type=\"button\"\n :disabled=\"isResetterDisabled\"\n @click=\"handleReset\"\n >\n {{ resetterConfig.text ?? \"重置\" }}\n </Button>\n <slot\n v-if=\"isSubmitterVisible && slots.submitter\"\n name=\"submitter\"\n :form=\"formInstance\"\n :loading=\"effectiveLoading\"\n :disabled=\"isSubmitterDisabled\"\n :submit=\"handleSubmit\"\n />\n <Button\n v-else-if=\"isSubmitterVisible\"\n v-bind=\"submitterButtonProps\"\n class=\"schemx-actions-button schemx-actions-button--submit\"\n type=\"button\"\n :loading=\"effectiveLoading\"\n :disabled=\"isSubmitterDisabled\"\n @click=\"handleSubmit\"\n >\n {{ submitterConfig.text ?? \"提交\" }}\n </Button>\n </div>\n </div>\n</template>\n","/**\n * schemx 组件导出\n *\n * 为 SchemxForm 组件挂载静态方法(install、FormItem、registerRequest 等),\n * 并作为默认导出。\n *\n * @module formExport\n */\n\nimport type { App } from \"vue\"\n\nimport FormItem from \"./components/FormItem\"\nimport { provideSchemxAppConfig } from \"./config\"\nimport SchemxForm from \"./formRuntime.js\"\n\nimport type { SchemxConfig } from \"@schemx/core\"\n\n/**\n * SchemxForm 插件安装选项\n *\n * 在 `app.use(SchemxForm, options)` 时传入,用于配置当前 Vue App 的默认行为。\n * 安装配置不会写入 Core 的模块级全局配置,因此多个 Vue App 可以彼此隔离。\n *\n * @example\n * ```ts\n * import SchemxForm from '@schemx/vue'\n *\n * app.use(SchemxForm, {\n * schemaConfig: { readonly: true },\n * })\n * ```\n */\nexport interface SchemxInstallOptions extends SchemxConfig {}\n\n/**\n * 为组件挂载静态属性并保留原始类型\n *\n * @param comp - 原始组件\n * @param extra - 要挂载的静态属性\n * @typeParam TComponent - 原始组件类型。\n * @typeParam TExtras - 待挂载的静态属性类型。\n */\nexport function withInstall<\n TComponent extends object,\n TExtras extends Record<string, unknown>,\n>(comp: TComponent, extra: TExtras) {\n return Object.assign(comp, extra) as TComponent & TExtras\n}\n\nexport type SchemxFormPlugin = typeof SchemxForm & {\n install: (app: App, options?: SchemxInstallOptions) => void\n FormItem: typeof FormItem\n}\n\nconst SchemxFormExport = withInstall(SchemxForm, {\n /** Vue 插件安装方法 */\n install(app: App, options: SchemxInstallOptions = {}) {\n provideSchemxAppConfig(app, options)\n app.component(\"SchemxForm\", SchemxForm)\n },\n /** FormItem 子组件引用 */\n FormItem,\n}) as unknown as SchemxFormPlugin\n\nexport default SchemxFormExport\n","/**\n * WithRemoteOptions - 选项高阶组件\n *\n * 包装任意渲染器组件,自动注入 useDictionary 的选项加载能力。\n * 被包装组件会额外收到 options、loading 两个 props,\n * 无需自行调用 useDictionary。\n *\n * @module hocs/withRemoteOptions\n */\n\nimport { Component, computed, defineComponent, h, PropType, SetupContext } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFieldContext } from \"../hooks/provideFieldContext\"\nimport { useDictionary } from \"../hooks/useDictionary\"\n\n\n/**\n * WithRemoteOptions 注入给被包装组件的额外 Props\n */\nexport interface RemoteOptionsInjectedProps {\n /** 加载的选项列表 */\n options: unknown[]\n /** 加载状态 */\n loading: boolean\n}\n\ntype SchemxDictionaryInput = SchemxDictionary | SchemxDictionary[\"api\"]\n\n/**\n * 将 api 简写规范化为完整的字典配置。\n */\nfunction normalizeDictionary(\n dictionary: SchemxDictionaryInput | undefined\n): SchemxDictionary | undefined {\n if (typeof dictionary === \"function\") {\n return { api: dictionary }\n }\n\n return dictionary\n}\n\n/**\n * 选项高阶组件\n *\n * 包装一个渲染器组件,当 dict prop 存在时,\n * 调用 useDictionary 加载选项,并将结果作为 options prop 注入。\n *\n * 当组件自身已传入非空 options 且无 dict 时,优先使用静态 options。\n *\n * @param WrappedComponent - 被包装的渲染器组件\n * @returns 增强后的组件,自动具备选项加载能力\n *\n * @example\n * ```ts\n * import { WithRemoteOptions } from '@schemx/vue'\n * import MySelect from './MySelect.vue'\n *\n * const RemoteSelect = WithRemoteOptions(MySelect)\n *\n * // 在 schema 中使用\n * const schema = {\n * name: 'city',\n * label: '城市',\n * componentType: 'select',\n * componentProps: {\n * dict: {\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * },\n * },\n * }\n * ```\n */\nexport function WithRemoteOptions(WrappedComponent: Component): Component {\n return defineComponent({\n name: `WithRemoteOptions(${WrappedComponent.name || \"Anonymous\"})`,\n inheritAttrs: false,\n props: {\n dict: {\n type: [Object, Function] as PropType<SchemxDictionaryInput>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }: SetupContext) {\n const dictionary = normalizeDictionary(props.dict)\n\n // 内嵌于 FormItem 时自动从字段 Context 获取目标字段;显式 fieldName\n // 保留给脱离 FormItem 的独立使用场景。\n const fieldName = dictionary\n ? (attrs.fieldName as string | undefined) ?? useFieldContext().name\n : undefined\n\n const dictResult = dictionary ? useDictionary(dictionary, fieldName) : null\n\n const childrenProps = computed(() => {\n return {\n ...attrs,\n dict: dictionary,\n options: dictionary ? dictResult?.list.value : attrs.options,\n loading: dictionary ? dictResult?.loading.value : attrs.loading,\n }\n })\n\n return () => h(WrappedComponent, childrenProps.value, slots)\n },\n })\n}\n\nexport default WithRemoteOptions\n"],"names":["SCHEMX_APP_CONFIG_KEY","Symbol","EMPTY_SCHEMX_CONFIG","Object","freeze","schemaConfig","validatorAdapters","normalizeRendererProps","source","entries","map","type","props","fromEntries","provideSchemxAppConfig","app","config","normalizedConfig","rendererProps","defaultRendererType","rendererRegistry","validationRuleRegistry","normalizeSchemxAppConfig","provide","formBridgeCache","WeakMap","formFacadeCache","facadeCoreFormCache","createVueShallowRef","value","shallowRef","getCoreForm","form","get","getVueFormBridge","coreForm","cachedBridge","bridge","externalStore","createFormExternalStore","values","getSnapshot","touchedFields","pendingFields","loading","fieldBridges","Map","syncValues","syncTouchedFields","syncPendingFields","syncLoading","unsubscribeValues","subscribe","unsubscribeTouchedFields","unsubscribePendingFields","unsubscribeLoading","unsubscribe","facade","getVueFormFacade","refCount","destroyed","createVueFormBridge","set","cachedFacade","getFieldValue","name","getVueFieldBridge","getFieldErrors","errors","isFieldTouched","touched","isFieldPending","pending","getFieldsValue","names","getTouchedFields","getPendingFields","isLoading","destroy","disposeVueFormBridge","createVueFormFacade","retainVueFormBridge","released","store","field","snapshot","nextSnapshot","previous","next","is","length","every","index","fieldBridge","dispose","clear","delete","createRendererRegistry","createValidationRuleRegistry","useForm","options","configuredOptions","mergeSchemxConfig","getUseFormSchemxConfig","getCurrentInstance","inject","mergedOptions","instance","createForm","releaseBridge","onScopeDispose","SCHEMX_FORM_INSTANCE_KEY","createFormContext","useFormContext","Error","useField","coreField","createField","computed","dirty","getValue","getErrors","isTouched","isPending","getValues","createFieldHook","SCHEMX_FORM_FIELD_KEY","createFieldContext","useFieldContext","useWatch","nameOrNamesOrCallback","callbackOrOptions","maybeOptions","createWatch","onUnmounted","useWatchFields","callback","normalizeError","err","String","useDictionary","fieldName","list","ref","error","requestCount","loadDict","async","formValues","shouldFetch","currentCount","res","maxRetries","retryCount","retryDelay","retryInterval","lastError","attempt","api","Promise","r","setTimeout","executeWithRetry","formatted","formatter","Array","isArray","format","onSuccess","normalized","onError","_a","dependsOn","latestSnapshot","_payload","onDepsChange","resetOnDepsChange","setFieldValue","immediate","onMounted","refresh","mutate","data","SCHEMX_FORM_CONFIG_KEY","createFormConfigContext","useFormConfigContext","context","useStableRef","factory","stableRef","watchEffect","a","b","keysA","keys","keysB","key","isShallowEqual","viewSchemaBridgeCache","useViewSchemas","acquireViewSchemaBridge","viewSchemas","createViewSchemaBridge","getViewSchemas","schemasByKey","appendSchemas","schemas","schema","isViewGroupSchema","children","createViewSchemaIndex","subscribeViewSchemas","nextSchemas","useFormSelector","selector","selected","equals","stop","watch","flush","readonly","isValidTrigger","v","shouldValidateOn","event","trigger","some","t","onBlur","onChange","onSubmit","blur","change","submit","normalizeTrigger","isCamelCase","str","test","isKebabCase","includes","camelToKebab","replace","toLowerCase","kebabToCamel","_match","letter","toUpperCase","resolveSlot","slots","alt","extractChildSlots","allSlots","result","camelPrefix","kebabPrefix","normalizeToKebab","startsWith","slice","FormGroup$1","defineComponent","internalCollapsed","Boolean","defaultCollapsed","collapsed","componentId","uid","nextCollapsed","previousCollapsed","toggle","collapsible","disabled","onCollapsedChange","visible","isCollapsed","destroyOnCollapse","idBase","debug","runtimeNodeId","normalizeId","headerId","bodyId","body","_createVNode","createVNode","id","role","label","undefined","class","style","display","FormItem","child","classnames","tabindex","onClick","onKeydown","e","preventDefault","required","normalizeNameKey","part","join","setup","h","FormGroup","FieldFormItem","inheritAttrs","attrs","inputSchema","latestSchema","getKey","useViewSchema","schemaRef","isSchemxViewFieldSchema","formContext","mergeTrigger","columnTrigger","validationTrigger","contextTrigger","defaultTrigger","canVerified","isOperate","rules","hasRules","handleChange","setValue","componentProps","validate","handleBlur","handleValueUpdate","createSlotProps","renderAfter","renderBefore","renderContent","renderError","renderLabel","additionalProps","renderFieldSlot","suffix","slot","component","getRenderer","componentType","childSlots","columnElement","contentSlot","errorSlot","labelSlot","labelAlign","labelWidth","colon","width","textAlign","showRequiredMark","_createTextVNode","createTextVNode","createFormItemSlotRenderers","itemSlot","labelPosition","_mergeProps","mergeProps","FormItem$1","__props","useAttrs","isDisabled","buttonClass","size","_createElementBlock","_unref","$slots","prefix","_openBlock","createElementBlock","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_cache","loadingText","_Fragment","_toDisplayString","_hoisted_4","isPositionItem","item","hasPositionItemInSection","startIndex","step","emit","__emit","useSlots","pickSchemaConfig","pick","defaultSchemxConfigKeys","formSchemaConfig","reactive","providedForm","initialValues","modelValue","onFinish","call","onFinishFailed","onReset","onLoadingChange","onValuesChange","changedValues","onFieldsChange","changedPaths","allPaths","isExternalForm","formInstance","effectiveLoading","normalizeActionConfig","action","submitterConfig","submitter","resetterConfig","resetter","getButtonProps","buttonProps","filter","submitterButtonProps","resetterButtonProps","isSubmitterVisible","isResetterVisible","isActionsVisible","fieldSlots","slotName","isSubmitterDisabled","isResetterDisabled","syncingFromModel","vue","setFieldsValue","deep","handleSubmit","handleReset","reset","isSchemxSchemas","setSchemas","getFormItemClass","isFirst","isLast","currentKey","currentIndex","findIndex","found","getSectionPosition","nextSchemaConfig","assign","updateSchemaConfig","__expose","_normalizeClass","_normalizeStyle","_renderList","renderList","_createBlock","unref","normalizeClass","_","fn","_withCtx","slotProps","openBlock","_hoisted_1","Button","toDisplayString","text","comp","extra","SchemxFormExport$1","SchemxForm","install","WrappedComponent","dict","Function","default","dictionary","normalizeDictionary","dictResult","childrenProps"],"mappings":"+OAcMA,EAAoDC,OAAO,qBAG3DC,EAAoCC,OAAOC,OAAO,CACtDC,aAAcF,OAAOC,OAAO,IAC5BE,kBAAmBH,OAAOC,OAAO,MAmCnC,SAASG,EACPC,GAEA,QAA0B,IAAtBA,EACK,OAIH,MAAAC,EAAUN,OAAOM,QAAQD,GAAQE,IAAI,EAAEC,EAAMC,KAC1C,CAACD,OAA4B,IAAtBC,OAAsB,EAAYT,OAAOC,OAAO,IAAKQ,MAGrE,OAAOT,OAAOC,OAAOD,OAAOU,YAAYJ,GAC1C,CAQO,SAASK,EAAuBC,EAAUC,EAAuB,IAEhE,MAAAC,EA9CR,SAAkCD,GAE1B,MAAAX,EAAeF,OAAOC,OAAO,IAAMY,EAAOX,cAAgB,CAAC,IAG3DC,EAAoBH,OAAOC,OAAO,IAAKY,EAAOV,mBAAqB,KAEzE,OAAOH,OAAOC,OAAO,CACnBC,eACAa,cAAeX,EAAuBS,EAAOE,eAC7CZ,oBACAa,oBAAqBH,EAAOG,oBAC5BC,iBAAkBJ,EAAOI,iBACzBC,uBAAwBL,EAAOK,wBAEnC,CA+B2BC,CAAyBN,GAE9CD,EAAAQ,QAAQvB,EAAuBiB,EACrC,CCqCA,MAAMO,MAAsBC,QAKtBC,MAAsBD,QAKtBE,MAA0BF,QAQhC,SAASG,EAA4BC,GAC5BC,OAAAA,EAAAA,WAAWD,EACpB,CAcO,SAASE,EACdC,GAIA,OAFiBL,EAAoBM,IAAID,IAErBA,CACtB,CAmBO,SAASE,EACdF,GAEM,MAAAG,EAAWJ,EAAYC,GACvBI,EAAeZ,EAAgBS,IAAIE,GAEzC,GAAIC,EACK,OAAAA,EAGH,MAAAC,EA+LR,SACEL,GAEM,MAAAM,EAAgBC,0BAAwBP,GACxCQ,EAASZ,EAA6BU,EAAcE,OAAOC,eAC3DC,EAAgBd,EACpBU,EAAcI,cAAcD,eAExBE,EAAgBf,EACpBU,EAAcK,cAAcF,eAExBG,EAAUhB,EAA6BU,EAAcM,QAAQH,eAC7DI,MAAmBC,IAEnBC,EAAa,KACVP,EAAAX,MAAQS,EAAcE,OAAOC,eAGhCO,EAAoB,KACVN,EAAAb,MAAQS,EAAcI,cAAcD,eAG9CQ,EAAoB,KACVN,EAAAd,MAAQS,EAAcK,cAAcF,eAM9CS,EAAc,KACVN,EAAAf,MAAQS,EAAcM,QAAQH,eAGlCU,EAAoBb,EAAcE,OAAOY,UAAUL,GACnDM,EACJf,EAAcI,cAAcU,UAAUJ,GAClCM,EACJhB,EAAcK,cAAcS,UAAUH,GAClCM,EAAqBjB,EAAcM,QAAQQ,UAAUF,GAErDM,EAAc,sBAOdC,EAASC,EAAiB1B,GAC1BK,EAAiC,CACrCL,OACAM,gBACAE,SACAE,gBACAC,gBACAC,UACAC,eACAc,SAAU,EACVC,WAAW,EACXJ,cACAC,UAGK,OAAApB,CACT,CA9PiBwB,CAAoB1B,GAI5B,OAFSX,EAAAsC,IAAI3B,EAAUE,GAEvBA,CACT,CAgBO,SAASqB,EACd1B,GAEM,MAAAG,EAAWJ,EAAYC,GACvB+B,EAAerC,EAAgBO,IAAIE,GAEzC,GAAI4B,EACK,OAAAA,EAGH,MAAAN,EAuOR,SACEzB,GAEA,IAAI4B,GAAY,EAOV,MAAAI,EAA2DC,IAC/D,IAAKL,EAAW,CAGTM,EAFUhC,EAAiBF,GAEDiC,GAAMpC,MAAMA,KAC7C,CAEO,OAAAG,EAAKgC,cAAcC,IAQtBE,EAA6DF,IACjE,IAAKL,EAAW,CAGTM,EAFUhC,EAAiBF,GAEDiC,GAAMG,OAAOvC,KAC9C,CAEO,OAAAG,EAAKmC,eAAeF,IAQvBI,EAA6DJ,IACjE,IAAKL,EAAW,CAGTM,EAFUhC,EAAiBF,GAEDiC,GAAMK,QAAQzC,KAC/C,CAEO,OAAAG,EAAKqC,eAAeJ,IAQvBM,EAA6DN,IACjE,IAAKL,EAAW,CAGTM,EAFUhC,EAAiBF,GAEDiC,GAAMO,QAAQ3C,KAC/C,CAEO,OAAAG,EAAKuC,eAAeN,IAQvBQ,EAAmBC,IACvB,IAAKd,EAAW,CACR,MAAAvB,EAASH,EAAiBF,GAEhC,QAAyB,IAArB0C,EACGrC,EAAOG,OAAOX,WAEnB,IAAA,MAAWoC,KAAQS,EACZR,EAAkB7B,EAAQ4B,GAAMpC,MAAMA,KAGjD,CAEO,OAAAG,EAAKyC,eAAeC,IAGvBC,EAAgE,KACpE,IAAKf,EAAW,CACC1B,EAAiBF,GAEpBU,cAAcb,KAC5B,CAEA,OAAOG,EAAK2C,oBAGRC,EAAgE,KACpE,IAAKhB,EAAW,CACC1B,EAAiBF,GAEpBW,cAAcd,KAC5B,CAEA,OAAOG,EAAK4C,oBAMRC,EAAkD,KACtD,IAAKjB,EAAW,CACC1B,EAAiBF,GAEpBY,QAAQf,KACtB,CAEA,OAAOG,EAAK6C,aAGRC,EAA8C,KAClD,GAAIlB,EACF,OAGUA,GAAA,EACN,MAAAvB,EAASb,EAAgBS,IAAID,GAE/BK,GACF0C,EAAqB1C,GAGvBL,EAAK8C,WAGA,MAAA,IACF9C,EACHgC,gBACAG,iBACAE,iBACAE,iBACAE,iBACAE,mBACAC,mBACAC,YACAC,UAEJ,CAzXiBE,CAAoB7C,GAK5B,OAHST,EAAAoC,IAAI3B,EAAUsB,GACV9B,EAAAmC,IAAIL,EAAkBtB,GAEnCsB,CACT,CAkBO,SAASwB,EACd5C,GAEA,GAAIA,EAAOuB,UACT,MAAO,OAGFvB,EAAAsB,WACP,IAAIuB,GAAW,EAEf,MAAO,KACDA,GAAY7C,EAAOuB,YAIZsB,GAAA,EACJ7C,EAAAsB,WAEiB,IAApBtB,EAAOsB,UACToB,EAAqB1C,IAG3B,CAgBgB,SAAA6B,EAGd7B,EAAgC4B,GAChC,MAAMkB,EAAQ9C,EAAOC,cAAc8C,MAAMnB,GACnC7B,EAAeC,EAAOQ,aAAaZ,IAAIkD,GAE7C,GAAI/C,EACK,OAAAA,EAGH,MAAAiD,EAAWF,EAAM1C,cACjBZ,EAAQD,EACZyD,EAASxD,OAELuC,EAASxC,EAAuCyD,EAASjB,QACzDE,EAAU1C,EAA6ByD,EAASf,SAChDE,EAAU5C,EAA6ByD,EAASb,SAsBhDhB,EAAc2B,EAAM/B,UApBF,KAChB,MAAAkC,EAAeH,EAAM1C,cAgT/B,IACE8C,EACAC,EAhTOrF,OAAOsF,GAAG5D,EAAMA,MAAOyD,EAAazD,SACvCA,EAAMA,MAAQyD,EAAazD,OA8S/B0D,EA3S2BnB,EAAOvC,MA4SlC2D,EA5SyCF,EAAalB,OA+SpDmB,EAASG,SAAWF,EAAKE,QACzBH,EAASI,MAAM,CAAC9D,EAAO+D,IAAU/D,IAAU2D,EAAKI,MA/S9CxB,EAAOvC,MAAQyD,EAAalB,QAG1BE,EAAQzC,QAAUyD,EAAahB,UACjCA,EAAQzC,MAAQyD,EAAahB,SAG3BE,EAAQ3C,QAAUyD,EAAad,UACjCA,EAAQ3C,MAAQyD,EAAad,WAS3BqB,EAA8C,CAClDV,QACAtD,QACAuC,SACAE,UACAE,UACAsB,QAVc,WAeT,OAFAzD,EAAAQ,aAAaiB,IAAIqB,EAAiBU,GAElCA,CACT,CAgBO,SAASd,EACd1C,GAEA,IAAIA,EAAOuB,UAAX,CAIAvB,EAAOuB,WAAY,EACnBvB,EAAOsB,SAAW,EAClBtB,EAAOmB,cAEP,IAAA,MAAWqC,KAAexD,EAAOQ,aAAaL,SAC5CqD,EAAYC,UAGdzD,EAAOQ,aAAakD,QACpB1D,EAAOC,cAAcwD,UACLtE,EAAAwE,OAAO3D,EAAOL,KAZ9B,CAaF,CCnVa,MAAAZ,EACX6E,yBAA0C,SCN/B5E,EACX6E,EAA6BA,+BCuDf,SAAAC,EACdC,EAAmC,IAGnC,MAAMC,EAAoBC,EAAAA,kBAoC5B,SACEF,GAEM,MAAA/F,aACJA,EAAe,CAAC,EAAAa,cAChBA,EAAgBZ,kBAChBA,EAAoB,GAACa,oBACrBA,EACAC,iBAAAA,EACAC,uBAAAA,GACE+E,EAEG,MAAA,CACL/F,eACAa,gBACAC,sBACAC,iBAAAA,EACAC,uBAAAA,EACAf,oBAEJ,CAvDIiG,CAAuBH,GJMI,OAAzBI,EAAAA,qBACKtG,EAIFuG,EAAAA,OAAOzG,EAAuBE,GIRnC,CACEkB,mBACAC,2BAKEqF,EAA4C,IAC7CN,KACAC,GAICM,EAAWC,aAAoBF,GAC/B1E,EAAO0B,EAAiBiD,GACxBE,EAAgB5B,EAAoB/C,EAAiByE,IAQpD,OALPG,EAAAA,eAAe,SAEb9E,EAAK8C,YAGA9C,CACT,CClFa,MAAA+E,EAA2B9G,OAAO,mBA+BxC,SAAS+G,EACdL,GAEM,MAAA3E,EAAO0B,EAAiBiD,GACxBE,EAAgB5B,EAAoB/C,EAAiBF,IAKpD,iBAHsB+E,EAA0B/E,GACvD8E,EAAAA,eAAeD,GAER7E,CACT,CAoBO,SAASiF,IAGR,MAAAN,EAAWF,EAAAA,OAAmCM,EAA0B,MAE9E,IAAKJ,EACH,MAAM,IAAIO,MACR,qJAKG,OAAAP,CACT,CCnCa,MAAAQ,EACXlD,IAEA,MAAMjC,EAAOiF,IAKN,OAjDT,SAIEjF,EACAiC,EACA4B,GAEM,MAAA1D,EAAWJ,EAAYC,GACvBoF,EAAYC,EAAAA,YAAqBlF,EAAU8B,GAC3CG,EAASkD,EAAAA,SAAS,IAAMzB,EAAYzB,OAAOvC,OAC3C0F,EAAQD,EAAAA,SAAS,IAAMzB,EAAYvB,QAAQzC,OAC3C2C,EAAU8C,EAAAA,SAAS,IAAMzB,EAAYrB,QAAQ3C,OAQ5C,MAAA,IACFuF,EACHnD,OACApC,MAAOgE,EAAYhE,MACnBuC,SACAmD,QACA/C,UACAgD,SAbe,IAA6C3B,EAAYhE,MAAMA,MAc9E4F,UAbgB,IAA8C5B,EAAYzB,OAAOvC,MAcjF6F,UAbgB,IAAe7B,EAAYvB,QAAQzC,MAcnD8F,UAbgB,IAAe9B,EAAYrB,QAAQ3C,MAcnD+F,UAbgB,IAAyB5F,EAAKyC,iBAelD,CAgBSoD,CAAgB7F,EAAMiC,EAFTC,EADDhC,EADFH,EAAYC,IAEqBiC,KCtE9C6D,EAAqD7H,OACzD,gBAsBK,SAAS8H,EACd3C,aAEgC0C,EAAuB1C,EACzD,CAkBO,SAAS4C,IACR,MAAA5C,EAAQqB,SAA8BqB,GAE5C,IAAK1C,EACH,MAAM,IAAI8B,MACR,sHAKG,OAAA9B,CACT,CC0DgB,SAAA6C,EACdC,EAEAC,EAEAC,GAEA,MAAMpG,EAAOiF,IAEPnB,EAAWuC,EAAAA,YACftG,EAAYC,GACZkG,EACAC,EACAC,GAKK,OAFPE,EAAAA,YAAYxC,GAELA,CACT,CAkDgB,SAAAyC,EACd7D,EACA8D,EACApC,GAEO,OAAA6B,EAAkBvD,EAAO8D,EAAUpC,EAC5C,CC/IO,SAASqC,EAAeC,GAC7B,OAAIA,aAAexB,MAAcwB,EAE1B,IAAIxB,MAAMyB,OAAOD,GAC1B,CAwCa,MAAAE,EAAgB,CAM3BxC,EACAyC,WAEA,MAAMlC,EAAWM,IAEX6B,EAAOhH,aAAsB,IAE7Bc,EAAUmG,OAAa,GAEvBC,EAAQD,EAAAA,SAAgC,GAG9C,IAAIE,EAAe,EAKb,MA0CAC,EAAWC,UACX,IACI,MAAAC,EAAazC,EAASlC,iBAGxB,GAA+B,mBAAxB2B,EAAQiD,cAA+BjD,EAAQiD,YAAYD,GAIpE,OAHAN,EAAKjH,MAAQ,QACbe,EAAQf,OAAQ,GAKlBe,EAAQf,OAAQ,EAChBmH,EAAMnH,WAAQ,EAGd,MAAMyH,IAAiBL,EAEjBM,OA3CeJ,OAAOC,IACxB,MAAAI,EAAapD,EAAQqD,YAAc,EAEnCC,EAAatD,EAAQuD,eAAiB,IAExC,IAAAC,EAAmB,IAAI1C,MAAM,iBAEjC,IAAA,IAAS2C,EAAU,EAAGA,GAAWL,EAAYK,IACvC,IACF,aAAazD,EAAQ0D,IAAIV,EAAYzC,SAC9B+B,GACPkB,EAAYnB,EAAeC,GAEvBmB,EAAUL,SACN,IAAIO,QAASC,GAAMC,WAAWD,EAAGN,GAE3C,CAGI,MAAAE,GAwBcM,CAAiBd,GAGnC,GAAIE,IAAiBL,EAAc,OAE7B,MAAAkB,OAjEKhB,OAAOI,IAChB,GAA8B,mBAAd,MAATnD,OAAS,EAAAA,EAAAgE,WAClB,aAAahE,EAAQgE,UAAUb,EAAK5C,GAGtC,IAAK0D,MAAMC,QAAQf,GACjB,MAAM,IAAIrC,MACR,gFAIG,OAAAqC,GAsDmBgB,CAAOhB,GAG/B,GAAID,IAAiBL,EAAc,OAEnCH,EAAKjH,MAAQsI,EACbnB,EAAMnH,WAAQ,EAGmB,mBAAtBuE,EAAQoE,WACTpE,EAAAoE,UAAUL,EAAWxD,GAG/B/D,EAAQf,OAAQ,QACT6G,GACD,MAAA+B,EAAahC,EAAeC,GAElCM,EAAMnH,MAAQ4I,EACd3B,EAAKjH,MAAQ,GACbe,EAAQf,OAAQ,EAEe,mBAApBuE,EAAQsE,SACTtE,EAAAsE,QAAQD,EAAY9D,EAEhC,IAUE,OAAAgE,EAAAvE,EAAQwE,gBAAR,EAAAD,EAAmBjF,SACrB6C,EAAenC,EAAQwE,UAAW,CAACC,EAAgBC,KAEb,mBAAzB1E,EAAQ2E,cACT3E,EAAA2E,aAAaF,EAA2BlE,GAI9CP,EAAQ4E,mBAAqBnC,GACtBlC,EAAAsE,cAAcpC,OAAoB,GAIxCK,MAKH,MAAAgC,EAAY9E,EAAQ8E,YAAa,EAQvC,OANAC,EAAAA,UAAU,KACJD,GACGhC,MAIF,CAAEJ,OAAMlG,UAASoG,QAAOE,WAAUkC,QAjCzB,IAAqBlC,IAiCamC,OA/BlCC,IACdxC,EAAKjH,MAAQyJ,KCxMJC,EAAyBtL,OACpC,sBA8BWuL,EAA2B5K,cACZ2K,EAAwB3K,IA+B7C,SAAS6K,IACR,MAAAC,EAAUjF,SAAyB8E,GAEzC,IAAKG,EACH,MAAM,IAAIxE,MACR,kKAKG,OAAAwE,CACT,CCxCO,SAASC,EACdC,GAEM,MAAAC,EAAY/J,aAAW,CAAA,GAUtB,OARPgK,EAAAA,YAAY,KACV,MAAMtG,EAAOoG,ICjCa,EAC5BG,EACAC,KAEM,MAAAC,EAAQ9L,OAAO+L,KAAKH,GAEpBI,EAAQhM,OAAO+L,KAAKF,GAEtB,OAAAC,EAAMvG,SAAWyG,EAAMzG,QAEpBuG,EAAMtG,MAAOyG,GAAQL,EAAEK,KAASJ,EAAEI,KDyBlCC,CAAeR,EAAUhK,MAAO2D,KACnCqG,EAAUhK,MAAQ2D,KAIfqG,CACT,CEnBA,MAAMS,MAA4B7K,QAmB3B,SAAS8K,EACdvK,GAIA,OAFewK,EAAwBzK,EAAYC,IAErCyK,WAChB,CAiCA,SAASC,EACP1K,GAEA,MAAMyK,EAAc3K,EAAAA,WAClBE,EAAK2K,kBAGDC,EAAetF,EAAAA,SAAyD,IA8DhF,SACEmF,GAEM,MAAAG,MAAmB9J,IAOnB+J,EAAiBC,IACrB,IAAA,MAAWC,KAAUD,EACNF,EAAA9I,IAAIiJ,EAAOX,IAAKW,GAEzBC,EAAAA,kBAAkBD,IACpBF,EAAcE,EAAOE,WAOpB,OAFPJ,EAAcJ,GAEPG,CACT,CApFWM,CAAsBT,EAAY5K,QAcpC,MAAA,CACL8B,SAAU,EACViJ,eACApJ,YAdkBxB,EAAKmL,qBAMtBC,IACCX,EAAY5K,MAAQuL,IAQtBX,cAEJ,CAOA,SAASD,EACPxK,GAEM,MAAAI,EAAekK,EAAsBrK,IAAID,GAGzCK,EAASD,GAAgBsK,EAAuB1K,GAmB/C,OAjBFI,GACmBkK,EAAAxI,IAAI9B,EAAMK,GAG3BA,EAAAsB,WAEPmD,EAAAA,eAAe,KACNzE,EAAAsB,WAEHtB,EAAOsB,SAAW,IAItBtB,EAAOmB,cACP8I,EAAsBtG,OAAOhE,MAGxBK,CACT,CCnHO,SAASgL,EAIdrL,EACAsL,EACAlH,EAA6C,CAAA,GAE7C,MAAM/D,EAASH,EAAiBH,EAAYC,IACtC6E,EAAgB5B,EAAoB5C,GACpCkL,EAAWzL,EAAAA,WAAsBwL,EAASjL,EAAOG,OAAOX,QACxD2L,EAASpH,EAAQoH,QAAUrN,OAAOsF,GAOlCgI,EAAOC,EAAAA,MACXrL,EAAOG,OACNA,IACO,MAAAgD,EAAO8H,EAAS9K,GAEjBgL,EAAOD,EAAS1L,MAAO2D,KAC1B+H,EAAS1L,MAAQ2D,IAGrB,CAAEmI,MAAO,SAQJC,OALP9G,EAAAA,eAAe,eAKR8G,EAAAA,SAASL,EAClB,CC1DA,SAASM,EAAeC,GACtB,YAAU,IAANA,KACAzD,MAAMC,QAAQwD,IAAmB,IAAbA,EAAEpI,OAG5B,CAuEgB,SAAAqI,EACdC,EACAC,GAEA,IAAKA,EAAgB,OAAA,EAGrB,OAFiB5D,MAAMC,QAAQ2D,GAAWA,EAAU,CAACA,IAErCC,KAAMC,GApCxB,SAA0BA,GAUjB,MATmD,CACxDC,OAAQ,OACRC,SAAU,SACVC,SAAU,SACVC,KAAM,OACNC,OAAQ,SACRC,OAAQ,UAGCN,IAAM,QACnB,CAyB8BO,CAAiBP,KAAOH,EACtD,CC9FA,MAAMW,EAAeC,GAAyB,QAAQC,KAAKD,GAQrDE,EAAeF,GAAyBA,EAAIG,SAAS,KAQrDC,EAAgBJ,GACbA,EAAIK,QAAQ,WAAY,OAAOC,cASlCC,EAAgBP,GACbA,EAAIK,QAAQ,YAAa,CAACG,EAAQC,IAAWA,EAAOC,eA8ChDC,EAAc,CACzBC,EACAvL,KAEA,GAAIuL,EAAMvL,GAAO,OAAOuL,EAAMvL,GAE9B,MAAMwL,EAAMd,EAAY1K,GACpB+K,EAAa/K,GACb6K,EAAY7K,GACVkL,EAAalL,QACb,EAEF,OAAAwL,GAAOD,EAAMC,GAAaD,EAAMC,QAAhC,GAwBOC,EAAoB,CAC/B7G,EACA8G,KAEA,MAAMC,EAA8B,CAAA,EAE9BC,GAnEkBjB,EAmEajG,OAAOE,IAlExCiG,EAAYF,GAAaO,EAAaP,GAEnCA,GAgEmD,KAnEnC,IAACA,EAqExB,MAAMkB,EAjFiB,CAAClB,GACpBD,EAAYC,GAAaI,EAAaJ,GAEnCA,EA8EamB,CAAiBpH,OAAOE,IAAc,IAE1D,IAAA,MAAYuD,EAAKvK,KAAU1B,OAAOM,QAAQkP,GACpCvD,EAAI4D,WAAWH,GACjBD,EAAOxD,EAAI6D,MAAMJ,EAAYnK,SAAW7D,EAC/BuK,EAAI4D,WAAWF,IAAgBA,IAAgBD,IACxDD,EAAOxD,EAAI6D,MAAMH,EAAYpK,SAAW7D,GAIrC,OAAA+N,GCsBTM,EAzIkBC,EAAAA,gBAChB,CACEvP,GACE4O,kBAEF,MAAMY,EAAoBrH,EAAAA,IAAIsH,QAAQzP,EAAMmM,OAAOuD,mBAE7CC,EAAYjJ,EAAQA,SAAC,IAAM1G,EAAMmM,OAAOwD,WAAaH,EAAkBvO,OAEvE2O,GAAchK,OAAAA,EAAAA,EAAAA,+BAAsBiK,MAAO,EAEjD/C,EAAKA,MACH,IAAM9M,EAAMmM,OAAOwD,UACnB,CAACG,EAAeC,UACmB,IAA7BD,EACFN,EAAkBvO,MAAQ6O,OACgB,IAAjCC,IACTP,EAAkBvO,MAAQ8O,KAKhC,MAAMC,EAASA,aACb,IAAKhQ,EAAMmM,OAAO8D,aAAejQ,EAAMmM,OAAO+D,SAC5C,OAGIJ,MAAAA,GAAiBH,EAAU1O,WAES,IAAtCjB,EAAMmM,OAAOwD,YACfH,EAAkBvO,MAAQ6O,GAG5B9P,OAAAA,GAAAA,EAAAA,EAAMmM,QAAOgE,2BAAbnQ,EAAiC8P,IAGnC,MAAO,WACL,MAAM3D,EAASnM,EAAMmM,OAEjBA,IAAmB,IAAnBA,EAAOiE,QACF,OAAA,KAGHH,MAAAA,EAAcR,QAAQtD,EAAO8D,aAE7BI,EAAcV,EAAU1O,MAExBqP,EAAoBnE,EAAOmE,oBAAqB,EAIhDC,EAAS,iBAFEpE,OAAAA,EAAAA,EAAOqE,cAAPrE,EAAcsE,gBAAiB,SAASb,OAEdc,EAAYvE,EAAOX,OAExDmF,EAAW,GAAGJ,WAEdK,EAAS,GAAGL,SAEZM,EAAIC,EAAAC,YAAA,MAAA,CAAAC,GAEFJ,EAAMK,KAAA,QAAA,kBAEO9E,EAAO+E,MAAQP,OAAWQ,EAAS,cACvCd,QAAec,EAASC,MAAA,qBAAAC,OAE7Bf,GAAqBD,EAAc,CAAEiB,QAAS,aAAWH,GAEhEhF,CAAAA,EAAOE,SAASvM,IAAUgR,GAAAA,EAAAC,YAAAQ,GAAA,CAAA/F,IAElBgG,EAAMhG,IAAGW,OACNqF,GACC5C,MAMjBkC,OAAAA,EAAAA,YAAA,MAAA,CAAAM,MAEWK,EACL,eACA,CACE,0BAA2BpB,EAC3B,cAAelE,EAAOa,SACtB,cAAeb,EAAO+D,UAExB/D,EAAOiF,OACRC,MACMlF,EAAOkF,MAAK,WACTlF,EAAOX,IAAG,gBACLW,EAAO+D,eAAYiB,GAAS,CAE1ChF,EAAO+E,OAAKJ,EAAAA,YAAA,MAAA,CAAAE,GAELL,EAAQM,KACNhB,EAAc,cAAWkB,EAASO,SAC9BzB,EAAe9D,EAAO+D,YAAgB,OAAKiB,EAAS,gBAC/ClB,GAAeI,OAAcc,EAAS,gBACtClB,EAAcW,OAASO,EAAS,gBAChClB,GAAc9D,EAAO+D,eAAwBiB,EAASC,MAC9DK,EAAW,uBAAwB,CACxC,kCAAmCxB,IAAgB9D,EAAO+D,WAC1DyB,QACO3B,EAAM4B,UACkBC,IAC1B1F,EAAO+D,UAAuB,UAAV2B,EAAErG,KAA6B,MAAVqG,EAAErG,MAC9CqG,EAAEC,wBAGL,CAAAhB,EAAAC,YAAA,OAAA,CAAAK,MAAA,uBAAA,CAEkCjF,EAAO+E,QACzCjB,GAAWa,EAAAC,YAAA,OAAA,CAAAK,MAEDK,EAAW,sBAAuB,CACvC,6BAA8BpB,KAGnC,QAGJC,GAAqBD,GAAeQ,EAAOA,MAKpD,CACExN,KAAM,cAENrD,MAAO,CACLmM,OAAQ,CACNpM,KAAMR,OACNwS,UAAU,MAQZrB,EAAuClF,GACpCzD,OAAOyD,GAAK6C,QAAQ,kBAAmB,KCOzC,SAAS2D,EAAiB3O,GAC3BoG,OAAAA,MAAMC,QAAQrG,GACTA,EAAKvD,IAAciI,GAAAA,OAAOkK,IAAOC,KAAK,KAGxCnK,OAAO1E,EAChB,CClIA,MAAMkO,IAAWhC,gBAAgB,CAC/BlM,KAAM,aAENrD,MAAO,CACLmM,OAAQ,CACNpM,KAAMR,OACNwS,UAAU,IAUdI,OAAMnS,GAAO4O,MAAEA,KACN,KACL,MAAMzC,EAASnM,EAAMmM,OAEjBC,OAAAA,EAAAA,kBAAkBD,GACbiG,EAAAA,EAAEC,EAAW,CAAElG,UAAUyC,GAG3BwD,EAAAA,EAAEE,GAAe,CAAEnG,UAAUyC,MAKpC0D,KAAgB/C,gBAAgB,CACpClM,KAAM,kBACNkP,cAAc,EAEdvS,MAAO,CACLmM,OAAQ,CACNpM,KAAMR,OACNwS,UAAU,IAWdI,KAAAA,CAAMnS,GAAOwS,MAAEA,EAAAA,MAAO5D,IACpB,MAAMxN,EAAOiF,IAEPoM,EAAc/L,EAAQA,SAC1B,IAAM1G,EAAMmM,QAIRuG,ENlBM,SACdtR,EACAuR,GAEA,MAAMlR,EAASmK,EAAwBzK,EAAYC,IAE5CsF,OAAAA,EAAAA,SAAS,IAAMjF,EAAOuK,aAAa/K,MAAMI,IAAIsR,KACtD,CMWyBC,CAAcxR,EAAM,IAAMqR,EAAYxR,MAAMuK,KAG3DqH,EAAYnM,EAAAA,SAAwC,IACjDgM,EAAazR,OAAS6R,EAAAA,wBAAwBJ,EAAazR,OAC9DyR,EAAazR,MACbwR,EAAYxR,OAGZ8R,EAAclI,IAEdrG,EAAQ+B,EAASsM,EAAU5R,MAAMoC,MAEvC8D,EAAmB3C,GAEnB,MAAM6I,EAAU3G,EAAAA,SAAwB,KACtCsM,OJtEJC,EIuEMJ,EAAU5R,MAAMiS,kBJtEtBC,EIuEMJ,EAAYtT,aAAayT,kBJtE/BE,EIuEM,WJrEFnG,EAAegG,GAAuBA,EAEtChG,EAAekG,GAAwBA,EAEpCC,EATO,IACdH,EACAE,EACAC,IIgFQC,EAAc3M,EAAAA,SAAS,KACrB4M,MAAAA,EACJT,EAAU5R,MAAMmP,UAAYyC,EAAU5R,MAAM+L,WAAa6F,EAAU5R,MAAMiP,SAErEqD,EAAQV,EAAU5R,MAAMsS,MAExBC,EAAW/J,MAAMC,QAAQ6J,IAAgBzO,MAAPyO,OAAOzO,EAAAA,EAAAA,QAAS,IAAM+N,EAAU5R,MAAMsS,MAE9E,OAAOD,IAAc7D,QAAQoD,EAAU5R,MAAM8Q,WAAayB,KAQtDC,EAA4DvG,YAChE1I,EAAMkP,SAASxG,GACf2F,OAAAA,EAAAA,OAAAA,EAAAA,EAAU5R,MAAM0S,qBAAgBlG,EAAAA,EAAAA,WAAWP,EAAAA,KAAAA,EAAAA,GAEvCmG,EAAYpS,OAASkM,EAAiB,SAAUE,EAAQpM,QAC1DuD,EAAMoP,YASJC,EAA0D3G,YAC9D2F,OAAAA,EAAAA,OAAAA,EAAAA,EAAU5R,MAAM0S,qBAAgBnG,EAAAA,EAAAA,SAASN,EAAAA,KAAAA,EAAAA,GAErCmG,EAAYpS,OAASkM,EAAiB,OAAQE,EAAQpM,QACxDuD,EAAMoP,YASJE,EAAiE5G,IACrE1I,EAAMkP,SAASxG,IAIXyG,EAAiB5I,EACrB,KAGS,IAFuB8H,EAAU5R,MAAM0S,gBAAkB,CAAA,EAI9D1S,MAAOuD,EAAMvD,MAAMA,MACnBwM,SAAUgG,EACVjG,OAAQqG,EACR,iBAAkBC,MAKlBC,gBACJA,EAAAA,YACAC,EAAAA,aACAC,EAAAA,cACAC,EAAAA,YACAC,EAAAA,YACAC,GD9JC,SACL5O,GAEM,MAAAqN,UAAEA,EAAAA,MAAWrO,EAAAA,KAAOpD,EAAAA,YAAM2R,EAAAA,eAAaY,EAAAA,MAAgB/E,GAAUpJ,EAsBjEuO,EAAkBA,CAACM,EAA2C,MAC3D,IACFV,EAAe1S,MAClBA,MAAOuD,EAAMvD,MAAMA,SAChBoT,IAKDC,EAAkDC,IAChDC,MAAAA,EAAO7F,EAAYC,EAAO,GAAGiE,EAAU5R,MAAMoC,OAAOkR,KAEnDC,OAAAA,MAAAA,OAAAA,EAAAA,EAAOT,OAAsB,MAmF/B,MAAA,CACLA,kBACAC,YAvBkBA,IAAkBM,EAAgB,SAwBpDL,aAtDmBA,IAAkBK,EAAgB,UAuDrDJ,cApDoBA,KACpB,MAAMO,EAAYrT,EAAKsT,YAAY7B,EAAU5R,MAAM0T,eAEnD,IAAKF,EACH,MAAM,IAAInO,MACR,gDAAgDuM,EAAU5R,MAAM0T,mBAIpE,MAAMC,EAAa9F,EAAkBkD,EAAiBa,EAAU5R,MAAMoC,MAAOuL,GAEvEiG,EAAgBzC,EAAAA,EAAEqC,EAAWd,EAAe1S,MAAO2T,GAEnDE,EAAcnG,EAAYC,EAAO,GAAGiE,EAAU5R,MAAMoC,eAE1D,OAAIyR,EACKA,EACLf,EAAgB,CACdc,mBAKN/D,EAAAA,YAAA,MAAA,CAAAM,MAAA,wBAAA,CAA0CyD,KA8B1CV,YAvBkBA,KAClB,MAAMY,EAAYpG,EAAYC,EAAO,GAAGiE,EAAU5R,MAAMoC,aAExD,OAAI0R,EACKA,EACLhB,EAAgB,CACdvQ,OAAQgB,EAAMhB,OAAOvC,SAKO,IAA9BuD,EAAMhB,OAAOvC,MAAM6D,OACd,KAGTgM,EAAAA,YAAA,MAAA,CAAAM,MAAA,sBAAA,CAAwC5M,EAAMhB,OAAOvC,MAAM,MAS3DmT,YArFkBA,KAClB,MAAMY,EAAYrG,EAAYC,EAAO,GAAGiE,EAAU5R,MAAMoC,aAExD,GAAI2R,EACKA,OAAAA,EAAUnC,EAAU5R,OAG7B,MAAMgU,EAAapC,EAAU5R,MAAMgU,YAAclC,EAAYtT,aAAawV,WAEpEC,EAAarC,EAAU5R,MAAMiU,YAAcnC,EAAYtT,aAAayV,WAEpEC,EAAQtC,EAAU5R,MAAMkU,OAASpC,EAAYtT,aAAa0V,MAEhErE,OAAAA,EAAAA,YAAA,QAAA,CAAAM,MAAA,qBAAAC,MAGW,CAAE+D,MAAOF,EAAYG,UAAWJ,IAAY,GA1CrDpC,EAAU5R,MAAMqU,kBAAoB7F,QAAQoD,EAAU5R,MAAM8Q,YAErCc,EAAU5R,MAAMiP,UAAY2C,EAAU5R,MAAM+L,SAC5D,KAGT8D,EAAAA,YAAA,OAAA,CAAAM,MAAA,yBAAA,CAAAmE,EAAAC,gBAAA,OAsCqB1E,EAAAC,YAAA,OAAA,CAAAK,MAAA,2BAEdyB,CAAAA,EAAU5R,MAAMiQ,MAChBiE,EAAQ,IAAM,QAkEzB,CC+BQM,CAA4B,CAC9B5C,YACArO,QACApD,OACA2R,cACAY,iBACA/E,UAGF,MAAO,KACD,IAACiE,EAAU5R,MAAMmP,QACZ,OAAA,KAIT,MAAMsF,EAAW/G,EAAYC,EAAOoD,EAAiBa,EAAU5R,MAAMoC,OAErE,GAAIqS,EACKA,OAAAA,EAAS3B,KAGlB,MAAM4B,EACJ9C,EAAU5R,MAAM0U,eAAiB5C,EAAYtT,aAAakW,cAE5D,OAAA7E,cAAA,MAAA8E,EAAAC,WAEQrD,EAAK,CAAApB,MACFK,EAAW,sBAAuBe,EAAMpB,OAAMC,MAC9C,CAACmB,EAAMnB,MAAOwB,EAAU5R,MAAMoQ,SAAM,CAAAP,EAAAC,YAAA,MAAA,CAAAK,MAGlCK,EACL,cACA,sBAAsBkE,IACtB9C,EAAU5R,MAAMmQ,MAChB,CACE,cAAeyB,EAAU5R,MAAM+L,SAC/B,cAAe6F,EAAU5R,MAAMiP,WAElCmB,MACM,IAAOwB,EAAU5R,MAAMoQ,OAAS,CAAE,IAAqB,CAE7D+C,IAAatD,EAAAC,YAAA,MAAA,CAAAK,MAAA,wBAAA,CAGX6C,IACAC,IACAF,IACAG,UAMb,IAGF2B,GAAevE,qjBCrPb,MAAMvR,EAAQ+V,EAORvD,EAAQwD,EAAAA,WAERC,EAAavP,EAAAA,SAAS,IAAM1G,EAAMkQ,UAAYlQ,EAAMgC,SAEpDkU,EAAcxP,WAAS,IAAM,CAAC,gBAAiB,kBAAkB1G,EAAMmW,qCAI7EC,qBAwCS,SAxCTR,EAAAA,WAwCSS,EAAAA,MAvCM7D,GAAA,CACZpB,MAAO8E,EAAWjV,MAClB,YAAWjB,EAAMgC,cAAW,EAC5B,eAAchC,EAAMgC,cAAW,EAC/BkO,SAAU+F,EAAUhV,SAETqV,EAAAA,OAAOC,QAAnBC,EAAAA,YAAAJ,EAAAK,mBAEO,OAFPC,GAEO,CADLC,aAAsBC,EAAAN,OAAA,yCAGhBtW,EAAMgC,SADdwU,EAAAA,YAAAJ,EAAAA,mBAqBM,MArBNS,GAqBM,IAAAC,EAAA,KAAAA,EAAA,GAAA,8SAEU9W,EAAMgC,SAAWhC,EAAM+W,2BAAvCX,EAAAK,mBAEWO,WAAA,CAAAxL,IAAA,GAAA,CADN+J,EAAAA,gBAAA0B,EAAAA,gBAAAjX,EAAM+W,aAAW,SAEtBJ,aAAeC,EAAAN,OAAA,UAAA,CAAA,OAAA,OAAA,EAAA,GACHA,EAAAA,OAAO/B,QAAnBiC,EAAAA,YAAAJ,EAAAK,mBAEO,OAFPS,GAEO,CADLP,aAAsBC,EAAAN,OAAA,qDChB5B,SAASa,GAA+CC,GAC/C,QAAEA,IAAShL,oBAAkBgL,KAA0B,IAAjBA,EAAKhH,OACpD,CAEA,SAASiH,GACPnP,EACAoP,EACAC,GAES,IAAA,IAAAvS,EAAQsS,EAAaC,EAAMvS,GAAS,GAAKA,EAAQkD,EAAKpD,OAAQE,GAASuS,EAAM,CAC9E,MAAAH,EAAOlP,EAAKlD,GAEd,IAAiB,IAAjBoS,EAAKhH,QAAL,CAIAhE,GAAAA,EAAAA,kBAAkBgL,GACb,OAAA,EAGL,GAAAD,GAAeC,GACV,OAAA,CAPT,CASF,CAEO,OAAA,CACT,oqCClCE,MAAMpX,EAAQ+V,EA2BRyB,EAAOC,EAOP7I,EAAQ8I,EAAAA,WAKRC,EAAmB,IAIhBC,EAAAA,KAAK5X,EAAO6X,EAAAA,yBAMfC,EAAmBC,EAAAA,SAAsCJ,KAOvC/M,EAAA,CAAEnL,aAAcqY,IAQxC,MAAME,EAAehY,EAAMoB,KACvBpB,EAAMoB,KACNmE,EAAiB,CACf2G,QAASlM,EAAMkM,QACfzM,aAAckY,IACdM,cACE1Y,OAAO+L,KAAKtL,EAAMkY,YAAYpT,OAAS,EACnC9E,EAAMkY,WACNlY,EAAMiY,cAEZ3X,cAAeN,EAAMM,cACrBE,iBAAkBR,EAAMQ,iBACxBD,oBAAqBP,EAAMO,oBAC3BE,uBAAwBT,EAAMS,uBAC9Bf,kBAAmBM,EAAMN,kBAOzByY,SAAWvW,UAAW,OAAA,OAAAmI,EAAA/J,EAAMmY,eAAW,EAAApO,EAAAqO,KAAApY,EAAA4B,IAMvCyW,eAAiB7U,UAAW,OAAA,OAAAuG,EAAA/J,EAAMqY,qBAAiB,EAAAtO,EAAAqO,KAAApY,EAAAwD,IAInD8U,QAAS,WACP,OAAAvO,EAAA/J,EAAMsY,UAANvO,EAAAqO,KAAApY,IASFuY,gBAAkBvW,UAChB,OAAA+H,EAAA/J,EAAMuY,kBAAkBxO,EAAAqO,KAAApY,EAAAgC,IAQ1BwW,eAAgB,CAACC,EAAexO,WACxB,OAAAF,EAAA/J,EAAAwY,0BAAiBC,EAAexO,IAQxCyO,eAAgB,CAACC,EAAcC,WACvB,OAAA7O,EAAA/J,EAAA0Y,0BAAiBC,EAAcC,MAIvCC,OAAgC,IAAf7Y,EAAMoB,KAQvB0X,EAAe1S,EAAkB4R,GAKjCe,EAAmBrS,EAAAA,SAAS,IAAM1G,EAAMgC,SAAW8W,EAAa7U,aAKhE+U,EACJC,IACuC,IAAXA,EAAkB,CAAC,EAAIA,GAAU,GAKzDC,EAAkBxS,EAAAA,SAAS,IAAMsS,EAAsBhZ,EAAMmZ,YAK7DC,EAAiB1S,EAAAA,SAAS,IAAMsS,EAAsBhZ,EAAMqZ,WAK5DC,EAAkBlZ,GACFb,OAAOU,YACzBV,OAAOM,QAAQO,EAAOmZ,aAAe,CAAA,GAAIC,OACvC,EAAEnW,KAAmB,SAATA,GAA4B,YAATA,IAU/BoW,EAAuB/S,EAAAA,SAAS,IAAM4S,EAAeJ,EAAgBjY,QAKrEyY,EAAsBhT,EAAAA,SAAS,IAAM4S,EAAeF,EAAenY,QAKnE0Y,EAAqBjT,EAAAA,SACzB,KAA0B,IAApB1G,EAAMmZ,qBAAwBnZ,EAAMmZ,WAA2BvK,EAAMuK,YAMvES,EAAoBlT,EAAAA,SACxB,KAAyB,IAAnB1G,EAAMqZ,oBAAuBrZ,EAAMqZ,UAA0BzK,EAAMyK,WAMrEQ,EAAmBnT,EAAAA,SACvB,IAAMiT,EAAmB1Y,OAAS2Y,EAAkB3Y,OAMhD6Y,EAAapT,EAAAA,SAAS,IAC1BnH,OAAOU,YACLV,OAAOM,QAAQ+O,GAAO4K,OACpB,EAAEO,KAA2B,cAAbA,GAAyC,aAAbA,KAQ5CC,EAAsBtT,EAAAA,SAC1B,IAAMqS,EAAiB9X,OAASwO,QAAQgK,EAAqBxY,MAAMiP,WAM/D+J,EAAqBvT,EAAAA,SACzB,IAAMqS,EAAiB9X,OAASwO,QAAQiK,EAAoBzY,MAAMiP,WAGpE,IAAIgK,GAAmB,EAOvBC,EAAArN,MACE,IAAM9M,EAAMkY,WACXtW,IACoBsY,GAAA,EACnBpB,EAAasB,eAAexY,GACTsY,GAAA,GAErB,CAAEG,MAAM,IAMJ,MAAAC,EAAkD,IAAMxB,EAAajL,SAKrE0M,EAAc,KAClBzB,EAAa0B,SAQThS,EAAaiE,EAAgBqM,EAAelX,GAAWA,GAO7DuY,EAAArN,MACEtE,EACCyB,IACKiQ,GAEJ1C,EAAK,oBAAqBvN,IAE5B,CAAE8C,MAAO,SAQXoN,EAAArN,MACE,IAAM9M,EAAMkM,QACXA,IACMuO,EAAAA,gBAAgBvO,IACnB4M,EAAa4B,WAAWxO,IAG5B,CAAEmO,MAAM,EAAO/P,YAAatK,EAAMoB,OAG9B,MAAAyK,EAAcF,EAAemN,GAQ7B6B,EAAoBxO,IAClB,MAAAyO,QAAEA,EAASC,OAAAA,GDvUL,SACd3S,EACA4S,GAEA,MAAMC,EAAe7S,EAAK8S,UAAW5D,GAASA,EAAK5L,MAAQsP,GAE3D,OAAyB,IAArBC,EACK,CACLE,OAAO,EACPL,SAAS,EACTC,QAAQ,GAMP1D,GAFejP,EAAK6S,IAUlB,CACLE,OAAO,EACPL,SAAUvD,GAAyBnP,EAAM6S,GAAgB,GACzDF,QAASxD,GAAyBnP,EAAM6S,EAAc,IAV/C,CACLE,OAAO,EACPL,SAAS,EACTC,QAAQ,EASd,CC0SgCK,CAC1BrP,EAAY5K,MACZkL,EAAOX,KAGF,MAAA,CACL,6BAA8BoP,EAC9B,4BAA6BC,WASjCV,EAAArN,MACE6K,EACCwD,IACQ5b,OAAA6b,OAAOtD,EAAkBqD,GAChCrC,EAAauC,mBAAmBF,IAElC,CAAEd,MAAM,EAAO/P,UAAWuO,IAMfyC,EAAA,IACRxC,EACHjL,OAAQyM,EACRE,MAAOD,0BAKTnE,EAAAK,mBAkDM,MAAA,CAlDArF,MAAKmK,EAAAA,eAAA,CAAA,SAAavb,EAAMoR,QAASC,MAAKmK,EAAAA,eAAExb,EAAMqR,0BAClD+E,EASWK,mBAAAO,WAAA,KAAAyE,EAAAC,WARQrF,QAAWxK,GAArBM,kBADTwP,EAAAA,YASWtF,EAAAuF,MAAArK,IAAA,CAPR/F,IAAKW,EAAOX,IACZW,SACAiF,MAAKmK,EAAAM,eAAElB,EAAiBxO,uCAES2N,EAAU7Y,MAAA,CAA1B6a,EAAG/B,WAA0BA,EAC7CgC,GAAAC,EAAAA,QADyDC,GAAS,CAClEtF,EAAAA,WAAkDC,EAArCN,OAAAyD,EAAbnE,EAAkDC,WAAA,aAAnBoG,GAAS,CAAA,4CAGjCpC,EAAgB5Y,OAA3BuV,EAAA0F,YAAA9F,EAAAA,mBAsCM,MAtCN+F,GAsCM,CApCIvC,EAAiB3Y,OAAIoV,EAAKuF,MAAAhN,GAACyK,SADnC1C,aAOEC,EAAAN,OAAA,WAAA,CAJClV,KAAMiV,QAAYyC,GAClB9W,QAAS+W,EAAgB9X,MACzBiP,SAAU+J,EAAkBhZ,MAC5BuZ,MAAOD,oBAGGX,EAAiB3Y,qBAD9B0a,cASStF,QAAA+F,IATTxG,aASS,CAAApK,IAAA,GAPCkO,EAAmBzY,MAAA,CAC3BmQ,MAAM,qDACNrR,KAAK,SACJmQ,SAAU+J,EAAkBhZ,MAC5B0Q,QAAO4I,uBAER,IAAiC,CAA9BhF,kBAAA0B,EAAAoF,gBAAAjD,EAAAnY,MAAeqb,MAAI,MAAA,wDAGhB3C,EAAkB1Y,OAAIoV,EAAKuF,MAAAhN,GAACuK,UADpCxC,aAOEC,EAAAN,OAAA,YAAA,CAJClV,KAAMiV,QAAYyC,GAClB9W,QAAS+W,EAAgB9X,MACzBiP,SAAU8J,EAAmB/Y,MAC7B4M,OAAQyM,oBAGEX,EAAkB1Y,qBAD/B0a,cAUStF,QAAA+F,IAVTxG,aAUS,CAAApK,IAAA,GARCiO,EAAoBxY,MAAA,CAC5BmQ,MAAM,sDACNrR,KAAK,SACJiC,QAAS+W,EAAgB9X,MACzBiP,SAAU8J,EAAmB/Y,MAC7B0Q,QAAO2I,uBAER,IAAkC,CAA/B/E,kBAAA0B,EAAAoF,gBAAAnD,EAAAjY,MAAgBqb,MAAI,MAAA,yGCpYf,IAGdC,GAAkBC,GAmBpB,MAAAC,IAnBEF,GASmCG,GATjBF,GAS6B,CAE/C,OAAAG,CAAQxc,EAAUqF,EAAgC,IAChDtF,EAAuBC,EAAKqF,GACxBrF,EAAAsU,UAAU,aAAciI,GAC9B,EAAAnL,SAEAA,IAfOhS,OAAO6b,OAAOmB,GAAMC,yFCgCtB,SAA2BI,GAChC,OAAOrN,kBAAgB,CACrBlM,KAAM,qBAAqBuZ,EAAiBvZ,MAAQ,eACpDkP,cAAc,EACdvS,MAAO,CACL6c,KAAM,CACJ9c,KAAM,CAACR,OAAQud,UACfC,aAAS,IAGb,KAAA5K,CAAMnS,GAAOwS,MAAEA,EAAA5D,MAAOA,IACd,MAAAoO,EAxDZ,SACEA,GAEI,MAAsB,mBAAfA,EACF,CAAE9T,IAAK8T,GAGTA,CACT,CAgDyBC,CAAoBjd,EAAM6c,MAIvC5U,EAAY+U,EACbxK,EAAMvK,WAAoCb,IAAkB/D,UAC7D,EAEE6Z,EAAaF,EAAahV,EAAcgV,EAAY/U,GAAa,KAEjEkV,EAAgBzW,EAAAA,SAAS,KACtB,IACF8L,EACHqK,KAAMG,EACNxX,QAASwX,EAAyB,MAAZE,OAAY,EAAAA,EAAAhV,KAAKjH,MAAQuR,EAAMhN,QACrDxD,QAASgb,EAAyB,MAAZE,OAAY,EAAAA,EAAAlb,QAAQf,MAAQuR,EAAMxQ,WAI5D,MAAO,IAAMoQ,EAAAA,EAAEwK,EAAkBO,EAAclc,MAAO2N,EACxD,GAEJ,ubhByGgB,SACdhH,EACApC,GAEO,OAAA6B,EAASO,EAAUpC,EAC5B,wBA5DgB,SACdnC,EACAuE,EACApC,GAEO,OAAA6B,EAAkBhE,EAAMuE,EAAUpC,EAC3C"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/bridge/helpers.ts","../src/bridge/fieldBridge.ts","../src/bridge/formBridge.ts","../src/bridge/formFacade.ts","../src/config/appConfig.ts","../src/utils/rendererProvider.ts","../src/utils/rulesProvider.ts","../src/hooks/useForm.ts","../src/hooks/provideFormContext.ts","../src/hooks/useField.ts","../src/hooks/provideFieldContext.ts","../src/hooks/useWatch.ts","../src/hooks/useDictionary.ts","../src/hooks/provideFormConfigContext.ts","../src/hooks/useStableRef.ts","../src/utils/equal.ts","../src/hooks/useViewSchemas.ts","../src/hooks/useFormSelector.ts","../src/utils/validation.ts","../src/utils/slot.ts","../src/components/FormGroup/index.tsx","../src/components/FormItem/slot.tsx","../src/components/FormItem/index.tsx","../src/components/Button/index.vue","../src/utils/helpers.ts","../src/form.vue","../src/form.ts","../src/hocs/withRemoteOptions.ts"],"sourcesContent":["/**\n * Vue Bridge 使用的 Ref 和快照比较工具。\n *\n * @module vue/bridge/helpers\n */\n\nimport { shallowRef } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\n/**\n * 创建类型为 `ShallowRef` 的 Vue 浅层 Ref。\n *\n * 运行时直接委托 Vue 的 `shallowRef`,类型断言用于保留当前 Bridge 对快照引用的控制。\n *\n * @typeParam TValue - Ref 中保存的值类型。\n * @param value - 要包装为 shallow Ref 的当前值。\n * @returns 包装后的 shallow Ref。\n *\n * @example\n * ```ts\n * const ready = createVueShallowRef(false)\n * ready.value = true\n * ```\n */\nexport function createVueShallowRef<TValue>(value: TValue): ShallowRef<TValue> {\n return shallowRef(value) as ShallowRef<TValue>\n}\n\n/**\n * 比较两份错误消息列表,避免无变化时写入 Vue Ref。\n *\n * @param previous - 当前 Ref 中保存的错误消息列表。\n * @param next - SnapshotSource 返回的最新错误消息列表。\n * @returns 两个列表的长度和每一项都相同时返回 `true`。\n *\n * @example\n * ```ts\n * const changed = !areStringListsEqual(previous, next)\n * ```\n */\nexport function areStringListsEqual(\n previous: readonly string[],\n next: readonly string[]\n): boolean {\n return (\n previous.length === next.length &&\n previous.every((value, index) => value === next[index])\n )\n}\n","/**\n * 将 Core 字段快照投影为可共享的 Vue Ref。\n *\n * @module vue/bridge/fieldBridge\n */\n\nimport { areStringListsEqual, createVueShallowRef } from \"./helpers\"\n\nimport type { ManagedVueFieldBridge, VueFieldBridge, VueFormBridge } from \"./types\"\nimport type { FieldValue, NamePath, Values } from \"@schemx/core\"\n\n/**\n * 获取一个字段的共享 Vue Ref 投影。\n *\n * 字段 Bridge 以 Core `FieldSnapshotSource` 的对象身份缓存,因此同一 Form\n * 中对同一规范化字段路径的多次读取会复用同一组 Ref。\n *\n * @typeParam TValues - Form 的值类型。\n * @typeParam TName - 字段路径类型。\n * @param bridge - 所属 Form Bridge。\n * @param name - 字段路径。\n * @returns 对应字段的共享 Vue Bridge。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const field = getVueFieldBridge(bridge, \"email\")\n * watchEffect(() => console.log(field.value.value))\n * ```\n */\nexport function getVueFieldBridge<\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(bridge: VueFormBridge<TValues>, name: TName): VueFieldBridge<TValues, TName> {\n // FormStateAdapter 按规范化路径缓存 Source;Source 身份可作为稳定的 Bridge key。\n const source = bridge.stateAdapter.field(name)\n\n // 同一 Source 复用同一组 Ref,避免多个消费者产生重复订阅。\n const cachedBridge = bridge.fieldBridges.get(source as object)\n\n if (cachedBridge) {\n return cachedBridge as VueFieldBridge<TValues, TName>\n }\n\n // 首次读取快照用于初始化所有字段 Ref。\n const snapshot = source.getSnapshot()\n\n const value = createVueShallowRef<FieldValue<TValues, TName> | undefined>(\n snapshot.value\n )\n\n const errors = createVueShallowRef<readonly string[]>(snapshot.errors)\n\n const touched = createVueShallowRef<boolean>(snapshot.touched)\n\n const pending = createVueShallowRef<boolean>(snapshot.pending)\n\n // 逐项比较后再写入,避免无关字段状态变化触发 Vue 更新。\n const updateFieldRefs = (): void => {\n const nextSnapshot = source.getSnapshot()\n\n if (!Object.is(value.value, nextSnapshot.value)) {\n value.value = nextSnapshot.value\n }\n\n if (!areStringListsEqual(errors.value, nextSnapshot.errors)) {\n errors.value = nextSnapshot.errors\n }\n\n if (touched.value !== nextSnapshot.touched) {\n touched.value = nextSnapshot.touched\n }\n\n if (pending.value !== nextSnapshot.pending) {\n pending.value = nextSnapshot.pending\n }\n }\n\n const unsubscribe = source.subscribe(updateFieldRefs)\n\n /**\n * 停止字段快照到 Vue Ref 的同步订阅。\n */\n const dispose = (): void => {\n unsubscribe()\n }\n\n // 缓存完整的可管理对象,但对外只暴露字段 Ref 投影。\n const fieldBridge: ManagedVueFieldBridge<TValues> = {\n source,\n value,\n errors,\n touched,\n pending,\n dispose,\n }\n\n bridge.fieldBridges.set(source as object, fieldBridge)\n\n return fieldBridge as VueFieldBridge<TValues, TName>\n}\n","/**\n * Core Form 到 Vue 响应式状态的共享桥接。\n *\n * @module vue/bridge/formBridge\n */\n\nimport { createFormStateAdapter } from \"@schemx/core/adapter\"\n\nimport { getVueFieldBridge } from \"./fieldBridge\"\nimport { createVueFormFacade } from \"./formFacade\"\nimport { createVueShallowRef } from \"./helpers\"\n\nimport type {\n ManagedVueFieldBridge,\n PendingFieldsSnapshot,\n VueFormBridge,\n VueSchemxInstance,\n} from \"./types\"\nimport type { NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * 同一 Core Form 只创建一个共享 Vue Bridge。\n */\nconst formBridgeCache = new WeakMap<object, VueFormBridge<Values>>()\n\n/**\n * 同一 Core Form 始终复用同一个 Vue Facade。\n */\nconst formFacadeCache = new WeakMap<object, VueSchemxInstance<Values>>()\n\n/**\n * 用于从公开 Facade 回到原始 Core Form。\n */\nconst facadeCoreFormCache = new WeakMap<object, SchemxInstance<Values>>()\n\n/**\n * 返回原始 Core Form;普通 Core Form 输入时保持引用不变。\n *\n * @param form - Core Form 或 Vue Facade。\n * @returns 原始 Core Form。\n *\n * @example\n * ```ts\n * const coreForm = getCoreForm(form)\n * const snapshot = coreForm.getFieldsSnapshot()\n * ```\n */\nexport function getCoreForm<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): SchemxInstance<TValues> {\n const coreForm = facadeCoreFormCache.get(form as object)\n\n return (coreForm ?? form) as SchemxInstance<TValues>\n}\n\n/**\n * 获取指定 Form 的共享 Vue Bridge。\n *\n * @param form - Core Form 或 Vue Facade。\n * @returns 对应的共享 Bridge。\n *\n * @remarks\n * 首次调用会创建 SnapshotSource 订阅;调用方应使用\n * `retainVueFormBridge()` 持有 Bridge,并在 owner 销毁时释放。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const release = retainVueFormBridge(bridge)\n * onScopeDispose(release)\n * ```\n */\nexport function getVueFormBridge<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): VueFormBridge<TValues> {\n const coreForm = getCoreForm(form)\n\n const cachedBridge = formBridgeCache.get(coreForm)\n\n if (cachedBridge) {\n return cachedBridge as VueFormBridge<TValues>\n }\n\n const bridge = createVueFormBridge(coreForm)\n\n formBridgeCache.set(coreForm, bridge as VueFormBridge<Values>)\n\n return bridge\n}\n\n/**\n * 获取指定 Form 的唯一 Vue Facade。\n *\n * @param form - Core Form 或已有 Vue Facade。\n * @returns 可被 Vue effect 追踪的 Facade。\n *\n * @example\n * ```ts\n * const reactiveForm = getVueFormFacade(form)\n * watchEffect(() => {\n * console.log(reactiveForm.getFieldsValue())\n * })\n * ```\n */\nexport function getVueFormFacade<TValues extends Values = Values>(\n form: SchemxInstance<TValues> | VueSchemxInstance<TValues>\n): VueSchemxInstance<TValues> {\n const coreForm = getCoreForm(form)\n\n const cachedFacade = formFacadeCache.get(coreForm)\n\n if (cachedFacade) {\n return cachedFacade as VueSchemxInstance<TValues>\n }\n\n const facade = createVueFormFacade(coreForm, {\n getFormBridge: (bridgeForm) => getVueFormBridge(bridgeForm),\n getFieldBridge: (bridge, name) => getVueFieldBridge(bridge, name),\n getCachedFormBridge: (bridgeForm) => {\n const bridge = formBridgeCache.get(bridgeForm)\n\n return bridge as VueFormBridge<TValues> | undefined\n },\n disposeFormBridge: (bridge) => disposeVueFormBridge(bridge),\n })\n\n formFacadeCache.set(coreForm, facade as VueSchemxInstance<Values>)\n facadeCoreFormCache.set(facade as object, coreForm as SchemxInstance<Values>)\n\n return facade\n}\n\n/**\n * 为一个 Vue owner 保留 Bridge,并返回对应的幂等释放函数。\n *\n * @param bridge - 要保留的共享 Bridge。\n * @returns 释放当前 owner 的函数。\n *\n * @remarks\n * 返回的释放函数可安全重复调用;最后一个 owner 释放后,Bridge 会自动销毁。\n *\n * @example\n * ```ts\n * const bridge = getVueFormBridge(form)\n * const release = retainVueFormBridge(bridge)\n * onScopeDispose(release)\n * ```\n */\nexport function retainVueFormBridge<TValues extends Values>(\n bridge: VueFormBridge<TValues>\n): () => void {\n if (bridge.destroyed) {\n return () => {}\n }\n\n bridge.refCount++\n // 每个 owner 只允许减少一次引用计数。\n let released = false\n\n return () => {\n if (released || bridge.destroyed) {\n return\n }\n\n released = true\n bridge.refCount--\n\n if (bridge.refCount === 0) {\n disposeVueFormBridge(bridge)\n }\n }\n}\n\n/**\n * 销毁共享 Bridge、全部字段订阅与 Core 状态适配器。\n *\n * @param bridge - 要销毁的共享 Form Bridge。\n *\n * @remarks\n * 销毁后 Bridge、字段 Ref 投影和状态适配器均不可继续使用;通常由\n * `retainVueFormBridge()` 返回的最后一个释放函数自动触发。\n *\n * @example\n * ```ts\n * disposeVueFormBridge(bridge)\n * ```\n */\nexport function disposeVueFormBridge<TValues extends Values>(\n bridge: VueFormBridge<TValues>\n): void {\n if (bridge.destroyed) {\n return\n }\n\n bridge.destroyed = true\n bridge.refCount = 0\n bridge.unsubscribe()\n\n for (const fieldBridge of bridge.fieldBridges.values()) {\n fieldBridge.dispose()\n }\n\n bridge.fieldBridges.clear()\n bridge.stateAdapter.dispose()\n formBridgeCache.delete(bridge.form)\n}\n\n/**\n * 创建单个 Core Form 的 Vue Bridge 和唯一 Facade。\n *\n * @param form - 要桥接的原始 Core Form。\n * @returns 新建的共享 Vue Form Bridge。\n */\nfunction createVueFormBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): VueFormBridge<TValues> {\n // Form Bridge 持有一个 Core 状态适配器,并负责将其快照同步到 Vue Ref。\n const stateAdapter = createFormStateAdapter(form)\n\n // 初始化时读取快照,后续只在 SnapshotSource 通知变化时写入 Ref。\n const values = createVueShallowRef<TValues>(stateAdapter.values.getSnapshot())\n\n const touchedFields = createVueShallowRef<readonly NamePath<TValues>[]>(\n stateAdapter.touchedFields.getSnapshot()\n )\n\n const pendingFields = createVueShallowRef<PendingFieldsSnapshot<TValues>>(\n stateAdapter.pendingFields.getSnapshot()\n )\n\n const loading = createVueShallowRef<boolean>(stateAdapter.loading.getSnapshot())\n\n // 字段 Bridge 与 Form Bridge 同生命周期,由 disposeVueFormBridge 统一释放。\n const fieldBridges = new Map<object, ManagedVueFieldBridge<TValues>>()\n\n // Form 级来源各自同步到对应的 Vue Ref。\n // 同步全表值快照。\n const syncValues = (): void => {\n values.value = stateAdapter.values.getSnapshot()\n }\n\n // 同步 touched 字段聚合快照。\n const syncTouchedFields = (): void => {\n touchedFields.value = stateAdapter.touchedFields.getSnapshot()\n }\n\n // 同步 pending 字段聚合快照。\n const syncPendingFields = (): void => {\n pendingFields.value = stateAdapter.pendingFields.getSnapshot()\n }\n\n /**\n * 将 Core 提交流程状态同步到 Vue Ref。\n */\n const syncLoading = (): void => {\n loading.value = stateAdapter.loading.getSnapshot()\n }\n\n // 分别保留四个来源的取消订阅函数,避免聚合来源之间相互影响。\n const unsubscribeValues = stateAdapter.values.subscribe(syncValues)\n\n const unsubscribeTouchedFields = stateAdapter.touchedFields.subscribe(syncTouchedFields)\n\n const unsubscribePendingFields = stateAdapter.pendingFields.subscribe(syncPendingFields)\n\n const unsubscribeLoading = stateAdapter.loading.subscribe(syncLoading)\n\n // 集中保存 Form 级来源的取消订阅函数,便于 Bridge 销毁时一次释放。\n const unsubscribe = (): void => {\n unsubscribeValues()\n unsubscribeTouchedFields()\n unsubscribePendingFields()\n unsubscribeLoading()\n }\n\n // Facade 与 Bridge 共享同一个 Core Form,但通过缓存保证唯一实例。\n const facade = getVueFormFacade(form)\n\n // Bridge 的 Ref、订阅和引用计数由当前 Form 统一持有。\n const bridge: VueFormBridge<TValues> = {\n form,\n stateAdapter,\n values,\n touchedFields,\n pendingFields,\n loading,\n fieldBridges,\n refCount: 0,\n destroyed: false,\n unsubscribe,\n facade,\n }\n\n return bridge\n}\n","/**\n * 创建保持 Core API 的 Vue Facade。\n *\n * @module vue/bridge/formFacade\n */\n\nimport type { VueFieldBridge, VueFormBridge, VueSchemxInstance } from \"./types\"\nimport type { NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * 创建 Facade 所需的 Bridge 生命周期操作。\n *\n * @internal\n * @typeParam TValues - Form 的值类型。\n */\nexport interface VueFormFacadeDependencies<TValues extends Values> {\n /**\n * 获取指定 Form 的共享 Bridge。\n *\n * @param form - 要获取 Bridge 的 Core Form。\n * @returns 对应的共享 Form Bridge。\n */\n getFormBridge(form: SchemxInstance<TValues>): VueFormBridge<TValues>\n /**\n * 获取指定字段的共享 Bridge。\n *\n * @param bridge - 所属 Form Bridge。\n * @param name - 字段路径。\n * @returns 对应字段的 Vue Bridge。\n */\n getFieldBridge<TName extends NamePath<TValues>>(\n bridge: VueFormBridge<TValues>,\n name: TName\n ): VueFieldBridge<TValues, TName>\n /**\n * 查找已创建的 Form Bridge,但不因销毁动作创建新 Bridge。\n *\n * @param form - 要查找的 Core Form。\n * @returns 已缓存的 Bridge;不存在时返回 `undefined`。\n */\n getCachedFormBridge(form: SchemxInstance<TValues>): VueFormBridge<TValues> | undefined\n /**\n * 销毁指定的共享 Form Bridge。\n *\n * @param bridge - 要销毁的 Form Bridge。\n */\n disposeFormBridge(bridge: VueFormBridge<TValues>): void\n}\n\n/**\n * 为 Core Form 创建保持完整 API 的 Vue Facade。\n *\n * Facade 将字段和聚合状态读取转发给 Core,并在读取期间访问 Vue Ref,\n * 从而让当前 Vue effect 建立对应的响应式依赖。\n *\n * @typeParam TValues - Form 的值类型。\n * @param form - 要包装的原始 Core Form。\n * @param dependencies - 提供 Bridge 获取、字段读取和销毁能力的运行时依赖。\n * @param dependencies.getFormBridge - 获取共享 Form Bridge。\n * @param dependencies.getFieldBridge - 获取共享字段 Bridge。\n * @param dependencies.getCachedFormBridge - 查找已缓存的 Form Bridge。\n * @param dependencies.disposeFormBridge - 销毁共享 Form Bridge。\n * @returns 可被 Vue effect 追踪的 Facade。\n *\n * @example\n * ```ts\n * const facade = createVueFormFacade(form, {\n * getFormBridge,\n * getFieldBridge,\n * getCachedFormBridge,\n * disposeFormBridge,\n * })\n * ```\n */\nexport function createVueFormFacade<TValues extends Values>(\n form: SchemxInstance<TValues>,\n dependencies: VueFormFacadeDependencies<TValues>\n): VueSchemxInstance<TValues> {\n // Facade 销毁后不再访问 Bridge Ref,避免重新建立已释放的响应式依赖。\n let destroyed = false\n\n /**\n * 读取字段值,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取的字段路径。\n */\n const getFieldValue: SchemxInstance<TValues>[\"getFieldValue\"] = (name) => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void dependencies.getFieldBridge(bridge, name).value.value\n }\n\n return form.getFieldValue(name)\n }\n\n /**\n * 读取字段错误,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取错误的字段路径。\n */\n const getFieldErrors: SchemxInstance<TValues>[\"getFieldErrors\"] = (name) => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void dependencies.getFieldBridge(bridge, name).errors.value\n }\n\n return form.getFieldErrors(name)\n }\n\n /**\n * 读取字段 touched 状态,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取状态的字段路径。\n */\n const isFieldTouched: SchemxInstance<TValues>[\"isFieldTouched\"] = (name) => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void dependencies.getFieldBridge(bridge, name).touched.value\n }\n\n return form.isFieldTouched(name)\n }\n\n /**\n * 读取字段 pending 状态,并在未销毁时收集对应字段 Ref 的 Vue 依赖。\n *\n * @param name - 要读取状态的字段路径。\n */\n const isFieldPending: SchemxInstance<TValues>[\"isFieldPending\"] = (name) => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void dependencies.getFieldBridge(bridge, name).pending.value\n }\n\n return form.isFieldPending(name)\n }\n\n /**\n * 读取全部或指定字段值,并为读取范围收集 Vue 依赖。\n *\n * @param names - 可选的字段路径列表;省略时追踪完整表单值。\n */\n const getFieldsValue = ((names?: NamePath<TValues>[]) => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n if (names === undefined) {\n void bridge.values.value\n } else {\n for (const name of names) {\n void dependencies.getFieldBridge(bridge, name).value.value\n }\n }\n }\n\n return form.getFieldsValue(names)\n }) as SchemxInstance<TValues>[\"getFieldsValue\"]\n\n /**\n * 读取 touched 字段,并收集聚合 touched Ref 的 Vue 依赖。\n */\n const getTouchedFields: SchemxInstance<TValues>[\"getTouchedFields\"] = () => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void bridge.touchedFields.value\n }\n\n return form.getTouchedFields()\n }\n\n /**\n * 读取 pending 字段,并收集聚合 pending Ref 的 Vue 依赖。\n */\n const getPendingFields: SchemxInstance<TValues>[\"getPendingFields\"] = () => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void bridge.pendingFields.value\n }\n\n return form.getPendingFields()\n }\n\n /**\n * 读取提交状态,并在 Vue effect 中收集对应 Ref 依赖。\n */\n const isLoading: SchemxInstance<TValues>[\"isLoading\"] = () => {\n if (!destroyed) {\n const bridge = dependencies.getFormBridge(form)\n\n void bridge.loading.value\n }\n\n return form.isLoading()\n }\n\n /**\n * 销毁 Facade 关联的 Bridge,并继续销毁原始 Core Form。\n */\n const destroy: SchemxInstance<TValues>[\"destroy\"] = () => {\n if (destroyed) {\n return\n }\n\n destroyed = true\n const bridge = dependencies.getCachedFormBridge(form)\n\n if (bridge) {\n dependencies.disposeFormBridge(bridge)\n }\n\n form.destroy()\n }\n\n return {\n ...form,\n getFieldValue,\n getFieldErrors,\n isFieldTouched,\n isFieldPending,\n getFieldsValue,\n getTouchedFields,\n getPendingFields,\n isLoading,\n destroy,\n }\n}\n","/**\n * Vue App 级 Schemx 配置的 provide/inject 适配。\n *\n * 安装配置属于当前 Vue App,而不是 Core 模块级共享状态;该模块只在 Vue\n * 适配层内部保存和读取安装配置,不将注入 key 暴露为公共 API。\n *\n * @module utils/appConfig\n */\n\nimport { type App, getCurrentInstance, inject, type InjectionKey } from \"vue\"\n\nimport type { SchemxConfig, SchemxRendererPropsMap } from \"@schemx/core\"\n\n// Vue App 级配置在组件树中的私有注入 key。\nconst SCHEMX_APP_CONFIG_KEY: InjectionKey<SchemxConfig> = Symbol(\"schemx:app-config\")\n\n// 没有安装配置时使用的不可变空配置。\nconst EMPTY_SCHEMX_CONFIG: SchemxConfig = Object.freeze({\n schemaConfig: Object.freeze({}),\n validatorAdapters: Object.freeze([]),\n})\n\n/**\n * 将插件安装选项复制为可安全注入的 App 配置快照。\n *\n * Registry 保留其可变实例语义,使同一 App 内创建的多个 Form 继续共享注册结果;\n * Schema 配置和 adapter 列表则复制并冻结,避免安装后修改原对象影响 App 配置。\n *\n * @param config - 插件安装时传入的配置。\n * @returns 与调用方输入隔离的 App 配置快照。\n */\nfunction normalizeSchemxAppConfig(config: SchemxConfig): SchemxConfig {\n // 复制并冻结 App 级字段默认值,避免外部对象后续变更配置。\n const schemaConfig = Object.freeze({ ...(config.schemaConfig ?? {}) })\n\n // 复制并冻结 App 级 adapter 列表,保持列表与调用方数组相互独立。\n const validatorAdapters = Object.freeze([...(config.validatorAdapters ?? [])])\n\n return Object.freeze({\n schemaConfig,\n rendererProps: normalizeRendererProps(config.rendererProps),\n validatorAdapters,\n defaultRendererType: config.defaultRendererType,\n rendererRegistry: config.rendererRegistry,\n validationRuleRegistry: config.validationRuleRegistry,\n })\n}\n\n/**\n * 复制并冻结 Renderer Props Map 外层及每个 Renderer 的 Props 对象。\n *\n * @param source - 待标准化的 Renderer Props Map。\n * @returns 与输入隔离一层的只读 App 配置;任意嵌套 Prop 值仍保留原引用。\n */\nfunction normalizeRendererProps(\n source: SchemxRendererPropsMap | undefined\n): SchemxRendererPropsMap | undefined {\n if (source === undefined) {\n return undefined\n }\n\n // 每个 Renderer 的 Props 对象单独复制,避免安装后修改源对象影响当前 App。\n const entries = Object.entries(source).map(([type, props]) => {\n return [type, props === undefined ? undefined : Object.freeze({ ...props })]\n })\n\n return Object.freeze(Object.fromEntries(entries)) as SchemxRendererPropsMap\n}\n\n/**\n * 将 Schemx 安装配置绑定到指定 Vue App。\n *\n * @param app - 要安装 Schemx 的 Vue App 实例。\n * @param config - 当前 App 的默认配置。\n */\nexport function provideSchemxAppConfig(app: App, config: SchemxConfig = {}): void {\n // 当前 App 后续组件实例应读取的配置快照。\n const normalizedConfig = normalizeSchemxAppConfig(config)\n\n app.provide(SCHEMX_APP_CONFIG_KEY, normalizedConfig)\n}\n\n/**\n * 读取当前组件所属 Vue App 的 Schemx 安装配置。\n *\n * 在组件 setup() 之外调用时,Vue 没有可用的注入上下文;此时返回空配置,\n * 使 `useForm()` 仍可在独立 effect scope 中使用 Vue 模块级 Registry。\n *\n * @returns 当前 App 的安装配置,或无 App 上下文时的空配置。\n */\nexport function getSchemxAppConfig(): SchemxConfig {\n // 只有存在当前组件实例时才读取 Vue 注入上下文,避免 setup 外触发 Vue 警告。\n if (getCurrentInstance() === null) {\n return EMPTY_SCHEMX_CONFIG\n }\n\n // 当前组件树中最近的 App 级配置,未安装插件时回退到空配置。\n return inject(SCHEMX_APP_CONFIG_KEY, EMPTY_SCHEMX_CONFIG)\n}\n","/**\n * 全局渲染器注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * vant 等 UI 适配层通过 import 后调用 registerAll 注册渲染器,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rendererProvider\n */\n\nimport {\n createRendererRegistry,\n type RendererRegistry,\n} from \"@schemx/core/adapter\"\n\nimport type { SchemxRendererKey } from \"@schemx/core\"\n\n/**\n * 全局渲染器注册实例\n *\n * @example\n * ```ts\n * // UI 适配层注册渲染器\n * import { rendererRegistry } from '@schemx/vue'\n * rendererRegistry.registerAll({ input: InputRenderer, ... })\n *\n * // useForm 内部自动使用\n * // props 传入的 rendererRegistry 优先级更高\n * ```\n */\nexport const rendererRegistry: RendererRegistry<SchemxRendererKey> =\n createRendererRegistry<SchemxRendererKey>(\"input\")\n","/**\n * 全局校验规则注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * UI 适配层通过 import 后调用 register/registerAll 注册自定义规则,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rulesProvider\n */\n\nimport { createValidationRuleRegistry, type ValidationRuleRegistry } from \"@schemx/core\"\n\n/**\n * 全局校验规则注册实例\n *\n * @example\n * ```ts\n * // 注册自定义规则\n * import { validationRuleRegistry } from '@schemx/vue'\n * validationRuleRegistry.register('phone', phoneRule)\n *\n * // useForm 内部自动使用\n * // props 传入的 validationRuleRegistry 优先级更高\n * ```\n */\nexport const validationRuleRegistry: ValidationRuleRegistry =\n createValidationRuleRegistry()\n","/**\n * useForm - Schemx 表单实例的 Vue 生命周期适配。\n *\n * 负责创建由当前 Vue effect scope 持有的 SchemxInstance,并在 scope 销毁时\n * 自动释放实例。该模块不负责 provide/inject;表单上下文统一由\n * createFormContext() 注册,避免外部传入 form 时跳过上下文注册。\n *\n * @module hooks/useForm\n */\nimport { onScopeDispose } from \"vue\"\n\nimport { createForm, mergeSchemxConfig, type SchemxConfig } from \"@schemx/core\"\n\nimport {\n getVueFormBridge,\n getVueFormFacade,\n retainVueFormBridge,\n type VueSchemxInstance,\n} from \"../bridge\"\nimport { getSchemxAppConfig } from \"../config\"\nimport { rendererRegistry as globalRendererRegistry } from \"../utils/rendererProvider\"\nimport { validationRuleRegistry as globalValidationRuleRegistry } from \"../utils/rulesProvider\"\n\nimport type { CreateFormOptions, NamePath, Values } from \"@schemx/core\"\n\n/**\n * useForm 配置选项。\n *\n * 当前与 core 层 CreateFormOptions 保持一致,并在 Vue 层自动补充默认的\n * rendererRegistry 和 validationRuleRegistry。保留独立类型用于后续扩展\n * Vue 专属配置,而不污染 core 层接口。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface UseFormOptions<TValues extends Values> extends CreateFormOptions<\n TValues,\n NamePath<TValues>\n> {}\n\n/**\n * 创建由当前 Vue effect scope 持有的表单实例。\n *\n * useForm 只负责以下职责:\n * 1. 合并表单显式、App 安装和 Vue 层默认配置;\n * 2. 同步创建 SchemxInstance;\n * 3. 在当前 effect scope 销毁时调用 instance.destroy()。\n *\n * useForm 不再自动调用 provide()。需要向后代组件暴露实例时,应由\n * <SchemxForm> 或其他 Provider 组件显式调用 createFormContext(form)。\n * 这样无论实例是内部创建还是通过 props.form 外部传入,都能走同一条\n * 上下文注册路径,并保持清晰的实例所有权:谁创建,谁销毁。\n *\n * 该函数应在组件 setup() 或其他有效的 Vue effect scope 中同步调用。\n * 非 Vue 生命周期场景请直接使用 @schemx/core 的 createForm(),并自行销毁实例。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 表单创建配置\n * @returns 当前 scope 持有的 SchemxInstance\n *\n * @example\n * ```ts\n * const form = useForm({\n * initialValues: {\n * name: \"\",\n * email: \"\",\n * },\n * onFinish: async (values) => {\n * await api.submit(values)\n * },\n * })\n *\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n *\n * @example\n * ```ts\n * // Provider 组件中统一注册上下文。\n * const form = props.form ?? useForm(options)\n * createFormContext(form)\n * ```\n */\nexport function useForm<TValues extends Values = Values>(\n options: UseFormOptions<TValues> = {}\n): VueSchemxInstance<TValues> {\n // 按表单、App、Vue 包默认值的优先级解析可继承配置。\n const configuredOptions = mergeSchemxConfig<TValues>(\n getUseFormSchemxConfig(options),\n // App 安装配置以 Values 存储;在 useForm 边界关联到当前 TValues。\n getSchemxAppConfig() as SchemxConfig<TValues>,\n {\n rendererRegistry: globalRendererRegistry,\n validationRuleRegistry: globalValidationRuleRegistry,\n }\n )\n\n // 将已合并配置写入 Form 创建选项,避免 Core 再按较低优先级覆盖 Vue 结果。\n const mergedOptions: CreateFormOptions<TValues> = {\n ...options,\n ...configuredOptions,\n }\n\n // 表单实例是当前 scope 内的一次性资源,不需要使用 computed 包装。\n const instance = createForm<TValues>(mergedOptions)\n\n const form = getVueFormFacade(instance)\n\n const releaseBridge = retainVueFormBridge(getVueFormBridge(instance))\n\n // useForm 创建的实例归当前 effect scope 所有,因此由当前 scope 负责销毁。\n onScopeDispose(() => {\n releaseBridge()\n form.destroy()\n })\n\n return form\n}\n\n/**\n * 从 useForm 选项中提取需要参与 Vue 优先级计算的可继承配置。\n *\n * @param options - useForm 的完整创建选项。\n * @returns 仅包含调用方实际提供字段的 SchemxConfig。\n */\nfunction getUseFormSchemxConfig<TValues extends Values>(\n options: UseFormOptions<TValues>\n): SchemxConfig<TValues> {\n const {\n schemaConfig = {},\n rendererProps = undefined,\n validatorAdapters = [],\n defaultRendererType = undefined,\n rendererRegistry = undefined,\n validationRuleRegistry = undefined,\n } = options\n\n return {\n schemaConfig,\n rendererProps,\n defaultRendererType,\n rendererRegistry,\n validationRuleRegistry,\n validatorAdapters,\n }\n}\n","/**\n * Schemx 表单实例的 Vue provide/inject 适配。\n *\n * 负责在组件树中注册和读取 SchemxInstance,不负责创建或销毁实例。\n * 表单实例无论由 <SchemxForm> 内部创建,还是通过 form 属性从外部传入,\n * 都应由 <SchemxForm> 在 setup() 同步阶段调用 createFormContext() 注册。\n *\n * @module hooks/provideFormContext\n */\nimport { inject, type InjectionKey, onScopeDispose, provide } from \"vue\"\n\nimport {\n getVueFormBridge,\n getVueFormFacade,\n retainVueFormBridge,\n type VueSchemxInstance,\n} from \"../bridge\"\n\nimport type { SchemxInstance, Values } from \"@schemx/core\"\n\n// provide/inject 只传递运行时实例,不在此边界固定表单值泛型;\n// 否则 SchemxInstance 的方法参数会使具体 TValues 与默认 Values 不兼容。\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype FormContextInstance = VueSchemxInstance<any>\n\n/**\n * SchemxInstance 在 Vue provide/inject 中使用的注入 key。\n *\n * 该 key 只应与同一份包代码导出的 createFormContext() 和 useFormContext()\n * 配套使用;重复安装不兼容版本的包不会共享此上下文。\n */\nexport const SCHEMX_FORM_INSTANCE_KEY = Symbol(\"schemx:instance\") as InjectionKey<\n FormContextInstance\n>\n\n/**\n * 旧版表单实例注入 key。\n *\n * @deprecated 请使用 SCHEMX_FORM_INSTANCE_KEY。该别名仅用于兼容旧测试和旧适配代码。\n */\nexport const FORM_INSTANCE_KEY = SCHEMX_FORM_INSTANCE_KEY\n\n/**\n * 向当前组件的后代组件提供表单实例。\n *\n * 必须在组件 setup() 的同步执行阶段调用;不要放入 onMounted、nextTick、\n * Promise 或其他异步回调中,否则 Vue 无法把上下文关联到当前组件实例。\n *\n * @typeParam TValues - 表单值类型\n * @param instance - 要提供给后代组件的表单实例\n *\n * @remarks\n * 该函数不接管实例所有权,也不会在组件卸载时销毁实例。\n *\n * @example\n * ```ts\n * const form = props.form ?? useForm(options)\n *\n * // 无论 form 来自外部还是内部,都统一注册上下文。\n * createFormContext(form)\n * ```\n */\nexport function createFormContext<TValues extends Values = Values>(\n instance: SchemxInstance<TValues>\n): VueSchemxInstance<TValues> {\n const form = getVueFormFacade(instance)\n\n const releaseBridge = retainVueFormBridge(getVueFormBridge(form))\n\n provide<FormContextInstance>(SCHEMX_FORM_INSTANCE_KEY, form as FormContextInstance)\n onScopeDispose(releaseBridge)\n\n return form\n}\n\n/**\n * 获取最近祖先组件提供的表单实例。\n *\n * 该函数只能读取祖先组件注册的上下文,不能读取当前组件自身或后代组件\n * 调用 createFormContext() 提供的值。通常由 useField()、useWatch() 等其他\n * 表单 hook 间接调用。\n *\n * @typeParam TValues - 表单值类型\n * @returns 当前组件所属表单树中的 SchemxInstance\n *\n * @throws Error 当前组件不在已注册表单实例上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const form = useFormContext<FormValues>()\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n */\nexport function useFormContext<\n TValues extends Values = Values,\n>(): VueSchemxInstance<TValues> {\n const instance = inject<FormContextInstance | null>(SCHEMX_FORM_INSTANCE_KEY, null)\n\n if (!instance) {\n throw new Error(\n \"[schemx] useFormContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormContext(form) is called synchronously during setup().\"\n )\n }\n\n return instance as VueSchemxInstance<TValues>\n}\n","/**\n * useField - 字段控制 Hook。\n *\n * 复用共享 Vue Form Bridge 的字段 Ref,并将 Core 的字段写入、校验和规则操作\n * 保持在原始 `createField()` 实例上。\n *\n * @module hooks/useField\n */\n\nimport { computed } from \"vue\"\n\nimport { createField } from \"@schemx/core\"\n\nimport {\n getCoreForm,\n getVueFieldBridge,\n getVueFormBridge,\n type VueFieldBridge,\n type VueSchemxInstance,\n} from \"../bridge\"\n\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { FieldInstance } from \"../types/field\"\nimport type { NamePath, Values } from \"@schemx/core\"\n\n/**\n * 创建一份字段控制器,并将所有读取状态投影到共享 Vue Field Bridge。\n */\nfunction createFieldHook<\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(\n form: VueSchemxInstance<TValues>,\n name: TName,\n fieldBridge: VueFieldBridge<TValues, TName>\n): FieldInstance<TValues> {\n const coreForm = getCoreForm(form)\n\n const coreField = createField<TValues>(coreForm, name)\n\n const errors = computed(() => fieldBridge.errors.value)\n\n const dirty = computed(() => fieldBridge.touched.value)\n\n const pending = computed(() => fieldBridge.pending.value)\n\n const getValue = (): ReturnType<typeof coreField.getValue> => fieldBridge.value.value\n\n const getErrors = (): ReturnType<typeof coreField.getErrors> => fieldBridge.errors.value\n\n const isTouched = (): boolean => fieldBridge.touched.value\n\n const isPending = (): boolean => fieldBridge.pending.value\n\n const getValues = (): Readonly<TValues> => form.getFieldsValue()\n\n return {\n ...coreField,\n name,\n value: fieldBridge.value,\n errors,\n dirty,\n pending,\n getValue,\n getErrors,\n isTouched,\n isPending,\n getValues,\n }\n}\n\n/**\n * 获取单个字段的控制能力。\n *\n * @param name - 字段路径(支持嵌套字段)。\n * @returns 包含 Vue Ref 状态和 Core 字段操作的控制器。\n */\nexport const useField = <TValues extends Values = Values>(\n name: NamePath<TValues>\n): FieldInstance<TValues> => {\n const form = useFormContext<TValues>()\n\n const coreForm = getCoreForm(form)\n\n const formBridge = getVueFormBridge(coreForm)\n\n const fieldBridge = getVueFieldBridge(formBridge, name)\n\n return createFieldHook(form, name, fieldBridge)\n}\n\nexport default useField\n","import { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { FieldInstance } from \"../types/field\"\nimport type { Values } from \"@schemx/core\"\n\n/** 当前字段实例在 Vue provide/inject 中使用的注入 key。 */\nconst SCHEMX_FORM_FIELD_KEY: InjectionKey<FieldInstance> = Symbol(\n \"schemx:field\"\n) as InjectionKey<FieldInstance<Values>>\n\n/**\n * 向后代组件提供当前字段实例。\n *\n * 应在创建字段实例的组件 setup() 同步阶段调用,通常由 FormItem\n * 在调用 useField() 后使用,使字段插槽或自定义子组件能够读取同一实例。\n *\n * @param field - useField() 返回的字段实例\n *\n * @remarks\n * 该函数只注册字段上下文,不创建字段实例,也不负责字段实例的生命周期。\n *\n * @example\n * ```typescript\n * // FormItem 中\n * const field = useField('date')\n * createFieldContext(field)\n *\n * ```\n */\nexport function createFieldContext<TValues extends Values = Values>(\n field: FieldInstance<TValues>\n): void {\n provide<FieldInstance<TValues>>(SCHEMX_FORM_FIELD_KEY, field)\n}\n\n/**\n * 获取最近祖先组件提供的当前字段实例。\n *\n * 必须在已调用 createFieldContext() 的组件子树内调用,否则抛出错误。\n *\n * @returns useField() 返回的字段实例\n *\n * @throws Error 当前组件不在字段上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const field = useFieldContext()\n * field.errors.value // readonly string[]\n * field.getValue() // 当前字段值\n * ```\n */\nexport function useFieldContext(): FieldInstance<Values> {\n const field = inject<FieldInstance<Values>>(SCHEMX_FORM_FIELD_KEY)\n\n if (!field) {\n throw new Error(\n \"[schemx] useFieldContext() must be called inside a component tree where \" +\n \"createFieldContext(field) has been called.\"\n )\n }\n\n return field\n}\n","/**\n * useWatch - 监听字段变化(Vue 组合式 API 版本)\n *\n * 基于 core 层的 {@link createWatch} 实现,\n * 自动通过 `useFormContext` 获取 formContext,\n * 并在 `onUnmounted` 时自动取消监听,无需手动管理生命周期。\n *\n * 提供三种监听模式:\n * - 全局监听:`useWatch(callback, options?)` — 监听表单中任意字段变化\n * - 单字段监听:`useWatch(name, callback, options?)` — 监听指定字段变化\n * - 多字段监听:`useWatch(names, callback, options?)` — 监听一组字段变化\n *\n * 同时提供语义化的便捷方法:\n * - {@link useWatchField} — 单字段监听\n * - {@link useWatchFields} — 多字段监听\n * - {@link useWatchAll} — 全局监听\n *\n * @module hooks/useWatch\n *\n * @example\n * ```ts\n * // 全局监听\n * useWatch((snapshot, payload) => {\n * console.log('form changed:', snapshot, payload)\n * })\n *\n * // 单字段监听\n * useWatch('username', (current, prev) => {\n * console.log('username changed:', prev, '->', current)\n * }, { immediate: true })\n *\n * // 多字段监听\n * useWatch(['firstName', 'lastName'], (currentValues, prevValues) => {\n * console.log('name changed:', currentValues)\n * }, { inequality: true })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createWatch } from \"@schemx/core\"\n\nimport { getCoreForm } from \"../bridge\"\n\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\nimport type {\n CreateWatchOptions,\n WatchAllCallback,\n WatchFieldCallback,\n WatchFieldsCallback,\n} from \"@schemx/core\"\n\n/**\n * 监听所有字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * const dispose = useWatch((values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues, values)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n callback: WatchAllCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听单个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param name - 字段路径\n * @param callback - 单字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch(\"email\", (_values, payload) => {\n * console.log(payload.prevValue, payload.value)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听多个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 字段路径数组\n * @param callback - 多字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch([\"firstName\", \"lastName\"], (_values, payload) => {\n * console.log(payload.changedPaths, payload.changedValues, payload.prevValues)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * useWatch 实现 — 获取 form 实例后委托给 core 层的 createWatch\n *\n * @typeParam TValues - 表单值类型。\n */\nexport function useWatch<TValues extends Values>(\n nameOrNamesOrCallback:\n NamePath<TValues> | NamePath<TValues>[] | WatchAllCallback<TValues>,\n callbackOrOptions?:\n WatchFieldCallback<TValues> | WatchFieldsCallback<TValues> | CreateWatchOptions,\n maybeOptions?: CreateWatchOptions\n): () => void {\n const form = useFormContext<TValues>()\n\n const dispose = (createWatch as any)(\n getCoreForm(form),\n nameOrNamesOrCallback,\n callbackOrOptions,\n maybeOptions\n )\n\n onUnmounted(dispose)\n\n return dispose\n}\n\n/**\n * 监听单个字段变化的便捷方法\n *\n * 等价于 `useWatch(name, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param name - 要监听的字段路径\n * @param callback - 字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchField(\"email\", (_values, payload) => {\n * const { value, prevValue } = payload\n * console.log(prevValue, value)\n * })\n * ```\n */\nexport function useWatchField<TValues extends Values = Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(name, callback, options)\n}\n\n/**\n * 监听多个字段变化的便捷方法\n *\n * 等价于 `useWatch(names, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 要监听的字段路径数组\n * @param callback - 字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchFields([\"firstName\", \"lastName\"], (_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchFields<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(names, callback, options)\n}\n\n/**\n * 监听所有字段变化的便捷方法\n *\n * 等价于 `useWatch(callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchAll((_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchAll<TValues extends Values = Values>(\n callback: WatchAllCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch(callback, options)\n}\n\nexport default useWatch\n","/**\n * useDictionary - 字典选项 Hook\n *\n * 通过 api 函数加载字典选项,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持通过 dependsOn 声明依赖字段,\n * 当依赖字段变化时自动重新请求。\n *\n * @module hooks/useDictionary\n */\n\nimport { onMounted, Ref, ref, shallowRef } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFormContext } from \"./provideFormContext\"\nimport { useWatchFields } from \"./useWatch\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\n\nexport type { SchemxDictionary, SchemxWithDictionary } from \"@/types/dictionary\"\n\n/**\n * useDictionary 返回值\n */\nexport interface UseDictionaryReturn<TOption = unknown> {\n /** 远程加载的字典选项列表(响应式) */\n list: Ref<TOption[]>\n /** 请求加载状态(响应式) */\n loading: Ref<boolean>\n /** 请求错误信息(响应式) */\n error: Ref<Error | undefined>\n /** 触发字典选项加载 */\n loadDict: () => Promise<void>\n /** 使用当前配置重新执行 api */\n refresh: () => Promise<void>\n /** 直接修改 list 的值,不触发 api 调用 */\n mutate: (data: TOption[]) => void\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **UseDictionaryReturn**\n */\nexport type UseDictOptionsReturn<TOption = unknown> = UseDictionaryReturn<TOption>\n\n/**\n * 将未知抛出值规范化为 `Error` 实例\n *\n * @param err - 捕获的值(可能是任意类型)\n * @returns 包装原始值的 `Error` 对象\n */\nexport function normalizeError(err: unknown): Error {\n if (err instanceof Error) return err\n\n return new Error(String(err))\n}\n\n/**\n * 加载字典选项\n *\n * 通过 api 函数获取数据,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持依赖字段联动、竞态控制、\n * 重试、错误处理等能力。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 字典配置选项\n * @param fieldName - 当前字段名,用于 resetOnDepsChange 时清空字段值\n * @returns `{ list, loading, error, loadDict, refresh, mutate }`:响应式选项、\n * 加载与错误状态,以及加载、刷新和本地更新方法\n *\n * @example\n * ```ts\n * // 基础用法\n * const { list, loading, error, loadDict, refresh, mutate } = useDictionary({\n * api: async () => {\n * const res = await fetch('/api/options')\n * return res.json()\n * },\n * })\n *\n * await loadDict() // 主动加载\n * await refresh() // 使用当前配置重新加载\n * mutate([{ label: \"手动选项\", value: \"manual\" }])\n * console.log(list.value, loading.value, error.value)\n *\n * // 依赖联动(带泛型)\n * const { list } = useDictionary<MyFormValues>({\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * }, 'city')\n * ```\n */\nexport const useDictionary = <\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n TResponse = unknown,\n TOption = unknown,\n>(\n options: SchemxDictionary<TValues, TResponse, TOption>,\n fieldName?: TName\n): UseDictionaryReturn<TOption> => {\n const instance = useFormContext<TValues>()\n\n const list = shallowRef<TOption[]>([])\n\n const loading = ref<boolean>(false)\n\n const error = ref<Error | undefined>(undefined)\n\n // 竞态控制:仅最新请求写入状态\n let requestCount = 0\n\n /**\n * 使用配置的 formatter 格式化原始响应数据\n */\n const format = async (res: Awaited<TResponse>): Promise<TOption[]> => {\n if (typeof options?.formatter === \"function\") {\n return await options.formatter(res, instance)\n }\n\n if (!Array.isArray(res)) {\n throw new Error(\n \"[schemx] Dictionary api must return an array when formatter is not provided.\"\n )\n }\n\n return res as TOption[]\n }\n\n /**\n * 带重试的执行\n */\n const executeWithRetry = async (formValues: TValues): Promise<Awaited<TResponse>> => {\n const maxRetries = options.retryCount ?? 0\n\n const retryDelay = options.retryInterval ?? 1000\n\n let lastError: Error = new Error(\"Unknown error\")\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await options.api(formValues, instance)\n } catch (err) {\n lastError = normalizeError(err)\n\n if (attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, retryDelay))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 执行 api 函数加载字典选项\n */\n const loadDict = async (): Promise<void> => {\n try {\n const formValues = instance.getFieldsValue()\n\n // shouldFetch 检查\n if (typeof options.shouldFetch === \"function\" && !options.shouldFetch(formValues)) {\n list.value = []\n loading.value = false\n\n return\n }\n\n loading.value = true\n error.value = undefined\n\n // 递增请求计数器用于竞态控制\n const currentCount = ++requestCount\n\n const res = await executeWithRetry(formValues)\n\n // 竞态检查:丢弃过期响应\n if (currentCount !== requestCount) return\n\n const formatted = await format(res)\n\n // 格式化后再次竞态检查(格式化可能是异步的)\n if (currentCount !== requestCount) return\n\n list.value = formatted\n error.value = undefined\n\n // onSuccess 回调\n if (typeof options.onSuccess === \"function\") {\n options.onSuccess(formatted, instance)\n }\n\n loading.value = false\n } catch (err) {\n const normalized = normalizeError(err)\n\n error.value = normalized\n list.value = []\n loading.value = false\n\n if (typeof options.onError === \"function\") {\n options.onError(normalized, instance)\n }\n }\n }\n\n const refresh = (): Promise<void> => loadDict()\n\n const mutate = (data: TOption[]): void => {\n list.value = data\n }\n\n // ========== 依赖字段监听 ==========\n if (options.dependsOn?.length) {\n useWatchFields(options.dependsOn, (latestSnapshot, _payload) => {\n // 1. 触发 onDepsChange 回调\n if (typeof options.onDepsChange === \"function\") {\n options.onDepsChange(latestSnapshot as TValues, instance)\n }\n\n // 2. 清空当前字段值\n if (options.resetOnDepsChange && fieldName) {\n instance.setFieldValue(fieldName, undefined)\n }\n\n // 3. 重新执行 api\n void loadDict()\n })\n }\n\n // ========== 挂载行为 ==========\n const immediate = options.immediate ?? true\n\n onMounted(() => {\n if (immediate) {\n void loadDict()\n }\n })\n\n return { list, loading, error, loadDict, refresh, mutate }\n}\n\nexport default useDictionary\n","/**\n * 表单级配置的 Vue provide/inject 适配。\n *\n * `SchemxForm` 通过 createFormConfigContext() 提供默认展示配置,\n * 字段组件通过 useFormConfigContext() 读取这些配置。\n *\n * @module hooks/provideFormConfigContext\n */\n\nimport { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { SchemxSchemaConfig } from \"@schemx/core\"\n\n/** 表单级展示配置在 Vue provide/inject 中使用的注入 key。 */\nexport const SCHEMX_FORM_CONFIG_KEY = Symbol(\n \"schemx:form-config\"\n) as InjectionKey<FormContextProps>\n\n/**\n * 表单级默认配置。\n *\n * 该类型只描述 Schema 通用配置,不包含表单实例、表单数据、回调、样式或 Registry。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface FormContextProps {\n schemaConfig: Partial<SchemxSchemaConfig>\n}\n\n/**\n * 创建并注入表单级配置上下文。\n *\n * 应在 `SchemxForm` 或自定义 Provider 的 setup() 同步阶段调用,\n * 使后代字段组件能够读取 readonly、disabled、labelAlign 等默认配置。\n *\n * @param props - 要提供给后代组件的 Schema 配置\n *\n * @remarks\n * 该函数只注册上下文,不创建或销毁表单实例,也不改变传入配置的所有权。\n *\n * @example\n * ```ts\n * createFormConfigContext({ schemaConfig: { readonly: true, labelAlign: \"right\" } })\n * ```\n */\nexport const createFormConfigContext = (props: FormContextProps): void => {\n provide<FormContextProps>(SCHEMX_FORM_CONFIG_KEY, props)\n}\n\n/**\n * `createFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 createFormConfigContext。\n *\n * @example\n * ```ts\n * createContext({ schemaConfig: { disabled: true } })\n * ```\n */\nexport const createContext = createFormConfigContext\n\n/**\n * 获取最近祖先组件提供的表单级配置。\n *\n * 只能读取祖先组件通过 createFormConfigContext() 注册的配置,\n * 不能读取当前组件或后代组件提供的值。\n *\n * @returns 当前表单树中的表单级默认配置\n *\n * @throws Error 当前组件不在已注册表单配置上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const context = useFormConfigContext()\n * console.log(context.schemaConfig.readonly, context.schemaConfig.disabled)\n * ```\n */\nexport function useFormConfigContext(): FormContextProps {\n const context = inject<FormContextProps>(SCHEMX_FORM_CONFIG_KEY)\n\n if (!context) {\n throw new Error(\n \"[schemx] useFormConfigContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormConfigContext(props) is called synchronously during setup().\"\n )\n }\n\n return context\n}\n\n/**\n * `useFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 useFormConfigContext。\n *\n * @example\n * ```ts\n * const context = useContext()\n * ```\n */\nexport const useContext = useFormConfigContext\n","/**\n * useStableRef - 引用稳定化的 shallowRef\n *\n * 封装 `shallowRef` + `watchEffect` + `isShallowEqual`,\n * 仅在工厂函数返回值与当前值浅不相等时才更新引用,\n * 避免下游组件因对象引用变化而不必要地重渲染。\n *\n * @module hooks/useStableRef\n *\n * @example\n * ```ts\n * import { useStableRef } from '@schemx/vue'\n *\n * const stableProps = useStableRef(() => ({\n * visible: visible.value,\n * disabled: disabled.value,\n * value: field.getValue(),\n * }))\n * ```\n */\n\nimport { type ShallowRef, shallowRef, watchEffect } from \"vue\"\n\nimport { isShallowEqual } from \"../utils/equal\"\n\n/**\n * 创建一个引用稳定的 shallowRef。\n *\n * 在 watchEffect 中执行工厂函数,自动追踪响应式依赖。\n * 仅当新旧值浅比较不相等时才替换 `.value`,保持引用稳定。\n *\n * @typeParam TValue - 需要保持引用稳定的对象类型\n *\n * @param factory - 返回目标对象的工厂函数,在 watchEffect 内执行\n *\n * @returns 只读的 ShallowRef,引用仅在属性值真正变化时更新\n *\n * @example\n * ```ts\n * const props = useStableRef(() => ({\n * value: field.getValue(),\n * onChange: handleChange,\n * }))\n *\n * // props.value 的引用在属性值未变时保持不变\n * h(Component, props.value)\n * ```\n */\nexport function useStableRef<TValue extends Record<string, any>>(\n factory: () => TValue\n): Readonly<ShallowRef<TValue>> {\n const stableRef = shallowRef({} as TValue) as ShallowRef<TValue>\n\n watchEffect(() => {\n const next = factory()\n\n if (!isShallowEqual(stableRef.value, next)) {\n stableRef.value = next\n }\n })\n\n return stableRef\n}\n\nexport default useStableRef\n","/**\n * 对象浅比较工具\n *\n * @module utils/equal\n */\n\n/**\n * 浅比较两个对象的自有属性值是否相同。\n *\n * 用于避免 computed / shallowRef 每次返回新对象引用导致子组件不必要的更新。\n * 仅比较第一层属性,函数引用通过 === 判断。\n *\n * @param a - 旧对象\n * @param b - 新对象\n *\n * @returns 两个对象浅相等时返回 true\n *\n * @example\n * shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // => true\n * shallowEqual({ a: 1 }, { a: 1, b: 2 }) // => false\n */\nexport const isShallowEqual = (\n a: Record<string, any>,\n b: Record<string, any>\n): boolean => {\n const keysA = Object.keys(a)\n\n const keysB = Object.keys(b)\n\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => a[key] === b[key])\n}\n","/**\n * useViewSchemas - Vue ViewSchemas bridge.\n *\n * @module hooks/useViewSchemas\n */\n\nimport { computed, onScopeDispose, shallowRef } from \"vue\"\nimport type { ComputedRef, ShallowRef } from \"vue\"\n\nimport {\n isViewGroupSchema,\n type SchemxInstance,\n type SchemxViewSchema,\n type Values,\n} from \"@schemx/core\"\n\nimport { getCoreForm } from \"../bridge\"\n\n/**\n * 同一表单内共享的 ViewSchema 响应式桥接。\n */\ninterface ViewSchemaBridge<TValues extends Values = Values> {\n /**\n * 当前桥接的使用者注册数;最后一个使用者释放后才取消订阅。\n */\n refCount: number\n /**\n * 按 schema key 建立的共享索引。\n */\n schemasByKey: ComputedRef<ReadonlyMap<string, SchemxViewSchema<TValues>>>\n /**\n * 取消 Core ViewSchema 订阅。\n */\n unsubscribe: () => void\n /**\n * 最新的 ViewSchema 列表。\n */\n viewSchemas: ShallowRef<readonly SchemxViewSchema<TValues>[]>\n}\n\n/**\n * 按表单实例复用订阅,避免每个 FormItem 重复桥接同一份 ViewSchema。\n */\nconst viewSchemaBridgeCache = new WeakMap<object, ViewSchemaBridge>()\n\n/**\n * 将表单 ViewSchema 映射为共享的 Vue 响应式引用。\n *\n * 当前作用域释放一个桥接使用者;所有使用者都释放后才取消 Core 订阅。\n *\n * @typeParam TValues - 表单值类型。\n * @param form - 要订阅的表单实例。\n * @returns 当前 ViewSchema 列表的共享响应式浅引用;调用方不应直接改写其值。\n *\n * @example\n * ```ts\n * const form = useForm()\n * const viewSchemas = useViewSchemas(form)\n *\n * watchEffect(() => console.log(viewSchemas.value))\n * ```\n */\nexport function useViewSchemas<TValues extends Values = Values>(\n form: SchemxInstance<TValues>\n): ShallowRef<readonly SchemxViewSchema<TValues>[]> {\n const bridge = acquireViewSchemaBridge(getCoreForm(form))\n\n return bridge.viewSchemas\n}\n\n/**\n * 获取指定 key 的响应式 ViewSchema,并复用当前表单的共享订阅。\n *\n * @typeParam TValues - 表单值类型。\n * @param form - 要读取的表单实例。\n * @param getKey - 返回当前 schema key 的响应式 getter。\n * @returns 对应的最新 ViewSchema;不存在时返回 undefined。\n *\n * @example\n * ```ts\n * const schema = useViewSchema(form, () => fieldKey)\n *\n * watchEffect(() => {\n * console.log(schema.value?.label)\n * })\n * ```\n */\nexport function useViewSchema<TValues extends Values = Values>(\n form: SchemxInstance<TValues>,\n getKey: () => string\n): ComputedRef<SchemxViewSchema<TValues> | undefined> {\n const bridge = acquireViewSchemaBridge(getCoreForm(form))\n\n return computed(() => bridge.schemasByKey.value.get(getKey()))\n}\n\n/**\n * 创建一个表单级 ViewSchema 桥接。\n *\n * @param form - 用于提供 ViewSchema 和订阅更新的 Core 表单实例。\n */\nfunction createViewSchemaBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): ViewSchemaBridge<TValues> {\n const viewSchemas = shallowRef<readonly SchemxViewSchema<TValues>[]>(\n form.getViewSchemas()\n )\n\n const schemasByKey = computed<ReadonlyMap<string, SchemxViewSchema<TValues>>>(() => {\n return createViewSchemaIndex(viewSchemas.value)\n })\n\n const unsubscribe = form.subscribeViewSchemas(\n /**\n * 接收 Core 发布的完整列表并替换共享引用,以保留更新边界。\n *\n * @param nextSchemas - Core 计算出的最新 ViewSchema 列表。\n */\n (nextSchemas) => {\n viewSchemas.value = nextSchemas\n }\n )\n\n return {\n refCount: 0,\n schemasByKey,\n unsubscribe,\n viewSchemas,\n }\n}\n\n/**\n * 获取共享桥接,并把使用权绑定到当前 Vue effect scope。\n *\n * @param form - 用于查找或创建共享桥接的 Core 表单实例。\n */\nfunction acquireViewSchemaBridge<TValues extends Values>(\n form: SchemxInstance<TValues>\n): ViewSchemaBridge<TValues> {\n const cachedBridge = viewSchemaBridgeCache.get(form) as\n ViewSchemaBridge<TValues> | undefined\n\n const bridge = cachedBridge ?? createViewSchemaBridge(form)\n\n if (!cachedBridge) {\n viewSchemaBridgeCache.set(form, bridge as ViewSchemaBridge)\n }\n\n bridge.refCount++\n\n onScopeDispose(() => {\n bridge.refCount--\n\n if (bridge.refCount > 0) {\n return\n }\n\n bridge.unsubscribe()\n viewSchemaBridgeCache.delete(form)\n })\n\n return bridge\n}\n\n/**\n * 递归索引顶层和分组内的 ViewSchema,供任意 FormItem 按 key 查询。\n *\n * @param viewSchemas - 待索引的顶层 ViewSchema 列表。\n * @returns 按 schema key 建立的只读索引。\n */\nfunction createViewSchemaIndex<TValues extends Values>(\n viewSchemas: readonly SchemxViewSchema<TValues>[]\n): ReadonlyMap<string, SchemxViewSchema<TValues>> {\n const schemasByKey = new Map<string, SchemxViewSchema<TValues>>()\n\n /**\n * 将当前层及其分组子节点追加到索引。\n *\n * @param schemas - 当前层待追加的 ViewSchema 列表。\n */\n const appendSchemas = (schemas: readonly SchemxViewSchema<TValues>[]): void => {\n for (const schema of schemas) {\n schemasByKey.set(schema.key, schema)\n\n if (isViewGroupSchema(schema)) {\n appendSchemas(schema.children)\n }\n }\n }\n\n appendSchemas(viewSchemas)\n\n return schemasByKey\n}\n\nexport default useViewSchemas\n","/**\n * useFormSelector - Core 表单值的 Vue 响应式选择器桥接。\n *\n * @module hooks/useFormSelector\n */\n\nimport { onScopeDispose, readonly, shallowRef, watch } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\nimport { getCoreForm, getVueFormBridge, retainVueFormBridge } from \"../bridge\"\n\nimport type { SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * `useFormSelector` 的选中结果比较配置。\n */\nexport interface UseFormSelectorOptions<TSelected> {\n /**\n * 判断两次 selector 结果是否等价;返回 true 时保持当前 Ref 值。\n * 默认使用 `Object.is`。\n */\n equals?: (previous: TSelected, next: TSelected) => boolean\n}\n\n/**\n * 将 Core 表单值按 selector 映射为 Vue 只读浅引用。\n *\n * selector 只应读取快照,不应通过快照修改表单值或调用表单写入方法。\n * `Readonly<TValues>` 仅表达顶层只读,嵌套字段应按不可变值使用;字段值需通过\n * `setFieldValue()` 或 `setFieldsValue()` 更新,才能触发后续计算。\n *\n * @typeParam TValues - 表单值类型。\n * @typeParam TSelected - selector 返回值类型。\n * @param form - 要订阅的表单实例。\n * @param selector - 从当前表单值快照派生结果的纯函数。\n * @param options - 选中结果的比较配置。\n * @returns selector 当前结果的 Vue 只读浅引用。\n *\n * @example\n * ```ts\n * const selectedName = useFormSelector(form, (values) => values.name)\n *\n * watchEffect(() => {\n * console.log(selectedName.value)\n * })\n * ```\n */\nexport function useFormSelector<\n TValues extends Values = Values,\n TSelected = unknown,\n>(\n form: SchemxInstance<TValues>,\n selector: (values: Readonly<TValues>) => TSelected,\n options: UseFormSelectorOptions<TSelected> = {}\n): Readonly<ShallowRef<TSelected>> {\n const bridge = getVueFormBridge(getCoreForm(form))\n\n const releaseBridge = retainVueFormBridge(bridge)\n\n const selected = shallowRef<TSelected>(selector(bridge.values.value))\n\n const equals = options.equals ?? Object.is\n\n /**\n * 根据完整快照重新计算 selector,并只在结果变化时更新 Ref。\n *\n * @param values - Bridge 发布的最新完整表单快照。\n */\n const stop = watch(\n bridge.values,\n (values) => {\n const next = selector(values)\n\n if (!equals(selected.value, next)) {\n selected.value = next\n }\n },\n { flush: \"sync\" }\n )\n\n onScopeDispose(() => {\n stop()\n releaseBridge()\n })\n\n return readonly(selected) as Readonly<ShallowRef<TSelected>>\n}\n\nexport default useFormSelector\n","/**\n * 校验触发工具\n *\n * 提供校验触发时机的合并、归一化和匹配判断功能。\n *\n * @module utils/validation\n */\n\nimport type { ValidationTrigger } from \"@schemx/core\"\n\n/** 触发时机类型(支持单个或数组) */\nexport type TriggerConfig = ValidationTrigger | ValidationTrigger[]\n\n/** 归一化后的触发类型 */\nexport type NormalizedTrigger = \"blur\" | \"change\" | \"submit\"\n\n/**\n * 判断触发时机配置是否有效。\n *\n * undefined 和空数组视为无效,其余值(包括单个字符串、非空数组)视为有效。\n *\n * @param v - 触发时机配置\n *\n * @returns 配置有效时返回 true\n */\nfunction isValidTrigger(v: TriggerConfig | undefined): v is TriggerConfig {\n if (v === undefined) return false\n if (Array.isArray(v) && v.length === 0) return false\n\n return true\n}\n\n/**\n * 合并校验触发时机配置。\n *\n * 按优先级从高到低取值:列级配置 > 表单上下文配置 > 默认值。\n * 跳过 undefined 和空数组,确保空数组不会意外覆盖后续配置。\n *\n * @param columnTrigger - 列级配置的触发时机\n * @param contextTrigger - 表单上下文配置的触发时机\n * @param defaultTrigger - 兜底默认值\n *\n * @returns 最终生效的触发时机\n *\n * @example\n * mergeTrigger(\"onBlur\", \"onChange\", \"onSubmit\") // => \"onBlur\"\n * mergeTrigger(undefined, \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger([], \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger(undefined, undefined, \"onSubmit\") // => \"onSubmit\"\n */\nexport function mergeTrigger(\n columnTrigger: TriggerConfig | undefined,\n contextTrigger: TriggerConfig | undefined,\n defaultTrigger: TriggerConfig\n): TriggerConfig {\n if (isValidTrigger(columnTrigger)) return columnTrigger\n\n if (isValidTrigger(contextTrigger)) return contextTrigger\n\n return defaultTrigger\n}\n\n/**\n * 归一化触发类型字符串。\n *\n * 将带 `on` 前缀的格式统一为短格式:\n * `\"onBlur\"` → `\"blur\"`、`\"onChange\"` → `\"change\"`、`\"onSubmit\"` → `\"submit\"`。\n *\n * @param t - 原始触发类型\n *\n * @returns 归一化后的触发类型\n */\nfunction normalizeTrigger(t: ValidationTrigger): NormalizedTrigger {\n const map: Record<ValidationTrigger, NormalizedTrigger> = {\n onBlur: \"blur\",\n onChange: \"change\",\n onSubmit: \"submit\",\n blur: \"blur\",\n change: \"change\",\n submit: \"submit\",\n }\n\n return map[t] ?? \"submit\"\n}\n\n/**\n * 判断当前事件是否应该触发校验。\n *\n * 将配置的触发时机归一化后,与当前事件类型进行匹配。\n *\n * @param event - 当前触发的事件类型\n * @param trigger - 配置的触发时机(支持单个或数组)\n *\n * @returns 是否应该触发校验\n *\n * @example\n * shouldValidateOn(\"change\", \"onChange\") // => true\n * shouldValidateOn(\"blur\", [\"onBlur\", \"onChange\"]) // => true\n * shouldValidateOn(\"change\", \"onSubmit\") // => false\n * shouldValidateOn(\"change\", undefined) // => false\n */\nexport function shouldValidateOn(\n event: NormalizedTrigger,\n trigger?: TriggerConfig\n): boolean {\n if (!trigger) return false\n const triggers = Array.isArray(trigger) ? trigger : [trigger]\n\n return triggers.some((t) => normalizeTrigger(t) === event)\n}\n","/**\n * 插槽解析与子插槽提取工具。\n *\n * 提供 camelCase / kebab-case 双向查找插槽,\n * 以及按字段名前缀提取子渲染器插槽。\n *\n * @module utils/slot\n */\n\n/**\n * 检查字符串是否包含大写字母(驼峰格式特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含大写字母\n */\nconst isCamelCase = (str: string): boolean => /[A-Z]/.test(str)\n\n/**\n * 检查字符串是否包含连字符(kebab-case 特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含连字符\n */\nconst isKebabCase = (str: string): boolean => str.includes(\"-\")\n\n/**\n * 将驼峰命名转换为连字符命名(kebab-case)。\n *\n * @param str - 驼峰格式的字符串\n * @returns 转换后的 kebab-case 字符串\n */\nconst camelToKebab = (str: string): string => {\n return str.replace(/([A-Z])/g, \"-$1\").toLowerCase()\n}\n\n/**\n * 将连字符命名转换为驼峰命名(camelCase)。\n *\n * @param str - kebab-case 格式的字符串\n * @returns 转换后的 camelCase 字符串\n */\nconst kebabToCamel = (str: string): string => {\n return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())\n}\n\n/**\n * 将命名格式归一化为 kebab-case。\n *\n * @param str - 任意命名格式的字符串\n * @returns kebab-case 格式的字符串\n */\nconst normalizeToKebab = (str: string): string => {\n if (isCamelCase(str)) return camelToKebab(str)\n\n return str\n}\n\n/**\n * 将命名格式归一化为 camelCase。\n *\n * @param str - 任意命名格式的字符串\n * @returns camelCase 格式的字符串\n */\nconst normalizeToCamel = (str: string): string => {\n if (isKebabCase(str)) return kebabToCamel(str)\n\n return str\n}\n\n/**\n * 从插槽对象中按名称查找插槽,同时支持 camelCase 和 kebab-case。\n *\n * 优先查找原始名称,未找到时尝试另一种命名格式。\n * 例如传入 `userName` 时,先查 `userName`,再查 `user-name`;\n * 传入 `user-name` 时,先查 `user-name`,再查 `userName`。\n *\n * @param slots - Vue 插槽对象\n * @param name - 插槽名称(camelCase 或 kebab-case)\n *\n * @returns 匹配的插槽函数,未找到返回 undefined\n *\n * @example\n * ```typescript\n * // slots 中定义了 'user-name' 插槽\n * resolveSlot(slots, 'userName') // => 找到 'user-name' 插槽\n * resolveSlot(slots, 'user-name') // => 找到 'user-name' 插槽\n * ```\n */\nexport const resolveSlot = (\n slots: Record<string, any>,\n name: string\n): ((...args: any[]) => any) | undefined => {\n if (slots[name]) return slots[name]\n\n const alt = isCamelCase(name)\n ? camelToKebab(name)\n : isKebabCase(name)\n ? kebabToCamel(name)\n : undefined\n\n if (alt && slots[alt]) return slots[alt]\n\n return undefined\n}\n\n/**\n * 从父级插槽中提取子渲染器插槽。\n *\n * 按 `fieldName:slotName` 和 `fieldName:slot-name` 格式匹配,\n * 提取后去掉前缀作为子渲染器的插槽名。\n * 同时支持 camelCase 和 kebab-case 的 fieldName。\n *\n * @param fieldName - 字段名\n * @param allSlots - 父级全部插槽\n *\n * @returns 子渲染器的插槽对象\n *\n * @example\n * ```typescript\n * // 父级插槽: { 'userName:prefix': fn, 'userName:suffix': fn, 'userNameLabel': fn }\n * extractChildSlots('userName', slots)\n * // => { prefix: fn, suffix: fn }\n * ```\n */\nexport const extractChildSlots = (\n fieldName: string,\n allSlots: Record<string, any>\n): Record<string, any> => {\n const result: Record<string, any> = {}\n\n const camelPrefix = normalizeToCamel(String(fieldName)) + \":\"\n\n const kebabPrefix = normalizeToKebab(String(fieldName)) + \":\"\n\n for (const [key, value] of Object.entries(allSlots)) {\n if (key.startsWith(camelPrefix)) {\n result[key.slice(camelPrefix.length)] = value\n } else if (key.startsWith(kebabPrefix) && kebabPrefix !== camelPrefix) {\n result[key.slice(kebabPrefix.length)] = value\n }\n }\n\n return result\n}\n","/**\n * FormGroup - 分组字段组件\n *\n * 用于渲染包含 children 的 Group ViewSchema。\n * 支持可折叠的分组容器,内部渲染子 columns。\n *\n * @module components/FormGroup\n */\n\nimport { computed, defineComponent, getCurrentInstance, PropType, ref, watch } from \"vue\"\nimport type { SetupContext, VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport FormItem from \"../FormItem\"\n\nimport type { SchemxViewGroupSchema, SchemxViewSchema, Values } from \"@schemx/core\"\n\n/**\n * FormGroup Props\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface SchemxGroupProps<TValues extends Values = Values> {\n schema: SchemxViewGroupSchema<TValues>\n}\n\nconst FormGroup = defineComponent(\n <TValues extends Values = Values>(\n props: SchemxGroupProps<TValues>,\n { slots }: SetupContext\n ) => {\n const internalCollapsed = ref(Boolean(props.schema.defaultCollapsed))\n\n const collapsed = computed(() => props.schema.collapsed ?? internalCollapsed.value)\n\n const componentId = getCurrentInstance()?.uid ?? 0\n\n watch(\n () => props.schema.collapsed,\n (nextCollapsed, previousCollapsed) => {\n if (nextCollapsed !== undefined) {\n internalCollapsed.value = nextCollapsed\n } else if (previousCollapsed !== undefined) {\n internalCollapsed.value = previousCollapsed\n }\n }\n )\n\n const toggle = () => {\n if (!props.schema.collapsible || props.schema.disabled) {\n return\n }\n\n const nextCollapsed = !collapsed.value\n\n if (props.schema.collapsed === undefined) {\n internalCollapsed.value = nextCollapsed\n }\n\n props.schema.onCollapsedChange?.(nextCollapsed)\n }\n\n return (): VNodeChild => {\n const schema = props.schema\n\n if (schema.visible === false) {\n return null\n }\n\n const collapsible = Boolean(schema.collapsible)\n\n const isCollapsed = collapsed.value\n\n const destroyOnCollapse = schema.destroyOnCollapse ?? true\n\n const uniqueId = schema.debug?.runtimeNodeId ?? `local-${componentId}`\n\n const idBase = `schemx-group-${uniqueId}-${normalizeId(schema.key)}`\n\n const headerId = `${idBase}-header`\n\n const bodyId = `${idBase}-body`\n\n const body = (\n <div\n id={bodyId}\n role=\"group\"\n aria-labelledby={schema.label ? headerId : undefined}\n aria-hidden={isCollapsed || undefined}\n class=\"schemx-group__body\"\n style={!destroyOnCollapse && isCollapsed ? { display: \"none\" } : undefined}\n >\n {schema.children.map((child) => (\n <FormItem\n key={child.key}\n schema={child as SchemxViewSchema}\n v-slots={slots}\n />\n ))}\n </div>\n )\n\n return (\n <div\n class={classnames(\n \"schemx-group\",\n {\n \"schemx-group--collapsed\": isCollapsed,\n \"is-readonly\": schema.readonly,\n \"is-disabled\": schema.disabled,\n },\n schema.class as string | undefined\n )}\n style={schema.style}\n data-key={schema.key}\n aria-disabled={schema.disabled || undefined}\n >\n {schema.label && (\n <div\n id={headerId}\n role={collapsible ? \"button\" : undefined}\n tabindex={collapsible ? (schema.disabled ? -1 : 0) : undefined}\n aria-expanded={collapsible ? !isCollapsed : undefined}\n aria-controls={collapsible ? bodyId : undefined}\n aria-disabled={collapsible ? schema.disabled || undefined : undefined}\n class={classnames(\"schemx-group__header\", {\n \"schemx-group__header--clickable\": collapsible && !schema.disabled,\n })}\n onClick={toggle}\n onKeydown={(e: KeyboardEvent) => {\n if (!schema.disabled && (e.key === \"Enter\" || e.key === \" \")) {\n e.preventDefault()\n toggle()\n }\n }}\n >\n <span class=\"schemx-group__title\">{schema.label}</span>\n {collapsible && (\n <span\n class={classnames(\"schemx-group__arrow\", {\n \"schemx-group__arrow--down\": !isCollapsed,\n })}\n />\n )}\n </div>\n )}\n {destroyOnCollapse ? !isCollapsed && body : body}\n </div>\n )\n }\n },\n {\n name: \"SchemxGroup\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewGroupSchema>,\n required: true,\n },\n },\n }\n)\n\nexport default FormGroup\n\nconst normalizeId = (key: string): string => {\n return String(key).replace(/[^a-zA-Z0-9_-]/g, \"-\")\n}\n","/**\n * FormItem 插槽渲染器。\n *\n * 集中处理字段各区域的插槽解析,保持 FormItem 仅负责字段状态和布局。\n *\n * @module components/FormItem/slot\n */\n\nimport { h } from \"vue\"\nimport type { ShallowRef, Slots, VNodeChild } from \"vue\"\n\nimport { FormContextProps } from \"../../hooks/provideFormConfigContext\"\nimport { extractChildSlots, resolveSlot } from \"../../utils\"\n\nimport type { FieldInstance } from \"../../types/field\"\nimport type {\n SchemxComponentProps,\n SchemxInstance,\n SchemxViewFieldSchema,\n Values,\n} from \"@schemx/core\"\n\n/** FormItem 插槽渲染所需的状态与依赖。 */\ninterface FormItemSlotRendererOptions<TValues extends Values = Values> {\n /** 当前字段 ViewSchema。 */\n schemaRef: Readonly<ShallowRef<SchemxViewFieldSchema<TValues>>>\n /** 当前字段的响应式控制器。 */\n field: FieldInstance<TValues>\n /** 当前表单实例,用于解析 Renderer。 */\n form: SchemxInstance<TValues>\n /** 表单级展示配置。 */\n formContext: FormContextProps\n /** 传给 Renderer 的稳定属性。 */\n componentProps: Readonly<ShallowRef<SchemxComponentProps<TValues>>>\n /** 父组件传入的所有具名插槽。 */\n slots: Slots\n}\n\n/**\n * 创建 FormItem 各区域的插槽渲染函数。\n *\n * `Before`、`Content`、`After` 依次包裹 Renderer;`Error` 位于控件之后。\n */\nexport function createFormItemSlotRenderers<TValues extends Values = Values>(\n options: FormItemSlotRendererOptions<TValues>\n) {\n const { schemaRef, field, form, formContext, componentProps, slots } = options\n\n /**\n * 渲染 required 星号。\n *\n * `showRequiredMark` 未设置时回退到 `required`;该展示开关不参与校验逻辑。\n * 禁用或只读字段始终不显示星号。\n *\n * @returns 星号 VNode 或空片段\n */\n const renderRequired = (): VNodeChild => {\n const showRequiredMark =\n schemaRef.value.showRequiredMark ?? Boolean(schemaRef.value.required)\n\n if (!showRequiredMark || schemaRef.value.disabled || schemaRef.value.readonly) {\n return null\n }\n\n return <span class=\"schemx-item__required\">*</span>\n }\n\n /** 构造各字段插槽共用的上下文参数。 */\n const createSlotProps = (additionalProps: Record<string, unknown> = {}) => {\n return {\n ...componentProps.value,\n value: field.value.value,\n ...additionalProps,\n }\n }\n\n /** 渲染指定后缀的字段区域插槽。 */\n const renderFieldSlot = (suffix: string): VNodeChild => {\n const slot = resolveSlot(slots, `${schemaRef.value.name}${suffix}`)\n\n return slot?.(createSlotProps()) ?? null\n }\n\n /** 渲染标签区域,优先使用 `{name}Label` 插槽。 */\n const renderLabel = (): VNodeChild => {\n const labelSlot = resolveSlot(slots, `${schemaRef.value.name}Label`)\n\n if (labelSlot) {\n return labelSlot(schemaRef.value)\n }\n\n const labelAlign = schemaRef.value.labelAlign || formContext.schemaConfig.labelAlign\n\n const labelWidth = schemaRef.value.labelWidth || formContext.schemaConfig.labelWidth\n\n const colon = schemaRef.value.colon ?? formContext.schemaConfig.colon\n\n return (\n <label\n class=\"schemx-item__label\"\n style={{ width: labelWidth, textAlign: labelAlign }}\n >\n {renderRequired()}\n <span class=\"schemx-item__label-text\">\n {schemaRef.value.label}\n {colon ? \":\" : \"\"}\n </span>\n </label>\n )\n }\n\n /** 渲染 Renderer 前的 `{name}Before` 插槽。 */\n const renderBefore = (): VNodeChild => renderFieldSlot(\"Before\")\n\n /** 渲染控件区域,优先使用 `{name}Content` 插槽。 */\n const renderContent = (): VNodeChild => {\n const component = form.getRenderer(schemaRef.value.componentType)\n\n if (!component) {\n throw new Error(\n `[schemx] Can not find component renderer of \"${schemaRef.value.componentType}\".`\n )\n }\n\n const childSlots = extractChildSlots(normalizeNameKey(schemaRef.value.name), slots)\n\n const columnElement = h(component, componentProps.value, childSlots)\n\n const contentSlot = resolveSlot(slots, `${schemaRef.value.name}Content`)\n\n if (contentSlot) {\n return contentSlot(\n createSlotProps({\n columnElement,\n })\n )\n }\n\n return <div class=\"schemx-item__control\">{columnElement}</div>\n }\n\n /** 渲染 Renderer 后的 `{name}After` 插槽。 */\n const renderAfter = (): VNodeChild => renderFieldSlot(\"After\")\n\n /** 渲染错误区域,优先使用 `{name}Error` 插槽。 */\n const renderError = (): VNodeChild => {\n const errorSlot = resolveSlot(slots, `${schemaRef.value.name}Error`)\n\n if (errorSlot) {\n return errorSlot(\n createSlotProps({\n errors: field.errors.value,\n })\n )\n }\n\n if (field.errors.value.length === 0) {\n return null\n }\n\n return <div class=\"schemx-item__error\">{field.errors.value[0]}</div>\n }\n\n return {\n createSlotProps,\n renderAfter,\n renderBefore,\n renderContent,\n renderError,\n renderLabel,\n }\n}\n\n/** 将 NamePath 转换为整体字段与 Renderer 子插槽使用的键。 */\nexport function normalizeNameKey(name: unknown): string {\n if (Array.isArray(name)) {\n return name.map((part) => String(part)).join(\".\")\n }\n\n return String(name)\n}\n","/**\n * FormItem\n *\n * schemx 和 FormGroup 实际渲染字段的组件。\n * 动态属性已由 core 解析到 ViewSchema 中;这里只负责消费\n * 已解析 schema、创建字段实例、组装渲染器属性与插槽。\n *\n * @module components/FormItem\n */\n\n/* eslint-disable vue/one-component-per-file */\nimport { computed, defineComponent, h, PropType } from \"vue\"\nimport type { VNodeChild } from \"vue\"\n\nimport { isSchemxViewFieldSchema, isViewGroupSchema } from \"@schemx/core\"\nimport classnames from \"classnames\"\n\nimport type { TriggerConfig } from \"@/utils\"\n\nimport {\n createFieldContext,\n useField,\n useFormConfigContext,\n useFormContext,\n useStableRef,\n} from \"../../hooks\"\nimport { useViewSchema } from \"../../hooks/useViewSchemas\"\nimport { mergeTrigger, resolveSlot, shouldValidateOn } from \"../../utils\"\nimport FormGroup from \"../FormGroup\"\n\nimport { createFormItemSlotRenderers, normalizeNameKey } from \"./slot\"\n\nimport type {\n FieldValue,\n NamePath,\n SchemxComponentProps,\n SchemxViewFieldSchema,\n SchemxViewSchema,\n Values,\n} from \"@schemx/core\"\n\n/**\n * FormItem 属性。\n *\n * 提供待渲染的原始 ViewSchema;具体字段或分组类型由组件内部运行时收窄。\n */\nexport interface SchemxItemProps {\n schema: unknown\n}\n\nconst FormItem = defineComponent({\n name: \"SchemxItem\",\n\n props: {\n schema: {\n type: Object as PropType<unknown>,\n required: true,\n },\n },\n\n /**\n * 根据 schema 类型分发字段组或字段项渲染。\n *\n * @param props - 当前组件的 schema 属性。\n * @param slots - Vue setup 上下文提供的插槽集合。\n */\n setup(props, { slots }) {\n return (): VNodeChild => {\n const schema = props.schema as SchemxViewSchema<Values>\n\n if (isViewGroupSchema(schema)) {\n return h(FormGroup, { schema }, slots)\n }\n\n return h(FieldFormItem, { schema }, slots)\n }\n },\n})\n\nconst FieldFormItem = defineComponent({\n name: \"SchemxFieldItem\",\n inheritAttrs: false,\n\n props: {\n schema: {\n type: Object as PropType<unknown>,\n required: true,\n },\n },\n\n /**\n * 初始化字段上下文,并组合响应式 schema、校验处理器与插槽渲染器。\n *\n * @param props - 当前字段项的 schema 属性。\n * @param attrs - Vue setup 上下文提供的透传属性。\n * @param slots - Vue setup 上下文提供的插槽集合。\n */\n setup(props, { attrs, slots }) {\n const form = useFormContext<Values>()\n\n const inputSchema = computed<SchemxViewFieldSchema<Values>>(\n () => props.schema as SchemxViewFieldSchema<Values>\n )\n\n // 按 key 复用表单级 ViewSchema 订阅,避免每个 FormItem 建立独立 Core effect。\n const latestSchema = useViewSchema(form, () => inputSchema.value.key)\n\n // 优先使用桥接中的最新字段;首帧或字段暂不存在时回退到输入 schema。\n const schemaRef = computed<SchemxViewFieldSchema<Values>>(() => {\n return latestSchema.value && isSchemxViewFieldSchema(latestSchema.value)\n ? latestSchema.value\n : inputSchema.value\n })\n\n const formContext = useFormConfigContext()\n\n const field = useField(schemaRef.value.name)\n\n createFieldContext(field)\n\n const trigger = computed<TriggerConfig>(() =>\n mergeTrigger(\n schemaRef.value.validationTrigger,\n formContext.schemaConfig.validationTrigger,\n \"onChange\"\n )\n )\n\n /**\n * 是否需要进行校验。\n *\n * 当字段不可见、只读或禁用时,无需进行校验。\n */\n const canVerified = computed(() => {\n const isOperate =\n schemaRef.value.visible && !schemaRef.value.readonly && !schemaRef.value.disabled\n\n const rules = schemaRef.value.rules\n\n const hasRules = Array.isArray(rules) ? rules?.length > 0 : !!schemaRef.value.rules\n\n return isOperate && (Boolean(schemaRef.value.required) || hasRules)\n })\n\n /**\n * 处理字段值变化,设置值后根据触发时机决定是否校验。\n *\n * @param v - 渲染器提交的最新字段值。\n */\n const handleChange = (v: FieldValue<Values, NamePath<Values>>) => {\n field.setValue(v)\n schemaRef.value.componentProps?.onChange?.(v)\n\n if (canVerified.value && shouldValidateOn(\"change\", trigger.value)) {\n field.validate()\n }\n }\n\n /**\n * 处理字段失焦,并根据触发时机决定是否校验。\n *\n * @param v - 触发失焦事件时对应的字段值。\n */\n const handleBlur = (v: FieldValue<Values, NamePath<Values>>) => {\n schemaRef.value.componentProps?.onBlur?.(v)\n\n if (canVerified.value && shouldValidateOn(\"blur\", trigger.value)) {\n field.validate()\n }\n }\n\n /**\n * 处理组件的 v-model 更新,只同步字段值,不主动触发校验。\n *\n * @param v - 渲染器提交的最新字段值。\n */\n const handleValueUpdate = (v: FieldValue<Values, NamePath<Values>>) => {\n field.setValue(v)\n }\n\n // 使用 useStableRef 避免每次生成新对象引用\n const componentProps = useStableRef<SchemxComponentProps<Values>>(\n (): SchemxComponentProps<Values> => {\n const currentComponentProps = schemaRef.value.componentProps ?? {}\n\n return {\n ...currentComponentProps,\n value: field.value.value,\n onChange: handleChange,\n onBlur: handleBlur,\n \"onUpdate:value\": handleValueUpdate,\n }\n }\n )\n\n const {\n createSlotProps,\n renderAfter,\n renderBefore,\n renderContent,\n renderError,\n renderLabel,\n } = createFormItemSlotRenderers({\n schemaRef,\n field,\n form,\n formContext,\n componentProps,\n slots,\n })\n\n return (): VNodeChild => {\n if (!schemaRef.value.visible) {\n return null\n }\n\n // 整体插槽:完全接管渲染,不包裹任何默认结构\n const itemSlot = resolveSlot(slots, normalizeNameKey(schemaRef.value.name))\n\n if (itemSlot) {\n return itemSlot(createSlotProps())\n }\n\n const labelPosition =\n schemaRef.value.labelPosition || formContext.schemaConfig.labelPosition\n\n return (\n <div\n {...attrs}\n class={classnames(\"schemx-item-wrapper\", attrs.class)}\n style={[attrs.style, schemaRef.value.style]}\n >\n <div\n class={classnames(\n \"schemx-item\",\n `schemx-item--label-${labelPosition}`,\n schemaRef.value.class,\n {\n \"is-readonly\": schemaRef.value.readonly,\n \"is-disabled\": schemaRef.value.disabled,\n }\n )}\n style={{ ...((schemaRef.value.style ?? {}) as CSSStyleValue) }}\n >\n {renderLabel()}\n\n <div class=\"schemx-item__content\">\n {renderBefore()}\n {renderContent()}\n {renderAfter()}\n {renderError()}\n </div>\n </div>\n </div>\n )\n }\n },\n})\n\nexport default FormItem\n","<script setup lang=\"ts\">\n import { computed, useAttrs } from \"vue\"\n\n import type { SchemxButtonSize } from \"./types\"\n\n interface Props {\n loading?: boolean\n loadingText?: string\n disabled?: boolean\n size?: SchemxButtonSize\n }\n\n defineOptions({ name: \"SchemxButton\", inheritAttrs: false })\n\n const props = withDefaults(defineProps<Props>(), {\n loading: false,\n loadingText: undefined,\n disabled: false,\n size: \"medium\",\n })\n\n const attrs = useAttrs()\n\n const isDisabled = computed(() => props.disabled || props.loading)\n\n const buttonClass = computed(() => [\"schemx-button\", `schemx-button--${props.size}`])\n</script>\n\n<template>\n <button\n v-bind=\"attrs\"\n :class=\"buttonClass\"\n :aria-busy=\"props.loading || undefined\"\n :data-loading=\"props.loading || undefined\"\n :disabled=\"isDisabled\"\n >\n <span v-if=\"$slots.prefix\" class=\"schemx-button__prefix\">\n <slot name=\"prefix\" />\n </span>\n <svg\n v-if=\"props.loading\"\n class=\"schemx-button__loading\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2v4\" />\n <path d=\"m16.2 7.8 2.9-2.9\" />\n <path d=\"M18 12h4\" />\n <path d=\"m16.2 16.2 2.9 2.9\" />\n <path d=\"M12 18v4\" />\n <path d=\"m4.9 19.1 2.9-2.9\" />\n <path d=\"M2 12h4\" />\n <path d=\"m4.9 4.9 2.9 2.9\" />\n </svg>\n\n <template v-if=\"props.loading && props.loadingText\">\n {{ props.loadingText }}\n </template>\n <slot v-else />\n <span v-if=\"$slots.suffix\" class=\"schemx-button__suffix\">\n <slot name=\"suffix\" />\n </span>\n </button>\n</template>\n\n<style>\n .schemx-button {\n box-sizing: content-box;\n flex: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--schemx-padding-xs);\n min-width: 72px;\n height: 24px;\n padding: var(--schemx-padding-xs) var(--schemx-padding-md);\n border: 1px solid var(--schemx-border-color);\n border-radius: var(--schemx-border-radius-sm);\n background: var(--schemx-background-color);\n color: var(--schemx-text-color);\n cursor: pointer;\n transition:\n border-color 0.15s ease,\n background-color 0.15s ease,\n color 0.15s ease,\n transform 0.1s ease;\n }\n\n .schemx-button:not(:disabled):hover {\n opacity: 0.8;\n }\n\n .schemx-button:not(:disabled):active {\n transform: scale(0.98);\n }\n\n .schemx-button--small {\n min-width: 56px;\n height: 20px;\n padding: var(--schemx-padding-base) var(--schemx-padding-sm);\n font-size: var(--schemx-font-size-sm);\n }\n\n .schemx-button--large {\n min-width: 88px;\n height: 28px;\n padding: var(--schemx-padding-sm) var(--schemx-padding-lg);\n font-size: var(--schemx-font-size-lg);\n }\n\n .schemx-button__loading {\n display: inline-block;\n border-radius: 50%;\n vertical-align: -0.15em;\n animation: schemx-button-loading 0.8s linear infinite;\n }\n\n .schemx-actions-button--submit {\n border-color: var(--schemx-primary-color);\n background: var(--schemx-primary-color);\n color: #ffffff;\n }\n\n .schemx-actions-button--submit:not(:disabled):hover {\n filter: brightness(0.92);\n color: #ffffff;\n }\n\n .schemx-button:disabled {\n cursor: not-allowed;\n opacity: var(--schemx-disabled-opacity);\n }\n\n @keyframes schemx-button-loading {\n to {\n transform: rotate(360deg);\n }\n }\n</style>\n","import { isViewGroupSchema, SchemxViewSchema } from \"@schemx/core\"\n\n/**\n * 判断当前项是否为顶层可见普通字段所在区段的第一个或最后一个。\n *\n * 可见 Group 是区段边界;不可见字段和不可见 Group 不参与计算。\n *\n * 使用示例:\n *\n * const list = [\n * { key: 'group-1', children: [] },\n * { key: 'a' },\n * { key: 'b' },\n * { key: 'group-2', children: [] },\n * { key: 'c' },\n * ]\n *\n * getSectionPosition(list, 'a')\n * // => { found: true, isFirst: true, isLast: false }\n */\nexport function getSectionPosition<TItem extends SchemxViewSchema>(\n list: TItem[],\n currentKey: string\n) {\n const currentIndex = list.findIndex((item) => item.key === currentKey)\n\n if (currentIndex === -1) {\n return {\n found: false,\n isFirst: false,\n isLast: false,\n }\n }\n\n const currentItem = list[currentIndex]\n\n if (!isPositionItem(currentItem)) {\n return {\n found: true,\n isFirst: false,\n isLast: false,\n }\n }\n\n return {\n found: true,\n isFirst: !hasPositionItemInSection(list, currentIndex, -1),\n isLast: !hasPositionItemInSection(list, currentIndex, 1),\n }\n}\n\nfunction isPositionItem<TItem extends SchemxViewSchema>(item?: TItem) {\n return !!item && !isViewGroupSchema(item) && item.visible !== false\n}\n\nfunction hasPositionItemInSection<TItem extends SchemxViewSchema>(\n list: TItem[],\n startIndex: number,\n step: 1 | -1\n): boolean {\n for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {\n const item = list[index]\n\n if (item.visible === false) {\n continue\n }\n\n if (isViewGroupSchema(item)) {\n return false\n }\n\n if (isPositionItem(item)) {\n return true\n }\n }\n\n return false\n}\n","<!--\n schemx - 主组件\n\n 基于 Schema 配置的表单组件,使用 useForm Hook 管理状态。\n\n @module schemx\n-->\n\n<script lang=\"ts\" setup generic=\"TValues extends Values = Values\">\n import { computed, reactive, useSlots, watch } from \"vue\"\n\n import { defaultSchemxConfigKeys, isSchemxSchemas } from \"@schemx/core\"\n import { pick } from \"es-toolkit\"\n\n import Button from \"./components/Button\"\n import FormItem from \"./components/FormItem\"\n import {\n createFormConfigContext,\n createFormContext,\n useForm,\n useFormSelector,\n useViewSchemas,\n } from \"./hooks\"\n import { getSectionPosition } from \"./utils/helpers\"\n\n import type { SchemxFormActionConfig, SchemxFormProps } from \"./types/index\"\n import type {\n SchemxInstance,\n SchemxSchemaConfig,\n SchemxViewSchema,\n Values,\n } from \"@schemx/core\"\n\n import \"./styles/index.css\"\n\n defineOptions({ name: \"SchemxForm\" })\n\n /**\n * 规范化表单组件的公开输入,并为未提供的可选项补充默认值。\n *\n * 具体字段语义由 `SchemxFormProps<TValues>` 定义;其中 `modelValue`、\n * `schemas` 和 `form` 共同决定表单数据与实例来源。\n */\n const props = withDefaults(defineProps<SchemxFormProps<TValues>>(), {\n modelValue: () => ({}) as TValues,\n initialValues: () => ({}) as TValues,\n form: undefined,\n schemas: () => [],\n class: \"\",\n style: () => ({}),\n onFinish: undefined,\n onFinishFailed: undefined,\n onReset: undefined,\n onLoadingChange: undefined,\n onValuesChange: undefined,\n onFieldsChange: undefined,\n loading: undefined,\n submitter: undefined,\n resetter: undefined,\n rendererProps: undefined,\n rendererRegistry: undefined,\n validationRuleRegistry: undefined,\n visible: true,\n })\n\n /**\n * 定义表单 v-model 的同步事件。\n *\n * 仅在内部表单值变化且不是外部 `modelValue` 同步窗口时发出更新。\n */\n const emit = defineEmits<{\n \"update:modelValue\": [value: TValues]\n }>()\n\n /**\n * 读取组件保留操作区插槽,并据此决定是否渲染默认操作区。\n */\n const slots = useSlots()\n\n /**\n * 提取需要同步到 Core 的表单级 schema 配置。\n */\n const pickSchemaConfig = (): Partial<SchemxSchemaConfig> => {\n // Vue 保留 `required` 对当前 TValues 的泛型约束;Core 的表单级配置使用\n // unknown 表示任意字段值。运行时该回调只会接收当前 Form 的字段值,因此在\n // Vue 到 Core 的适配边界收窄为 Core 配置类型。\n return pick(props, defaultSchemxConfigKeys) as Partial<SchemxSchemaConfig>\n }\n\n /**\n * 保存供后代组件读取的响应式表单级 schema 配置。\n */\n const formSchemaConfig = reactive<Partial<SchemxSchemaConfig>>(pickSchemaConfig())\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createFormConfigContext({ schemaConfig: formSchemaConfig })\n\n /**\n * 获取或创建表单实例\n *\n * 优先使用外部传入的 form,否则内部通过 useForm 创建。\n * 必须在 setup 同步阶段调用,确保 provide 正确注入。\n */\n const providedForm = props.form\n ? props.form\n : useForm<TValues>({\n schemas: props.schemas,\n schemaConfig: pickSchemaConfig(),\n initialValues:\n Object.keys(props.modelValue).length > 0\n ? props.modelValue\n : props.initialValues,\n\n rendererProps: props.rendererProps,\n rendererRegistry: props.rendererRegistry,\n defaultRendererType: props.defaultRendererType,\n validationRuleRegistry: props.validationRuleRegistry,\n validatorAdapters: props.validatorAdapters,\n\n /**\n * 转发提交成功回调。\n *\n * @param values - 提交成功时的完整表单快照。\n */\n onFinish: (values) => props.onFinish?.(values),\n /**\n * 转发提交失败回调。\n *\n * @param errors - 提交失败时的字段错误集合。\n */\n onFinishFailed: (errors) => props.onFinishFailed?.(errors),\n /**\n * 转发完整表单重置回调。\n */\n onReset: () => {\n props.onReset?.()\n },\n /**\n * 转发 Core 发出的提交 loading 状态。\n *\n * 外部传入 Form 时不会注入该回调,保持实例的创建期边界。\n *\n * @param loading - 当前是否处于提交流程中。\n */\n onLoadingChange: (loading) => {\n props.onLoadingChange?.(loading)\n },\n /**\n * 转发值变化回调。\n *\n * @param changedValues - 本次变更涉及的字段值。\n * @param latestSnapshot - 变更后的完整表单快照。\n */\n onValuesChange: (changedValues, latestSnapshot) => {\n props.onValuesChange?.(changedValues, latestSnapshot)\n },\n /**\n * 转发字段变化回调。\n *\n * @param changedPaths - 本次发生变化的字段路径。\n * @param allPaths - 当前已变更字段路径集合。\n */\n onFieldsChange: (changedPaths, allPaths) => {\n props.onFieldsChange?.(changedPaths, allPaths)\n },\n })\n\n const isExternalForm = props.form !== undefined\n\n /**\n * 注册表单上下文。\n *\n * 无论实例来自 props.form 还是 useForm,都必须同步注册,\n * 从而保证 FormItem、useField 等后代逻辑能够获取同一个实例。\n */\n const formInstance = createFormContext(providedForm)\n\n /**\n * 组件未受控时直接跟随 Core 提交状态;受控值仅覆盖操作区展示。\n */\n const effectiveLoading = computed(() => props.loading ?? formInstance.isLoading())\n\n /**\n * 归一化启用状态下的操作配置。\n */\n const normalizeActionConfig = (\n action: SchemxFormProps<TValues>[\"submitter\"]\n ): SchemxFormActionConfig => (action === true ? {} : action || {})\n\n /**\n * 提交操作的归一化配置。\n */\n const submitterConfig = computed(() => normalizeActionConfig(props.submitter))\n\n /**\n * 重置操作的归一化配置。\n */\n const resetterConfig = computed(() => normalizeActionConfig(props.resetter))\n\n /**\n * 操作区必须保护内部事件和 button type,不接受运行时透传的同名属性。\n */\n const getButtonProps = (config: SchemxFormActionConfig) => {\n const buttonProps = Object.fromEntries(\n Object.entries(config.buttonProps ?? {}).filter(\n ([name]) => name !== \"type\" && name !== \"onClick\"\n )\n )\n\n return buttonProps\n }\n\n /**\n * 提交按钮可安全透传的原生属性。\n */\n const submitterButtonProps = computed(() => getButtonProps(submitterConfig.value))\n\n /**\n * 重置按钮可安全透传的原生属性。\n */\n const resetterButtonProps = computed(() => getButtonProps(resetterConfig.value))\n\n /**\n * 仅配置或提供对应插槽时展示提交操作。\n */\n const isSubmitterVisible = computed(\n () => props.submitter !== false && (props.submitter !== undefined || slots.submitter)\n )\n\n /**\n * 仅配置或提供对应插槽时展示重置操作。\n */\n const isResetterVisible = computed(\n () => props.resetter !== false && (props.resetter !== undefined || slots.resetter)\n )\n\n /**\n * 操作区的整体显示状态。\n */\n const isActionsVisible = computed(\n () => isSubmitterVisible.value || isResetterVisible.value\n )\n\n /**\n * 操作区保留插槽不能下传给字段,避免与同名字段插槽冲突。\n */\n const fieldSlots = computed(() =>\n Object.fromEntries(\n Object.entries(slots).filter(\n ([slotName]) => slotName !== \"submitter\" && slotName !== \"resetter\"\n )\n )\n )\n\n /**\n * 提交按钮在 loading 或显式 disabled 时不可点击。\n */\n const isSubmitterDisabled = computed(\n () => effectiveLoading.value || Boolean(submitterButtonProps.value.disabled)\n )\n\n /**\n * 重置按钮在 loading 或显式 disabled 时不可点击。\n */\n const isResetterDisabled = computed(\n () => effectiveLoading.value || Boolean(resetterButtonProps.value.disabled)\n )\n\n let syncingFromModel = false\n\n /**\n * 将外部 modelValue 同步到 Core,并标记窗口以阻止 v-model 回写。\n *\n * @param values - 外部传入的最新表单值。\n */\n watch(\n () => props.modelValue,\n (values) => {\n syncingFromModel = true\n formInstance.setFieldsValue(values)\n syncingFromModel = false\n },\n { deep: true }\n )\n\n /**\n * 响应提交按钮点击,并返回 Core Promise 以让 Vue 处理异常。\n */\n const handleSubmit: SchemxInstance<TValues>[\"submit\"] = () => formInstance.submit()\n\n /**\n * 响应重置按钮点击。\n */\n const handleReset = (): void => {\n formInstance.reset()\n }\n\n /**\n * 通过共享 selector bridge 订阅完整快照,避免组件树重复注册 Core 监听。\n *\n * @param values - 当前表单的完整快照。\n */\n const formValues = useFormSelector(formInstance, (values) => values)\n\n /**\n * 将 Core 值同步回 v-model;外部 modelValue 写入期间跳过回写,避免循环。\n *\n * @param latestSnapshot - Core 发布的最新完整表单快照。\n */\n watch(\n formValues,\n (latestSnapshot) => {\n if (syncingFromModel) return\n\n emit(\"update:modelValue\", latestSnapshot)\n },\n { flush: \"sync\" }\n )\n\n /**\n * 将外部传入的原始 Schema 同步更新到 Core。\n *\n * @param schemas - 最新的 props.schemas 配置。\n */\n watch(\n () => props.schemas,\n (schemas) => {\n if (!isSchemxSchemas(schemas)) {\n formInstance.setSchemas(schemas)\n }\n },\n { deep: false, immediate: !!props.form }\n )\n\n const viewSchemas = useViewSchemas(formInstance)\n\n /**\n * 根据 ViewSchema 在当前列表中的位置生成首尾样式类。\n *\n * @param schema - 当前需要计算样式的 ViewSchema。\n * @returns 用于包裹元素的首尾位置 class 映射。\n */\n const getFormItemClass = (schema: SchemxViewSchema<TValues>) => {\n const { isFirst, isLast } = getSectionPosition(\n viewSchemas.value as SchemxViewSchema[],\n schema.key\n )\n\n return {\n \"schemx-item-wrapper--first\": isFirst,\n \"schemx-item-wrapper--last\": isLast,\n }\n }\n\n /**\n * 将公开配置快照写入本地上下文并同步到 Core。\n *\n * @param nextSchemaConfig - 最新的表单级 schema 配置。\n */\n watch(\n pickSchemaConfig,\n (nextSchemaConfig) => {\n Object.assign(formSchemaConfig, nextSchemaConfig)\n formInstance.updateSchemaConfig(nextSchemaConfig)\n },\n { deep: false, immediate: isExternalForm }\n )\n\n /**\n * 暴露当前表单 Facade 的完整控制 API,供模板 ref 调用。\n */\n const exposed: SchemxInstance<TValues> = {\n ...formInstance,\n submit: handleSubmit,\n reset: handleReset,\n }\n\n defineExpose(exposed)\n</script>\n\n<template>\n <div :class=\"['schemx', props.class]\" :style=\"props.style\">\n <FormItem\n v-for=\"schema in viewSchemas\"\n :key=\"schema.key\"\n :schema=\"schema as SchemxViewSchema\"\n :class=\"getFormItemClass(schema as SchemxViewSchema<TValues>)\"\n >\n <template v-for=\"(_, slotName) in fieldSlots\" #[slotName]=\"slotProps\">\n <slot :name=\"slotName\" v-bind=\"slotProps ?? {}\" />\n </template>\n </FormItem>\n <div v-if=\"isActionsVisible\" class=\"schemx-actions\">\n <slot\n v-if=\"isResetterVisible && slots.resetter\"\n name=\"resetter\"\n :form=\"formInstance\"\n :loading=\"effectiveLoading\"\n :disabled=\"isResetterDisabled\"\n :reset=\"handleReset\"\n />\n <Button\n v-else-if=\"isResetterVisible\"\n v-bind=\"resetterButtonProps\"\n class=\"schemx-actions-button schemx-actions-button--reset\"\n type=\"button\"\n :disabled=\"isResetterDisabled\"\n @click=\"handleReset\"\n >\n {{ resetterConfig.text ?? \"重置\" }}\n </Button>\n <slot\n v-if=\"isSubmitterVisible && slots.submitter\"\n name=\"submitter\"\n :form=\"formInstance\"\n :loading=\"effectiveLoading\"\n :disabled=\"isSubmitterDisabled\"\n :submit=\"handleSubmit\"\n />\n <Button\n v-else-if=\"isSubmitterVisible\"\n v-bind=\"submitterButtonProps\"\n class=\"schemx-actions-button schemx-actions-button--submit\"\n type=\"button\"\n :loading=\"effectiveLoading\"\n :disabled=\"isSubmitterDisabled\"\n @click=\"handleSubmit\"\n >\n {{ submitterConfig.text ?? \"提交\" }}\n </Button>\n </div>\n </div>\n</template>\n","/**\n * schemx 组件导出\n *\n * 为 SchemxForm 组件挂载静态方法(install、FormItem、registerRequest 等),\n * 并作为默认导出。\n *\n * @module formExport\n */\n\nimport type { App } from \"vue\"\n\nimport FormItem from \"./components/FormItem\"\nimport { provideSchemxAppConfig } from \"./config\"\nimport SchemxForm from \"./formRuntime.js\"\n\nimport type { SchemxConfig } from \"@schemx/core\"\n\n/**\n * SchemxForm 插件安装选项\n *\n * 在 `app.use(SchemxForm, options)` 时传入,用于配置当前 Vue App 的默认行为。\n * 安装配置不会写入 Core 的模块级全局配置,因此多个 Vue App 可以彼此隔离。\n *\n * @example\n * ```ts\n * import SchemxForm from '@schemx/vue'\n *\n * app.use(SchemxForm, {\n * schemaConfig: { readonly: true },\n * })\n * ```\n */\nexport interface SchemxInstallOptions extends SchemxConfig {}\n\n/**\n * 为组件挂载静态属性并保留原始类型\n *\n * @param comp - 原始组件\n * @param extra - 要挂载的静态属性\n * @typeParam TComponent - 原始组件类型。\n * @typeParam TExtras - 待挂载的静态属性类型。\n */\nexport function withInstall<\n TComponent extends object,\n TExtras extends Record<string, unknown>,\n>(comp: TComponent, extra: TExtras) {\n return Object.assign(comp, extra) as TComponent & TExtras\n}\n\nexport type SchemxFormPlugin = typeof SchemxForm & {\n install: (app: App, options?: SchemxInstallOptions) => void\n FormItem: typeof FormItem\n}\n\nconst SchemxFormExport = withInstall(SchemxForm, {\n /** Vue 插件安装方法 */\n install(app: App, options: SchemxInstallOptions = {}) {\n provideSchemxAppConfig(app, options)\n app.component(\"SchemxForm\", SchemxForm)\n },\n /** FormItem 子组件引用 */\n FormItem,\n}) as unknown as SchemxFormPlugin\n\nexport default SchemxFormExport\n","/**\n * WithRemoteOptions - 选项高阶组件\n *\n * 包装任意渲染器组件,自动注入 useDictionary 的选项加载能力。\n * 被包装组件会额外收到 options、loading 两个 props,\n * 无需自行调用 useDictionary。\n *\n * @module hocs/withRemoteOptions\n */\n\nimport { Component, computed, defineComponent, h, PropType, SetupContext } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFieldContext } from \"../hooks/provideFieldContext\"\nimport { useDictionary } from \"../hooks/useDictionary\"\n\n\n/**\n * WithRemoteOptions 注入给被包装组件的额外 Props\n */\nexport interface RemoteOptionsInjectedProps {\n /** 加载的选项列表 */\n options: unknown[]\n /** 加载状态 */\n loading: boolean\n}\n\ntype SchemxDictionaryInput = SchemxDictionary | SchemxDictionary[\"api\"]\n\n/**\n * 将 api 简写规范化为完整的字典配置。\n */\nfunction normalizeDictionary(\n dictionary: SchemxDictionaryInput | undefined\n): SchemxDictionary | undefined {\n if (typeof dictionary === \"function\") {\n return { api: dictionary }\n }\n\n return dictionary\n}\n\n/**\n * 选项高阶组件\n *\n * 包装一个渲染器组件,当 dict prop 存在时,\n * 调用 useDictionary 加载选项,并将结果作为 options prop 注入。\n *\n * 当组件自身已传入非空 options 且无 dict 时,优先使用静态 options。\n *\n * @param WrappedComponent - 被包装的渲染器组件\n * @returns 增强后的组件,自动具备选项加载能力\n *\n * @example\n * ```ts\n * import { WithRemoteOptions } from '@schemx/vue'\n * import MySelect from './MySelect.vue'\n *\n * const RemoteSelect = WithRemoteOptions(MySelect)\n *\n * // 在 schema 中使用\n * const schema = {\n * name: 'city',\n * label: '城市',\n * componentType: 'select',\n * componentProps: {\n * dict: {\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * },\n * },\n * }\n * ```\n */\nexport function WithRemoteOptions(WrappedComponent: Component): Component {\n return defineComponent({\n name: `WithRemoteOptions(${WrappedComponent.name || \"Anonymous\"})`,\n inheritAttrs: false,\n props: {\n dict: {\n type: [Object, Function] as PropType<SchemxDictionaryInput>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }: SetupContext) {\n const dictionary = normalizeDictionary(props.dict)\n\n // 内嵌于 FormItem 时自动从字段 Context 获取目标字段;显式 fieldName\n // 保留给脱离 FormItem 的独立使用场景。\n const fieldName = dictionary\n ? (attrs.fieldName as string | undefined) ?? useFieldContext().name\n : undefined\n\n const dictResult = dictionary ? useDictionary(dictionary, fieldName) : null\n\n const childrenProps = computed(() => {\n return {\n ...attrs,\n dict: dictionary,\n options: dictionary ? dictResult?.list.value : attrs.options,\n loading: dictionary ? dictResult?.loading.value : attrs.loading,\n }\n })\n\n return () => h(WrappedComponent, childrenProps.value, slots)\n },\n })\n}\n\nexport default WithRemoteOptions\n"],"names":["createVueShallowRef","value","shallowRef","getVueFieldBridge","bridge","name","source","stateAdapter","field","cachedBridge","fieldBridges","get","snapshot","getSnapshot","errors","touched","pending","unsubscribe","subscribe","nextSnapshot","previous","next","Object","is","length","every","index","fieldBridge","dispose","set","formBridgeCache","WeakMap","formFacadeCache","facadeCoreFormCache","getCoreForm","form","getVueFormBridge","coreForm","createFormStateAdapter","values","touchedFields","pendingFields","loading","Map","syncValues","syncTouchedFields","syncPendingFields","syncLoading","unsubscribeValues","unsubscribeTouchedFields","unsubscribePendingFields","unsubscribeLoading","facade","getVueFormFacade","refCount","destroyed","createVueFormBridge","cachedFacade","dependencies","getFieldValue","getFormBridge","getFieldBridge","getFieldErrors","isFieldTouched","isFieldPending","getFieldsValue","names","getTouchedFields","getPendingFields","isLoading","destroy","getCachedFormBridge","disposeFormBridge","createVueFormFacade","bridgeForm","disposeVueFormBridge","retainVueFormBridge","released","clear","delete","SCHEMX_APP_CONFIG_KEY","Symbol","EMPTY_SCHEMX_CONFIG","freeze","schemaConfig","validatorAdapters","normalizeRendererProps","entries","map","type","props","fromEntries","provideSchemxAppConfig","app","config","normalizedConfig","rendererProps","defaultRendererType","rendererRegistry","validationRuleRegistry","normalizeSchemxAppConfig","provide","createRendererRegistry","createValidationRuleRegistry","useForm","options","configuredOptions","mergeSchemxConfig","getUseFormSchemxConfig","getCurrentInstance","inject","mergedOptions","instance","createForm","releaseBridge","onScopeDispose","SCHEMX_FORM_INSTANCE_KEY","createFormContext","useFormContext","Error","useField","coreField","createField","computed","dirty","getValue","getErrors","isTouched","isPending","getValues","createFieldHook","SCHEMX_FORM_FIELD_KEY","createFieldContext","useFieldContext","useWatch","nameOrNamesOrCallback","callbackOrOptions","maybeOptions","createWatch","onUnmounted","useWatchFields","callback","normalizeError","err","String","useDictionary","fieldName","list","ref","error","requestCount","loadDict","async","formValues","shouldFetch","currentCount","res","maxRetries","retryCount","retryDelay","retryInterval","lastError","attempt","api","Promise","r","setTimeout","executeWithRetry","formatted","formatter","Array","isArray","format","onSuccess","normalized","onError","_a","dependsOn","latestSnapshot","_payload","onDepsChange","resetOnDepsChange","setFieldValue","immediate","onMounted","refresh","mutate","data","SCHEMX_FORM_CONFIG_KEY","createFormConfigContext","useFormConfigContext","context","useStableRef","factory","stableRef","watchEffect","a","b","keysA","keys","keysB","key","isShallowEqual","viewSchemaBridgeCache","useViewSchemas","acquireViewSchemaBridge","viewSchemas","createViewSchemaBridge","getViewSchemas","schemasByKey","appendSchemas","schemas","schema","isViewGroupSchema","children","createViewSchemaIndex","subscribeViewSchemas","nextSchemas","useFormSelector","selector","selected","equals","stop","watch","flush","readonly","isValidTrigger","v","shouldValidateOn","event","trigger","some","t","onBlur","onChange","onSubmit","blur","change","submit","normalizeTrigger","isCamelCase","str","test","isKebabCase","includes","camelToKebab","replace","toLowerCase","kebabToCamel","_match","letter","toUpperCase","resolveSlot","slots","alt","extractChildSlots","allSlots","result","camelPrefix","kebabPrefix","normalizeToKebab","startsWith","slice","FormGroup$1","defineComponent","internalCollapsed","Boolean","defaultCollapsed","collapsed","componentId","uid","nextCollapsed","previousCollapsed","toggle","collapsible","disabled","onCollapsedChange","visible","isCollapsed","destroyOnCollapse","idBase","debug","runtimeNodeId","normalizeId","headerId","bodyId","body","_createVNode","createVNode","id","role","label","undefined","class","style","display","FormItem","child","classnames","tabindex","onClick","onKeydown","e","preventDefault","required","normalizeNameKey","part","join","setup","h","FormGroup","FieldFormItem","inheritAttrs","attrs","inputSchema","latestSchema","getKey","useViewSchema","schemaRef","isSchemxViewFieldSchema","formContext","mergeTrigger","columnTrigger","validationTrigger","contextTrigger","defaultTrigger","canVerified","isOperate","rules","hasRules","handleChange","setValue","componentProps","validate","handleBlur","handleValueUpdate","createSlotProps","renderAfter","renderBefore","renderContent","renderError","renderLabel","additionalProps","renderFieldSlot","suffix","slot","component","getRenderer","componentType","childSlots","columnElement","contentSlot","errorSlot","labelSlot","labelAlign","labelWidth","colon","width","textAlign","showRequiredMark","_createTextVNode","createTextVNode","createFormItemSlotRenderers","itemSlot","labelPosition","_mergeProps","mergeProps","FormItem$1","__props","useAttrs","isDisabled","buttonClass","size","_createElementBlock","_unref","$slots","prefix","_openBlock","createElementBlock","_hoisted_2","_renderSlot","_ctx","_hoisted_3","_cache","loadingText","_Fragment","_toDisplayString","_hoisted_4","isPositionItem","item","hasPositionItemInSection","startIndex","step","emit","__emit","useSlots","pickSchemaConfig","pick","defaultSchemxConfigKeys","formSchemaConfig","reactive","providedForm","initialValues","modelValue","onFinish","call","onFinishFailed","onReset","onLoadingChange","onValuesChange","changedValues","onFieldsChange","changedPaths","allPaths","isExternalForm","formInstance","effectiveLoading","normalizeActionConfig","action","submitterConfig","submitter","resetterConfig","resetter","getButtonProps","buttonProps","filter","submitterButtonProps","resetterButtonProps","isSubmitterVisible","isResetterVisible","isActionsVisible","fieldSlots","slotName","isSubmitterDisabled","isResetterDisabled","syncingFromModel","vue","setFieldsValue","deep","handleSubmit","handleReset","reset","isSchemxSchemas","setSchemas","getFormItemClass","isFirst","isLast","currentKey","currentIndex","findIndex","found","getSectionPosition","nextSchemaConfig","assign","updateSchemaConfig","__expose","_normalizeClass","_normalizeStyle","_renderList","renderList","_createBlock","unref","normalizeClass","_","fn","_withCtx","slotProps","openBlock","_hoisted_1","Button","toDisplayString","text","comp","extra","SchemxFormExport$1","SchemxForm","install","WrappedComponent","dict","Function","default","dictionary","normalizeDictionary","dictResult","childrenProps"],"mappings":"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"}
|