@agent-ui-kit/components 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/base-styles.css +8 -0
  2. package/dist/component-styles.css +147 -0
  3. package/dist/controls/_base/index.d.ts +3 -0
  4. package/dist/controls/_base/index.js +9 -0
  5. package/dist/controls/_base/indicator-element.d.ts +132 -0
  6. package/dist/controls/_base/indicator-element.js +158 -0
  7. package/dist/controls/_base/listbox-element.d.ts +70 -0
  8. package/dist/controls/_base/listbox-element.js +74 -0
  9. package/dist/controls/_base/range-element.d.ts +120 -0
  10. package/dist/controls/_base/range-element.js +158 -0
  11. package/dist/controls/_surface/container-box.css +301 -0
  12. package/dist/controls/_surface/container.css +156 -0
  13. package/dist/controls/_token-surface/token-surface.d.ts +35 -0
  14. package/dist/controls/_token-surface/token-surface.js +97 -0
  15. package/dist/controls/attachment/attachment-meta.d.ts +8 -0
  16. package/dist/controls/attachment/attachment-meta.js +84 -0
  17. package/dist/controls/attachment/attachment.css +91 -0
  18. package/dist/controls/attachment/attachment.d.ts +40 -0
  19. package/dist/controls/attachment/attachment.js +70 -0
  20. package/dist/controls/avatar/avatar-initials.d.ts +4 -0
  21. package/dist/controls/avatar/avatar-initials.js +30 -0
  22. package/dist/controls/avatar/avatar.css +87 -0
  23. package/dist/controls/avatar/avatar.d.ts +40 -0
  24. package/dist/controls/avatar/avatar.js +77 -0
  25. package/dist/controls/badge/badge.css +175 -0
  26. package/dist/controls/badge/badge.d.ts +36 -0
  27. package/dist/controls/badge/badge.js +71 -0
  28. package/dist/controls/bar-chart/bar-chart.css +110 -0
  29. package/dist/controls/bar-chart/bar-chart.d.ts +26 -0
  30. package/dist/controls/bar-chart/bar-chart.js +80 -0
  31. package/dist/controls/bar-chart/bar-math.d.ts +31 -0
  32. package/dist/controls/bar-chart/bar-math.js +106 -0
  33. package/dist/controls/button/button.css +263 -0
  34. package/dist/controls/button/button.d.ts +61 -0
  35. package/dist/controls/button/button.js +115 -0
  36. package/dist/controls/calendar/calendar.css +495 -0
  37. package/dist/controls/calendar/calendar.d.ts +161 -0
  38. package/dist/controls/calendar/calendar.js +1004 -0
  39. package/dist/controls/card/card-content.d.ts +24 -0
  40. package/dist/controls/card/card-content.js +133 -0
  41. package/dist/controls/card/card-footer.d.ts +3 -0
  42. package/dist/controls/card/card-footer.js +16 -0
  43. package/dist/controls/card/card-header.d.ts +3 -0
  44. package/dist/controls/card/card-header.js +18 -0
  45. package/dist/controls/card/card.css +445 -0
  46. package/dist/controls/card/card.d.ts +51 -0
  47. package/dist/controls/card/card.js +61 -0
  48. package/dist/controls/checkbox/checkbox.css +204 -0
  49. package/dist/controls/checkbox/checkbox.d.ts +114 -0
  50. package/dist/controls/checkbox/checkbox.js +130 -0
  51. package/dist/controls/code/code.css +72 -0
  52. package/dist/controls/code/code.d.ts +23 -0
  53. package/dist/controls/code/code.js +40 -0
  54. package/dist/controls/color-picker/color-picker.css +209 -0
  55. package/dist/controls/color-picker/color-picker.d.ts +84 -0
  56. package/dist/controls/color-picker/color-picker.js +545 -0
  57. package/dist/controls/color-picker/color.d.ts +40 -0
  58. package/dist/controls/color-picker/color.js +159 -0
  59. package/dist/controls/column/column.css +151 -0
  60. package/dist/controls/column/column.d.ts +107 -0
  61. package/dist/controls/column/column.js +57 -0
  62. package/dist/controls/combo-box/combo-box.css +397 -0
  63. package/dist/controls/combo-box/combo-box.d.ts +133 -0
  64. package/dist/controls/combo-box/combo-box.js +680 -0
  65. package/dist/controls/command-modal/command-modal.css +226 -0
  66. package/dist/controls/command-modal/command-modal.d.ts +42 -0
  67. package/dist/controls/command-modal/command-modal.js +315 -0
  68. package/dist/controls/disclosure/disclosure.css +162 -0
  69. package/dist/controls/disclosure/disclosure.d.ts +37 -0
  70. package/dist/controls/disclosure/disclosure.js +207 -0
  71. package/dist/controls/field/field.css +140 -0
  72. package/dist/controls/field/field.d.ts +27 -0
  73. package/dist/controls/field/field.js +228 -0
  74. package/dist/controls/form-provider/form-provider.css +40 -0
  75. package/dist/controls/form-provider/form-provider.d.ts +42 -0
  76. package/dist/controls/form-provider/form-provider.js +105 -0
  77. package/dist/controls/grid/grid.css +72 -0
  78. package/dist/controls/grid/grid.d.ts +60 -0
  79. package/dist/controls/grid/grid.js +56 -0
  80. package/dist/controls/icon/icon.css +37 -0
  81. package/dist/controls/icon/icon.d.ts +25 -0
  82. package/dist/controls/icon/icon.js +55 -0
  83. package/dist/controls/index.d.ts +57 -0
  84. package/dist/controls/index.js +136 -0
  85. package/dist/controls/ladder/ladder.css +89 -0
  86. package/dist/controls/ladder/ladder.d.ts +27 -0
  87. package/dist/controls/ladder/ladder.js +78 -0
  88. package/dist/controls/list/list.css +118 -0
  89. package/dist/controls/list/list.d.ts +84 -0
  90. package/dist/controls/list/list.js +48 -0
  91. package/dist/controls/menu/menu.css +195 -0
  92. package/dist/controls/menu/menu.d.ts +44 -0
  93. package/dist/controls/menu/menu.js +248 -0
  94. package/dist/controls/modal/modal.css +99 -0
  95. package/dist/controls/modal/modal.d.ts +61 -0
  96. package/dist/controls/modal/modal.js +155 -0
  97. package/dist/controls/popover/popover.css +83 -0
  98. package/dist/controls/popover/popover.d.ts +44 -0
  99. package/dist/controls/popover/popover.js +164 -0
  100. package/dist/controls/progress/progress.css +106 -0
  101. package/dist/controls/progress/progress.d.ts +28 -0
  102. package/dist/controls/progress/progress.js +80 -0
  103. package/dist/controls/radio/radio-group.css +84 -0
  104. package/dist/controls/radio/radio-group.d.ts +152 -0
  105. package/dist/controls/radio/radio-group.js +350 -0
  106. package/dist/controls/radio/radio.css +219 -0
  107. package/dist/controls/radio/radio.d.ts +19 -0
  108. package/dist/controls/radio/radio.js +75 -0
  109. package/dist/controls/ramp/ramp.css +75 -0
  110. package/dist/controls/ramp/ramp.d.ts +29 -0
  111. package/dist/controls/ramp/ramp.js +72 -0
  112. package/dist/controls/row/row.css +152 -0
  113. package/dist/controls/row/row.d.ts +95 -0
  114. package/dist/controls/row/row.js +45 -0
  115. package/dist/controls/segment/segment.css +66 -0
  116. package/dist/controls/segment/segment.d.ts +3 -0
  117. package/dist/controls/segment/segment.js +26 -0
  118. package/dist/controls/segmented-control/segmented-control.css +189 -0
  119. package/dist/controls/segmented-control/segmented-control.d.ts +20 -0
  120. package/dist/controls/segmented-control/segmented-control.js +64 -0
  121. package/dist/controls/select/select.css +467 -0
  122. package/dist/controls/select/select.d.ts +147 -0
  123. package/dist/controls/select/select.js +538 -0
  124. package/dist/controls/slider/slider.css +231 -0
  125. package/dist/controls/slider/slider.d.ts +111 -0
  126. package/dist/controls/slider/slider.js +60 -0
  127. package/dist/controls/slider-multi/slider-multi.css +197 -0
  128. package/dist/controls/slider-multi/slider-multi.d.ts +149 -0
  129. package/dist/controls/slider-multi/slider-multi.js +380 -0
  130. package/dist/controls/sparkline/sparkline-math.d.ts +23 -0
  131. package/dist/controls/sparkline/sparkline-math.js +79 -0
  132. package/dist/controls/sparkline/sparkline.css +61 -0
  133. package/dist/controls/sparkline/sparkline.d.ts +27 -0
  134. package/dist/controls/sparkline/sparkline.js +74 -0
  135. package/dist/controls/split/constrain.d.ts +44 -0
  136. package/dist/controls/split/constrain.js +126 -0
  137. package/dist/controls/split/split-pane.css +63 -0
  138. package/dist/controls/split/split-pane.d.ts +49 -0
  139. package/dist/controls/split/split-pane.js +42 -0
  140. package/dist/controls/split/split.css +127 -0
  141. package/dist/controls/split/split.d.ts +42 -0
  142. package/dist/controls/split/split.js +364 -0
  143. package/dist/controls/stat/stat-model.d.ts +22 -0
  144. package/dist/controls/stat/stat-model.js +69 -0
  145. package/dist/controls/stat/stat.css +101 -0
  146. package/dist/controls/stat/stat.d.ts +30 -0
  147. package/dist/controls/stat/stat.js +73 -0
  148. package/dist/controls/status-stream/status-stream.css +151 -0
  149. package/dist/controls/status-stream/status-stream.d.ts +108 -0
  150. package/dist/controls/status-stream/status-stream.js +417 -0
  151. package/dist/controls/swatch/swatch.css +66 -0
  152. package/dist/controls/swatch/swatch.d.ts +28 -0
  153. package/dist/controls/swatch/swatch.js +74 -0
  154. package/dist/controls/swiper/swiper-item.css +17 -0
  155. package/dist/controls/swiper/swiper-item.d.ts +31 -0
  156. package/dist/controls/swiper/swiper-item.js +47 -0
  157. package/dist/controls/swiper/swiper-label.css +10 -0
  158. package/dist/controls/swiper/swiper-label.d.ts +4 -0
  159. package/dist/controls/swiper/swiper-label.js +20 -0
  160. package/dist/controls/swiper/swiper-paddles.css +26 -0
  161. package/dist/controls/swiper/swiper-paddles.d.ts +10 -0
  162. package/dist/controls/swiper/swiper-paddles.js +62 -0
  163. package/dist/controls/swiper/swiper-pagination.css +60 -0
  164. package/dist/controls/swiper/swiper-pagination.d.ts +31 -0
  165. package/dist/controls/swiper/swiper-pagination.js +78 -0
  166. package/dist/controls/swiper/swiper.css +153 -0
  167. package/dist/controls/swiper/swiper.d.ts +158 -0
  168. package/dist/controls/swiper/swiper.js +659 -0
  169. package/dist/controls/switch/switch.css +194 -0
  170. package/dist/controls/switch/switch.d.ts +5 -0
  171. package/dist/controls/switch/switch.js +18 -0
  172. package/dist/controls/table/table-model.d.ts +43 -0
  173. package/dist/controls/table/table-model.js +124 -0
  174. package/dist/controls/table/table.css +90 -0
  175. package/dist/controls/table/table.d.ts +29 -0
  176. package/dist/controls/table/table.js +178 -0
  177. package/dist/controls/tabs/tab-panel.d.ts +10 -0
  178. package/dist/controls/tabs/tab-panel.js +89 -0
  179. package/dist/controls/tabs/tab.d.ts +26 -0
  180. package/dist/controls/tabs/tab.js +67 -0
  181. package/dist/controls/tabs/tabs.css +181 -0
  182. package/dist/controls/tabs/tabs.d.ts +61 -0
  183. package/dist/controls/tabs/tabs.js +176 -0
  184. package/dist/controls/text/href.d.ts +26 -0
  185. package/dist/controls/text/href.js +48 -0
  186. package/dist/controls/text/text.css +328 -0
  187. package/dist/controls/text/text.d.ts +85 -0
  188. package/dist/controls/text/text.js +273 -0
  189. package/dist/controls/text-field/text-field.css +498 -0
  190. package/dist/controls/text-field/text-field.d.ts +226 -0
  191. package/dist/controls/text-field/text-field.js +905 -0
  192. package/dist/controls/textarea/textarea.css +251 -0
  193. package/dist/controls/textarea/textarea.d.ts +136 -0
  194. package/dist/controls/textarea/textarea.js +312 -0
  195. package/dist/controls/theme-provider/theme-provider.css +48 -0
  196. package/dist/controls/theme-provider/theme-provider.d.ts +59 -0
  197. package/dist/controls/theme-provider/theme-provider.js +40 -0
  198. package/dist/controls/timeline/timeline.css +33 -0
  199. package/dist/controls/timeline/timeline.d.ts +39 -0
  200. package/dist/controls/timeline/timeline.js +52 -0
  201. package/dist/controls/timeline-item/timeline-item.css +495 -0
  202. package/dist/controls/timeline-item/timeline-item.d.ts +79 -0
  203. package/dist/controls/timeline-item/timeline-item.js +360 -0
  204. package/dist/controls/toast/toast-region.css +56 -0
  205. package/dist/controls/toast/toast-region.d.ts +20 -0
  206. package/dist/controls/toast/toast-region.js +113 -0
  207. package/dist/controls/toast/toast.css +96 -0
  208. package/dist/controls/toast/toast.d.ts +28 -0
  209. package/dist/controls/toast/toast.js +191 -0
  210. package/dist/controls/toolbar/toolbar.css +125 -0
  211. package/dist/controls/toolbar/toolbar.d.ts +108 -0
  212. package/dist/controls/toolbar/toolbar.js +68 -0
  213. package/dist/controls/tooltip/tooltip.css +80 -0
  214. package/dist/controls/tooltip/tooltip.d.ts +57 -0
  215. package/dist/controls/tooltip/tooltip.js +205 -0
  216. package/dist/descriptor/component-descriptor.d.ts +112 -0
  217. package/dist/descriptor/component-descriptor.js +492 -0
  218. package/dist/descriptor/index.d.ts +1 -0
  219. package/dist/descriptor/index.js +5 -0
  220. package/dist/dom/container.d.ts +45 -0
  221. package/dist/dom/container.js +60 -0
  222. package/dist/dom/element.d.ts +98 -0
  223. package/dist/dom/element.js +221 -0
  224. package/dist/dom/form.d.ts +240 -0
  225. package/dist/dom/form.js +399 -0
  226. package/dist/dom/index.d.ts +10 -0
  227. package/dist/dom/index.js +31 -0
  228. package/dist/dom/props.d.ts +56 -0
  229. package/dist/dom/props.js +231 -0
  230. package/dist/dom/repeat.d.ts +10 -0
  231. package/dist/dom/repeat.js +202 -0
  232. package/dist/dom/template.d.ts +161 -0
  233. package/dist/dom/template.js +634 -0
  234. package/dist/dom/watch.d.ts +11 -0
  235. package/dist/dom/watch.js +82 -0
  236. package/dist/foundation-styles.css +9 -0
  237. package/dist/index.d.ts +2 -0
  238. package/dist/index.js +3 -0
  239. package/dist/reactive/graph.d.ts +65 -0
  240. package/dist/reactive/graph.js +310 -0
  241. package/dist/reactive/index.d.ts +3 -0
  242. package/dist/reactive/index.js +4 -0
  243. package/dist/reactive/scheduler.d.ts +13 -0
  244. package/dist/reactive/scheduler.js +49 -0
  245. package/dist/traits/area-drag.d.ts +12 -0
  246. package/dist/traits/area-drag.js +70 -0
  247. package/dist/traits/form-registry.d.ts +36 -0
  248. package/dist/traits/form-registry.js +108 -0
  249. package/dist/traits/index.d.ts +24 -0
  250. package/dist/traits/index.js +19 -0
  251. package/dist/traits/overlay.d.ts +50 -0
  252. package/dist/traits/overlay.js +277 -0
  253. package/dist/traits/pane-resize.d.ts +26 -0
  254. package/dist/traits/pane-resize.js +103 -0
  255. package/dist/traits/press-activation.d.ts +11 -0
  256. package/dist/traits/press-activation.js +55 -0
  257. package/dist/traits/roving-focus.d.ts +52 -0
  258. package/dist/traits/roving-focus.js +214 -0
  259. package/dist/traits/scroll-fade.d.ts +28 -0
  260. package/dist/traits/scroll-fade.js +120 -0
  261. package/dist/traits/selection-commit.d.ts +30 -0
  262. package/dist/traits/selection-commit.js +146 -0
  263. package/dist/traits/tabbable.d.ts +12 -0
  264. package/dist/traits/tabbable.js +59 -0
  265. package/dist/traits/track-user-invalid.d.ts +36 -0
  266. package/dist/traits/track-user-invalid.js +52 -0
  267. package/dist/traits/value-codec.d.ts +120 -0
  268. package/dist/traits/value-codec.js +497 -0
  269. package/dist/traits/value-drag.d.ts +18 -0
  270. package/dist/traits/value-drag.js +84 -0
  271. package/package.json +263 -0
@@ -0,0 +1,98 @@
1
+ import { TemplateResult } from './template.js';
2
+ export declare class UIElement extends HTMLElement {
3
+ #private;
4
+ /** Light DOM by default; a subclass sets `static shadow = true` to render into a shadow root instead. */
5
+ static shadow: boolean;
6
+ static get observedAttributes(): string[];
7
+ constructor();
8
+ connectedCallback(): void;
9
+ disconnectedCallback(): void;
10
+ /**
11
+ * Inbound attribute → typed prop crossing. Maps the platform's ATTRIBUTE name to its PROP name, then
12
+ * hands off to the shipped `coerceAttribute` (string→typed via the prop's codec). The directional lock
13
+ * inside `coerceAttribute` owns loop suppression — both ways — so this adapter adds NO lock logic:
14
+ * a `reflect` write's own echo arrives here and is suppressed (outbound lock); an external change
15
+ * crosses to the value without reflecting back (inbound lock).
16
+ */
17
+ attributeChangedCallback(attr: string, _old: string | null, next: string | null): void;
18
+ /**
19
+ * Resolve one pre-upgrade property shadow. A `.prop=` set BEFORE the element upgraded (before `finalize`
20
+ * installed the accessor) created an OWN data property that MASKS the accessor — reads/writes hit the
21
+ * dead own property, never the signal. Replay the value THROUGH the accessor: prefer the value the
22
+ * CONSTRUCTOR captured (property-wins, ADR-0005 — it predates any initial-attribute write to the shadow);
23
+ * fall back to the current own-property value when nothing was captured (e.g. a manual array/object
24
+ * accessor a control upgrades by hand, or a shadow created after construction). No-op with neither.
25
+ */
26
+ protected upgradeProperty(name: string): void;
27
+ /**
28
+ * Replay every declared prop's pre-upgrade shadow. Run at the START of `connectedCallback`. Ordering
29
+ * note (the lazy-upgrade ↔ attributeChanged seam): an INITIAL observed attribute is applied during
30
+ * upgrade — before connect — while the shadow is still present, so its inbound write lands on the
31
+ * shadow; but the constructor already CAPTURED the pre-upgrade property value, so the replay restores
32
+ * the property over the attribute (property-wins, ADR-0005). See element-upgrade.test.ts.
33
+ */
34
+ protected upgradeProps(): void;
35
+ /**
36
+ * Register a connection-scoped effect: it runs now and re-runs on dependency change, and is disposed
37
+ * when the host disconnects (the scope owns it). This is the one effect-install primitive —
38
+ * `connectedCallback` routes the render effect through it too. Returns the effect's disposer (call it
39
+ * to end the effect early; otherwise disconnect ends it). Throws if called outside the connected
40
+ * lifetime, where there is no scope to own it.
41
+ */
42
+ effect(fn: () => void | (() => void)): () => void;
43
+ /**
44
+ * Add a platform event listener that rides the connection `AbortSignal`, so it is removed
45
+ * automatically on disconnect (`ac.abort()`) — zero live listeners after removal, no manual teardown.
46
+ * `type` stays an open string so custom event names are accepted; a control narrows at its call site.
47
+ * Throws if called outside the connected lifetime, where there is no signal to bind to.
48
+ */
49
+ listen(target: EventTarget, type: string, handler: (event: Event) => void, opts?: AddEventListenerOptions): void;
50
+ /**
51
+ * Dispatch a composed, bubbling, cancelable `CustomEvent`. Typed generically over the detail `D`, so a
52
+ * caller that pins `D` (today explicitly; via an event-detail map / `HTMLElementEventMap` augmentation
53
+ * once controls land) gets the detail type-checked. Returns `false` if a listener called
54
+ * `preventDefault()`, mirroring the platform's `dispatchEvent`.
55
+ */
56
+ emit<D = undefined>(type: string, detail?: D): boolean;
57
+ /** Resolves after the next render flush settles — `await el.updateComplete` waits for a pending render. */
58
+ get updateComplete(): Promise<void>;
59
+ /**
60
+ * The render target: the shadow root when `static shadow` opted one in, else the host itself (light DOM
61
+ * is the default). G3's render effect commits into this — the seam lands now so G3 + the barrel compose.
62
+ */
63
+ get renderRoot(): ShadowRoot | HTMLElement;
64
+ /**
65
+ * The single `ElementInternals` handle (acquired once in the constructor). ARIA is set THROUGH this —
66
+ * `this.internals.role = …`, `this.internals.ariaChecked = …` — NEVER host attributes, so the host
67
+ * stays free of `role`/`aria-*`. The protected seam the G4 form subclass + traits reuse without
68
+ * re-acquiring (a second `attachInternals()` throws).
69
+ */
70
+ protected get internals(): ElementInternals;
71
+ /**
72
+ * The connection `AbortSignal`: connection-scoped platform listeners ride it, so disconnect
73
+ * (`abort()`) frees them in one shot. Null while disconnected. The minimal protected seam by which
74
+ * subclasses / traits observe the connection lifetime; `this.listen` (e-helpers) rides it.
75
+ */
76
+ protected get connectionSignal(): AbortSignal | null;
77
+ /**
78
+ * Overridable connect hook. Runs on connect with the connection scope + `AbortSignal` live, so a
79
+ * `this.effect`/`this.listen` registered here is scope-owned and auto-removed on disconnect. It runs
80
+ * BEFORE the first render (the ordering: upgradeProps → scope/ac → `connected()` → render effect), so a
81
+ * control's host-level setup (traits, signals) is in place before the first commit. Override instead of
82
+ * `connectedCallback` — no `super` call needed. (Post-render DOM work belongs in `updateComplete`.)
83
+ */
84
+ protected connected(): void;
85
+ /**
86
+ * Overridable disconnect hook. Runs on disconnect BEFORE the connection scope is disposed / the signal
87
+ * aborted, so the control can act while its connected resources are still live. Override instead of
88
+ * `disconnectedCallback` — no `super` call needed.
89
+ */
90
+ protected disconnected(): void;
91
+ /**
92
+ * Overridable render hook. Return a `TemplateResult` (`html\`…\``) and the scope-owned render effect
93
+ * commits it into `renderRoot`; return nothing to render imperatively (or not at all). Runs inside the
94
+ * connection-scoped render effect, so any signal it reads re-runs only this render, committing only the
95
+ * holes whose values changed (the template engine's per-part `Object.is` skip).
96
+ */
97
+ protected render(): TemplateResult | void;
98
+ }
@@ -0,0 +1,221 @@
1
+ // element.ts — `UIElement`, the FACE custom-element host (plan §5).
2
+ //
3
+ // Maps the platform's connect/disconnect callbacks onto the kernel's two lifetimes: a connection
4
+ // **scope** (every computed/effect created under it dies on disconnect) and an **AbortController**
5
+ // (every platform listener riding its signal dies on disconnect). "Zero residue after removal" is then
6
+ // provable, not aspirational (goals.md G2 DoD3; rubric D4). Built so far: the connection scope/abort
7
+ // wiring + the ONE render effect (e-lifecycle), and the public helper surface — `this.effect` (scope-
8
+ // owned), `this.listen` (rides the abort signal → auto-removed on disconnect), `this.emit` (typed
9
+ // `CustomEvent`), `updateComplete` (e-helpers), and the attribute inbound crossing —
10
+ // `observedAttributes` + `attributeChangedCallback` → `coerceAttribute` (e-attrs), the lazy-property
11
+ // upgrade dance — `upgradeProps`/`upgradeProperty` at connect (e-upgrade), and internals-only ARIA + the
12
+ // light-DOM-default render root (e-internals). Only the dom barrel remains to close G2; `render()` stays
13
+ // a no-op hook until the template layer (G3).
14
+ //
15
+ // Imports only `../reactive` (the kernel) + `./props.ts` (same dom layer). `HTMLElement` /
16
+ // `AbortController` / `AbortSignal` / `CustomEvent` / `ElementInternals` are ambient DOM globals, not imports.
17
+ import { createScope, effect as createEffect, whenFlushed } from "../reactive/index.js";
18
+ import { finalize, coerceAttribute, observedAttributesFor, propForAttribute } from "./props.js";
19
+ import { render as commitTemplate, TemplateResult } from "./template.js";
20
+ export class UIElement extends HTMLElement {
21
+ // The connection lifetimes. Null while disconnected; opened on connect, torn down + nulled on
22
+ // disconnect. `#`-private (real JS privacy) so nothing outside the host can hold or revive them.
23
+ #scope = null;
24
+ #ac = null;
25
+ // The single `ElementInternals` handle, acquired ONCE here (a second `attachInternals()` throws).
26
+ // Surfaced to subclasses via the protected `internals` getter — a `#private` field can't cross to one.
27
+ #internals = this.attachInternals();
28
+ // Pre-upgrade `.prop=` values captured in the constructor (property-wins, ADR-0005). See the constructor.
29
+ #captured = new Map();
30
+ /** Light DOM by default; a subclass sets `static shadow = true` to render into a shadow root instead. */
31
+ static shadow = false;
32
+ // The attribute names the platform watches, derived from `static props` (property-only props excluded;
33
+ // `attribute` overrides honoured). Read once by `customElements.define`, before any instance exists —
34
+ // so it reads the schema directly, not the finalize-installed accessors.
35
+ static get observedAttributes() {
36
+ return observedAttributesFor(this);
37
+ }
38
+ constructor() {
39
+ super();
40
+ // Opt-in shadow root (light DOM is the default), attached once here so `renderRoot` is a pure getter.
41
+ if (this.constructor.shadow)
42
+ this.attachShadow({ mode: 'open' });
43
+ // Light the props-as-signals subsystem on this host: install the signal-backed prototype accessors
44
+ // declared by `static props`. Idempotent per class (props.ts' FINALIZED set), so every instance
45
+ // calls it but only the first instance of a class finalizes it — no props.ts change needed.
46
+ const ctor = this.constructor;
47
+ finalize(ctor);
48
+ // Property-wins (ADR-0005): snapshot each declared prop's pre-upgrade own-property value HERE — the
49
+ // constructor runs during upgrade BEFORE attributeChangedCallback can overwrite the shadow — so an
50
+ // imperatively-set `.prop=` beats an initial attribute. `upgradeProperty` replays the captured value.
51
+ if (ctor.props) {
52
+ for (const name of Object.keys(ctor.props)) {
53
+ if (Object.hasOwn(this, name))
54
+ this.#captured.set(name, this[name]);
55
+ }
56
+ }
57
+ }
58
+ connectedCallback() {
59
+ // FIRST: replay any pre-upgrade `.prop=` shadow into its signal — before the render effect installs
60
+ // and before anything reads a prop, so first render sees the assigned value, not the default.
61
+ this.upgradeProps();
62
+ this.#scope = createScope();
63
+ this.#ac = new AbortController();
64
+ // The connection scope + AbortController are now live, so a `this.effect`/`this.listen` registered in
65
+ // `connected()` is scope-owned + rides the abort signal. `connected()` runs BEFORE the first render so
66
+ // host-level setup (traits, signals) is in place before the first commit — see the ordering note below.
67
+ this.connected();
68
+ // The ONE render effect, installed through `this.effect` so there is a SINGLE scope-owned-effect
69
+ // path (no duplication). It is created inside the scope (the kernel's `activeOwner.add`) and
70
+ // disposed with it at disconnect — every render pass runs under the scope, so a directive attaching
71
+ // on a later conditional re-render is scope-owned too. The disposer is intentionally discarded.
72
+ // `render()` returns a `TemplateResult` (or nothing); when it returns one, commit it into `renderRoot`.
73
+ this.effect(() => {
74
+ const result = this.render();
75
+ // Pass `this` as the RenderContext (the scope_seam): `UIElement` structurally satisfies it via the
76
+ // scope-owned `effect`, so a per-hole directive (`watch`) installs its effect under the connection
77
+ // scope — surviving unrelated re-renders, dying on disconnect — not under this transient render effect.
78
+ if (result instanceof TemplateResult)
79
+ commitTemplate(result, this.renderRoot, this);
80
+ });
81
+ }
82
+ disconnectedCallback() {
83
+ // Let the control act while its connected resources are STILL live (scope/effects alive, listeners
84
+ // unaborted) — `disconnected()` runs BEFORE the teardown below.
85
+ this.disconnected();
86
+ this.#scope?.dispose(); // every computed/effect created under the scope dies → zero subscribers
87
+ this.#ac?.abort(); // every listener riding the connection signal dies → zero live listeners
88
+ this.#scope = null;
89
+ this.#ac = null;
90
+ }
91
+ /**
92
+ * Inbound attribute → typed prop crossing. Maps the platform's ATTRIBUTE name to its PROP name, then
93
+ * hands off to the shipped `coerceAttribute` (string→typed via the prop's codec). The directional lock
94
+ * inside `coerceAttribute` owns loop suppression — both ways — so this adapter adds NO lock logic:
95
+ * a `reflect` write's own echo arrives here and is suppressed (outbound lock); an external change
96
+ * crosses to the value without reflecting back (inbound lock).
97
+ */
98
+ attributeChangedCallback(attr, _old, next) {
99
+ const ctor = this.constructor;
100
+ const name = propForAttribute(ctor, attr);
101
+ if (name !== undefined)
102
+ coerceAttribute(this, ctor, name, next);
103
+ }
104
+ /**
105
+ * Resolve one pre-upgrade property shadow. A `.prop=` set BEFORE the element upgraded (before `finalize`
106
+ * installed the accessor) created an OWN data property that MASKS the accessor — reads/writes hit the
107
+ * dead own property, never the signal. Replay the value THROUGH the accessor: prefer the value the
108
+ * CONSTRUCTOR captured (property-wins, ADR-0005 — it predates any initial-attribute write to the shadow);
109
+ * fall back to the current own-property value when nothing was captured (e.g. a manual array/object
110
+ * accessor a control upgrades by hand, or a shadow created after construction). No-op with neither.
111
+ */
112
+ upgradeProperty(name) {
113
+ const hasCaptured = this.#captured.has(name);
114
+ const hasOwn = Object.hasOwn(this, name);
115
+ if (!hasCaptured && !hasOwn)
116
+ return;
117
+ const value = hasCaptured ? this.#captured.get(name) : this[name];
118
+ this.#captured.delete(name);
119
+ if (hasOwn)
120
+ delete this[name] // dissolve the shadow → reveal the accessor
121
+ ;
122
+ this[name] = value; // replay through the accessor → the signal (+ reflect once)
123
+ }
124
+ /**
125
+ * Replay every declared prop's pre-upgrade shadow. Run at the START of `connectedCallback`. Ordering
126
+ * note (the lazy-upgrade ↔ attributeChanged seam): an INITIAL observed attribute is applied during
127
+ * upgrade — before connect — while the shadow is still present, so its inbound write lands on the
128
+ * shadow; but the constructor already CAPTURED the pre-upgrade property value, so the replay restores
129
+ * the property over the attribute (property-wins, ADR-0005). See element-upgrade.test.ts.
130
+ */
131
+ upgradeProps() {
132
+ const props = this.constructor.props;
133
+ if (!props)
134
+ return;
135
+ for (const name of Object.keys(props))
136
+ this.upgradeProperty(name);
137
+ }
138
+ /**
139
+ * Register a connection-scoped effect: it runs now and re-runs on dependency change, and is disposed
140
+ * when the host disconnects (the scope owns it). This is the one effect-install primitive —
141
+ * `connectedCallback` routes the render effect through it too. Returns the effect's disposer (call it
142
+ * to end the effect early; otherwise disconnect ends it). Throws if called outside the connected
143
+ * lifetime, where there is no scope to own it.
144
+ */
145
+ effect(fn) {
146
+ const scope = this.#scope;
147
+ if (!scope)
148
+ throw new Error('UIElement.effect: no connection scope — call it during the connected lifetime');
149
+ return scope.run(() => createEffect(fn));
150
+ }
151
+ /**
152
+ * Add a platform event listener that rides the connection `AbortSignal`, so it is removed
153
+ * automatically on disconnect (`ac.abort()`) — zero live listeners after removal, no manual teardown.
154
+ * `type` stays an open string so custom event names are accepted; a control narrows at its call site.
155
+ * Throws if called outside the connected lifetime, where there is no signal to bind to.
156
+ */
157
+ listen(target, type, handler, opts) {
158
+ const ac = this.#ac;
159
+ if (!ac)
160
+ throw new Error('UIElement.listen: no connection signal — call it during the connected lifetime');
161
+ target.addEventListener(type, handler, { ...opts, signal: ac.signal });
162
+ }
163
+ /**
164
+ * Dispatch a composed, bubbling, cancelable `CustomEvent`. Typed generically over the detail `D`, so a
165
+ * caller that pins `D` (today explicitly; via an event-detail map / `HTMLElementEventMap` augmentation
166
+ * once controls land) gets the detail type-checked. Returns `false` if a listener called
167
+ * `preventDefault()`, mirroring the platform's `dispatchEvent`.
168
+ */
169
+ emit(type, detail) {
170
+ return this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true }));
171
+ }
172
+ /** Resolves after the next render flush settles — `await el.updateComplete` waits for a pending render. */
173
+ get updateComplete() {
174
+ return whenFlushed();
175
+ }
176
+ /**
177
+ * The render target: the shadow root when `static shadow` opted one in, else the host itself (light DOM
178
+ * is the default). G3's render effect commits into this — the seam lands now so G3 + the barrel compose.
179
+ */
180
+ get renderRoot() {
181
+ return this.shadowRoot ?? this;
182
+ }
183
+ /**
184
+ * The single `ElementInternals` handle (acquired once in the constructor). ARIA is set THROUGH this —
185
+ * `this.internals.role = …`, `this.internals.ariaChecked = …` — NEVER host attributes, so the host
186
+ * stays free of `role`/`aria-*`. The protected seam the G4 form subclass + traits reuse without
187
+ * re-acquiring (a second `attachInternals()` throws).
188
+ */
189
+ get internals() {
190
+ return this.#internals;
191
+ }
192
+ /**
193
+ * The connection `AbortSignal`: connection-scoped platform listeners ride it, so disconnect
194
+ * (`abort()`) frees them in one shot. Null while disconnected. The minimal protected seam by which
195
+ * subclasses / traits observe the connection lifetime; `this.listen` (e-helpers) rides it.
196
+ */
197
+ get connectionSignal() {
198
+ return this.#ac?.signal ?? null;
199
+ }
200
+ /**
201
+ * Overridable connect hook. Runs on connect with the connection scope + `AbortSignal` live, so a
202
+ * `this.effect`/`this.listen` registered here is scope-owned and auto-removed on disconnect. It runs
203
+ * BEFORE the first render (the ordering: upgradeProps → scope/ac → `connected()` → render effect), so a
204
+ * control's host-level setup (traits, signals) is in place before the first commit. Override instead of
205
+ * `connectedCallback` — no `super` call needed. (Post-render DOM work belongs in `updateComplete`.)
206
+ */
207
+ connected() { }
208
+ /**
209
+ * Overridable disconnect hook. Runs on disconnect BEFORE the connection scope is disposed / the signal
210
+ * aborted, so the control can act while its connected resources are still live. Override instead of
211
+ * `disconnectedCallback` — no `super` call needed.
212
+ */
213
+ disconnected() { }
214
+ /**
215
+ * Overridable render hook. Return a `TemplateResult` (`html\`…\``) and the scope-owned render effect
216
+ * commits it into `renderRoot`; return nothing to render imperatively (or not at all). Runs inside the
217
+ * connection-scoped render effect, so any signal it reads re-runs only this render, committing only the
218
+ * holes whose values changed (the template engine's per-part `Object.is` skip).
219
+ */
220
+ render() { }
221
+ }
@@ -0,0 +1,240 @@
1
+ import { UIElement } from './element.js';
2
+ import { type ReactiveProps } from './props.js';
3
+ /**
4
+ * The value a form control contributes to its owning form — exactly the platform's `setFormValue` value
5
+ * type. `null` contributes no entry (the base's default). The subclass's `formValue()` returns its typed
6
+ * value coerced into this shape (a string for text-field; a `File` / `FormData` for richer controls).
7
+ */
8
+ export type FormValue = File | string | FormData | null;
9
+ /**
10
+ * A control's validity VERDICT — a discriminated union the base maps onto `internals.setValidity`: valid
11
+ * clears it; invalid publishes the platform `flags` + the `message` (+ an optional `anchor` element the UA
12
+ * focuses on `reportValidity`). Distinct from the platform's `ValidityState` (the live IDL object the
13
+ * `validity` getter returns): this is the control's per-render assertion that DRIVES it.
14
+ */
15
+ export type ValidityResult = {
16
+ valid: true;
17
+ } | {
18
+ valid: false;
19
+ flags: ValidityStateFlags;
20
+ message: string;
21
+ anchor?: HTMLElement;
22
+ };
23
+ /** Fired once, at the end of `connectedCallback` — the control announcing full connection to the nearest
24
+ * provider/field ancestor. */
25
+ export declare const FORM_CONNECT_EVENT = "ui-form-connect";
26
+ /** Fired from `formResetCallback`, after `formReset()` — detail: none (the target IS the resetting
27
+ * control). Covers BOTH reset paths: native `form.reset()` and a provider's direct `formResetCallback()` call. */
28
+ export declare const FORM_RESET_EVENT = "ui-form-reset";
29
+ /** The `ui-form-connect` detail. The closures are the control's PUBLIC reactive surface, minted by the base
30
+ * (ADR-0050 §4 realized without widening the class API): each read, inside a computed/effect, tracks
31
+ * exactly the signals the underlying hook reads. */
32
+ export interface FormConnectDetail {
33
+ control: UIFormElement;
34
+ /** The connection-scoped teardown handle — the control's live connection AbortSignal (ADR-0050 §3).
35
+ * Aborts on disconnect; a NEW signal is minted per connection (reconnect ⇒ fresh handle). */
36
+ signal: AbortSignal;
37
+ /** Reactive read of the submission value — closes over the protected `formValue()`. */
38
+ value: () => FormValue;
39
+ /** Reactive read of the MERGED validity verdict — `formValidity()` ⊕ `setCustomValidity` (the same merge
40
+ * the base publishes to internals; `#mergedValidity` is the single source feeding both). */
41
+ validity: () => ValidityResult;
42
+ /** Reactive read of the user-invalid gate — closes over the `formUserInvalid()` hook (below). jsdom
43
+ * cannot match `:state(user-invalid)` (no CustomStateSet), so the field's error gate rides THIS read;
44
+ * the browser smokes assert its equivalence with `:state(user-invalid)` (same tracker source). */
45
+ userInvalid: () => boolean;
46
+ }
47
+ /** The field-labelling handoff (ADR-0051 cl.1). ELEMENTS, not id strings — the internals path consumes
48
+ * elements; the id path reads `.id` off the node (the field seeds ids before handing over). */
49
+ export interface FieldLabelling {
50
+ label: HTMLElement | null;
51
+ description: HTMLElement | null;
52
+ error: HTMLElement | null;
53
+ }
54
+ declare const formProps: {
55
+ name: {
56
+ reflect: true;
57
+ type: import("./props.js").PropType<string>;
58
+ default: string;
59
+ attribute?: string | false;
60
+ };
61
+ disabled: {
62
+ reflect: true;
63
+ type: import("./props.js").PropType<boolean>;
64
+ default: boolean;
65
+ attribute?: string | false;
66
+ };
67
+ required: {
68
+ reflect: true;
69
+ type: import("./props.js").PropType<boolean>;
70
+ default: boolean;
71
+ attribute?: string | false;
72
+ };
73
+ };
74
+ export interface UIFormElement extends ReactiveProps<typeof formProps> {
75
+ }
76
+ export declare class UIFormElement extends UIElement {
77
+ #private;
78
+ /** Activates platform form association — `setFormValue`/`setValidity` + the form lifecycle callbacks (clause 1). */
79
+ static formAssociated: boolean;
80
+ /** The spreadable universal form attributes — the no-static-props-inheritance workaround (clause 2). */
81
+ static formProps: {
82
+ name: {
83
+ reflect: true;
84
+ type: import("./props.js").PropType<string>;
85
+ default: string;
86
+ attribute?: string | false;
87
+ };
88
+ disabled: {
89
+ reflect: true;
90
+ type: import("./props.js").PropType<boolean>;
91
+ default: boolean;
92
+ attribute?: string | false;
93
+ };
94
+ required: {
95
+ reflect: true;
96
+ type: import("./props.js").PropType<boolean>;
97
+ default: boolean;
98
+ attribute?: string | false;
99
+ };
100
+ };
101
+ /**
102
+ * Super-wrapped wiring (clause 4). `super.connectedCallback()` opens the connection scope + AbortController,
103
+ * runs the no-`super` `connected()` hook, and installs the render effect; the scope is now live, so the
104
+ * effects below are scope-owned and disposed on disconnect (zero residue). They install AFTER the render
105
+ * effect — harmless: the form family's `render()` is the inherited void. The subclass keeps the clean,
106
+ * no-`super` `connected()` hook — it never sees this wrapper.
107
+ */
108
+ connectedCallback(): void;
109
+ /**
110
+ * Re-announce full connection (F1 — the review-caught upgrade-order hole). "Ancestors connect before
111
+ * descendants" holds for INSERTION order but NOT custom-element UPGRADE order: pre-existing DOM whose
112
+ * control module registers/upgrades before the provider/field module does means the ONE connect dispatch
113
+ * in `connectedCallback` fires into the void — no listener is live yet — and registration would be lost
114
+ * forever without a catch-up path. Public so a provider/field's one-shot catch-up scan (built at their
115
+ * own build slice) can call it on every already-connected `UIFormElement` it discovers late, re-dispatching
116
+ * `ui-form-connect` with a FRESH detail (the same minting `#connectDetail()` uses, so a reconnect since
117
+ * the original dispatch is reflected — a NEW connection signal, current prop values).
118
+ *
119
+ * Guard: `connectionSignal === null`, NOT `isConnected` (a real bug an s9 repro caught — the bulk-insert
120
+ * hazard). `isConnected` reflects DOM tree membership, set the instant a subtree is spliced in; a custom
121
+ * element's OWN `connectedCallback` (which opens the connection scope + mints `#ac`) fires ASYNC to that,
122
+ * in a separate reaction-queue pass, ancestor-first. So on a bulk insert of a pre-built offline subtree
123
+ * (assemble, then one `append()` — the house test idiom), every descendant is already `isConnected` the
124
+ * moment the PARENT's `connectedCallback` runs (and could catch-up-scan its already-`isConnected`
125
+ * children) even though THEIR OWN `connectedCallback` — and so `connectionSignal` — hasn't fired yet.
126
+ * NOT a jsdom accommodation: atomic-subtree-connect ordering (ancestor-`connectedCallback`-before-
127
+ * descendant, all descendants already `isConnected` throughout) is SPEC-MANDATED and holds identically in
128
+ * every real engine (s8 confirmed with a plain-HTMLElement probe) — this is a real-browser correctness
129
+ * fix, not a test-environment workaround. `connectionSignal === null` is the correct predicate for "has
130
+ * this control's OWN connect wiring run yet": it is null before that control's first `connectedCallback`
131
+ * (nothing to announce — that `connectedCallback` will dispatch for itself momentarily) AND after its
132
+ * `disconnectedCallback` (`#ac` is nulled there too, element.ts) — one guard covers both the not-yet-mine
133
+ * and no-longer-mine legs; `isConnected` is redundant once this holds and is dropped to avoid it masking
134
+ * the real invariant.
135
+ *
136
+ * This guard closes TWO bugs, not one. The obvious one: the pre-fix crash (a `null` `signal` in the
137
+ * minted detail, `detail.signal.aborted` throwing in a registry/field listener). The SILENT one (s8-found):
138
+ * in the same-cascade race, `ui-field`'s association still applies `aria-labelledby` (the labelling signal
139
+ * write does not ride the connection scope), but the affected control's OWN `connectedCallback` — and so
140
+ * its abort listener — had not yet run, so `this.listen(detail.signal, 'abort', …)` in `#associate` would
141
+ * have wired against a signal that was never live, and a later removal would never dissociate (stale
142
+ * labelling, forever). This guard skips the same-cascade announce entirely and lets the descendant's OWN
143
+ * end-of-`connectedCallback` dispatch (below the guard here — see that dispatch's own signal, always live
144
+ * by construction) carry the association instead, once the field/provider is already listening — so the
145
+ * abort listener installs correctly, every time.
146
+ *
147
+ * Also routes through `#dispatchFormConnect`'s nested-member guard — a catch-up scan's
148
+ * `querySelectorAll('*')` finds every `UIFormElement` descendant indiscriminately (it cannot itself
149
+ * distinguish an internal part like a text-field's calendar from a real member), so the guard MUST live
150
+ * here too, not only on the initial dispatch, or a late-discovered internal part would still register.
151
+ */
152
+ announceFormConnect(): void;
153
+ /**
154
+ * Set a renderer-driven custom validity message (ADR-0029 §5 — the A2UI `checks` seam, native-parity name).
155
+ * An empty string clears the custom contribution (native parity: `el.setCustomValidity('')` = valid).
156
+ * A non-empty message drives `{valid:false, flags:{customError:true}, message}` unless `formValidity()`
157
+ * is already invalid (native wins — e.g. `valueMissing` takes the display slot over a custom message).
158
+ * The reactive effect in `connectedCallback` picks up the `#customValidity` signal change and re-publishes
159
+ * through `#applyValidity` without any direct caller → `internals.setValidity` call here.
160
+ */
161
+ setCustomValidity(message: string): void;
162
+ /** The value this control contributes to its form. Default: nothing (`null`); the subclass returns its value. */
163
+ protected formValue(): FormValue;
164
+ /**
165
+ * This control's validity verdict, published to `internals.setValidity` by a scope-owned effect. Default:
166
+ * always valid; the subclass computes it from its props (e.g. `required && value === '' → valueMissing`).
167
+ *
168
+ * NAMED `formValidity`, NOT `validity` (parallel to `formValue`): the public IDL `validity` GETTER below
169
+ * returns the platform's `ValidityState`, so the verdict hook cannot share that name. ADR-0013 clauses 3/6
170
+ * both wrote `validity`; this base resolves the collision in favour of native IDL parity for `validity`.
171
+ */
172
+ protected formValidity(): ValidityResult;
173
+ /**
174
+ * The disabled state a control should obey: its OWN `disabled` prop OR an ancestor `<fieldset disabled>` /
175
+ * form-disabled state (`formDisabledCallback`). Reactive — read it inside an effect and it re-runs when
176
+ * either source flips. (The form-disabled channel is the ADR-0013 divergence from ADR-0010's `ariaDisabled`
177
+ * for non-form controls.)
178
+ */
179
+ effectiveDisabled(): boolean;
180
+ /**
181
+ * Platform reset → the `formReset()` hook (the subclass restores value ← its defaultValue; clause 2), THEN
182
+ * the ADR-0051 `ui-form-reset` protocol dispatch (composed, bubbling, no detail — the target IS the
183
+ * resetting control) so an observing `ui-field` re-suppresses its visible error. Covers BOTH reset paths:
184
+ * native `form.reset()` (the platform walks its FACE members' `formResetCallback`s) and a form-less
185
+ * provider's direct call to this same public platform callback.
186
+ */
187
+ formResetCallback(): void;
188
+ /** Platform fieldset/form-disabled change → the reactive `#formDisabled` signal behind `effectiveDisabled()`. */
189
+ formDisabledCallback(disabled: boolean): void;
190
+ /** Platform state restore (navigation/autofill) → the `formStateRestore(state)` hook. */
191
+ formStateRestoreCallback(state: File | string | FormData | null, _mode: 'restore' | 'autocomplete'): void;
192
+ /** Platform association change → the no-op `formAssociated(form)` hook (an extension point). */
193
+ formAssociatedCallback(form: HTMLFormElement | null): void;
194
+ /** Restore value ← defaultValue on form reset. Default no-op — the subclass owns `value` + its defaultValue. */
195
+ protected formReset(): void;
196
+ /** Restore value from a previously-submitted/autofilled form state. Default no-op — the subclass decodes `state`. */
197
+ protected formStateRestore(_state: File | string | FormData | null): void;
198
+ /** Called when the element associates with / disassociates from a form. Default no-op extension point. */
199
+ protected formAssociated(_form: HTMLFormElement | null): void;
200
+ /** The owning `<form>`, or null. */
201
+ get form(): HTMLFormElement | null;
202
+ /** The platform's live `ValidityState` (the IDL object; distinct from the `formValidity()` verdict hook). */
203
+ get validity(): ValidityState;
204
+ /** The platform validation message (the `setValidity` message; empty when valid). */
205
+ get validationMessage(): string;
206
+ /** Whether this control is a candidate for constraint validation. */
207
+ get willValidate(): boolean;
208
+ /** Run constraint validation, firing an `invalid` event when invalid. */
209
+ checkValidity(): boolean;
210
+ /** Like `checkValidity`, and additionally reports the problem to the user (focuses the anchor). */
211
+ reportValidity(): boolean;
212
+ /**
213
+ * Public — a `ui-field` calls this with its label/description/error part elements; `null` clears the
214
+ * association (control removed from its field, or the field itself torn down). Signal-backed (the
215
+ * `setCustomValidity` precedent, ADR-0029 §5) — deliberately NOT a prop: elements can't ride attributes,
216
+ * so this stays off the attributes-as-API descriptor surface. Safe on a disconnected control (a plain
217
+ * signal write); the base effect applies it on next connect.
218
+ */
219
+ setFieldLabelling(refs: FieldLabelling | null): void;
220
+ /** Protected reactive read — an override's forwarding effect (or a subclass's own label/message effects,
221
+ * e.g. text-field's wire) re-runs on association/clear. */
222
+ protected get fieldLabelling(): FieldLabelling | null;
223
+ /**
224
+ * Protected forwarding hook (ADR-0051 cl.2), applied by the base scope-owned effect installed in
225
+ * `connectedCallback`. Default: internals ARIA element reflection, GUARDED on `internals.role != null` —
226
+ * every internals-role control (checkbox/switch/radio/slider/calendar) wires for FREE, while a role-less
227
+ * host deliberately no-ops (a half-attached name on a role-less AX node is silent lossiness that can fool
228
+ * a probe — a part-role control, e.g. text-field, MUST override instead). Contract for an override:
229
+ * idempotent; handle `null` (clear); guard a not-yet-created part.
230
+ */
231
+ protected applyFieldLabelling(refs: FieldLabelling | null): void;
232
+ /**
233
+ * Protected user-invalid gate, feeding `FormConnectDetail.userInvalid` (ADR-0050). Default `false`. A
234
+ * control composing `trackUserInvalid` overrides it with its tracker's gate (`() =>
235
+ * tracker.userInvalid()`), keeping ONE timing source — the field's error observation rides this read
236
+ * (jsdom cannot match `:state(user-invalid)`; the browser gate asserts the equivalence).
237
+ */
238
+ protected formUserInvalid(): boolean;
239
+ }
240
+ export {};