@kouji-ui/core 0.0.3 → 0.1.1
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/.turbo/turbo-build.log +6 -3
- package/CHANGELOG.md +177 -0
- package/ng-package.json +5 -1
- package/package.json +18 -6
- package/src/a11y/aria-describedby.ts +7 -3
- package/src/a11y/aria-labelledby.spec.ts +55 -0
- package/src/a11y/aria-labelledby.ts +38 -0
- package/src/a11y/focus-trap.ts +4 -2
- package/src/a11y/index.ts +1 -0
- package/src/a11y/live-region.ts +4 -2
- package/src/a11y/roving-tabindex.spec.ts +139 -0
- package/src/a11y/roving-tabindex.ts +56 -7
- package/src/a11y/visually-hidden.ts +4 -2
- package/src/accordion/accordion.context.ts +22 -0
- package/src/accordion/accordion.spec.ts +233 -23
- package/src/accordion/accordion.ts +311 -39
- package/src/accordion/index.ts +8 -2
- package/src/alert/alert.context.ts +28 -0
- package/src/alert/alert.spec.ts +217 -0
- package/src/alert/alert.ts +417 -0
- package/src/alert/config.ts +46 -0
- package/src/alert/index.ts +19 -0
- package/src/avatar/avatar-group.context.ts +45 -0
- package/src/avatar/avatar-group.spec.ts +358 -0
- package/src/avatar/avatar-group.ts +259 -0
- package/src/avatar/avatar.ts +17 -4
- package/src/avatar/index.ts +11 -1
- package/src/badge/badge.spec.ts +8 -0
- package/src/badge/badge.ts +24 -3
- package/src/breadcrumb/breadcrumb-current.ts +38 -0
- package/src/breadcrumb/breadcrumb-ellipsis.ts +71 -0
- package/src/breadcrumb/breadcrumb-item.ts +41 -0
- package/src/breadcrumb/breadcrumb-link.ts +44 -0
- package/src/breadcrumb/breadcrumb-list.ts +42 -0
- package/src/breadcrumb/breadcrumb-separator.ts +45 -0
- package/src/breadcrumb/breadcrumb.context.ts +45 -0
- package/src/breadcrumb/breadcrumb.spec.ts +354 -0
- package/src/breadcrumb/breadcrumb.ts +239 -0
- package/src/breadcrumb/config.ts +84 -0
- package/src/breadcrumb/index.ts +18 -0
- package/src/button/_examples/button.example.ts +71 -0
- package/src/button/_examples/button.finance.example.ts +83 -0
- package/src/button/_examples/button.retro.example.ts +91 -0
- package/src/button/_examples/button.sizes.example.ts +60 -0
- package/src/button/_examples/index.ts +6 -0
- package/src/button/button.spec.ts +139 -8
- package/src/button/button.ts +114 -16
- package/src/button/config.spec.ts +33 -0
- package/src/button/config.ts +45 -0
- package/src/button/index.ts +2 -1
- package/src/button-group/button-group.context.ts +28 -0
- package/src/button-group/button-group.spec.ts +99 -0
- package/src/button-group/button-group.ts +99 -0
- package/src/button-group/index.ts +2 -0
- package/src/calendar/calendar-day.ts +102 -0
- package/src/calendar/calendar-grid.ts +90 -0
- package/src/calendar/calendar-header.ts +65 -0
- package/src/calendar/calendar.context.ts +53 -0
- package/src/calendar/calendar.spec.ts +225 -0
- package/src/calendar/calendar.ts +233 -0
- package/src/calendar/date-utils.ts +225 -0
- package/src/calendar/index.ts +28 -0
- package/src/carousel/carousel.context.ts +81 -0
- package/src/carousel/carousel.spec.ts +337 -0
- package/src/carousel/carousel.ts +919 -0
- package/src/carousel/index.ts +22 -0
- package/src/cascade-select/cascade-select-option.ts +205 -0
- package/src/cascade-select/cascade-select-panel.ts +106 -0
- package/src/cascade-select/cascade-select-root.ts +265 -0
- package/src/cascade-select/cascade-select-sub-panel.ts +168 -0
- package/src/cascade-select/cascade-select-trigger.ts +44 -0
- package/src/cascade-select/cascade-select.context.ts +71 -0
- package/src/cascade-select/cascade-select.spec.ts +241 -0
- package/src/cascade-select/cascade-select.ts +5 -0
- package/src/cascade-select/index.ts +11 -0
- package/src/chart/chart.ts +6 -2
- package/src/chat/chat-avatar.ts +31 -0
- package/src/chat/chat-bubble.ts +49 -0
- package/src/chat/chat-footer.ts +67 -0
- package/src/chat/chat-header.ts +41 -0
- package/src/chat/chat-log.ts +43 -0
- package/src/chat/chat.context.ts +64 -0
- package/src/chat/chat.spec.ts +341 -0
- package/src/chat/chat.ts +191 -0
- package/src/chat/config.ts +53 -0
- package/src/chat/index.ts +21 -0
- package/src/checkbox/checkbox.ts +9 -2
- package/src/color-picker/color-picker.context.ts +74 -0
- package/src/color-picker/color-picker.spec.ts +241 -0
- package/src/color-picker/color-picker.ts +698 -0
- package/src/color-picker/color-picker.utils.ts +140 -0
- package/src/color-picker/index.ts +28 -0
- package/src/combobox/combobox-input.ts +143 -0
- package/src/combobox/combobox-listbox.ts +49 -0
- package/src/combobox/combobox-option.ts +43 -0
- package/src/combobox/combobox-root.ts +281 -0
- package/src/combobox/combobox.spec.ts +75 -0
- package/src/combobox/combobox.ts +4 -0
- package/src/combobox/index.ts +4 -0
- package/src/command-palette/command-empty.ts +36 -0
- package/src/command-palette/command-group.ts +21 -0
- package/src/command-palette/command-input.ts +70 -0
- package/src/command-palette/command-item.ts +60 -0
- package/src/command-palette/command-list.ts +25 -0
- package/src/command-palette/command-palette-dialog.ts +74 -0
- package/src/command-palette/command-palette-trigger.ts +64 -0
- package/src/command-palette/command-palette.filters.ts +53 -0
- package/src/command-palette/command-palette.spec.ts +349 -0
- package/src/command-palette/command-palette.ts +204 -0
- package/src/command-palette/command-separator.ts +18 -0
- package/src/command-palette/index.ts +15 -0
- package/src/confirm-popup/confirm-popup-action.ts +39 -0
- package/src/confirm-popup/confirm-popup-cancel.ts +38 -0
- package/src/confirm-popup/confirm-popup-content.ts +113 -0
- package/src/confirm-popup/confirm-popup-message.ts +36 -0
- package/src/confirm-popup/confirm-popup-trigger.ts +31 -0
- package/src/confirm-popup/confirm-popup.context.ts +41 -0
- package/src/confirm-popup/confirm-popup.spec.ts +337 -0
- package/src/confirm-popup/confirm-popup.ts +109 -0
- package/src/confirm-popup/index.ts +12 -0
- package/src/date-picker/date-picker-calendar.ts +65 -0
- package/src/date-picker/date-picker-trigger.ts +215 -0
- package/src/date-picker/date-picker.context.ts +50 -0
- package/src/date-picker/date-picker.spec.ts +117 -0
- package/src/date-picker/date-picker.ts +127 -0
- package/src/date-picker/index.ts +7 -0
- package/src/dialog/_examples/dialog.confirm.example.ts +145 -0
- package/src/dialog/_examples/dialog.example.ts +106 -0
- package/src/dialog/_examples/dialog.finance.example.ts +121 -0
- package/src/dialog/_examples/dialog.retro.example.ts +126 -0
- package/src/dialog/_examples/index.ts +6 -0
- package/src/dialog/dialog.ref.ts +42 -0
- package/src/dialog/dialog.service.ts +57 -167
- package/src/dialog/dialog.spec.ts +35 -120
- package/src/dialog/dialog.ts +17 -192
- package/src/dialog/index.ts +3 -9
- package/src/divider/divider.spec.ts +223 -0
- package/src/divider/divider.ts +137 -0
- package/src/divider/index.ts +5 -0
- package/src/drawer/drawer.ref.ts +63 -0
- package/src/drawer/drawer.service.ts +106 -0
- package/src/drawer/drawer.spec.ts +117 -0
- package/src/drawer/drawer.ts +122 -0
- package/src/drawer/index.ts +10 -0
- package/src/dropdown-menu/dropdown-menu-content.ts +258 -0
- package/src/dropdown-menu/dropdown-menu-group.ts +31 -0
- package/src/dropdown-menu/dropdown-menu-item.ts +51 -0
- package/src/dropdown-menu/dropdown-menu-label.ts +41 -0
- package/src/dropdown-menu/dropdown-menu-separator.ts +27 -0
- package/src/dropdown-menu/dropdown-menu-trigger.ts +140 -0
- package/src/dropdown-menu/dropdown-menu.spec.ts +265 -0
- package/src/dropdown-menu/dropdown-menu.ts +60 -0
- package/src/dropdown-menu/index.ts +15 -0
- package/src/example-components.ts +39 -30
- package/src/field/field-error.ts +49 -0
- package/src/field/field-group.ts +39 -0
- package/src/field/field-help.ts +47 -0
- package/src/field/field-label.ts +30 -0
- package/src/field/field.context.ts +28 -0
- package/src/field/field.spec.ts +179 -0
- package/src/field/field.ts +121 -0
- package/src/field/index.ts +6 -0
- package/src/file-upload/file-upload.context.ts +89 -0
- package/src/file-upload/file-upload.spec.ts +290 -0
- package/src/file-upload/file-upload.ts +639 -0
- package/src/file-upload/file-upload.types.ts +59 -0
- package/src/file-upload/index.ts +23 -0
- package/src/form/form-error-summary.ts +87 -0
- package/src/form/form-field.context.ts +3 -0
- package/src/form/form-field.spec.ts +25 -0
- package/src/form/form-field.ts +52 -0
- package/src/form/form.context.ts +52 -2
- package/src/form/form.spec.ts +155 -18
- package/src/form/form.ts +315 -35
- package/src/form/index.ts +11 -2
- package/src/icon/E2E.md +14 -0
- package/src/icon/icon.css +40 -0
- package/src/icon/icon.directive.spec.ts +198 -0
- package/src/icon/icon.directive.ts +113 -0
- package/src/icon/icon.mode.spec.ts +29 -0
- package/src/icon/icon.mode.ts +23 -0
- package/src/icon/icon.providers.spec.ts +63 -0
- package/src/icon/icon.providers.ts +47 -0
- package/src/icon/icon.resolver.spec.ts +96 -0
- package/src/icon/icon.resolver.ts +49 -0
- package/src/icon/icon.tokens.ts +88 -0
- package/src/icon/icon.types.ts +48 -0
- package/src/icon/index.ts +21 -0
- package/src/input/input.ts +12 -3
- package/src/input-group/index.ts +3 -0
- package/src/input-group/input-group-addon.ts +92 -0
- package/src/input-group/input-group.context.ts +27 -0
- package/src/input-group/input-group.spec.ts +186 -0
- package/src/input-group/input-group.ts +136 -0
- package/src/input-mask/index.ts +11 -0
- package/src/input-mask/input-mask.compile.ts +69 -0
- package/src/input-mask/input-mask.engine.ts +376 -0
- package/src/input-mask/input-mask.presets.ts +24 -0
- package/src/input-mask/input-mask.spec.ts +304 -0
- package/src/input-mask/input-mask.tokens.ts +49 -0
- package/src/input-mask/input-mask.ts +280 -0
- package/src/input-otp/index.ts +2 -0
- package/src/input-otp/input-otp.context.ts +41 -0
- package/src/input-otp/input-otp.spec.ts +308 -0
- package/src/input-otp/input-otp.ts +470 -0
- package/src/kbd/index.ts +1 -0
- package/src/kbd/kbd.spec.ts +127 -0
- package/src/kbd/kbd.ts +132 -0
- package/src/link/config.ts +44 -0
- package/src/link/index.ts +2 -0
- package/src/link/link.spec.ts +246 -0
- package/src/link/link.ts +226 -0
- package/src/list/index.ts +11 -0
- package/src/list/list.spec.ts +237 -0
- package/src/list/list.ts +277 -0
- package/src/menubar/index.ts +7 -0
- package/src/menubar/menubar-item.ts +204 -0
- package/src/menubar/menubar.context.ts +43 -0
- package/src/menubar/menubar.spec.ts +433 -0
- package/src/menubar/menubar.ts +309 -0
- package/src/number-input/index.ts +4 -0
- package/src/number-input/number-input-group.ts +64 -0
- package/src/number-input/number-input.context.ts +31 -0
- package/src/number-input/number-input.format.ts +148 -0
- package/src/number-input/number-input.spec.ts +170 -0
- package/src/number-input/number-input.ts +457 -0
- package/src/number-input/number-stepper.ts +193 -0
- package/src/overlay-badge/index.ts +7 -0
- package/src/overlay-badge/overlay-badge-content.ts +73 -0
- package/src/overlay-badge/overlay-badge.context.ts +37 -0
- package/src/overlay-badge/overlay-badge.spec.ts +220 -0
- package/src/overlay-badge/overlay-badge.ts +128 -0
- package/src/pagination/config.ts +82 -0
- package/src/pagination/index.ts +19 -0
- package/src/pagination/page-tokens.ts +78 -0
- package/src/pagination/pagination-ellipsis.ts +69 -0
- package/src/pagination/pagination-first.ts +84 -0
- package/src/pagination/pagination-info.ts +70 -0
- package/src/pagination/pagination-item.ts +81 -0
- package/src/pagination/pagination-last.ts +85 -0
- package/src/pagination/pagination-next.ts +86 -0
- package/src/pagination/pagination-previous.ts +90 -0
- package/src/pagination/pagination.context.ts +52 -0
- package/src/pagination/pagination.spec.ts +388 -0
- package/src/pagination/pagination.ts +288 -0
- package/src/password-input/index.ts +15 -0
- package/src/password-input/password-input.context.ts +65 -0
- package/src/password-input/password-input.scorer.ts +34 -0
- package/src/password-input/password-input.spec.ts +274 -0
- package/src/password-input/password-input.ts +488 -0
- package/src/popover/_examples/index.ts +5 -0
- package/src/popover/_examples/popover.example.ts +92 -0
- package/src/popover/_examples/popover.finance.example.ts +106 -0
- package/src/popover/_examples/popover.retro.example.ts +103 -0
- package/src/popover/index.ts +5 -12
- package/src/popover/popover-arrow.ts +30 -0
- package/src/popover/popover-close.ts +36 -0
- package/src/popover/popover-content.ts +41 -0
- package/src/popover/popover-title.ts +39 -0
- package/src/popover/popover-trigger.ts +40 -0
- package/src/popover/popover.spec.ts +51 -44
- package/src/popover/popover.ts +5 -124
- package/src/presets/bind-presets.spec.ts +50 -0
- package/src/presets/bind-presets.ts +43 -0
- package/src/presets/index.ts +3 -0
- package/src/presets/size.spec.ts +67 -0
- package/src/presets/size.ts +59 -0
- package/src/presets/variant.spec.ts +72 -0
- package/src/presets/variant.ts +62 -0
- package/src/primitives/directionality/directionality.spec.ts +112 -0
- package/src/primitives/directionality/directionality.ts +89 -0
- package/src/primitives/directionality/index.ts +1 -0
- package/src/primitives/forms/form-control.spec.ts +14 -6
- package/src/primitives/forms/form-control.ts +6 -2
- package/src/primitives/index.ts +3 -0
- package/src/primitives/interaction/disabled.ts +12 -5
- package/src/primitives/interaction/focus-ring.ts +4 -2
- package/src/primitives/list/filterable-list.spec.ts +114 -0
- package/src/primitives/list/filterable-list.ts +126 -0
- package/src/primitives/list/group.spec.ts +99 -0
- package/src/primitives/list/group.ts +139 -0
- package/src/primitives/list/index.ts +8 -0
- package/src/primitives/list/inject-helpers.ts +8 -0
- package/src/primitives/list/item.spec.ts +173 -0
- package/src/primitives/list/item.ts +222 -0
- package/src/primitives/list/navigator.spec.ts +286 -0
- package/src/primitives/list/navigator.ts +238 -0
- package/src/primitives/list/selection.spec.ts +214 -0
- package/src/primitives/list/selection.ts +339 -0
- package/src/primitives/list/tokens.ts +106 -0
- package/src/primitives/list/type-ahead.spec.ts +58 -0
- package/src/primitives/list/type-ahead.ts +46 -0
- package/src/primitives/overlay/backdrop.spec.ts +54 -0
- package/src/primitives/overlay/backdrop.ts +31 -0
- package/src/primitives/overlay/builder.spec.ts +38 -0
- package/src/primitives/overlay/builder.ts +151 -0
- package/src/primitives/overlay/container.ts +42 -0
- package/src/primitives/overlay/context.ts +14 -0
- package/src/primitives/overlay/controller.spec.ts +86 -0
- package/src/primitives/overlay/controller.ts +190 -0
- package/src/primitives/overlay/id.spec.ts +24 -0
- package/src/primitives/overlay/id.ts +17 -0
- package/src/primitives/overlay/index.ts +18 -1
- package/src/primitives/overlay/overlay.css +42 -0
- package/src/primitives/overlay/panel.spec.ts +52 -0
- package/src/primitives/overlay/panel.ts +102 -0
- package/src/primitives/overlay/stack.spec.ts +55 -0
- package/src/primitives/overlay/stack.ts +113 -0
- package/src/primitives/overlay/strategies/backdrop/blurred.spec.ts +11 -0
- package/src/primitives/overlay/strategies/backdrop/blurred.ts +5 -0
- package/src/primitives/overlay/strategies/backdrop/none.spec.ts +10 -0
- package/src/primitives/overlay/strategies/backdrop/none.ts +9 -0
- package/src/primitives/overlay/strategies/backdrop/solid.spec.ts +18 -0
- package/src/primitives/overlay/strategies/backdrop/solid.ts +23 -0
- package/src/primitives/overlay/strategies/focus-trap/inert-based.spec.ts +28 -0
- package/src/primitives/overlay/strategies/focus-trap/inert-based.ts +38 -0
- package/src/primitives/overlay/strategies/focus-trap/no-trap.spec.ts +9 -0
- package/src/primitives/overlay/strategies/focus-trap/no-trap.ts +8 -0
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.spec.ts +49 -0
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.ts +85 -0
- package/src/primitives/overlay/strategies/index.ts +34 -0
- package/src/primitives/overlay/strategies/live-announcer/_announce.ts +35 -0
- package/src/primitives/overlay/strategies/live-announcer/assertive.spec.ts +21 -0
- package/src/primitives/overlay/strategies/live-announcer/assertive.ts +11 -0
- package/src/primitives/overlay/strategies/live-announcer/polite.spec.ts +21 -0
- package/src/primitives/overlay/strategies/live-announcer/polite.ts +11 -0
- package/src/primitives/overlay/strategies/live-announcer/silent.spec.ts +12 -0
- package/src/primitives/overlay/strategies/live-announcer/silent.ts +5 -0
- package/src/primitives/overlay/strategies/mount/body-portal.spec.ts +40 -0
- package/src/primitives/overlay/strategies/mount/body-portal.ts +91 -0
- package/src/primitives/overlay/strategies/mount/in-container.spec.ts +43 -0
- package/src/primitives/overlay/strategies/mount/in-container.ts +39 -0
- package/src/primitives/overlay/strategies/mount/in-place.spec.ts +40 -0
- package/src/primitives/overlay/strategies/mount/in-place.ts +20 -0
- package/src/primitives/overlay/strategies/position/anchored-to.spec.ts +56 -0
- package/src/primitives/overlay/strategies/position/anchored-to.ts +248 -0
- package/src/primitives/overlay/strategies/position/corner.spec.ts +36 -0
- package/src/primitives/overlay/strategies/position/corner.ts +33 -0
- package/src/primitives/overlay/strategies/position/edge-sheet.spec.ts +32 -0
- package/src/primitives/overlay/strategies/position/edge-sheet.ts +36 -0
- package/src/primitives/overlay/strategies/position/in-place-sibling.spec.ts +25 -0
- package/src/primitives/overlay/strategies/position/in-place-sibling.ts +13 -0
- package/src/primitives/overlay/strategies/position/point-at.spec.ts +40 -0
- package/src/primitives/overlay/strategies/position/point-at.ts +34 -0
- package/src/primitives/overlay/strategies/position/viewport-centered.spec.ts +31 -0
- package/src/primitives/overlay/strategies/position/viewport-centered.ts +31 -0
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.spec.ts +15 -0
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.ts +39 -0
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.spec.ts +21 -0
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.ts +44 -0
- package/src/primitives/overlay/strategies/scroll-lock/none.spec.ts +9 -0
- package/src/primitives/overlay/strategies/scroll-lock/none.ts +5 -0
- package/src/primitives/overlay/strategies/trigger-event/on-click.spec.ts +32 -0
- package/src/primitives/overlay/strategies/trigger-event/on-click.ts +28 -0
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.spec.ts +45 -0
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.ts +70 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.spec.ts +35 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.ts +34 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus.spec.ts +64 -0
- package/src/primitives/overlay/strategies/trigger-event/on-focus.ts +37 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.spec.ts +37 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.ts +59 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hover.spec.ts +34 -0
- package/src/primitives/overlay/strategies/trigger-event/on-hover.ts +86 -0
- package/src/primitives/overlay/strategies/trigger-event/programmatic.spec.ts +16 -0
- package/src/primitives/overlay/strategies/trigger-event/programmatic.ts +12 -0
- package/src/primitives/overlay/tokens.ts +54 -0
- package/src/primitives/overlay/trigger.spec.ts +103 -0
- package/src/primitives/overlay/trigger.ts +75 -0
- package/src/primitives/overlay/types.ts +24 -0
- package/src/primitives/overlay/wrapper.ts +50 -0
- package/src/primitives/signals/deep-signal.ts +109 -0
- package/src/primitives/signals/index.ts +1 -0
- package/src/progress-bar/config.ts +48 -0
- package/src/progress-bar/index.ts +12 -0
- package/src/progress-bar/progress-bar-fill.ts +37 -0
- package/src/progress-bar/progress-bar.context.ts +34 -0
- package/src/progress-bar/progress-bar.spec.ts +423 -0
- package/src/progress-bar/progress-bar.ts +217 -0
- package/src/public-api.ts +58 -9
- package/src/radio/radio.ts +14 -3
- package/src/select/index.ts +4 -2
- package/src/select/select-content.ts +69 -0
- package/src/select/select-option.ts +35 -0
- package/src/select/select-root.ts +129 -0
- package/src/select/select-trigger.ts +93 -0
- package/src/select/select.spec.ts +133 -140
- package/src/select/select.ts +4 -250
- package/src/skeleton/index.ts +5 -0
- package/src/skeleton/skeleton.spec.ts +115 -0
- package/src/skeleton/skeleton.ts +56 -0
- package/src/slider/index.ts +10 -0
- package/src/slider/slider-range.ts +49 -0
- package/src/slider/slider-thumb.ts +335 -0
- package/src/slider/slider-track.ts +70 -0
- package/src/slider/slider.context.ts +79 -0
- package/src/slider/slider.geometry.ts +73 -0
- package/src/slider/slider.spec.ts +216 -0
- package/src/slider/slider.ts +443 -0
- package/src/speed-dial/index.ts +10 -0
- package/src/speed-dial/speed-dial-action.ts +40 -0
- package/src/speed-dial/speed-dial-actions.ts +31 -0
- package/src/speed-dial/speed-dial-trigger.ts +55 -0
- package/src/speed-dial/speed-dial.context.ts +36 -0
- package/src/speed-dial/speed-dial.spec.ts +143 -0
- package/src/speed-dial/speed-dial.ts +127 -0
- package/src/spinner/config.ts +48 -0
- package/src/spinner/index.ts +8 -0
- package/src/spinner/spinner.spec.ts +262 -0
- package/src/spinner/spinner.ts +129 -0
- package/src/stepper/index.ts +15 -0
- package/src/stepper/stepper.context.ts +63 -0
- package/src/stepper/stepper.spec.ts +403 -0
- package/src/stepper/stepper.ts +544 -0
- package/src/styles.css +17 -0
- package/src/table/column-helpers.spec.ts +74 -0
- package/src/table/column-helpers.ts +68 -0
- package/src/table/filter-fns.ts +230 -0
- package/src/table/filter-models.spec.ts +240 -0
- package/src/table/filter-models.ts +124 -0
- package/src/table/filter-params.ts +90 -0
- package/src/table/grid-api-impl.ts +321 -0
- package/src/table/grid-api.spec.ts +178 -0
- package/src/table/grid-api.ts +285 -0
- package/src/table/index.ts +73 -1
- package/src/table/table-cell.spec.ts +43 -0
- package/src/table/table-cell.ts +38 -0
- package/src/table/table-filter-outlet.ts +120 -0
- package/src/table/table-header.spec.ts +54 -0
- package/src/table/table-header.ts +63 -0
- package/src/table/table-keyboard.spec.ts +68 -0
- package/src/table/table-keyboard.ts +107 -0
- package/src/table/table-resource.spec.ts +37 -0
- package/src/table/table-resource.ts +36 -0
- package/src/table/table-row.spec.ts +54 -0
- package/src/table/table-row.ts +31 -0
- package/src/table/table-storage.spec.ts +67 -0
- package/src/table/table-storage.ts +76 -0
- package/src/table/table.spec.ts +102 -43
- package/src/table/table.ts +228 -73
- package/src/table/table.types.ts +83 -0
- package/src/tabs/index.ts +7 -2
- package/src/tabs/tabs.context.ts +27 -4
- package/src/tabs/tabs.spec.ts +336 -24
- package/src/tabs/tabs.ts +313 -79
- package/src/tag/config.ts +53 -0
- package/src/tag/index.ts +7 -0
- package/src/tag/tag-list.ts +76 -0
- package/src/tag/tag-remove.ts +61 -0
- package/src/tag/tag.context.ts +36 -0
- package/src/tag/tag.spec.ts +240 -0
- package/src/tag/tag.ts +203 -0
- package/src/textarea/config.ts +42 -0
- package/src/textarea/index.ts +8 -0
- package/src/textarea/textarea.spec.ts +176 -0
- package/src/textarea/textarea.ts +235 -0
- package/src/time-picker/index.ts +15 -0
- package/src/time-picker/time-picker-meridiem.ts +123 -0
- package/src/time-picker/time-picker-segment.ts +318 -0
- package/src/time-picker/time-picker.context.ts +38 -0
- package/src/time-picker/time-picker.format.ts +181 -0
- package/src/time-picker/time-picker.spec.ts +189 -0
- package/src/time-picker/time-picker.ts +274 -0
- package/src/toast/_examples/index.ts +5 -0
- package/src/toast/_examples/toast.example.ts +282 -0
- package/src/toast/_examples/toast.finance.example.ts +180 -0
- package/src/toast/_examples/toast.retro.example.ts +161 -0
- package/src/toast/index.ts +8 -1
- package/src/toast/toast.ref.ts +26 -0
- package/src/toast/toast.service.ts +227 -41
- package/src/toast/toast.spec.ts +178 -25
- package/src/toast/toast.strategy.ts +10 -0
- package/src/toast/toast.ts +178 -22
- package/src/toggle/toggle.ts +10 -2
- package/src/tooltip/_examples/index.ts +6 -0
- package/src/tooltip/_examples/tooltip.example.ts +82 -0
- package/src/tooltip/_examples/tooltip.finance.example.ts +97 -0
- package/src/tooltip/_examples/tooltip.placements.example.ts +83 -0
- package/src/tooltip/_examples/tooltip.retro.example.ts +107 -0
- package/src/tooltip/index.ts +4 -5
- package/src/tooltip/tooltip-arrow.ts +31 -0
- package/src/tooltip/tooltip-content.ts +38 -0
- package/src/tooltip/tooltip-group.ts +32 -0
- package/src/tooltip/tooltip-trigger.ts +47 -0
- package/src/tooltip/tooltip.spec.ts +38 -102
- package/src/tooltip/tooltip.ts +5 -129
- package/src/tree-select/index.ts +12 -0
- package/src/tree-select/tree-select-content.ts +199 -0
- package/src/tree-select/tree-select-node.ts +132 -0
- package/src/tree-select/tree-select-root.ts +272 -0
- package/src/tree-select/tree-select-trigger.ts +90 -0
- package/src/tree-select/tree-select.context.ts +63 -0
- package/src/tree-select/tree-select.spec.ts +433 -0
- package/src/tree-select/tree-select.ts +2 -0
- package/src/typography/blockquote.ts +57 -0
- package/src/typography/code.ts +53 -0
- package/src/typography/index.ts +22 -0
- package/src/typography/lead.ts +49 -0
- package/src/typography/muted.ts +29 -0
- package/src/typography/prose.css +312 -0
- package/src/typography/truncate.ts +99 -0
- package/src/typography/typography.spec.ts +203 -0
- package/tsconfig.lib.json +9 -1
- package/tsconfig.lib.prod.json +7 -1
- package/tsconfig.spec.json +9 -1
- package/vite.config.ts +8 -0
- package/src/button/button.example.ts +0 -30
- package/src/button/button.finance.example.ts +0 -37
- package/src/button/button.retro.example.ts +0 -47
- package/src/button/button.sizes.example.ts +0 -25
- package/src/dialog/dialog.confirm.example.ts +0 -57
- package/src/dialog/dialog.context.ts +0 -11
- package/src/dialog/dialog.example.ts +0 -40
- package/src/dialog/dialog.finance.example.ts +0 -47
- package/src/dialog/dialog.retro.example.ts +0 -46
- package/src/menu/index.ts +0 -2
- package/src/menu/menu.context.ts +0 -10
- package/src/menu/menu.example.ts +0 -39
- package/src/menu/menu.spec.ts +0 -168
- package/src/menu/menu.ts +0 -176
- package/src/popover/popover.context.ts +0 -18
- package/src/popover/popover.example.ts +0 -50
- package/src/popover/popover.finance.example.ts +0 -52
- package/src/popover/popover.retro.example.ts +0 -53
- package/src/primitives/overlay/overlay.spec.ts +0 -90
- package/src/primitives/overlay/overlay.ts +0 -102
- package/src/select/select.context.ts +0 -3
- package/src/toast/toast.example.ts +0 -134
- package/src/toast/toast.finance.example.ts +0 -68
- package/src/toast/toast.retro.example.ts +0 -69
- package/src/tooltip/tooltip.example.ts +0 -46
- package/src/tooltip/tooltip.finance.example.ts +0 -54
- package/src/tooltip/tooltip.placements.example.ts +0 -56
- package/src/tooltip/tooltip.retro.example.ts +0 -65
package/src/tabs/tabs.ts
CHANGED
|
@@ -1,130 +1,364 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
Directive,
|
|
4
|
+
effect,
|
|
5
|
+
ElementRef,
|
|
6
|
+
inject,
|
|
7
|
+
input,
|
|
8
|
+
model,
|
|
9
|
+
OnDestroy,
|
|
10
|
+
OnInit,
|
|
11
|
+
output,
|
|
12
|
+
signal,
|
|
13
|
+
Signal,
|
|
14
|
+
untracked,
|
|
15
|
+
} from '@angular/core';
|
|
16
|
+
import { KjRovingTabindex, KjRovingTabindexItemDirective } from '../a11y/roving-tabindex';
|
|
17
|
+
import { KjDisabled } from '../primitives/interaction/disabled';
|
|
18
|
+
import { KjFocusRing } from '../primitives/interaction/focus-ring';
|
|
19
|
+
import {
|
|
20
|
+
KJ_TABS,
|
|
21
|
+
KjTabsActivationMode,
|
|
22
|
+
KjTabsContext,
|
|
23
|
+
KjTabsOrientation,
|
|
24
|
+
} from './tabs.context';
|
|
25
|
+
|
|
26
|
+
let kjTabsSeedCounter = 0;
|
|
27
|
+
function nextSeed(): string {
|
|
28
|
+
// Try crypto.randomUUID where available (browser, jsdom 22+, node 19+).
|
|
29
|
+
// Fall back to a counter so SSR / older environments still produce a stable string.
|
|
30
|
+
const cryptoLike = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;
|
|
31
|
+
if (cryptoLike?.randomUUID) return cryptoLike.randomUUID().slice(0, 8);
|
|
32
|
+
return `kj${(++kjTabsSeedCounter).toString(36)}`;
|
|
33
|
+
}
|
|
3
34
|
|
|
4
35
|
/**
|
|
5
|
-
* Root tabs container.
|
|
36
|
+
* Root tabs container. Owns the active value, orientation, activation mode,
|
|
37
|
+
* and the registration list of child tabs/panels. Provides `KJ_TABS` to its
|
|
38
|
+
* descendants for ARIA wiring and selection coordination.
|
|
39
|
+
*
|
|
6
40
|
* @example
|
|
7
41
|
* ```html
|
|
8
|
-
* <div kjTabs [(
|
|
42
|
+
* <div kjTabs [(kjValue)]="active" kjOrientation="horizontal" kjActivationMode="automatic">
|
|
43
|
+
* <div kjTabList>
|
|
44
|
+
* <button kjTab kjTabValue="overview">Overview</button>
|
|
45
|
+
* <button kjTab kjTabValue="billing">Billing</button>
|
|
46
|
+
* </div>
|
|
47
|
+
* <div kjTabPanel kjPanelValue="overview">…</div>
|
|
48
|
+
* <div kjTabPanel kjPanelValue="billing">…</div>
|
|
49
|
+
* </div>
|
|
9
50
|
* ```
|
|
51
|
+
* @doc-category Core/Navigation
|
|
10
52
|
* @doc
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* import { KjTabs, KjTabList, KjTab, KjTabPanel } from '@kouji-ui/core';
|
|
15
|
-
*
|
|
16
|
-
* @Component({
|
|
17
|
-
* standalone: true,
|
|
18
|
-
* imports: [KjTabs, KjTabList, KjTab, KjTabPanel],
|
|
19
|
-
* styles: [`
|
|
20
|
-
* .tabs { background: #0c0c0c; padding: 2rem; }
|
|
21
|
-
* [kjTabList] { display: flex; border-bottom: 1px solid #1e1e1e; margin-bottom: 1.5rem; }
|
|
22
|
-
* [kjTab] { background: none; border: none; border-bottom: 2px solid transparent; color: #555; font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; padding: 0.625rem 1.25rem; cursor: pointer; transition: color 0.15s, border-color 0.15s; margin-bottom: -1px; }
|
|
23
|
-
* [kjTab][aria-selected="true"] { color: #f0ede6; border-bottom-color: #b8f500; }
|
|
24
|
-
* [kjTab]:hover { color: #f0ede6; }
|
|
25
|
-
* [kjTabPanel] { color: #666; font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; line-height: 1.6; }
|
|
26
|
-
* [hidden] { display: none; }
|
|
27
|
-
* `],
|
|
28
|
-
* template: `
|
|
29
|
-
* <div class="tabs">
|
|
30
|
-
* <div kjTabs [(kjTabsValue)]="activeTab" aria-label="Example tabs">
|
|
31
|
-
* <div kjTabList>
|
|
32
|
-
* <button kjTab [kjTabValue]="'tab1'">Overview</button>
|
|
33
|
-
* <button kjTab [kjTabValue]="'tab2'">API</button>
|
|
34
|
-
* <button kjTab [kjTabValue]="'tab3'">Examples</button>
|
|
35
|
-
* </div>
|
|
36
|
-
* <div kjTabPanel [kjPanelFor]="'tab1'">The overview tab content goes here.</div>
|
|
37
|
-
* <div kjTabPanel [kjPanelFor]="'tab2'">API reference content goes here.</div>
|
|
38
|
-
* <div kjTabPanel [kjPanelFor]="'tab3'">Code examples go here.</div>
|
|
39
|
-
* </div>
|
|
40
|
-
* </div>
|
|
41
|
-
* `,
|
|
42
|
-
* })
|
|
43
|
-
* export class TabsExampleComponent {
|
|
44
|
-
* activeTab = signal('tab1');
|
|
45
|
-
* }
|
|
46
|
-
* ```
|
|
47
|
-
* @category Core/Navigation
|
|
53
|
+
* @doc-name tabs
|
|
54
|
+
* @doc-description Unstyled tabs root with active-value, orientation, and activation mode for tab and panel children.
|
|
55
|
+
* @doc-is-main
|
|
48
56
|
*/
|
|
49
57
|
@Directive({
|
|
50
58
|
selector: '[kjTabs]',
|
|
51
59
|
standalone: true,
|
|
52
60
|
providers: [{ provide: KJ_TABS, useExisting: KjTabs }],
|
|
61
|
+
host: {
|
|
62
|
+
'[attr.data-orientation]': 'kjOrientation()',
|
|
63
|
+
},
|
|
53
64
|
})
|
|
54
65
|
export class KjTabs implements KjTabsContext {
|
|
55
|
-
/** Two-way bound active tab value. */
|
|
56
|
-
|
|
66
|
+
/** Two-way bound active tab value. String-keyed; `''` means "no tab active yet". */
|
|
67
|
+
readonly kjValue = model<string>('');
|
|
68
|
+
|
|
69
|
+
/** Tabs orientation. Drives `aria-orientation` on the tablist and the arrow-key axis. */
|
|
70
|
+
readonly kjOrientation = input<KjTabsOrientation>('horizontal');
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Activation mode per WAI-ARIA APG.
|
|
74
|
+
* - `'automatic'` (default): focus moves and the focused tab is activated immediately.
|
|
75
|
+
* - `'manual'`: focus moves only; Enter or Space activates the focused tab.
|
|
76
|
+
*/
|
|
77
|
+
readonly kjActivationMode = input<KjTabsActivationMode>('automatic');
|
|
78
|
+
|
|
79
|
+
/** Read-only view of the active tab value. */
|
|
80
|
+
readonly value: Signal<string> = this.kjValue.asReadonly();
|
|
81
|
+
|
|
82
|
+
/** Read-only view of the orientation. */
|
|
83
|
+
readonly orientation: Signal<KjTabsOrientation> = this.kjOrientation;
|
|
84
|
+
|
|
85
|
+
/** Read-only view of the activation mode. */
|
|
86
|
+
readonly activationMode: Signal<KjTabsActivationMode> = this.kjActivationMode;
|
|
87
|
+
|
|
88
|
+
private readonly idSeed = nextSeed();
|
|
89
|
+
|
|
90
|
+
/** @internal Map of value → registered tab; keeps document order via insertion. */
|
|
91
|
+
private readonly _tabs = signal<readonly KjTab[]>([]);
|
|
92
|
+
|
|
93
|
+
/** Public read-only registration list, in document order. */
|
|
94
|
+
readonly tabs = this._tabs.asReadonly();
|
|
95
|
+
|
|
96
|
+
/** Currently resolved active tab (may be undefined when value matches no tab). */
|
|
97
|
+
readonly activeTab = computed<KjTab | undefined>(() =>
|
|
98
|
+
this._tabs().find((t) => t.kjTabValue() === this.kjValue()),
|
|
99
|
+
);
|
|
57
100
|
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
constructor() {
|
|
102
|
+
// Default-value reconciliation: when kjValue is unset (`''`) and at least one
|
|
103
|
+
// non-disabled tab is registered, activate the first non-disabled tab.
|
|
104
|
+
effect(() => {
|
|
105
|
+
const list = this._tabs();
|
|
106
|
+
const current = this.kjValue();
|
|
107
|
+
if (current === '' && list.length > 0) {
|
|
108
|
+
const first = untracked(() => list.find((t) => !t.kjTabDisabled()) ?? list[0]);
|
|
109
|
+
if (first) {
|
|
110
|
+
// Defer to avoid writing during a parent change-detection pass.
|
|
111
|
+
queueMicrotask(() => {
|
|
112
|
+
if (this.kjValue() === '') this.kjValue.set(first.kjTabValue());
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** @internal Called by `KjTab.ngOnInit` to register itself in document order. */
|
|
120
|
+
register(tab: KjTab): void {
|
|
121
|
+
this._tabs.update((list) => (list.includes(tab) ? list : [...list, tab]));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @internal Called by `KjTab` on destroy. */
|
|
125
|
+
unregister(tab: KjTab): void {
|
|
126
|
+
this._tabs.update((list) => list.filter((t) => t !== tab));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Returns whether the tab/panel pair for `value` is the active one. */
|
|
130
|
+
isActive(value: string): boolean {
|
|
131
|
+
return this.kjValue() === value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Stable id for the tab element associated with `value`. */
|
|
135
|
+
tabId(value: string): string {
|
|
136
|
+
return `kj-tab-${value}-${this.idSeed}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Stable id for the panel element associated with `value`. */
|
|
140
|
+
panelId(value: string): string {
|
|
141
|
+
return `kj-panel-${value}-${this.idSeed}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Activates the tab/panel pair for `value`. No-op if the matching tab is disabled. */
|
|
145
|
+
select(value: string): void {
|
|
146
|
+
const target = this._tabs().find((t) => t.kjTabValue() === value);
|
|
147
|
+
if (target?.kjTabDisabled()) return;
|
|
148
|
+
if (this.kjValue() !== value) this.kjValue.set(value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Imperative: advance to the next non-disabled tab. Clamps at the end. */
|
|
152
|
+
next(): void {
|
|
153
|
+
const list = this._tabs();
|
|
154
|
+
const idx = list.findIndex((t) => t.kjTabValue() === this.kjValue());
|
|
155
|
+
for (let i = idx + 1; i < list.length; i++) {
|
|
156
|
+
if (!list[i].kjTabDisabled()) {
|
|
157
|
+
this.select(list[i].kjTabValue());
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
60
162
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
this.
|
|
163
|
+
/** Imperative: advance to the previous non-disabled tab. Clamps at the start. */
|
|
164
|
+
previous(): void {
|
|
165
|
+
const list = this._tabs();
|
|
166
|
+
const idx = list.findIndex((t) => t.kjTabValue() === this.kjValue());
|
|
167
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
168
|
+
if (!list[i].kjTabDisabled()) {
|
|
169
|
+
this.select(list[i].kjTabValue());
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
64
173
|
}
|
|
65
174
|
}
|
|
66
175
|
|
|
67
176
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
177
|
+
* Tab list container. Hosts `role="tablist"` + `aria-orientation` and composes
|
|
178
|
+
* `KjRovingTabindex` to manage the single-tab-stop arrow-key navigation.
|
|
179
|
+
*
|
|
180
|
+
* Orientation is read from the parent `KJ_TABS` context and forwarded to the
|
|
181
|
+
* roving primitive through its `kjRovingOrientation` input via a host binding
|
|
182
|
+
* on the exposed input.
|
|
183
|
+
*
|
|
184
|
+
* @doc-category Core/Navigation
|
|
185
|
+
* @doc
|
|
186
|
+
* @doc-name tabs
|
|
70
187
|
*/
|
|
71
188
|
@Directive({
|
|
72
189
|
selector: '[kjTabList]',
|
|
73
190
|
standalone: true,
|
|
74
|
-
|
|
191
|
+
hostDirectives: [KjRovingTabindex],
|
|
192
|
+
host: {
|
|
193
|
+
'[attr.role]': '"tablist"',
|
|
194
|
+
'[attr.aria-orientation]': 'tabs.orientation()',
|
|
195
|
+
'[attr.data-orientation]': 'tabs.orientation()',
|
|
196
|
+
},
|
|
75
197
|
})
|
|
76
|
-
export class KjTabList {
|
|
198
|
+
export class KjTabList implements OnInit, OnDestroy {
|
|
199
|
+
/** @internal */
|
|
200
|
+
readonly tabs = inject(KJ_TABS);
|
|
201
|
+
private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
202
|
+
|
|
203
|
+
private readonly keydownFilter = (event: KeyboardEvent): void => {
|
|
204
|
+
const orientation = this.tabs.orientation();
|
|
205
|
+
if (orientation === 'horizontal') {
|
|
206
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
207
|
+
// Off-axis: prevent the composed KjRovingTabindex from acting.
|
|
208
|
+
event.stopImmediatePropagation();
|
|
209
|
+
}
|
|
210
|
+
} else if (orientation === 'vertical') {
|
|
211
|
+
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
|
212
|
+
event.stopImmediatePropagation();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
ngOnInit(): void {
|
|
218
|
+
// Capture phase + `addEventListener` directly on the host: this listener
|
|
219
|
+
// fires before the composed KjRovingTabindex's `(keydown)` host binding,
|
|
220
|
+
// so we can swallow off-axis arrow keys per the parent KjTabs orientation
|
|
221
|
+
// (the WAI-ARIA APG contract for tab strips).
|
|
222
|
+
this.el.nativeElement.addEventListener('keydown', this.keydownFilter, true);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
ngOnDestroy(): void {
|
|
226
|
+
this.el.nativeElement.removeEventListener('keydown', this.keydownFilter, true);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
77
229
|
|
|
78
230
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
231
|
+
* Tab trigger. Hosts `role="tab"` + the ARIA wiring (`aria-selected`,
|
|
232
|
+
* `aria-controls`, `id`) and composes `KjRovingTabindexItem`, `KjFocusRing`
|
|
233
|
+
* and `KjDisabled`. Click activates the tab; Enter/Space activate (always
|
|
234
|
+
* honoured, required in manual mode); Delete fires `kjClose` when
|
|
235
|
+
* `kjClosable` is true.
|
|
236
|
+
*
|
|
237
|
+
* @doc-category Core/Navigation
|
|
238
|
+
* @doc
|
|
239
|
+
* @doc-name tabs
|
|
82
240
|
*/
|
|
83
241
|
@Directive({
|
|
84
242
|
selector: '[kjTab]',
|
|
85
243
|
standalone: true,
|
|
244
|
+
hostDirectives: [KjRovingTabindexItemDirective, KjFocusRing, KjDisabled],
|
|
86
245
|
host: {
|
|
87
|
-
role: 'tab',
|
|
88
|
-
'[attr.
|
|
89
|
-
'[attr.
|
|
90
|
-
'[attr.
|
|
91
|
-
'
|
|
246
|
+
'[attr.role]': '"tab"',
|
|
247
|
+
'[attr.id]': 'tabs.tabId(kjTabValue())',
|
|
248
|
+
'[attr.aria-controls]': 'tabs.panelId(kjTabValue())',
|
|
249
|
+
'[attr.aria-selected]': 'isActive() ? "true" : "false"',
|
|
250
|
+
'[attr.aria-disabled]': 'kjTabDisabled() ? "true" : null',
|
|
251
|
+
'[attr.data-state]': 'isActive() ? "active" : "inactive"',
|
|
252
|
+
'[attr.data-disabled]': 'kjTabDisabled() ? "" : null',
|
|
253
|
+
'(click)': 'onClick()',
|
|
254
|
+
'(keydown)': 'onKeydown($event)',
|
|
255
|
+
'(focus)': 'onFocus()',
|
|
92
256
|
},
|
|
93
257
|
})
|
|
94
|
-
export class KjTab {
|
|
95
|
-
|
|
258
|
+
export class KjTab implements OnInit, OnDestroy {
|
|
259
|
+
/** @internal */
|
|
260
|
+
readonly tabs = inject(KJ_TABS) as KjTabs;
|
|
261
|
+
/** @internal Native host element. */
|
|
262
|
+
readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
263
|
+
|
|
264
|
+
/** Required string key wiring this tab to its corresponding panel. */
|
|
265
|
+
readonly kjTabValue = input.required<string>();
|
|
266
|
+
|
|
267
|
+
/** When true, the tab cannot be activated but remains in the focus ring. */
|
|
268
|
+
readonly kjTabDisabled = input<boolean>(false);
|
|
96
269
|
|
|
97
|
-
/**
|
|
98
|
-
|
|
270
|
+
/** When true, Delete on the focused tab fires `kjClose`. */
|
|
271
|
+
readonly kjClosable = input<boolean>(false);
|
|
99
272
|
|
|
100
|
-
/**
|
|
101
|
-
readonly
|
|
273
|
+
/** Fires when the user closes this tab (Delete key). Emits the tab's value. */
|
|
274
|
+
readonly kjClose = output<string>();
|
|
102
275
|
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
276
|
+
/** Whether this tab is the currently active one. */
|
|
277
|
+
readonly isActive = computed(() => this.tabs.isActive(this.kjTabValue()));
|
|
278
|
+
|
|
279
|
+
ngOnInit(): void {
|
|
280
|
+
this.tabs.register(this);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
ngOnDestroy(): void {
|
|
284
|
+
this.tabs.unregister(this);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** @internal Click handler. */
|
|
288
|
+
onClick(): void {
|
|
289
|
+
if (this.kjTabDisabled()) return;
|
|
290
|
+
this.tabs.select(this.kjTabValue());
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** @internal Focus handler — activates immediately in `automatic` mode. */
|
|
294
|
+
onFocus(): void {
|
|
295
|
+
if (this.kjTabDisabled()) return;
|
|
296
|
+
if (this.tabs.activationMode() === 'automatic') {
|
|
297
|
+
this.tabs.select(this.kjTabValue());
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** @internal Keydown — Enter/Space activate (always honoured); Delete closes when closable. */
|
|
302
|
+
onKeydown(event: KeyboardEvent): void {
|
|
303
|
+
if (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar') {
|
|
304
|
+
if (this.kjTabDisabled()) return;
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
this.tabs.select(this.kjTabValue());
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (event.key === 'Delete' && this.kjClosable()) {
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
this.kjClose.emit(this.kjTabValue());
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
106
314
|
}
|
|
107
315
|
}
|
|
108
316
|
|
|
109
317
|
/**
|
|
110
|
-
* Tab panel
|
|
111
|
-
*
|
|
112
|
-
*
|
|
318
|
+
* Tab panel. Hosts `role="tabpanel"` + `aria-labelledby` + `id` + `hidden`.
|
|
319
|
+
* Tracks first-activation history via `mounted`: it flips `true` on first
|
|
320
|
+
* activation and stays `true` thereafter, so consumers can gate the
|
|
321
|
+
* projected content with `@if (panel.mounted())` to implement the
|
|
322
|
+
* lazy-then-persistent mount posture from the analysis.
|
|
323
|
+
*
|
|
324
|
+
* @doc-category Core/Navigation
|
|
325
|
+
* @doc
|
|
326
|
+
* @doc-name tabs
|
|
113
327
|
*/
|
|
114
328
|
@Directive({
|
|
115
329
|
selector: '[kjTabPanel]',
|
|
116
330
|
standalone: true,
|
|
331
|
+
exportAs: 'kjTabPanel',
|
|
117
332
|
host: {
|
|
118
|
-
role: 'tabpanel',
|
|
119
|
-
'[attr.
|
|
333
|
+
'[attr.role]': '"tabpanel"',
|
|
334
|
+
'[attr.id]': 'tabs.panelId(kjPanelValue())',
|
|
335
|
+
'[attr.aria-labelledby]': 'tabs.tabId(kjPanelValue())',
|
|
336
|
+
'[attr.hidden]': 'isActive() ? null : ""',
|
|
337
|
+
'[attr.data-state]': 'isActive() ? "active" : "inactive"',
|
|
120
338
|
},
|
|
121
339
|
})
|
|
122
340
|
export class KjTabPanel {
|
|
123
|
-
|
|
341
|
+
/** @internal */
|
|
342
|
+
readonly tabs = inject(KJ_TABS);
|
|
343
|
+
|
|
344
|
+
/** Required string key wiring this panel to its corresponding tab. */
|
|
345
|
+
readonly kjPanelValue = input.required<string>();
|
|
124
346
|
|
|
125
|
-
/**
|
|
126
|
-
|
|
347
|
+
/** Whether this panel is the active one. */
|
|
348
|
+
readonly isActive = computed(() => this.tabs.isActive(this.kjPanelValue()));
|
|
127
349
|
|
|
128
|
-
|
|
129
|
-
|
|
350
|
+
private readonly _mounted = signal(false);
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* `true` after this panel has been activated at least once. Consumers wrap
|
|
354
|
+
* the panel's projected content in `@if (panel.mounted())` to mount the
|
|
355
|
+
* content lazily on first activation and keep it mounted thereafter.
|
|
356
|
+
*/
|
|
357
|
+
readonly mounted = this._mounted.asReadonly();
|
|
358
|
+
|
|
359
|
+
constructor() {
|
|
360
|
+
effect(() => {
|
|
361
|
+
if (this.isActive() && !this._mounted()) this._mounted.set(true);
|
|
362
|
+
});
|
|
363
|
+
}
|
|
130
364
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
/** Preset configuration consumed by `KjTag` via `bindPresets`. */
|
|
4
|
+
export interface KjTagConfig {
|
|
5
|
+
variants: string[];
|
|
6
|
+
sizes: string[];
|
|
7
|
+
defaults: { variant: string; size: string };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default Tag presets shipped by kouji-ui. Variant list is intentionally
|
|
12
|
+
* kept in lock-step with `KjBadge` (see analysis §"Variant parity with
|
|
13
|
+
* Badge") — a non-interactive Tag and a Badge with the same `kjVariant`
|
|
14
|
+
* must look identical.
|
|
15
|
+
*/
|
|
16
|
+
export const KJ_TAG_DEFAULTS: KjTagConfig = {
|
|
17
|
+
variants: [
|
|
18
|
+
'default',
|
|
19
|
+
'primary',
|
|
20
|
+
'secondary',
|
|
21
|
+
'success',
|
|
22
|
+
'warning',
|
|
23
|
+
'danger',
|
|
24
|
+
'info',
|
|
25
|
+
'outline',
|
|
26
|
+
'ghost',
|
|
27
|
+
],
|
|
28
|
+
sizes: ['xs', 'sm', 'md', 'lg'],
|
|
29
|
+
defaults: { variant: 'default', size: 'md' },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* DI token for the active Tag presets. Default factory yields
|
|
34
|
+
* `KJ_TAG_DEFAULTS`. Override via `provideKjTag(...)` at the application
|
|
35
|
+
* scope or at a component scope.
|
|
36
|
+
*/
|
|
37
|
+
export const KJ_TAG_CONFIG = new InjectionToken<KjTagConfig>('kj.tag.config', {
|
|
38
|
+
factory: () => KJ_TAG_DEFAULTS,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Configures the Tag presets for the enclosing injector. Replaces (does
|
|
43
|
+
* not merge) `variants` and `sizes`; spread `KJ_TAG_DEFAULTS.variants` to
|
|
44
|
+
* extend.
|
|
45
|
+
*/
|
|
46
|
+
export function provideKjTag(config: Partial<KjTagConfig>): Provider[] {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
provide: KJ_TAG_CONFIG,
|
|
50
|
+
useValue: { ...KJ_TAG_DEFAULTS, ...config },
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
package/src/tag/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { KjTag } from './tag';
|
|
2
|
+
export { KjTagRemove } from './tag-remove';
|
|
3
|
+
export { KjTagList } from './tag-list';
|
|
4
|
+
export { KJ_TAG, KJ_TAG_LIST } from './tag.context';
|
|
5
|
+
export type { KjTagContext, KjTagListContext, KjTagListRole } from './tag.context';
|
|
6
|
+
export { KJ_TAG_CONFIG, KJ_TAG_DEFAULTS, provideKjTag } from './config';
|
|
7
|
+
export type { KjTagConfig } from './config';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Directive, Signal, computed, input } from '@angular/core';
|
|
2
|
+
import { KjRovingTabindex } from '../a11y/roving-tabindex';
|
|
3
|
+
import { KJ_TAG_LIST, KjTagListContext, KjTagListRole } from './tag.context';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Optional container that coordinates a group of `KjTag` chips. Provides
|
|
7
|
+
* the chip-group keyboard story (roving tabindex via `KjRovingTabindex`)
|
|
8
|
+
* and the ARIA wiring for the `listbox` / `grid` / `group` shapes called
|
|
9
|
+
* out in the analysis.
|
|
10
|
+
*
|
|
11
|
+
* Standalone tags work fine without this container — the container is the
|
|
12
|
+
* opt-in surface that turns a pile of independent chips into a single
|
|
13
|
+
* keyboardable composite.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```html
|
|
17
|
+
* <div kjTagList kjTagListRole="listbox" [kjTagListMultiple]="true" aria-label="Filters">
|
|
18
|
+
* <span kjTag kjTagSelectable>One</span>
|
|
19
|
+
* <span kjTag kjTagSelectable>Two</span>
|
|
20
|
+
* </div>
|
|
21
|
+
* ```
|
|
22
|
+
* @doc-category Core/Data display
|
|
23
|
+
* @doc
|
|
24
|
+
* @doc-name tag
|
|
25
|
+
*/
|
|
26
|
+
@Directive({
|
|
27
|
+
selector: '[kjTagList]',
|
|
28
|
+
standalone: true,
|
|
29
|
+
providers: [{ provide: KJ_TAG_LIST, useExisting: KjTagList }],
|
|
30
|
+
hostDirectives: [
|
|
31
|
+
{ directive: KjRovingTabindex, inputs: ['kjRovingOrientation: kjTagListOrientation'] },
|
|
32
|
+
],
|
|
33
|
+
host: {
|
|
34
|
+
'[attr.role]': 'kjTagListRole()',
|
|
35
|
+
'[attr.aria-orientation]': 'ariaOrientation()',
|
|
36
|
+
'[attr.aria-multiselectable]': 'ariaMultiSelectable()',
|
|
37
|
+
'[attr.aria-disabled]': 'kjTagListDisabled() ? "true" : null',
|
|
38
|
+
'[attr.tabindex]': 'kjTagListRole() !== "group" ? "-1" : null',
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
export class KjTagList implements KjTagListContext {
|
|
42
|
+
/** Container ARIA role — drives chip role selection (option / row / none). */
|
|
43
|
+
readonly kjTagListRole = input<KjTagListRole>('group');
|
|
44
|
+
|
|
45
|
+
/** Roving tabindex axis. Forwarded to `KjRovingTabindex`. */
|
|
46
|
+
readonly kjTagListOrientation = input<'horizontal' | 'vertical' | 'both'>('horizontal');
|
|
47
|
+
|
|
48
|
+
/** Only meaningful in `listbox` mode. Drives `aria-multiselectable`. */
|
|
49
|
+
readonly kjTagListMultiple = input(false);
|
|
50
|
+
|
|
51
|
+
/** Cascading disabled flag. Each chip's effective disabled OR-merges this. */
|
|
52
|
+
readonly kjTagListDisabled = input(false);
|
|
53
|
+
|
|
54
|
+
/** Read-only role view used by `KjTag` to compute its own role. */
|
|
55
|
+
readonly role: Signal<KjTagListRole> = this.kjTagListRole;
|
|
56
|
+
|
|
57
|
+
/** Read-only disabled view used by `KjTag` to merge into its effective disabled. */
|
|
58
|
+
readonly disabled: Signal<boolean> = this.kjTagListDisabled;
|
|
59
|
+
|
|
60
|
+
/** Read-only multi-selectable view (listbox-only meaning). */
|
|
61
|
+
readonly multiple: Signal<boolean> = this.kjTagListMultiple;
|
|
62
|
+
|
|
63
|
+
/** Only emit `aria-orientation` when the container has a real role. */
|
|
64
|
+
protected readonly ariaOrientation = computed<string | null>(() => {
|
|
65
|
+
if (this.kjTagListRole() === 'group') return null;
|
|
66
|
+
return this.kjTagListOrientation() === 'both'
|
|
67
|
+
? 'horizontal'
|
|
68
|
+
: this.kjTagListOrientation();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/** `aria-multiselectable` only set in listbox mode. */
|
|
72
|
+
protected readonly ariaMultiSelectable = computed<string | null>(() => {
|
|
73
|
+
if (this.kjTagListRole() !== 'listbox') return null;
|
|
74
|
+
return this.kjTagListMultiple() ? 'true' : 'false';
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Directive, computed, inject, input } from '@angular/core';
|
|
2
|
+
import { KjButton } from '../button/button';
|
|
3
|
+
import { KJ_TAG } from './tag.context';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Trailing remove control for a `KjTag`. Composes `KjButton` so the remove
|
|
7
|
+
* action inherits native `<button>` semantics, ARIA-disabled handling,
|
|
8
|
+
* capture-phase click suppression, and the focus ring — there is no second
|
|
9
|
+
* keyboard / a11y story to maintain.
|
|
10
|
+
*
|
|
11
|
+
* The accessible name is derived automatically from the parent tag's
|
|
12
|
+
* projected text content (`"Remove {label}"`) via a `MutationObserver` on
|
|
13
|
+
* `KjTag`. Override with `kjTagRemoveLabel` when the auto-derived name is
|
|
14
|
+
* insufficient (e.g. icon-only tag, custom phrasing).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```html
|
|
18
|
+
* <span kjTag>
|
|
19
|
+
* Acme
|
|
20
|
+
* <button kjTagRemove>×</button>
|
|
21
|
+
* </span>
|
|
22
|
+
* ```
|
|
23
|
+
* @doc-category Core/Data display
|
|
24
|
+
* @doc
|
|
25
|
+
* @doc-name tag
|
|
26
|
+
*/
|
|
27
|
+
@Directive({
|
|
28
|
+
selector: '[kjTagRemove]',
|
|
29
|
+
standalone: true,
|
|
30
|
+
hostDirectives: [KjButton],
|
|
31
|
+
host: {
|
|
32
|
+
'type': 'button',
|
|
33
|
+
'[attr.aria-label]': 'effectiveLabel()',
|
|
34
|
+
'(click)': 'onClick($event)',
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
export class KjTagRemove {
|
|
38
|
+
private readonly tag = inject(KJ_TAG);
|
|
39
|
+
|
|
40
|
+
/** Override the auto-derived `aria-label` for the remove button. */
|
|
41
|
+
readonly kjTagRemoveLabel = input<string | undefined>(undefined);
|
|
42
|
+
|
|
43
|
+
/** Computed accessible name: explicit override wins, else `"Remove {label}"`. */
|
|
44
|
+
protected readonly effectiveLabel = computed<string>(() => {
|
|
45
|
+
const override = this.kjTagRemoveLabel();
|
|
46
|
+
if (override) return override;
|
|
47
|
+
const label = this.tag.textContent();
|
|
48
|
+
return label ? `Remove ${label}` : 'Remove';
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** @internal Click handler — fires the parent tag's `(kjTagRemoved)`. */
|
|
52
|
+
onClick(event: Event): void {
|
|
53
|
+
if (this.tag.disabled()) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
event.stopImmediatePropagation();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
event.stopPropagation();
|
|
59
|
+
this.tag.remove();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { InjectionToken, Signal } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public context exposed by `KjTag` to its descendants (notably
|
|
5
|
+
* `KjTagRemove`). Mirrors the minimal surface called out in the analysis:
|
|
6
|
+
* three signals + a `remove()` callback.
|
|
7
|
+
*/
|
|
8
|
+
export interface KjTagContext {
|
|
9
|
+
/** Live read of the projected text content (label) for the tag. */
|
|
10
|
+
readonly textContent: Signal<string>;
|
|
11
|
+
/** Whether the tag is currently selected (selectable shape only). */
|
|
12
|
+
readonly selected: Signal<boolean>;
|
|
13
|
+
/** Whether the tag is disabled (own + inherited from `KjTagList`). */
|
|
14
|
+
readonly disabled: Signal<boolean>;
|
|
15
|
+
/** Triggers removal: emits the parent's `(kjTagRemoved)` output. */
|
|
16
|
+
remove(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Role that a `KjTagList` container exposes. Drives chip role selection. */
|
|
20
|
+
export type KjTagListRole = 'listbox' | 'grid' | 'group';
|
|
21
|
+
|
|
22
|
+
/** Public context exposed by `KjTagList` so nested tags can self-configure. */
|
|
23
|
+
export interface KjTagListContext {
|
|
24
|
+
/** Active container role; chips read this to compute their own role. */
|
|
25
|
+
readonly role: Signal<KjTagListRole>;
|
|
26
|
+
/** Whether the entire tag list is disabled (cascades into chips). */
|
|
27
|
+
readonly disabled: Signal<boolean>;
|
|
28
|
+
/** Whether the listbox container allows multi-selection. */
|
|
29
|
+
readonly multiple: Signal<boolean>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Injection token providing the parent `KjTag` to projected children. */
|
|
33
|
+
export const KJ_TAG = new InjectionToken<KjTagContext>('KjTag');
|
|
34
|
+
|
|
35
|
+
/** Injection token providing the optional `KjTagList` container. */
|
|
36
|
+
export const KJ_TAG_LIST = new InjectionToken<KjTagListContext>('KjTagList');
|