schema-components 2.1.0 → 3.0.0
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 +41 -6
- package/dist/{SchemaComponent-B__6-5-E.d.mts → SchemaComponent-CRgCVDhz.d.mts} +27 -15
- package/dist/{SchemaComponent-BxzzsHsK.mjs → SchemaComponent-Cga5oJfP.mjs} +3 -3
- package/dist/core/renderField.mjs +41 -7
- package/dist/html/streamRenderers.d.mts +12 -3
- package/dist/html/streamRenderers.mjs +56 -10
- package/dist/lit/SchemaComponent.d.mts +2 -2
- package/dist/lit/SchemaComponent.mjs +1 -1
- package/dist/lit/SchemaField.d.mts +1 -1
- package/dist/lit/SchemaField.mjs +1 -1
- package/dist/lit/SchemaView.mjs +1 -1
- package/dist/lit/defaultResolver.mjs +1 -1
- package/dist/lit/registry.mjs +1 -1
- package/dist/preact/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.d.mts +1 -1
- package/dist/react/SchemaComponent.mjs +6 -6
- package/dist/react/SchemaView.d.mts +16 -11
- package/dist/react/SchemaView.mjs +2 -2
- package/dist/solid/SchemaComponent.d.mts +6 -6
- package/dist/solid/SchemaComponent.mjs +1 -1
- package/dist/solid/SchemaField.d.mts +3 -3
- package/dist/solid/SchemaField.mjs +1 -1
- package/dist/solid/SchemaView.d.mts +5 -5
- package/dist/solid/SchemaView.mjs +1 -1
- package/package.json +5 -3
- package/src/svelte/SchemaComponent.svelte +3 -3
- package/src/svelte/SchemaField.svelte +3 -3
- package/src/svelte/SchemaView.svelte +3 -3
- package/src/vue/SchemaComponent.vue +274 -0
- package/src/vue/SchemaErrorBoundary.vue +60 -0
- package/src/vue/SchemaField.vue +178 -0
- package/src/vue/SchemaProvider.vue +39 -0
- package/src/vue/SchemaView.vue +198 -0
- package/src/vue/VNodeHost.ts +32 -0
- package/src/vue/contexts.ts +116 -0
- package/src/vue/eventTargets.ts +35 -0
- package/src/vue/headless.ts +61 -0
- package/src/vue/idPrefix.ts +79 -0
- package/src/vue/renderField.ts +182 -0
- package/src/vue/renderers.ts +1297 -0
- package/src/vue/resolver.ts +45 -0
- package/src/vue/types.ts +140 -0
- package/src/vue/vue-shim.d.ts +25 -0
- package/src/vue/widget.ts +51 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue resolver helpers — resolver-key lookup and resolver merging.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the React adapter's `getRenderFunction` / `mergeResolvers`
|
|
5
|
+
* pair, parameterised over {@link VueComponentResolver} and
|
|
6
|
+
* {@link VueRenderFunction}. Reuses {@link RESOLVER_KEYS} and
|
|
7
|
+
* {@link typeToKey} from `core/renderer.ts` so the per-type → key
|
|
8
|
+
* mapping has one canonical definition shared by every adapter.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { RESOLVER_KEYS, typeToKey } from "../core/renderer.ts";
|
|
12
|
+
import type { WalkedField } from "../core/types.ts";
|
|
13
|
+
import type { VueComponentResolver, VueRenderFunction } from "./types.ts";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Look up the {@link VueRenderFunction} for a schema type in a
|
|
17
|
+
* {@link VueComponentResolver}. Returns `undefined` when the resolver
|
|
18
|
+
* has no entry for the type — the caller (typically the dispatcher in
|
|
19
|
+
* `core/renderField.ts`) then falls through to the headless resolver.
|
|
20
|
+
*/
|
|
21
|
+
export function getVueRenderFunction(
|
|
22
|
+
type: WalkedField["type"],
|
|
23
|
+
resolver: VueComponentResolver
|
|
24
|
+
): VueRenderFunction | undefined {
|
|
25
|
+
return resolver[typeToKey(type)];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Merge two {@link VueComponentResolver}s — user values take priority,
|
|
30
|
+
* fallback fills gaps. Iterates {@link RESOLVER_KEYS} so every field
|
|
31
|
+
* variant the walker can emit has a deterministic resolution path.
|
|
32
|
+
*/
|
|
33
|
+
export function mergeVueResolvers(
|
|
34
|
+
user: VueComponentResolver,
|
|
35
|
+
fallback: VueComponentResolver
|
|
36
|
+
): VueComponentResolver {
|
|
37
|
+
const merged: VueComponentResolver = {};
|
|
38
|
+
for (const key of RESOLVER_KEYS) {
|
|
39
|
+
const fn = user[key] ?? fallback[key];
|
|
40
|
+
if (fn !== undefined) {
|
|
41
|
+
merged[key] = fn;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return merged;
|
|
45
|
+
}
|
package/src/vue/types.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue-flavoured render-prop and resolver shapes.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the React adapter's `RenderProps` / `RenderFunction` /
|
|
5
|
+
* `ComponentResolver` trio, parameterised over Vue's {@link VNode}
|
|
6
|
+
* output type. Built on top of the framework-agnostic generic
|
|
7
|
+
* `BaseRenderProps` and `RenderFunction` from `core/renderer.ts` so the
|
|
8
|
+
* Vue adapter shares a single source of truth for the per-field props
|
|
9
|
+
* shape with React, HTML, and any future framework adapter.
|
|
10
|
+
*
|
|
11
|
+
* The `onChange` callback semantics are deliberately kept identical to
|
|
12
|
+
* the React adapter — see the design note in `vue/SchemaComponent.vue`.
|
|
13
|
+
* Vue authors who prefer `v-model` / emit-based wiring use the
|
|
14
|
+
* top-level `<SchemaComponent>` and `<SchemaView>` SFCs (which translate
|
|
15
|
+
* the Vue-idiomatic surface back to the imperative `onChange`); the
|
|
16
|
+
* inner render functions consume the imperative shape directly so the
|
|
17
|
+
* dispatcher contract is uniform across adapters.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { VNode } from "vue";
|
|
21
|
+
import type { BaseRenderProps, RenderFunction } from "../core/renderer.ts";
|
|
22
|
+
import type { WalkedField } from "../core/types.ts";
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// VueRenderProps — per-field props passed to every Vue render function
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Props for Vue render functions. Extends {@link BaseRenderProps} with:
|
|
30
|
+
*
|
|
31
|
+
* - `onChange` — imperative callback to propagate value changes back to
|
|
32
|
+
* the host component. The top-level `<SchemaComponent>` SFC bridges
|
|
33
|
+
* this to a `change` emit / `v-model` update, so consumers writing
|
|
34
|
+
* pure render functions still operate against the same imperative
|
|
35
|
+
* contract as the React adapter.
|
|
36
|
+
* - `renderChild` — recursively renders a child field, threading
|
|
37
|
+
* `onChange` through the four-argument signature inherited from
|
|
38
|
+
* `RenderProps` so theme adapters (or future widget code) can share
|
|
39
|
+
* helpers between React and Vue with minimal adaptation.
|
|
40
|
+
*/
|
|
41
|
+
export interface VueRenderProps extends BaseRenderProps<VNode> {
|
|
42
|
+
/**
|
|
43
|
+
* Callback to update the field value. Wired to the Vue
|
|
44
|
+
* `@change` / `v-model` surface by the `<SchemaComponent>` SFC.
|
|
45
|
+
*/
|
|
46
|
+
onChange: (value: unknown) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Render a child field. Theme adapters call this to recursively
|
|
49
|
+
* render nested structures (object fields, array elements, union
|
|
50
|
+
* options).
|
|
51
|
+
*
|
|
52
|
+
* @param tree - The walked field tree for the child.
|
|
53
|
+
* @param value - The child's current value.
|
|
54
|
+
* @param onChange - Callback receiving the child's next value.
|
|
55
|
+
* @param pathSuffix - Path segment from the parent (e.g. `"city"`,
|
|
56
|
+
* `"[0]"`). Joined to the parent's path with a dot, or
|
|
57
|
+
* substituted when the parent acts as a transparent wrapper
|
|
58
|
+
* (union options). Required for every container — without it
|
|
59
|
+
* children inherit no path and `fieldDomId()` will throw.
|
|
60
|
+
*/
|
|
61
|
+
renderChild: (
|
|
62
|
+
tree: WalkedField,
|
|
63
|
+
value: unknown,
|
|
64
|
+
onChange: (v: unknown) => void,
|
|
65
|
+
pathSuffix?: string
|
|
66
|
+
) => VNode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// VueRenderFunction — render function signature
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Signature for a Vue render function. Specialisation of the generic
|
|
75
|
+
* {@link RenderFunction} with `Output = VNode` and
|
|
76
|
+
* `Props = VueRenderProps`.
|
|
77
|
+
*
|
|
78
|
+
* Composes cleanly with the generic dispatch in `core/renderField.ts`:
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* const r: VueRenderFunction = (props) => h("input", { value: props.value });
|
|
82
|
+
* const generic: RenderFunction<VNode, VueRenderProps> = r; // assignable
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export type VueRenderFunction = RenderFunction<VNode, VueRenderProps>;
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// VueComponentResolver — theme adapter interface for Vue
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Vue theme adapter — maps every schema field type to its Vue
|
|
93
|
+
* {@link VueRenderFunction}. Structurally mirrors the React
|
|
94
|
+
* `ComponentResolver` but produces `VNode`s.
|
|
95
|
+
*
|
|
96
|
+
* Unset keys fall back to the headless resolver. Pass to the
|
|
97
|
+
* `<SchemaProvider>` SFC or directly to a `<SchemaComponent>`'s
|
|
98
|
+
* `resolver` prop to drive every schema-driven render with a specific
|
|
99
|
+
* theme.
|
|
100
|
+
*/
|
|
101
|
+
export interface VueComponentResolver {
|
|
102
|
+
string?: VueRenderFunction;
|
|
103
|
+
number?: VueRenderFunction;
|
|
104
|
+
boolean?: VueRenderFunction;
|
|
105
|
+
null?: VueRenderFunction;
|
|
106
|
+
enum?: VueRenderFunction;
|
|
107
|
+
object?: VueRenderFunction;
|
|
108
|
+
array?: VueRenderFunction;
|
|
109
|
+
tuple?: VueRenderFunction;
|
|
110
|
+
record?: VueRenderFunction;
|
|
111
|
+
union?: VueRenderFunction;
|
|
112
|
+
discriminatedUnion?: VueRenderFunction;
|
|
113
|
+
conditional?: VueRenderFunction;
|
|
114
|
+
negation?: VueRenderFunction;
|
|
115
|
+
literal?: VueRenderFunction;
|
|
116
|
+
file?: VueRenderFunction;
|
|
117
|
+
never?: VueRenderFunction;
|
|
118
|
+
unknown?: VueRenderFunction;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// VueWidgetMap — scoped widget registry
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Widget map — maps component hints (from `.meta({ component })`) to
|
|
127
|
+
* {@link VueRenderFunction}s. Parallels the React `WidgetMap` but
|
|
128
|
+
* produces `VNode`s.
|
|
129
|
+
*
|
|
130
|
+
* Scoped at three levels:
|
|
131
|
+
*
|
|
132
|
+
* 1. **Per-instance** — `widgets` prop on `<SchemaComponent>`
|
|
133
|
+
* 2. **Context-scoped** — provided via {@link VueWidgetsContext}
|
|
134
|
+
* 3. **Global** — `registerWidget()` (app-wide defaults)
|
|
135
|
+
*
|
|
136
|
+
* Resolution order: instance → context → global → resolver → headless,
|
|
137
|
+
* mirroring the React resolution chain so dual-target consumers see
|
|
138
|
+
* identical fallback behaviour.
|
|
139
|
+
*/
|
|
140
|
+
export type VueWidgetMap = ReadonlyMap<string, VueRenderFunction>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient module declaration for `.vue` Single-File Component imports.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript cannot natively parse `.vue` files, so we shim every
|
|
5
|
+
* `*.vue` import as a `DefineComponent`. Vue's official tooling
|
|
6
|
+
* (`vue-tsc`, Volar in editor mode) reads the actual SFC and produces
|
|
7
|
+
* a fully-typed component definition; this shim provides a sound
|
|
8
|
+
* fallback for plain `tsc --noEmit` so the existing typecheck step
|
|
9
|
+
* (`packages/core/_typecheck`) continues to pass without depending on
|
|
10
|
+
* `vue-tsc` being added to the toolchain.
|
|
11
|
+
*
|
|
12
|
+
* The catch-all generics deliberately accept any prop shape — Vitest's
|
|
13
|
+
* `mount()` and the consuming SFCs supply concrete props at the call
|
|
14
|
+
* site, and the runtime Vue compiler validates them.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
declare module "*.vue" {
|
|
18
|
+
import type { DefineComponent } from "vue";
|
|
19
|
+
const component: DefineComponent<
|
|
20
|
+
Record<string, unknown>,
|
|
21
|
+
Record<string, unknown>,
|
|
22
|
+
unknown
|
|
23
|
+
>;
|
|
24
|
+
export default component;
|
|
25
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue widget registry — custom renderers keyed by `.meta({ component })`
|
|
3
|
+
* hint.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `react/SchemaComponent.tsx`'s widget surface: a module-level
|
|
6
|
+
* map storing app-wide widget defaults plus a clear hook used by tests
|
|
7
|
+
* to isolate state between cases. Scoped registration (per-instance,
|
|
8
|
+
* per-provider) lives on the corresponding props of the
|
|
9
|
+
* `<SchemaComponent>` / `<SchemaProvider>` SFCs.
|
|
10
|
+
*
|
|
11
|
+
* Resolution order, matching the React adapter: instance → context →
|
|
12
|
+
* global → resolver → headless.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { VueRenderFunction } from "./types.ts";
|
|
16
|
+
|
|
17
|
+
/** Global widget registry — app-wide defaults. */
|
|
18
|
+
const globalWidgets = new Map<string, VueRenderFunction>();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a widget globally. The widget is resolved when a schema field
|
|
22
|
+
* has `.meta({ component: name })`.
|
|
23
|
+
*
|
|
24
|
+
* For scoped registration, use the `widgets` prop on `<SchemaComponent>`
|
|
25
|
+
* or `<SchemaProvider>` instead.
|
|
26
|
+
*/
|
|
27
|
+
export function registerWidget(name: string, render: VueRenderFunction): void {
|
|
28
|
+
globalWidgets.set(name, render);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Look up a widget in the global registry. Used by the Vue dispatcher
|
|
33
|
+
* after the per-instance and context maps have been checked.
|
|
34
|
+
*/
|
|
35
|
+
export function lookupGlobalWidget(
|
|
36
|
+
name: string
|
|
37
|
+
): VueRenderFunction | undefined {
|
|
38
|
+
return globalWidgets.get(name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear every globally registered widget. Intended for test isolation
|
|
43
|
+
* — `registerWidget` writes to module-level state and that state
|
|
44
|
+
* otherwise leaks across test cases, making the test suite
|
|
45
|
+
* order-dependent. Tests should call this from an `afterEach` hook.
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export function __clearGlobalWidgets(): void {
|
|
50
|
+
globalWidgets.clear();
|
|
51
|
+
}
|