@adia-ai/web-components 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -24
- package/USAGE.md +604 -0
- package/components/accordion/accordion.d.ts +17 -0
- package/components/accordion/accordion.js +10 -117
- package/components/accordion/class.js +132 -0
- package/components/action-list/action-list.d.ts +15 -0
- package/components/action-list/action-list.js +9 -140
- package/components/action-list/class.js +156 -0
- package/components/agent-artifact/agent-artifact.d.ts +25 -0
- package/components/agent-artifact/agent-artifact.js +8 -181
- package/components/agent-artifact/class.js +200 -0
- package/components/agent-feedback-bar/agent-feedback-bar.d.ts +21 -0
- package/components/agent-feedback-bar/agent-feedback-bar.js +8 -143
- package/components/agent-feedback-bar/class.js +162 -0
- package/components/agent-questions/agent-questions.d.ts +23 -0
- package/components/agent-questions/agent-questions.js +8 -180
- package/components/agent-questions/class.js +199 -0
- package/components/agent-reasoning/agent-reasoning.d.ts +23 -0
- package/components/agent-reasoning/agent-reasoning.js +8 -494
- package/components/agent-reasoning/class.js +513 -0
- package/components/agent-suggestions/agent-suggestions.d.ts +21 -0
- package/components/agent-suggestions/agent-suggestions.js +8 -78
- package/components/agent-suggestions/class.js +97 -0
- package/components/agent-trace/agent-trace.d.ts +19 -0
- package/components/alert/alert.d.ts +29 -0
- package/components/alert/alert.js +8 -175
- package/components/alert/class.js +194 -0
- package/components/avatar/avatar.d.ts +27 -0
- package/components/avatar/avatar.js +9 -159
- package/components/avatar/class.js +173 -0
- package/components/badge/badge.d.ts +27 -0
- package/components/badge/badge.js +9 -75
- package/components/badge/class.js +93 -0
- package/components/block/block.d.ts +19 -0
- package/components/block/block.js +9 -15
- package/components/block/class.js +33 -0
- package/components/breadcrumb/breadcrumb.d.ts +23 -0
- package/components/breadcrumb/breadcrumb.js +8 -113
- package/components/breadcrumb/class.js +132 -0
- package/components/button/button.d.ts +34 -0
- package/components/button/button.js +15 -66
- package/components/button/class.js +80 -0
- package/components/calendar-picker/calendar-picker.a2ui.json +6 -1
- package/components/calendar-picker/calendar-picker.d.ts +27 -0
- package/components/calendar-picker/calendar-picker.js +8 -332
- package/components/calendar-picker/calendar-picker.yaml +51 -177
- package/components/calendar-picker/class.js +351 -0
- package/components/canvas/canvas.a2ui.json +6 -1
- package/components/canvas/canvas.d.ts +17 -0
- package/components/canvas/canvas.yaml +19 -36
- package/components/card/card.a2ui.json +3 -0
- package/components/card/card.d.ts +27 -0
- package/components/card/card.js +9 -50
- package/components/card/card.yaml +171 -433
- package/components/card/class.js +68 -0
- package/components/chart/chart.d.ts +41 -0
- package/components/chart/chart.js +8 -2131
- package/components/chart/class.js +2150 -0
- package/components/chart-legend/chart-legend.d.ts +27 -0
- package/components/chart-legend/chart-legend.js +8 -197
- package/components/chart-legend/class.js +215 -0
- package/components/chat-thread/chat-thread.d.ts +17 -0
- package/components/chat-thread/chat-thread.js +8 -157
- package/components/chat-thread/class.js +176 -0
- package/components/check/check.d.ts +30 -0
- package/components/check/check.js +11 -52
- package/components/check/class.js +68 -0
- package/components/code/class.js +501 -0
- package/components/code/code.d.ts +39 -0
- package/components/code/code.js +8 -482
- package/components/col/class.js +30 -0
- package/components/col/col.d.ts +23 -0
- package/components/col/col.js +10 -13
- package/components/color-picker/class.js +550 -0
- package/components/color-picker/color-picker.d.ts +37 -0
- package/components/color-picker/color-picker.js +8 -531
- package/components/command/class.js +364 -0
- package/components/command/command.a2ui.json +3 -0
- package/components/command/command.d.ts +19 -0
- package/components/command/command.js +8 -345
- package/components/command/command.yaml +105 -124
- package/components/demo-toggle/class.js +153 -0
- package/components/demo-toggle/demo-toggle.d.ts +23 -0
- package/components/demo-toggle/demo-toggle.js +8 -135
- package/components/description-list/class.js +86 -0
- package/components/description-list/description-list.d.ts +21 -0
- package/components/description-list/description-list.js +8 -67
- package/components/divider/class.js +57 -0
- package/components/divider/divider.d.ts +19 -0
- package/components/divider/divider.js +10 -40
- package/components/drawer/class.js +306 -0
- package/components/drawer/drawer.d.ts +25 -0
- package/components/drawer/drawer.js +8 -287
- package/components/embed/class.js +73 -0
- package/components/embed/embed.d.ts +23 -0
- package/components/embed/embed.js +9 -55
- package/components/empty-state/class.js +108 -0
- package/components/empty-state/empty-state.d.ts +21 -0
- package/components/empty-state/empty-state.js +9 -90
- package/components/feed/class.js +381 -0
- package/components/feed/feed.d.ts +19 -0
- package/components/feed/feed.js +9 -367
- package/components/field/class.js +266 -0
- package/components/field/field.d.ts +23 -0
- package/components/field/field.js +8 -247
- package/components/fields/class.js +106 -0
- package/components/fields/fields.d.ts +19 -0
- package/components/fields/fields.js +8 -87
- package/components/grid/class.js +31 -0
- package/components/grid/grid.d.ts +23 -0
- package/components/grid/grid.js +10 -14
- package/components/heatmap/class.js +305 -0
- package/components/heatmap/heatmap.d.ts +31 -0
- package/components/heatmap/heatmap.js +8 -286
- package/components/icon/class.js +54 -0
- package/components/icon/icon.d.ts +23 -0
- package/components/icon/icon.js +13 -40
- package/components/image/class.js +112 -0
- package/components/image/image.d.ts +33 -0
- package/components/image/image.js +9 -94
- package/components/index.js +1 -0
- package/components/input/class.js +773 -0
- package/components/input/input.a2ui.json +3 -0
- package/components/input/input.d.ts +61 -0
- package/components/input/input.js +8 -755
- package/components/input/input.yaml +171 -442
- package/components/inspector/class.js +142 -0
- package/components/inspector/inspector.a2ui.json +8 -1
- package/components/inspector/inspector.d.ts +17 -0
- package/components/inspector/inspector.js +8 -124
- package/components/inspector/inspector.yaml +15 -30
- package/components/kbd/class.js +34 -0
- package/components/kbd/kbd.a2ui.json +3 -0
- package/components/kbd/kbd.d.ts +17 -0
- package/components/kbd/kbd.js +10 -17
- package/components/kbd/kbd.yaml +54 -185
- package/components/link/class.js +187 -0
- package/components/link/link.d.ts +55 -0
- package/components/link/link.js +8 -168
- package/components/list/class.js +249 -0
- package/components/list/list.d.ts +23 -0
- package/components/list/list.js +9 -231
- package/components/menu/class.js +332 -0
- package/components/menu/menu.d.ts +21 -0
- package/components/menu/menu.js +11 -316
- package/components/modal/class.js +231 -0
- package/components/modal/modal.a2ui.json +5 -1
- package/components/modal/modal.d.ts +23 -0
- package/components/modal/modal.js +8 -212
- package/components/modal/modal.yaml +19 -39
- package/components/nav/class.js +150 -0
- package/components/nav/nav.d.ts +31 -0
- package/components/nav/nav.js +8 -131
- package/components/nav-group/class.js +152 -0
- package/components/nav-group/nav-group.d.ts +35 -0
- package/components/nav-group/nav-group.js +9 -134
- package/components/nav-item/class.js +86 -0
- package/components/nav-item/nav-item.d.ts +37 -0
- package/components/nav-item/nav-item.js +10 -69
- package/components/noodles/class.js +510 -0
- package/components/noodles/noodles.d.ts +33 -0
- package/components/noodles/noodles.js +9 -493
- package/components/option-card/class.js +167 -0
- package/components/option-card/option-card.d.ts +30 -0
- package/components/option-card/option-card.js +8 -149
- package/components/otp-input/class.js +180 -0
- package/components/otp-input/otp-input.a2ui.json +5 -1
- package/components/otp-input/otp-input.d.ts +25 -0
- package/components/otp-input/otp-input.js +9 -162
- package/components/otp-input/otp-input.yaml +45 -174
- package/components/page/class.js +97 -0
- package/components/page/page.d.ts +46 -0
- package/components/page/page.js +8 -79
- package/components/pagination/class.js +195 -0
- package/components/pagination/pagination.d.ts +23 -0
- package/components/pagination/pagination.js +9 -177
- package/components/pane/class.js +186 -0
- package/components/pane/pane.a2ui.json +12 -1
- package/components/pane/pane.css +10 -0
- package/components/pane/pane.d.ts +31 -0
- package/components/pane/pane.js +8 -143
- package/components/pane/pane.yaml +57 -157
- package/components/pipeline-status/class.js +189 -0
- package/components/pipeline-status/pipeline-status.a2ui.json +7 -1
- package/components/pipeline-status/pipeline-status.d.ts +21 -0
- package/components/pipeline-status/pipeline-status.js +9 -172
- package/components/pipeline-status/pipeline-status.yaml +34 -72
- package/components/popover/class.js +194 -0
- package/components/popover/popover.d.ts +23 -0
- package/components/popover/popover.js +9 -176
- package/components/progress/class.js +74 -0
- package/components/progress/progress.a2ui.json +3 -0
- package/components/progress/progress.d.ts +19 -0
- package/components/progress/progress.js +10 -57
- package/components/progress/progress.yaml +124 -287
- package/components/progress-row/class.js +110 -0
- package/components/progress-row/progress-row.d.ts +23 -0
- package/components/progress-row/progress-row.js +8 -92
- package/components/radio/class.js +83 -0
- package/components/radio/radio.d.ts +28 -0
- package/components/radio/radio.js +11 -67
- package/components/range/class.js +194 -0
- package/components/range/range.d.ts +31 -0
- package/components/range/range.js +9 -176
- package/components/rating/class.js +148 -0
- package/components/rating/rating.d.ts +33 -0
- package/components/rating/rating.js +9 -130
- package/components/richtext/class.js +87 -0
- package/components/richtext/richtext.a2ui.json +7 -1
- package/components/richtext/richtext.d.ts +19 -0
- package/components/richtext/richtext.js +8 -68
- package/components/richtext/richtext.yaml +30 -65
- package/components/row/class.js +50 -0
- package/components/row/row.d.ts +27 -0
- package/components/row/row.js +10 -33
- package/components/search/class.js +134 -0
- package/components/search/search.d.ts +35 -0
- package/components/search/search.js +10 -117
- package/components/segment/class.js +62 -0
- package/components/segment/segment.d.ts +25 -0
- package/components/segment/segment.js +10 -45
- package/components/segmented/class.js +165 -0
- package/components/segmented/segmented.a2ui.json +4 -0
- package/components/segmented/segmented.d.ts +24 -0
- package/components/segmented/segmented.js +10 -148
- package/components/segmented/segmented.yaml +41 -59
- package/components/select/class.js +408 -0
- package/components/select/select.d.ts +57 -0
- package/components/select/select.js +15 -396
- package/components/skeleton/class.js +52 -0
- package/components/skeleton/skeleton.d.ts +23 -0
- package/components/skeleton/skeleton.js +8 -34
- package/components/slider/class.js +184 -0
- package/components/slider/slider.d.ts +31 -0
- package/components/slider/slider.js +9 -166
- package/components/stack/class.js +28 -0
- package/components/stack/stack.d.ts +17 -0
- package/components/stack/stack.js +10 -11
- package/components/step-progress/class.js +98 -0
- package/components/step-progress/step-progress.d.ts +27 -0
- package/components/step-progress/step-progress.js +8 -79
- package/components/stepper/class.js +126 -0
- package/components/stepper/stepper.d.ts +19 -0
- package/components/stepper/stepper.js +9 -112
- package/components/stream/class.js +109 -0
- package/components/stream/stream.d.ts +19 -0
- package/components/stream/stream.js +8 -90
- package/components/swatch/class.js +131 -0
- package/components/swatch/swatch.d.ts +28 -0
- package/components/swatch/swatch.js +8 -112
- package/components/swiper/class.js +373 -0
- package/components/swiper/swiper.a2ui.json +4 -0
- package/components/swiper/swiper.d.ts +31 -0
- package/components/swiper/swiper.js +8 -354
- package/components/swiper/swiper.yaml +68 -212
- package/components/switch/class.js +63 -0
- package/components/switch/switch.a2ui.json +6 -1
- package/components/switch/switch.d.ts +30 -0
- package/components/switch/switch.js +11 -47
- package/components/switch/switch.yaml +70 -265
- package/components/table/class.js +1453 -0
- package/components/table/table.d.ts +37 -0
- package/components/table/table.js +8 -1435
- package/components/table-toolbar/class.js +680 -0
- package/components/table-toolbar/table-toolbar.d.ts +33 -0
- package/components/table-toolbar/table-toolbar.js +8 -689
- package/components/tabs/class.js +242 -0
- package/components/tabs/tabs.d.ts +21 -0
- package/components/tabs/tabs.js +8 -223
- package/components/tag/class.js +99 -0
- package/components/tag/tag.d.ts +27 -0
- package/components/tag/tag.js +8 -80
- package/components/text/class.js +46 -0
- package/components/text/text.d.ts +25 -0
- package/components/text/text.js +9 -28
- package/components/textarea/class.js +134 -0
- package/components/textarea/textarea.d.ts +31 -0
- package/components/textarea/textarea.js +11 -118
- package/components/timeline/class.js +176 -0
- package/components/timeline/timeline.d.ts +19 -0
- package/components/timeline/timeline.js +9 -162
- package/components/toast/class.js +92 -0
- package/components/toast/toast.d.ts +23 -0
- package/components/toast/toast.js +9 -76
- package/components/toggle-group/class.js +154 -0
- package/components/toggle-group/toggle-group.d.ts +19 -0
- package/components/toggle-group/toggle-group.js +11 -140
- package/components/toggle-scheme/class.js +286 -0
- package/components/toggle-scheme/toggle-scheme.a2ui.json +197 -0
- package/components/toggle-scheme/toggle-scheme.css +20 -0
- package/components/toggle-scheme/toggle-scheme.d.ts +41 -0
- package/components/toggle-scheme/toggle-scheme.js +17 -0
- package/components/toggle-scheme/toggle-scheme.yaml +173 -0
- package/components/toolbar/class.js +388 -0
- package/components/toolbar/toolbar.d.ts +23 -0
- package/components/toolbar/toolbar.js +10 -376
- package/components/tooltip/class.js +299 -0
- package/components/tooltip/tooltip.d.ts +27 -0
- package/components/tooltip/tooltip.js +8 -280
- package/components/tree/class.js +245 -0
- package/components/tree/tree.d.ts +15 -0
- package/components/tree/tree.js +9 -244
- package/components/upload/class.js +199 -0
- package/components/upload/upload.d.ts +27 -0
- package/components/upload/upload.js +11 -183
- package/core/element.d.ts +174 -0
- package/core/form.d.ts +108 -0
- package/core/index.d.ts +11 -0
- package/core/index.js +1 -0
- package/core/register.d.ts +25 -0
- package/core/register.js +58 -0
- package/core/signals.d.ts +94 -0
- package/core/template.d.ts +70 -0
- package/index.d.ts +315 -0
- package/package.json +25 -6
- package/traits/CATEGORIES.md +1 -1
package/USAGE.md
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
# Using `@adia-ai/web-components` — Consumer Guide
|
|
2
|
+
|
|
3
|
+
The complete reference for engineers and agents **integrating** AdiaUI into an app. For authoring new primitives (contributing to the library), see [`README.md`](./README.md) § "Authoring a primitive".
|
|
4
|
+
|
|
5
|
+
> This document answers the five most-common questions consumers ask. If something here is wrong, surprising, or missing, that's a bug — open an issue with the surface it describes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
1. [Install](#install)
|
|
12
|
+
2. [The mental model](#the-mental-model)
|
|
13
|
+
3. [Property reactivity (signal-backed)](#property-reactivity-signal-backed)
|
|
14
|
+
4. [Property binding patterns (templates / framework integration)](#property-binding-patterns)
|
|
15
|
+
5. [Event contract — `CustomEvent` with `detail`](#event-contract)
|
|
16
|
+
6. [Form participation — `UIFormElement` + `ElementInternals`](#form-participation)
|
|
17
|
+
7. [Lifecycle — `connected` / `render` / `updated` / `disconnected`](#lifecycle)
|
|
18
|
+
8. [Theming, density, size](#theming-density-size)
|
|
19
|
+
9. [Registration — auto vs explicit](#registration--auto-vs-explicit)
|
|
20
|
+
10. [TypeScript](#typescript)
|
|
21
|
+
11. [Anti-patterns](#anti-patterns)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @adia-ai/web-components
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Pair with [`@adia-ai/web-modules`](../web-modules) for composite shells (admin / chat / editor / simple / theme):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @adia-ai/web-components @adia-ai/web-modules
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Importing
|
|
38
|
+
|
|
39
|
+
The package is ESM-only — works under any modern bundler (Vite, esbuild, webpack 5+, Rollup) or plain `<script type="module">`.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// Everything — registers every *-ui tag (95 primitives)
|
|
43
|
+
import '@adia-ai/web-components';
|
|
44
|
+
import '@adia-ai/web-components/css';
|
|
45
|
+
|
|
46
|
+
// Or per-component for tree-shaking-conscious bundles
|
|
47
|
+
import '@adia-ai/web-components/components/button';
|
|
48
|
+
import '@adia-ai/web-components/components/button.css';
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Subpath exports also available:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// Core utilities — signals, templating, base classes
|
|
55
|
+
import { signal, computed, effect, html, UIElement, UIFormElement } from '@adia-ai/web-components/core';
|
|
56
|
+
|
|
57
|
+
// Traits — composable behavior decorators
|
|
58
|
+
import '@adia-ai/web-components/traits';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## The mental model
|
|
64
|
+
|
|
65
|
+
Every AdiaUI primitive is a **standard light-DOM custom element**. There's no shadow DOM, no compiler, no framework — just `customElements.define()` extending a small `UIElement` base class. The base class wires a signal-backed property system on top.
|
|
66
|
+
|
|
67
|
+
The contract every primitive obeys:
|
|
68
|
+
|
|
69
|
+
| Surface | What it is | How to use |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| **Properties** | Reactive — every prop in `static properties` is backed by a signal. Setting `el.prop = v` triggers a re-render. | `slider.value = 75` |
|
|
72
|
+
| **Attributes** | Mirror of properties (where `reflect: true`). Setting `[attr]` round-trips through the property setter. | `<slider-ui value="50">` |
|
|
73
|
+
| **Events** | `CustomEvent` with typed `detail` payload. Bubbling unless noted otherwise. | `slider.addEventListener('change', e => e.detail.value)` |
|
|
74
|
+
| **Slots** | Light-DOM children projected by attribute selectors (`:scope > [slot="icon"]`). No `<slot>` element. | `<card-ui><span slot="heading">Title</span></card-ui>` |
|
|
75
|
+
|
|
76
|
+
This is the entire surface. There are no magic helpers; no proprietary state-management; no compiler. If you know vanilla web components + standard DOM, you know AdiaUI.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Property reactivity (signal-backed)
|
|
81
|
+
|
|
82
|
+
Every property declared in a primitive's `static properties` map is **signal-backed**. The setter mutates an internal `signal()`; the host's render effect subscribes to all signals and re-runs on every property change. So:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const slider = document.querySelector('slider-ui');
|
|
86
|
+
|
|
87
|
+
slider.value = 75; // thumb moves to 75%, readout updates, aria-valuenow reflects
|
|
88
|
+
slider.value = 10; // thumb moves to 10%
|
|
89
|
+
slider.disabled = true; // [disabled] reflects, click handlers no-op
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This works for **every primitive**. There's no "is this prop reactive?" question — they all are.
|
|
93
|
+
|
|
94
|
+
### Attributes round-trip
|
|
95
|
+
|
|
96
|
+
Setting an attribute also propagates. `attributeChangedCallback` writes back into the property setter, which mutates the signal, which fires the render effect:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
slider.setAttribute('value', '25'); // same effect as slider.value = 25
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This means you can drive a primitive from either side. Whatever your tooling sets — properties or attributes — works.
|
|
103
|
+
|
|
104
|
+
### Why this matters
|
|
105
|
+
|
|
106
|
+
Consumers sometimes assume that property changes only update the DOM if explicitly subscribed to via `attributeChangedCallback`. **That's not the case here.** The signal-backed property system means every property is reactive by default, with zero per-component opt-in. If you set a property and the DOM doesn't update, it's a bug in the primitive, not a missing subscription on your side.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Property binding patterns
|
|
111
|
+
|
|
112
|
+
When wiring a primitive's property to external state (your own signals, framework state, RxJS observables, etc.), the binding pattern matters. **An eagerly-evaluated value snapshot is one-shot**, not reactive.
|
|
113
|
+
|
|
114
|
+
### Using the built-in `html` template
|
|
115
|
+
|
|
116
|
+
The package ships its own lightweight template tag at `@adia-ai/web-components/core`. It detects signals and functions as bound values and wraps them in effects:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import { html, signal, stamp } from '@adia-ai/web-components/core';
|
|
120
|
+
|
|
121
|
+
const minL = signal(0.5);
|
|
122
|
+
|
|
123
|
+
// ✅ Reactive — signal passed directly. Re-fires on every minL.value mutation.
|
|
124
|
+
const tpl1 = html`<slider-ui .value=${minL} min="0" max="1" step="0.01"></slider-ui>`;
|
|
125
|
+
|
|
126
|
+
// ✅ Reactive with transform — function-as-value. Function re-runs on dependency change.
|
|
127
|
+
const tpl2 = html`<slider-ui .value=${() => minL.value * 100} min="0" max="100"></slider-ui>`;
|
|
128
|
+
|
|
129
|
+
// ❌ NON-REACTIVE — eagerly evaluated. Snapshots at render time only.
|
|
130
|
+
const tpl3 = html`<slider-ui .value=${minL.value * 100} min="0" max="100"></slider-ui>`;
|
|
131
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^
|
|
132
|
+
// evaluates to a number now; nothing to subscribe to
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The `.prop=` syntax sets the JS property (not the attribute). The `attr=` syntax sets an attribute. Both round-trip through the signal-backed setter, so either works for reactive updates from your side.
|
|
136
|
+
|
|
137
|
+
### Using React, Lit, Vue, Svelte, etc.
|
|
138
|
+
|
|
139
|
+
When the binding system isn't AdiaUI's `html` tag, the rule generalizes:
|
|
140
|
+
|
|
141
|
+
**Set the primitive's property inside whatever effect/watcher hook your framework provides.** The property setter mutates the signal, the host re-renders. Don't try to "bind" the value via attributes alone — properties are the reactive path.
|
|
142
|
+
|
|
143
|
+
```jsx
|
|
144
|
+
// React example
|
|
145
|
+
function MinLSlider({ minL, onMinLChange }) {
|
|
146
|
+
const ref = useRef(null);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (ref.current) ref.current.value = minL * 100;
|
|
149
|
+
}, [minL]);
|
|
150
|
+
return <slider-ui ref={ref} min="0" max="100" onChange={e => onMinLChange(e.detail.value / 100)} />;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<!-- Lit example -->
|
|
156
|
+
<slider-ui .value=${this.minL * 100} @change=${e => this.minL = e.detail.value / 100}></slider-ui>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<!-- Vue example -->
|
|
161
|
+
<slider-ui :value="minL * 100" @change="onMinLChange($event.detail.value / 100)"></slider-ui>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```svelte
|
|
165
|
+
<!-- Svelte example -->
|
|
166
|
+
<slider-ui value={minL * 100} on:change={e => minL = e.detail.value / 100}></slider-ui>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### The eager-evaluation trap
|
|
170
|
+
|
|
171
|
+
Reported real-world bug: a consumer wrote `.value=${signal.value * 100}` and reported "the slider doesn't move on undo." The slider IS reactive; the binding pattern wasn't. `signal.value * 100` evaluates eagerly to a number; the template system never sees the signal, so it can't subscribe.
|
|
172
|
+
|
|
173
|
+
**Fix:** pass `${signal}` (signal direct) or `${() => signal.value * 100}` (function as value). Both get effect-wrapped.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Event contract
|
|
178
|
+
|
|
179
|
+
Every interactive primitive emits **`CustomEvent` with a `detail` payload**. Since v0.4.5, this is uniform across all 17 form-bearing primitives.
|
|
180
|
+
|
|
181
|
+
| Event | Fires on | Detail shape |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `change` | Value commit (blur, drag end, keystroke commit) | `{ value }` or `{ value, checked }` for boolean primitives |
|
|
184
|
+
| `input` | Every value tick (typing, dragging) | `{ value }` |
|
|
185
|
+
| `submit` | Enter without shift in `<textarea-ui>` | (none — `event.preventDefault()` to suppress form submission) |
|
|
186
|
+
| Component-specific | See per-component yaml | Per-component detail |
|
|
187
|
+
|
|
188
|
+
### Listening
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
// Typed payload (canonical)
|
|
192
|
+
slider.addEventListener('change', (e) => {
|
|
193
|
+
console.log(e.detail.value); // canonical reading path
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Untyped (backwards-compatible — `this.value = v` runs before dispatch)
|
|
197
|
+
slider.addEventListener('change', (e) => {
|
|
198
|
+
console.log(e.target.value); // also valid
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Both paths return the same value. Pick whichever your tooling makes ergonomic.
|
|
203
|
+
|
|
204
|
+
### Value-semantic vs boolean-semantic
|
|
205
|
+
|
|
206
|
+
| Class | Components | Detail |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| Value-semantic | input, select, slider, textarea, range, rating, search, otp-input, calendar-picker | `{ value }` |
|
|
209
|
+
| Boolean-semantic | switch, check, radio, option-card | `{ value, checked }` |
|
|
210
|
+
| Special | upload | `{ value, files }` |
|
|
211
|
+
|
|
212
|
+
For boolean primitives, `value` is the form-submission string ("on" by default; the form value attribute if set), and `checked` is the actual checked state.
|
|
213
|
+
|
|
214
|
+
### Component-specific events
|
|
215
|
+
|
|
216
|
+
Some primitives emit named events beyond `change`/`input`:
|
|
217
|
+
|
|
218
|
+
- `<select-ui>` — emits `action` for option-callbacks with `detail.action`
|
|
219
|
+
- `<search-ui>` — emits `search` after debounce with `detail.value`
|
|
220
|
+
- `<chat-composer>` — emits `composer-submit` on Enter with `detail.value`
|
|
221
|
+
- `<color-picker-ui>` — emits multiple typed events (`input`, `change`, `format-change`) with structured detail
|
|
222
|
+
|
|
223
|
+
Each primitive's yaml lists its complete event surface. Live demos at `https://ui-kit.exe.xyz/site/components/<name>`.
|
|
224
|
+
|
|
225
|
+
### Bubbling
|
|
226
|
+
|
|
227
|
+
All standard events bubble. Internal-routing collisions (e.g. a trait listening for an event then re-emitting one with the same name) are avoided by name discipline — see the `event-name-collision` memory entry for the lesson.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Form participation
|
|
232
|
+
|
|
233
|
+
15 primitives extend `UIFormElement` and participate in standard forms via `ElementInternals`:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
input, select, slider, switch, segmented, check, radio, textarea,
|
|
237
|
+
range, color-picker, rating, option-card, search, otp-input, code
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
They behave like native form controls:
|
|
241
|
+
|
|
242
|
+
```html
|
|
243
|
+
<form id="settings">
|
|
244
|
+
<input-ui name="username" type="text" value="alice"></input-ui>
|
|
245
|
+
<select-ui name="theme" value="ocean">…</select-ui>
|
|
246
|
+
<slider-ui name="density" value="50" min="0" max="100"></slider-ui>
|
|
247
|
+
<switch-ui name="notifications" checked></switch-ui>
|
|
248
|
+
<button-ui type="submit" text="Save"></button-ui>
|
|
249
|
+
</form>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
const form = document.getElementById('settings');
|
|
254
|
+
const data = new FormData(form);
|
|
255
|
+
// → { username: "alice", theme: "ocean", density: "50", notifications: "on" }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Validation
|
|
259
|
+
|
|
260
|
+
The full validation surface from native form controls works:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
input.checkValidity(); // boolean
|
|
264
|
+
input.reportValidity(); // boolean + shows error UI
|
|
265
|
+
input.setInvalid('Custom'); // mark invalid with custom message
|
|
266
|
+
input.setValid(); // clear error state
|
|
267
|
+
input.validity; // ValidityState — { valid, valueMissing, patternMismatch, ... }
|
|
268
|
+
input.validationMessage; // localized error message
|
|
269
|
+
input.willValidate; // boolean
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Constraint attributes (`required`, `pattern`, `minlength`, `maxlength`) work natively. CSS pseudo-classes (`:valid`, `:invalid`, `:required`, `:disabled`) work in your own CSS without any opt-in.
|
|
273
|
+
|
|
274
|
+
### Custom validation timing
|
|
275
|
+
|
|
276
|
+
`UIFormElement` validates **on blur if dirty** and **on form reset**. Override the timing via:
|
|
277
|
+
|
|
278
|
+
```js
|
|
279
|
+
const input = document.querySelector('input-ui');
|
|
280
|
+
input.addEventListener('blur', () => {
|
|
281
|
+
if (!input.value.includes('@')) {
|
|
282
|
+
input.setInvalid('Must be an email');
|
|
283
|
+
} else {
|
|
284
|
+
input.setValid();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Form events
|
|
290
|
+
|
|
291
|
+
Submit events bubble normally:
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
form.addEventListener('submit', (e) => {
|
|
295
|
+
e.preventDefault();
|
|
296
|
+
const data = new FormData(form);
|
|
297
|
+
fetch('/api/save', { method: 'POST', body: data });
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Lifecycle
|
|
304
|
+
|
|
305
|
+
`UIElement` exposes four overridable lifecycle methods. Defaults are no-ops. Override what you need:
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
import { UIElement } from '@adia-ai/web-components/core';
|
|
309
|
+
|
|
310
|
+
class MyWidget extends UIElement {
|
|
311
|
+
static properties = {
|
|
312
|
+
label: { type: String, default: '' },
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
#onKeydown = (e) => { // stable arrow field — required for remove
|
|
316
|
+
if (e.key === 'Escape') this.label = '';
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
connected() {
|
|
320
|
+
// Called once per insertion.
|
|
321
|
+
// Set up listeners on document / external targets here.
|
|
322
|
+
document.addEventListener('keydown', this.#onKeydown);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
render() {
|
|
326
|
+
// Called every time any property signal mutates.
|
|
327
|
+
// Update internal DOM here. Reading `this.foo` subscribes the host's
|
|
328
|
+
// render effect to that signal.
|
|
329
|
+
this.textContent = this.label || 'Empty';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
updated(changed) {
|
|
333
|
+
// Called after `render()`.
|
|
334
|
+
// `changed` is a Map<propName, oldValue> for this tick.
|
|
335
|
+
if (changed.has('label')) {
|
|
336
|
+
this.dispatchEvent(new CustomEvent('label-change', {
|
|
337
|
+
bubbles: true, detail: { value: this.label }
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
disconnected() {
|
|
343
|
+
// Called once per removal.
|
|
344
|
+
// Clean up listeners + intervals + observers here.
|
|
345
|
+
document.removeEventListener('keydown', this.#onKeydown);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
customElements.define('my-widget', MyWidget);
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Why the `#field` arrow pattern
|
|
353
|
+
|
|
354
|
+
`addEventListener('event', fn)` and `removeEventListener('event', fn)` only match when `fn` is the same reference. Inline arrows allocate a fresh closure every call, so the remove never fires:
|
|
355
|
+
|
|
356
|
+
```js
|
|
357
|
+
// ❌ Wrong — different reference each call
|
|
358
|
+
connected() { document.addEventListener('click', (e) => this.foo()); }
|
|
359
|
+
disconnected() { document.removeEventListener('click', (e) => this.foo()); } // doesn't remove
|
|
360
|
+
|
|
361
|
+
// ✅ Correct — stable reference
|
|
362
|
+
#onClick = (e) => this.foo();
|
|
363
|
+
connected() { document.addEventListener('click', this.#onClick); }
|
|
364
|
+
disconnected() { document.removeEventListener('click', this.#onClick); }
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The same applies to `bind()` — call it once in the constructor or use a field, not at the listener site.
|
|
368
|
+
|
|
369
|
+
### Order of operations
|
|
370
|
+
|
|
371
|
+
1. `constructor()` — `installProps()` defines reactive getters/setters from `static properties`
|
|
372
|
+
2. Element inserted into DOM → `connectedCallback()` fires
|
|
373
|
+
3. `connected()` (the overridable hook) runs in `untracked()` so reads don't subscribe outer effects
|
|
374
|
+
4. Host's main render effect installs: subscribes to all property signals, calls `template()` + `render()`
|
|
375
|
+
5. On any property change: effect re-runs, re-stamps template, calls `render()` then `updated(changed)`
|
|
376
|
+
6. Element removed → `disconnectedCallback()` fires → `disconnected()` (overridable)
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Theming, density, size
|
|
381
|
+
|
|
382
|
+
AdiaUI is parametric. Three attributes drive global appearance:
|
|
383
|
+
|
|
384
|
+
```html
|
|
385
|
+
<div data-theme="ocean" density="compact" size="sm">
|
|
386
|
+
<!-- all descendants re-theme / re-densify / re-scale -->
|
|
387
|
+
</div>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
| Attribute | Values | Effect |
|
|
391
|
+
|---|---|---|
|
|
392
|
+
| `[data-theme]` | `default`, `ocean`, `forest`, `sunset`, `lavender`, `rose`, `slate`, `midnight` | Swaps the OKLCH color ramp |
|
|
393
|
+
| `[density]` | `compact` (0.85×), `spacious` (1.15×) | Multiplies `--a-density` token |
|
|
394
|
+
| `[size]` | `sm`, `md`, `lg` | Shifts typescale + component dimensions |
|
|
395
|
+
| `[radius]` | `sharp` (0), `rounded` (1), `round` (2) | Multiplies `--a-radius-k` token |
|
|
396
|
+
|
|
397
|
+
Each is a CSS-variable override; no class toggles, no re-imports. Set on any ancestor → applies to that subtree.
|
|
398
|
+
|
|
399
|
+
For a control surface to let users change these at runtime, use [`<theme-panel>`](https://ui-kit.exe.xyz/site/components/theme-panel) from `@adia-ai/web-modules/theme`.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Registration — auto vs explicit
|
|
404
|
+
|
|
405
|
+
### Auto-register (default)
|
|
406
|
+
|
|
407
|
+
Importing a primitive's `.js` file immediately runs `customElements.define('button-ui', UIButton)`. This is the canonical path — once imported, the tag works everywhere:
|
|
408
|
+
|
|
409
|
+
```js
|
|
410
|
+
import '@adia-ai/web-components/components/button';
|
|
411
|
+
// <button-ui> is now usable in any markup
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
The `sideEffects` field in `package.json` keeps this path tree-shake-safe.
|
|
415
|
+
|
|
416
|
+
### Conflict-safe registration (`defineIfFree`)
|
|
417
|
+
|
|
418
|
+
Multiple packages may try to register the same tag (e.g. when a host page imports the AdiaUI bundle AND a downstream consumer imports per-component subpaths). Native `customElements.define` throws `NotSupportedError` on duplicate registration. The package ships a no-op-on-duplicate helper:
|
|
419
|
+
|
|
420
|
+
```js
|
|
421
|
+
import { defineIfFree, isRegistered, register } from '@adia-ai/web-components/core';
|
|
422
|
+
|
|
423
|
+
class MyWidget extends HTMLElement { /* … */ }
|
|
424
|
+
|
|
425
|
+
// Registers if free; returns false (no throw) if already taken
|
|
426
|
+
const ok = defineIfFree('my-widget', MyWidget);
|
|
427
|
+
|
|
428
|
+
// Check before registering
|
|
429
|
+
if (!isRegistered('my-widget')) {
|
|
430
|
+
register('my-widget', MyWidget);
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
When subclassing an AdiaUI primitive under a different tag name:
|
|
435
|
+
|
|
436
|
+
```js
|
|
437
|
+
import { UISlider } from '@adia-ai/web-components/components/slider';
|
|
438
|
+
import { register } from '@adia-ai/web-components/core';
|
|
439
|
+
|
|
440
|
+
class MySlider extends UISlider {
|
|
441
|
+
static properties = { ...UISlider.properties, custom: { type: String } };
|
|
442
|
+
}
|
|
443
|
+
register('my-slider', MySlider);
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
The `slider-ui` import auto-registers, but that doesn't interfere — `my-slider` is a distinct tag that hosts your subclass.
|
|
447
|
+
|
|
448
|
+
### Non-side-effect class imports (since v0.4.7)
|
|
449
|
+
|
|
450
|
+
Every component ships a sibling `class` subpath that exports the class **without** running `customElements.define`. Use it when you need test isolation, tag-name override, or selective composition:
|
|
451
|
+
|
|
452
|
+
```js
|
|
453
|
+
// Auto-register (default; tag registered immediately):
|
|
454
|
+
import '@adia-ai/web-components/components/slider';
|
|
455
|
+
|
|
456
|
+
// Class import (no auto-register; you decide when to define):
|
|
457
|
+
import { UISlider } from '@adia-ai/web-components/components/slider/class';
|
|
458
|
+
|
|
459
|
+
// Subclass + register under a different tag
|
|
460
|
+
class MySlider extends UISlider {
|
|
461
|
+
static properties = { ...UISlider.properties, custom: { type: String } };
|
|
462
|
+
}
|
|
463
|
+
customElements.define('my-slider', MySlider);
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**Test isolation example** — register the class under a unique per-test tag so subclass tests don't clobber the global registration:
|
|
467
|
+
|
|
468
|
+
```js
|
|
469
|
+
import { UISlider } from '@adia-ai/web-components/components/slider/class';
|
|
470
|
+
import { defineIfFree } from '@adia-ai/web-components/core';
|
|
471
|
+
|
|
472
|
+
beforeEach(() => {
|
|
473
|
+
const tag = `slider-ui-test-${Math.random().toString(36).slice(2, 8)}`;
|
|
474
|
+
defineIfFree(tag, class extends UISlider { /* test overrides */ });
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The class subpath is available for every component that has an `<x-ui>` tag (~95 primitives). TypeScript types are picked up from the same per-primitive `.d.ts` files used by the auto-register path — no separate `.d.ts` per subpath needed.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## TypeScript
|
|
483
|
+
|
|
484
|
+
Since v0.4.6, the package ships `.d.ts` declarations. v0.4.6 covered the 17 form-bearing primitives (input/select/slider/etc.) with hand-authored rich event types; v0.4.7 codegen extended coverage to the remaining 73 non-form primitives (button/card/table/chart/etc.) from each component's `.a2ui.json` sidecar. **90 of 95** primitives that ship a JS class are now typed (the 5 CSS-only "elements" — `header`, `footer`, `section`, etc. — have no class). Adding the package to your dependencies automatically picks up:
|
|
485
|
+
|
|
486
|
+
- Typed properties on every primitive (e.g. `el.value` is `number` on `<slider-ui>`, `boolean` on `<switch-ui>`)
|
|
487
|
+
- Typed events — `addEventListener('change', e => e.detail.value)` infers the detail shape
|
|
488
|
+
- `HTMLElementTagNameMap` augmentation — `document.querySelector('slider-ui')` returns `UISlider`
|
|
489
|
+
- Public types for `UIElement`, `UIFormElement`, signal, computed, effect, html, stamp
|
|
490
|
+
|
|
491
|
+
```ts
|
|
492
|
+
import type { UISlider, SliderChangeEvent } from '@adia-ai/web-components';
|
|
493
|
+
|
|
494
|
+
const slider: UISlider = document.querySelector('slider-ui')!;
|
|
495
|
+
slider.value = 75; // typed Number — string would error
|
|
496
|
+
|
|
497
|
+
slider.addEventListener('change', (e: SliderChangeEvent) => {
|
|
498
|
+
console.log(e.detail.value); // number, inferred
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
slider.addEventListener('change', (e) => { // also inferred
|
|
502
|
+
e.detail.value.toFixed(2); // ok — Number.prototype is in scope
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
For older bundlers without `exports` field support, add to `tsconfig.json`:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"compilerOptions": {
|
|
511
|
+
"moduleResolution": "bundler"
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Anti-patterns
|
|
519
|
+
|
|
520
|
+
Things to avoid, with why:
|
|
521
|
+
|
|
522
|
+
### ❌ Don't bind eagerly-evaluated values to reactive props
|
|
523
|
+
|
|
524
|
+
```js
|
|
525
|
+
// BREAKS — value is a number, not a signal
|
|
526
|
+
<slider-ui .value=${signal.value * 100}></slider-ui>
|
|
527
|
+
|
|
528
|
+
// WORKS — pass the signal or a function
|
|
529
|
+
<slider-ui .value=${signal}></slider-ui>
|
|
530
|
+
<slider-ui .value=${() => signal.value * 100}></slider-ui>
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### ❌ Don't subscribe via `attributeChangedCallback` to a primitive's attributes
|
|
534
|
+
|
|
535
|
+
The base class already handles this. Use the property setter instead:
|
|
536
|
+
|
|
537
|
+
```js
|
|
538
|
+
// BREAKS — fragile, duplicates the base class's mirror
|
|
539
|
+
const observer = new MutationObserver(...);
|
|
540
|
+
observer.observe(slider, { attributes: ['value'] });
|
|
541
|
+
|
|
542
|
+
// WORKS — set the property; signals propagate
|
|
543
|
+
slider.value = 75;
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### ❌ Don't use shadow DOM patterns
|
|
547
|
+
|
|
548
|
+
AdiaUI is light-DOM. `::part()`, `::slotted()`, `<slot>` elements don't exist:
|
|
549
|
+
|
|
550
|
+
```js
|
|
551
|
+
// BREAKS — no shadow tree
|
|
552
|
+
slider.shadowRoot.querySelector('.thumb');
|
|
553
|
+
|
|
554
|
+
// WORKS — children are real DOM
|
|
555
|
+
slider.querySelector('[slot="thumb"]');
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### ❌ Don't add inline event handlers via `onchange=`
|
|
559
|
+
|
|
560
|
+
The HTML `on*` attributes don't pick up the typed `detail`:
|
|
561
|
+
|
|
562
|
+
```html
|
|
563
|
+
<!-- BREAKS — fires but no typed handler signature -->
|
|
564
|
+
<slider-ui onchange="console.log(this.value)"></slider-ui>
|
|
565
|
+
|
|
566
|
+
<!-- WORKS — addEventListener gets the CustomEvent -->
|
|
567
|
+
<slider-ui id="s"></slider-ui>
|
|
568
|
+
<script>
|
|
569
|
+
document.getElementById('s').addEventListener('change', e => console.log(e.detail.value));
|
|
570
|
+
</script>
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### ❌ Don't try to register a tag name twice
|
|
574
|
+
|
|
575
|
+
`customElements.define()` throws on re-registration. The auto-register pattern handles this for you. If you import a primitive twice (e.g. via two different paths), the bundler de-dupes; if it doesn't, the first define wins and the second throws.
|
|
576
|
+
|
|
577
|
+
### ❌ Don't write `static properties` with un-typed defaults
|
|
578
|
+
|
|
579
|
+
```js
|
|
580
|
+
// BREAKS — undefined defaults cause attribute parsing surprises
|
|
581
|
+
static properties = { value: {} };
|
|
582
|
+
|
|
583
|
+
// WORKS — declare type + default
|
|
584
|
+
static properties = {
|
|
585
|
+
value: { type: Number, default: 0, reflect: true },
|
|
586
|
+
};
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### ❌ Don't read property values from inside `constructor()`
|
|
590
|
+
|
|
591
|
+
Properties are installed by `installProps()` in `super()`, but the host's render effect doesn't install until `connectedCallback()`. Reading properties in the constructor returns defaults; mutating them is fine, but if you depend on the rendered DOM existing, do it in `connected()` or later.
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
## More
|
|
596
|
+
|
|
597
|
+
- [`README.md`](./README.md) — package overview + authoring guide
|
|
598
|
+
- [Component yaml contracts](./components/) — every primitive's complete API (props, events, slots, examples)
|
|
599
|
+
- [Live demos](https://ui-kit.exe.xyz/site/components/) — every primitive in action
|
|
600
|
+
- [`docs/specs/component-token-contract.md`](../../docs/specs/component-token-contract.md) — full authoring contract
|
|
601
|
+
- [`docs/specs/component-implementation-patterns.md`](../../docs/specs/component-implementation-patterns.md) — render strategies
|
|
602
|
+
- [`docs/specs/traits.md`](../../docs/specs/traits.md) — composable behaviors
|
|
603
|
+
|
|
604
|
+
For consumer questions not covered here, file an issue against [adiahealth/gen-ui-kit](https://github.com/adiahealth/gen-ui-kit/issues). This document is the canonical answer — if it doesn't have your answer, it's a doc gap worth fixing.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<accordion-ui>` — Accordion container managing single/multiple open states.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/accordion
|
|
5
|
+
*
|
|
6
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
7
|
+
* the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
|
|
8
|
+
* run `npm run components`, then `npm run codegen:dts` to regenerate;
|
|
9
|
+
* or hand-author this file fully if rich event types are needed.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { UIElement } from '../../core/element.js';
|
|
13
|
+
|
|
14
|
+
export class UIAccordion extends UIElement {
|
|
15
|
+
/** Allow multiple panels to be open simultaneously */
|
|
16
|
+
multiple: boolean;
|
|
17
|
+
}
|