@furystack/shades-common-components 13.5.0 → 15.0.0
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 +109 -0
- package/README.md +3 -3
- package/esm/components/accordion/accordion-item.d.ts.map +1 -1
- package/esm/components/accordion/accordion-item.js +7 -10
- package/esm/components/accordion/accordion-item.js.map +1 -1
- package/esm/components/accordion/accordion.d.ts +7 -0
- package/esm/components/accordion/accordion.d.ts.map +1 -1
- package/esm/components/accordion/accordion.js +5 -2
- package/esm/components/accordion/accordion.js.map +1 -1
- package/esm/components/accordion/accordion.spec.js +91 -50
- package/esm/components/accordion/accordion.spec.js.map +1 -1
- package/esm/components/alert.js +1 -1
- package/esm/components/alert.js.map +1 -1
- package/esm/components/app-bar-link.js +1 -1
- package/esm/components/app-bar-link.js.map +1 -1
- package/esm/components/app-bar.js +1 -1
- package/esm/components/app-bar.js.map +1 -1
- package/esm/components/avatar.js +1 -1
- package/esm/components/avatar.js.map +1 -1
- package/esm/components/badge.js +1 -1
- package/esm/components/badge.js.map +1 -1
- package/esm/components/breadcrumb.js +1 -1
- package/esm/components/breadcrumb.js.map +1 -1
- package/esm/components/breadcrumb.spec.js +3 -3
- package/esm/components/breadcrumb.spec.js.map +1 -1
- package/esm/components/button-group.js +4 -4
- package/esm/components/button-group.js.map +1 -1
- package/esm/components/button.js +1 -1
- package/esm/components/button.js.map +1 -1
- package/esm/components/button.spec.js +1 -1
- package/esm/components/button.spec.js.map +1 -1
- package/esm/components/cache-view.js +1 -1
- package/esm/components/cache-view.js.map +1 -1
- package/esm/components/cache-view.spec.js +2 -2
- package/esm/components/cache-view.spec.js.map +1 -1
- package/esm/components/card.js +5 -5
- package/esm/components/card.js.map +1 -1
- package/esm/components/carousel.js +2 -2
- package/esm/components/carousel.js.map +1 -1
- package/esm/components/chip.d.ts.map +1 -1
- package/esm/components/chip.js +5 -3
- package/esm/components/chip.js.map +1 -1
- package/esm/components/chip.spec.js +42 -0
- package/esm/components/chip.spec.js.map +1 -1
- package/esm/components/circular-progress.d.ts +2 -4
- package/esm/components/circular-progress.d.ts.map +1 -1
- package/esm/components/circular-progress.js +3 -6
- package/esm/components/circular-progress.js.map +1 -1
- package/esm/components/circular-progress.spec.js +19 -14
- package/esm/components/circular-progress.spec.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +1 -1
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -1
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +15 -2
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/command-palette/index.spec.js +78 -33
- package/esm/components/command-palette/index.spec.js.map +1 -1
- package/esm/components/context-menu/context-menu-item.js +1 -1
- package/esm/components/context-menu/context-menu-item.js.map +1 -1
- package/esm/components/context-menu/context-menu.js +1 -1
- package/esm/components/context-menu/context-menu.js.map +1 -1
- package/esm/components/data-grid/body.js +1 -1
- package/esm/components/data-grid/body.js.map +1 -1
- package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid-row.js +19 -3
- package/esm/components/data-grid/data-grid-row.js.map +1 -1
- package/esm/components/data-grid/data-grid.d.ts +12 -2
- package/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid.js +33 -13
- package/esm/components/data-grid/data-grid.js.map +1 -1
- package/esm/components/data-grid/data-grid.spec.js +170 -90
- package/esm/components/data-grid/data-grid.spec.js.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/boolean-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.js +4 -4
- package/esm/components/data-grid/filters/boolean-filter.js.map +1 -1
- package/esm/components/data-grid/filters/boolean-filter.spec.js +18 -17
- package/esm/components/data-grid/filters/boolean-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/date-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/date-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/date-filter.js +6 -6
- package/esm/components/data-grid/filters/date-filter.js.map +1 -1
- package/esm/components/data-grid/filters/date-filter.spec.js +26 -21
- package/esm/components/data-grid/filters/date-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/enum-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.js +5 -5
- package/esm/components/data-grid/filters/enum-filter.js.map +1 -1
- package/esm/components/data-grid/filters/enum-filter.spec.js +21 -19
- package/esm/components/data-grid/filters/enum-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/filter-dropdown.js +1 -1
- package/esm/components/data-grid/filters/filter-dropdown.js.map +1 -1
- package/esm/components/data-grid/filters/number-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/number-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/number-filter.js +5 -5
- package/esm/components/data-grid/filters/number-filter.js.map +1 -1
- package/esm/components/data-grid/filters/number-filter.spec.js +23 -21
- package/esm/components/data-grid/filters/number-filter.spec.js.map +1 -1
- package/esm/components/data-grid/filters/string-filter.d.ts +2 -2
- package/esm/components/data-grid/filters/string-filter.d.ts.map +1 -1
- package/esm/components/data-grid/filters/string-filter.js +5 -5
- package/esm/components/data-grid/filters/string-filter.js.map +1 -1
- package/esm/components/data-grid/filters/string-filter.spec.js +21 -19
- package/esm/components/data-grid/filters/string-filter.spec.js.map +1 -1
- package/esm/components/data-grid/footer.d.ts +2 -2
- package/esm/components/data-grid/footer.d.ts.map +1 -1
- package/esm/components/data-grid/footer.js +8 -13
- package/esm/components/data-grid/footer.js.map +1 -1
- package/esm/components/data-grid/footer.spec.js +38 -27
- package/esm/components/data-grid/footer.spec.js.map +1 -1
- package/esm/components/data-grid/header.d.ts +6 -6
- package/esm/components/data-grid/header.d.ts.map +1 -1
- package/esm/components/data-grid/header.js +16 -17
- package/esm/components/data-grid/header.js.map +1 -1
- package/esm/components/data-grid/header.spec.js +66 -60
- package/esm/components/data-grid/header.spec.js.map +1 -1
- package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
- package/esm/components/data-grid/selection-cell.js +2 -2
- package/esm/components/data-grid/selection-cell.js.map +1 -1
- package/esm/components/dialog.d.ts +11 -0
- package/esm/components/dialog.d.ts.map +1 -1
- package/esm/components/dialog.js +3 -3
- package/esm/components/dialog.js.map +1 -1
- package/esm/components/dialog.spec.js +54 -2
- package/esm/components/dialog.spec.js.map +1 -1
- package/esm/components/divider.js +1 -1
- package/esm/components/divider.js.map +1 -1
- package/esm/components/drawer/drawer-toggle-button.js +1 -1
- package/esm/components/drawer/drawer-toggle-button.js.map +1 -1
- package/esm/components/drawer/index.js +1 -1
- package/esm/components/drawer/index.js.map +1 -1
- package/esm/components/dropdown.d.ts.map +1 -1
- package/esm/components/dropdown.js +2 -2
- package/esm/components/dropdown.js.map +1 -1
- package/esm/components/dropdown.spec.js +8 -0
- package/esm/components/dropdown.spec.js.map +1 -1
- package/esm/components/fab.js +1 -1
- package/esm/components/fab.js.map +1 -1
- package/esm/components/form.js +1 -1
- package/esm/components/form.js.map +1 -1
- package/esm/components/grid.js +1 -1
- package/esm/components/grid.js.map +1 -1
- package/esm/components/icons/icon.js +1 -1
- package/esm/components/icons/icon.js.map +1 -1
- package/esm/components/image.d.ts.map +1 -1
- package/esm/components/image.js +17 -8
- package/esm/components/image.js.map +1 -1
- package/esm/components/image.spec.js +60 -0
- package/esm/components/image.spec.js.map +1 -1
- package/esm/components/inputs/autocomplete.js +1 -1
- package/esm/components/inputs/autocomplete.js.map +1 -1
- package/esm/components/inputs/checkbox.d.ts.map +1 -1
- package/esm/components/inputs/checkbox.js +2 -1
- package/esm/components/inputs/checkbox.js.map +1 -1
- package/esm/components/inputs/checkbox.spec.js +1 -1
- package/esm/components/inputs/checkbox.spec.js.map +1 -1
- package/esm/components/inputs/input-number.js +1 -1
- package/esm/components/inputs/input-number.js.map +1 -1
- package/esm/components/inputs/input-number.spec.js +1 -1
- package/esm/components/inputs/input-number.spec.js.map +1 -1
- package/esm/components/inputs/input.js +1 -1
- package/esm/components/inputs/input.js.map +1 -1
- package/esm/components/inputs/input.spec.js +1 -1
- package/esm/components/inputs/input.spec.js.map +1 -1
- package/esm/components/inputs/radio-group.js +1 -1
- package/esm/components/inputs/radio-group.js.map +1 -1
- package/esm/components/inputs/radio-group.spec.js +1 -1
- package/esm/components/inputs/radio-group.spec.js.map +1 -1
- package/esm/components/inputs/radio.d.ts.map +1 -1
- package/esm/components/inputs/radio.js +2 -1
- package/esm/components/inputs/radio.js.map +1 -1
- package/esm/components/inputs/radio.spec.js +1 -1
- package/esm/components/inputs/radio.spec.js.map +1 -1
- package/esm/components/inputs/select.js +1 -1
- package/esm/components/inputs/select.js.map +1 -1
- package/esm/components/inputs/slider.d.ts.map +1 -1
- package/esm/components/inputs/slider.js +2 -1
- package/esm/components/inputs/slider.js.map +1 -1
- package/esm/components/inputs/switch.d.ts.map +1 -1
- package/esm/components/inputs/switch.js +2 -1
- package/esm/components/inputs/switch.js.map +1 -1
- package/esm/components/inputs/switch.spec.js +1 -1
- package/esm/components/inputs/switch.spec.js.map +1 -1
- package/esm/components/inputs/text-area.js +1 -1
- package/esm/components/inputs/text-area.js.map +1 -1
- package/esm/components/inputs/text-area.spec.js +1 -1
- package/esm/components/inputs/text-area.spec.js.map +1 -1
- package/esm/components/linear-progress.d.ts +2 -4
- package/esm/components/linear-progress.d.ts.map +1 -1
- package/esm/components/linear-progress.js +3 -6
- package/esm/components/linear-progress.js.map +1 -1
- package/esm/components/linear-progress.spec.js +21 -18
- package/esm/components/linear-progress.spec.js.map +1 -1
- package/esm/components/list/list-item.d.ts.map +1 -1
- package/esm/components/list/list-item.js +22 -6
- package/esm/components/list/list-item.js.map +1 -1
- package/esm/components/list/list.d.ts +7 -0
- package/esm/components/list/list.d.ts.map +1 -1
- package/esm/components/list/list.js +29 -9
- package/esm/components/list/list.js.map +1 -1
- package/esm/components/list/list.spec.js +117 -23
- package/esm/components/list/list.spec.js.map +1 -1
- package/esm/components/loader.js +1 -1
- package/esm/components/loader.js.map +1 -1
- package/esm/components/loader.spec.js +1 -1
- package/esm/components/loader.spec.js.map +1 -1
- package/esm/components/markdown/markdown-display.d.ts.map +1 -1
- package/esm/components/markdown/markdown-display.js +12 -2
- package/esm/components/markdown/markdown-display.js.map +1 -1
- package/esm/components/markdown/markdown-display.spec.js +98 -1
- package/esm/components/markdown/markdown-display.spec.js.map +1 -1
- package/esm/components/markdown/markdown-editor.js +1 -1
- package/esm/components/markdown/markdown-editor.js.map +1 -1
- package/esm/components/markdown/markdown-editor.spec.js +88 -1
- package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
- package/esm/components/markdown/markdown-input.js +1 -1
- package/esm/components/markdown/markdown-input.js.map +1 -1
- package/esm/components/markdown/markdown-input.spec.js +1 -1
- package/esm/components/markdown/markdown-input.spec.js.map +1 -1
- package/esm/components/menu/menu.js +2 -2
- package/esm/components/menu/menu.js.map +1 -1
- package/esm/components/modal.d.ts +10 -0
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +25 -5
- package/esm/components/modal.js.map +1 -1
- package/esm/components/modal.spec.js +89 -4
- package/esm/components/modal.spec.js.map +1 -1
- package/esm/components/noty-list.js +2 -2
- package/esm/components/noty-list.js.map +1 -1
- package/esm/components/page-container/index.js +1 -1
- package/esm/components/page-container/index.js.map +1 -1
- package/esm/components/page-container/page-header.js +1 -1
- package/esm/components/page-container/page-header.js.map +1 -1
- package/esm/components/page-layout/index.js +2 -2
- package/esm/components/page-layout/index.js.map +1 -1
- package/esm/components/page-layout/index.spec.js +14 -0
- package/esm/components/page-layout/index.spec.js.map +1 -1
- package/esm/components/pagination.js +1 -1
- package/esm/components/pagination.js.map +1 -1
- package/esm/components/paper.js +1 -1
- package/esm/components/paper.js.map +1 -1
- package/esm/components/rating.d.ts.map +1 -1
- package/esm/components/rating.js +29 -22
- package/esm/components/rating.js.map +1 -1
- package/esm/components/rating.spec.js +152 -5
- package/esm/components/rating.spec.js.map +1 -1
- package/esm/components/result.js +1 -1
- package/esm/components/result.js.map +1 -1
- package/esm/components/skeleton.js +1 -1
- package/esm/components/skeleton.js.map +1 -1
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +15 -2
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/index.spec.js +99 -44
- package/esm/components/suggest/index.spec.js.map +1 -1
- package/esm/components/suggest/suggest-input.js +1 -1
- package/esm/components/suggest/suggest-input.js.map +1 -1
- package/esm/components/suggest/suggest-input.spec.js +1 -1
- package/esm/components/suggest/suggest-input.spec.js.map +1 -1
- package/esm/components/suggest/suggestion-list.js +1 -1
- package/esm/components/suggest/suggestion-list.js.map +1 -1
- package/esm/components/suggest/suggestion-list.spec.js +1 -1
- package/esm/components/suggest/suggestion-list.spec.js.map +1 -1
- package/esm/components/tabs.d.ts.map +1 -1
- package/esm/components/tabs.js +6 -2
- package/esm/components/tabs.js.map +1 -1
- package/esm/components/timeline.js +2 -2
- package/esm/components/timeline.js.map +1 -1
- package/esm/components/tooltip.js +1 -1
- package/esm/components/tooltip.js.map +1 -1
- package/esm/components/tree/tree-item.d.ts.map +1 -1
- package/esm/components/tree/tree-item.js +19 -6
- package/esm/components/tree/tree-item.js.map +1 -1
- package/esm/components/tree/tree.d.ts +7 -0
- package/esm/components/tree/tree.d.ts.map +1 -1
- package/esm/components/tree/tree.js +13 -4
- package/esm/components/tree/tree.js.map +1 -1
- package/esm/components/tree/tree.spec.js +64 -2
- package/esm/components/tree/tree.spec.js.map +1 -1
- package/esm/components/typography.js +1 -1
- package/esm/components/typography.js.map +1 -1
- package/esm/components/wizard/index.js +1 -1
- package/esm/components/wizard/index.js.map +1 -1
- package/esm/components/wizard/index.spec.js +3 -3
- package/esm/components/wizard/index.spec.js.map +1 -1
- package/esm/services/collection-service.d.ts +9 -0
- package/esm/services/collection-service.d.ts.map +1 -1
- package/esm/services/collection-service.js +33 -11
- package/esm/services/collection-service.js.map +1 -1
- package/esm/services/collection-service.spec.js +33 -24
- package/esm/services/collection-service.spec.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts +7 -0
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +23 -0
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.js +1 -0
- package/esm/services/css-variable-theme.spec.js.map +1 -1
- package/esm/services/list-service.d.ts +9 -0
- package/esm/services/list-service.d.ts.map +1 -1
- package/esm/services/list-service.js +13 -13
- package/esm/services/list-service.js.map +1 -1
- package/esm/services/list-service.spec.js +13 -33
- package/esm/services/list-service.spec.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +3 -0
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/tree-service.d.ts.map +1 -1
- package/esm/services/tree-service.js +5 -9
- package/esm/services/tree-service.js.map +1 -1
- package/esm/services/tree-service.spec.js +12 -9
- package/esm/services/tree-service.spec.js.map +1 -1
- package/esm/themes/architect-theme.d.ts +1 -0
- package/esm/themes/architect-theme.d.ts.map +1 -1
- package/esm/themes/architect-theme.js +1 -0
- package/esm/themes/architect-theme.js.map +1 -1
- package/esm/themes/auditore-theme.d.ts +1 -0
- package/esm/themes/auditore-theme.d.ts.map +1 -1
- package/esm/themes/auditore-theme.js +1 -0
- package/esm/themes/auditore-theme.js.map +1 -1
- package/esm/themes/black-mesa-theme.d.ts +1 -0
- package/esm/themes/black-mesa-theme.d.ts.map +1 -1
- package/esm/themes/black-mesa-theme.js +1 -0
- package/esm/themes/black-mesa-theme.js.map +1 -1
- package/esm/themes/chieftain-theme.d.ts +1 -0
- package/esm/themes/chieftain-theme.d.ts.map +1 -1
- package/esm/themes/chieftain-theme.js +1 -0
- package/esm/themes/chieftain-theme.js.map +1 -1
- package/esm/themes/default-dark-theme.d.ts +1 -0
- package/esm/themes/default-dark-theme.d.ts.map +1 -1
- package/esm/themes/default-dark-theme.js +1 -0
- package/esm/themes/default-dark-theme.js.map +1 -1
- package/esm/themes/default-light-theme.d.ts +1 -0
- package/esm/themes/default-light-theme.d.ts.map +1 -1
- package/esm/themes/default-light-theme.js +1 -0
- package/esm/themes/default-light-theme.js.map +1 -1
- package/esm/themes/dragonborn-theme.d.ts +1 -0
- package/esm/themes/dragonborn-theme.d.ts.map +1 -1
- package/esm/themes/dragonborn-theme.js +1 -0
- package/esm/themes/dragonborn-theme.js.map +1 -1
- package/esm/themes/hawkins-theme.d.ts +1 -0
- package/esm/themes/hawkins-theme.d.ts.map +1 -1
- package/esm/themes/hawkins-theme.js +1 -0
- package/esm/themes/hawkins-theme.js.map +1 -1
- package/esm/themes/jedi-theme.d.ts +1 -0
- package/esm/themes/jedi-theme.d.ts.map +1 -1
- package/esm/themes/jedi-theme.js +1 -0
- package/esm/themes/jedi-theme.js.map +1 -1
- package/esm/themes/neon-runner-theme.d.ts +1 -0
- package/esm/themes/neon-runner-theme.d.ts.map +1 -1
- package/esm/themes/neon-runner-theme.js +1 -0
- package/esm/themes/neon-runner-theme.js.map +1 -1
- package/esm/themes/paladin-theme.d.ts +1 -0
- package/esm/themes/paladin-theme.d.ts.map +1 -1
- package/esm/themes/paladin-theme.js +1 -0
- package/esm/themes/paladin-theme.js.map +1 -1
- package/esm/themes/plumber-theme.d.ts +1 -0
- package/esm/themes/plumber-theme.d.ts.map +1 -1
- package/esm/themes/plumber-theme.js +1 -0
- package/esm/themes/plumber-theme.js.map +1 -1
- package/esm/themes/replicant-theme.d.ts +1 -0
- package/esm/themes/replicant-theme.d.ts.map +1 -1
- package/esm/themes/replicant-theme.js +1 -0
- package/esm/themes/replicant-theme.js.map +1 -1
- package/esm/themes/sandworm-theme.d.ts +1 -0
- package/esm/themes/sandworm-theme.d.ts.map +1 -1
- package/esm/themes/sandworm-theme.js +1 -0
- package/esm/themes/sandworm-theme.js.map +1 -1
- package/esm/themes/shadow-broker-theme.d.ts +1 -0
- package/esm/themes/shadow-broker-theme.d.ts.map +1 -1
- package/esm/themes/shadow-broker-theme.js +1 -0
- package/esm/themes/shadow-broker-theme.js.map +1 -1
- package/esm/themes/sith-theme.d.ts +1 -0
- package/esm/themes/sith-theme.d.ts.map +1 -1
- package/esm/themes/sith-theme.js +1 -0
- package/esm/themes/sith-theme.js.map +1 -1
- package/esm/themes/vault-dweller-theme.d.ts +1 -0
- package/esm/themes/vault-dweller-theme.d.ts.map +1 -1
- package/esm/themes/vault-dweller-theme.js +1 -0
- package/esm/themes/vault-dweller-theme.js.map +1 -1
- package/esm/themes/wild-hunt-theme.d.ts +1 -0
- package/esm/themes/wild-hunt-theme.d.ts.map +1 -1
- package/esm/themes/wild-hunt-theme.js +1 -0
- package/esm/themes/wild-hunt-theme.js.map +1 -1
- package/esm/themes/xenomorph-theme.d.ts +1 -0
- package/esm/themes/xenomorph-theme.d.ts.map +1 -1
- package/esm/themes/xenomorph-theme.js +1 -0
- package/esm/themes/xenomorph-theme.js.map +1 -1
- package/package.json +3 -3
- package/src/components/accordion/accordion-item.tsx +10 -15
- package/src/components/accordion/accordion.spec.tsx +134 -79
- package/src/components/accordion/accordion.tsx +14 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/app-bar-link.tsx +1 -1
- package/src/components/app-bar.tsx +1 -1
- package/src/components/avatar.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/breadcrumb.spec.tsx +3 -3
- package/src/components/breadcrumb.tsx +1 -1
- package/src/components/button-group.tsx +4 -4
- package/src/components/button.spec.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/cache-view.spec.tsx +2 -2
- package/src/components/cache-view.tsx +3 -3
- package/src/components/card.tsx +5 -5
- package/src/components/carousel.tsx +2 -2
- package/src/components/chip.spec.tsx +64 -0
- package/src/components/chip.tsx +5 -2
- package/src/components/circular-progress.spec.tsx +20 -14
- package/src/components/circular-progress.tsx +5 -11
- package/src/components/command-palette/command-palette-input.tsx +1 -1
- package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +1 -1
- package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
- package/src/components/command-palette/index.spec.tsx +95 -33
- package/src/components/command-palette/index.tsx +16 -4
- package/src/components/context-menu/context-menu-item.tsx +1 -1
- package/src/components/context-menu/context-menu.tsx +1 -1
- package/src/components/data-grid/body.tsx +1 -1
- package/src/components/data-grid/data-grid-row.tsx +21 -3
- package/src/components/data-grid/data-grid.spec.tsx +246 -92
- package/src/components/data-grid/data-grid.tsx +52 -21
- package/src/components/data-grid/filters/boolean-filter.spec.tsx +29 -18
- package/src/components/data-grid/filters/boolean-filter.tsx +6 -6
- package/src/components/data-grid/filters/date-filter.spec.tsx +35 -22
- package/src/components/data-grid/filters/date-filter.tsx +8 -8
- package/src/components/data-grid/filters/enum-filter.spec.tsx +35 -20
- package/src/components/data-grid/filters/enum-filter.tsx +7 -7
- package/src/components/data-grid/filters/filter-dropdown.tsx +1 -1
- package/src/components/data-grid/filters/number-filter.spec.tsx +32 -22
- package/src/components/data-grid/filters/number-filter.tsx +7 -7
- package/src/components/data-grid/filters/string-filter.spec.tsx +32 -20
- package/src/components/data-grid/filters/string-filter.tsx +7 -7
- package/src/components/data-grid/footer.spec.tsx +79 -31
- package/src/components/data-grid/footer.tsx +10 -15
- package/src/components/data-grid/header.spec.tsx +152 -68
- package/src/components/data-grid/header.tsx +64 -27
- package/src/components/data-grid/selection-cell.tsx +2 -1
- package/src/components/dialog.spec.tsx +77 -2
- package/src/components/dialog.tsx +15 -2
- package/src/components/divider.tsx +1 -1
- package/src/components/drawer/drawer-toggle-button.tsx +1 -1
- package/src/components/drawer/index.tsx +1 -1
- package/src/components/dropdown.spec.tsx +9 -0
- package/src/components/dropdown.tsx +2 -1
- package/src/components/fab.tsx +1 -1
- package/src/components/form.tsx +1 -1
- package/src/components/grid.tsx +1 -1
- package/src/components/icons/icon.tsx +1 -1
- package/src/components/image.spec.tsx +82 -0
- package/src/components/image.tsx +18 -9
- package/src/components/inputs/autocomplete.tsx +1 -1
- package/src/components/inputs/checkbox.spec.tsx +1 -1
- package/src/components/inputs/checkbox.tsx +2 -1
- package/src/components/inputs/input-number.spec.tsx +1 -1
- package/src/components/inputs/input-number.tsx +1 -1
- package/src/components/inputs/input.spec.tsx +1 -1
- package/src/components/inputs/input.tsx +1 -1
- package/src/components/inputs/radio-group.spec.tsx +1 -1
- package/src/components/inputs/radio-group.tsx +1 -1
- package/src/components/inputs/radio.spec.tsx +1 -1
- package/src/components/inputs/radio.tsx +2 -1
- package/src/components/inputs/select.tsx +1 -1
- package/src/components/inputs/slider.tsx +2 -1
- package/src/components/inputs/switch.spec.tsx +1 -1
- package/src/components/inputs/switch.tsx +2 -1
- package/src/components/inputs/text-area.spec.tsx +1 -1
- package/src/components/inputs/text-area.tsx +1 -1
- package/src/components/linear-progress.spec.tsx +22 -18
- package/src/components/linear-progress.tsx +5 -11
- package/src/components/list/list-item.tsx +23 -5
- package/src/components/list/list.spec.tsx +165 -32
- package/src/components/list/list.tsx +38 -11
- package/src/components/loader.spec.tsx +1 -1
- package/src/components/loader.tsx +1 -1
- package/src/components/markdown/markdown-display.spec.tsx +133 -1
- package/src/components/markdown/markdown-display.tsx +13 -2
- package/src/components/markdown/markdown-editor.spec.tsx +124 -1
- package/src/components/markdown/markdown-editor.tsx +1 -1
- package/src/components/markdown/markdown-input.spec.tsx +1 -1
- package/src/components/markdown/markdown-input.tsx +1 -1
- package/src/components/menu/menu.tsx +2 -2
- package/src/components/modal.spec.tsx +127 -4
- package/src/components/modal.tsx +42 -4
- package/src/components/noty-list.tsx +2 -2
- package/src/components/page-container/index.tsx +1 -1
- package/src/components/page-container/page-header.tsx +1 -1
- package/src/components/page-layout/index.spec.tsx +20 -0
- package/src/components/page-layout/index.tsx +2 -2
- package/src/components/pagination.tsx +1 -1
- package/src/components/paper.tsx +1 -1
- package/src/components/rating.spec.tsx +200 -5
- package/src/components/rating.tsx +29 -23
- package/src/components/result.tsx +1 -1
- package/src/components/skeleton.tsx +1 -1
- package/src/components/suggest/index.spec.tsx +148 -44
- package/src/components/suggest/index.tsx +16 -3
- package/src/components/suggest/suggest-input.spec.tsx +1 -1
- package/src/components/suggest/suggest-input.tsx +1 -1
- package/src/components/suggest/suggestion-list.spec.tsx +1 -1
- package/src/components/suggest/suggestion-list.tsx +1 -1
- package/src/components/tabs.tsx +6 -2
- package/src/components/timeline.tsx +2 -2
- package/src/components/tooltip.tsx +1 -1
- package/src/components/tree/tree-item.tsx +20 -5
- package/src/components/tree/tree.spec.tsx +101 -2
- package/src/components/tree/tree.tsx +22 -4
- package/src/components/typography.tsx +1 -1
- package/src/components/wizard/index.spec.tsx +3 -3
- package/src/components/wizard/index.tsx +1 -1
- package/src/services/collection-service.spec.ts +33 -24
- package/src/services/collection-service.ts +35 -13
- package/src/services/css-variable-theme.spec.ts +1 -0
- package/src/services/css-variable-theme.ts +25 -0
- package/src/services/list-service.spec.ts +13 -42
- package/src/services/list-service.ts +15 -13
- package/src/services/theme-provider-service.ts +2 -0
- package/src/services/tree-service.spec.ts +12 -9
- package/src/services/tree-service.ts +5 -8
- package/src/themes/architect-theme.ts +1 -0
- package/src/themes/auditore-theme.ts +1 -0
- package/src/themes/black-mesa-theme.ts +1 -0
- package/src/themes/chieftain-theme.ts +1 -0
- package/src/themes/default-dark-theme.ts +1 -0
- package/src/themes/default-light-theme.ts +1 -0
- package/src/themes/dragonborn-theme.ts +1 -0
- package/src/themes/hawkins-theme.ts +1 -0
- package/src/themes/jedi-theme.ts +1 -0
- package/src/themes/neon-runner-theme.ts +1 -0
- package/src/themes/paladin-theme.ts +1 -0
- package/src/themes/plumber-theme.ts +1 -0
- package/src/themes/replicant-theme.ts +1 -0
- package/src/themes/sandworm-theme.ts +1 -0
- package/src/themes/shadow-broker-theme.ts +1 -0
- package/src/themes/sith-theme.ts +1 -0
- package/src/themes/vault-dweller-theme.ts +1 -0
- package/src/themes/wild-hunt-theme.ts +1 -0
- package/src/themes/xenomorph-theme.ts +1 -0
|
@@ -501,8 +501,107 @@ describe('Tree', () => {
|
|
|
501
501
|
})
|
|
502
502
|
})
|
|
503
503
|
|
|
504
|
+
describe('item spatial navigation attributes', () => {
|
|
505
|
+
it('should set data-spatial-nav-target on tree items', async () => {
|
|
506
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
507
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
508
|
+
const treeData = createTreeData()
|
|
509
|
+
const service = createTestService()
|
|
510
|
+
|
|
511
|
+
service.rootItems.setValue(treeData)
|
|
512
|
+
service.updateFlattenedNodes()
|
|
513
|
+
|
|
514
|
+
initializeShadeRoot({
|
|
515
|
+
injector,
|
|
516
|
+
rootElement,
|
|
517
|
+
jsxElement: (
|
|
518
|
+
<Tree<TestNode>
|
|
519
|
+
rootItems={treeData}
|
|
520
|
+
treeService={service}
|
|
521
|
+
renderItem={(node) => <span>{node.name}</span>}
|
|
522
|
+
/>
|
|
523
|
+
),
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
await flushUpdates()
|
|
527
|
+
|
|
528
|
+
const items = document.querySelectorAll('shade-tree-item')
|
|
529
|
+
for (const item of items) {
|
|
530
|
+
expect(item.hasAttribute('data-spatial-nav-target')).toBe(true)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
service[Symbol.dispose]()
|
|
534
|
+
})
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
it('should set tabIndex 0 on focused item and -1 on others', async () => {
|
|
538
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
539
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
540
|
+
const treeData = createTreeData()
|
|
541
|
+
const service = createTestService()
|
|
542
|
+
|
|
543
|
+
service.rootItems.setValue(treeData)
|
|
544
|
+
service.updateFlattenedNodes()
|
|
545
|
+
service.focusedItem.setValue(treeData[1])
|
|
546
|
+
|
|
547
|
+
initializeShadeRoot({
|
|
548
|
+
injector,
|
|
549
|
+
rootElement,
|
|
550
|
+
jsxElement: (
|
|
551
|
+
<Tree<TestNode>
|
|
552
|
+
rootItems={treeData}
|
|
553
|
+
treeService={service}
|
|
554
|
+
renderItem={(node) => <span>{node.name}</span>}
|
|
555
|
+
/>
|
|
556
|
+
),
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
await flushUpdates()
|
|
560
|
+
|
|
561
|
+
const items = document.querySelectorAll<HTMLDivElement>('shade-tree-item')
|
|
562
|
+
expect(items[0]?.tabIndex).toBe(-1)
|
|
563
|
+
expect(items[1]?.tabIndex).toBe(0)
|
|
564
|
+
|
|
565
|
+
service[Symbol.dispose]()
|
|
566
|
+
})
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
it('should sync focusedItem on item onfocus', async () => {
|
|
570
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
571
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
572
|
+
const treeData = createTreeData()
|
|
573
|
+
const service = createTestService()
|
|
574
|
+
|
|
575
|
+
service.rootItems.setValue(treeData)
|
|
576
|
+
service.updateFlattenedNodes()
|
|
577
|
+
|
|
578
|
+
initializeShadeRoot({
|
|
579
|
+
injector,
|
|
580
|
+
rootElement,
|
|
581
|
+
jsxElement: (
|
|
582
|
+
<Tree<TestNode>
|
|
583
|
+
rootItems={treeData}
|
|
584
|
+
treeService={service}
|
|
585
|
+
renderItem={(node) => <span>{node.name}</span>}
|
|
586
|
+
/>
|
|
587
|
+
),
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
await flushUpdates()
|
|
591
|
+
|
|
592
|
+
const items = document.querySelectorAll('shade-tree-item')
|
|
593
|
+
items[1]?.dispatchEvent(new FocusEvent('focus'))
|
|
594
|
+
|
|
595
|
+
expect(service.focusedItem.getValue()).toEqual(treeData[1])
|
|
596
|
+
expect(service.hasFocus.getValue()).toBe(true)
|
|
597
|
+
|
|
598
|
+
service[Symbol.dispose]()
|
|
599
|
+
})
|
|
600
|
+
})
|
|
601
|
+
})
|
|
602
|
+
|
|
504
603
|
describe('keyboard navigation', () => {
|
|
505
|
-
it('should handle ArrowDown
|
|
604
|
+
it('should not handle ArrowDown (delegated to spatial navigation)', async () => {
|
|
506
605
|
await usingAsync(new Injector(), async (injector) => {
|
|
507
606
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
508
607
|
const treeData = createTreeData()
|
|
@@ -529,7 +628,7 @@ describe('Tree', () => {
|
|
|
529
628
|
|
|
530
629
|
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }))
|
|
531
630
|
|
|
532
|
-
expect(service.focusedItem.getValue()).toEqual(treeData[
|
|
631
|
+
expect(service.focusedItem.getValue()).toEqual(treeData[0])
|
|
533
632
|
|
|
534
633
|
service[Symbol.dispose]()
|
|
535
634
|
})
|
|
@@ -5,6 +5,8 @@ import { cssVariableTheme } from '../../services/css-variable-theme.js'
|
|
|
5
5
|
import type { TreeService } from '../../services/tree-service.js'
|
|
6
6
|
import { TreeItem } from './tree-item.js'
|
|
7
7
|
|
|
8
|
+
let nextTreeId = 0
|
|
9
|
+
|
|
8
10
|
export type TreeItemState = {
|
|
9
11
|
isFocused: boolean
|
|
10
12
|
isSelected: boolean
|
|
@@ -21,17 +23,26 @@ export type TreeProps<T> = {
|
|
|
21
23
|
variant?: 'contained' | 'outlined'
|
|
22
24
|
onItemActivate?: (item: T) => void
|
|
23
25
|
onSelectionChange?: (selected: T[]) => void
|
|
26
|
+
/**
|
|
27
|
+
* Section name for spatial navigation scoping.
|
|
28
|
+
* Sets `data-nav-section` on the tree host so that SpatialNavigationService
|
|
29
|
+
* constrains arrow-key navigation within the tree.
|
|
30
|
+
* Auto-generated per instance when not provided.
|
|
31
|
+
*/
|
|
32
|
+
navSection?: string
|
|
24
33
|
} & PartialElement<HTMLDivElement>
|
|
25
34
|
|
|
26
35
|
export const Tree: <T>(props: TreeProps<T>, children: ChildrenList) => JSX.Element<any> = Shade({
|
|
27
|
-
|
|
36
|
+
customElementName: 'shade-tree',
|
|
28
37
|
css: {
|
|
29
38
|
display: 'block',
|
|
30
39
|
fontFamily: cssVariableTheme.typography.fontFamily,
|
|
31
40
|
width: '100%',
|
|
32
41
|
overflow: 'auto',
|
|
33
42
|
},
|
|
34
|
-
render: ({ props, useDisposable, useObservable, useHostProps }) => {
|
|
43
|
+
render: ({ props, useDisposable, useObservable, useHostProps, useState }) => {
|
|
44
|
+
const [navSectionId] = useState('navSectionId', String(nextTreeId++))
|
|
45
|
+
|
|
35
46
|
useDisposable('keydown-handler', () => {
|
|
36
47
|
const listener = (ev: KeyboardEvent) => {
|
|
37
48
|
props.treeService.handleKeyDown(ev)
|
|
@@ -43,8 +54,8 @@ export const Tree: <T>(props: TreeProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
|
-
window.addEventListener('keydown', listener)
|
|
47
|
-
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener) }
|
|
57
|
+
window.addEventListener('keydown', listener, true)
|
|
58
|
+
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener, true) }
|
|
48
59
|
})
|
|
49
60
|
|
|
50
61
|
if (props.treeService.rootItems.getValue() !== props.rootItems) {
|
|
@@ -82,9 +93,16 @@ export const Tree: <T>(props: TreeProps<T>, children: ChildrenList) => JSX.Eleme
|
|
|
82
93
|
useHostProps({
|
|
83
94
|
'data-variant': props.variant || undefined,
|
|
84
95
|
'data-tree-instance-id': treeInstanceId.value,
|
|
96
|
+
'data-nav-section': props.navSection ?? `tree-${navSectionId}`,
|
|
85
97
|
role: 'tree',
|
|
86
98
|
'aria-multiselectable': 'true',
|
|
87
99
|
onclick: () => props.treeService.hasFocus.setValue(true),
|
|
100
|
+
onfocusout: (ev: FocusEvent) => {
|
|
101
|
+
const hostEl = ev.currentTarget as HTMLElement
|
|
102
|
+
if (!ev.relatedTarget || !hostEl.contains(ev.relatedTarget as Node)) {
|
|
103
|
+
props.treeService.hasFocus.setValue(false)
|
|
104
|
+
}
|
|
105
|
+
},
|
|
88
106
|
})
|
|
89
107
|
|
|
90
108
|
const [flattenedNodes] = useObservable('flattenedNodes', props.treeService.flattenedNodes)
|
|
@@ -298,7 +298,7 @@ const shadesByTag = {} as Record<TypographyTag, (props: TypographyProps, childre
|
|
|
298
298
|
|
|
299
299
|
for (const [tag, elementBase] of tagConfigs) {
|
|
300
300
|
shadesByTag[tag] = Shade<TypographyProps>({
|
|
301
|
-
|
|
301
|
+
customElementName: `shade-typography-${tag}`,
|
|
302
302
|
elementBase,
|
|
303
303
|
elementBaseName: tag,
|
|
304
304
|
css: typographyCss,
|
|
@@ -7,7 +7,7 @@ import type { WizardStepProps } from './index.js'
|
|
|
7
7
|
import { Wizard } from './index.js'
|
|
8
8
|
|
|
9
9
|
const Step1 = Shade<WizardStepProps>({
|
|
10
|
-
|
|
10
|
+
customElementName: 'wizard-test-step-1',
|
|
11
11
|
render: ({ props }) => (
|
|
12
12
|
<div className="wizard-step" data-step-name="step1">
|
|
13
13
|
<span className="step-info">
|
|
@@ -25,7 +25,7 @@ const Step1 = Shade<WizardStepProps>({
|
|
|
25
25
|
})
|
|
26
26
|
|
|
27
27
|
const Step2 = Shade<WizardStepProps>({
|
|
28
|
-
|
|
28
|
+
customElementName: 'wizard-test-step-2',
|
|
29
29
|
render: ({ props }) => (
|
|
30
30
|
<div className="wizard-step" data-step-name="step2">
|
|
31
31
|
<span className="step-info">
|
|
@@ -43,7 +43,7 @@ const Step2 = Shade<WizardStepProps>({
|
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
const Step3 = Shade<WizardStepProps>({
|
|
46
|
-
|
|
46
|
+
customElementName: 'wizard-test-step-3',
|
|
47
47
|
render: ({ props }) => (
|
|
48
48
|
<div className="wizard-step" data-step-name="step3">
|
|
49
49
|
<span className="step-info">
|
|
@@ -206,8 +206,8 @@ describe('CollectionService', () => {
|
|
|
206
206
|
})
|
|
207
207
|
})
|
|
208
208
|
|
|
209
|
-
describe('
|
|
210
|
-
it('Should move focus to previous entry', () => {
|
|
209
|
+
describe('Arrow keys', () => {
|
|
210
|
+
it('Should move focus to the previous entry on ArrowUp', () => {
|
|
211
211
|
const testEntries = createTestEntries()
|
|
212
212
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
213
213
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
@@ -222,22 +222,22 @@ describe('CollectionService', () => {
|
|
|
222
222
|
})
|
|
223
223
|
})
|
|
224
224
|
|
|
225
|
-
it('Should not
|
|
225
|
+
it('Should not preventDefault ArrowUp at the first entry', () => {
|
|
226
226
|
const testEntries = createTestEntries()
|
|
227
227
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
228
228
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
229
229
|
service.hasFocus.setValue(true)
|
|
230
230
|
service.focusedEntry.setValue(testEntries[0])
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
const ev = createKeyboardEvent('ArrowUp')
|
|
233
|
+
service.handleKeyDown(ev)
|
|
233
234
|
|
|
235
|
+
expect(ev.preventDefault).not.toHaveBeenCalled()
|
|
234
236
|
expect(service.focusedEntry.getValue()).toBe(testEntries[0])
|
|
235
237
|
})
|
|
236
238
|
})
|
|
237
|
-
})
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
it('Should move focus to next entry', () => {
|
|
240
|
+
it('Should move focus to the next entry on ArrowDown', () => {
|
|
241
241
|
const testEntries = createTestEntries()
|
|
242
242
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
243
243
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
@@ -252,60 +252,69 @@ describe('CollectionService', () => {
|
|
|
252
252
|
})
|
|
253
253
|
})
|
|
254
254
|
|
|
255
|
-
it('Should not
|
|
255
|
+
it('Should not preventDefault ArrowDown at the last entry', () => {
|
|
256
256
|
const testEntries = createTestEntries()
|
|
257
257
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
258
258
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
259
259
|
service.hasFocus.setValue(true)
|
|
260
260
|
service.focusedEntry.setValue(testEntries[2])
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
const ev = createKeyboardEvent('ArrowDown')
|
|
263
|
+
service.handleKeyDown(ev)
|
|
263
264
|
|
|
265
|
+
expect(ev.preventDefault).not.toHaveBeenCalled()
|
|
264
266
|
expect(service.focusedEntry.getValue()).toBe(testEntries[2])
|
|
265
267
|
})
|
|
266
268
|
})
|
|
267
|
-
})
|
|
268
269
|
|
|
269
|
-
|
|
270
|
-
it('Should focus the first entry', () => {
|
|
270
|
+
it('Should not handle arrow keys when focusedEntry is undefined', () => {
|
|
271
271
|
const testEntries = createTestEntries()
|
|
272
272
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
273
273
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
274
274
|
service.hasFocus.setValue(true)
|
|
275
|
-
service.focusedEntry.setValue(
|
|
275
|
+
service.focusedEntry.setValue(undefined)
|
|
276
276
|
|
|
277
|
-
|
|
277
|
+
const evDown = createKeyboardEvent('ArrowDown')
|
|
278
|
+
service.handleKeyDown(evDown)
|
|
279
|
+
expect(evDown.preventDefault).not.toHaveBeenCalled()
|
|
278
280
|
|
|
279
|
-
|
|
281
|
+
const evUp = createKeyboardEvent('ArrowUp')
|
|
282
|
+
service.handleKeyDown(evUp)
|
|
283
|
+
expect(evUp.preventDefault).not.toHaveBeenCalled()
|
|
280
284
|
})
|
|
281
285
|
})
|
|
282
286
|
})
|
|
283
287
|
|
|
284
|
-
describe('
|
|
285
|
-
it('Should focus the
|
|
288
|
+
describe('Home key', () => {
|
|
289
|
+
it('Should focus the first entry and preventDefault', () => {
|
|
286
290
|
const testEntries = createTestEntries()
|
|
287
291
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
288
292
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
289
293
|
service.hasFocus.setValue(true)
|
|
290
|
-
service.focusedEntry.setValue(testEntries[
|
|
294
|
+
service.focusedEntry.setValue(testEntries[2])
|
|
291
295
|
|
|
292
|
-
|
|
296
|
+
const ev = createKeyboardEvent('Home')
|
|
297
|
+
service.handleKeyDown(ev)
|
|
293
298
|
|
|
294
|
-
expect(
|
|
299
|
+
expect(ev.preventDefault).toHaveBeenCalled()
|
|
300
|
+
expect(service.focusedEntry.getValue()).toBe(testEntries[0])
|
|
295
301
|
})
|
|
296
302
|
})
|
|
297
303
|
})
|
|
298
304
|
|
|
299
|
-
describe('
|
|
300
|
-
it('Should
|
|
305
|
+
describe('End key', () => {
|
|
306
|
+
it('Should focus the last entry and preventDefault', () => {
|
|
301
307
|
const testEntries = createTestEntries()
|
|
302
308
|
using(new CollectionService<TestEntry>({}), (service) => {
|
|
303
309
|
service.data.setValue({ count: 3, entries: testEntries })
|
|
304
310
|
service.hasFocus.setValue(true)
|
|
311
|
+
service.focusedEntry.setValue(testEntries[0])
|
|
305
312
|
|
|
306
|
-
|
|
313
|
+
const ev = createKeyboardEvent('End')
|
|
314
|
+
service.handleKeyDown(ev)
|
|
307
315
|
|
|
308
|
-
expect(
|
|
316
|
+
expect(ev.preventDefault).toHaveBeenCalled()
|
|
317
|
+
expect(service.focusedEntry.getValue()).toBe(testEntries[2])
|
|
309
318
|
})
|
|
310
319
|
})
|
|
311
320
|
})
|
|
@@ -62,6 +62,19 @@ export class CollectionService<T>
|
|
|
62
62
|
|
|
63
63
|
public focusedEntry = new ObservableValue<T | undefined>(undefined)
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Stores the focused entry captured on pointerdown, before the focus event
|
|
67
|
+
* can update focusedEntry. Used as the anchor for SHIFT+click range selection.
|
|
68
|
+
* Call {@link setFocusAnchor} from `onpointerdown` to snapshot the anchor
|
|
69
|
+
* before focus shifts.
|
|
70
|
+
*/
|
|
71
|
+
private focusAnchor: T | undefined = undefined
|
|
72
|
+
|
|
73
|
+
/** Snapshot the current focused entry as the anchor for SHIFT+click range selection. */
|
|
74
|
+
public setFocusAnchor(): void {
|
|
75
|
+
this.focusAnchor = this.focusedEntry.getValue()
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
public selection = new ObservableValue<T[]>([])
|
|
66
79
|
|
|
67
80
|
public searchTerm = new ObservableValue('')
|
|
@@ -107,28 +120,36 @@ export class CollectionService<T>
|
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
break
|
|
110
|
-
case '
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
case 'ArrowDown': {
|
|
124
|
+
if (focusedEntry !== undefined) {
|
|
125
|
+
const currentIndex = entries.indexOf(focusedEntry)
|
|
126
|
+
if (currentIndex >= 0 && currentIndex < entries.length - 1) {
|
|
127
|
+
ev.preventDefault()
|
|
128
|
+
this.focusedEntry.setValue(entries[currentIndex + 1])
|
|
129
|
+
}
|
|
130
|
+
}
|
|
113
131
|
break
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
132
|
+
}
|
|
133
|
+
case 'ArrowUp': {
|
|
134
|
+
if (focusedEntry !== undefined) {
|
|
135
|
+
const currentIndex = entries.indexOf(focusedEntry)
|
|
136
|
+
if (currentIndex > 0) {
|
|
137
|
+
ev.preventDefault()
|
|
138
|
+
this.focusedEntry.setValue(entries[currentIndex - 1])
|
|
139
|
+
}
|
|
140
|
+
}
|
|
119
141
|
break
|
|
142
|
+
}
|
|
120
143
|
case 'Home': {
|
|
144
|
+
ev.preventDefault()
|
|
121
145
|
this.focusedEntry.setValue(entries[0])
|
|
122
146
|
break
|
|
123
147
|
}
|
|
124
148
|
case 'End': {
|
|
149
|
+
ev.preventDefault()
|
|
125
150
|
this.focusedEntry.setValue(entries[entries.length - 1])
|
|
126
151
|
break
|
|
127
152
|
}
|
|
128
|
-
case 'Tab': {
|
|
129
|
-
this.hasFocus.setValue(!hasFocus)
|
|
130
|
-
break
|
|
131
|
-
}
|
|
132
153
|
case 'Escape': {
|
|
133
154
|
this.searchTerm.setValue('')
|
|
134
155
|
this.selection.setValue([])
|
|
@@ -152,7 +173,8 @@ export class CollectionService<T>
|
|
|
152
173
|
public handleRowClick(entry: T, ev: MouseEvent) {
|
|
153
174
|
this.emit('onRowClick', entry)
|
|
154
175
|
const currentSelectionValue = this.selection.getValue()
|
|
155
|
-
const lastFocused = this.focusedEntry.getValue()
|
|
176
|
+
const lastFocused = this.focusAnchor ?? this.focusedEntry.getValue()
|
|
177
|
+
this.focusAnchor = undefined
|
|
156
178
|
if (ev.ctrlKey) {
|
|
157
179
|
if (currentSelectionValue.includes(entry)) {
|
|
158
180
|
this.selection.setValue(currentSelectionValue.filter((s) => s !== entry))
|
|
@@ -35,6 +35,7 @@ describe('css-variable-theme', () => {
|
|
|
35
35
|
expect(cssVariableTheme.action.selectedBackground).toBe('var(--shades-theme-action-selected-background)')
|
|
36
36
|
expect(cssVariableTheme.action.activeBackground).toBe('var(--shades-theme-action-active-background)')
|
|
37
37
|
expect(cssVariableTheme.action.focusRing).toBe('var(--shades-theme-action-focus-ring)')
|
|
38
|
+
expect(cssVariableTheme.action.focusOutline).toBe('var(--shades-theme-action-focus-outline)')
|
|
38
39
|
expect(cssVariableTheme.action.disabledOpacity).toBe('var(--shades-theme-action-disabled-opacity)')
|
|
39
40
|
expect(cssVariableTheme.action.backdrop).toBe('var(--shades-theme-action-backdrop)')
|
|
40
41
|
expect(cssVariableTheme.action.subtleBorder).toBe('var(--shades-theme-action-subtle-border)')
|
|
@@ -75,6 +75,7 @@ export const cssVariableTheme = {
|
|
|
75
75
|
selectedBackground: 'var(--shades-theme-action-selected-background)',
|
|
76
76
|
activeBackground: 'var(--shades-theme-action-active-background)',
|
|
77
77
|
focusRing: 'var(--shades-theme-action-focus-ring)',
|
|
78
|
+
focusOutline: 'var(--shades-theme-action-focus-outline)',
|
|
78
79
|
disabledOpacity: 'var(--shades-theme-action-disabled-opacity)',
|
|
79
80
|
backdrop: 'var(--shades-theme-action-backdrop)',
|
|
80
81
|
subtleBorder: 'var(--shades-theme-action-subtle-border)',
|
|
@@ -176,6 +177,30 @@ export const cssVariableTheme = {
|
|
|
176
177
|
export const buildTransition = (...specs: Array<[property: string, duration: string, easing: string]>): string =>
|
|
177
178
|
specs.map(([prop, dur, ease]) => `${prop} ${dur} ${ease}`).join(', ')
|
|
178
179
|
|
|
180
|
+
const FOCUS_STYLES_ID = 'shades-focus-visible-styles'
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Injects global `:focus-visible` styles using the theme's `focusOutline` CSS variable.
|
|
184
|
+
* Ensures keyboard/spatial navigation focus is visible while mouse clicks produce no outline.
|
|
185
|
+
* Safe to call multiple times — the style element is only created once.
|
|
186
|
+
*/
|
|
187
|
+
export const injectFocusVisibleStyles = (): void => {
|
|
188
|
+
if (document.getElementById(FOCUS_STYLES_ID)) return
|
|
189
|
+
|
|
190
|
+
const style = document.createElement('style')
|
|
191
|
+
style.id = FOCUS_STYLES_ID
|
|
192
|
+
style.textContent = `
|
|
193
|
+
:focus-visible {
|
|
194
|
+
outline: ${cssVariableTheme.action.focusOutline};
|
|
195
|
+
outline-offset: 2px;
|
|
196
|
+
}
|
|
197
|
+
:focus:not(:focus-visible) {
|
|
198
|
+
outline: none;
|
|
199
|
+
}
|
|
200
|
+
`
|
|
201
|
+
document.head.appendChild(style)
|
|
202
|
+
}
|
|
203
|
+
|
|
179
204
|
const extractVarName = (key: string): string => key.replace(/^var\(/, '').replace(/[,)].*/, '')
|
|
180
205
|
|
|
181
206
|
export const setCssVariable = (key: string, value: string, root: HTMLElement) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { using } from '@furystack/utils'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
import { ListService } from './list-service.js'
|
|
4
4
|
|
|
5
5
|
type TestItem = { id: number; name: string }
|
|
@@ -80,51 +80,33 @@ describe('ListService', () => {
|
|
|
80
80
|
})
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
it('should handle ArrowDown
|
|
83
|
+
it('should not handle ArrowDown (delegated to spatial navigation)', () => {
|
|
84
84
|
const { service, items } = createTestService()
|
|
85
85
|
using(service, () => {
|
|
86
86
|
service.hasFocus.setValue(true)
|
|
87
87
|
service.focusedItem.setValue(items[0])
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
})
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('should not move past last item on ArrowDown', () => {
|
|
96
|
-
const { service, items } = createTestService()
|
|
97
|
-
using(service, () => {
|
|
98
|
-
service.hasFocus.setValue(true)
|
|
99
|
-
service.focusedItem.setValue(items[2])
|
|
100
|
-
|
|
101
|
-
service.handleKeyDown(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
|
102
|
-
|
|
103
|
-
expect(service.focusedItem.getValue()).toBe(items[2])
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('should handle ArrowUp to move focus to previous item', () => {
|
|
108
|
-
const { service, items } = createTestService()
|
|
109
|
-
using(service, () => {
|
|
110
|
-
service.hasFocus.setValue(true)
|
|
111
|
-
service.focusedItem.setValue(items[1])
|
|
112
|
-
|
|
113
|
-
service.handleKeyDown(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
|
89
|
+
const ev = new KeyboardEvent('keydown', { key: 'ArrowDown', cancelable: true })
|
|
90
|
+
const preventSpy = vi.spyOn(ev, 'preventDefault')
|
|
91
|
+
service.handleKeyDown(ev)
|
|
114
92
|
|
|
115
93
|
expect(service.focusedItem.getValue()).toBe(items[0])
|
|
94
|
+
expect(preventSpy).not.toHaveBeenCalled()
|
|
116
95
|
})
|
|
117
96
|
})
|
|
118
97
|
|
|
119
|
-
it('should not
|
|
98
|
+
it('should not handle ArrowUp (delegated to spatial navigation)', () => {
|
|
120
99
|
const { service, items } = createTestService()
|
|
121
100
|
using(service, () => {
|
|
122
101
|
service.hasFocus.setValue(true)
|
|
123
|
-
service.focusedItem.setValue(items[
|
|
102
|
+
service.focusedItem.setValue(items[1])
|
|
124
103
|
|
|
125
|
-
|
|
104
|
+
const ev = new KeyboardEvent('keydown', { key: 'ArrowUp', cancelable: true })
|
|
105
|
+
const preventSpy = vi.spyOn(ev, 'preventDefault')
|
|
106
|
+
service.handleKeyDown(ev)
|
|
126
107
|
|
|
127
|
-
expect(service.focusedItem.getValue()).toBe(items[
|
|
108
|
+
expect(service.focusedItem.getValue()).toBe(items[1])
|
|
109
|
+
expect(preventSpy).not.toHaveBeenCalled()
|
|
128
110
|
})
|
|
129
111
|
})
|
|
130
112
|
|
|
@@ -232,17 +214,6 @@ describe('ListService', () => {
|
|
|
232
214
|
})
|
|
233
215
|
})
|
|
234
216
|
|
|
235
|
-
it('should handle Tab to toggle focus', () => {
|
|
236
|
-
const { service } = createTestService()
|
|
237
|
-
using(service, () => {
|
|
238
|
-
service.hasFocus.setValue(true)
|
|
239
|
-
|
|
240
|
-
service.handleKeyDown(new KeyboardEvent('keydown', { key: 'Tab' }))
|
|
241
|
-
|
|
242
|
-
expect(service.hasFocus.getValue()).toBe(false)
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
|
|
246
217
|
it('should handle Escape to clear selection and search term', () => {
|
|
247
218
|
const { service, items } = createTestService()
|
|
248
219
|
using(service, () => {
|
|
@@ -41,6 +41,19 @@ export class ListService<T> implements Disposable {
|
|
|
41
41
|
|
|
42
42
|
public focusedItem = new ObservableValue<T | undefined>(undefined)
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Stores the focused item captured on pointerdown, before the focus event
|
|
46
|
+
* can update focusedItem. Used as the anchor for SHIFT+click range selection.
|
|
47
|
+
* Call {@link setFocusAnchor} from `onpointerdown` to snapshot the anchor
|
|
48
|
+
* before focus shifts.
|
|
49
|
+
*/
|
|
50
|
+
private focusAnchor: T | undefined = undefined
|
|
51
|
+
|
|
52
|
+
/** Snapshot the current focused item as the anchor for SHIFT+click range selection. */
|
|
53
|
+
public setFocusAnchor(): void {
|
|
54
|
+
this.focusAnchor = this.focusedItem.getValue()
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
public selection = new ObservableValue<T[]>([])
|
|
45
58
|
|
|
46
59
|
public searchTerm = new ObservableValue('')
|
|
@@ -89,14 +102,6 @@ export class ListService<T> implements Disposable {
|
|
|
89
102
|
this.focusedItem.setValue(items[items.findIndex((e) => e === this.focusedItem.getValue()) + 1])
|
|
90
103
|
}
|
|
91
104
|
break
|
|
92
|
-
case 'ArrowUp':
|
|
93
|
-
ev.preventDefault()
|
|
94
|
-
this.focusedItem.setValue(items[Math.max(0, items.findIndex((e) => e === focusedItem) - 1)])
|
|
95
|
-
break
|
|
96
|
-
case 'ArrowDown':
|
|
97
|
-
ev.preventDefault()
|
|
98
|
-
this.focusedItem.setValue(items[Math.min(items.length - 1, items.findIndex((e) => e === focusedItem) + 1)])
|
|
99
|
-
break
|
|
100
105
|
case 'Home': {
|
|
101
106
|
ev.preventDefault()
|
|
102
107
|
this.focusedItem.setValue(items[0])
|
|
@@ -107,10 +112,6 @@ export class ListService<T> implements Disposable {
|
|
|
107
112
|
this.focusedItem.setValue(items[items.length - 1])
|
|
108
113
|
break
|
|
109
114
|
}
|
|
110
|
-
case 'Tab': {
|
|
111
|
-
this.hasFocus.setValue(!hasFocus)
|
|
112
|
-
break
|
|
113
|
-
}
|
|
114
115
|
case 'Escape': {
|
|
115
116
|
ev.preventDefault()
|
|
116
117
|
this.searchTerm.setValue('')
|
|
@@ -134,7 +135,8 @@ export class ListService<T> implements Disposable {
|
|
|
134
135
|
|
|
135
136
|
public handleItemClick(item: T, ev: MouseEvent) {
|
|
136
137
|
const currentSelectionValue = this.selection.getValue()
|
|
137
|
-
const lastFocused = this.focusedItem.getValue()
|
|
138
|
+
const lastFocused = this.focusAnchor ?? this.focusedItem.getValue()
|
|
139
|
+
this.focusAnchor = undefined
|
|
138
140
|
if (ev.ctrlKey) {
|
|
139
141
|
if (currentSelectionValue.includes(item)) {
|
|
140
142
|
this.selection.setValue(currentSelectionValue.filter((s) => s !== item))
|
|
@@ -101,6 +101,8 @@ export type ActionColors = {
|
|
|
101
101
|
activeBackground: Color
|
|
102
102
|
/** Box-shadow value for focus ring indicators */
|
|
103
103
|
focusRing: string
|
|
104
|
+
/** CSS outline value for keyboard/spatial focus indicators (e.g. '2px solid #3f51b5') */
|
|
105
|
+
focusOutline: string
|
|
104
106
|
/** Opacity value for disabled elements (e.g. '0.6') */
|
|
105
107
|
disabledOpacity: string
|
|
106
108
|
/** Overlay background color for backdrops (drawers, modals) */
|