@adia-ai/web-components 0.5.4 → 0.5.6
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/components/accordion/accordion-item.a2ui.json +50 -0
- package/components/accordion/accordion-item.yaml +27 -0
- package/components/action-list/action-item.a2ui.json +63 -0
- package/components/action-list/action-item.yaml +37 -0
- package/components/agent-feedback-bar/class.js +9 -3
- package/components/avatar/avatar-group.a2ui.json +50 -0
- package/components/avatar/avatar-group.yaml +26 -0
- package/components/avatar/avatar.a2ui.json +4 -1
- package/components/avatar/avatar.yaml +7 -0
- package/components/button/class.js +39 -0
- package/components/chart/chart.a2ui.json +4 -2
- package/components/list/list-item.a2ui.json +53 -0
- package/components/list/list-item.yaml +29 -0
- package/components/segmented/segmented.d.ts +3 -3
- package/components/select/class.js +14 -0
- package/components/select/select.a2ui.json +5 -0
- package/components/select/select.css +10 -0
- package/components/select/select.d.ts +3 -3
- package/components/select/select.yaml +5 -0
- package/components/slider/class.js +58 -0
- package/components/slider/slider.a2ui.json +10 -0
- package/components/slider/slider.css +13 -0
- package/components/slider/slider.yaml +10 -0
- package/components/switch/class.js +18 -4
- package/components/switch/switch.css +10 -0
- package/components/switch/switch.d.ts +3 -3
- package/components/tabs/tab.a2ui.json +58 -0
- package/components/tabs/tab.yaml +33 -0
- package/components/timeline/timeline-item.a2ui.json +76 -0
- package/components/timeline/timeline-item.yaml +47 -0
- package/components/toast/toast.d.ts +35 -0
- package/components/tree/class.js +91 -0
- package/components/tree/tree-item.a2ui.json +65 -0
- package/components/tree/tree-item.yaml +41 -0
- package/components/tree/tree.a2ui.json +15 -0
- package/components/tree/tree.css +18 -0
- package/components/tree/tree.yaml +10 -0
- package/core/anchor.d.ts +71 -0
- package/core/controller.d.ts +171 -0
- package/core/markdown.d.ts +26 -0
- package/core/polyfills.d.ts +31 -0
- package/core/provider.d.ts +82 -0
- package/core/streams-bridge.d.ts +78 -0
- package/core/template.js +21 -3
- package/core/transport.d.ts +78 -0
- package/package.json +2 -2
|
@@ -42,13 +42,31 @@ export class UISlider extends UIFormElement {
|
|
|
42
42
|
step: { type: Number, default: 1, reflect: true },
|
|
43
43
|
label: { type: String, default: '', reflect: true },
|
|
44
44
|
suffix: { type: String, default: '', reflect: true },
|
|
45
|
+
// §184 (v0.5.5, FEEDBACK-08 §4): declarative debounce for the
|
|
46
|
+
// `input` event when driving expensive computation (palette regen,
|
|
47
|
+
// shader compile, large list reflow). When > 0, value updates +
|
|
48
|
+
// visual feedback are immediate but `input` event emission is
|
|
49
|
+
// debounced — only the FINAL value in the throttle window dispatches.
|
|
50
|
+
// `change` fires unthrottled on pointerup / track click / keyboard;
|
|
51
|
+
// any pending `input` flushes BEFORE `change` so consumers always
|
|
52
|
+
// see input→input→…→input→change ordering. throttle="0" (default)
|
|
53
|
+
// preserves the pre-§184 every-pointer-move-fires-input behavior.
|
|
54
|
+
throttle: { type: Number, default: 0, reflect: true },
|
|
45
55
|
};
|
|
46
56
|
|
|
47
57
|
static template = () => null;
|
|
48
58
|
|
|
59
|
+
// §184: per-instance hint id counter for aria-describedby wiring.
|
|
60
|
+
static #hintSeq = 0;
|
|
61
|
+
|
|
49
62
|
#trackEl = null;
|
|
50
63
|
#thumbEl = null;
|
|
51
64
|
#dragging = false;
|
|
65
|
+
// §184 (v0.5.5, FEEDBACK-08 §4): debounce timer for the `input`
|
|
66
|
+
// event. When `throttle > 0`, #setValue stores a pending dispatch
|
|
67
|
+
// here + restarts the timer on every value change. Flushed before
|
|
68
|
+
// any `change` event so input always precedes change.
|
|
69
|
+
#inputTimer = null;
|
|
52
70
|
|
|
53
71
|
get #pct() {
|
|
54
72
|
const range = this.max - this.min;
|
|
@@ -69,6 +87,9 @@ export class UISlider extends UIFormElement {
|
|
|
69
87
|
if (this.label) this.setAttribute('aria-label', this.label);
|
|
70
88
|
|
|
71
89
|
if (!this.querySelector('[slot="track"]')) {
|
|
90
|
+
// §184 (v0.5.5, FEEDBACK-08 §7): hint slot stamped underneath
|
|
91
|
+
// the track when [hint] is set. Wired to aria-describedby below.
|
|
92
|
+
const hintId = this.hint ? `slider-hint-${++UISlider.#hintSeq}` : '';
|
|
72
93
|
this.innerHTML = `
|
|
73
94
|
<div slot="header">
|
|
74
95
|
${this.label ? `<span slot="label">${this.label}</span>` : ''}
|
|
@@ -81,7 +102,9 @@ export class UISlider extends UIFormElement {
|
|
|
81
102
|
<div slot="fill"></div>
|
|
82
103
|
<div slot="thumb" tabindex="0"></div>
|
|
83
104
|
</div>
|
|
105
|
+
${this.hint ? `<span slot="hint" id="${hintId}">${this.hint}</span>` : ''}
|
|
84
106
|
`;
|
|
107
|
+
if (this.hint) this.setAttribute('aria-describedby', hintId);
|
|
85
108
|
}
|
|
86
109
|
|
|
87
110
|
this.#trackEl = this.querySelector('[slot="track"]');
|
|
@@ -148,9 +171,36 @@ export class UISlider extends UIFormElement {
|
|
|
148
171
|
#setValue(v) {
|
|
149
172
|
if (v === this.value) return;
|
|
150
173
|
this.value = v;
|
|
174
|
+
// §184: when throttle > 0, debounce the `input` dispatch.
|
|
175
|
+
// The value update + UI is still immediate; only event emission is
|
|
176
|
+
// accumulated. Same value can dispatch input multiple times across
|
|
177
|
+
// pointer moves, but the throttle collapses them to one trailing
|
|
178
|
+
// emission at quiet+throttle ms.
|
|
179
|
+
const t = Number(this.throttle) || 0;
|
|
180
|
+
if (t > 0) {
|
|
181
|
+
if (this.#inputTimer != null) clearTimeout(this.#inputTimer);
|
|
182
|
+
this.#inputTimer = setTimeout(() => {
|
|
183
|
+
this.#inputTimer = null;
|
|
184
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
185
|
+
}, t);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
151
188
|
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
152
189
|
}
|
|
153
190
|
|
|
191
|
+
/**
|
|
192
|
+
* §184: flush any pending throttled `input` dispatch synchronously.
|
|
193
|
+
* Called before `change` so consumers see the trailing input event
|
|
194
|
+
* BEFORE the change commit. No-op when no timer is pending.
|
|
195
|
+
*/
|
|
196
|
+
#flushInput() {
|
|
197
|
+
if (this.#inputTimer != null) {
|
|
198
|
+
clearTimeout(this.#inputTimer);
|
|
199
|
+
this.#inputTimer = null;
|
|
200
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
154
204
|
#onPointerDown = (e) => {
|
|
155
205
|
if (this.disabled) return;
|
|
156
206
|
e.preventDefault();
|
|
@@ -170,12 +220,14 @@ export class UISlider extends UIFormElement {
|
|
|
170
220
|
this.#thumbEl.releasePointerCapture(e.pointerId);
|
|
171
221
|
this.#thumbEl.removeEventListener('pointermove', this.#onPointerMove);
|
|
172
222
|
this.#thumbEl.removeEventListener('pointerup', this.#onPointerUp);
|
|
223
|
+
this.#flushInput(); // §184: pending throttled input fires before change
|
|
173
224
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
174
225
|
};
|
|
175
226
|
|
|
176
227
|
#onTrackClick = (e) => {
|
|
177
228
|
if (this.disabled || e.target === this.#thumbEl) return;
|
|
178
229
|
this.#setValue(this.#valueFromX(e.clientX));
|
|
230
|
+
this.#flushInput(); // §184: ensure trailing input precedes change
|
|
179
231
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
180
232
|
};
|
|
181
233
|
|
|
@@ -193,6 +245,7 @@ export class UISlider extends UIFormElement {
|
|
|
193
245
|
}
|
|
194
246
|
e.preventDefault();
|
|
195
247
|
this.#setValue(this.#snap(v));
|
|
248
|
+
this.#flushInput(); // §184: trailing input fires before change
|
|
196
249
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
197
250
|
};
|
|
198
251
|
|
|
@@ -203,6 +256,11 @@ export class UISlider extends UIFormElement {
|
|
|
203
256
|
this.#thumbEl?.removeEventListener('pointermove', this.#onPointerMove);
|
|
204
257
|
this.#thumbEl?.removeEventListener('pointerup', this.#onPointerUp);
|
|
205
258
|
this.removeEventListener('keydown', this.#onKey);
|
|
259
|
+
// §184: drop any pending throttle timer (no flush — element is gone)
|
|
260
|
+
if (this.#inputTimer != null) {
|
|
261
|
+
clearTimeout(this.#inputTimer);
|
|
262
|
+
this.#inputTimer = null;
|
|
263
|
+
}
|
|
206
264
|
this.#trackEl = null;
|
|
207
265
|
this.#thumbEl = null;
|
|
208
266
|
}
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"type": "string",
|
|
32
32
|
"default": ""
|
|
33
33
|
},
|
|
34
|
+
"hint": {
|
|
35
|
+
"description": "§184 (v0.5.5, FEEDBACK-08 §7): small caption rendered beneath the slider track. Sets `aria-describedby` on the host so screen readers announce it as a description (distinct from `aria-label`, which comes from `label`). Does not conflict with the in-component `label`. Use for semantic clarifications a `<field-ui>` wrapper would be overkill for.",
|
|
36
|
+
"type": "string",
|
|
37
|
+
"default": ""
|
|
38
|
+
},
|
|
34
39
|
"label": {
|
|
35
40
|
"description": "Label text above the slider",
|
|
36
41
|
"type": "string",
|
|
@@ -61,6 +66,11 @@
|
|
|
61
66
|
"type": "string",
|
|
62
67
|
"default": ""
|
|
63
68
|
},
|
|
69
|
+
"throttle": {
|
|
70
|
+
"description": "§184 (v0.5.5, FEEDBACK-08 §4): when > 0, debounce the `input` event by this many milliseconds. Value updates + visual feedback remain immediate; only event dispatch accumulates. Pending input flushes BEFORE `change` so consumers always see input→…→input→change ordering. throttle=\"0\" (default) preserves the pre-§184 every-pointer-move-fires-input behavior. Common values: 50-100ms for palette regen / shader compile / large list reflow.",
|
|
71
|
+
"type": "number",
|
|
72
|
+
"default": 0
|
|
73
|
+
},
|
|
64
74
|
"value": {
|
|
65
75
|
"description": "Current slider value",
|
|
66
76
|
"type": "number",
|
|
@@ -134,4 +134,17 @@
|
|
|
134
134
|
:scope[disabled] [slot="fill"] { background: var(--slider-fill-bg-disabled); }
|
|
135
135
|
:scope[disabled] [slot="thumb"] { cursor: not-allowed; background: var(--slider-thumb-bg-disabled); }
|
|
136
136
|
:scope[disabled] [slot="thumb"]:hover { box-shadow: none; }
|
|
137
|
+
|
|
138
|
+
/* ── Hint (§184, v0.5.5, FEEDBACK-08 §7) ──
|
|
139
|
+
Small caption rendered beneath the track. aria-describedby is
|
|
140
|
+
wired on the host in class.js so screen readers announce this
|
|
141
|
+
as a description (distinct from aria-label, which comes from
|
|
142
|
+
[label]). Uses the same muted typography as field-ui's hint. */
|
|
143
|
+
[slot="hint"] {
|
|
144
|
+
display: block;
|
|
145
|
+
margin-top: var(--slider-hint-mt, var(--a-space-1));
|
|
146
|
+
font-size: var(--slider-hint-size, var(--a-fine-size));
|
|
147
|
+
color: var(--slider-hint-fg, var(--a-fg-muted));
|
|
148
|
+
line-height: var(--slider-hint-lh, 1.4);
|
|
149
|
+
}
|
|
137
150
|
}
|
|
@@ -51,6 +51,16 @@ props:
|
|
|
51
51
|
description: Current slider value
|
|
52
52
|
type: number
|
|
53
53
|
default: 50
|
|
54
|
+
throttle:
|
|
55
|
+
description: |-
|
|
56
|
+
§184 (v0.5.5, FEEDBACK-08 §4): when > 0, debounce the `input` event by this many milliseconds. Value updates + visual feedback remain immediate; only event dispatch accumulates. Pending input flushes BEFORE `change` so consumers always see input→…→input→change ordering. throttle="0" (default) preserves the pre-§184 every-pointer-move-fires-input behavior. Common values: 50-100ms for palette regen / shader compile / large list reflow.
|
|
57
|
+
type: number
|
|
58
|
+
default: 0
|
|
59
|
+
hint:
|
|
60
|
+
description: |-
|
|
61
|
+
§184 (v0.5.5, FEEDBACK-08 §7): small caption rendered beneath the slider track. Sets `aria-describedby` on the host so screen readers announce it as a description (distinct from `aria-label`, which comes from `label`). Does not conflict with the in-component `label`. Use for semantic clarifications a `<field-ui>` wrapper would be overkill for.
|
|
62
|
+
type: string
|
|
63
|
+
default: ""
|
|
54
64
|
events:
|
|
55
65
|
change:
|
|
56
66
|
description: "Fired when the value changes (on blur for inputs, on selection for pickers)."
|
|
@@ -26,12 +26,26 @@ export class UISwitch extends UIFormElement {
|
|
|
26
26
|
checked: { type: Boolean, default: false, reflect: true },
|
|
27
27
|
label: { type: String, default: '', reflect: true },
|
|
28
28
|
size: { type: String, default: '', reflect: true },
|
|
29
|
+
// §184 (v0.5.5, FEEDBACK-08 §7): caption beneath the toggle.
|
|
30
|
+
// switch.yaml has declared `hint` since §170 but the template
|
|
31
|
+
// never rendered it — this arc closes the spec-vs-impl gap +
|
|
32
|
+
// wires aria-describedby on the host for screen readers.
|
|
33
|
+
hint: { type: String, default: '', reflect: true },
|
|
29
34
|
};
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
// §184: per-instance hint id counter for aria-describedby wiring.
|
|
37
|
+
static #hintSeq = 0;
|
|
38
|
+
|
|
39
|
+
static template = (el) => {
|
|
40
|
+
const hintId = el.hint ? `switch-hint-${++UISwitch.#hintSeq}` : '';
|
|
41
|
+
if (hintId) el.setAttribute('aria-describedby', hintId);
|
|
42
|
+
else el.removeAttribute('aria-describedby');
|
|
43
|
+
return html`
|
|
44
|
+
<span slot="track"><span slot="thumb"></span></span>
|
|
45
|
+
${el.label ? html`<span slot="label">${el.label}</span>` : null}
|
|
46
|
+
${el.hint ? html`<span slot="hint" id=${hintId}>${el.hint}</span>` : null}
|
|
47
|
+
`;
|
|
48
|
+
};
|
|
35
49
|
|
|
36
50
|
connected() {
|
|
37
51
|
super.connected();
|
|
@@ -105,6 +105,16 @@ switch-ui[checked] [slot="thumb"] {
|
|
|
105
105
|
|
|
106
106
|
[slot="label"] { font-size: var(--switch-font-size); }
|
|
107
107
|
|
|
108
|
+
/* ── Hint (§184, v0.5.5, FEEDBACK-08 §7) ──
|
|
109
|
+
Caption beneath the toggle row; wired to aria-describedby on host. */
|
|
110
|
+
[slot="hint"] {
|
|
111
|
+
display: block;
|
|
112
|
+
margin-top: var(--switch-hint-mt, var(--a-space-1));
|
|
113
|
+
font-size: var(--switch-hint-size, var(--a-fine-size));
|
|
114
|
+
color: var(--switch-hint-fg, var(--a-fg-muted));
|
|
115
|
+
line-height: var(--switch-hint-lh, 1.4);
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
:scope:focus-visible { outline: none; }
|
|
109
119
|
:scope:focus-visible [slot="track"] { box-shadow: var(--switch-focus-ring); }
|
|
110
120
|
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import { UIFormElement } from '../../core/form.js';
|
|
8
8
|
|
|
9
|
-
export interface SwitchChangeEventDetail {
|
|
9
|
+
export interface SwitchChangeEventDetail<V extends string = string> {
|
|
10
10
|
/** Submitted value (defaults to `"on"` when checked). */
|
|
11
|
-
value:
|
|
11
|
+
value: V;
|
|
12
12
|
/** Current checked state. */
|
|
13
13
|
checked: boolean;
|
|
14
14
|
}
|
|
15
|
-
export type SwitchChangeEvent = CustomEvent<SwitchChangeEventDetail
|
|
15
|
+
export type SwitchChangeEvent<V extends string = string> = CustomEvent<SwitchChangeEventDetail<V>>;
|
|
16
16
|
|
|
17
17
|
export class UISwitch extends UIFormElement {
|
|
18
18
|
/** Checked state — reflected, toggles on click. */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/Tab.json",
|
|
4
|
+
"title": "Tab",
|
|
5
|
+
"description": "Child of <tabs-ui>. One tab panel — the tab BUTTON is rendered by the parent from this child's text/icon. Wraps panel content as light DOM.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"component": {
|
|
17
|
+
"const": "Tab"
|
|
18
|
+
},
|
|
19
|
+
"disabled": {
|
|
20
|
+
"description": "Whether the tab is selectable.",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": false
|
|
23
|
+
},
|
|
24
|
+
"icon": {
|
|
25
|
+
"description": "Optional leading icon name (Phosphor) for the tab button.",
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"text": {
|
|
29
|
+
"description": "Tab button label (rendered by parent <tabs-ui>).",
|
|
30
|
+
"type": "string"
|
|
31
|
+
},
|
|
32
|
+
"value": {
|
|
33
|
+
"description": "Stable id for the tab. Parent uses this to coordinate active state.",
|
|
34
|
+
"type": "string"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"required": [
|
|
38
|
+
"component"
|
|
39
|
+
],
|
|
40
|
+
"unevaluatedProperties": false,
|
|
41
|
+
"x-adiaui": {
|
|
42
|
+
"anti_patterns": [],
|
|
43
|
+
"category": "navigation",
|
|
44
|
+
"composes": [],
|
|
45
|
+
"events": {},
|
|
46
|
+
"examples": [],
|
|
47
|
+
"keywords": [],
|
|
48
|
+
"name": "UITab",
|
|
49
|
+
"related": [],
|
|
50
|
+
"slots": {},
|
|
51
|
+
"states": [],
|
|
52
|
+
"synonyms": {},
|
|
53
|
+
"tag": "tab-ui",
|
|
54
|
+
"tokens": {},
|
|
55
|
+
"traits": [],
|
|
56
|
+
"version": 1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
#
|
|
3
|
+
# §176 (v0.5.5): authored to close the §175 baseline-orphan class. The
|
|
4
|
+
# component already existed as a sibling class in the parent's class.js
|
|
5
|
+
# + was registered alongside the parent (e.g. UIList + UIListItem both
|
|
6
|
+
# from list/class.js). The catalog just lacked its own entry. With the
|
|
7
|
+
# §172 sibling-yaml scanner, this file gets picked up next to the parent
|
|
8
|
+
# yaml.
|
|
9
|
+
|
|
10
|
+
# Child component of <tabs-ui>. Surface only inside that parent.
|
|
11
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
12
|
+
name: UITab
|
|
13
|
+
tag: tab-ui
|
|
14
|
+
component: Tab
|
|
15
|
+
category: navigation
|
|
16
|
+
version: 1
|
|
17
|
+
description: |-
|
|
18
|
+
Child of <tabs-ui>. One tab panel — the tab BUTTON is rendered by the parent from this child's text/icon. Wraps panel content as light DOM.
|
|
19
|
+
|
|
20
|
+
props:
|
|
21
|
+
text:
|
|
22
|
+
description: Tab button label (rendered by parent <tabs-ui>).
|
|
23
|
+
type: string
|
|
24
|
+
value:
|
|
25
|
+
description: Stable id for the tab. Parent uses this to coordinate active state.
|
|
26
|
+
type: string
|
|
27
|
+
icon:
|
|
28
|
+
description: Optional leading icon name (Phosphor) for the tab button.
|
|
29
|
+
type: string
|
|
30
|
+
disabled:
|
|
31
|
+
description: Whether the tab is selectable.
|
|
32
|
+
type: boolean
|
|
33
|
+
default: false
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/TimelineItem.json",
|
|
4
|
+
"title": "TimelineItem",
|
|
5
|
+
"description": "Child of <timeline-ui>. One step in a sequenced reasoning/process timeline. Used heavily by <agent-reasoning-ui> + <agent-trace-ui>.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"description": {
|
|
17
|
+
"description": "Subtitle below the label.",
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"component": {
|
|
21
|
+
"const": "TimelineItem"
|
|
22
|
+
},
|
|
23
|
+
"duration": {
|
|
24
|
+
"description": "Elapsed time (e.g. \"1.2s\", \"340ms\").",
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"icon": {
|
|
28
|
+
"description": "Optional leading icon name (Phosphor).",
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
"spinner": {
|
|
32
|
+
"description": "Show a spinner instead of the icon while status=pending.",
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": false
|
|
35
|
+
},
|
|
36
|
+
"status": {
|
|
37
|
+
"description": "Lifecycle state (idle | pending | done | failed).",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"default": "idle"
|
|
40
|
+
},
|
|
41
|
+
"text": {
|
|
42
|
+
"description": "Primary step label.",
|
|
43
|
+
"type": "string"
|
|
44
|
+
},
|
|
45
|
+
"time": {
|
|
46
|
+
"description": "Absolute timestamp (HH:MM or ISO).",
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"variant": {
|
|
50
|
+
"description": "Visual variant (default | accent | success | warning | danger).",
|
|
51
|
+
"type": "string",
|
|
52
|
+
"default": "default"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"required": [
|
|
56
|
+
"component"
|
|
57
|
+
],
|
|
58
|
+
"unevaluatedProperties": false,
|
|
59
|
+
"x-adiaui": {
|
|
60
|
+
"anti_patterns": [],
|
|
61
|
+
"category": "feedback",
|
|
62
|
+
"composes": [],
|
|
63
|
+
"events": {},
|
|
64
|
+
"examples": [],
|
|
65
|
+
"keywords": [],
|
|
66
|
+
"name": "UITimelineItem",
|
|
67
|
+
"related": [],
|
|
68
|
+
"slots": {},
|
|
69
|
+
"states": [],
|
|
70
|
+
"synonyms": {},
|
|
71
|
+
"tag": "timeline-item-ui",
|
|
72
|
+
"tokens": {},
|
|
73
|
+
"traits": [],
|
|
74
|
+
"version": 1
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
#
|
|
3
|
+
# §176 (v0.5.5): authored to close the §175 baseline-orphan class. The
|
|
4
|
+
# component already existed as a sibling class in the parent's class.js
|
|
5
|
+
# + was registered alongside the parent (e.g. UIList + UIListItem both
|
|
6
|
+
# from list/class.js). The catalog just lacked its own entry. With the
|
|
7
|
+
# §172 sibling-yaml scanner, this file gets picked up next to the parent
|
|
8
|
+
# yaml.
|
|
9
|
+
|
|
10
|
+
# Child component of <timeline-ui>. Surface only inside that parent.
|
|
11
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
12
|
+
name: UITimelineItem
|
|
13
|
+
tag: timeline-item-ui
|
|
14
|
+
component: TimelineItem
|
|
15
|
+
category: feedback
|
|
16
|
+
version: 1
|
|
17
|
+
description: |-
|
|
18
|
+
Child of <timeline-ui>. One step in a sequenced reasoning/process timeline. Used heavily by <agent-reasoning-ui> + <agent-trace-ui>.
|
|
19
|
+
|
|
20
|
+
props:
|
|
21
|
+
text:
|
|
22
|
+
description: Primary step label.
|
|
23
|
+
type: string
|
|
24
|
+
description:
|
|
25
|
+
description: Subtitle below the label.
|
|
26
|
+
type: string
|
|
27
|
+
time:
|
|
28
|
+
description: Absolute timestamp (HH:MM or ISO).
|
|
29
|
+
type: string
|
|
30
|
+
duration:
|
|
31
|
+
description: Elapsed time (e.g. "1.2s", "340ms").
|
|
32
|
+
type: string
|
|
33
|
+
icon:
|
|
34
|
+
description: Optional leading icon name (Phosphor).
|
|
35
|
+
type: string
|
|
36
|
+
variant:
|
|
37
|
+
description: Visual variant (default | accent | success | warning | danger).
|
|
38
|
+
type: string
|
|
39
|
+
default: 'default'
|
|
40
|
+
status:
|
|
41
|
+
description: Lifecycle state (idle | pending | done | failed).
|
|
42
|
+
type: string
|
|
43
|
+
default: 'idle'
|
|
44
|
+
spinner:
|
|
45
|
+
description: Show a spinner instead of the icon while status=pending.
|
|
46
|
+
type: boolean
|
|
47
|
+
default: false
|
|
@@ -12,6 +12,28 @@
|
|
|
12
12
|
|
|
13
13
|
import { UIElement } from '../../core/element.js';
|
|
14
14
|
|
|
15
|
+
/** Options accepted by `UIToast.show(opts)` — the imperative one-shot path. */
|
|
16
|
+
export interface UIToastShowOptions {
|
|
17
|
+
/** Toast message text. */
|
|
18
|
+
text?: string;
|
|
19
|
+
/** Semantic variant. Legacy alias `"error"` is auto-mapped to `"danger"`. */
|
|
20
|
+
variant?: 'default' | 'info' | 'success' | 'warning' | 'danger' | 'primary' | 'muted' | 'neutral' | 'error';
|
|
21
|
+
/** Auto-dismiss time in milliseconds. 0 disables auto-dismiss. Default `4000`. */
|
|
22
|
+
duration?: number;
|
|
23
|
+
/** Screen position. Default `'bottom-right'`. */
|
|
24
|
+
position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Returned by `UIToast.show()` — imperative handle for dismiss / update. */
|
|
28
|
+
export interface UIToastFeedHandle {
|
|
29
|
+
/** Stable id assigned by `<feed-ui>`. `null` if the toast couldn't be posted. */
|
|
30
|
+
id: string | null;
|
|
31
|
+
/** Dismiss the toast programmatically. */
|
|
32
|
+
dismiss(): void;
|
|
33
|
+
/** Mutate the toast's content in-place (e.g. promote from "loading" to "done"). */
|
|
34
|
+
update(patch: Partial<UIToastShowOptions>): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
15
37
|
export class UIToast extends UIElement {
|
|
16
38
|
/** Auto-dismiss time in milliseconds. 0 disables auto-dismiss. */
|
|
17
39
|
duration: number;
|
|
@@ -21,4 +43,17 @@ export class UIToast extends UIElement {
|
|
|
21
43
|
text: string;
|
|
22
44
|
/** Semantic variant — `default | info | success | warning | danger`. `primary` and `muted` are style hints; canonical "neutral but interesting" tone is `info`. */
|
|
23
45
|
variant: 'default' | 'info' | 'success' | 'warning' | 'danger' | 'primary' | 'muted' | 'neutral';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Post a one-shot toast through the shared `<feed-ui>` host. Imperative
|
|
49
|
+
* alternative to declarative `<toast-ui>`. Returns a `UIToastFeedHandle`
|
|
50
|
+
* for programmatic dismiss / update.
|
|
51
|
+
*
|
|
52
|
+
* Legacy alias `variant: 'error'` is auto-mapped to `variant: 'danger'`.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const t = UIToast.show({ text: 'Saved!', variant: 'success' });
|
|
56
|
+
* setTimeout(() => t.dismiss(), 1000);
|
|
57
|
+
*/
|
|
58
|
+
static show(opts?: UIToastShowOptions): UIToastFeedHandle;
|
|
24
59
|
}
|
package/components/tree/class.js
CHANGED
|
@@ -63,6 +63,82 @@ export class UITree extends UIElement {
|
|
|
63
63
|
}));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// ──────────────────────────────────────────────────────────────────
|
|
67
|
+
// §184 (v0.5.5, FEEDBACK-08 §2): programmatic expand/collapse API.
|
|
68
|
+
// Pre-§184 consumers had to maintain local `_expanded[]` state +
|
|
69
|
+
// manually sync `tree-item.open` on every `tree-select` event. These
|
|
70
|
+
// five methods + the `auto-expand-selected` reflection let consumers
|
|
71
|
+
// declare the open state directly. All methods accept either a
|
|
72
|
+
// tree-item-ui element OR a value string (matched against [value]
|
|
73
|
+
// first, then [text] for ergonomics).
|
|
74
|
+
// ──────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve an `item` argument to a tree-item-ui element, or `null` if
|
|
78
|
+
* no match. Accepts: HTMLElement (used as-is when it matches `tree-item-ui`)
|
|
79
|
+
* OR a string (matched against [value] first, then [text]).
|
|
80
|
+
*/
|
|
81
|
+
#resolveItem(arg) {
|
|
82
|
+
if (!arg) return null;
|
|
83
|
+
if (arg instanceof Element) {
|
|
84
|
+
return arg.matches?.('tree-item-ui') ? arg : null;
|
|
85
|
+
}
|
|
86
|
+
if (typeof arg === 'string') {
|
|
87
|
+
const escaped = arg.replace(/"/g, '\\"');
|
|
88
|
+
return (
|
|
89
|
+
this.querySelector(`tree-item-ui[value="${escaped}"]`) ||
|
|
90
|
+
this.querySelector(`tree-item-ui[text="${escaped}"]`) ||
|
|
91
|
+
null
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Expand the given item (no-op when already open or it has no children). */
|
|
98
|
+
expand(itemOrValue) {
|
|
99
|
+
const item = this.#resolveItem(itemOrValue);
|
|
100
|
+
if (item && item.hasChildren && !item.open) item.open = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Collapse the given item (no-op when already collapsed). */
|
|
104
|
+
collapse(itemOrValue) {
|
|
105
|
+
const item = this.#resolveItem(itemOrValue);
|
|
106
|
+
if (item && item.open) item.open = false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Expand every item with children. */
|
|
110
|
+
expandAll() {
|
|
111
|
+
for (const it of this.querySelectorAll('tree-item-ui')) {
|
|
112
|
+
if (it.hasChildren && !it.open) it.open = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Collapse every item. */
|
|
117
|
+
collapseAll() {
|
|
118
|
+
for (const it of this.querySelectorAll('tree-item-ui')) {
|
|
119
|
+
if (it.open) it.open = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Expand every ancestor of the given item so it becomes visible, then
|
|
125
|
+
* scroll it into view. Useful for revealing a programmatically-selected
|
|
126
|
+
* item nested inside collapsed parents.
|
|
127
|
+
*/
|
|
128
|
+
expandTo(itemOrValue) {
|
|
129
|
+
const item = this.#resolveItem(itemOrValue);
|
|
130
|
+
if (!item) return;
|
|
131
|
+
let parent = item.parentElement?.closest('tree-item-ui');
|
|
132
|
+
while (parent) {
|
|
133
|
+
if (parent.hasChildren && !parent.open) parent.open = true;
|
|
134
|
+
parent = parent.parentElement?.closest('tree-item-ui');
|
|
135
|
+
}
|
|
136
|
+
item.querySelector(':scope > [slot="row"]')?.scrollIntoView?.({
|
|
137
|
+
block: 'nearest',
|
|
138
|
+
inline: 'nearest',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
66
142
|
#onClick = (e) => {
|
|
67
143
|
const item = e.target.closest('tree-item-ui');
|
|
68
144
|
if (!item || !this.contains(item)) return;
|
|
@@ -171,6 +247,10 @@ export class UITreeItem extends UIElement {
|
|
|
171
247
|
value: { type: String, default: '', reflect: true },
|
|
172
248
|
open: { type: Boolean, default: false, reflect: true },
|
|
173
249
|
selected: { type: Boolean, default: false, reflect: true },
|
|
250
|
+
// §184 (v0.5.5, FEEDBACK-08 §1): optional trailing badge for counts
|
|
251
|
+
// / labels (e.g. "Colors (7)" → text="Colors" badge="7"). Mirrors
|
|
252
|
+
// nav-item-ui badge API; styled muted-and-small via tree.css.
|
|
253
|
+
badge: { type: String, default: '', reflect: true },
|
|
174
254
|
};
|
|
175
255
|
|
|
176
256
|
static template = () => null;
|
|
@@ -222,6 +302,13 @@ export class UITreeItem extends UIElement {
|
|
|
222
302
|
textEl.textContent = this.text;
|
|
223
303
|
row.appendChild(textEl);
|
|
224
304
|
|
|
305
|
+
// §184: trailing badge (when [badge] is set). Stamped even when
|
|
306
|
+
// empty so render() can populate without re-stamping the DOM.
|
|
307
|
+
const badgeEl = document.createElement('span');
|
|
308
|
+
badgeEl.setAttribute('slot', 'badge');
|
|
309
|
+
if (this.badge) badgeEl.textContent = this.badge;
|
|
310
|
+
row.appendChild(badgeEl);
|
|
311
|
+
|
|
225
312
|
// Actions slot placeholder
|
|
226
313
|
const actions = document.createElement('span');
|
|
227
314
|
actions.setAttribute('slot', 'actions');
|
|
@@ -247,5 +334,9 @@ export class UITreeItem extends UIElement {
|
|
|
247
334
|
// Update text
|
|
248
335
|
const textEl = row.querySelector('[slot="text"]');
|
|
249
336
|
if (textEl && this.text) textEl.textContent = this.text;
|
|
337
|
+
|
|
338
|
+
// §184: keep badge slot synced with the [badge] attribute.
|
|
339
|
+
const badgeEl = row.querySelector('[slot="badge"]');
|
|
340
|
+
if (badgeEl) badgeEl.textContent = this.badge || '';
|
|
250
341
|
}
|
|
251
342
|
}
|