@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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Directive, computed, input } from '@angular/core';
|
|
2
|
+
import { getIconMode } from './icon.mode';
|
|
3
|
+
import { injectKjIconResolver } from './icon.resolver';
|
|
4
|
+
import type { KjIconColor, KjIconSize } from './icon.types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Renders an icon on its host element via CSS custom properties. Owns icon
|
|
8
|
+
* accessibility (decorative by default; meaningful when `kjIconLabel` is set).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```html
|
|
12
|
+
* <span [kjIcon]="'settings'"></span>
|
|
13
|
+
* <span [kjIcon]="'alert-triangle'" [kjIconLabel]="'Warning'"></span>
|
|
14
|
+
* <span [kjIcon]="'check'" [kjIconColor]="'success'" [kjIconSize]="'lg'"></span>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @doc-example Gallery
|
|
18
|
+
* Browseable Lucide icon grid with a substring filter. Click any tile to
|
|
19
|
+
* copy its `<i kjIcon="…">` snippet.
|
|
20
|
+
* @doc-file ../../../components/src/icon/icon.gallery.example.ts
|
|
21
|
+
* @doc-example Usage
|
|
22
|
+
* Common icon shapes — decorative inline, meaningful with a label, sized
|
|
23
|
+
* variants, and semantic color tokens.
|
|
24
|
+
* @doc-file ../../../components/src/icon/icon.usage.example.ts
|
|
25
|
+
*
|
|
26
|
+
* @doc-keyboard
|
|
27
|
+
* None — the icon directive is non-interactive. Place inside a focusable parent (button/link) for activation semantics.
|
|
28
|
+
*
|
|
29
|
+
* @doc-aria
|
|
30
|
+
* aria-hidden — Set to "true" by default (decorative). Removed when [kjIconLabel] is provided
|
|
31
|
+
* role="img" — Set when [kjIconLabel] is provided (meaningful icon)
|
|
32
|
+
* aria-label — Bound to [kjIconLabel] when provided
|
|
33
|
+
* data-kj-icon-mode — Mirrors the resolved mode ("svg" | "font") for theme CSS
|
|
34
|
+
*
|
|
35
|
+
* @doc-css-var
|
|
36
|
+
* --kj-icon — CSS-ready resolved icon value (a url() for svg mode or a quoted glyph for font mode). Set by the directive.
|
|
37
|
+
* --kj-icon-font — Font family used in font mode. Consumers set this on a parent when shipping icon fonts.
|
|
38
|
+
* --kj-color-icon-* — Foreground color tokens for [kjIconColor] (muted/primary/success/warning/danger/info).
|
|
39
|
+
* --kj-icon-size-* — Em-relative size tokens for [kjIconSize] (xs/sm/md/lg/xl).
|
|
40
|
+
*
|
|
41
|
+
* @doc-touch
|
|
42
|
+
* The icon is layout-only and does not require a 44×44 target on its own.
|
|
43
|
+
* When the icon is the sole label of an interactive control, the parent
|
|
44
|
+
* (`kj-button kjSize="icon"`, `<a>`, etc.) must meet WCAG 2.5.5.
|
|
45
|
+
*
|
|
46
|
+
* @doc-a11y
|
|
47
|
+
* Decorative by default — `aria-hidden="true"` keeps the icon silent for
|
|
48
|
+
* AT. Pass `[kjIconLabel]` only when the icon conveys meaning that is not
|
|
49
|
+
* duplicated in adjacent text; this swaps in `role="img"` and the label.
|
|
50
|
+
* Color tokens map to `--kj-color-icon-{token}` so theme palettes can
|
|
51
|
+
* retarget per-mode without API churn.
|
|
52
|
+
*
|
|
53
|
+
* @doc-related button,link,badge
|
|
54
|
+
*
|
|
55
|
+
* @doc-category Core/Icon
|
|
56
|
+
* @doc
|
|
57
|
+
* @doc-name icon
|
|
58
|
+
* @doc-description Renders an accessible icon on its host element by name with color and size tokens.
|
|
59
|
+
* @doc-is-main
|
|
60
|
+
*/
|
|
61
|
+
@Directive({
|
|
62
|
+
selector: 'span[kjIcon], i[kjIcon]',
|
|
63
|
+
standalone: true,
|
|
64
|
+
host: {
|
|
65
|
+
class: 'kj-icon',
|
|
66
|
+
'[style.--kj-icon]': 'iconValue()',
|
|
67
|
+
'[style.color]': 'colorVar()',
|
|
68
|
+
'[style.font-size]': 'sizeVar()',
|
|
69
|
+
'[attr.data-kj-icon-mode]': 'mode()',
|
|
70
|
+
'[attr.aria-hidden]': 'ariaHidden()',
|
|
71
|
+
'[attr.role]': 'roleAttr()',
|
|
72
|
+
'[attr.aria-label]': 'ariaLabelAttr()',
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
export class KjIconDirective {
|
|
76
|
+
private readonly resolve = injectKjIconResolver();
|
|
77
|
+
|
|
78
|
+
/** Icon name. Names starting with `@font.` use font mode. */
|
|
79
|
+
readonly kjIcon = input.required<string>();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Accessible name. When set, the icon is treated as meaningful:
|
|
83
|
+
* `role="img"`, `aria-label` set, `aria-hidden` removed. When unset
|
|
84
|
+
* (the default), the icon is decorative and `aria-hidden="true"`.
|
|
85
|
+
*/
|
|
86
|
+
readonly kjIconLabel = input<string | null>(null);
|
|
87
|
+
|
|
88
|
+
/** Semantic color token. Default `null` means inherit (CSS `currentColor`). */
|
|
89
|
+
readonly kjIconColor = input<KjIconColor | null>(null);
|
|
90
|
+
|
|
91
|
+
/** Size token. Default `null` means inherit from surrounding text. */
|
|
92
|
+
readonly kjIconSize = input<KjIconSize | null>(null);
|
|
93
|
+
|
|
94
|
+
protected readonly mode = computed(() => getIconMode(this.kjIcon()));
|
|
95
|
+
protected readonly iconValue = computed(() => this.resolve(this.kjIcon()));
|
|
96
|
+
|
|
97
|
+
protected readonly ariaHidden = computed(() =>
|
|
98
|
+
this.kjIconLabel() ? null : 'true',
|
|
99
|
+
);
|
|
100
|
+
protected readonly roleAttr = computed(() =>
|
|
101
|
+
this.kjIconLabel() ? 'img' : null,
|
|
102
|
+
);
|
|
103
|
+
protected readonly ariaLabelAttr = computed(() => this.kjIconLabel());
|
|
104
|
+
|
|
105
|
+
protected readonly colorVar = computed(() => {
|
|
106
|
+
const c = this.kjIconColor();
|
|
107
|
+
return c && c !== 'inherit' ? `var(--kj-color-icon-${c})` : null;
|
|
108
|
+
});
|
|
109
|
+
protected readonly sizeVar = computed(() => {
|
|
110
|
+
const s = this.kjIconSize();
|
|
111
|
+
return s ? `var(--kj-icon-size-${s})` : null;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getIconMode } from './icon.mode';
|
|
3
|
+
|
|
4
|
+
describe('getIconMode', () => {
|
|
5
|
+
it('returns "font" when name starts with "@font."', () => {
|
|
6
|
+
expect(getIconMode('@font.fa-cog')).toBe('font');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns "svg" for plain names', () => {
|
|
10
|
+
expect(getIconMode('settings')).toBe('svg');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns "svg" for hyphenated names', () => {
|
|
14
|
+
expect(getIconMode('alert-triangle')).toBe('svg');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns "svg" for namespaced names that are not @font', () => {
|
|
18
|
+
expect(getIconMode('lucide:settings')).toBe('svg');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns "svg" for empty string', () => {
|
|
22
|
+
expect(getIconMode('')).toBe('svg');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('returns "svg" for "@font" with no trailing dot', () => {
|
|
26
|
+
// Strict: must be exactly "@font." prefix; "@fontawesome" stays svg.
|
|
27
|
+
expect(getIconMode('@fontawesome')).toBe('svg');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// packages/core/src/icon/icon.mode.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render mode inferred from an icon name.
|
|
5
|
+
* @doc
|
|
6
|
+
* @doc-name icon
|
|
7
|
+
* @doc-order 14
|
|
8
|
+
*/
|
|
9
|
+
export type IconMode = 'svg' | 'font';
|
|
10
|
+
|
|
11
|
+
const FONT_PREFIX = '@font.';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Infer render mode from icon name. Names starting with `@font.` use the
|
|
15
|
+
* font code path (CSS `content`); everything else uses the svg/css-mask
|
|
16
|
+
* code path.
|
|
17
|
+
* @doc
|
|
18
|
+
* @doc-name icon
|
|
19
|
+
* @doc-order 5
|
|
20
|
+
*/
|
|
21
|
+
export function getIconMode(name: string): IconMode {
|
|
22
|
+
return name.startsWith(FONT_PREFIX) ? 'font' : 'svg';
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
provideIconLoader,
|
|
5
|
+
provideIconResolver,
|
|
6
|
+
provideIcons,
|
|
7
|
+
} from './icon.providers';
|
|
8
|
+
import {
|
|
9
|
+
KJ_ICON_LOADER,
|
|
10
|
+
KJ_ICON_REGISTRY,
|
|
11
|
+
KJ_ICON_RESOLVER,
|
|
12
|
+
} from './icon.tokens';
|
|
13
|
+
|
|
14
|
+
describe('provideIcons', () => {
|
|
15
|
+
it('seeds the registry with the given map', () => {
|
|
16
|
+
TestBed.configureTestingModule({
|
|
17
|
+
providers: [provideIcons({ settings: 'url("a")', save: 'url("b")' })],
|
|
18
|
+
});
|
|
19
|
+
const reg = TestBed.inject(KJ_ICON_REGISTRY);
|
|
20
|
+
expect(reg()).toEqual({ settings: 'url("a")', save: 'url("b")' });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('merges multiple provideIcons calls (last wins on collision)', () => {
|
|
24
|
+
TestBed.configureTestingModule({
|
|
25
|
+
providers: [
|
|
26
|
+
provideIcons({ settings: 'url("first")', save: 'url("b")' }),
|
|
27
|
+
provideIcons({ settings: 'url("second")', trash: 'url("c")' }),
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
const reg = TestBed.inject(KJ_ICON_REGISTRY);
|
|
31
|
+
expect(reg()).toEqual({
|
|
32
|
+
settings: 'url("second")',
|
|
33
|
+
save: 'url("b")',
|
|
34
|
+
trash: 'url("c")',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('default registry is empty when no provideIcons is called', () => {
|
|
39
|
+
TestBed.configureTestingModule({ providers: [] });
|
|
40
|
+
const reg = TestBed.inject(KJ_ICON_REGISTRY);
|
|
41
|
+
expect(reg()).toEqual({});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('provideIconResolver', () => {
|
|
46
|
+
it('overrides the default resolver', () => {
|
|
47
|
+
TestBed.configureTestingModule({
|
|
48
|
+
providers: [provideIconResolver((n) => `url("/icons/${n}.svg")`)],
|
|
49
|
+
});
|
|
50
|
+
const fn = TestBed.inject(KJ_ICON_RESOLVER);
|
|
51
|
+
expect(fn('settings')).toBe('url("/icons/settings.svg")');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('provideIconLoader', () => {
|
|
56
|
+
it('registers an async loader', async () => {
|
|
57
|
+
const loader = async (n: string) => `url("loaded:${n}")`;
|
|
58
|
+
TestBed.configureTestingModule({ providers: [provideIconLoader(loader)] });
|
|
59
|
+
const fn = TestBed.inject(KJ_ICON_LOADER);
|
|
60
|
+
expect(fn).not.toBeNull();
|
|
61
|
+
await expect(fn!('x')).resolves.toBe('url("loaded:x")');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type EnvironmentProviders,
|
|
3
|
+
makeEnvironmentProviders,
|
|
4
|
+
} from '@angular/core';
|
|
5
|
+
import type { IconLoader, IconResolver } from './icon.types';
|
|
6
|
+
import {
|
|
7
|
+
KJ_ICON_ENTRIES,
|
|
8
|
+
KJ_ICON_LOADER,
|
|
9
|
+
KJ_ICON_RESOLVER,
|
|
10
|
+
} from './icon.tokens';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Register a map of icon names to CSS-ready values. Call multiple times to
|
|
14
|
+
* compose icon sets; later calls win on key collision.
|
|
15
|
+
* @doc
|
|
16
|
+
* @doc-name icon
|
|
17
|
+
* @doc-order 1
|
|
18
|
+
*/
|
|
19
|
+
export function provideIcons(
|
|
20
|
+
map: Record<string, string>,
|
|
21
|
+
): EnvironmentProviders {
|
|
22
|
+
return makeEnvironmentProviders([
|
|
23
|
+
{ provide: KJ_ICON_ENTRIES, useValue: map, multi: true },
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Override the synchronous fallback resolver used when a name is not in the
|
|
29
|
+
* registry and no async loader is configured.
|
|
30
|
+
* @doc
|
|
31
|
+
* @doc-name icon
|
|
32
|
+
* @doc-order 2
|
|
33
|
+
*/
|
|
34
|
+
export function provideIconResolver(fn: IconResolver): EnvironmentProviders {
|
|
35
|
+
return makeEnvironmentProviders([{ provide: KJ_ICON_RESOLVER, useValue: fn }]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register an async loader. When present, missing icons are loaded via this
|
|
40
|
+
* function and cached into the registry.
|
|
41
|
+
* @doc
|
|
42
|
+
* @doc-name icon
|
|
43
|
+
* @doc-order 3
|
|
44
|
+
*/
|
|
45
|
+
export function provideIconLoader(fn: IconLoader): EnvironmentProviders {
|
|
46
|
+
return makeEnvironmentProviders([{ provide: KJ_ICON_LOADER, useValue: fn }]);
|
|
47
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Injector, runInInjectionContext } from '@angular/core';
|
|
2
|
+
import { TestBed } from '@angular/core/testing';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { Provider, EnvironmentProviders } from '@angular/core';
|
|
5
|
+
import { provideIcons, provideIconLoader, provideIconResolver } from './icon.providers';
|
|
6
|
+
import { injectKjIconResolver } from './icon.resolver';
|
|
7
|
+
|
|
8
|
+
function makeResolver(providers: (Provider | EnvironmentProviders)[]) {
|
|
9
|
+
TestBed.configureTestingModule({ providers });
|
|
10
|
+
const injector = TestBed.inject(Injector);
|
|
11
|
+
return runInInjectionContext(injector, () => injectKjIconResolver());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('injectKjIconResolver', () => {
|
|
15
|
+
beforeEach(() => TestBed.resetTestingModule());
|
|
16
|
+
|
|
17
|
+
describe('registry path', () => {
|
|
18
|
+
it('returns the registry value when name is registered', () => {
|
|
19
|
+
const resolve = makeResolver([
|
|
20
|
+
provideIcons({ settings: 'url("a")' }),
|
|
21
|
+
]);
|
|
22
|
+
expect(resolve('settings')).toBe('url("a")');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('fallback resolver path (no loader)', () => {
|
|
27
|
+
it('falls back to KJ_ICON_RESOLVER when name is missing', () => {
|
|
28
|
+
const resolve = makeResolver([
|
|
29
|
+
provideIconResolver((n) => `url("/icons/${n}.svg")`),
|
|
30
|
+
]);
|
|
31
|
+
expect(resolve('missing')).toBe('url("/icons/missing.svg")');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('default resolver returns name unchanged', () => {
|
|
35
|
+
const resolve = makeResolver([]);
|
|
36
|
+
expect(resolve('missing')).toBe('missing');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('loader path', () => {
|
|
41
|
+
it('returns null while load is pending, then fills registry on success', async () => {
|
|
42
|
+
let resolveLoad: (v: string) => void;
|
|
43
|
+
const loader = vi.fn(
|
|
44
|
+
(_n: string) =>
|
|
45
|
+
new Promise<string>((res) => {
|
|
46
|
+
resolveLoad = (v) => res(v);
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
const resolve = makeResolver([provideIconLoader(loader)]);
|
|
50
|
+
|
|
51
|
+
expect(resolve('settings')).toBeNull();
|
|
52
|
+
expect(loader).toHaveBeenCalledTimes(1);
|
|
53
|
+
|
|
54
|
+
// Concurrent call must NOT trigger a second load (dedupe).
|
|
55
|
+
expect(resolve('settings')).toBeNull();
|
|
56
|
+
expect(loader).toHaveBeenCalledTimes(1);
|
|
57
|
+
|
|
58
|
+
resolveLoad!('url("loaded")');
|
|
59
|
+
await Promise.resolve();
|
|
60
|
+
await Promise.resolve();
|
|
61
|
+
|
|
62
|
+
expect(resolve('settings')).toBe('url("loaded")');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('on loader rejection, removes pending so a subsequent call retries', async () => {
|
|
66
|
+
let calls = 0;
|
|
67
|
+
const loader = vi.fn(async (_n: string) => {
|
|
68
|
+
calls++;
|
|
69
|
+
if (calls === 1) throw new Error('boom');
|
|
70
|
+
return 'url("ok")';
|
|
71
|
+
});
|
|
72
|
+
const resolve = makeResolver([provideIconLoader(loader)]);
|
|
73
|
+
|
|
74
|
+
expect(resolve('x')).toBeNull();
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
await Promise.resolve();
|
|
77
|
+
expect(resolve('x')).toBeNull();
|
|
78
|
+
expect(loader).toHaveBeenCalledTimes(2);
|
|
79
|
+
await Promise.resolve();
|
|
80
|
+
await Promise.resolve();
|
|
81
|
+
expect(resolve('x')).toBe('url("ok")');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('loader path takes precedence over fallback resolver', async () => {
|
|
85
|
+
const loader = async () => 'url("from-loader")';
|
|
86
|
+
const resolve = makeResolver([
|
|
87
|
+
provideIconLoader(loader),
|
|
88
|
+
provideIconResolver(() => 'url("from-fallback")'),
|
|
89
|
+
]);
|
|
90
|
+
expect(resolve('x')).toBeNull();
|
|
91
|
+
await Promise.resolve();
|
|
92
|
+
await Promise.resolve();
|
|
93
|
+
expect(resolve('x')).toBe('url("from-loader")');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { inject } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
KJ_ICON_LOADER,
|
|
4
|
+
KJ_ICON_REGISTRY,
|
|
5
|
+
KJ_ICON_RESOLVER,
|
|
6
|
+
} from './icon.tokens';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns a function that resolves an icon name to a CSS-ready value, or
|
|
10
|
+
* `null` while an async load is pending.
|
|
11
|
+
*
|
|
12
|
+
* Lookup order:
|
|
13
|
+
* 1. `KJ_ICON_REGISTRY` (eager)
|
|
14
|
+
* 2. `KJ_ICON_LOADER` (async; fills registry on success, returns `null` while pending)
|
|
15
|
+
* 3. `KJ_ICON_RESOLVER` (sync default, e.g. URL synthesis)
|
|
16
|
+
*
|
|
17
|
+
* Must be called in an injection context.
|
|
18
|
+
* @doc
|
|
19
|
+
* @doc-name icon
|
|
20
|
+
* @doc-order 4
|
|
21
|
+
*/
|
|
22
|
+
export function injectKjIconResolver(): (name: string) => string | null {
|
|
23
|
+
const registry = inject(KJ_ICON_REGISTRY);
|
|
24
|
+
const loader = inject(KJ_ICON_LOADER);
|
|
25
|
+
const fallback = inject(KJ_ICON_RESOLVER);
|
|
26
|
+
const pending = new Set<string>();
|
|
27
|
+
|
|
28
|
+
return (name: string): string | null => {
|
|
29
|
+
const map = registry();
|
|
30
|
+
if (Object.prototype.hasOwnProperty.call(map, name)) return map[name];
|
|
31
|
+
|
|
32
|
+
if (loader) {
|
|
33
|
+
if (!pending.has(name)) {
|
|
34
|
+
pending.add(name);
|
|
35
|
+
Promise.resolve(loader(name))
|
|
36
|
+
.then((value) => {
|
|
37
|
+
registry.update((m) => ({ ...m, [name]: value }));
|
|
38
|
+
pending.delete(name);
|
|
39
|
+
})
|
|
40
|
+
.catch(() => {
|
|
41
|
+
pending.delete(name);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return fallback(name);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { InjectionToken, type WritableSignal, inject, signal } from '@angular/core';
|
|
2
|
+
import type { IconLoader, IconResolver } from './icon.types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Multi-provider entries contributed by `provideIcons(...)`. Each entry is a
|
|
6
|
+
* `Record<name, cssReadyValue>`. The registry factory merges all entries at
|
|
7
|
+
* injection time.
|
|
8
|
+
*
|
|
9
|
+
* Internal — consumers use `provideIcons(...)`.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export const KJ_ICON_ENTRIES = new InjectionToken<Record<string, string>>(
|
|
13
|
+
'KJ_ICON_ENTRIES',
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Runtime icon registry. A writable signal so the async loader can fill in
|
|
18
|
+
* resolved icons and any consuming directive's computed re-evaluates.
|
|
19
|
+
*
|
|
20
|
+
* Stored values are CSS-ready: `url("...")` for svg mode, quoted glyphs for
|
|
21
|
+
* font mode. Adapter authors are responsible for the wrapping.
|
|
22
|
+
* @doc
|
|
23
|
+
* @doc-name icon
|
|
24
|
+
* @doc-order 6
|
|
25
|
+
*/
|
|
26
|
+
export const KJ_ICON_REGISTRY = new InjectionToken<
|
|
27
|
+
WritableSignal<Record<string, string>>
|
|
28
|
+
>('KJ_ICON_REGISTRY', {
|
|
29
|
+
providedIn: 'root',
|
|
30
|
+
factory: () => {
|
|
31
|
+
const entries = inject(KJ_ICON_ENTRIES, { optional: true }) as
|
|
32
|
+
| Record<string, string>[]
|
|
33
|
+
| Record<string, string>
|
|
34
|
+
| null;
|
|
35
|
+
const list: Record<string, string>[] = Array.isArray(entries)
|
|
36
|
+
? entries
|
|
37
|
+
: entries
|
|
38
|
+
? [entries]
|
|
39
|
+
: [];
|
|
40
|
+
const merged = list.reduce<Record<string, string>>(
|
|
41
|
+
(acc, m) => ({ ...acc, ...m }),
|
|
42
|
+
{},
|
|
43
|
+
);
|
|
44
|
+
return signal<Record<string, string>>(merged);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Synchronous fallback resolver. Called when a name is not in the registry
|
|
50
|
+
* and no async loader is configured. Returns a CSS-ready string.
|
|
51
|
+
*
|
|
52
|
+
* Default: returns the name unchanged.
|
|
53
|
+
* @doc
|
|
54
|
+
* @doc-name icon
|
|
55
|
+
* @doc-order 7
|
|
56
|
+
*/
|
|
57
|
+
export const KJ_ICON_RESOLVER = new InjectionToken<IconResolver>(
|
|
58
|
+
'KJ_ICON_RESOLVER',
|
|
59
|
+
{ providedIn: 'root', factory: () => (name: string) => name },
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Optional async loader. When set, missing icons are loaded via this fn and
|
|
64
|
+
* the result is written into `KJ_ICON_REGISTRY`. While loading, the
|
|
65
|
+
* directive renders nothing for that name. If unset, the resolver fallback
|
|
66
|
+
* is used instead.
|
|
67
|
+
* @doc
|
|
68
|
+
* @doc-name icon
|
|
69
|
+
* @doc-order 8
|
|
70
|
+
*/
|
|
71
|
+
export const KJ_ICON_LOADER = new InjectionToken<IconLoader | null>(
|
|
72
|
+
'KJ_ICON_LOADER',
|
|
73
|
+
{ providedIn: 'root', factory: () => null },
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolvable path to the kouji icon stylesheet.
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { KJ_ICON_CSS_PATH } from '@kouji-ui/core';
|
|
81
|
+
* // or import the file directly:
|
|
82
|
+
* import '@kouji-ui/core/icon/icon.css';
|
|
83
|
+
* ```
|
|
84
|
+
* @doc
|
|
85
|
+
* @doc-name icon
|
|
86
|
+
* @doc-order 9
|
|
87
|
+
*/
|
|
88
|
+
export const KJ_ICON_CSS_PATH = '@kouji-ui/core/icon/icon.css' as const;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// packages/core/src/icon/icon.types.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolves an icon name to a CSS-ready value (e.g. `url("data:...")` for svg
|
|
5
|
+
* mode, or a quoted glyph like `"\\f013"` for font mode). Synchronous.
|
|
6
|
+
*
|
|
7
|
+
* Used as a fallback when an icon is not in the registry and no async loader
|
|
8
|
+
* is configured.
|
|
9
|
+
* @doc
|
|
10
|
+
* @doc-name icon
|
|
11
|
+
* @doc-order 10
|
|
12
|
+
*/
|
|
13
|
+
export type IconResolver = (name: string) => string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Asynchronously resolves an icon name to a CSS-ready value. Result is
|
|
17
|
+
* memoized into the registry so subsequent reads are synchronous.
|
|
18
|
+
* @doc
|
|
19
|
+
* @doc-name icon
|
|
20
|
+
* @doc-order 11
|
|
21
|
+
*/
|
|
22
|
+
export type IconLoader = (name: string) => Promise<string>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Semantic color tokens for `[kjIconColor]`. Maps to
|
|
26
|
+
* `var(--kj-color-icon-{token})`. `'inherit'` is the default and means
|
|
27
|
+
* "use whatever the surrounding text color is" (CSS `currentColor`).
|
|
28
|
+
* @doc
|
|
29
|
+
* @doc-name icon
|
|
30
|
+
* @doc-order 12
|
|
31
|
+
*/
|
|
32
|
+
export type KjIconColor =
|
|
33
|
+
| 'inherit'
|
|
34
|
+
| 'muted'
|
|
35
|
+
| 'primary'
|
|
36
|
+
| 'success'
|
|
37
|
+
| 'warning'
|
|
38
|
+
| 'danger'
|
|
39
|
+
| 'info';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Size tokens for `[kjIconSize]`. Maps to `var(--kj-icon-size-{token})`.
|
|
43
|
+
* Sizes are em-relative so an icon scales with surrounding text by default.
|
|
44
|
+
* @doc
|
|
45
|
+
* @doc-name icon
|
|
46
|
+
* @doc-order 13
|
|
47
|
+
*/
|
|
48
|
+
export type KjIconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { getIconMode, type IconMode } from './icon.mode';
|
|
2
|
+
export type {
|
|
3
|
+
IconResolver,
|
|
4
|
+
IconLoader,
|
|
5
|
+
KjIconColor,
|
|
6
|
+
KjIconSize,
|
|
7
|
+
} from './icon.types';
|
|
8
|
+
export {
|
|
9
|
+
KJ_ICON_ENTRIES,
|
|
10
|
+
KJ_ICON_REGISTRY,
|
|
11
|
+
KJ_ICON_RESOLVER,
|
|
12
|
+
KJ_ICON_LOADER,
|
|
13
|
+
KJ_ICON_CSS_PATH,
|
|
14
|
+
} from './icon.tokens';
|
|
15
|
+
export {
|
|
16
|
+
provideIcons,
|
|
17
|
+
provideIconResolver,
|
|
18
|
+
provideIconLoader,
|
|
19
|
+
} from './icon.providers';
|
|
20
|
+
export { injectKjIconResolver } from './icon.resolver';
|
|
21
|
+
export { KjIconDirective } from './icon.directive';
|
package/src/input/input.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { Directive, ElementRef, effect, inject, input } from '@angular/core';
|
|
2
2
|
import { KjDisabled, KjFocusRing, KjFormControl } from '../primitives';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -45,7 +45,10 @@ import { KjDisabled, KjFocusRing, KjFormControl } from '../primitives';
|
|
|
45
45
|
* emailCtrl = new FormControl('', [Validators.email, Validators.required]);
|
|
46
46
|
* }
|
|
47
47
|
* ```
|
|
48
|
-
* @category Core/Inputs
|
|
48
|
+
* @doc-category Core/Inputs
|
|
49
|
+
* @doc-name input
|
|
50
|
+
* @doc-description Adds Angular forms integration, invalid and disabled state, and a focus ring to a native input.
|
|
51
|
+
* @doc-is-main
|
|
49
52
|
*/
|
|
50
53
|
@Directive({
|
|
51
54
|
selector: '[kjInput]',
|
|
@@ -72,9 +75,15 @@ export class KjInput {
|
|
|
72
75
|
|
|
73
76
|
constructor() {
|
|
74
77
|
// Reflect the CVA value signal back to the native input element.
|
|
78
|
+
// Skip when the form control's value is null/undefined so external [value]
|
|
79
|
+
// bindings (template attribute, parent component) are preserved when no
|
|
80
|
+
// ngModel/formControl is wired. When a form is wired, callers clear via
|
|
81
|
+
// setValue('') rather than setValue(null), preserving the clear-on-empty
|
|
82
|
+
// behavior.
|
|
75
83
|
effect(() => {
|
|
76
84
|
const val = this.formCtrl.value();
|
|
77
|
-
|
|
85
|
+
if (val == null) return;
|
|
86
|
+
this.el.nativeElement.value = String(val);
|
|
78
87
|
});
|
|
79
88
|
}
|
|
80
89
|
}
|