schema-components 2.0.1 → 2.1.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.
Files changed (196) hide show
  1. package/README.md +98 -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/adapter.mjs +33 -25
  8. package/dist/core/constraintHint.d.mts +1 -1
  9. package/dist/core/constraints.d.mts +2 -2
  10. package/dist/core/contexts.d.mts +71 -0
  11. package/dist/core/contexts.mjs +1 -0
  12. package/dist/core/diagnostics.d.mts +1 -1
  13. package/dist/core/errors.d.mts +1 -1
  14. package/dist/core/fieldOrder.d.mts +1 -1
  15. package/dist/{react → core}/fieldPath.d.mts +2 -2
  16. package/dist/{react → core}/fieldPath.mjs +3 -3
  17. package/dist/core/formats.d.mts +1 -1
  18. package/dist/core/guards.d.mts +2 -2
  19. package/dist/core/guards.mjs +2 -2
  20. package/dist/core/inferValue.d.mts +1 -1
  21. package/dist/core/limits.d.mts +1 -1
  22. package/dist/core/merge.d.mts +1 -1
  23. package/dist/core/normalise.d.mts +6 -6
  24. package/dist/core/normalise.mjs +1 -1
  25. package/dist/core/openapi30.d.mts +1 -1
  26. package/dist/core/openapi30.mjs +1 -1
  27. package/dist/core/ref.d.mts +1 -1
  28. package/dist/core/renderField.d.mts +147 -0
  29. package/dist/core/renderField.mjs +81 -0
  30. package/dist/core/renderer.d.mts +2 -199
  31. package/dist/core/swagger2.d.mts +1 -1
  32. package/dist/core/swagger2.mjs +1 -1
  33. package/dist/core/typeInference.d.mts +1 -981
  34. package/dist/core/types.d.mts +1 -1
  35. package/dist/core/unionMatch.d.mts +1 -1
  36. package/dist/core/uri.d.mts +2 -2
  37. package/dist/core/uri.mjs +2 -2
  38. package/dist/core/version.d.mts +1 -1
  39. package/dist/core/walkBuilders.d.mts +4 -4
  40. package/dist/core/walkBuilders.mjs +1 -1
  41. package/dist/core/walker.d.mts +1 -1
  42. package/dist/core/walker.mjs +3 -3
  43. package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
  44. package/dist/html/a11y.d.mts +2 -2
  45. package/dist/html/html.d.mts +10 -8
  46. package/dist/html/renderToHtml.d.mts +5 -5
  47. package/dist/html/renderToHtml.mjs +45 -24
  48. package/dist/html/renderToHtmlStream.d.mts +5 -5
  49. package/dist/html/renderers.d.mts +1 -1
  50. package/dist/html/streamRenderers.d.mts +3 -3
  51. package/dist/{inferValue-PPXWJpbN.d.mts → inferValue-eAnh50EM.d.mts} +6 -6
  52. package/dist/lit/SchemaComponent.d.mts +125 -0
  53. package/dist/lit/SchemaComponent.mjs +2 -0
  54. package/dist/lit/SchemaField.d.mts +65 -0
  55. package/dist/lit/SchemaField.mjs +2 -0
  56. package/dist/lit/SchemaView.d.mts +14 -0
  57. package/dist/lit/SchemaView.mjs +2 -0
  58. package/dist/lit/constructorTypes.d.mts +2 -0
  59. package/dist/lit/constructorTypes.mjs +1 -0
  60. package/dist/lit/contexts.d.mts +78 -0
  61. package/dist/lit/contexts.mjs +238 -0
  62. package/dist/lit/defaultResolver.d.mts +33 -0
  63. package/dist/lit/defaultResolver.mjs +2 -0
  64. package/dist/lit/registry.d.mts +66 -0
  65. package/dist/lit/registry.mjs +2 -0
  66. package/dist/lit/renderers/baseElement.d.mts +131 -0
  67. package/dist/lit/renderers/baseElement.mjs +109 -0
  68. package/dist/lit/renderers/recordHelpers.d.mts +25 -0
  69. package/dist/lit/renderers/recordHelpers.mjs +55 -0
  70. package/dist/lit/renderers/scArray.d.mts +14 -0
  71. package/dist/lit/renderers/scArray.mjs +86 -0
  72. package/dist/lit/renderers/scBoolean.d.mts +15 -0
  73. package/dist/lit/renderers/scBoolean.mjs +47 -0
  74. package/dist/lit/renderers/scConditional.d.mts +23 -0
  75. package/dist/lit/renderers/scConditional.mjs +65 -0
  76. package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
  77. package/dist/lit/renderers/scDiscriminated.mjs +138 -0
  78. package/dist/lit/renderers/scEnum.d.mts +16 -0
  79. package/dist/lit/renderers/scEnum.mjs +66 -0
  80. package/dist/lit/renderers/scFile.d.mts +15 -0
  81. package/dist/lit/renderers/scFile.mjs +53 -0
  82. package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
  83. package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
  84. package/dist/lit/renderers/scNumber.d.mts +15 -0
  85. package/dist/lit/renderers/scNumber.mjs +64 -0
  86. package/dist/lit/renderers/scObject.d.mts +14 -0
  87. package/dist/lit/renderers/scObject.mjs +57 -0
  88. package/dist/lit/renderers/scRecord.d.mts +14 -0
  89. package/dist/lit/renderers/scRecord.mjs +112 -0
  90. package/dist/lit/renderers/scString.d.mts +19 -0
  91. package/dist/lit/renderers/scString.mjs +165 -0
  92. package/dist/lit/renderers/scTuple.d.mts +14 -0
  93. package/dist/lit/renderers/scTuple.mjs +58 -0
  94. package/dist/lit/renderers/scUnion.d.mts +14 -0
  95. package/dist/lit/renderers/scUnion.mjs +44 -0
  96. package/dist/lit/renderers/scUnknown.d.mts +15 -0
  97. package/dist/lit/renderers/scUnknown.mjs +45 -0
  98. package/dist/lit/ssr.d.mts +37 -0
  99. package/dist/lit/ssr.mjs +9565 -0
  100. package/dist/lit/types.d.mts +2 -0
  101. package/dist/lit/types.mjs +1 -0
  102. package/dist/lit/widget.d.mts +71 -0
  103. package/dist/lit/widget.mjs +87 -0
  104. package/dist/{normalise-DB-Xtjmn.mjs → normalise-BkePrJ4v.mjs} +6 -6
  105. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  106. package/dist/openapi/ApiLinks.d.mts +1 -1
  107. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  108. package/dist/openapi/ApiSecurity.d.mts +1 -1
  109. package/dist/openapi/components.d.mts +5 -5
  110. package/dist/openapi/components.mjs +1 -1
  111. package/dist/openapi/parser.d.mts +2 -2
  112. package/dist/openapi/resolve.d.mts +1 -1
  113. package/dist/openapi/resolve.mjs +1 -1
  114. package/dist/preact/SchemaComponent.d.mts +3 -0
  115. package/dist/preact/SchemaComponent.mjs +26 -0
  116. package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
  117. package/dist/preact/SchemaErrorBoundary.mjs +20 -0
  118. package/dist/preact/SchemaView.d.mts +2 -0
  119. package/dist/preact/SchemaView.mjs +22 -0
  120. package/dist/preact/headless.d.mts +2 -0
  121. package/dist/preact/headless.mjs +18 -0
  122. package/dist/react/SchemaComponent.d.mts +3 -270
  123. package/dist/react/SchemaComponent.mjs +48 -39
  124. package/dist/react/SchemaErrorBoundary.mjs +7 -4
  125. package/dist/react/SchemaView.d.mts +11 -10
  126. package/dist/react/SchemaView.mjs +32 -29
  127. package/dist/react/a11y.d.mts +2 -2
  128. package/dist/react/fieldShell.d.mts +1 -1
  129. package/dist/react/headless.d.mts +1 -1
  130. package/dist/react/headlessRenderers.d.mts +2 -2
  131. package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
  132. package/dist/renderer-ab9E52Bp.d.mts +245 -0
  133. package/dist/solid/SchemaComponent.d.mts +136 -0
  134. package/dist/solid/SchemaComponent.mjs +391 -0
  135. package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
  136. package/dist/solid/SchemaErrorBoundary.mjs +57 -0
  137. package/dist/solid/SchemaField.d.mts +40 -0
  138. package/dist/solid/SchemaField.mjs +113 -0
  139. package/dist/solid/SchemaView.d.mts +54 -0
  140. package/dist/solid/SchemaView.mjs +168 -0
  141. package/dist/solid/a11y.d.mts +70 -0
  142. package/dist/solid/a11y.mjs +71 -0
  143. package/dist/solid/contexts.d.mts +37 -0
  144. package/dist/solid/contexts.mjs +66 -0
  145. package/dist/solid/headless.d.mts +10 -0
  146. package/dist/solid/headless.mjs +27 -0
  147. package/dist/solid/renderers.d.mts +79 -0
  148. package/dist/solid/renderers.mjs +840 -0
  149. package/dist/solid/types.d.mts +90 -0
  150. package/dist/solid/types.mjs +1 -0
  151. package/dist/solid/widget.d.mts +29 -0
  152. package/dist/solid/widget.mjs +35 -0
  153. package/dist/themes/mantine.d.mts +1 -1
  154. package/dist/themes/mui.d.mts +1 -1
  155. package/dist/themes/radix.d.mts +1 -1
  156. package/dist/themes/shadcn.d.mts +1 -1
  157. package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
  158. package/dist/types-BCy7K3nk.d.mts +125 -0
  159. package/package.json +71 -1
  160. package/src/svelte/SchemaComponent.svelte +427 -0
  161. package/src/svelte/SchemaErrorBoundary.svelte +66 -0
  162. package/src/svelte/SchemaField.svelte +216 -0
  163. package/src/svelte/SchemaProvider.svelte +46 -0
  164. package/src/svelte/SchemaView.svelte +244 -0
  165. package/src/svelte/a11y.ts +112 -0
  166. package/src/svelte/contexts.ts +79 -0
  167. package/src/svelte/dispatch.ts +267 -0
  168. package/src/svelte/headless.ts +73 -0
  169. package/src/svelte/headlessFns.ts +124 -0
  170. package/src/svelte/renderers/Array.svelte +98 -0
  171. package/src/svelte/renderers/Boolean.svelte +43 -0
  172. package/src/svelte/renderers/Conditional.svelte +67 -0
  173. package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
  174. package/src/svelte/renderers/Enum.svelte +53 -0
  175. package/src/svelte/renderers/Fallback.svelte +24 -0
  176. package/src/svelte/renderers/File.svelte +46 -0
  177. package/src/svelte/renderers/Literal.svelte +29 -0
  178. package/src/svelte/renderers/Mount.svelte +24 -0
  179. package/src/svelte/renderers/Negation.svelte +35 -0
  180. package/src/svelte/renderers/Never.svelte +24 -0
  181. package/src/svelte/renderers/Null.svelte +19 -0
  182. package/src/svelte/renderers/Number.svelte +68 -0
  183. package/src/svelte/renderers/Object.svelte +74 -0
  184. package/src/svelte/renderers/Record.svelte +134 -0
  185. package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
  186. package/src/svelte/renderers/String.svelte +152 -0
  187. package/src/svelte/renderers/Tuple.svelte +84 -0
  188. package/src/svelte/renderers/Union.svelte +49 -0
  189. package/src/svelte/renderers/Unknown.svelte +42 -0
  190. package/src/svelte/svelte-modules.d.ts +25 -0
  191. package/src/svelte/types.ts +238 -0
  192. package/src/svelte/widget.ts +62 -0
  193. /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
  194. /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
  195. /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
  196. /package/dist/{version-DL8U5RuA.d.mts → version-BEBx10ND.d.mts} +0 -0
@@ -0,0 +1,86 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { defaultRecordValueLit } from "./recordHelpers.mjs";
3
+ import { html } from "lit";
4
+ //#region src/lit/renderers/scArray.ts
5
+ /**
6
+ * `<sc-array>` — Custom Element renderer for `ArrayField`.
7
+ *
8
+ * Mirrors React's `renderArray`: an ordered list in read-only mode,
9
+ * an editable list with per-item remove and footer add buttons in
10
+ * editable mode. Uses the same `defaultRecordValue()` helper as the
11
+ * record renderer for new entries — the resolver-agnostic default
12
+ * lives in `lit/recordHelpers.ts`.
13
+ *
14
+ * Parts: `list`, `item`, `remove`, `add`.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ function ariaLabel(description) {
19
+ return typeof description === "string" ? description : void 0;
20
+ }
21
+ /**
22
+ * Lit Custom Element rendering an array schema field.
23
+ *
24
+ * Tag: `<sc-array>` (registered by `registerSchemaComponents`).
25
+ */
26
+ var ScArray = class extends BaseScElement {
27
+ render() {
28
+ if (this.tree.type !== "array") return html``;
29
+ const arr = Array.isArray(this.value) ? this.value : [];
30
+ const element = this.tree.element;
31
+ if (element === void 0) return html``;
32
+ const label = ariaLabel(this.meta.description);
33
+ if (this.readOnly) {
34
+ if (arr.length === 0) return html``;
35
+ return html`<ul part="list" role="group" aria-label=${label ?? ""}>
36
+ ${arr.map((item, i) => html`<li part="item">
37
+ ${this.renderChild(element, item, () => {}, `[${String(i)}]`)}
38
+ </li>`)}
39
+ </ul>`;
40
+ }
41
+ const handleRemove = (index) => {
42
+ const next = arr.slice();
43
+ next.splice(index, 1);
44
+ this.emitChange(next);
45
+ };
46
+ const handleAdd = () => {
47
+ const next = arr.slice();
48
+ next.push(defaultRecordValueLit(element));
49
+ this.emitChange(next);
50
+ };
51
+ return html`<div role="group" aria-label=${label ?? ""}>
52
+ <ul part="list">
53
+ ${arr.map((item, i) => {
54
+ const childChange = (v) => {
55
+ const nextArr = arr.slice();
56
+ nextArr[i] = v;
57
+ this.emitChange(nextArr);
58
+ };
59
+ return html`<li part="item">
60
+ ${this.renderChild(element, item, childChange, `[${String(i)}]`)}
61
+ <button
62
+ part="remove"
63
+ type="button"
64
+ aria-label="Remove item ${String(i)}"
65
+ @click=${() => {
66
+ handleRemove(i);
67
+ }}
68
+ >
69
+ Remove
70
+ </button>
71
+ </li>`;
72
+ })}
73
+ </ul>
74
+ <button
75
+ part="add"
76
+ type="button"
77
+ aria-label="Add item"
78
+ @click=${handleAdd}
79
+ >
80
+ Add
81
+ </button>
82
+ </div>`;
83
+ }
84
+ };
85
+ //#endregion
86
+ export { ScArray };
@@ -0,0 +1,15 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scBoolean.d.ts
5
+ /**
6
+ * Lit Custom Element rendering a boolean-valued schema field.
7
+ *
8
+ * Tag: `<sc-boolean>` (registered by `registerSchemaComponents`).
9
+ */
10
+ declare class ScBoolean extends BaseScElement {
11
+ render(): TemplateResult;
12
+ private handleChange;
13
+ }
14
+ //#endregion
15
+ export { ScBoolean };
@@ -0,0 +1,47 @@
1
+ import "../../core/cssClasses.mjs";
2
+ import { fieldDomId } from "../../core/idPath.mjs";
3
+ import { BaseScElement } from "./baseElement.mjs";
4
+ import { html } from "lit";
5
+ //#region src/lit/renderers/scBoolean.ts
6
+ /**
7
+ * `<sc-boolean>` — Custom Element renderer for `BooleanField`.
8
+ *
9
+ * Mirrors React's `renderBoolean`: `<input type="checkbox">` in
10
+ * editable mode, `Yes` / `No` text in read-only mode.
11
+ *
12
+ * Parts: `input`, `value`.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ /**
17
+ * Lit Custom Element rendering a boolean-valued schema field.
18
+ *
19
+ * Tag: `<sc-boolean>` (registered by `registerSchemaComponents`).
20
+ */
21
+ var ScBoolean = class extends BaseScElement {
22
+ render() {
23
+ const id = fieldDomId(this.path);
24
+ if (this.readOnly) {
25
+ if (typeof this.value !== "boolean") return html`<span part="value" id=${id}>${"—"}</span>`;
26
+ return html`<span part="value" id=${id}
27
+ >${this.value ? "Yes" : "No"}</span
28
+ >`;
29
+ }
30
+ const ariaLabel = typeof this.meta.description === "string" ? this.meta.description : void 0;
31
+ return html`<input
32
+ part="input"
33
+ id=${id}
34
+ type="checkbox"
35
+ .checked=${this.writeOnly ? false : this.value === true}
36
+ aria-label=${ariaLabel ?? ""}
37
+ @change=${this.handleChange}
38
+ />`;
39
+ }
40
+ handleChange = (e) => {
41
+ const target = e.target;
42
+ if (!(target instanceof HTMLInputElement)) return;
43
+ this.emitChange(target.checked);
44
+ };
45
+ };
46
+ //#endregion
47
+ export { ScBoolean };
@@ -0,0 +1,23 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scConditional.d.ts
5
+ /**
6
+ * Lit Custom Element rendering a JSON Schema `if`/`then`/`else`
7
+ * conditional field.
8
+ *
9
+ * Tag: `<sc-conditional>` (registered by `registerSchemaComponents`).
10
+ */
11
+ declare class ScConditional extends BaseScElement {
12
+ render(): TemplateResult;
13
+ }
14
+ /**
15
+ * Lit Custom Element rendering a JSON Schema `not` (negation) field.
16
+ *
17
+ * Tag: `<sc-negation>` (registered by `registerSchemaComponents`).
18
+ */
19
+ declare class ScNegation extends BaseScElement {
20
+ render(): TemplateResult;
21
+ }
22
+ //#endregion
23
+ export { ScConditional, ScNegation };
@@ -0,0 +1,65 @@
1
+ import { SC_CLASSES } from "../../core/cssClasses.mjs";
2
+ import { BaseScElement } from "./baseElement.mjs";
3
+ import { html } from "lit";
4
+ //#region src/lit/renderers/scConditional.ts
5
+ /**
6
+ * `<sc-conditional>` and `<sc-negation>` — Custom Element renderers
7
+ * for the two JSON-Schema-composition variants.
8
+ *
9
+ * Mirror React's `renderConditional` and `renderNegation`: each clause
10
+ * is surfaced as a labelled `<fieldset>` with a discoverable structure
11
+ * so theme adapters and assistive tech can interpret the schema
12
+ * constraint as well as the rendered value.
13
+ *
14
+ * Parts: `fieldset`, `clause`.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ /**
19
+ * Lit Custom Element rendering a JSON Schema `if`/`then`/`else`
20
+ * conditional field.
21
+ *
22
+ * Tag: `<sc-conditional>` (registered by `registerSchemaComponents`).
23
+ */
24
+ var ScConditional = class extends BaseScElement {
25
+ render() {
26
+ if (this.tree.type !== "conditional") return html``;
27
+ const { ifClause, thenClause, elseClause } = this.tree;
28
+ const onChange = (next) => {
29
+ this.emitChange(next);
30
+ };
31
+ return html`<fieldset part="fieldset" class=${SC_CLASSES.conditional}>
32
+ <div part="clause" class=${SC_CLASSES.conditionalIf}>
33
+ <strong>if:</strong>
34
+ ${this.renderChild(ifClause, this.value, onChange)}
35
+ </div>
36
+ ${thenClause === void 0 ? html`` : html`<div part="clause" class=${SC_CLASSES.conditionalThen}>
37
+ <strong>then:</strong>
38
+ ${this.renderChild(thenClause, this.value, onChange)}
39
+ </div>`}
40
+ ${elseClause === void 0 ? html`` : html`<div part="clause" class=${SC_CLASSES.conditionalElse}>
41
+ <strong>else:</strong>
42
+ ${this.renderChild(elseClause, this.value, onChange)}
43
+ </div>`}
44
+ </fieldset>`;
45
+ }
46
+ };
47
+ /**
48
+ * Lit Custom Element rendering a JSON Schema `not` (negation) field.
49
+ *
50
+ * Tag: `<sc-negation>` (registered by `registerSchemaComponents`).
51
+ */
52
+ var ScNegation = class extends BaseScElement {
53
+ render() {
54
+ if (this.tree.type !== "negation") return html``;
55
+ const onChange = (next) => {
56
+ this.emitChange(next);
57
+ };
58
+ return html`<fieldset part="fieldset" class=${SC_CLASSES.negation}>
59
+ <strong>Must NOT match:</strong>
60
+ ${this.renderChild(this.tree.negated, this.value, onChange)}
61
+ </fieldset>`;
62
+ }
63
+ };
64
+ //#endregion
65
+ export { ScConditional, ScNegation };
@@ -0,0 +1,23 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scDiscriminated.d.ts
5
+ /**
6
+ * Pure helper: convert a tab index into the new value the discriminated
7
+ * union should emit. Mirrors React's
8
+ * `discriminatedUnionValueForTab` so the contract is unit-testable
9
+ * without instantiating the Custom Element.
10
+ */
11
+ declare function discriminatedUnionValueForTabLit(optionLabels: readonly string[], discKey: string, newIndex: number): Record<string, string> | undefined;
12
+ /**
13
+ * Lit Custom Element rendering a discriminated-union schema field.
14
+ *
15
+ * Tag: `<sc-discriminated>` (registered by `registerSchemaComponents`).
16
+ */
17
+ declare class ScDiscriminated extends BaseScElement {
18
+ private pendingFocus;
19
+ render(): TemplateResult;
20
+ updated(): void;
21
+ }
22
+ //#endregion
23
+ export { ScDiscriminated, discriminatedUnionValueForTabLit };
@@ -0,0 +1,138 @@
1
+ import { isObject } from "../../core/guards.mjs";
2
+ import "../../core/cssClasses.mjs";
3
+ import { panelIdFor, tabIdFor } from "../../core/idPath.mjs";
4
+ import { resolveDiscriminatedActive } from "../../core/unionMatch.mjs";
5
+ import { BaseScElement } from "./baseElement.mjs";
6
+ import { html } from "lit";
7
+ //#region src/lit/renderers/scDiscriminated.ts
8
+ /**
9
+ * `<sc-discriminated>` — Custom Element renderer for
10
+ * `DiscriminatedUnionField`.
11
+ *
12
+ * Implements the WAI-ARIA "Tabs with Automatic Activation" pattern in
13
+ * parity with React's `DiscriminatedUnionTabs`:
14
+ *
15
+ * - `ArrowRight` / `ArrowLeft` move between tabs with wrap-around
16
+ * - `Home` / `End` jump to the first / last tab
17
+ * - `role="tablist"` / `"tab"` / `"tabpanel"`, `aria-selected`,
18
+ * `aria-controls`, `aria-labelledby`
19
+ * - Roving tabindex: the active tab carries `tabindex="0"`, the rest
20
+ * `tabindex="-1"`
21
+ * - Selection and focus stay aligned on every keystroke
22
+ *
23
+ * Parts: `tablist`, `tab`, `tab-active`, `panel`.
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+ /**
28
+ * Pure helper: convert a tab index into the new value the discriminated
29
+ * union should emit. Mirrors React's
30
+ * `discriminatedUnionValueForTab` so the contract is unit-testable
31
+ * without instantiating the Custom Element.
32
+ */
33
+ function discriminatedUnionValueForTabLit(optionLabels, discKey, newIndex) {
34
+ const label = optionLabels[newIndex];
35
+ if (label === void 0) return void 0;
36
+ return { [discKey]: label };
37
+ }
38
+ /**
39
+ * Lit Custom Element rendering a discriminated-union schema field.
40
+ *
41
+ * Tag: `<sc-discriminated>` (registered by `registerSchemaComponents`).
42
+ */
43
+ var ScDiscriminated = class extends BaseScElement {
44
+ pendingFocus = false;
45
+ render() {
46
+ if (this.tree.type !== "discriminatedUnion") {
47
+ if (this.value === void 0 || this.value === null) return html`<span part="value">${"—"}</span>`;
48
+ return html`<span part="value"
49
+ >${JSON.stringify(this.value)}</span
50
+ >`;
51
+ }
52
+ const { options, discriminator: discKey } = this.tree;
53
+ if (options.length === 0) {
54
+ if (this.value === void 0 || this.value === null) return html`<span part="value">${"—"}</span>`;
55
+ return html`<span part="value"
56
+ >${JSON.stringify(this.value)}</span
57
+ >`;
58
+ }
59
+ const { optionLabels, activeIndex, activeOption } = resolveDiscriminatedActive(options, discKey, isObject(this.value) ? this.value : void 0);
60
+ if (this.readOnly) {
61
+ if (activeOption !== void 0) return this.renderChild(activeOption, this.value, (next) => {
62
+ this.emitChange(next);
63
+ });
64
+ return html`<span part="value">${"—"}</span>`;
65
+ }
66
+ const panelId = panelIdFor(this.path);
67
+ const wrapIndex = (index) => (index % options.length + options.length) % options.length;
68
+ const handleTabChange = (newIndex) => {
69
+ const next = discriminatedUnionValueForTabLit(optionLabels, discKey, newIndex);
70
+ if (next === void 0) return;
71
+ this.emitChange(next);
72
+ };
73
+ const handleKeyDown = (e) => {
74
+ let target;
75
+ if (e.key === "ArrowRight") target = wrapIndex(activeIndex + 1);
76
+ else if (e.key === "ArrowLeft") target = wrapIndex(activeIndex - 1);
77
+ else if (e.key === "Home") target = 0;
78
+ else if (e.key === "End") target = options.length - 1;
79
+ if (target === void 0) return;
80
+ e.preventDefault();
81
+ if (target === activeIndex) return;
82
+ this.pendingFocus = true;
83
+ handleTabChange(target);
84
+ };
85
+ return html`<div part="container">
86
+ <div
87
+ part="tablist"
88
+ role="tablist"
89
+ aria-label="Select variant"
90
+ aria-orientation="horizontal"
91
+ @keydown=${handleKeyDown}
92
+ >
93
+ ${options.map((_opt, i) => {
94
+ const tabId = tabIdFor(this.path, i);
95
+ const isActive = i === activeIndex;
96
+ return html`<button
97
+ part=${isActive ? "tab tab-active" : "tab"}
98
+ type="button"
99
+ role="tab"
100
+ id=${tabId}
101
+ aria-selected=${isActive ? "true" : "false"}
102
+ aria-controls=${panelId}
103
+ tabindex=${isActive ? 0 : -1}
104
+ @click=${() => {
105
+ handleTabChange(i);
106
+ }}
107
+ >
108
+ ${optionLabels[i]}
109
+ </button>`;
110
+ })}
111
+ </div>
112
+ <div
113
+ part="panel"
114
+ role="tabpanel"
115
+ id=${panelId}
116
+ aria-labelledby=${tabIdFor(this.path, activeIndex)}
117
+ >
118
+ ${activeOption === void 0 ? html`` : this.renderChild(activeOption, this.value, (next) => {
119
+ this.emitChange(next);
120
+ })}
121
+ </div>
122
+ </div>`;
123
+ }
124
+ updated() {
125
+ if (!this.pendingFocus) return;
126
+ this.pendingFocus = false;
127
+ if (this.tree.type !== "discriminatedUnion") return;
128
+ const { options, discriminator: discKey } = this.tree;
129
+ const { activeIndex } = resolveDiscriminatedActive(options, discKey, isObject(this.value) ? this.value : void 0);
130
+ const id = tabIdFor(this.path, activeIndex);
131
+ const root = this.shadowRoot;
132
+ if (root === null) return;
133
+ const tab = root.getElementById(id);
134
+ if (tab !== null && tab instanceof HTMLElement) tab.focus();
135
+ }
136
+ };
137
+ //#endregion
138
+ export { ScDiscriminated, discriminatedUnionValueForTabLit };
@@ -0,0 +1,16 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scEnum.d.ts
5
+ /**
6
+ * Lit Custom Element rendering an enumerated string-valued schema
7
+ * field.
8
+ *
9
+ * Tag: `<sc-enum>` (registered by `registerSchemaComponents`).
10
+ */
11
+ declare class ScEnum extends BaseScElement {
12
+ render(): TemplateResult;
13
+ private handleChange;
14
+ }
15
+ //#endregion
16
+ export { ScEnum };
@@ -0,0 +1,66 @@
1
+ import { constraintHint } from "../../core/constraintHint.mjs";
2
+ import "../../core/cssClasses.mjs";
3
+ import { fieldDomId, hintIdFor } from "../../core/idPath.mjs";
4
+ import { displayJsonValue } from "../../core/walkBuilders.mjs";
5
+ import { BaseScElement } from "./baseElement.mjs";
6
+ import { html } from "lit";
7
+ //#region src/lit/renderers/scEnum.ts
8
+ /**
9
+ * `<sc-enum>` — Custom Element renderer for `EnumField`.
10
+ *
11
+ * Mirrors React's `renderEnum`: `<select>` listing each option, with
12
+ * a hint advertising any pattern / format constraints.
13
+ *
14
+ * Parts: `input`, `value`, `hint`.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ /**
19
+ * Lit Custom Element rendering an enumerated string-valued schema
20
+ * field.
21
+ *
22
+ * Tag: `<sc-enum>` (registered by `registerSchemaComponents`).
23
+ */
24
+ var ScEnum = class extends BaseScElement {
25
+ render() {
26
+ const id = fieldDomId(this.path);
27
+ const enumValue = typeof this.value === "string" ? this.value : "";
28
+ if (this.readOnly) return html`<span part="value" id=${id}
29
+ >${enumValue.length === 0 ? "—" : enumValue}</span
30
+ >`;
31
+ const enumValues = this.tree.type === "enum" ? this.tree.enumValues : [];
32
+ const hint = constraintHint(this.constraints);
33
+ const describedBy = hint !== void 0 ? hintIdFor(id) : void 0;
34
+ const selected = this.writeOnly ? "" : enumValue;
35
+ return html`
36
+ <select
37
+ part="input"
38
+ id=${id}
39
+ .value=${selected}
40
+ aria-describedby=${describedBy ?? ""}
41
+ @change=${this.handleChange}
42
+ >
43
+ <option value="">Select${"…"}</option>
44
+ ${enumValues.map((v) => {
45
+ const display = displayJsonValue(v);
46
+ return html`<option
47
+ value=${display}
48
+ ?selected=${display === selected}
49
+ >
50
+ ${display}
51
+ </option>`;
52
+ })}
53
+ </select>
54
+ ${hint === void 0 ? html`` : html`<small part="hint" id=${hintIdFor(id)} class="sc-hint"
55
+ >${hint}</small
56
+ >`}
57
+ `;
58
+ }
59
+ handleChange = (e) => {
60
+ const target = e.target;
61
+ if (!(target instanceof HTMLSelectElement)) return;
62
+ this.emitChange(target.value);
63
+ };
64
+ };
65
+ //#endregion
66
+ export { ScEnum };
@@ -0,0 +1,15 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scFile.d.ts
5
+ /**
6
+ * Lit Custom Element rendering a file-upload schema field.
7
+ *
8
+ * Tag: `<sc-file>` (registered by `registerSchemaComponents`).
9
+ */
10
+ declare class ScFile extends BaseScElement {
11
+ render(): TemplateResult;
12
+ private handleChange;
13
+ }
14
+ //#endregion
15
+ export { ScFile };
@@ -0,0 +1,53 @@
1
+ import { constraintHint } from "../../core/constraintHint.mjs";
2
+ import { fieldDomId, hintIdFor } from "../../core/idPath.mjs";
3
+ import { BaseScElement } from "./baseElement.mjs";
4
+ import { html } from "lit";
5
+ //#region src/lit/renderers/scFile.ts
6
+ /**
7
+ * `<sc-file>` — Custom Element renderer for `FileField`.
8
+ *
9
+ * Mirrors React's `renderFile`: `<input type="file">` honouring the
10
+ * schema's `contentMediaType` constraint via the `accept` attribute,
11
+ * plus a constraint hint.
12
+ *
13
+ * Parts: `input`, `hint`.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ /**
18
+ * Lit Custom Element rendering a file-upload schema field.
19
+ *
20
+ * Tag: `<sc-file>` (registered by `registerSchemaComponents`).
21
+ */
22
+ var ScFile = class extends BaseScElement {
23
+ render() {
24
+ const id = fieldDomId(this.path);
25
+ const accept = this.constraints.mimeTypes?.join(",");
26
+ if (this.readOnly) return html`<span part="value" id=${id}>File field</span>`;
27
+ const hint = constraintHint(this.constraints);
28
+ const describedBy = hint !== void 0 ? hintIdFor(id) : void 0;
29
+ const ariaLabel = typeof this.meta.description === "string" ? this.meta.description : void 0;
30
+ return html`
31
+ <input
32
+ part="input"
33
+ id=${id}
34
+ type="file"
35
+ accept=${accept ?? ""}
36
+ aria-describedby=${describedBy ?? ""}
37
+ aria-label=${ariaLabel ?? ""}
38
+ @change=${this.handleChange}
39
+ />
40
+ ${hint === void 0 ? html`` : html`<small part="hint" id=${hintIdFor(id)} class="sc-hint"
41
+ >${hint}</small
42
+ >`}
43
+ `;
44
+ }
45
+ handleChange = (e) => {
46
+ const target = e.target;
47
+ if (!(target instanceof HTMLInputElement)) return;
48
+ const file = target.files?.[0];
49
+ if (file !== void 0) this.emitChange(file);
50
+ };
51
+ };
52
+ //#endregion
53
+ export { ScFile };
@@ -0,0 +1,30 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scLiteralNullNever.d.ts
5
+ /**
6
+ * Lit Custom Element rendering a `literal` schema field.
7
+ *
8
+ * Tag: `<sc-literal>` (registered by `registerSchemaComponents`).
9
+ */
10
+ declare class ScLiteral extends BaseScElement {
11
+ render(): TemplateResult;
12
+ }
13
+ /**
14
+ * Lit Custom Element rendering a `null` schema field.
15
+ *
16
+ * Tag: `<sc-null>` (registered by `registerSchemaComponents`).
17
+ */
18
+ declare class ScNull extends BaseScElement {
19
+ render(): TemplateResult;
20
+ }
21
+ /**
22
+ * Lit Custom Element rendering a `never` schema field.
23
+ *
24
+ * Tag: `<sc-never>` (registered by `registerSchemaComponents`).
25
+ */
26
+ declare class ScNever extends BaseScElement {
27
+ render(): TemplateResult;
28
+ }
29
+ //#endregion
30
+ export { ScLiteral, ScNever, ScNull };
@@ -0,0 +1,57 @@
1
+ import { SC_CLASSES } from "../../core/cssClasses.mjs";
2
+ import { fieldDomId } from "../../core/idPath.mjs";
3
+ import { displayJsonValue } from "../../core/walkBuilders.mjs";
4
+ import { BaseScElement } from "./baseElement.mjs";
5
+ import { html } from "lit";
6
+ //#region src/lit/renderers/scLiteralNullNever.ts
7
+ /**
8
+ * `<sc-literal>`, `<sc-null>`, `<sc-never>` — Custom Element renderers
9
+ * for the three non-editable leaf types.
10
+ *
11
+ * Each emits a single `<span>` placeholder. The literal renderer
12
+ * displays the literal value (or comma-separated list); the null and
13
+ * never renderers display the em-dash and a `never matches` italic
14
+ * note respectively. Mirrors the React headless renderers.
15
+ *
16
+ * Parts: `value`.
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+ /**
21
+ * Lit Custom Element rendering a `literal` schema field.
22
+ *
23
+ * Tag: `<sc-literal>` (registered by `registerSchemaComponents`).
24
+ */
25
+ var ScLiteral = class extends BaseScElement {
26
+ render() {
27
+ const id = fieldDomId(this.path);
28
+ if (this.tree.type !== "literal") return html`<span part="value" id=${id}>${"—"}</span>`;
29
+ const values = this.tree.literalValues;
30
+ if (values.length === 0) return html`<span part="value" id=${id}>${"—"}</span>`;
31
+ return html`<span part="value" id=${id}>${values.map((v) => displayJsonValue(v)).join(", ")}</span>`;
32
+ }
33
+ };
34
+ /**
35
+ * Lit Custom Element rendering a `null` schema field.
36
+ *
37
+ * Tag: `<sc-null>` (registered by `registerSchemaComponents`).
38
+ */
39
+ var ScNull = class extends BaseScElement {
40
+ render() {
41
+ return html`<span part="value" id=${fieldDomId(this.path)}>${"—"}</span>`;
42
+ }
43
+ };
44
+ /**
45
+ * Lit Custom Element rendering a `never` schema field.
46
+ *
47
+ * Tag: `<sc-never>` (registered by `registerSchemaComponents`).
48
+ */
49
+ var ScNever = class extends BaseScElement {
50
+ render() {
51
+ return html`<span part="value" id=${fieldDomId(this.path)} class=${SC_CLASSES.never}
52
+ ><em>never matches</em></span
53
+ >`;
54
+ }
55
+ };
56
+ //#endregion
57
+ export { ScLiteral, ScNever, ScNull };
@@ -0,0 +1,15 @@
1
+ import { BaseScElement } from "./baseElement.mjs";
2
+ import { TemplateResult } from "lit";
3
+
4
+ //#region src/lit/renderers/scNumber.d.ts
5
+ /**
6
+ * Lit Custom Element rendering a number-valued schema field.
7
+ *
8
+ * Tag: `<sc-number>` (registered by `registerSchemaComponents`).
9
+ */
10
+ declare class ScNumber extends BaseScElement {
11
+ render(): TemplateResult;
12
+ private handleInput;
13
+ }
14
+ //#endregion
15
+ export { ScNumber };