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.
Files changed (197) hide show
  1. package/README.md +133 -1
  2. package/dist/SchemaComponent-B__6-5-E.d.mts +277 -0
  3. package/dist/SchemaComponent-BxzzsHsK.mjs +668 -0
  4. package/dist/adapter-ktQaheWB.d.mts +213 -0
  5. package/dist/constructorTypes-BdCiMS6e.d.mts +30 -0
  6. package/dist/core/adapter.d.mts +3 -213
  7. package/dist/core/constraintHint.d.mts +1 -1
  8. package/dist/core/constraints.d.mts +2 -2
  9. package/dist/core/contexts.d.mts +71 -0
  10. package/dist/core/contexts.mjs +1 -0
  11. package/dist/core/diagnostics.d.mts +1 -1
  12. package/dist/core/errors.d.mts +1 -1
  13. package/dist/core/fieldOrder.d.mts +1 -1
  14. package/dist/{react → core}/fieldPath.d.mts +2 -2
  15. package/dist/{react → core}/fieldPath.mjs +3 -3
  16. package/dist/core/formats.d.mts +1 -1
  17. package/dist/core/inferValue.d.mts +1 -1
  18. package/dist/core/limits.d.mts +1 -1
  19. package/dist/core/merge.d.mts +1 -1
  20. package/dist/core/normalise.d.mts +2 -2
  21. package/dist/core/ref.d.mts +1 -1
  22. package/dist/core/renderField.d.mts +147 -0
  23. package/dist/core/renderField.mjs +115 -0
  24. package/dist/core/renderer.d.mts +2 -199
  25. package/dist/core/swagger2.d.mts +1 -1
  26. package/dist/core/typeInference.d.mts +1 -982
  27. package/dist/core/types.d.mts +1 -1
  28. package/dist/core/unionMatch.d.mts +1 -1
  29. package/dist/core/version.d.mts +1 -1
  30. package/dist/core/walkBuilders.d.mts +3 -3
  31. package/dist/core/walker.d.mts +1 -1
  32. package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
  33. package/dist/html/a11y.d.mts +2 -2
  34. package/dist/html/renderToHtml.d.mts +5 -5
  35. package/dist/html/renderToHtml.mjs +33 -18
  36. package/dist/html/renderToHtmlStream.d.mts +5 -5
  37. package/dist/html/renderers.d.mts +1 -1
  38. package/dist/html/streamRenderers.d.mts +15 -6
  39. package/dist/html/streamRenderers.mjs +56 -10
  40. package/dist/{inferValue-Ce-PviSD.d.mts → inferValue-eAnh50EM.d.mts} +3 -3
  41. package/dist/lit/SchemaComponent.d.mts +125 -0
  42. package/dist/lit/SchemaComponent.mjs +2 -0
  43. package/dist/lit/SchemaField.d.mts +65 -0
  44. package/dist/lit/SchemaField.mjs +2 -0
  45. package/dist/lit/SchemaView.d.mts +14 -0
  46. package/dist/lit/SchemaView.mjs +2 -0
  47. package/dist/lit/constructorTypes.d.mts +2 -0
  48. package/dist/lit/constructorTypes.mjs +1 -0
  49. package/dist/lit/contexts.d.mts +78 -0
  50. package/dist/lit/contexts.mjs +238 -0
  51. package/dist/lit/defaultResolver.d.mts +33 -0
  52. package/dist/lit/defaultResolver.mjs +2 -0
  53. package/dist/lit/registry.d.mts +66 -0
  54. package/dist/lit/registry.mjs +2 -0
  55. package/dist/lit/renderers/baseElement.d.mts +131 -0
  56. package/dist/lit/renderers/baseElement.mjs +109 -0
  57. package/dist/lit/renderers/recordHelpers.d.mts +25 -0
  58. package/dist/lit/renderers/recordHelpers.mjs +55 -0
  59. package/dist/lit/renderers/scArray.d.mts +14 -0
  60. package/dist/lit/renderers/scArray.mjs +86 -0
  61. package/dist/lit/renderers/scBoolean.d.mts +15 -0
  62. package/dist/lit/renderers/scBoolean.mjs +47 -0
  63. package/dist/lit/renderers/scConditional.d.mts +23 -0
  64. package/dist/lit/renderers/scConditional.mjs +65 -0
  65. package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
  66. package/dist/lit/renderers/scDiscriminated.mjs +138 -0
  67. package/dist/lit/renderers/scEnum.d.mts +16 -0
  68. package/dist/lit/renderers/scEnum.mjs +66 -0
  69. package/dist/lit/renderers/scFile.d.mts +15 -0
  70. package/dist/lit/renderers/scFile.mjs +53 -0
  71. package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
  72. package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
  73. package/dist/lit/renderers/scNumber.d.mts +15 -0
  74. package/dist/lit/renderers/scNumber.mjs +64 -0
  75. package/dist/lit/renderers/scObject.d.mts +14 -0
  76. package/dist/lit/renderers/scObject.mjs +57 -0
  77. package/dist/lit/renderers/scRecord.d.mts +14 -0
  78. package/dist/lit/renderers/scRecord.mjs +112 -0
  79. package/dist/lit/renderers/scString.d.mts +19 -0
  80. package/dist/lit/renderers/scString.mjs +165 -0
  81. package/dist/lit/renderers/scTuple.d.mts +14 -0
  82. package/dist/lit/renderers/scTuple.mjs +58 -0
  83. package/dist/lit/renderers/scUnion.d.mts +14 -0
  84. package/dist/lit/renderers/scUnion.mjs +44 -0
  85. package/dist/lit/renderers/scUnknown.d.mts +15 -0
  86. package/dist/lit/renderers/scUnknown.mjs +45 -0
  87. package/dist/lit/ssr.d.mts +37 -0
  88. package/dist/lit/ssr.mjs +9565 -0
  89. package/dist/lit/types.d.mts +2 -0
  90. package/dist/lit/types.mjs +1 -0
  91. package/dist/lit/widget.d.mts +71 -0
  92. package/dist/lit/widget.mjs +87 -0
  93. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  94. package/dist/openapi/ApiLinks.d.mts +1 -1
  95. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  96. package/dist/openapi/ApiSecurity.d.mts +1 -1
  97. package/dist/openapi/components.d.mts +4 -4
  98. package/dist/openapi/parser.d.mts +2 -2
  99. package/dist/openapi/resolve.d.mts +1 -1
  100. package/dist/preact/SchemaComponent.d.mts +3 -0
  101. package/dist/preact/SchemaComponent.mjs +26 -0
  102. package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
  103. package/dist/preact/SchemaErrorBoundary.mjs +20 -0
  104. package/dist/preact/SchemaView.d.mts +2 -0
  105. package/dist/preact/SchemaView.mjs +22 -0
  106. package/dist/preact/headless.d.mts +2 -0
  107. package/dist/preact/headless.mjs +18 -0
  108. package/dist/react/SchemaComponent.d.mts +3 -270
  109. package/dist/react/SchemaComponent.mjs +41 -32
  110. package/dist/react/SchemaView.d.mts +6 -6
  111. package/dist/react/SchemaView.mjs +32 -29
  112. package/dist/react/a11y.d.mts +2 -2
  113. package/dist/react/fieldShell.d.mts +1 -1
  114. package/dist/react/headless.d.mts +1 -1
  115. package/dist/react/headlessRenderers.d.mts +2 -2
  116. package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
  117. package/dist/renderer-ab9E52Bp.d.mts +245 -0
  118. package/dist/solid/SchemaComponent.d.mts +136 -0
  119. package/dist/solid/SchemaComponent.mjs +391 -0
  120. package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
  121. package/dist/solid/SchemaErrorBoundary.mjs +57 -0
  122. package/dist/solid/SchemaField.d.mts +40 -0
  123. package/dist/solid/SchemaField.mjs +113 -0
  124. package/dist/solid/SchemaView.d.mts +54 -0
  125. package/dist/solid/SchemaView.mjs +168 -0
  126. package/dist/solid/a11y.d.mts +70 -0
  127. package/dist/solid/a11y.mjs +71 -0
  128. package/dist/solid/contexts.d.mts +37 -0
  129. package/dist/solid/contexts.mjs +66 -0
  130. package/dist/solid/headless.d.mts +10 -0
  131. package/dist/solid/headless.mjs +27 -0
  132. package/dist/solid/renderers.d.mts +79 -0
  133. package/dist/solid/renderers.mjs +840 -0
  134. package/dist/solid/types.d.mts +90 -0
  135. package/dist/solid/types.mjs +1 -0
  136. package/dist/solid/widget.d.mts +29 -0
  137. package/dist/solid/widget.mjs +35 -0
  138. package/dist/themes/mantine.d.mts +1 -1
  139. package/dist/themes/mui.d.mts +1 -1
  140. package/dist/themes/radix.d.mts +1 -1
  141. package/dist/themes/shadcn.d.mts +1 -1
  142. package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
  143. package/dist/types-BCy7K3nk.d.mts +125 -0
  144. package/package.json +73 -1
  145. package/src/svelte/SchemaComponent.svelte +427 -0
  146. package/src/svelte/SchemaErrorBoundary.svelte +66 -0
  147. package/src/svelte/SchemaField.svelte +216 -0
  148. package/src/svelte/SchemaProvider.svelte +46 -0
  149. package/src/svelte/SchemaView.svelte +244 -0
  150. package/src/svelte/a11y.ts +112 -0
  151. package/src/svelte/contexts.ts +79 -0
  152. package/src/svelte/dispatch.ts +267 -0
  153. package/src/svelte/headless.ts +73 -0
  154. package/src/svelte/headlessFns.ts +124 -0
  155. package/src/svelte/renderers/Array.svelte +98 -0
  156. package/src/svelte/renderers/Boolean.svelte +43 -0
  157. package/src/svelte/renderers/Conditional.svelte +67 -0
  158. package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
  159. package/src/svelte/renderers/Enum.svelte +53 -0
  160. package/src/svelte/renderers/Fallback.svelte +24 -0
  161. package/src/svelte/renderers/File.svelte +46 -0
  162. package/src/svelte/renderers/Literal.svelte +29 -0
  163. package/src/svelte/renderers/Mount.svelte +24 -0
  164. package/src/svelte/renderers/Negation.svelte +35 -0
  165. package/src/svelte/renderers/Never.svelte +24 -0
  166. package/src/svelte/renderers/Null.svelte +19 -0
  167. package/src/svelte/renderers/Number.svelte +68 -0
  168. package/src/svelte/renderers/Object.svelte +74 -0
  169. package/src/svelte/renderers/Record.svelte +134 -0
  170. package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
  171. package/src/svelte/renderers/String.svelte +152 -0
  172. package/src/svelte/renderers/Tuple.svelte +84 -0
  173. package/src/svelte/renderers/Union.svelte +49 -0
  174. package/src/svelte/renderers/Unknown.svelte +42 -0
  175. package/src/svelte/svelte-modules.d.ts +25 -0
  176. package/src/svelte/types.ts +238 -0
  177. package/src/svelte/widget.ts +62 -0
  178. package/src/vue/SchemaComponent.vue +274 -0
  179. package/src/vue/SchemaErrorBoundary.vue +60 -0
  180. package/src/vue/SchemaField.vue +178 -0
  181. package/src/vue/SchemaProvider.vue +39 -0
  182. package/src/vue/SchemaView.vue +198 -0
  183. package/src/vue/VNodeHost.ts +32 -0
  184. package/src/vue/contexts.ts +116 -0
  185. package/src/vue/eventTargets.ts +35 -0
  186. package/src/vue/headless.ts +61 -0
  187. package/src/vue/idPrefix.ts +79 -0
  188. package/src/vue/renderField.ts +182 -0
  189. package/src/vue/renderers.ts +1297 -0
  190. package/src/vue/resolver.ts +45 -0
  191. package/src/vue/types.ts +140 -0
  192. package/src/vue/vue-shim.d.ts +25 -0
  193. package/src/vue/widget.ts +51 -0
  194. /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
  195. /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
  196. /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
  197. /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
+ }