@adia-ai/web-components 0.8.30 → 0.8.32
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/CHANGELOG.md +59 -0
- package/components/accordion/accordion.d.ts +2 -2
- package/components/action-list/action-list.class.js +6 -2
- package/components/action-list/action-list.d.ts +1 -1
- package/components/agent-artifact/agent-artifact.d.ts +1 -1
- package/components/agent-feedback-bar/agent-feedback-bar.d.ts +2 -2
- package/components/agent-questions/agent-questions.d.ts +1 -1
- package/components/agent-reasoning/agent-reasoning.d.ts +3 -3
- package/components/agent-suggestions/agent-suggestions.d.ts +1 -1
- package/components/agent-trace/agent-trace.d.ts +1 -1
- package/components/alert/alert.d.ts +2 -2
- package/components/anchor-bar/anchor-bar.d.ts +2 -2
- package/components/avatar/avatar.class.js +0 -1
- package/components/avatar/avatar.css +16 -2
- package/components/badge/badge.css +30 -2
- package/components/breadcrumb/breadcrumb.css +15 -3
- package/components/button/button.d.ts +1 -1
- package/components/calendar-grid/calendar-grid.d.ts +2 -2
- package/components/card/card.css +21 -0
- package/components/card/card.yaml +9 -0
- package/components/chart/chart.d.ts +4 -4
- package/components/chart-legend/chart-legend.d.ts +1 -1
- package/components/chat-thread/chat-input.css +1 -1
- package/components/chat-thread/chat-input.js +9 -1
- package/components/chat-thread/chat-thread.class.js +6 -0
- package/components/chat-thread/chat-thread.d.ts +2 -2
- package/components/check/check.css +3 -3
- package/components/command/command.d.ts +2 -2
- package/components/context-menu/context-menu.d.ts +3 -3
- package/components/datetime-picker/datetime-picker.d.ts +5 -5
- package/components/demo-toggle/demo-toggle.d.ts +1 -1
- package/components/drawer/drawer.d.ts +2 -2
- package/components/heatmap/heatmap.d.ts +5 -5
- package/components/index.js +1 -0
- package/components/inline-edit/inline-edit.d.ts +4 -4
- package/components/input/input.a2ui.json +4 -1
- package/components/input/input.css +18 -0
- package/components/input/input.yaml +11 -1
- package/components/integration-card/integration-card.d.ts +4 -4
- package/components/link/link.d.ts +1 -1
- package/components/list/list-item.a2ui.json +6 -0
- package/components/list/list-item.yaml +13 -0
- package/components/list/list.class.js +28 -7
- package/components/list/list.d.ts +1 -1
- package/components/list-window/list-window.d.ts +5 -5
- package/components/menu/menu.css +2 -2
- package/components/menu/menu.d.ts +1 -1
- package/components/modal/modal.d.ts +1 -1
- package/components/nav/nav.css +8 -0
- package/components/nav/nav.d.ts +1 -1
- package/components/nav-group/nav-group.css +58 -9
- package/components/nav-group/nav-group.d.ts +1 -1
- package/components/nav-item/nav-item.css +7 -4
- package/components/nav-item/nav-item.d.ts +1 -1
- package/components/noodles/noodles.d.ts +3 -3
- package/components/option-card/option-card.css +1 -1
- package/components/pagination/pagination.d.ts +1 -1
- package/components/pane/pane.d.ts +1 -1
- package/components/password-strength/password-strength.d.ts +1 -1
- package/components/pipeline-status/pipeline-status.a2ui.json +14 -8
- package/components/pipeline-status/pipeline-status.d.ts +5 -4
- package/components/pipeline-status/pipeline-status.yaml +17 -11
- package/components/radio/radio.a2ui.json +4 -3
- package/components/radio/radio.css +2 -2
- package/components/radio/radio.yaml +7 -14
- package/components/radio-group/radio-group.a2ui.json +118 -0
- package/components/radio-group/radio-group.class.js +75 -0
- package/components/radio-group/radio-group.css +31 -0
- package/components/radio-group/radio-group.d.ts +20 -0
- package/components/radio-group/radio-group.examples.md +37 -0
- package/components/radio-group/radio-group.js +17 -0
- package/components/radio-group/radio-group.yaml +114 -0
- package/components/segmented/segmented.a2ui.json +5 -0
- package/components/segmented/segmented.class.js +20 -2
- package/components/segmented/segmented.yaml +5 -0
- package/components/stat/stat.css +12 -8
- package/components/stat/stat.js +66 -1
- package/components/stat/stat.yaml +3 -0
- package/components/stepper/stepper-item.a2ui.json +5 -0
- package/components/stepper/stepper-item.yaml +11 -0
- package/components/stepper/stepper.a2ui.json +38 -3
- package/components/stepper/stepper.class.js +199 -2
- package/components/stepper/stepper.css +30 -0
- package/components/stepper/stepper.d.ts +28 -1
- package/components/stepper/stepper.yaml +76 -2
- package/components/stream/stream.d.ts +3 -3
- package/components/swatch/swatch.class.js +1 -1
- package/components/swatch/swatch.d.ts +1 -1
- package/components/swiper/swiper.d.ts +3 -3
- package/components/table/table.a2ui.json +2 -2
- package/components/table/table.d.ts +7 -7
- package/components/table/table.yaml +9 -1
- package/components/table-toolbar/table-toolbar.d.ts +4 -4
- package/components/tabs/tabs.css +11 -3
- package/components/tabs/tabs.d.ts +1 -1
- package/components/tag/tag.a2ui.json +1 -1
- package/components/tag/tag.css +78 -13
- package/components/tag/tag.d.ts +12 -8
- package/components/tag/tag.examples.md +4 -4
- package/components/tag/tag.yaml +18 -7
- package/components/tags-input/tags-input.d.ts +4 -4
- package/components/timeline/timeline.d.ts +1 -1
- package/components/toc/toc.d.ts +1 -1
- package/components/toggle-group/toggle-group.d.ts +1 -1
- package/components/toggle-scheme/toggle-scheme.d.ts +1 -1
- package/components/tour/tour.d.ts +4 -4
- package/components/tree/tree.d.ts +1 -1
- package/core/icons-phosphor.js +129 -71
- package/core/provider.js +84 -23
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +1 -1
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +89 -87
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
- package/styles/api/sizing-fallback.css +32 -0
- package/styles/api/sizing.css +7 -3
- package/styles/components.css +1 -0
- package/styles/tokens.css +1 -0
|
@@ -47,10 +47,10 @@ export class UINavItem extends UIElement {
|
|
|
47
47
|
/** Visual treatment. Default ('') renders as a primary-rail item (reserved icon space, in-icon selected accent). 'section' renders flat — no icon space when absent, left-edge accent bar for selected. When the parent <nav-ui> carries variant="section", this item inherits it via CSS cascade unless an explicit variant is set. */
|
|
48
48
|
variant: '' | 'section';
|
|
49
49
|
|
|
50
|
+
addEventListener(type: 'nav-select', listener: (ev: NavItemNavSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
50
51
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
51
52
|
type: K,
|
|
52
53
|
listener: (this: UINavItem, ev: HTMLElementEventMap[K]) => unknown,
|
|
53
54
|
options?: boolean | AddEventListenerOptions,
|
|
54
55
|
): void;
|
|
55
|
-
addEventListener(type: 'nav-select', listener: (ev: NavItemNavSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
56
56
|
}
|
|
@@ -62,12 +62,12 @@ export class UINoodles extends UIElement {
|
|
|
62
62
|
/** Bezier control point distance (0-1). Higher = wider curves */
|
|
63
63
|
tension: number;
|
|
64
64
|
|
|
65
|
+
addEventListener(type: 'noodle-connect', listener: (ev: NoodlesNoodleConnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
66
|
+
addEventListener(type: 'noodle-disconnect', listener: (ev: NoodlesNoodleDisconnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
67
|
+
addEventListener(type: 'noodle-drag', listener: (ev: NoodlesNoodleDragEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
65
68
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
66
69
|
type: K,
|
|
67
70
|
listener: (this: UINoodles, ev: HTMLElementEventMap[K]) => unknown,
|
|
68
71
|
options?: boolean | AddEventListenerOptions,
|
|
69
72
|
): void;
|
|
70
|
-
addEventListener(type: 'noodle-connect', listener: (ev: NoodlesNoodleConnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
71
|
-
addEventListener(type: 'noodle-disconnect', listener: (ev: NoodlesNoodleDisconnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
72
|
-
addEventListener(type: 'noodle-drag', listener: (ev: NoodlesNoodleDragEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
73
73
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Safari 17.x bug: `:scope:not(...):hover` (Flavor A) and `:scope[checked]`
|
|
2
2
|
(Flavor B — attribute-removal restyle) both fail inside `@scope`.
|
|
3
3
|
Selectors moved out. See docs/BROWSER-COMPAT.md §3a. */
|
|
4
|
-
option-card-ui:not([checked]):not([disabled]):hover {
|
|
4
|
+
option-card-ui:not([checked]):not([disabled]):hover:not(:focus-visible) {
|
|
5
5
|
background: var(--option-card-bg-hover);
|
|
6
6
|
border-color: var(--option-card-border-hover);
|
|
7
7
|
}
|
|
@@ -35,10 +35,10 @@ default; pass `size="sm"` for a denser numbered row.
|
|
|
35
35
|
/** Visual variant — `default` (ghost buttons w/ hover bg) or `button` (1×1 bordered cells; active page filled). */
|
|
36
36
|
variant: 'default' | 'button';
|
|
37
37
|
|
|
38
|
+
addEventListener(type: 'page-change', listener: (ev: PaginationPageChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
38
39
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
39
40
|
type: K,
|
|
40
41
|
listener: (this: UIPagination, ev: HTMLElementEventMap[K]) => unknown,
|
|
41
42
|
options?: boolean | AddEventListenerOptions,
|
|
42
43
|
): void;
|
|
43
|
-
addEventListener(type: 'page-change', listener: (ev: PaginationPageChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
44
44
|
}
|
|
@@ -38,10 +38,10 @@ grabber when resizable).
|
|
|
38
38
|
*/
|
|
39
39
|
side: '' | 'leading' | 'trailing';
|
|
40
40
|
|
|
41
|
+
addEventListener(type: 'toggle', listener: (ev: PaneToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
41
42
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
42
43
|
type: K,
|
|
43
44
|
listener: (this: UIPane, ev: HTMLElementEventMap[K]) => unknown,
|
|
44
45
|
options?: boolean | AddEventListenerOptions,
|
|
45
46
|
): void;
|
|
46
|
-
addEventListener(type: 'toggle', listener: (ev: PaneToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
47
47
|
}
|
|
@@ -50,10 +50,10 @@ meter.value = e.target.value)`.
|
|
|
50
50
|
*/
|
|
51
51
|
value: string;
|
|
52
52
|
|
|
53
|
+
addEventListener(type: 'score-change', listener: (ev: PasswordStrengthScoreChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
53
54
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
54
55
|
type: K,
|
|
55
56
|
listener: (this: UIPasswordStrength, ev: HTMLElementEventMap[K]) => unknown,
|
|
56
57
|
options?: boolean | AddEventListenerOptions,
|
|
57
58
|
): void;
|
|
58
|
-
addEventListener(type: 'score-change', listener: (ev: PasswordStrengthScoreChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
59
59
|
}
|
|
@@ -13,23 +13,29 @@
|
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"properties": {
|
|
16
|
-
"complete": {
|
|
17
|
-
"description": "Component property: complete.",
|
|
18
|
-
"type": "boolean",
|
|
19
|
-
"default": false
|
|
20
|
-
},
|
|
21
16
|
"component": {
|
|
22
17
|
"const": "PipelineStatus"
|
|
23
18
|
},
|
|
24
19
|
"message": {
|
|
25
|
-
"description": "
|
|
20
|
+
"description": "Current stage description shown alongside the stage label.",
|
|
26
21
|
"type": "string",
|
|
27
22
|
"default": ""
|
|
28
23
|
},
|
|
29
24
|
"stage": {
|
|
30
|
-
"description": "
|
|
25
|
+
"description": "Current stage name (interpret, analyze, plan, generate, validate, render).",
|
|
31
26
|
"type": "string",
|
|
32
27
|
"default": ""
|
|
28
|
+
},
|
|
29
|
+
"status": {
|
|
30
|
+
"description": "Canonical stage-progress state. Set to `completed` to render the\nfinal state and collapse the history log.",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": [
|
|
33
|
+
"idle",
|
|
34
|
+
"active",
|
|
35
|
+
"completed",
|
|
36
|
+
"error"
|
|
37
|
+
],
|
|
38
|
+
"default": "idle"
|
|
33
39
|
}
|
|
34
40
|
},
|
|
35
41
|
"required": [
|
|
@@ -59,7 +65,7 @@
|
|
|
59
65
|
},
|
|
60
66
|
{
|
|
61
67
|
"description": "Pipeline status marked complete; indicator switches to success state.",
|
|
62
|
-
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\": \"All 100 intents passed\",\n \"
|
|
68
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\": \"All 100 intents passed\",\n \"status\": \"completed\"\n }\n]",
|
|
63
69
|
"name": "complete"
|
|
64
70
|
}
|
|
65
71
|
],
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
import { UIElement } from '../../core/element.js';
|
|
14
14
|
|
|
15
15
|
export class UIPipelineStatus extends UIElement {
|
|
16
|
-
/**
|
|
17
|
-
complete: boolean;
|
|
18
|
-
/** Component property: message. */
|
|
16
|
+
/** Current stage description shown alongside the stage label. */
|
|
19
17
|
message: string;
|
|
20
|
-
/**
|
|
18
|
+
/** Current stage name (interpret, analyze, plan, generate, validate, render). */
|
|
21
19
|
stage: string;
|
|
20
|
+
/** Canonical stage-progress state. Set to `completed` to render the
|
|
21
|
+
final state and collapse the history log. */
|
|
22
|
+
status: 'idle' | 'active' | 'completed' | 'error';
|
|
22
23
|
}
|
|
@@ -11,19 +11,25 @@ description: >-
|
|
|
11
11
|
updates via the status property. Use for long-running agent or build
|
|
12
12
|
pipelines; for multi-step wizards use <stepper-ui> instead.
|
|
13
13
|
props:
|
|
14
|
-
|
|
15
|
-
description: '
|
|
16
|
-
type: boolean
|
|
17
|
-
default: false
|
|
18
|
-
reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
|
|
19
|
-
message:
|
|
20
|
-
description: 'Component property: message.'
|
|
14
|
+
stage:
|
|
15
|
+
description: 'Current stage name (interpret, analyze, plan, generate, validate, render).'
|
|
21
16
|
type: string
|
|
22
17
|
default: ''
|
|
23
|
-
|
|
24
|
-
description: '
|
|
18
|
+
message:
|
|
19
|
+
description: 'Current stage description shown alongside the stage label.'
|
|
25
20
|
type: string
|
|
26
21
|
default: ''
|
|
22
|
+
status:
|
|
23
|
+
description: |-
|
|
24
|
+
Canonical stage-progress state. Set to `completed` to render the
|
|
25
|
+
final state and collapse the history log.
|
|
26
|
+
type: string
|
|
27
|
+
default: idle
|
|
28
|
+
enum:
|
|
29
|
+
- idle
|
|
30
|
+
- active
|
|
31
|
+
- completed
|
|
32
|
+
- error
|
|
27
33
|
events: {}
|
|
28
34
|
slots:
|
|
29
35
|
default:
|
|
@@ -37,7 +43,7 @@ a2ui:
|
|
|
37
43
|
rules:
|
|
38
44
|
- rule: 'Single updating pipeline status indicator — status pill with optional progress bar.'
|
|
39
45
|
reason: 'Long-running task indicator.'
|
|
40
|
-
- rule: 'stage +
|
|
46
|
+
- rule: 'stage + status + message attributes drive the current display state.'
|
|
41
47
|
reason: 'State contract.'
|
|
42
48
|
- rule: 'For multi-step wizards use <stepper-ui>; for chronological history use <timeline-ui>; pipeline-status is a single current-state pill.'
|
|
43
49
|
reason: 'Sibling boundary.'
|
|
@@ -61,7 +67,7 @@ examples:
|
|
|
61
67
|
- name: complete
|
|
62
68
|
description: Pipeline status marked complete; indicator switches to success state.
|
|
63
69
|
a2ui: "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\"\
|
|
64
|
-
: \"All 100 intents passed\",\n \"
|
|
70
|
+
: \"All 100 intents passed\",\n \"status\": \"completed\"\n }\n]"
|
|
65
71
|
keywords:
|
|
66
72
|
- pipelinestatus
|
|
67
73
|
- pipeline-status
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
},
|
|
85
85
|
"examples": [
|
|
86
86
|
{
|
|
87
|
-
"description": "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers.",
|
|
88
|
-
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"hdr\",\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"hdr\",\n \"component\": \"Header\",\n \"children\": [\n \"title\"
|
|
87
|
+
"description": "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers. The options are wrapped in radio-group-ui (gh#729) — its own [label] IS the group's accessible name (role=radiogroup + aria-labelledby), not a sibling Text node.",
|
|
88
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"hdr\",\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"hdr\",\n \"component\": \"Header\",\n \"children\": [\n \"title\"\n ]\n },\n {\n \"id\": \"title\",\n \"component\": \"Text\",\n \"slot\": \"heading\",\n \"textContent\": \"Choose a Plan\",\n \"variant\": \"section\"\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"options\"\n ]\n },\n {\n \"id\": \"options\",\n \"component\": \"RadioGroup\",\n \"label\": \"Select the plan that works best for you\",\n \"children\": [\n \"basic\",\n \"pro\",\n \"enterprise\"\n ]\n },\n {\n \"id\": \"basic\",\n \"component\": \"Radio\",\n \"label\": \"Basic — $9/mo\",\n \"value\": \"basic\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"pro\",\n \"component\": \"Radio\",\n \"label\": \"Pro — $29/mo\",\n \"value\": \"pro\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"enterprise\",\n \"component\": \"Radio\",\n \"label\": \"Enterprise — $99/mo\",\n \"value\": \"enterprise\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"ftr\",\n \"component\": \"Footer\",\n \"children\": [\n \"confirm-btn\"\n ]\n },\n {\n \"id\": \"confirm-btn\",\n \"component\": \"Button\",\n \"text\": \"Confirm Plan\",\n \"variant\": \"primary\",\n \"block\": true,\n \"slot\": \"action\"\n }\n]",
|
|
89
89
|
"name": "radio-group"
|
|
90
90
|
}
|
|
91
91
|
],
|
|
@@ -102,7 +102,8 @@
|
|
|
102
102
|
],
|
|
103
103
|
"name": "UIRadio",
|
|
104
104
|
"related": [
|
|
105
|
-
"button"
|
|
105
|
+
"button",
|
|
106
|
+
"radio-group"
|
|
106
107
|
],
|
|
107
108
|
"slots": {
|
|
108
109
|
"dot": {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
`:scope[checked] [descendant]` (Flavor B — attribute-removal restyle)
|
|
3
3
|
both fail inside `@scope`. Selectors moved out. See
|
|
4
4
|
docs/BROWSER-COMPAT.md §3a. */
|
|
5
|
-
radio-ui:not([disabled]):hover [slot="dot"] {
|
|
5
|
+
radio-ui:not([disabled]):hover:not(:focus-visible) [slot="dot"] {
|
|
6
6
|
border-color: var(--radio-border-hover);
|
|
7
7
|
background: var(--radio-bg-hover);
|
|
8
8
|
}
|
|
9
|
-
radio-ui[checked]:not([disabled]):hover [slot="dot"] {
|
|
9
|
+
radio-ui[checked]:not([disabled]):hover:not(:focus-visible) [slot="dot"] {
|
|
10
10
|
background: var(--radio-bg-checked-hover);
|
|
11
11
|
border-color: var(--radio-bg-checked-hover);
|
|
12
12
|
}
|
|
@@ -111,7 +111,7 @@ tokens:
|
|
|
111
111
|
description: Override transition timing
|
|
112
112
|
a2ui:
|
|
113
113
|
rules:
|
|
114
|
-
- "Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For radio groups, the canonical pattern is
|
|
114
|
+
- "Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For radio groups, the canonical pattern is <radio-group-ui label='…'> wrapping bare <radio-ui label='…'> elements sharing a [name=] — radio-group-ui supplies the group's accessible name (role=radiogroup + aria-labelledby); a bare Column of radios cannot (gh#729)."
|
|
115
115
|
- rule: 'Use the [name] attribute to group radios — exactly one is selected per name group; form-associated.'
|
|
116
116
|
reason: 'Standard radio-group semantics; name= is the group key.'
|
|
117
117
|
- rule: 'For button-style single-select clusters (visually richer) use <segmented-ui> + <segment-ui> instead.'
|
|
@@ -127,7 +127,7 @@ anti_patterns:
|
|
|
127
127
|
rule: Use [label] on radio-ui directly; do not wrap in field-ui.
|
|
128
128
|
examples:
|
|
129
129
|
- name: radio-group
|
|
130
|
-
description: "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers."
|
|
130
|
+
description: "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers. The options are wrapped in radio-group-ui (gh#729) — its own [label] IS the group's accessible name (role=radiogroup + aria-labelledby), not a sibling Text node."
|
|
131
131
|
a2ui: >-
|
|
132
132
|
[
|
|
133
133
|
{
|
|
@@ -143,8 +143,7 @@ examples:
|
|
|
143
143
|
"id": "hdr",
|
|
144
144
|
"component": "Header",
|
|
145
145
|
"children": [
|
|
146
|
-
"title"
|
|
147
|
-
"desc"
|
|
146
|
+
"title"
|
|
148
147
|
]
|
|
149
148
|
},
|
|
150
149
|
{
|
|
@@ -154,13 +153,6 @@ examples:
|
|
|
154
153
|
"textContent": "Choose a Plan",
|
|
155
154
|
"variant": "section"
|
|
156
155
|
},
|
|
157
|
-
{
|
|
158
|
-
"id": "desc",
|
|
159
|
-
"component": "Text",
|
|
160
|
-
"slot": "description",
|
|
161
|
-
"variant": "caption",
|
|
162
|
-
"textContent": "Select the plan that works best for you"
|
|
163
|
-
},
|
|
164
156
|
{
|
|
165
157
|
"id": "sec",
|
|
166
158
|
"component": "Section",
|
|
@@ -170,13 +162,13 @@ examples:
|
|
|
170
162
|
},
|
|
171
163
|
{
|
|
172
164
|
"id": "options",
|
|
173
|
-
"component": "
|
|
165
|
+
"component": "RadioGroup",
|
|
166
|
+
"label": "Select the plan that works best for you",
|
|
174
167
|
"children": [
|
|
175
168
|
"basic",
|
|
176
169
|
"pro",
|
|
177
170
|
"enterprise"
|
|
178
|
-
]
|
|
179
|
-
"gap": "3"
|
|
171
|
+
]
|
|
180
172
|
},
|
|
181
173
|
{
|
|
182
174
|
"id": "basic",
|
|
@@ -262,3 +254,4 @@ synonyms:
|
|
|
262
254
|
- settings
|
|
263
255
|
related:
|
|
264
256
|
- button
|
|
257
|
+
- radio-group
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/RadioGroup.json",
|
|
4
|
+
"title": "RadioGroup",
|
|
5
|
+
"description": "Groups a set of bare <radio-ui label=\"…\"> children under one accessible name — role=\"radiogroup\" on the host plus aria-labelledby pointing at its own [label]. Radio-ui already supports ancestor fieldset/[role=radiogroup] grouping for exclusivity + arrow-key navigation (unchanged by this wrapper); a plain layout container (Column) can carry neither the role nor the label association, which left the canonical questionnaire shape (radio.yaml's own example) with no programmatic question↔group link (gh#729). Use this wherever a question/prompt introduces a set of mutually-exclusive radio-ui options — it is the ONLY primitive that carries the group's accessible name; radio-ui itself stays self-labeling per-option.",
|
|
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
|
+
"required": {
|
|
17
|
+
"description": "Marks the group as requiring a selection for form validation (visual + aria-required only — the constraint itself lives on each radio-ui's own [required]).",
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"default": false
|
|
20
|
+
},
|
|
21
|
+
"component": {
|
|
22
|
+
"const": "RadioGroup"
|
|
23
|
+
},
|
|
24
|
+
"label": {
|
|
25
|
+
"description": "The question or prompt text — rendered above the options and wired to the host via aria-labelledby. This is the group's accessible name; without it the group has none.",
|
|
26
|
+
"type": "string",
|
|
27
|
+
"default": ""
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"required": [
|
|
31
|
+
"component",
|
|
32
|
+
"label"
|
|
33
|
+
],
|
|
34
|
+
"unevaluatedProperties": false,
|
|
35
|
+
"x-adiaui": {
|
|
36
|
+
"anti_patterns": [
|
|
37
|
+
{
|
|
38
|
+
"description": "Wrapping radio-ui options in a bare Column/Section with the question as a sibling Text node instead of radio-group-ui's [label].",
|
|
39
|
+
"right": "<radio-group-ui label=\"How often do you exercise?\">\n <radio-ui label=\"Never\" name=\"exercise\"></radio-ui>\n <radio-ui label=\"Sometimes\" name=\"exercise\"></radio-ui>\n</radio-group-ui>\n",
|
|
40
|
+
"rule": "The question that introduces a radio-ui set belongs in radio-group-ui's [label], not a sibling Text node.",
|
|
41
|
+
"wrong": "<column>\n <text>How often do you exercise?</text>\n <radio-ui label=\"Never\" name=\"exercise\"></radio-ui>\n <radio-ui label=\"Sometimes\" name=\"exercise\"></radio-ui>\n</column>\n"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"category": "input",
|
|
45
|
+
"composes": [],
|
|
46
|
+
"events": {},
|
|
47
|
+
"examples": [
|
|
48
|
+
{
|
|
49
|
+
"description": "A single scored-questionnaire item: the question as the group's accessible label, three bare radio-ui options sharing one [name=].",
|
|
50
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"RadioGroup\",\n \"label\": \"Little interest or pleasure in doing things?\",\n \"children\": [\"opt-0\", \"opt-1\", \"opt-2\"]\n },\n { \"id\": \"opt-0\", \"component\": \"Radio\", \"label\": \"Not at all\", \"name\": \"phq2-1\", \"value\": \"0\" },\n { \"id\": \"opt-1\", \"component\": \"Radio\", \"label\": \"Several days\", \"name\": \"phq2-1\", \"value\": \"1\" },\n { \"id\": \"opt-2\", \"component\": \"Radio\", \"label\": \"More than half the days\", \"name\": \"phq2-1\", \"value\": \"2\" }\n]",
|
|
51
|
+
"name": "questionnaire-item"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"keywords": [
|
|
55
|
+
"radio",
|
|
56
|
+
"radiogroup",
|
|
57
|
+
"fieldset",
|
|
58
|
+
"legend",
|
|
59
|
+
"questionnaire",
|
|
60
|
+
"survey",
|
|
61
|
+
"poll",
|
|
62
|
+
"accessibility",
|
|
63
|
+
"aria-labelledby"
|
|
64
|
+
],
|
|
65
|
+
"name": "UIRadioGroup",
|
|
66
|
+
"related": [
|
|
67
|
+
"radio",
|
|
68
|
+
"field"
|
|
69
|
+
],
|
|
70
|
+
"slots": {
|
|
71
|
+
"default": {
|
|
72
|
+
"description": "radio-ui children sharing one [name=] — exclusivity and arrow-key navigation are unchanged; they still resolve via closest('fieldset, [role=radiogroup]'), which now matches this host."
|
|
73
|
+
},
|
|
74
|
+
"label": {
|
|
75
|
+
"description": "Auto-minted label element (mounted by radio-group-ui from the [label] prop) — do not author this slot directly."
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"states": [
|
|
79
|
+
{
|
|
80
|
+
"description": "Default, ready for interaction.",
|
|
81
|
+
"name": "idle"
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"status": "stable",
|
|
85
|
+
"synonyms": {
|
|
86
|
+
"poll": [
|
|
87
|
+
"radio-group",
|
|
88
|
+
"questionnaire"
|
|
89
|
+
],
|
|
90
|
+
"questionnaire": [
|
|
91
|
+
"radio-group",
|
|
92
|
+
"form",
|
|
93
|
+
"survey"
|
|
94
|
+
],
|
|
95
|
+
"survey": [
|
|
96
|
+
"radio-group",
|
|
97
|
+
"questionnaire"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"tag": "radio-group-ui",
|
|
101
|
+
"tokens": {
|
|
102
|
+
"--radio-group-gap": {
|
|
103
|
+
"description": "Gap between the label and the option stack, and between each option."
|
|
104
|
+
},
|
|
105
|
+
"--radio-group-label-color": {
|
|
106
|
+
"description": "Override the question/prompt label color."
|
|
107
|
+
},
|
|
108
|
+
"--radio-group-label-font-size": {
|
|
109
|
+
"description": "Override the question/prompt label font size."
|
|
110
|
+
},
|
|
111
|
+
"--radio-group-label-font-weight": {
|
|
112
|
+
"description": "Override the question/prompt label font weight."
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"traits": [],
|
|
116
|
+
"version": 1
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<radio-group-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class(es) without auto-registering the tag.
|
|
5
|
+
* Useful for test isolation, subclassing with tag-name override, or selective
|
|
6
|
+
* composition.
|
|
7
|
+
*
|
|
8
|
+
* The auto-register path stays at `@adia-ai/web-components/components/radio-group`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* <radio-group-ui label="Choose a plan">
|
|
16
|
+
* <radio-ui label="Basic" name="plan" value="basic"></radio-ui>
|
|
17
|
+
* <radio-ui label="Pro" name="plan" value="pro"></radio-ui>
|
|
18
|
+
* </radio-group-ui>
|
|
19
|
+
*
|
|
20
|
+
* Programmatic question↔group association for a set of bare radio-ui
|
|
21
|
+
* options (gh#729). role="radiogroup" on the host + aria-labelledby
|
|
22
|
+
* pointing at an auto-minted label element rendered from [label] — the
|
|
23
|
+
* ONLY job this component does. Exclusivity + arrow-key navigation are
|
|
24
|
+
* unchanged: radio-ui's own #select/#onKey already resolve their group
|
|
25
|
+
* via `closest('fieldset, [role="radiogroup"]')` (radio.class.js:48),
|
|
26
|
+
* which now matches this host instead of falling through to
|
|
27
|
+
* `this.parentElement`.
|
|
28
|
+
*
|
|
29
|
+
* Not a form-associated element itself — it carries no value; each child
|
|
30
|
+
* radio-ui is its own form participant per the existing [name=] contract.
|
|
31
|
+
*/
|
|
32
|
+
import { UIElement } from '../../core/element.js';
|
|
33
|
+
|
|
34
|
+
export class UIRadioGroup extends UIElement {
|
|
35
|
+
static properties = {
|
|
36
|
+
label: { type: String, default: '', reflect: true },
|
|
37
|
+
required: { type: Boolean, default: false, reflect: true },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
static template = () => null;
|
|
41
|
+
|
|
42
|
+
#labelEl = null;
|
|
43
|
+
|
|
44
|
+
connected() {
|
|
45
|
+
this.setAttribute('role', 'radiogroup');
|
|
46
|
+
this.#ensureLabelElement();
|
|
47
|
+
if (this.#labelEl?.id) this.setAttribute('aria-labelledby', this.#labelEl.id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
disconnected() {
|
|
51
|
+
this.#labelEl = null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
render() {
|
|
55
|
+
if (this.#labelEl) this.#labelEl.textContent = this.label || '';
|
|
56
|
+
if (this.required) this.setAttribute('aria-required', 'true');
|
|
57
|
+
else this.removeAttribute('aria-required');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#ensureLabelElement() {
|
|
61
|
+
this.#labelEl = this.querySelector(':scope > [data-radio-group-label]');
|
|
62
|
+
if (!this.#labelEl) {
|
|
63
|
+
const el = document.createElement('div');
|
|
64
|
+
el.setAttribute('slot', 'label');
|
|
65
|
+
el.setAttribute('data-radio-group-label', '');
|
|
66
|
+
el.id = UIRadioGroup.#nextId();
|
|
67
|
+
el.textContent = this.label || '';
|
|
68
|
+
this.prepend(el);
|
|
69
|
+
this.#labelEl = el;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static #idSeq = 0;
|
|
74
|
+
static #nextId() { return `radio-group-label-${++UIRadioGroup.#idSeq}`; }
|
|
75
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@scope (radio-group-ui) {
|
|
2
|
+
:where(:scope) {
|
|
3
|
+
/* ── Tokens ── */
|
|
4
|
+
--radio-group-gap: var(--a-space-2);
|
|
5
|
+
--radio-group-label-color: var(--md-sys-color-neutral-on-surface);
|
|
6
|
+
--radio-group-label-font-size: var(--a-ui-md);
|
|
7
|
+
--radio-group-label-font-weight: var(--a-weight-medium);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* ── Base — vertical stack: label row, then each radio-ui option. ── */
|
|
11
|
+
:scope {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: var(--radio-group-gap);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:scope [slot="label"] {
|
|
19
|
+
color: var(--radio-group-label-color);
|
|
20
|
+
font-size: var(--radio-group-label-font-size);
|
|
21
|
+
font-weight: var(--radio-group-label-font-weight);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* No accessible name set → hide the (empty) label row rather than
|
|
25
|
+
reserve blank space. label is required in practice (radio-group-ui's
|
|
26
|
+
entire purpose is carrying it); this only guards the transient/
|
|
27
|
+
misuse case. */
|
|
28
|
+
:scope:not([label]) [slot="label"] {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<radio-group-ui>` — Groups a set of bare <radio-ui label="…"> children under one accessible name — role="radiogroup" on the host plus aria-labelledby pointing at its own [label]. Radio-ui already supports ancestor fieldset/[role=radiogroup] grouping for exclusivity + arrow-key navigation (unchanged by this wrapper); a plain layout container (Column) can carry neither the role nor the label association, which left the canonical questionnaire shape (radio.yaml's own example) with no programmatic question↔group link (gh#729). Use this wherever a question/prompt introduces a set of mutually-exclusive radio-ui options — it is the ONLY primitive that carries the group's accessible name; radio-ui itself stays self-labeling per-option.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/radio-group
|
|
5
|
+
*
|
|
6
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
7
|
+
* the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
|
|
8
|
+
* run `npm run build:components`, then `npm run codegen:dts` to
|
|
9
|
+
* regenerate; or hand-author this file fully if rich event types are
|
|
10
|
+
* needed beyond what the yaml `events:` block can express.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { UIElement } from '../../core/element.js';
|
|
14
|
+
|
|
15
|
+
export class UIRadioGroup extends UIElement {
|
|
16
|
+
/** Marks the group as requiring a selection for form validation (visual + aria-required only — the constraint itself lives on each radio-ui's own [required]). */
|
|
17
|
+
required: boolean;
|
|
18
|
+
/** The question or prompt text — rendered above the options and wired to the host via aria-labelledby. This is the group's accessible name; without it the group has none. */
|
|
19
|
+
label: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# radio-group — Examples
|
|
2
|
+
|
|
3
|
+
## label
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<radio-group-ui label="Little interest or pleasure in doing things?">
|
|
7
|
+
<radio-ui label="Not at all" name="rg-demo-phq" value="0" checked></radio-ui>
|
|
8
|
+
<radio-ui label="Several days" name="rg-demo-phq" value="1"></radio-ui>
|
|
9
|
+
<radio-ui label="More than half the days" name="rg-demo-phq" value="2"></radio-ui>
|
|
10
|
+
</radio-group-ui>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## required
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<radio-group-ui label="How would you rate your overall health?" required>
|
|
17
|
+
<radio-ui label="Excellent" name="rg-demo-health" value="excellent" required></radio-ui>
|
|
18
|
+
<radio-ui label="Good" name="rg-demo-health" value="good" required></radio-ui>
|
|
19
|
+
<radio-ui label="Fair" name="rg-demo-health" value="fair" required></radio-ui>
|
|
20
|
+
<radio-ui label="Poor" name="rg-demo-health" value="poor" required></radio-ui>
|
|
21
|
+
</radio-group-ui>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Combinations
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<col-ui gap="5">
|
|
28
|
+
<radio-group-ui label="Little interest or pleasure in doing things?">
|
|
29
|
+
<radio-ui label="Not at all" name="rg-demo-multi-1" value="0" checked></radio-ui>
|
|
30
|
+
<radio-ui label="Several days" name="rg-demo-multi-1" value="1"></radio-ui>
|
|
31
|
+
</radio-group-ui>
|
|
32
|
+
<radio-group-ui label="Feeling down, depressed, or hopeless?">
|
|
33
|
+
<radio-ui label="Not at all" name="rg-demo-multi-2" value="0"></radio-ui>
|
|
34
|
+
<radio-ui label="Several days" name="rg-demo-multi-2" value="1" checked></radio-ui>
|
|
35
|
+
</radio-group-ui>
|
|
36
|
+
</col-ui>
|
|
37
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<radio-group-ui>` — auto-registers the tag on import.
|
|
3
|
+
*
|
|
4
|
+
* For non-side-effect class import (test isolation, tag override), use
|
|
5
|
+
* the `class` subpath:
|
|
6
|
+
*
|
|
7
|
+
* import { UIRadioGroup } from '@adia-ai/web-components/components/radio-group/class';
|
|
8
|
+
*
|
|
9
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineIfFree } from '../../core/register.js';
|
|
13
|
+
import { UIRadioGroup } from './radio-group.class.js';
|
|
14
|
+
|
|
15
|
+
defineIfFree('radio-group-ui', UIRadioGroup);
|
|
16
|
+
|
|
17
|
+
export { UIRadioGroup };
|