@adia-ai/web-components 0.8.40 → 0.8.41
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 +50 -3
- package/MIGRATION.md +1042 -16
- package/README.md +25 -30
- package/USAGE.md +26 -0
- package/bin/audit-template-children.mjs +556 -0
- package/components/accordion/accordion-item.a2ui.json +4 -0
- package/components/accordion/accordion-item.yaml +2 -0
- package/components/accordion/accordion.a2ui.json +4 -0
- package/components/accordion/accordion.yaml +2 -0
- package/components/action-list/action-item.a2ui.json +24 -2
- package/components/action-list/action-item.yaml +28 -3
- package/components/action-list/action-list.a2ui.json +6 -1
- package/components/action-list/action-list.class.js +52 -11
- package/components/action-list/action-list.css +43 -3
- package/components/action-list/action-list.d.ts +4 -2
- package/components/action-list/action-list.yaml +5 -0
- package/components/adia-mark/adia-mark.a2ui.json +1 -0
- package/components/adia-wordmark/adia-wordmark.a2ui.json +1 -0
- package/components/agent-artifact/agent-artifact.a2ui.json +1 -0
- package/components/agent-feedback-bar/agent-feedback-bar.a2ui.json +6 -5
- package/components/agent-feedback-bar/agent-feedback-bar.yaml +6 -2
- package/components/agent-questions/agent-questions.a2ui.json +1 -0
- package/components/agent-reasoning/agent-reasoning.a2ui.json +6 -5
- package/components/agent-reasoning/agent-reasoning.yaml +6 -2
- package/components/agent-suggestions/agent-suggestions.a2ui.json +11 -2
- package/components/agent-suggestions/agent-suggestions.class.js +2 -1
- package/components/agent-suggestions/agent-suggestions.d.ts +4 -4
- package/components/agent-suggestions/agent-suggestions.yaml +4 -2
- package/components/agent-trace/agent-trace.a2ui.json +6 -5
- package/components/agent-trace/agent-trace.yaml +6 -2
- package/components/alert/alert.a2ui.json +1 -0
- package/components/alert/alert.css +1 -1
- package/components/anchor-bar/anchor-bar.a2ui.json +1 -0
- package/components/aside/aside.a2ui.json +1 -0
- package/components/avatar/avatar-group.a2ui.json +7 -1
- package/components/avatar/avatar-group.yaml +1 -0
- package/components/avatar/avatar.a2ui.json +12 -11
- package/components/avatar/avatar.d.ts +1 -1
- package/components/avatar/avatar.yaml +4 -4
- package/components/badge/badge.a2ui.json +1 -0
- package/components/block/block.a2ui.json +1 -0
- package/components/blockquote/blockquote.a2ui.json +1 -0
- package/components/breadcrumb/breadcrumb.a2ui.json +1 -0
- package/components/button/button.a2ui.json +1 -3
- package/components/button/button.yaml +1 -3
- package/components/calendar-grid/calendar-grid.a2ui.json +1 -0
- package/components/calendar-grid/calendar-grid.class.js +2 -1
- package/components/calendar-picker/calendar-picker.a2ui.json +143 -24
- package/components/calendar-picker/calendar-picker.class.js +490 -197
- package/components/calendar-picker/calendar-picker.css +120 -171
- package/components/calendar-picker/calendar-picker.yaml +225 -47
- package/components/canvas/canvas.a2ui.json +6 -5
- package/components/canvas/canvas.d.ts +37 -0
- package/components/canvas/canvas.js +72 -3
- package/components/canvas/canvas.yaml +6 -2
- package/components/card/card.a2ui.json +25 -2
- package/components/card/card.class.js +3 -0
- package/components/card/card.css +31 -1
- package/components/card/card.d.ts +2 -2
- package/components/card/card.examples.md +3 -3
- package/components/card/card.yaml +28 -4
- package/components/chart/chart.a2ui.json +9 -4
- package/components/chart/chart.class.js +6 -6
- package/components/chart/chart.css +6 -4
- package/components/chart/chart.d.ts +1 -1
- package/components/chart/chart.yaml +5 -3
- package/components/chart-legend/chart-legend.a2ui.json +1 -0
- package/components/chat-thread/chat-input.a2ui.json +1 -0
- package/components/chat-thread/chat-thread.a2ui.json +10 -9
- package/components/chat-thread/chat-thread.yaml +13 -20
- package/components/check/check.a2ui.json +10 -9
- package/components/check/check.yaml +4 -4
- package/components/code/code.a2ui.json +1 -0
- package/components/col/col.a2ui.json +1 -0
- package/components/color-input/color-input.a2ui.json +1 -0
- package/components/color-picker/color-picker.a2ui.json +1 -0
- package/components/combobox/combobox.a2ui.json +3 -2
- package/components/combobox/combobox.yaml +2 -2
- package/components/command/command.a2ui.json +12 -10
- package/components/command/command.class.js +3 -2
- package/components/command/command.yaml +17 -4
- package/components/context-menu/context-menu.a2ui.json +1 -0
- package/components/date-range-picker/date-range-picker.a2ui.json +2 -1
- package/components/date-range-picker/date-range-picker.yaml +1 -1
- package/components/datetime-picker/datetime-picker.a2ui.json +15 -108
- package/components/datetime-picker/datetime-picker.class.js +37 -690
- package/components/datetime-picker/datetime-picker.css +11 -183
- package/components/datetime-picker/datetime-picker.d.ts +4 -5
- package/components/datetime-picker/datetime-picker.yaml +44 -135
- package/components/demo-toggle/demo-toggle.a2ui.json +1 -0
- package/components/demo-toggle/demo-toggle.css +1 -1
- package/components/description-list/description-list.a2ui.json +1 -0
- package/components/display-field/display-field.a2ui.json +1 -0
- package/components/divider/divider.a2ui.json +1 -0
- package/components/drawer/drawer.a2ui.json +1 -3
- package/components/drawer/drawer.css +1 -1
- package/components/drawer/drawer.yaml +0 -2
- package/components/drilldown/drilldown.a2ui.json +22 -20
- package/components/drilldown/drilldown.class.js +4 -3
- package/components/drilldown/drilldown.css +6 -6
- package/components/drilldown/drilldown.yaml +40 -9
- package/components/embed/embed.a2ui.json +6 -5
- package/components/embed/embed.yaml +2 -2
- package/components/empty-state/empty-state.a2ui.json +1 -0
- package/components/feed/feed-item.a2ui.json +25 -5
- package/components/feed/feed-item.yaml +28 -7
- package/components/feed/feed.a2ui.json +9 -2
- package/components/feed/feed.class.js +19 -12
- package/components/feed/feed.css +8 -8
- package/components/feed/feed.yaml +14 -1
- package/components/field/field.a2ui.json +1 -0
- package/components/field/field.examples.md +1 -1
- package/components/fields/fields.a2ui.json +1 -0
- package/components/fields/fields.examples.md +1 -1
- package/components/footer/footer.a2ui.json +1 -0
- package/components/frame/frame.a2ui.json +1 -0
- package/components/grid/grid.a2ui.json +1 -0
- package/components/header/header.a2ui.json +3 -2
- package/components/header/header.yaml +3 -3
- package/components/heatmap/heatmap.a2ui.json +2 -1
- package/components/heatmap/heatmap.class.js +1 -1
- package/components/heatmap/heatmap.css +4 -4
- package/components/heatmap/heatmap.yaml +1 -1
- package/components/icon/icon.a2ui.json +1 -0
- package/components/image/image.a2ui.json +28 -14
- package/components/image/image.class.js +38 -23
- package/components/image/image.css +18 -8
- package/components/image/image.d.ts +2 -2
- package/components/image/image.examples.md +3 -3
- package/components/image/image.yaml +23 -7
- package/components/inline-edit/inline-edit.a2ui.json +1 -0
- package/components/inline-message/inline-message.a2ui.json +3 -1
- package/components/inline-message/inline-message.class.js +3 -1
- package/components/inline-message/inline-message.d.ts +2 -2
- package/components/inline-message/inline-message.yaml +2 -1
- package/components/input/input.a2ui.json +66 -16
- package/components/input/input.class.js +64 -7
- package/components/input/input.css +14 -14
- package/components/input/input.d.ts +9 -4
- package/components/input/input.yaml +83 -10
- package/components/inspector/inspector.a2ui.json +1 -0
- package/components/integration-card/integration-card.a2ui.json +1 -0
- package/components/kbd/kbd.a2ui.json +1 -0
- package/components/link/link.a2ui.json +2 -1
- package/components/link/link.yaml +1 -1
- package/components/list/list-item.a2ui.json +9 -0
- package/components/list/list-item.yaml +12 -0
- package/components/list/list.a2ui.json +9 -3
- package/components/list/list.class.js +14 -1
- package/components/list/list.css +12 -1
- package/components/list/list.d.ts +7 -5
- package/components/list/list.yaml +16 -6
- package/components/list-window/list-window.a2ui.json +1 -0
- package/components/list-window/list-window.class.js +2 -1
- package/components/loading-overlay/loading-overlay.a2ui.json +1 -0
- package/components/mark/mark.a2ui.json +2 -1
- package/components/mark/mark.yaml +1 -1
- package/components/menu/menu-divider.a2ui.json +1 -0
- package/components/menu/menu-item.a2ui.json +3 -1
- package/components/menu/menu-item.yaml +6 -0
- package/components/menu/menu-label.a2ui.json +1 -0
- package/components/menu/menu.a2ui.json +3 -2
- package/components/menu/menu.class.js +2 -2
- package/components/menu/menu.d.ts +2 -2
- package/components/menu/menu.yaml +3 -3
- package/components/modal/modal.a2ui.json +1 -0
- package/components/nav/nav.a2ui.json +1 -0
- package/components/nav/nav.class.js +14 -3
- package/components/nav-group/nav-group.a2ui.json +1 -0
- package/components/nav-group/nav-group.class.js +3 -1
- package/components/nav-item/nav-item.a2ui.json +1 -0
- package/components/noodles/noodles.a2ui.json +1 -0
- package/components/noodles/noodles.class.js +5 -1
- package/components/number-format/number-format.a2ui.json +1 -0
- package/components/option-card/option-card.a2ui.json +1 -0
- package/components/otp-input/otp-input.a2ui.json +3 -2
- package/components/otp-input/otp-input.yaml +9 -2
- package/components/page/page.a2ui.json +13 -0
- package/components/page/page.d.ts +1 -1
- package/components/page/page.yaml +1 -0
- package/components/pagination/pagination.a2ui.json +1 -0
- package/components/pagination/pagination.class.js +3 -2
- package/components/pane/pane.a2ui.json +6 -0
- package/components/pane/pane.class.js +3 -3
- package/components/pane/pane.css +2 -2
- package/components/pane/pane.yaml +3 -0
- package/components/password-strength/password-strength.a2ui.json +1 -0
- package/components/pipeline-status/pipeline-status.a2ui.json +1 -0
- package/components/popover/popover.a2ui.json +7 -6
- package/components/popover/popover.class.js +2 -2
- package/components/popover/popover.d.ts +2 -2
- package/components/popover/popover.yaml +4 -4
- package/components/preview/preview.a2ui.json +1 -0
- package/components/preview/preview.class.js +5 -1
- package/components/progress/progress.a2ui.json +47 -24
- package/components/progress/progress.class.js +66 -11
- package/components/progress/progress.css +44 -26
- package/components/progress/progress.d.ts +7 -5
- package/components/progress/progress.examples.md +2 -2
- package/components/progress/progress.yaml +235 -53
- package/components/progress-row/progress-row.a2ui.json +16 -12
- package/components/progress-row/progress-row.d.ts +2 -2
- package/components/progress-row/progress-row.examples.md +2 -14
- package/components/progress-row/progress-row.yaml +28 -36
- package/components/qr-code/qr-code.a2ui.json +3 -2
- package/components/qr-code/qr-code.class.js +4 -4
- package/components/qr-code/qr-code.css +1 -1
- package/components/qr-code/qr-code.yaml +5 -3
- package/components/radio/radio.a2ui.json +8 -7
- package/components/radio/radio.class.js +8 -0
- package/components/radio/radio.yaml +3 -3
- package/components/radio-group/radio-group.a2ui.json +5 -3
- package/components/radio-group/radio-group.class.js +11 -0
- package/components/radio-group/radio-group.yaml +7 -1
- package/components/range/range.a2ui.json +6 -0
- package/components/range/range.class.js +2 -2
- package/components/range/range.css +2 -2
- package/components/range/range.yaml +3 -0
- package/components/rating/rating.a2ui.json +1 -0
- package/components/relative-time/relative-time.a2ui.json +1 -0
- package/components/richtext/richtext.a2ui.json +2 -1
- package/components/richtext/richtext.yaml +1 -1
- package/components/row/row.a2ui.json +1 -0
- package/components/search/search.a2ui.json +1 -0
- package/components/search/search.class.js +4 -1
- package/components/section/section.a2ui.json +1 -0
- package/components/segment/segment.a2ui.json +21 -10
- package/components/segment/segment.css +15 -0
- package/components/segment/segment.d.ts +2 -1
- package/components/segment/segment.yaml +20 -13
- package/components/segmented/segmented.a2ui.json +36 -7
- package/components/segmented/segmented.class.js +113 -22
- package/components/segmented/segmented.css +3 -0
- package/components/segmented/segmented.d.ts +3 -1
- package/components/segmented/segmented.yaml +80 -8
- package/components/select/select.a2ui.json +18 -13
- package/components/select/select.class.js +61 -37
- package/components/select/select.css +15 -13
- package/components/select/select.yaml +18 -7
- package/components/skeleton/skeleton.a2ui.json +8 -7
- package/components/skeleton/skeleton.class.js +8 -7
- package/components/skeleton/skeleton.css +21 -6
- package/components/skeleton/skeleton.d.ts +2 -2
- package/components/skeleton/skeleton.examples.md +2 -2
- package/components/skeleton/skeleton.yaml +3 -2
- package/components/skip-nav/skip-nav.a2ui.json +1 -0
- package/components/slider/slider.a2ui.json +6 -0
- package/components/slider/slider.class.js +20 -3
- package/components/slider/slider.css +2 -2
- package/components/slider/slider.yaml +3 -0
- package/components/spinner/spinner.a2ui.json +1 -0
- package/components/stack/stack.a2ui.json +1 -0
- package/components/stat/stat.a2ui.json +1 -0
- package/components/stat/stat.js +2 -2
- package/components/step-progress/step-progress.a2ui.json +1 -0
- package/components/step-progress/step-progress.yaml +1 -1
- package/components/stepper/stepper-item.a2ui.json +7 -3
- package/components/stepper/stepper-item.yaml +15 -5
- package/components/stepper/stepper.a2ui.json +5 -1
- package/components/stepper/stepper.class.js +31 -16
- package/components/stepper/stepper.d.ts +1 -1
- package/components/stepper/stepper.yaml +4 -2
- package/components/stream/stream.a2ui.json +1 -0
- package/components/swatch/swatch.a2ui.json +1 -0
- package/components/swiper/swiper.a2ui.json +1 -0
- package/components/swiper/swiper.class.js +5 -1
- package/components/switch/switch.a2ui.json +12 -11
- package/components/switch/switch.css +1 -1
- package/components/switch/switch.yaml +6 -6
- package/components/table/table.a2ui.json +1 -0
- package/components/table/table.class.js +14 -12
- package/components/table/table.css +11 -9
- package/components/table-toolbar/table-toolbar.a2ui.json +1 -0
- package/components/table-toolbar/table-toolbar.class.js +38 -19
- package/components/table-toolbar/table-toolbar.css +1 -1
- package/components/tabs/tab.a2ui.json +9 -0
- package/components/tabs/tab.js +4 -0
- package/components/tabs/tab.yaml +15 -0
- package/components/tabs/tabs.a2ui.json +9 -4
- package/components/tabs/tabs.class.js +13 -1
- package/components/tabs/tabs.css +8 -0
- package/components/tabs/tabs.d.ts +2 -0
- package/components/tabs/tabs.yaml +9 -2
- package/components/tag/tag.a2ui.json +1 -0
- package/components/tags-input/tags-input.a2ui.json +22 -17
- package/components/tags-input/tags-input.class.js +13 -11
- package/components/tags-input/tags-input.css +10 -6
- package/components/tags-input/tags-input.d.ts +16 -7
- package/components/tags-input/tags-input.examples.md +2 -2
- package/components/tags-input/tags-input.yaml +25 -8
- package/components/text/text.a2ui.json +6 -5
- package/components/text/text.css +10 -5
- package/components/text/text.d.ts +2 -2
- package/components/text/text.yaml +9 -4
- package/components/textarea/textarea.a2ui.json +2 -1
- package/components/textarea/textarea.yaml +1 -1
- package/components/theme-provider/theme-provider.a2ui.json +2 -1
- package/components/theme-provider/theme-provider.d.ts +5 -3
- package/components/theme-provider/theme-provider.yaml +5 -3
- package/components/time-picker/time-picker.a2ui.json +1 -0
- package/components/time-picker/time-picker.class.js +4 -2
- package/components/timeline/timeline-item.a2ui.json +26 -3
- package/components/timeline/timeline-item.yaml +22 -7
- package/components/timeline/timeline.a2ui.json +4 -0
- package/components/timeline/timeline.class.js +29 -18
- package/components/timeline/timeline.d.ts +3 -3
- package/components/timeline/timeline.yaml +2 -0
- package/components/toast/toast.a2ui.json +10 -3
- package/components/toast/toast.yaml +20 -13
- package/components/toc/toc.a2ui.json +1 -0
- package/components/toggle-group/toggle-group.a2ui.json +7 -3
- package/components/toggle-group/toggle-group.class.js +40 -1
- package/components/toggle-group/toggle-group.css +6 -1
- package/components/toggle-group/toggle-group.d.ts +2 -2
- package/components/toggle-group/toggle-group.yaml +21 -12
- package/components/toggle-group/toggle-option.a2ui.json +7 -3
- package/components/toggle-group/toggle-option.yaml +13 -7
- package/components/toggle-scheme/toggle-scheme.a2ui.json +2 -1
- package/components/toggle-scheme/toggle-scheme.yaml +1 -1
- package/components/toolbar/toolbar-group.a2ui.json +1 -0
- package/components/toolbar/toolbar.a2ui.json +1 -0
- package/components/toolbar/toolbar.class.js +3 -0
- package/components/tooltip/tooltip.a2ui.json +1 -0
- package/components/tour/tour-step.a2ui.json +4 -0
- package/components/tour/tour-step.yaml +2 -0
- package/components/tour/tour.a2ui.json +4 -0
- package/components/tour/tour.yaml +2 -0
- package/components/tree/tree-item.a2ui.json +5 -0
- package/components/tree/tree-item.yaml +3 -0
- package/components/tree/tree.a2ui.json +4 -0
- package/components/tree/tree.class.js +2 -1
- package/components/tree/tree.yaml +2 -0
- package/components/upload/upload.a2ui.json +8 -1
- package/components/upload/upload.class.js +35 -5
- package/components/upload/upload.css +28 -0
- package/components/upload/upload.d.ts +16 -0
- package/components/upload/upload.yaml +27 -7
- package/components/visually-hidden/visually-hidden.a2ui.json +1 -0
- package/core/dom.d.ts +11 -0
- package/core/dom.js +34 -0
- package/core/form.js +7 -0
- package/core/index.d.ts +1 -0
- package/core/index.js +1 -0
- package/core/responsive.js +16 -4
- package/core/template.js +2 -1
- package/custom-elements.json +437 -489
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +1 -1
- package/dist/theme-provider.min.js +5 -5
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +91 -104
- package/dist/web-components.sheet.js +1 -1
- package/package.json +4 -2
- package/patterns/conversion-funnel/conversion-funnel.examples.html +15 -15
- package/patterns/inline-dialog/inline-dialog.examples.html +2 -2
- package/patterns/inline-dialog/inline-dialog.html +4 -0
- package/patterns/timezone-selector/timezone-selector.examples.html +1 -1
- package/patterns/timezone-selector/timezone-selector.html +2 -2
- package/styles/api/text.css +7 -0
- package/traits/_catalog.json +129 -41
- package/traits/anchor-positioning/anchor-positioning.js +1 -0
- package/traits/announcer/announcer.js +1 -0
- package/traits/arrow-grid-nav/arrow-grid-nav.js +1 -0
- package/traits/attention-pulse/attention-pulse.js +1 -0
- package/traits/count-up/count-up.js +1 -0
- package/traits/define.js +78 -0
- package/traits/focus-restore/focus-restore.js +2 -1
- package/traits/haptic-feedback/haptic-feedback.js +1 -0
- package/traits/input-mask/input-mask.js +1 -0
- package/traits/intersection-observer/intersection-observer.js +1 -0
- package/traits/layout-animation/layout-animation.js +1 -0
- package/traits/noise-texture/noise-texture.js +1 -0
- package/traits/resize-observer/resize-observer.js +1 -0
- package/traits/scroll-progress/scroll-progress.js +1 -0
- package/traits/snap-to-grid/snap-to-grid.js +1 -0
- package/traits/sound-feedback/sound-feedback.js +1 -0
- package/traits/spring-animate/spring-animate.js +1 -0
- package/traits/validation/validation.js +1 -0
|
@@ -148,12 +148,7 @@ export class UIInput extends UIFormElement {
|
|
|
148
148
|
const trailingNodes = logicalSlotted(this, 'trailing');
|
|
149
149
|
|
|
150
150
|
if (!this.querySelector('[slot="text"]')) {
|
|
151
|
-
|
|
152
|
-
this.innerHTML = this.#shellHTML(labelId);
|
|
153
|
-
// innerHTML wiped the consumer-supplied leading/trailing nodes; re-attach
|
|
154
|
-
// them into the field at the right positions. Order matches the JSDoc:
|
|
155
|
-
// prefix → leading → label → text → suffix → trailing → controls
|
|
156
|
-
this.#installAffordances(leadingNodes, trailingNodes);
|
|
151
|
+
this.#buildShell(leadingNodes, trailingNodes);
|
|
157
152
|
}
|
|
158
153
|
|
|
159
154
|
this.#textEl = this.querySelector('[slot="text"]');
|
|
@@ -200,6 +195,64 @@ export class UIInput extends UIFormElement {
|
|
|
200
195
|
}
|
|
201
196
|
}
|
|
202
197
|
|
|
198
|
+
#buildShell(leadingNodes, trailingNodes) {
|
|
199
|
+
const labelId = this.label ? `input-label-${++UIInput.#labelSeq}` : '';
|
|
200
|
+
this.innerHTML = this.#shellHTML(labelId);
|
|
201
|
+
// innerHTML wiped the consumer-supplied leading/trailing nodes; re-attach
|
|
202
|
+
// them into the field at the right positions. Order matches the JSDoc:
|
|
203
|
+
// prefix → leading → label → text → suffix → trailing → controls
|
|
204
|
+
this.#installAffordances(leadingNodes, trailingNodes);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Post-connect `type` reconciliation (gh#1332 review follow-up). The shell
|
|
209
|
+
* is built once at connect, but `type` is reactive and reflected — since
|
|
210
|
+
* number-mode CSS now keys off the host's [type="number"] directly, a
|
|
211
|
+
* text↔number flip after connect would restyle without restructuring
|
|
212
|
+
* (role, inputmode, stepper controls, beforeinput filter all stale).
|
|
213
|
+
* Rebuild the shell + listeners when the built mode disagrees with the
|
|
214
|
+
* live property. No-op on every render where the modes already agree.
|
|
215
|
+
*/
|
|
216
|
+
#reconcileTypeMode() {
|
|
217
|
+
if (this.#isNativePassword || !this.#textEl) return;
|
|
218
|
+
const builtNumberMode = !!this.querySelector(':scope > [slot="field"] [data-controls]');
|
|
219
|
+
if (builtNumberMode === this.#isNumberMode) return;
|
|
220
|
+
|
|
221
|
+
// Unwire the old surface, capture consumer affordances from inside the
|
|
222
|
+
// live field (logicalSlotted won't re-match nodes already moved there).
|
|
223
|
+
this.#textEl.removeEventListener('input', this.#onInput);
|
|
224
|
+
this.#textEl.removeEventListener('keydown', this.#onKeydown);
|
|
225
|
+
this.#textEl.removeEventListener('blur', this.#onBlur);
|
|
226
|
+
this.#textEl.removeEventListener('focus', this.#onFocus);
|
|
227
|
+
this.#textEl.removeEventListener('paste', this.#onPaste);
|
|
228
|
+
this.#textEl.removeEventListener('beforeinput', this.#onBeforeInput);
|
|
229
|
+
const field = this.querySelector(':scope > [slot="field"]');
|
|
230
|
+
const leadingNodes = field ? [...field.querySelectorAll(':scope > [slot="leading"]')] : [];
|
|
231
|
+
const trailingNodes = field ? [...field.querySelectorAll(':scope > [slot="trailing"]')] : [];
|
|
232
|
+
|
|
233
|
+
this.#buildShell(leadingNodes, trailingNodes);
|
|
234
|
+
this.setAttribute('role', this.#isNumberMode ? 'spinbutton' : 'textbox');
|
|
235
|
+
this.#textEl = this.querySelector('[slot="text"]');
|
|
236
|
+
this.#labelEl = this.querySelector('[slot="label"]');
|
|
237
|
+
this.#upBtn = this.querySelector('[data-step="up"]');
|
|
238
|
+
this.#downBtn = this.querySelector('[data-step="down"]');
|
|
239
|
+
if (this.#textEl) {
|
|
240
|
+
this.#textEl.addEventListener('input', this.#onInput);
|
|
241
|
+
this.#textEl.addEventListener('keydown', this.#onKeydown);
|
|
242
|
+
this.#textEl.addEventListener('blur', this.#onBlur);
|
|
243
|
+
this.#textEl.addEventListener('focus', this.#onFocus);
|
|
244
|
+
this.#textEl.addEventListener('paste', this.#onPaste);
|
|
245
|
+
if (this.#isNumberMode) {
|
|
246
|
+
this.#textEl.addEventListener('beforeinput', this.#onBeforeInput);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
this.#upBtn?.addEventListener('pointerdown', this.#onStepperUpDown);
|
|
250
|
+
this.#downBtn?.addEventListener('pointerdown', this.#onStepperDownDown);
|
|
251
|
+
if (this.prefix || this.suffix) {
|
|
252
|
+
whenIconRegistryReady.then(() => this.#promoteAffixes());
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
203
256
|
#shellHTML(labelId) {
|
|
204
257
|
const prefix = this.prefix ? `<span slot="prefix">${renderAffix(this.prefix)}</span>` : '';
|
|
205
258
|
const label = this.label ? `<span slot="label" id="${labelId}">${this.label}</span>` : '';
|
|
@@ -234,8 +287,11 @@ export class UIInput extends UIFormElement {
|
|
|
234
287
|
icon="caret-down" data-step="down" aria-label="Decrease"></button-ui>
|
|
235
288
|
</span>` : '';
|
|
236
289
|
|
|
290
|
+
// No private data-number mirror on the field div — number-mode CSS keys
|
|
291
|
+
// off the declared, reflected host [type="number"] directly (gh#1332
|
|
292
|
+
// Category A; select-ui data-multi-chips precedent).
|
|
237
293
|
return `
|
|
238
|
-
<div slot="field"
|
|
294
|
+
<div slot="field">
|
|
239
295
|
${prefix}${label}${editable}${suffix}${controls}
|
|
240
296
|
</div>
|
|
241
297
|
`;
|
|
@@ -372,6 +428,7 @@ export class UIInput extends UIFormElement {
|
|
|
372
428
|
|
|
373
429
|
render() {
|
|
374
430
|
if (!this.#textEl) return;
|
|
431
|
+
this.#reconcileTypeMode();
|
|
375
432
|
|
|
376
433
|
const text = this.value ?? '';
|
|
377
434
|
|
|
@@ -191,10 +191,10 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
191
191
|
absolutely against the field's inline-end edge so it NEVER affects
|
|
192
192
|
the field's flex-sized height — number-mode inputs share the same
|
|
193
193
|
24/30/36px (sm/md/lg) baseline as text/email/password/etc. */
|
|
194
|
-
[
|
|
194
|
+
:scope[type="number"] [slot="field"] [slot="text"] {
|
|
195
195
|
text-align: end;
|
|
196
196
|
font-variant-numeric: tabular-nums;
|
|
197
|
-
font-weight: var(--a-weight-medium
|
|
197
|
+
font-weight: var(--a-weight-medium);
|
|
198
198
|
/* Reserve space for the absolutely-positioned controls column so the
|
|
199
199
|
value never collides with the stepper buttons. */
|
|
200
200
|
padding-inline-end: var(--input-controls-width, calc(var(--input-height) * 0.7));
|
|
@@ -202,7 +202,7 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
202
202
|
|
|
203
203
|
/* Suffix sits flush after the value in number mode (no auto margin so
|
|
204
204
|
the value+suffix pair stays right-aligned together). */
|
|
205
|
-
[
|
|
205
|
+
:scope[type="number"] [slot="field"] [slot="suffix"] {
|
|
206
206
|
margin-inline-start: 0;
|
|
207
207
|
/* Reserve space for the absolutely-positioned controls column so the
|
|
208
208
|
suffix never collides with the stepper buttons. The padding moves
|
|
@@ -210,11 +210,11 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
210
210
|
— only one element needs the reservation since they're adjacent. */
|
|
211
211
|
margin-inline-end: var(--input-controls-width, calc(var(--input-height) * 0.7));
|
|
212
212
|
}
|
|
213
|
-
[
|
|
213
|
+
:scope[type="number"] [slot="field"]:has([slot="suffix"]) [slot="text"] {
|
|
214
214
|
padding-inline-end: 0;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
[
|
|
217
|
+
:scope[type="number"] [slot="field"] {
|
|
218
218
|
position: relative;
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -230,7 +230,7 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
230
230
|
overflow: hidden;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
[
|
|
233
|
+
:scope[type="number"] [slot="field"] [slot="controls"] button-ui {
|
|
234
234
|
/* Defeat button-ui's intrinsic min-height (driven by --button-height) so
|
|
235
235
|
its content can't push past half the column height. Padding + border
|
|
236
236
|
are zeroed; the divider between the two buttons comes from the
|
|
@@ -256,14 +256,14 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
256
256
|
because its own `:where(:scope)` declaration of --icon-size wins over
|
|
257
257
|
any value inherited from its parent button-ui. Tying it to
|
|
258
258
|
--input-height keeps the caret proportional across sm/md/lg. */
|
|
259
|
-
[
|
|
259
|
+
:scope[type="number"] [slot="field"] [slot="controls"] icon-ui {
|
|
260
260
|
--icon-size: calc(var(--input-height) * 0.4);
|
|
261
261
|
}
|
|
262
|
-
[
|
|
262
|
+
:scope[type="number"] [slot="field"] [slot="controls"] button-ui:hover {
|
|
263
263
|
--button-bg: var(--a-ui-bg-hover);
|
|
264
264
|
--button-fg: var(--a-ui-text);
|
|
265
265
|
}
|
|
266
|
-
[
|
|
266
|
+
:scope[type="number"] [slot="field"] [slot="controls"] button-ui[disabled] {
|
|
267
267
|
--button-fg: var(--a-ui-text-disabled);
|
|
268
268
|
pointer-events: none;
|
|
269
269
|
}
|
|
@@ -273,11 +273,11 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
273
273
|
field radius cleanly. The divider between the two buttons is the
|
|
274
274
|
inline-start border on the column + the bottom-border on the upper
|
|
275
275
|
button. */
|
|
276
|
-
[
|
|
276
|
+
:scope[type="number"] [slot="field"] [slot="controls"] button-ui:first-child {
|
|
277
277
|
border-bottom: 1px solid var(--input-border);
|
|
278
278
|
border-start-end-radius: calc(var(--input-radius) - 1px);
|
|
279
279
|
}
|
|
280
|
-
[
|
|
280
|
+
:scope[type="number"] [slot="field"] [slot="controls"] button-ui:last-child {
|
|
281
281
|
border-end-end-radius: calc(var(--input-radius) - 1px);
|
|
282
282
|
}
|
|
283
283
|
|
|
@@ -287,8 +287,8 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
287
287
|
margin-inline-end: 0;
|
|
288
288
|
border-inline-start: none;
|
|
289
289
|
}
|
|
290
|
-
:scope[raw]
|
|
291
|
-
:scope[raw]
|
|
290
|
+
:scope[raw][type="number"] [slot="field"] [slot="controls"] button-ui:first-child,
|
|
291
|
+
:scope[raw][type="number"] [slot="field"] [slot="controls"] button-ui:last-child {
|
|
292
292
|
border-bottom: none;
|
|
293
293
|
border-radius: 0;
|
|
294
294
|
}
|
|
@@ -373,7 +373,7 @@ input-ui:not([disabled]) [slot="field"]:hover [slot="suffix"] {
|
|
|
373
373
|
for the stepper column so it doesn't overlap. (controls is
|
|
374
374
|
absolutely positioned at inset-inline-end: 0; trailing sits in the
|
|
375
375
|
flex flow.) Same pattern as [slot="suffix"] in number mode. */
|
|
376
|
-
[
|
|
376
|
+
:scope[type="number"] [slot="field"]:has(> [slot="trailing"]) > [slot="trailing"] {
|
|
377
377
|
margin-inline-end: var(--input-controls-width, calc(var(--input-height) * 0.7));
|
|
378
378
|
}
|
|
379
379
|
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `<input-ui>` — Text + email + password + number + tel + url
|
|
2
|
+
* `<input-ui>` — Text + email + password + number + tel + url input.
|
|
3
3
|
*
|
|
4
4
|
* @see https://ui-kit.exe.xyz/site/components/input
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { UIFormElement } from '../../core/form.js';
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Native HTML input types this primitive actually implements. Does NOT
|
|
11
|
+
* cover date/datetime-local/time/month/week/color/search — those never
|
|
12
|
+
* rendered a native affordance on this primitive (v0.9.0, gh#1363 B10);
|
|
13
|
+
* route to CalendarPicker/DatetimePicker/TimePicker/ColorInput/Search
|
|
14
|
+
* instead.
|
|
15
|
+
*/
|
|
10
16
|
export type UIInputType =
|
|
11
17
|
| 'text'
|
|
12
18
|
| 'email'
|
|
13
19
|
| 'password'
|
|
14
20
|
| 'number'
|
|
15
21
|
| 'tel'
|
|
16
|
-
| 'url'
|
|
17
|
-
| 'search';
|
|
22
|
+
| 'url';
|
|
18
23
|
|
|
19
24
|
export interface InputChangeEventDetail {
|
|
20
25
|
value: string;
|
|
@@ -23,8 +23,19 @@ props:
|
|
|
23
23
|
type: string
|
|
24
24
|
default: ''
|
|
25
25
|
type:
|
|
26
|
-
description:
|
|
27
|
-
|
|
26
|
+
description: >-
|
|
27
|
+
Input type. `password` wraps a native `<input>` for disc masking;
|
|
28
|
+
`number` renders a contenteditable + stepper buttons (no native
|
|
29
|
+
input). `text`/`email`/`tel`/`url` use plain contenteditable.
|
|
30
|
+
input-ui does NOT implement date/time/color/search affordances —
|
|
31
|
+
those HTML5 type values render as a plain contenteditable with no
|
|
32
|
+
calendar, clock, swatch, or search affordance (first-pass-VALID
|
|
33
|
+
against this enum historically, but functionally broken generation
|
|
34
|
+
output). Use the owning primitive instead — CalendarPicker for date
|
|
35
|
+
(+ month/week, no dedicated granularity primitive exists),
|
|
36
|
+
DatetimePicker for datetime-local, TimePicker for time,
|
|
37
|
+
ColorInput/ColorPicker for color, Search for search. See
|
|
38
|
+
anti_patterns.
|
|
28
39
|
type: string
|
|
29
40
|
default: text
|
|
30
41
|
enum:
|
|
@@ -34,13 +45,6 @@ props:
|
|
|
34
45
|
- number
|
|
35
46
|
- tel
|
|
36
47
|
- url
|
|
37
|
-
- search
|
|
38
|
-
- date
|
|
39
|
-
- datetime-local
|
|
40
|
-
- time
|
|
41
|
-
- month
|
|
42
|
-
- week
|
|
43
|
-
- color
|
|
44
48
|
required:
|
|
45
49
|
description: Marks the field as required for form validation. Sets aria-required.
|
|
46
50
|
type: boolean
|
|
@@ -282,7 +286,71 @@ a2ui:
|
|
|
282
286
|
primitive ALSO fires a bubbling `submit` event on Enter
|
|
283
287
|
(unconditional, no opt-in attribute); <chat-input-ui> simply
|
|
284
288
|
builds on that semantic.
|
|
285
|
-
|
|
289
|
+
- >-
|
|
290
|
+
input-ui's `type` enum covers only the modes it actually implements
|
|
291
|
+
— text/email/password/number/tel/url (gh#1363 B10). Never
|
|
292
|
+
set [type] to an HTML5 value input-ui doesn't render — route
|
|
293
|
+
date/time/color/search intents to the owning primitive instead:
|
|
294
|
+
CalendarPicker, DatetimePicker, TimePicker, ColorInput (or
|
|
295
|
+
ColorPicker directly), Search. See anti_patterns.
|
|
296
|
+
anti_patterns:
|
|
297
|
+
- wrong: |
|
|
298
|
+
{"component": "Input", "type": "date"}
|
|
299
|
+
why: |
|
|
300
|
+
input-ui's `type` enum no longer includes "date" (gh#1363
|
|
301
|
+
B10) — it never rendered a native date picker, just a plain
|
|
302
|
+
contenteditable surface with no calendar affordance. Use
|
|
303
|
+
CalendarPicker for single-date input.
|
|
304
|
+
fix: |
|
|
305
|
+
{"component": "CalendarPicker"}
|
|
306
|
+
- wrong: |
|
|
307
|
+
{"component": "Input", "type": "datetime-local"}
|
|
308
|
+
why: |
|
|
309
|
+
Same gap as "date" — no native datetime picker rendered. Use
|
|
310
|
+
DatetimePicker for combined date + time input.
|
|
311
|
+
fix: |
|
|
312
|
+
{"component": "DatetimePicker"}
|
|
313
|
+
- wrong: |
|
|
314
|
+
{"component": "Input", "type": "time"}
|
|
315
|
+
why: |
|
|
316
|
+
Same gap as "date" — no native time picker rendered. Use
|
|
317
|
+
TimePicker for time-of-day input.
|
|
318
|
+
fix: |
|
|
319
|
+
{"component": "TimePicker"}
|
|
320
|
+
- wrong: |
|
|
321
|
+
{"component": "Input", "type": "month"}
|
|
322
|
+
why: |
|
|
323
|
+
No native month picker rendered, and no dedicated month-granularity
|
|
324
|
+
primitive exists in the catalog. CalendarPicker (day granularity)
|
|
325
|
+
is the closest routed primitive.
|
|
326
|
+
fix: |
|
|
327
|
+
{"component": "CalendarPicker"}
|
|
328
|
+
- wrong: |
|
|
329
|
+
{"component": "Input", "type": "week"}
|
|
330
|
+
why: |
|
|
331
|
+
No native week picker rendered, and no dedicated week-granularity
|
|
332
|
+
primitive exists in the catalog. CalendarPicker (day granularity)
|
|
333
|
+
is the closest routed primitive.
|
|
334
|
+
fix: |
|
|
335
|
+
{"component": "CalendarPicker"}
|
|
336
|
+
- wrong: |
|
|
337
|
+
{"component": "Input", "type": "color"}
|
|
338
|
+
why: |
|
|
339
|
+
No native color swatch/picker rendered — just a plain text
|
|
340
|
+
surface. Use ColorInput for a compact form-bearing swatch +
|
|
341
|
+
popover, or ColorPicker directly for the full OKLCH picker
|
|
342
|
+
surface.
|
|
343
|
+
fix: |
|
|
344
|
+
{"component": "ColorInput"}
|
|
345
|
+
- wrong: |
|
|
346
|
+
{"component": "Input", "type": "search"}
|
|
347
|
+
why: |
|
|
348
|
+
No search icon or clear affordance rendered — plain
|
|
349
|
+
contenteditable, functionally identical to the default text
|
|
350
|
+
type. Use Search for the dedicated magnifying-glass + clear +
|
|
351
|
+
debounced-search-event primitive.
|
|
352
|
+
fix: |
|
|
353
|
+
{"component": "Search"}
|
|
286
354
|
examples:
|
|
287
355
|
- name: quantity-stepper
|
|
288
356
|
description: Number input with [+]/[-] stepper buttons, min/max bounds, and a quantity label. Use for product quantity,
|
|
@@ -391,3 +459,8 @@ related:
|
|
|
391
459
|
- table
|
|
392
460
|
- pagination
|
|
393
461
|
- check-box
|
|
462
|
+
- search
|
|
463
|
+
- calendar-picker
|
|
464
|
+
- datetime-picker
|
|
465
|
+
- time-picker
|
|
466
|
+
- color-input
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"❌ `<link-ui>` with no `href` AND no `press` handler — a link to nowhere is a bug. Either set `href` or wire a navigate action handler.",
|
|
80
80
|
"❌ `<link-ui>` for form submission — submission is a button concern. Use `<button-ui type=\"submit\">`."
|
|
81
81
|
],
|
|
82
|
-
"category": "
|
|
82
|
+
"category": "display",
|
|
83
83
|
"composes": [],
|
|
84
84
|
"events": {
|
|
85
85
|
"press": {
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"url"
|
|
122
122
|
],
|
|
123
123
|
"name": "UILink",
|
|
124
|
+
"parts": {},
|
|
124
125
|
"related": [
|
|
125
126
|
"Button",
|
|
126
127
|
"NavItem",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"description": "Optional leading icon name (Phosphor).",
|
|
25
25
|
"type": "string"
|
|
26
26
|
},
|
|
27
|
+
"selected": {
|
|
28
|
+
"description": "Whether this row is the current selection. Managed by the parent <list-ui>[selectable] — don't set directly; the parent's [selectedKey] is the single source of truth (ADR-0056).",
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"default": false
|
|
31
|
+
},
|
|
27
32
|
"text": {
|
|
28
33
|
"description": "Primary text. Renders as semibold inline body.",
|
|
29
34
|
"type": "string"
|
|
@@ -34,6 +39,9 @@
|
|
|
34
39
|
],
|
|
35
40
|
"unevaluatedProperties": false,
|
|
36
41
|
"x-adiaui": {
|
|
42
|
+
"allowedParents": [
|
|
43
|
+
"List"
|
|
44
|
+
],
|
|
37
45
|
"anti_patterns": [],
|
|
38
46
|
"category": "layout",
|
|
39
47
|
"composes": [],
|
|
@@ -47,6 +55,7 @@
|
|
|
47
55
|
"row"
|
|
48
56
|
],
|
|
49
57
|
"name": "UIListItem",
|
|
58
|
+
"parts": {},
|
|
50
59
|
"related": [
|
|
51
60
|
"List",
|
|
52
61
|
"NavItem",
|
|
@@ -28,6 +28,14 @@ props:
|
|
|
28
28
|
description:
|
|
29
29
|
description: Secondary line below the primary text. Subtle color.
|
|
30
30
|
type: string
|
|
31
|
+
selected:
|
|
32
|
+
description: >-
|
|
33
|
+
Whether this row is the current selection. Managed by the parent
|
|
34
|
+
<list-ui>[selectable] — don't set directly; the parent's
|
|
35
|
+
[selectedKey] is the single source of truth (ADR-0056).
|
|
36
|
+
type: boolean
|
|
37
|
+
default: false
|
|
38
|
+
reflect: true
|
|
31
39
|
|
|
32
40
|
slots:
|
|
33
41
|
text:
|
|
@@ -78,6 +86,8 @@ related:
|
|
|
78
86
|
- TreeItem
|
|
79
87
|
|
|
80
88
|
a2ui:
|
|
89
|
+
allowedParents:
|
|
90
|
+
- List
|
|
81
91
|
rules:
|
|
82
92
|
- rule: 'Child of <list-ui> — one row of generic-list content.'
|
|
83
93
|
reason: 'Cluster contract.'
|
|
@@ -85,3 +95,5 @@ a2ui:
|
|
|
85
95
|
reason: 'Per-context list-row primitives — list-item is generic.'
|
|
86
96
|
- rule: 'Interactive list-items should have role=''button'' or be wrapped in <button-ui>; default is non-interactive.'
|
|
87
97
|
reason: 'Accessibility — semantic clarity.'
|
|
98
|
+
- rule: '[selected] reflects the row current-selection state under <list-ui selectable> — it is parent-managed (ADR-0056); drive selection via the parent''s [selectedKey], never [selected] directly.'
|
|
99
|
+
reason: 'Single source of truth stays on the parent, mirroring <segment-ui>.'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://adiaui.dev/a2ui/v0_9/components/List.json",
|
|
4
4
|
"title": "List",
|
|
5
|
-
"description": "Styled flat list with optional row dividers and icons — each row\nis a <list-item-ui> child. Selectable mode via [selectable] +\n[selectedKey] / `
|
|
5
|
+
"description": "Styled flat list with optional row dividers and icons — each row\nis a <list-item-ui> child. Selectable mode via [selectable] +\n[selectedKey] / `selection-change` event. Composes <icon-ui> for row\nleading glyphs. Distinct from <nav-ui> (sidebar navigation, route-aware),\n<tree-ui> (hierarchical with arbitrary nesting), and <table-ui>\n(tabular data with columns). Use list-ui for non-navigable,\nnon-tabular vertical rows — comment lists, file lists, message\nstreams outside chat-shell.\n",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"allOf": [
|
|
8
8
|
{
|
|
@@ -32,13 +32,18 @@
|
|
|
32
32
|
"default": false
|
|
33
33
|
},
|
|
34
34
|
"selectedKey": {
|
|
35
|
-
"description": "ID (or data-key) of the currently-selected child listitem.",
|
|
35
|
+
"description": "ID (or data-key) of the currently-selected child listitem — drives each child's [selected] reflected prop (ADR-0056, #1363 B7). Kept as `selectedKey` rather than renamed to `value` (segmented/tabs/ select's parent-selection prop name): list rows are identified by DOM id/data-key, not an option value, and ADR-0056 itself governs only the child's own [selected] state surface, not this naming — a cross-family rename has no ratifying ADR (B5/B9-shaped follow-up).",
|
|
36
36
|
"type": "string",
|
|
37
37
|
"default": ""
|
|
38
38
|
},
|
|
39
39
|
"spacing": {
|
|
40
|
-
"description": "Item spacing",
|
|
40
|
+
"description": "Item spacing scale — `sm`/`lg` modulate the per-item inset; `md` (default) is the base inset.",
|
|
41
41
|
"type": "string",
|
|
42
|
+
"enum": [
|
|
43
|
+
"sm",
|
|
44
|
+
"md",
|
|
45
|
+
"lg"
|
|
46
|
+
],
|
|
42
47
|
"default": "md"
|
|
43
48
|
}
|
|
44
49
|
},
|
|
@@ -84,6 +89,7 @@
|
|
|
84
89
|
"star"
|
|
85
90
|
],
|
|
86
91
|
"name": "UIList",
|
|
92
|
+
"parts": {},
|
|
87
93
|
"related": [
|
|
88
94
|
"ListItem",
|
|
89
95
|
"Tree",
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
* When `selectable`:
|
|
29
29
|
* - Children (role="listitem") become keyboard-focusable via roving tabindex.
|
|
30
30
|
* - Click selects the child; key is its id or data-key.
|
|
31
|
-
* - aria-selected
|
|
31
|
+
* - [selected] (ADR-0056, #1363 B7) and aria-selected are both set on
|
|
32
|
+
* all children — [selected] is the declared API state, aria-selected
|
|
33
|
+
* is wiring.
|
|
32
34
|
* - Fires `selection-change` with { key, previousKey }.
|
|
33
35
|
*/
|
|
34
36
|
|
|
@@ -73,6 +75,7 @@ export class UIList extends UIElement {
|
|
|
73
75
|
// (patient-visit rail's roving tabindex, gh#746).
|
|
74
76
|
if (this.#stampedSelection) {
|
|
75
77
|
for (const ch of this.#items()) {
|
|
78
|
+
ch.removeAttribute('selected');
|
|
76
79
|
ch.removeAttribute('aria-selected');
|
|
77
80
|
ch.removeAttribute('tabindex');
|
|
78
81
|
}
|
|
@@ -87,6 +90,11 @@ export class UIList extends UIElement {
|
|
|
87
90
|
for (const ch of items) {
|
|
88
91
|
const k = this.#keyOf(ch);
|
|
89
92
|
const selected = k && k === key;
|
|
93
|
+
// [selected] (ADR-0056, #1363 B7) is the declared, reflected API
|
|
94
|
+
// surface — parent-managed, mirrors <segment-ui>. aria-selected
|
|
95
|
+
// stays as-is: ARIA is wiring, not API.
|
|
96
|
+
if (selected) ch.setAttribute('selected', '');
|
|
97
|
+
else ch.removeAttribute('selected');
|
|
90
98
|
ch.setAttribute('aria-selected', selected ? 'true' : 'false');
|
|
91
99
|
ch.setAttribute('tabindex', selected ? '0' : '-1');
|
|
92
100
|
if (selected) focusTarget = ch;
|
|
@@ -173,6 +181,11 @@ export class UIListItem extends UIElement {
|
|
|
173
181
|
icon: { type: String, default: '', reflect: true },
|
|
174
182
|
text: { type: String, default: '', reflect: true },
|
|
175
183
|
description: { type: String, default: '', reflect: true },
|
|
184
|
+
// ADR-0056 (gh#1303, #1363 B7): declared reflected selection-item
|
|
185
|
+
// state, parent-managed — <list-ui>[selectable] stamps this instead
|
|
186
|
+
// of leaving selection visible only via aria-selected. Mirrors
|
|
187
|
+
// <segment-ui>'s [selected].
|
|
188
|
+
selected: { type: Boolean, default: false, reflect: true },
|
|
176
189
|
};
|
|
177
190
|
|
|
178
191
|
static template = () => null;
|
package/components/list/list.css
CHANGED
|
@@ -67,7 +67,18 @@
|
|
|
67
67
|
background: var(--a-bg-hover);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
/* [selected] is the declared, reflected state hook (ADR-0056, #1363 B7)
|
|
71
|
+
— list.class.js stamps it in lockstep with aria-selected; ARIA stays
|
|
72
|
+
wiring, not the style hook. Matched on the tag directly (not only
|
|
73
|
+
[role="listitem"]): UIList's adopt-or-stamp (gh#753) lets a
|
|
74
|
+
consumer-authored role (e.g. role="button") survive on <list-item-ui>
|
|
75
|
+
— #items() still selects/stamps it by TAG, so the style hook must
|
|
76
|
+
too, or a role-overridden selected row silently loses its selected
|
|
77
|
+
colors. The role fallback stays for non-list-item-ui children that
|
|
78
|
+
opt into role="listitem" themselves (list-ui also hosts "arbitrary
|
|
79
|
+
children", list.yaml). */
|
|
80
|
+
:scope[selectable] > list-item-ui[selected],
|
|
81
|
+
:scope[selectable] > [role="listitem"][selected] {
|
|
71
82
|
background: var(--list-bg-selected);
|
|
72
83
|
color: var(--list-fg-selected);
|
|
73
84
|
border-inline-start-color: var(--list-border-selected);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `<list-ui>` — Styled flat list with optional row dividers and icons — each row
|
|
3
3
|
is a <list-item-ui> child. Selectable mode via [selectable] +
|
|
4
|
-
[selectedKey] / `
|
|
5
|
-
glyphs. Distinct from <nav-ui> (sidebar navigation, route-aware),
|
|
4
|
+
[selectedKey] / `selection-change` event. Composes <icon-ui> for row
|
|
5
|
+
leading glyphs. Distinct from <nav-ui> (sidebar navigation, route-aware),
|
|
6
6
|
<tree-ui> (hierarchical with arbitrary nesting), and <table-ui>
|
|
7
7
|
(tabular data with columns). Use list-ui for non-navigable,
|
|
8
8
|
non-tabular vertical rows — comment lists, file lists, message
|
|
@@ -41,10 +41,10 @@ so content sits at the list-ui edges (canonical edge-to-edge style).
|
|
|
41
41
|
divider: boolean;
|
|
42
42
|
/** Enable selection on child listitems (roving tabindex + aria-selected). */
|
|
43
43
|
selectable: boolean;
|
|
44
|
-
/** ID (or data-key) of the currently-selected child listitem. */
|
|
44
|
+
/** ID (or data-key) of the currently-selected child listitem — drives each child's [selected] reflected prop (ADR-0056, #1363 B7). Kept as `selectedKey` rather than renamed to `value` (segmented/tabs/ select's parent-selection prop name): list rows are identified by DOM id/data-key, not an option value, and ADR-0056 itself governs only the child's own [selected] state surface, not this naming — a cross-family rename has no ratifying ADR (B5/B9-shaped follow-up). */
|
|
45
45
|
selectedKey: string;
|
|
46
|
-
/** Item spacing */
|
|
47
|
-
spacing:
|
|
46
|
+
/** Item spacing scale — `sm`/`lg` modulate the per-item inset; `md` (default) is the base inset. */
|
|
47
|
+
spacing: 'sm' | 'md' | 'lg';
|
|
48
48
|
|
|
49
49
|
addEventListener(type: 'selection-change', listener: (ev: ListSelectionChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
50
50
|
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
@@ -59,6 +59,8 @@ export class UIListItem extends UIElement {
|
|
|
59
59
|
description: string;
|
|
60
60
|
/** Optional leading icon name (Phosphor). */
|
|
61
61
|
icon: string;
|
|
62
|
+
/** Whether this row is the current selection. Managed by the parent <list-ui>[selectable] — don't set directly; the parent's [selectedKey] is the single source of truth (ADR-0056). */
|
|
63
|
+
selected: boolean;
|
|
62
64
|
/** Primary text. Renders as semibold inline body. */
|
|
63
65
|
text: string;
|
|
64
66
|
}
|
|
@@ -10,8 +10,8 @@ version: 1
|
|
|
10
10
|
description: |
|
|
11
11
|
Styled flat list with optional row dividers and icons — each row
|
|
12
12
|
is a <list-item-ui> child. Selectable mode via [selectable] +
|
|
13
|
-
[selectedKey] / `
|
|
14
|
-
glyphs. Distinct from <nav-ui> (sidebar navigation, route-aware),
|
|
13
|
+
[selectedKey] / `selection-change` event. Composes <icon-ui> for row
|
|
14
|
+
leading glyphs. Distinct from <nav-ui> (sidebar navigation, route-aware),
|
|
15
15
|
<tree-ui> (hierarchical with arbitrary nesting), and <table-ui>
|
|
16
16
|
(tabular data with columns). Use list-ui for non-navigable,
|
|
17
17
|
non-tabular vertical rows — comment lists, file lists, message
|
|
@@ -40,14 +40,22 @@ props:
|
|
|
40
40
|
type: boolean
|
|
41
41
|
default: false
|
|
42
42
|
selectedKey:
|
|
43
|
-
description:
|
|
43
|
+
description: >-
|
|
44
|
+
ID (or data-key) of the currently-selected child listitem — drives
|
|
45
|
+
each child's [selected] reflected prop (ADR-0056, #1363 B7). Kept
|
|
46
|
+
as `selectedKey` rather than renamed to `value` (segmented/tabs/
|
|
47
|
+
select's parent-selection prop name): list rows are identified by
|
|
48
|
+
DOM id/data-key, not an option value, and ADR-0056 itself governs
|
|
49
|
+
only the child's own [selected] state surface, not this naming —
|
|
50
|
+
a cross-family rename has no ratifying ADR (B5/B9-shaped follow-up).
|
|
44
51
|
type: string
|
|
45
52
|
default: ""
|
|
46
53
|
attribute: selected-key
|
|
47
54
|
spacing:
|
|
48
|
-
description: Item spacing
|
|
55
|
+
description: Item spacing scale — `sm`/`lg` modulate the per-item inset; `md` (default) is the base inset.
|
|
49
56
|
type: string
|
|
50
57
|
default: md
|
|
58
|
+
enum: [sm, md, lg]
|
|
51
59
|
events:
|
|
52
60
|
selection-change:
|
|
53
61
|
description: "Fired on selection-change."
|
|
@@ -68,10 +76,12 @@ a2ui:
|
|
|
68
76
|
rules:
|
|
69
77
|
- rule: 'Generic vertical list container — hosts <list-item-ui> or arbitrary children.'
|
|
70
78
|
reason: 'Lowest-friction list primitive.'
|
|
71
|
-
- rule: '
|
|
72
|
-
reason: '
|
|
79
|
+
- rule: 'list-ui supports single selection natively via [selectable] + [selectedKey] (ADR-0056, #1363 B7) — the parent stamps the matching <list-item-ui>''s [selected] + aria-selected and fires selection-change. For route-aware navigation use <nav-ui> instead; for a popover action menu use <menu-ui> instead — both have different interaction contracts than a plain selectable list.'
|
|
80
|
+
reason: 'list-ui selectable is the supported non-navigation, non-menu selection surface; nav-ui/menu-ui remain the right choice for their own contracts.'
|
|
73
81
|
- rule: 'For data-grid / sortable / sticky-header needs use <table-ui> instead.'
|
|
74
82
|
reason: 'Table has different semantics.'
|
|
83
|
+
- rule: 'list-ui carries no fire-and-forget "action" mode. [selectable] keeps a persisted single selection (`selectedKey` / `aria-selected`, roving tabindex); for rows that activate immediately with no persisted selection state use <action-list-ui> + <action-item-ui> instead.'
|
|
84
|
+
reason: 'Container-level decision (#1363 C5): kept separate rather than absorbed as a mode axis — stateful selection and fire-and-forget activation are genuinely different interaction contracts, and list-item-ui''s selection state is under active convergence elsewhere (#1379); rationale in gh#1374 Findings.'
|
|
75
85
|
anti_patterns: []
|
|
76
86
|
examples:
|
|
77
87
|
- name: basic-list
|