schema-components 2.0.2 → 2.1.1
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 +133 -1
- package/dist/SchemaComponent-B__6-5-E.d.mts +277 -0
- package/dist/SchemaComponent-BxzzsHsK.mjs +668 -0
- package/dist/adapter-ktQaheWB.d.mts +213 -0
- package/dist/constructorTypes-BdCiMS6e.d.mts +30 -0
- package/dist/core/adapter.d.mts +3 -213
- package/dist/core/constraintHint.d.mts +1 -1
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/contexts.d.mts +71 -0
- package/dist/core/contexts.mjs +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/{react → core}/fieldPath.d.mts +2 -2
- package/dist/{react → core}/fieldPath.mjs +3 -3
- package/dist/core/formats.d.mts +1 -1
- package/dist/core/inferValue.d.mts +1 -1
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/merge.d.mts +1 -1
- package/dist/core/normalise.d.mts +2 -2
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderField.d.mts +147 -0
- package/dist/core/renderField.mjs +115 -0
- package/dist/core/renderer.d.mts +2 -199
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/typeInference.d.mts +1 -982
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +3 -3
- package/dist/core/walker.d.mts +1 -1
- package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/renderToHtml.d.mts +5 -5
- package/dist/html/renderToHtml.mjs +33 -18
- package/dist/html/renderToHtmlStream.d.mts +5 -5
- package/dist/html/renderers.d.mts +1 -1
- package/dist/html/streamRenderers.d.mts +15 -6
- package/dist/html/streamRenderers.mjs +56 -10
- package/dist/{inferValue-Ce-PviSD.d.mts → inferValue-eAnh50EM.d.mts} +3 -3
- package/dist/lit/SchemaComponent.d.mts +125 -0
- package/dist/lit/SchemaComponent.mjs +2 -0
- package/dist/lit/SchemaField.d.mts +65 -0
- package/dist/lit/SchemaField.mjs +2 -0
- package/dist/lit/SchemaView.d.mts +14 -0
- package/dist/lit/SchemaView.mjs +2 -0
- package/dist/lit/constructorTypes.d.mts +2 -0
- package/dist/lit/constructorTypes.mjs +1 -0
- package/dist/lit/contexts.d.mts +78 -0
- package/dist/lit/contexts.mjs +238 -0
- package/dist/lit/defaultResolver.d.mts +33 -0
- package/dist/lit/defaultResolver.mjs +2 -0
- package/dist/lit/registry.d.mts +66 -0
- package/dist/lit/registry.mjs +2 -0
- package/dist/lit/renderers/baseElement.d.mts +131 -0
- package/dist/lit/renderers/baseElement.mjs +109 -0
- package/dist/lit/renderers/recordHelpers.d.mts +25 -0
- package/dist/lit/renderers/recordHelpers.mjs +55 -0
- package/dist/lit/renderers/scArray.d.mts +14 -0
- package/dist/lit/renderers/scArray.mjs +86 -0
- package/dist/lit/renderers/scBoolean.d.mts +15 -0
- package/dist/lit/renderers/scBoolean.mjs +47 -0
- package/dist/lit/renderers/scConditional.d.mts +23 -0
- package/dist/lit/renderers/scConditional.mjs +65 -0
- package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
- package/dist/lit/renderers/scDiscriminated.mjs +138 -0
- package/dist/lit/renderers/scEnum.d.mts +16 -0
- package/dist/lit/renderers/scEnum.mjs +66 -0
- package/dist/lit/renderers/scFile.d.mts +15 -0
- package/dist/lit/renderers/scFile.mjs +53 -0
- package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
- package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
- package/dist/lit/renderers/scNumber.d.mts +15 -0
- package/dist/lit/renderers/scNumber.mjs +64 -0
- package/dist/lit/renderers/scObject.d.mts +14 -0
- package/dist/lit/renderers/scObject.mjs +57 -0
- package/dist/lit/renderers/scRecord.d.mts +14 -0
- package/dist/lit/renderers/scRecord.mjs +112 -0
- package/dist/lit/renderers/scString.d.mts +19 -0
- package/dist/lit/renderers/scString.mjs +165 -0
- package/dist/lit/renderers/scTuple.d.mts +14 -0
- package/dist/lit/renderers/scTuple.mjs +58 -0
- package/dist/lit/renderers/scUnion.d.mts +14 -0
- package/dist/lit/renderers/scUnion.mjs +44 -0
- package/dist/lit/renderers/scUnknown.d.mts +15 -0
- package/dist/lit/renderers/scUnknown.mjs +45 -0
- package/dist/lit/ssr.d.mts +37 -0
- package/dist/lit/ssr.mjs +9565 -0
- package/dist/lit/types.d.mts +2 -0
- package/dist/lit/types.mjs +1 -0
- package/dist/lit/widget.d.mts +71 -0
- package/dist/lit/widget.mjs +87 -0
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/components.d.mts +4 -4
- package/dist/openapi/parser.d.mts +2 -2
- package/dist/openapi/resolve.d.mts +1 -1
- package/dist/preact/SchemaComponent.d.mts +3 -0
- package/dist/preact/SchemaComponent.mjs +26 -0
- package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
- package/dist/preact/SchemaErrorBoundary.mjs +20 -0
- package/dist/preact/SchemaView.d.mts +2 -0
- package/dist/preact/SchemaView.mjs +22 -0
- package/dist/preact/headless.d.mts +2 -0
- package/dist/preact/headless.mjs +18 -0
- package/dist/react/SchemaComponent.d.mts +3 -270
- package/dist/react/SchemaComponent.mjs +41 -32
- package/dist/react/SchemaView.d.mts +6 -6
- package/dist/react/SchemaView.mjs +32 -29
- package/dist/react/a11y.d.mts +2 -2
- package/dist/react/fieldShell.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
- package/dist/renderer-ab9E52Bp.d.mts +245 -0
- package/dist/solid/SchemaComponent.d.mts +136 -0
- package/dist/solid/SchemaComponent.mjs +391 -0
- package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
- package/dist/solid/SchemaErrorBoundary.mjs +57 -0
- package/dist/solid/SchemaField.d.mts +40 -0
- package/dist/solid/SchemaField.mjs +113 -0
- package/dist/solid/SchemaView.d.mts +54 -0
- package/dist/solid/SchemaView.mjs +168 -0
- package/dist/solid/a11y.d.mts +70 -0
- package/dist/solid/a11y.mjs +71 -0
- package/dist/solid/contexts.d.mts +37 -0
- package/dist/solid/contexts.mjs +66 -0
- package/dist/solid/headless.d.mts +10 -0
- package/dist/solid/headless.mjs +27 -0
- package/dist/solid/renderers.d.mts +79 -0
- package/dist/solid/renderers.mjs +840 -0
- package/dist/solid/types.d.mts +90 -0
- package/dist/solid/types.mjs +1 -0
- package/dist/solid/widget.d.mts +29 -0
- package/dist/solid/widget.mjs +35 -0
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
- package/dist/types-BCy7K3nk.d.mts +125 -0
- package/package.json +73 -1
- package/src/svelte/SchemaComponent.svelte +427 -0
- package/src/svelte/SchemaErrorBoundary.svelte +66 -0
- package/src/svelte/SchemaField.svelte +216 -0
- package/src/svelte/SchemaProvider.svelte +46 -0
- package/src/svelte/SchemaView.svelte +244 -0
- package/src/svelte/a11y.ts +112 -0
- package/src/svelte/contexts.ts +79 -0
- package/src/svelte/dispatch.ts +267 -0
- package/src/svelte/headless.ts +73 -0
- package/src/svelte/headlessFns.ts +124 -0
- package/src/svelte/renderers/Array.svelte +98 -0
- package/src/svelte/renderers/Boolean.svelte +43 -0
- package/src/svelte/renderers/Conditional.svelte +67 -0
- package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
- package/src/svelte/renderers/Enum.svelte +53 -0
- package/src/svelte/renderers/Fallback.svelte +24 -0
- package/src/svelte/renderers/File.svelte +46 -0
- package/src/svelte/renderers/Literal.svelte +29 -0
- package/src/svelte/renderers/Mount.svelte +24 -0
- package/src/svelte/renderers/Negation.svelte +35 -0
- package/src/svelte/renderers/Never.svelte +24 -0
- package/src/svelte/renderers/Null.svelte +19 -0
- package/src/svelte/renderers/Number.svelte +68 -0
- package/src/svelte/renderers/Object.svelte +74 -0
- package/src/svelte/renderers/Record.svelte +134 -0
- package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
- package/src/svelte/renderers/String.svelte +152 -0
- package/src/svelte/renderers/Tuple.svelte +84 -0
- package/src/svelte/renderers/Union.svelte +49 -0
- package/src/svelte/renderers/Unknown.svelte +42 -0
- package/src/svelte/svelte-modules.d.ts +25 -0
- package/src/svelte/types.ts +238 -0
- package/src/svelte/widget.ts +62 -0
- 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
- /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
- /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
- /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
- /package/dist/{version-DL8U5RuA.d.mts → version-BEBx10ND.d.mts} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Headless Svelte 5 renderer for `RecordField` — editable key/value
|
|
3
|
+
rows with add / remove controls. Mirror of
|
|
4
|
+
`react/headlessRenderers.tsx :: renderRecord`.
|
|
5
|
+
|
|
6
|
+
Read-only mode renders a labelled list, falling back to an
|
|
7
|
+
em-dash placeholder when empty. Editable mode wraps each entry
|
|
8
|
+
in a row with a renameable key input, the value editor, and a
|
|
9
|
+
Remove button; the footer Add button appends a new entry with a
|
|
10
|
+
type-appropriate default via `defaultRecordValue`.
|
|
11
|
+
|
|
12
|
+
Key rename uses `onblur` rather than `oninput` so intermediate
|
|
13
|
+
typing states don't trigger early `onChange` calls — matches the
|
|
14
|
+
React renderer's `onBlur` semantics.
|
|
15
|
+
-->
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
18
|
+
import { fieldDomId } from "../../core/idPath.ts";
|
|
19
|
+
import { EM_DASH } from "../../core/cssClasses.ts";
|
|
20
|
+
import { isObject } from "../../core/guards.ts";
|
|
21
|
+
import { ariaLabel } from "../a11y.ts";
|
|
22
|
+
import {
|
|
23
|
+
defaultRecordValue,
|
|
24
|
+
nextRecordKey,
|
|
25
|
+
renameRecordKey,
|
|
26
|
+
} from "../headlessFns.ts";
|
|
27
|
+
import Mount from "./Mount.svelte";
|
|
28
|
+
|
|
29
|
+
const props = $props<SvelteRenderProps>();
|
|
30
|
+
|
|
31
|
+
const obj = $derived<Record<string, unknown>>(
|
|
32
|
+
isObject(props.value) ? props.value : {}
|
|
33
|
+
);
|
|
34
|
+
const valueType = $derived(
|
|
35
|
+
props.tree.type === "record" ? props.tree.valueType : undefined
|
|
36
|
+
);
|
|
37
|
+
const entries = $derived(Object.entries(obj));
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
{#if props.tree.type === "record" && valueType !== undefined}
|
|
41
|
+
{#if props.readOnly}
|
|
42
|
+
{#if entries.length === 0}
|
|
43
|
+
<span>{EM_DASH}</span>
|
|
44
|
+
{:else}
|
|
45
|
+
<div role="group" aria-label={ariaLabel(props.meta.description)}>
|
|
46
|
+
{#each entries as [key, value] (key)}
|
|
47
|
+
{@const childId = fieldDomId(`${props.path}.${key}`)}
|
|
48
|
+
{@const child = props.renderChild(
|
|
49
|
+
valueType,
|
|
50
|
+
value,
|
|
51
|
+
() => {
|
|
52
|
+
/* read-only: noop */
|
|
53
|
+
},
|
|
54
|
+
key
|
|
55
|
+
)}
|
|
56
|
+
<div>
|
|
57
|
+
<label for={childId}>{key}</label>
|
|
58
|
+
{#if child !== null}
|
|
59
|
+
<Mount descriptor={child} />
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
{/each}
|
|
63
|
+
</div>
|
|
64
|
+
{/if}
|
|
65
|
+
{:else}
|
|
66
|
+
{@const handleRename = (oldKey: string, newKey: string) => {
|
|
67
|
+
const renamed = renameRecordKey(obj, oldKey, newKey);
|
|
68
|
+
if (renamed === obj) return;
|
|
69
|
+
props.onChange(renamed);
|
|
70
|
+
}}
|
|
71
|
+
{@const handleValueChange = (key: string, nextValue: unknown) => {
|
|
72
|
+
const updated: Record<string, unknown> = {};
|
|
73
|
+
for (const [k, val] of Object.entries(obj)) {
|
|
74
|
+
updated[k] = val;
|
|
75
|
+
}
|
|
76
|
+
updated[key] = nextValue;
|
|
77
|
+
props.onChange(updated);
|
|
78
|
+
}}
|
|
79
|
+
{@const handleRemove = (key: string) => {
|
|
80
|
+
const next: Record<string, unknown> = {};
|
|
81
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
82
|
+
if (k === key) continue;
|
|
83
|
+
next[k] = v;
|
|
84
|
+
}
|
|
85
|
+
props.onChange(next);
|
|
86
|
+
}}
|
|
87
|
+
{@const handleAdd = () => {
|
|
88
|
+
const newKey = nextRecordKey(Object.keys(obj));
|
|
89
|
+
const next: Record<string, unknown> = { ...obj };
|
|
90
|
+
next[newKey] = defaultRecordValue(valueType);
|
|
91
|
+
props.onChange(next);
|
|
92
|
+
}}
|
|
93
|
+
<div role="group" aria-label={ariaLabel(props.meta.description)}>
|
|
94
|
+
{#each entries as [key, value] (key)}
|
|
95
|
+
{@const childId = fieldDomId(`${props.path}.${key}`)}
|
|
96
|
+
{@const keyId = `${childId}-key`}
|
|
97
|
+
{@const childOnChange = (v: unknown) => {
|
|
98
|
+
handleValueChange(key, v);
|
|
99
|
+
}}
|
|
100
|
+
{@const child = props.renderChild(
|
|
101
|
+
valueType,
|
|
102
|
+
value,
|
|
103
|
+
childOnChange,
|
|
104
|
+
key
|
|
105
|
+
)}
|
|
106
|
+
<div>
|
|
107
|
+
<input
|
|
108
|
+
id={keyId}
|
|
109
|
+
type="text"
|
|
110
|
+
aria-label="Entry key"
|
|
111
|
+
value={key}
|
|
112
|
+
onblur={(e) => {
|
|
113
|
+
const target = e.currentTarget;
|
|
114
|
+
if (target instanceof HTMLInputElement) {
|
|
115
|
+
handleRename(key, target.value);
|
|
116
|
+
}
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
{#if child !== null}
|
|
120
|
+
<Mount descriptor={child} />
|
|
121
|
+
{/if}
|
|
122
|
+
<button
|
|
123
|
+
type="button"
|
|
124
|
+
aria-label={`Remove entry ${key}`}
|
|
125
|
+
onclick={() => handleRemove(key)}>Remove</button
|
|
126
|
+
>
|
|
127
|
+
</div>
|
|
128
|
+
{/each}
|
|
129
|
+
<button type="button" aria-label="Add entry" onclick={handleAdd}
|
|
130
|
+
>Add</button
|
|
131
|
+
>
|
|
132
|
+
</div>
|
|
133
|
+
{/if}
|
|
134
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Recursion-cap sentinel rendered when the Svelte dispatcher reaches
|
|
3
|
+
`MAX_RENDER_DEPTH`. Mirror of the React recursion sentinel
|
|
4
|
+
(a `<fieldset>` with an em-dashed "(recursive)" label) from
|
|
5
|
+
`react/SchemaComponent.tsx :: renderField`.
|
|
6
|
+
|
|
7
|
+
Receives the standard {@link SvelteRenderProps} bag and renders
|
|
8
|
+
only the `meta.description` (falling back to "schema") inside a
|
|
9
|
+
`<fieldset>` — the children are intentionally elided to break the
|
|
10
|
+
recursion.
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
14
|
+
import { SC_CLASSES } from "../../core/cssClasses.ts";
|
|
15
|
+
|
|
16
|
+
const props = $props<SvelteRenderProps>();
|
|
17
|
+
|
|
18
|
+
const label = $derived(
|
|
19
|
+
typeof props.meta.description === "string"
|
|
20
|
+
? props.meta.description
|
|
21
|
+
: "schema"
|
|
22
|
+
);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<fieldset class={SC_CLASSES.recursive}>
|
|
26
|
+
<em>{`↻ ${label} (recursive)`}</em>
|
|
27
|
+
</fieldset>
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Headless Svelte 5 renderer for `StringField` — plain `<input>` /
|
|
3
|
+
`<span>` mirror of `react/headlessRenderers.tsx :: renderString`.
|
|
4
|
+
|
|
5
|
+
Honours the same accessibility wiring as the React equivalent:
|
|
6
|
+
`aria-required`, `aria-describedby`, an optional sibling
|
|
7
|
+
`<small class="sc-hint">` and a forced `aria-label` when the
|
|
8
|
+
`meta.description` is supplied. Date / time / email / URL formats
|
|
9
|
+
swap the `<input type="…">` accordingly; password format triggers
|
|
10
|
+
masking and an `autocomplete="(current|new)-password"` hint.
|
|
11
|
+
|
|
12
|
+
Read-only mode renders a `<span>` (or a safe `<a>` for valid
|
|
13
|
+
`mailto:` / `https:` URIs). All editable inputs propagate value
|
|
14
|
+
changes via the supplied `props.onChange` callback — no synthetic
|
|
15
|
+
events, no two-way binding, just a raw DOM `onchange` handler.
|
|
16
|
+
-->
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
19
|
+
import { fieldDomId } from "../../core/idPath.ts";
|
|
20
|
+
import { dateInputType } from "../../core/formats.ts";
|
|
21
|
+
import { isSafeHyperlink, isSafeMailtoAddress } from "../../core/uri.ts";
|
|
22
|
+
import { displayJsonValue } from "../../core/walkBuilders.ts";
|
|
23
|
+
import { EM_DASH, ELLIPSIS } from "../../core/cssClasses.ts";
|
|
24
|
+
import { buildAriaAttrs, buildHintInfo } from "../a11y.ts";
|
|
25
|
+
|
|
26
|
+
const props = $props<SvelteRenderProps>();
|
|
27
|
+
|
|
28
|
+
const id = $derived(fieldDomId(props.path));
|
|
29
|
+
const strValue = $derived(
|
|
30
|
+
typeof props.value === "string" ? props.value : ""
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
function formatDateTime(value: string): string {
|
|
34
|
+
const date = new Date(value);
|
|
35
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
36
|
+
return date.toLocaleString();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatDate(value: string): string {
|
|
40
|
+
const date = new Date(value);
|
|
41
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
42
|
+
return date.toLocaleDateString();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function formatTime(value: string): string {
|
|
46
|
+
const date = new Date(value);
|
|
47
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
48
|
+
return date.toLocaleTimeString();
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
{#if props.readOnly}
|
|
53
|
+
{@const readValue = typeof props.value === "string" ? props.value : ""}
|
|
54
|
+
{#if readValue.length === 0}
|
|
55
|
+
<span {id}>{EM_DASH}</span>
|
|
56
|
+
{:else if props.constraints.format === "email" && isSafeMailtoAddress(readValue)}
|
|
57
|
+
<!-- svelte-ignore a11y_role_supports_aria_props_implicit -->
|
|
58
|
+
<a href={`mailto:${readValue}`} {id} aria-readonly="true">{readValue}</a>
|
|
59
|
+
{:else if (props.constraints.format === "uri" || props.constraints.format === "url") && isSafeHyperlink(readValue)}
|
|
60
|
+
<!-- svelte-ignore a11y_role_supports_aria_props_implicit -->
|
|
61
|
+
<a href={readValue} {id} aria-readonly="true">{readValue}</a>
|
|
62
|
+
{:else if props.constraints.format === "date"}
|
|
63
|
+
<span {id}>{formatDate(readValue)}</span>
|
|
64
|
+
{:else if props.constraints.format === "time"}
|
|
65
|
+
<span {id}>{formatTime(readValue)}</span>
|
|
66
|
+
{:else if props.constraints.format === "date-time" || props.constraints.format === "datetime"}
|
|
67
|
+
<span {id}>{formatDateTime(readValue)}</span>
|
|
68
|
+
{:else}
|
|
69
|
+
<span {id}>{readValue}</span>
|
|
70
|
+
{/if}
|
|
71
|
+
{:else}
|
|
72
|
+
{@const dateType = dateInputType(props.constraints.format)}
|
|
73
|
+
{@const ariaAttrs = buildAriaAttrs(props.tree)}
|
|
74
|
+
{@const hintInfo = buildHintInfo(id, props.constraints)}
|
|
75
|
+
{@const enumValues =
|
|
76
|
+
props.tree.type === "enum" ? props.tree.enumValues : []}
|
|
77
|
+
{@const isCredential =
|
|
78
|
+
props.writeOnly === true && props.constraints.format === "password"}
|
|
79
|
+
{@const inputType = isCredential
|
|
80
|
+
? "password"
|
|
81
|
+
: props.constraints.format === "email"
|
|
82
|
+
? "email"
|
|
83
|
+
: props.constraints.format === "uri"
|
|
84
|
+
? "url"
|
|
85
|
+
: "text"}
|
|
86
|
+
{@const autoComplete = isCredential
|
|
87
|
+
? strValue.length > 0
|
|
88
|
+
? "current-password"
|
|
89
|
+
: "new-password"
|
|
90
|
+
: undefined}
|
|
91
|
+
{@const placeholderText =
|
|
92
|
+
typeof props.meta.description === "string"
|
|
93
|
+
? props.meta.description
|
|
94
|
+
: undefined}
|
|
95
|
+
|
|
96
|
+
{#if dateType !== undefined}
|
|
97
|
+
<input
|
|
98
|
+
{id}
|
|
99
|
+
type={dateType}
|
|
100
|
+
value={props.writeOnly ? "" : strValue}
|
|
101
|
+
onchange={(e) => {
|
|
102
|
+
const target = e.currentTarget;
|
|
103
|
+
if (target instanceof HTMLInputElement) {
|
|
104
|
+
props.onChange(target.value);
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
aria-describedby={hintInfo?.ariaDescribedBy}
|
|
108
|
+
{...ariaAttrs}
|
|
109
|
+
/>
|
|
110
|
+
{:else if props.tree.type === "enum" && enumValues.length > 0}
|
|
111
|
+
<select
|
|
112
|
+
{id}
|
|
113
|
+
value={props.writeOnly ? "" : strValue}
|
|
114
|
+
onchange={(e) => {
|
|
115
|
+
const target = e.currentTarget;
|
|
116
|
+
if (target instanceof HTMLSelectElement) {
|
|
117
|
+
props.onChange(target.value);
|
|
118
|
+
}
|
|
119
|
+
}}
|
|
120
|
+
aria-describedby={hintInfo?.ariaDescribedBy}
|
|
121
|
+
{...ariaAttrs}
|
|
122
|
+
>
|
|
123
|
+
<option value="">Select{ELLIPSIS}</option>
|
|
124
|
+
{#each enumValues as v (displayJsonValue(v))}
|
|
125
|
+
<option value={displayJsonValue(v)}
|
|
126
|
+
>{displayJsonValue(v)}</option
|
|
127
|
+
>
|
|
128
|
+
{/each}
|
|
129
|
+
</select>
|
|
130
|
+
{:else}
|
|
131
|
+
<input
|
|
132
|
+
{id}
|
|
133
|
+
type={inputType}
|
|
134
|
+
autocomplete={autoComplete}
|
|
135
|
+
value={props.writeOnly ? "" : strValue}
|
|
136
|
+
onchange={(e) => {
|
|
137
|
+
const target = e.currentTarget;
|
|
138
|
+
if (target instanceof HTMLInputElement) {
|
|
139
|
+
props.onChange(target.value);
|
|
140
|
+
}
|
|
141
|
+
}}
|
|
142
|
+
placeholder={placeholderText}
|
|
143
|
+
minlength={props.constraints.minLength}
|
|
144
|
+
maxlength={props.constraints.maxLength}
|
|
145
|
+
aria-describedby={hintInfo?.ariaDescribedBy}
|
|
146
|
+
{...ariaAttrs}
|
|
147
|
+
/>
|
|
148
|
+
{/if}
|
|
149
|
+
{#if hintInfo !== undefined}
|
|
150
|
+
<small id={hintInfo.id} class="sc-hint">{hintInfo.hint}</small>
|
|
151
|
+
{/if}
|
|
152
|
+
{/if}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Headless Svelte 5 renderer for tuple fields — positional
|
|
3
|
+
rendering of `prefixItems` followed by any `restItems`. Mirror of
|
|
4
|
+
`react/headlessRenderers.tsx :: renderTuple`.
|
|
5
|
+
|
|
6
|
+
Each prefix entry is rendered at its index with structural suffix
|
|
7
|
+
`[i]`. Rest items beyond `prefixItems.length` are rendered when a
|
|
8
|
+
`restItems` schema is present and the value array is longer than
|
|
9
|
+
the prefix.
|
|
10
|
+
|
|
11
|
+
Renders nothing when the tuple has no prefix items, no rest
|
|
12
|
+
schema, and no values — keeps empty positions from emitting
|
|
13
|
+
pointless wrappers.
|
|
14
|
+
-->
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
17
|
+
import { ariaLabel } from "../a11y.ts";
|
|
18
|
+
import Mount from "./Mount.svelte";
|
|
19
|
+
|
|
20
|
+
const props = $props<SvelteRenderProps>();
|
|
21
|
+
|
|
22
|
+
const prefixItems = $derived(
|
|
23
|
+
props.tree.type === "tuple" ? props.tree.prefixItems : []
|
|
24
|
+
);
|
|
25
|
+
const restItems = $derived(
|
|
26
|
+
props.tree.type === "tuple" ? props.tree.restItems : undefined
|
|
27
|
+
);
|
|
28
|
+
const arr = $derived<unknown[]>(
|
|
29
|
+
Array.isArray(props.value) ? props.value : []
|
|
30
|
+
);
|
|
31
|
+
const restCount = $derived(
|
|
32
|
+
restItems !== undefined
|
|
33
|
+
? Math.max(arr.length - prefixItems.length, 0)
|
|
34
|
+
: 0
|
|
35
|
+
);
|
|
36
|
+
const shouldRender = $derived(
|
|
37
|
+
prefixItems.length > 0 || restItems !== undefined || arr.length > 0
|
|
38
|
+
);
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
{#if props.tree.type === "tuple" && shouldRender}
|
|
42
|
+
<div role="group" aria-label={ariaLabel(props.meta.description)}>
|
|
43
|
+
{#each prefixItems as element, i (i)}
|
|
44
|
+
{@const itemValue = arr[i]}
|
|
45
|
+
{@const childOnChange = (v: unknown) => {
|
|
46
|
+
const next = arr.slice();
|
|
47
|
+
next[i] = v;
|
|
48
|
+
props.onChange(next);
|
|
49
|
+
}}
|
|
50
|
+
{@const child = props.renderChild(
|
|
51
|
+
element,
|
|
52
|
+
itemValue,
|
|
53
|
+
childOnChange,
|
|
54
|
+
`[${String(i)}]`
|
|
55
|
+
)}
|
|
56
|
+
<div>
|
|
57
|
+
{#if child !== null}
|
|
58
|
+
<Mount descriptor={child} />
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
61
|
+
{/each}
|
|
62
|
+
{#if restItems !== undefined}
|
|
63
|
+
{#each Array.from({ length: restCount }, (_, j) => prefixItems.length + j) as i (i)}
|
|
64
|
+
{@const itemValue = arr[i]}
|
|
65
|
+
{@const childOnChange = (v: unknown) => {
|
|
66
|
+
const next = arr.slice();
|
|
67
|
+
next[i] = v;
|
|
68
|
+
props.onChange(next);
|
|
69
|
+
}}
|
|
70
|
+
{@const child = props.renderChild(
|
|
71
|
+
restItems,
|
|
72
|
+
itemValue,
|
|
73
|
+
childOnChange,
|
|
74
|
+
`[${String(i)}]`
|
|
75
|
+
)}
|
|
76
|
+
<div>
|
|
77
|
+
{#if child !== null}
|
|
78
|
+
<Mount descriptor={child} />
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
{/each}
|
|
82
|
+
{/if}
|
|
83
|
+
</div>
|
|
84
|
+
{/if}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Headless Svelte 5 renderer for plain `UnionField` — picks the
|
|
3
|
+
matching option from the union's `options` list and renders it.
|
|
4
|
+
Mirror of `react/headlessRenderers.tsx :: renderUnion`.
|
|
5
|
+
|
|
6
|
+
Falls back to the first option when no structural match is found,
|
|
7
|
+
matching React's behaviour. Empty unions render an em-dash for
|
|
8
|
+
nullish values or `JSON.stringify(value)` otherwise.
|
|
9
|
+
-->
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
12
|
+
import { EM_DASH } from "../../core/cssClasses.ts";
|
|
13
|
+
import { matchUnionOption } from "../../core/unionMatch.ts";
|
|
14
|
+
import Mount from "./Mount.svelte";
|
|
15
|
+
|
|
16
|
+
const props = $props<SvelteRenderProps>();
|
|
17
|
+
|
|
18
|
+
const options = $derived(
|
|
19
|
+
props.tree.type === "union" ||
|
|
20
|
+
props.tree.type === "discriminatedUnion"
|
|
21
|
+
? props.tree.options
|
|
22
|
+
: undefined
|
|
23
|
+
);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
{#if options === undefined || options.length === 0}
|
|
27
|
+
{#if props.value === undefined || props.value === null}
|
|
28
|
+
<span>{EM_DASH}</span>
|
|
29
|
+
{:else}
|
|
30
|
+
<span>{JSON.stringify(props.value)}</span>
|
|
31
|
+
{/if}
|
|
32
|
+
{:else}
|
|
33
|
+
{@const matched = matchUnionOption(options, props.value)}
|
|
34
|
+
{@const chosen = matched ?? options[0]}
|
|
35
|
+
{#if chosen !== undefined}
|
|
36
|
+
{@const child = props.renderChild(
|
|
37
|
+
chosen,
|
|
38
|
+
props.value,
|
|
39
|
+
props.onChange
|
|
40
|
+
)}
|
|
41
|
+
{#if child !== null}
|
|
42
|
+
<Mount descriptor={child} />
|
|
43
|
+
{:else}
|
|
44
|
+
<span>{EM_DASH}</span>
|
|
45
|
+
{/if}
|
|
46
|
+
{:else}
|
|
47
|
+
<span>{EM_DASH}</span>
|
|
48
|
+
{/if}
|
|
49
|
+
{/if}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Headless Svelte 5 renderer for `UnknownField` — JSON-encoded
|
|
3
|
+
fallback for unconstrained values. Mirror of
|
|
4
|
+
`react/headlessRenderers.tsx :: renderUnknown`.
|
|
5
|
+
|
|
6
|
+
Read-only mode stringifies non-string values through
|
|
7
|
+
`JSON.stringify` so any JSON-shaped value renders intelligibly.
|
|
8
|
+
Editable mode emits a plain text input so the user can edit at
|
|
9
|
+
least the string projection of the value.
|
|
10
|
+
-->
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import type { SvelteRenderProps } from "../types.ts";
|
|
13
|
+
import { fieldDomId } from "../../core/idPath.ts";
|
|
14
|
+
import { EM_DASH } from "../../core/cssClasses.ts";
|
|
15
|
+
|
|
16
|
+
const props = $props<SvelteRenderProps>();
|
|
17
|
+
|
|
18
|
+
const id = $derived(fieldDomId(props.path));
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
{#if props.readOnly}
|
|
22
|
+
{#if props.value === undefined || props.value === null}
|
|
23
|
+
<span {id}>{EM_DASH}</span>
|
|
24
|
+
{:else if typeof props.value === "string"}
|
|
25
|
+
<span {id}>{props.value}</span>
|
|
26
|
+
{:else}
|
|
27
|
+
<span {id}>{JSON.stringify(props.value)}</span>
|
|
28
|
+
{/if}
|
|
29
|
+
{:else}
|
|
30
|
+
{@const strValue = typeof props.value === "string" ? props.value : ""}
|
|
31
|
+
<input
|
|
32
|
+
{id}
|
|
33
|
+
type="text"
|
|
34
|
+
value={props.writeOnly ? "" : strValue}
|
|
35
|
+
onchange={(e) => {
|
|
36
|
+
const target = e.currentTarget;
|
|
37
|
+
if (target instanceof HTMLInputElement) {
|
|
38
|
+
props.onChange(target.value);
|
|
39
|
+
}
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
{/if}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient module declaration for `.svelte` imports inside the
|
|
3
|
+
* Svelte adapter source tree.
|
|
4
|
+
*
|
|
5
|
+
* TypeScript does not natively understand `.svelte` files — the
|
|
6
|
+
* `@sveltejs/vite-plugin-svelte` toolchain compiles them at build /
|
|
7
|
+
* test time. This declaration tells TypeScript that any `.svelte`
|
|
8
|
+
* import resolves to a default-exported
|
|
9
|
+
* `Component<SvelteRenderProps>` constructor (or a
|
|
10
|
+
* `Record<string, unknown>`-keyed superset for renderer-internal
|
|
11
|
+
* components like `Mount.svelte` that accept their own shape).
|
|
12
|
+
*
|
|
13
|
+
* The declaration is intentionally broad — every `.svelte` module
|
|
14
|
+
* is treated as a `Component<Record<string, unknown>>` constructor.
|
|
15
|
+
* The Svelte 5 `Component<Props>` type is contravariant in `Props`,
|
|
16
|
+
* so the broad declaration is assignable to any specific
|
|
17
|
+
* `Component<…>` slot at the consumer site without casts.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
declare module "*.svelte" {
|
|
21
|
+
import type { Component } from "svelte";
|
|
22
|
+
|
|
23
|
+
const component: Component<Record<string, unknown>>;
|
|
24
|
+
export default component;
|
|
25
|
+
}
|