@kouji-ui/core 0.1.1 → 0.1.2
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/fesm2022/kouji-ui-core.mjs +26789 -0
- package/fesm2022/kouji-ui-core.mjs.map +1 -0
- package/package.json +5 -6
- package/types/kouji-ui-core.d.ts +13545 -0
- package/.turbo/turbo-build.log +0 -30
- package/CHANGELOG.md +0 -190
- package/ng-package.json +0 -11
- package/src/a11y/.gitkeep +0 -0
- package/src/a11y/aria-describedby.spec.ts +0 -55
- package/src/a11y/aria-describedby.ts +0 -37
- package/src/a11y/aria-labelledby.spec.ts +0 -55
- package/src/a11y/aria-labelledby.ts +0 -38
- package/src/a11y/focus-trap.spec.ts +0 -86
- package/src/a11y/focus-trap.ts +0 -72
- package/src/a11y/index.ts +0 -10
- package/src/a11y/live-region.spec.ts +0 -58
- package/src/a11y/live-region.ts +0 -50
- package/src/a11y/roving-tabindex.spec.ts +0 -201
- package/src/a11y/roving-tabindex.ts +0 -153
- package/src/a11y/visually-hidden.spec.ts +0 -34
- package/src/a11y/visually-hidden.ts +0 -26
- package/src/accordion/accordion.context.ts +0 -44
- package/src/accordion/accordion.spec.ts +0 -242
- package/src/accordion/accordion.ts +0 -401
- package/src/accordion/index.ts +0 -8
- package/src/alert/alert.context.ts +0 -28
- package/src/alert/alert.spec.ts +0 -217
- package/src/alert/alert.ts +0 -417
- package/src/alert/config.ts +0 -46
- package/src/alert/index.ts +0 -19
- package/src/avatar/avatar-group.context.ts +0 -45
- package/src/avatar/avatar-group.spec.ts +0 -358
- package/src/avatar/avatar-group.ts +0 -259
- package/src/avatar/avatar.spec.ts +0 -18
- package/src/avatar/avatar.ts +0 -39
- package/src/avatar/index.ts +0 -11
- package/src/badge/badge.spec.ts +0 -28
- package/src/badge/badge.ts +0 -35
- package/src/badge/index.ts +0 -1
- package/src/breadcrumb/breadcrumb-current.ts +0 -38
- package/src/breadcrumb/breadcrumb-ellipsis.ts +0 -71
- package/src/breadcrumb/breadcrumb-item.ts +0 -41
- package/src/breadcrumb/breadcrumb-link.ts +0 -44
- package/src/breadcrumb/breadcrumb-list.ts +0 -42
- package/src/breadcrumb/breadcrumb-separator.ts +0 -45
- package/src/breadcrumb/breadcrumb.context.ts +0 -45
- package/src/breadcrumb/breadcrumb.spec.ts +0 -354
- package/src/breadcrumb/breadcrumb.ts +0 -239
- package/src/breadcrumb/config.ts +0 -84
- package/src/breadcrumb/index.ts +0 -18
- package/src/button/_examples/button.example.ts +0 -71
- package/src/button/_examples/button.finance.example.ts +0 -83
- package/src/button/_examples/button.retro.example.ts +0 -91
- package/src/button/_examples/button.sizes.example.ts +0 -60
- package/src/button/_examples/index.ts +0 -6
- package/src/button/button.spec.ts +0 -163
- package/src/button/button.ts +0 -142
- package/src/button/config.spec.ts +0 -33
- package/src/button/config.ts +0 -45
- package/src/button/index.ts +0 -2
- package/src/button-group/button-group.context.ts +0 -28
- package/src/button-group/button-group.spec.ts +0 -99
- package/src/button-group/button-group.ts +0 -99
- package/src/button-group/index.ts +0 -2
- package/src/calendar/calendar-day.ts +0 -102
- package/src/calendar/calendar-grid.ts +0 -90
- package/src/calendar/calendar-header.ts +0 -65
- package/src/calendar/calendar.context.ts +0 -53
- package/src/calendar/calendar.spec.ts +0 -225
- package/src/calendar/calendar.ts +0 -233
- package/src/calendar/date-utils.ts +0 -225
- package/src/calendar/index.ts +0 -28
- package/src/carousel/carousel.context.ts +0 -81
- package/src/carousel/carousel.spec.ts +0 -337
- package/src/carousel/carousel.ts +0 -919
- package/src/carousel/index.ts +0 -22
- package/src/cascade-select/cascade-select-option.ts +0 -205
- package/src/cascade-select/cascade-select-panel.ts +0 -106
- package/src/cascade-select/cascade-select-root.ts +0 -265
- package/src/cascade-select/cascade-select-sub-panel.ts +0 -168
- package/src/cascade-select/cascade-select-trigger.ts +0 -44
- package/src/cascade-select/cascade-select.context.ts +0 -71
- package/src/cascade-select/cascade-select.spec.ts +0 -241
- package/src/cascade-select/cascade-select.ts +0 -5
- package/src/cascade-select/index.ts +0 -11
- package/src/chart/chart.spec.ts +0 -27
- package/src/chart/chart.ts +0 -52
- package/src/chart/index.ts +0 -1
- package/src/chat/chat-avatar.ts +0 -31
- package/src/chat/chat-bubble.ts +0 -49
- package/src/chat/chat-footer.ts +0 -67
- package/src/chat/chat-header.ts +0 -41
- package/src/chat/chat-log.ts +0 -43
- package/src/chat/chat.context.ts +0 -64
- package/src/chat/chat.spec.ts +0 -341
- package/src/chat/chat.ts +0 -191
- package/src/chat/config.ts +0 -53
- package/src/chat/index.ts +0 -21
- package/src/checkbox/checkbox.spec.ts +0 -35
- package/src/checkbox/checkbox.ts +0 -89
- package/src/checkbox/index.ts +0 -1
- package/src/color-picker/color-picker.context.ts +0 -74
- package/src/color-picker/color-picker.spec.ts +0 -241
- package/src/color-picker/color-picker.ts +0 -698
- package/src/color-picker/color-picker.utils.ts +0 -140
- package/src/color-picker/index.ts +0 -28
- package/src/combobox/combobox-input.ts +0 -143
- package/src/combobox/combobox-listbox.ts +0 -49
- package/src/combobox/combobox-option.ts +0 -43
- package/src/combobox/combobox-root.ts +0 -281
- package/src/combobox/combobox.spec.ts +0 -75
- package/src/combobox/combobox.ts +0 -4
- package/src/combobox/index.ts +0 -4
- package/src/command-palette/command-empty.ts +0 -36
- package/src/command-palette/command-group.ts +0 -21
- package/src/command-palette/command-input.ts +0 -70
- package/src/command-palette/command-item.ts +0 -60
- package/src/command-palette/command-list.ts +0 -25
- package/src/command-palette/command-palette-dialog.ts +0 -74
- package/src/command-palette/command-palette-trigger.ts +0 -64
- package/src/command-palette/command-palette.filters.ts +0 -53
- package/src/command-palette/command-palette.spec.ts +0 -349
- package/src/command-palette/command-palette.ts +0 -204
- package/src/command-palette/command-separator.ts +0 -18
- package/src/command-palette/index.ts +0 -15
- package/src/confirm-popup/confirm-popup-action.ts +0 -39
- package/src/confirm-popup/confirm-popup-cancel.ts +0 -38
- package/src/confirm-popup/confirm-popup-content.ts +0 -113
- package/src/confirm-popup/confirm-popup-message.ts +0 -36
- package/src/confirm-popup/confirm-popup-trigger.ts +0 -31
- package/src/confirm-popup/confirm-popup.context.ts +0 -41
- package/src/confirm-popup/confirm-popup.spec.ts +0 -337
- package/src/confirm-popup/confirm-popup.ts +0 -109
- package/src/confirm-popup/index.ts +0 -12
- package/src/date-picker/date-picker-calendar.ts +0 -65
- package/src/date-picker/date-picker-trigger.ts +0 -215
- package/src/date-picker/date-picker.context.ts +0 -50
- package/src/date-picker/date-picker.spec.ts +0 -117
- package/src/date-picker/date-picker.ts +0 -127
- package/src/date-picker/index.ts +0 -7
- package/src/dialog/_examples/dialog.confirm.example.ts +0 -145
- package/src/dialog/_examples/dialog.example.ts +0 -106
- package/src/dialog/_examples/dialog.finance.example.ts +0 -121
- package/src/dialog/_examples/dialog.retro.example.ts +0 -126
- package/src/dialog/_examples/index.ts +0 -6
- package/src/dialog/dialog.ref.ts +0 -42
- package/src/dialog/dialog.service.ts +0 -66
- package/src/dialog/dialog.spec.ts +0 -37
- package/src/dialog/dialog.ts +0 -26
- package/src/dialog/index.ts +0 -3
- package/src/divider/divider.spec.ts +0 -223
- package/src/divider/divider.ts +0 -137
- package/src/divider/index.ts +0 -5
- package/src/drawer/drawer.ref.ts +0 -63
- package/src/drawer/drawer.service.ts +0 -106
- package/src/drawer/drawer.spec.ts +0 -117
- package/src/drawer/drawer.ts +0 -122
- package/src/drawer/index.ts +0 -10
- package/src/dropdown-menu/dropdown-menu-content.ts +0 -258
- package/src/dropdown-menu/dropdown-menu-group.ts +0 -31
- package/src/dropdown-menu/dropdown-menu-item.ts +0 -51
- package/src/dropdown-menu/dropdown-menu-label.ts +0 -41
- package/src/dropdown-menu/dropdown-menu-separator.ts +0 -27
- package/src/dropdown-menu/dropdown-menu-trigger.ts +0 -140
- package/src/dropdown-menu/dropdown-menu.spec.ts +0 -265
- package/src/dropdown-menu/dropdown-menu.ts +0 -60
- package/src/dropdown-menu/index.ts +0 -15
- package/src/example-components.ts +0 -42
- package/src/field/field-error.ts +0 -49
- package/src/field/field-group.ts +0 -39
- package/src/field/field-help.ts +0 -47
- package/src/field/field-label.ts +0 -30
- package/src/field/field.context.ts +0 -28
- package/src/field/field.spec.ts +0 -179
- package/src/field/field.ts +0 -121
- package/src/field/index.ts +0 -6
- package/src/file-upload/file-upload.context.ts +0 -89
- package/src/file-upload/file-upload.spec.ts +0 -290
- package/src/file-upload/file-upload.ts +0 -639
- package/src/file-upload/file-upload.types.ts +0 -59
- package/src/file-upload/index.ts +0 -23
- package/src/form/form-error-summary.ts +0 -87
- package/src/form/form-field.context.ts +0 -3
- package/src/form/form-field.spec.ts +0 -25
- package/src/form/form-field.ts +0 -52
- package/src/form/form.context.ts +0 -53
- package/src/form/form.spec.ts +0 -162
- package/src/form/form.ts +0 -326
- package/src/form/index.ts +0 -11
- package/src/icon/E2E.md +0 -14
- package/src/icon/icon.directive.spec.ts +0 -198
- package/src/icon/icon.directive.ts +0 -113
- package/src/icon/icon.mode.spec.ts +0 -29
- package/src/icon/icon.mode.ts +0 -23
- package/src/icon/icon.providers.spec.ts +0 -63
- package/src/icon/icon.providers.ts +0 -47
- package/src/icon/icon.resolver.spec.ts +0 -96
- package/src/icon/icon.resolver.ts +0 -49
- package/src/icon/icon.tokens.ts +0 -88
- package/src/icon/icon.types.ts +0 -48
- package/src/icon/index.ts +0 -21
- package/src/input/index.ts +0 -1
- package/src/input/input.spec.ts +0 -43
- package/src/input/input.ts +0 -89
- package/src/input-group/index.ts +0 -3
- package/src/input-group/input-group-addon.ts +0 -92
- package/src/input-group/input-group.context.ts +0 -27
- package/src/input-group/input-group.spec.ts +0 -186
- package/src/input-group/input-group.ts +0 -136
- package/src/input-mask/index.ts +0 -11
- package/src/input-mask/input-mask.compile.ts +0 -69
- package/src/input-mask/input-mask.engine.ts +0 -376
- package/src/input-mask/input-mask.presets.ts +0 -24
- package/src/input-mask/input-mask.spec.ts +0 -304
- package/src/input-mask/input-mask.tokens.ts +0 -49
- package/src/input-mask/input-mask.ts +0 -280
- package/src/input-otp/index.ts +0 -2
- package/src/input-otp/input-otp.context.ts +0 -41
- package/src/input-otp/input-otp.spec.ts +0 -308
- package/src/input-otp/input-otp.ts +0 -470
- package/src/kbd/index.ts +0 -1
- package/src/kbd/kbd.spec.ts +0 -127
- package/src/kbd/kbd.ts +0 -132
- package/src/link/config.ts +0 -44
- package/src/link/index.ts +0 -2
- package/src/link/link.spec.ts +0 -246
- package/src/link/link.ts +0 -226
- package/src/list/index.ts +0 -11
- package/src/list/list.spec.ts +0 -237
- package/src/list/list.ts +0 -277
- package/src/menubar/index.ts +0 -7
- package/src/menubar/menubar-item.ts +0 -204
- package/src/menubar/menubar.context.ts +0 -43
- package/src/menubar/menubar.spec.ts +0 -433
- package/src/menubar/menubar.ts +0 -309
- package/src/number-input/index.ts +0 -4
- package/src/number-input/number-input-group.ts +0 -64
- package/src/number-input/number-input.context.ts +0 -31
- package/src/number-input/number-input.format.ts +0 -148
- package/src/number-input/number-input.spec.ts +0 -170
- package/src/number-input/number-input.ts +0 -457
- package/src/number-input/number-stepper.ts +0 -193
- package/src/overlay-badge/index.ts +0 -7
- package/src/overlay-badge/overlay-badge-content.ts +0 -73
- package/src/overlay-badge/overlay-badge.context.ts +0 -37
- package/src/overlay-badge/overlay-badge.spec.ts +0 -220
- package/src/overlay-badge/overlay-badge.ts +0 -128
- package/src/pagination/config.ts +0 -82
- package/src/pagination/index.ts +0 -19
- package/src/pagination/page-tokens.ts +0 -78
- package/src/pagination/pagination-ellipsis.ts +0 -69
- package/src/pagination/pagination-first.ts +0 -84
- package/src/pagination/pagination-info.ts +0 -70
- package/src/pagination/pagination-item.ts +0 -81
- package/src/pagination/pagination-last.ts +0 -85
- package/src/pagination/pagination-next.ts +0 -86
- package/src/pagination/pagination-previous.ts +0 -90
- package/src/pagination/pagination.context.ts +0 -52
- package/src/pagination/pagination.spec.ts +0 -388
- package/src/pagination/pagination.ts +0 -288
- package/src/password-input/index.ts +0 -15
- package/src/password-input/password-input.context.ts +0 -65
- package/src/password-input/password-input.scorer.ts +0 -34
- package/src/password-input/password-input.spec.ts +0 -274
- package/src/password-input/password-input.ts +0 -488
- package/src/popover/_examples/index.ts +0 -5
- package/src/popover/_examples/popover.example.ts +0 -92
- package/src/popover/_examples/popover.finance.example.ts +0 -106
- package/src/popover/_examples/popover.retro.example.ts +0 -103
- package/src/popover/index.ts +0 -5
- package/src/popover/popover-arrow.ts +0 -30
- package/src/popover/popover-close.ts +0 -36
- package/src/popover/popover-content.ts +0 -41
- package/src/popover/popover-title.ts +0 -39
- package/src/popover/popover-trigger.ts +0 -40
- package/src/popover/popover.spec.ts +0 -59
- package/src/popover/popover.ts +0 -5
- package/src/presets/bind-presets.spec.ts +0 -50
- package/src/presets/bind-presets.ts +0 -43
- package/src/presets/index.ts +0 -3
- package/src/presets/size.spec.ts +0 -67
- package/src/presets/size.ts +0 -59
- package/src/presets/variant.spec.ts +0 -72
- package/src/presets/variant.ts +0 -62
- package/src/primitives/.gitkeep +0 -0
- package/src/primitives/directionality/directionality.spec.ts +0 -112
- package/src/primitives/directionality/directionality.ts +0 -89
- package/src/primitives/directionality/index.ts +0 -1
- package/src/primitives/forms/form-control.spec.ts +0 -67
- package/src/primitives/forms/form-control.ts +0 -94
- package/src/primitives/forms/index.ts +0 -1
- package/src/primitives/index.ts +0 -6
- package/src/primitives/interaction/disabled.spec.ts +0 -47
- package/src/primitives/interaction/disabled.ts +0 -31
- package/src/primitives/interaction/focus-ring.spec.ts +0 -62
- package/src/primitives/interaction/focus-ring.ts +0 -59
- package/src/primitives/interaction/index.ts +0 -2
- package/src/primitives/list/filterable-list.spec.ts +0 -114
- package/src/primitives/list/filterable-list.ts +0 -126
- package/src/primitives/list/group.spec.ts +0 -99
- package/src/primitives/list/group.ts +0 -139
- package/src/primitives/list/index.ts +0 -8
- package/src/primitives/list/inject-helpers.ts +0 -8
- package/src/primitives/list/item.spec.ts +0 -173
- package/src/primitives/list/item.ts +0 -222
- package/src/primitives/list/navigator.spec.ts +0 -286
- package/src/primitives/list/navigator.ts +0 -238
- package/src/primitives/list/selection.spec.ts +0 -214
- package/src/primitives/list/selection.ts +0 -339
- package/src/primitives/list/tokens.ts +0 -106
- package/src/primitives/list/type-ahead.spec.ts +0 -58
- package/src/primitives/list/type-ahead.ts +0 -46
- package/src/primitives/overlay/backdrop.spec.ts +0 -54
- package/src/primitives/overlay/backdrop.ts +0 -31
- package/src/primitives/overlay/builder.spec.ts +0 -38
- package/src/primitives/overlay/builder.ts +0 -151
- package/src/primitives/overlay/container.ts +0 -42
- package/src/primitives/overlay/context.ts +0 -14
- package/src/primitives/overlay/controller.spec.ts +0 -86
- package/src/primitives/overlay/controller.ts +0 -190
- package/src/primitives/overlay/id.spec.ts +0 -24
- package/src/primitives/overlay/id.ts +0 -17
- package/src/primitives/overlay/index.ts +0 -18
- package/src/primitives/overlay/overlay.css +0 -42
- package/src/primitives/overlay/panel.spec.ts +0 -52
- package/src/primitives/overlay/panel.ts +0 -102
- package/src/primitives/overlay/stack.spec.ts +0 -55
- package/src/primitives/overlay/stack.ts +0 -113
- package/src/primitives/overlay/strategies/backdrop/blurred.spec.ts +0 -11
- package/src/primitives/overlay/strategies/backdrop/blurred.ts +0 -5
- package/src/primitives/overlay/strategies/backdrop/none.spec.ts +0 -10
- package/src/primitives/overlay/strategies/backdrop/none.ts +0 -9
- package/src/primitives/overlay/strategies/backdrop/solid.spec.ts +0 -18
- package/src/primitives/overlay/strategies/backdrop/solid.ts +0 -23
- package/src/primitives/overlay/strategies/focus-trap/inert-based.spec.ts +0 -28
- package/src/primitives/overlay/strategies/focus-trap/inert-based.ts +0 -38
- package/src/primitives/overlay/strategies/focus-trap/no-trap.spec.ts +0 -9
- package/src/primitives/overlay/strategies/focus-trap/no-trap.ts +0 -8
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.spec.ts +0 -49
- package/src/primitives/overlay/strategies/focus-trap/tab-cycle.ts +0 -85
- package/src/primitives/overlay/strategies/index.ts +0 -34
- package/src/primitives/overlay/strategies/live-announcer/_announce.ts +0 -35
- package/src/primitives/overlay/strategies/live-announcer/assertive.spec.ts +0 -21
- package/src/primitives/overlay/strategies/live-announcer/assertive.ts +0 -11
- package/src/primitives/overlay/strategies/live-announcer/polite.spec.ts +0 -21
- package/src/primitives/overlay/strategies/live-announcer/polite.ts +0 -11
- package/src/primitives/overlay/strategies/live-announcer/silent.spec.ts +0 -12
- package/src/primitives/overlay/strategies/live-announcer/silent.ts +0 -5
- package/src/primitives/overlay/strategies/mount/body-portal.spec.ts +0 -40
- package/src/primitives/overlay/strategies/mount/body-portal.ts +0 -91
- package/src/primitives/overlay/strategies/mount/in-container.spec.ts +0 -43
- package/src/primitives/overlay/strategies/mount/in-container.ts +0 -39
- package/src/primitives/overlay/strategies/mount/in-place.spec.ts +0 -40
- package/src/primitives/overlay/strategies/mount/in-place.ts +0 -20
- package/src/primitives/overlay/strategies/position/anchored-to.spec.ts +0 -56
- package/src/primitives/overlay/strategies/position/anchored-to.ts +0 -248
- package/src/primitives/overlay/strategies/position/corner.spec.ts +0 -36
- package/src/primitives/overlay/strategies/position/corner.ts +0 -33
- package/src/primitives/overlay/strategies/position/edge-sheet.spec.ts +0 -32
- package/src/primitives/overlay/strategies/position/edge-sheet.ts +0 -36
- package/src/primitives/overlay/strategies/position/in-place-sibling.spec.ts +0 -25
- package/src/primitives/overlay/strategies/position/in-place-sibling.ts +0 -13
- package/src/primitives/overlay/strategies/position/point-at.spec.ts +0 -40
- package/src/primitives/overlay/strategies/position/point-at.ts +0 -34
- package/src/primitives/overlay/strategies/position/viewport-centered.spec.ts +0 -31
- package/src/primitives/overlay/strategies/position/viewport-centered.ts +0 -31
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.spec.ts +0 -15
- package/src/primitives/overlay/strategies/scroll-lock/css-clip.ts +0 -39
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.spec.ts +0 -21
- package/src/primitives/overlay/strategies/scroll-lock/html-overflow.ts +0 -44
- package/src/primitives/overlay/strategies/scroll-lock/none.spec.ts +0 -9
- package/src/primitives/overlay/strategies/scroll-lock/none.ts +0 -5
- package/src/primitives/overlay/strategies/trigger-event/on-click.spec.ts +0 -32
- package/src/primitives/overlay/strategies/trigger-event/on-click.ts +0 -28
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.spec.ts +0 -45
- package/src/primitives/overlay/strategies/trigger-event/on-context-menu.ts +0 -70
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.spec.ts +0 -35
- package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.ts +0 -34
- package/src/primitives/overlay/strategies/trigger-event/on-focus.spec.ts +0 -64
- package/src/primitives/overlay/strategies/trigger-event/on-focus.ts +0 -37
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.spec.ts +0 -37
- package/src/primitives/overlay/strategies/trigger-event/on-hotkey.ts +0 -59
- package/src/primitives/overlay/strategies/trigger-event/on-hover.spec.ts +0 -34
- package/src/primitives/overlay/strategies/trigger-event/on-hover.ts +0 -86
- package/src/primitives/overlay/strategies/trigger-event/programmatic.spec.ts +0 -16
- package/src/primitives/overlay/strategies/trigger-event/programmatic.ts +0 -12
- package/src/primitives/overlay/tokens.ts +0 -54
- package/src/primitives/overlay/trigger.spec.ts +0 -103
- package/src/primitives/overlay/trigger.ts +0 -75
- package/src/primitives/overlay/types.ts +0 -24
- package/src/primitives/overlay/wrapper.ts +0 -50
- package/src/primitives/signals/deep-signal.ts +0 -109
- package/src/primitives/signals/index.ts +0 -1
- package/src/progress-bar/config.ts +0 -48
- package/src/progress-bar/index.ts +0 -12
- package/src/progress-bar/progress-bar-fill.ts +0 -37
- package/src/progress-bar/progress-bar.context.ts +0 -34
- package/src/progress-bar/progress-bar.spec.ts +0 -423
- package/src/progress-bar/progress-bar.ts +0 -217
- package/src/public-api.ts +0 -84
- package/src/radio/index.ts +0 -2
- package/src/radio/radio.context.ts +0 -3
- package/src/radio/radio.spec.ts +0 -36
- package/src/radio/radio.ts +0 -66
- package/src/select/index.ts +0 -4
- package/src/select/select-content.ts +0 -69
- package/src/select/select-option.ts +0 -35
- package/src/select/select-root.ts +0 -129
- package/src/select/select-trigger.ts +0 -93
- package/src/select/select.spec.ts +0 -138
- package/src/select/select.ts +0 -4
- package/src/skeleton/index.ts +0 -5
- package/src/skeleton/skeleton.spec.ts +0 -115
- package/src/skeleton/skeleton.ts +0 -56
- package/src/slider/index.ts +0 -10
- package/src/slider/slider-range.ts +0 -49
- package/src/slider/slider-thumb.ts +0 -335
- package/src/slider/slider-track.ts +0 -70
- package/src/slider/slider.context.ts +0 -79
- package/src/slider/slider.geometry.ts +0 -73
- package/src/slider/slider.spec.ts +0 -216
- package/src/slider/slider.ts +0 -443
- package/src/speed-dial/index.ts +0 -10
- package/src/speed-dial/speed-dial-action.ts +0 -40
- package/src/speed-dial/speed-dial-actions.ts +0 -31
- package/src/speed-dial/speed-dial-trigger.ts +0 -55
- package/src/speed-dial/speed-dial.context.ts +0 -36
- package/src/speed-dial/speed-dial.spec.ts +0 -143
- package/src/speed-dial/speed-dial.ts +0 -127
- package/src/spinner/config.ts +0 -48
- package/src/spinner/index.ts +0 -8
- package/src/spinner/spinner.spec.ts +0 -262
- package/src/spinner/spinner.ts +0 -129
- package/src/stepper/index.ts +0 -15
- package/src/stepper/stepper.context.ts +0 -63
- package/src/stepper/stepper.spec.ts +0 -403
- package/src/stepper/stepper.ts +0 -544
- package/src/styles/docs-themes.css +0 -82
- package/src/styles.css +0 -17
- package/src/table/column-helpers.spec.ts +0 -74
- package/src/table/column-helpers.ts +0 -68
- package/src/table/filter-fns.ts +0 -230
- package/src/table/filter-models.spec.ts +0 -240
- package/src/table/filter-models.ts +0 -124
- package/src/table/filter-params.ts +0 -90
- package/src/table/grid-api-impl.ts +0 -321
- package/src/table/grid-api.spec.ts +0 -178
- package/src/table/grid-api.ts +0 -285
- package/src/table/index.ts +0 -73
- package/src/table/table-cell.spec.ts +0 -43
- package/src/table/table-cell.ts +0 -38
- package/src/table/table-filter-outlet.ts +0 -120
- package/src/table/table-header.spec.ts +0 -54
- package/src/table/table-header.ts +0 -63
- package/src/table/table-keyboard.spec.ts +0 -68
- package/src/table/table-keyboard.ts +0 -107
- package/src/table/table-resource.spec.ts +0 -37
- package/src/table/table-resource.ts +0 -36
- package/src/table/table-row.spec.ts +0 -54
- package/src/table/table-row.ts +0 -31
- package/src/table/table-storage.spec.ts +0 -67
- package/src/table/table-storage.ts +0 -76
- package/src/table/table.spec.ts +0 -172
- package/src/table/table.ts +0 -281
- package/src/table/table.types.ts +0 -83
- package/src/tabs/index.ts +0 -7
- package/src/tabs/tabs.context.ts +0 -32
- package/src/tabs/tabs.spec.ts +0 -352
- package/src/tabs/tabs.ts +0 -364
- package/src/tag/config.ts +0 -53
- package/src/tag/index.ts +0 -7
- package/src/tag/tag-list.ts +0 -76
- package/src/tag/tag-remove.ts +0 -61
- package/src/tag/tag.context.ts +0 -36
- package/src/tag/tag.spec.ts +0 -240
- package/src/tag/tag.ts +0 -203
- package/src/test-setup.ts +0 -5
- package/src/textarea/config.ts +0 -42
- package/src/textarea/index.ts +0 -8
- package/src/textarea/textarea.spec.ts +0 -176
- package/src/textarea/textarea.ts +0 -235
- package/src/time-picker/index.ts +0 -15
- package/src/time-picker/time-picker-meridiem.ts +0 -123
- package/src/time-picker/time-picker-segment.ts +0 -318
- package/src/time-picker/time-picker.context.ts +0 -38
- package/src/time-picker/time-picker.format.ts +0 -181
- package/src/time-picker/time-picker.spec.ts +0 -189
- package/src/time-picker/time-picker.ts +0 -274
- package/src/toast/_examples/index.ts +0 -5
- package/src/toast/_examples/toast.example.ts +0 -282
- package/src/toast/_examples/toast.finance.example.ts +0 -180
- package/src/toast/_examples/toast.retro.example.ts +0 -161
- package/src/toast/index.ts +0 -28
- package/src/toast/toast.ref.ts +0 -26
- package/src/toast/toast.service.ts +0 -339
- package/src/toast/toast.spec.ts +0 -247
- package/src/toast/toast.strategy.ts +0 -112
- package/src/toast/toast.ts +0 -463
- package/src/toast/toast.types.ts +0 -5
- package/src/toggle/index.ts +0 -1
- package/src/toggle/toggle.spec.ts +0 -24
- package/src/toggle/toggle.ts +0 -48
- package/src/tooltip/_examples/index.ts +0 -6
- package/src/tooltip/_examples/tooltip.example.ts +0 -82
- package/src/tooltip/_examples/tooltip.finance.example.ts +0 -97
- package/src/tooltip/_examples/tooltip.placements.example.ts +0 -83
- package/src/tooltip/_examples/tooltip.retro.example.ts +0 -107
- package/src/tooltip/index.ts +0 -4
- package/src/tooltip/tooltip-arrow.ts +0 -31
- package/src/tooltip/tooltip-content.ts +0 -38
- package/src/tooltip/tooltip-group.ts +0 -32
- package/src/tooltip/tooltip-trigger.ts +0 -47
- package/src/tooltip/tooltip.spec.ts +0 -40
- package/src/tooltip/tooltip.ts +0 -5
- package/src/tree-select/index.ts +0 -12
- package/src/tree-select/tree-select-content.ts +0 -199
- package/src/tree-select/tree-select-node.ts +0 -132
- package/src/tree-select/tree-select-root.ts +0 -272
- package/src/tree-select/tree-select-trigger.ts +0 -90
- package/src/tree-select/tree-select.context.ts +0 -63
- package/src/tree-select/tree-select.spec.ts +0 -433
- package/src/tree-select/tree-select.ts +0 -2
- package/src/typography/blockquote.ts +0 -57
- package/src/typography/code.ts +0 -53
- package/src/typography/index.ts +0 -22
- package/src/typography/lead.ts +0 -49
- package/src/typography/muted.ts +0 -29
- package/src/typography/truncate.ts +0 -99
- package/src/typography/typography.spec.ts +0 -203
- package/tsconfig.lib.json +0 -21
- package/tsconfig.lib.prod.json +0 -17
- package/tsconfig.spec.json +0 -18
- package/vite.config.ts +0 -27
- /package/{src/icon → icon}/icon.css +0 -0
- /package/{src/typography → typography}/prose.css +0 -0
package/src/kbd/kbd.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Directive,
|
|
3
|
-
ElementRef,
|
|
4
|
-
afterNextRender,
|
|
5
|
-
inject,
|
|
6
|
-
isDevMode,
|
|
7
|
-
} from '@angular/core';
|
|
8
|
-
import { KjSize, type KjSizePreset } from '../presets';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Default size preset used by Kbd consumers (`xs / sm / md / lg`, default `md`).
|
|
12
|
-
*
|
|
13
|
-
* Re-exported so core-package consumers applying `[kjKbd]` directly to their
|
|
14
|
-
* own `<kbd>` elements can opt into the same size scale the eventual styled
|
|
15
|
-
* wrapper will provide, via:
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* providers: [{ provide: KJ_SIZE_PRESET, useValue: KJ_KBD_SIZE_PRESET }]
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* Avoids the silent-drift case where a consumer picks `'huge'` and the
|
|
22
|
-
* directive's dev-mode validator passes (because the consumer's preset didn't
|
|
23
|
-
* list it) but the CSS doesn't recognise the value. See
|
|
24
|
-
* `docs/component-analyses/data-display/kbd.md` (Open question 13).
|
|
25
|
-
*/
|
|
26
|
-
export const KJ_KBD_SIZE_PRESET: KjSizePreset = {
|
|
27
|
-
values: ['xs', 'sm', 'md', 'lg'],
|
|
28
|
-
default: 'md',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Marks an element as a kouji keyboard-key visual (`<kbd>`). The directive is
|
|
33
|
-
* a pure visual primitive: it composes `KjSize` (the only configurable axis)
|
|
34
|
-
* and otherwise relies on the native `<kbd>` element's a11y semantics.
|
|
35
|
-
*
|
|
36
|
-
* The host element is consumer-supplied. The recommended host is `<kbd>`
|
|
37
|
-
* (native semantic — implicit `role="generic"` per HTML AAM, but commonly
|
|
38
|
-
* announced with a "key" prefix in verbose AT modes); applying `[kjKbd]` to
|
|
39
|
-
* any other element is dev-mode warned but not enforced (consumers wanting a
|
|
40
|
-
* `<span>` host for inline-baseline reasons get an escape hatch).
|
|
41
|
-
*
|
|
42
|
-
* **No variant.** Kbd is tonally neutral — there is no "destructive kbd" or
|
|
43
|
-
* "primary kbd". A future design language wanting e.g. a "deprecated shortcut"
|
|
44
|
-
* tone can fold in via a CSS-only `[data-deprecated="true"]` attribute without
|
|
45
|
-
* API churn.
|
|
46
|
-
*
|
|
47
|
-
* **No keyboard contract.** Kbd is non-focusable, non-clickable. Consumers
|
|
48
|
-
* wanting a clickable shortcut chip wrap a `<button kjButton>` around the
|
|
49
|
-
* `<kbd kjKbd>` and the button owns the click / focus / a11y story. Embedding
|
|
50
|
-
* a focusable descendant inside `[kjKbd]` is dev-mode warned because most AT
|
|
51
|
-
* will re-parent or strip the interactive child out of the kbd's accessible
|
|
52
|
-
* name.
|
|
53
|
-
*
|
|
54
|
-
* **No `aria-label` injection.** When the visible text is a unicode glyph
|
|
55
|
-
* (`⌘`, `⌥`, `↵`) the consumer sets `aria-label` directly on the host —
|
|
56
|
-
* exactly the same idiom as setting `aria-label` on any HTML element. The
|
|
57
|
-
* directive does not declare a duplicate input for it. (The eventual
|
|
58
|
-
* `<kj-kbd>` wrapper exposes a template-driven `kjKbdAriaLabel` input that
|
|
59
|
-
* forwards to `[attr.aria-label]`; that is the wrapper's call, not the
|
|
60
|
-
* directive's.)
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```html
|
|
64
|
-
* <kbd kjKbd>Enter</kbd>
|
|
65
|
-
* <kbd kjKbd kjSize="xs">⌘</kbd>
|
|
66
|
-
* <kbd kjKbd aria-label="Command">⌘</kbd>
|
|
67
|
-
* ```
|
|
68
|
-
* @doc-category Core/Data display
|
|
69
|
-
* @doc
|
|
70
|
-
* @doc-name kbd
|
|
71
|
-
* @doc-description Marks a kbd element as a keyboard-key visual with a configurable size.
|
|
72
|
-
* @doc-is-main
|
|
73
|
-
*/
|
|
74
|
-
@Directive({
|
|
75
|
-
selector: '[kjKbd]',
|
|
76
|
-
standalone: true,
|
|
77
|
-
hostDirectives: [{ directive: KjSize, inputs: ['kjSize'] }],
|
|
78
|
-
})
|
|
79
|
-
export class KjKbd {
|
|
80
|
-
private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
81
|
-
|
|
82
|
-
constructor() {
|
|
83
|
-
if (isDevMode()) {
|
|
84
|
-
afterNextRender(() => {
|
|
85
|
-
const host = this.el.nativeElement;
|
|
86
|
-
|
|
87
|
-
if (host.tagName?.toLowerCase() !== 'kbd') {
|
|
88
|
-
console.warn(
|
|
89
|
-
`[kj] kjKbd applied to <${host.tagName?.toLowerCase()}>. ` +
|
|
90
|
-
`Recommended host element is <kbd> for native semantics ` +
|
|
91
|
-
`(WCAG 1.3.1 / 4.1.2). Apply only to non-kbd elements when ` +
|
|
92
|
-
`you have a specific styling reason.`,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (hasFocusableDescendant(host)) {
|
|
97
|
-
console.warn(
|
|
98
|
-
`[kj] kjKbd contains a focusable descendant. Kbd is a ` +
|
|
99
|
-
`non-interactive visual primitive; most AT will re-parent or ` +
|
|
100
|
-
`strip interactive children out of the kbd's accessible name. ` +
|
|
101
|
-
`Wrap the <kbd kjKbd> inside a <button kjButton> instead of ` +
|
|
102
|
-
`nesting an interactive element inside it.`,
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Selector matching elements that are focusable by default. Intentionally
|
|
112
|
-
* narrow: this is a dev-mode advisory, not an a11y enforcement gate, so we
|
|
113
|
-
* accept the small risk of missing exotic focusable descendants
|
|
114
|
-
* (`contenteditable`, embedded `<object>` with focusable controls, etc.) in
|
|
115
|
-
* favour of a fast first-pass check.
|
|
116
|
-
*/
|
|
117
|
-
const FOCUSABLE_SELECTOR = [
|
|
118
|
-
'a[href]',
|
|
119
|
-
'button',
|
|
120
|
-
'input',
|
|
121
|
-
'select',
|
|
122
|
-
'textarea',
|
|
123
|
-
'audio[controls]',
|
|
124
|
-
'video[controls]',
|
|
125
|
-
'[tabindex]',
|
|
126
|
-
'[contenteditable=""]',
|
|
127
|
-
'[contenteditable="true"]',
|
|
128
|
-
].join(',');
|
|
129
|
-
|
|
130
|
-
function hasFocusableDescendant(host: HTMLElement): boolean {
|
|
131
|
-
return host.querySelector(FOCUSABLE_SELECTOR) !== null;
|
|
132
|
-
}
|
package/src/link/config.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { InjectionToken, Provider } from '@angular/core';
|
|
2
|
-
|
|
3
|
-
export interface KjLinkConfig {
|
|
4
|
-
variants: string[];
|
|
5
|
-
sizes: string[];
|
|
6
|
-
defaults: { variant: string; size: string };
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Default Link presets shipped by kouji-ui. Exported so consumers can spread
|
|
11
|
-
* them when extending: `[...KJ_LINK_DEFAULTS.variants, 'brand']`.
|
|
12
|
-
*/
|
|
13
|
-
export const KJ_LINK_DEFAULTS: KjLinkConfig = {
|
|
14
|
-
variants: ['primary', 'secondary', 'muted', 'destructive'],
|
|
15
|
-
sizes: ['sm', 'md', 'lg', 'inherit'],
|
|
16
|
-
defaults: { variant: 'primary', size: 'inherit' },
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* DI token for the active Link presets. Default factory yields
|
|
21
|
-
* `KJ_LINK_DEFAULTS`. Override via `provideKjLink(…)` at the application
|
|
22
|
-
* scope (e.g. `bootstrapApplication`'s `providers` or a route's `providers`)
|
|
23
|
-
* or at the component scope (a component's own `providers: […]`).
|
|
24
|
-
*/
|
|
25
|
-
export const KJ_LINK_CONFIG = new InjectionToken<KjLinkConfig>('kj.link.config', {
|
|
26
|
-
factory: () => KJ_LINK_DEFAULTS,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Configures the Link presets for the enclosing injector. Replaces (does not
|
|
31
|
-
* merge) `variants` and `sizes`; spread `KJ_LINK_DEFAULTS.variants` to extend.
|
|
32
|
-
*
|
|
33
|
-
* Returns a `Provider[]` so it can be spread into either an environment
|
|
34
|
-
* `providers` (`bootstrapApplication`, route config) or a component-level
|
|
35
|
-
* `providers` array.
|
|
36
|
-
*/
|
|
37
|
-
export function provideKjLink(config: Partial<KjLinkConfig>): Provider[] {
|
|
38
|
-
return [
|
|
39
|
-
{
|
|
40
|
-
provide: KJ_LINK_CONFIG,
|
|
41
|
-
useValue: { ...KJ_LINK_DEFAULTS, ...config },
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
}
|
package/src/link/index.ts
DELETED
package/src/link/link.spec.ts
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { render } from '@testing-library/angular';
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
import { KjLink } from './link';
|
|
6
|
-
|
|
7
|
-
@Component({
|
|
8
|
-
standalone: true,
|
|
9
|
-
imports: [KjLink],
|
|
10
|
-
changeDetection: ChangeDetectionStrategy.Eager,
|
|
11
|
-
template: `<a kjLink href="/x" [kjDisabled]="d" (click)="onClick()">x</a>`,
|
|
12
|
-
})
|
|
13
|
-
class DisabledClickHost {
|
|
14
|
-
d = true;
|
|
15
|
-
fired = 0;
|
|
16
|
-
onClick() {
|
|
17
|
-
this.fired++;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
@Component({
|
|
22
|
-
standalone: true,
|
|
23
|
-
imports: [KjLink],
|
|
24
|
-
changeDetection: ChangeDetectionStrategy.Eager,
|
|
25
|
-
template: `<a kjLink href="/x" [kjDisabled]="d" (keydown)="onKeydown($event)">x</a>`,
|
|
26
|
-
})
|
|
27
|
-
class DisabledKeydownHost {
|
|
28
|
-
d = true;
|
|
29
|
-
navigated = 0;
|
|
30
|
-
onKeydown(_event: KeyboardEvent) {
|
|
31
|
-
this.navigated++;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe('KjLink', () => {
|
|
36
|
-
it('renders an anchor with role=link via native <a>', async () => {
|
|
37
|
-
const { getByRole } = await render(`<a kjLink href="/foo">Foo</a>`, { imports: [KjLink] });
|
|
38
|
-
expect(getByRole('link')).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('default variant comes from KJ_LINK_DEFAULTS', async () => {
|
|
42
|
-
const { getByRole } = await render(`<a kjLink href="/x">x</a>`, { imports: [KjLink] });
|
|
43
|
-
expect(getByRole('link')).toHaveAttribute('data-variant', 'primary');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('default size comes from KJ_LINK_DEFAULTS', async () => {
|
|
47
|
-
const { getByRole } = await render(`<a kjLink href="/x">x</a>`, { imports: [KjLink] });
|
|
48
|
-
expect(getByRole('link')).toHaveAttribute('data-size', 'inherit');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('reflects kjVariant to data-variant', async () => {
|
|
52
|
-
const { getByRole } = await render(`<a kjLink href="/x" [kjVariant]="'destructive'">x</a>`, {
|
|
53
|
-
imports: [KjLink],
|
|
54
|
-
});
|
|
55
|
-
expect(getByRole('link')).toHaveAttribute('data-variant', 'destructive');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('reflects kjSize to data-size', async () => {
|
|
59
|
-
const { getByRole } = await render(`<a kjLink href="/x" [kjSize]="'sm'">x</a>`, {
|
|
60
|
-
imports: [KjLink],
|
|
61
|
-
});
|
|
62
|
-
expect(getByRole('link')).toHaveAttribute('data-size', 'sm');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('reflects kjUnderline to data-underline (default hover)', async () => {
|
|
66
|
-
const { getByRole } = await render(`<a kjLink href="/x">x</a>`, { imports: [KjLink] });
|
|
67
|
-
expect(getByRole('link')).toHaveAttribute('data-underline', 'hover');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('reflects kjUnderline=always', async () => {
|
|
71
|
-
const { getByRole } = await render(`<a kjLink href="/x" [kjUnderline]="'always'">x</a>`, {
|
|
72
|
-
imports: [KjLink],
|
|
73
|
-
});
|
|
74
|
-
expect(getByRole('link')).toHaveAttribute('data-underline', 'always');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('reflects kjUnderline=none', async () => {
|
|
78
|
-
const { getByRole } = await render(`<a kjLink href="/x" [kjUnderline]="'none'">x</a>`, {
|
|
79
|
-
imports: [KjLink],
|
|
80
|
-
});
|
|
81
|
-
expect(getByRole('link')).toHaveAttribute('data-underline', 'none');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('auto-detects external from target="_blank" and reflects data-external', async () => {
|
|
85
|
-
const { getByRole } = await render(
|
|
86
|
-
`<a kjLink href="https://example.com" target="_blank">Docs</a>`,
|
|
87
|
-
{ imports: [KjLink] },
|
|
88
|
-
);
|
|
89
|
-
expect(getByRole('link')).toHaveAttribute('data-external', 'true');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('does not mark as external when no target="_blank" and kjExternal unset', async () => {
|
|
93
|
-
const { getByRole } = await render(`<a kjLink href="https://example.com">Docs</a>`, {
|
|
94
|
-
imports: [KjLink],
|
|
95
|
-
});
|
|
96
|
-
expect(getByRole('link')).not.toHaveAttribute('data-external');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('explicit kjExternal=true overrides target detection', async () => {
|
|
100
|
-
const { getByRole } = await render(`<a kjLink href="/internal" [kjExternal]="true">Docs</a>`, {
|
|
101
|
-
imports: [KjLink],
|
|
102
|
-
});
|
|
103
|
-
expect(getByRole('link')).toHaveAttribute('data-external', 'true');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('explicit kjExternal=false suppresses external treatment even with target=_blank', async () => {
|
|
107
|
-
const { getByRole } = await render(
|
|
108
|
-
`<a kjLink href="/x" target="_blank" [kjExternal]="false">x</a>`,
|
|
109
|
-
{ imports: [KjLink] },
|
|
110
|
-
);
|
|
111
|
-
expect(getByRole('link')).not.toHaveAttribute('data-external');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('injects rel="noopener noreferrer" when external', async () => {
|
|
115
|
-
const { getByRole } = await render(
|
|
116
|
-
`<a kjLink href="https://example.com" target="_blank">Docs</a>`,
|
|
117
|
-
{ imports: [KjLink] },
|
|
118
|
-
);
|
|
119
|
-
const rel = getByRole('link').getAttribute('rel') ?? '';
|
|
120
|
-
const tokens = rel.split(/\s+/u).filter(Boolean);
|
|
121
|
-
expect(tokens).toContain('noopener');
|
|
122
|
-
expect(tokens).toContain('noreferrer');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('rel injection is additive — preserves consumer-supplied tokens', async () => {
|
|
126
|
-
const { getByRole } = await render(
|
|
127
|
-
`<a kjLink href="https://example.com" target="_blank" rel="nofollow me">Docs</a>`,
|
|
128
|
-
{ imports: [KjLink] },
|
|
129
|
-
);
|
|
130
|
-
const rel = getByRole('link').getAttribute('rel') ?? '';
|
|
131
|
-
const tokens = rel.split(/\s+/u).filter(Boolean);
|
|
132
|
-
expect(tokens).toContain('nofollow');
|
|
133
|
-
expect(tokens).toContain('me');
|
|
134
|
-
expect(tokens).toContain('noopener');
|
|
135
|
-
expect(tokens).toContain('noreferrer');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('does not add noopener/noreferrer when not external', async () => {
|
|
139
|
-
const { getByRole } = await render(`<a kjLink href="/internal" rel="nofollow">x</a>`, {
|
|
140
|
-
imports: [KjLink],
|
|
141
|
-
});
|
|
142
|
-
const rel = getByRole('link').getAttribute('rel') ?? '';
|
|
143
|
-
expect(rel.split(/\s+/u)).not.toContain('noopener');
|
|
144
|
-
expect(rel.split(/\s+/u)).toContain('nofollow');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('appends visually-hidden "(opens in new tab)" suffix span when external', async () => {
|
|
148
|
-
const { getByRole } = await render(
|
|
149
|
-
`<a kjLink href="https://example.com" target="_blank">Docs</a>`,
|
|
150
|
-
{ imports: [KjLink] },
|
|
151
|
-
);
|
|
152
|
-
const link = getByRole('link');
|
|
153
|
-
const suffix = link.querySelector('.kj-link-external-suffix');
|
|
154
|
-
expect(suffix).not.toBeNull();
|
|
155
|
-
expect(suffix!.textContent).toContain('opens in new tab');
|
|
156
|
-
// Visually hidden inline style applied.
|
|
157
|
-
const style = (suffix as HTMLElement).getAttribute('style') ?? '';
|
|
158
|
-
expect(style).toContain('width:1px');
|
|
159
|
-
expect(style).toContain('height:1px');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('does not inject AT suffix when consumer supplies aria-label', async () => {
|
|
163
|
-
const { getByRole } = await render(
|
|
164
|
-
`<a kjLink href="https://example.com" target="_blank" aria-label="Docs (new window)">Docs</a>`,
|
|
165
|
-
{ imports: [KjLink] },
|
|
166
|
-
);
|
|
167
|
-
const link = getByRole('link');
|
|
168
|
-
expect(link.querySelector('.kj-link-external-suffix')).toBeNull();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('does not inject AT suffix for non-external links', async () => {
|
|
172
|
-
const { getByRole } = await render(`<a kjLink href="/x">x</a>`, { imports: [KjLink] });
|
|
173
|
-
expect(getByRole('link').querySelector('.kj-link-external-suffix')).toBeNull();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('disabled bundle: aria-disabled, data-disabled, tabindex=-1', async () => {
|
|
177
|
-
const { getByRole } = await render(`<a kjLink href="/x" [kjDisabled]="true">x</a>`, {
|
|
178
|
-
imports: [KjLink],
|
|
179
|
-
});
|
|
180
|
-
const link = getByRole('link');
|
|
181
|
-
expect(link).toHaveAttribute('aria-disabled', 'true');
|
|
182
|
-
expect(link).toHaveAttribute('data-disabled', '');
|
|
183
|
-
expect(link).toHaveAttribute('tabindex', '-1');
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('omits aria-disabled / tabindex when not disabled', async () => {
|
|
187
|
-
const { getByRole } = await render(`<a kjLink href="/x">x</a>`, { imports: [KjLink] });
|
|
188
|
-
const link = getByRole('link');
|
|
189
|
-
expect(link).not.toHaveAttribute('aria-disabled');
|
|
190
|
-
expect(link).not.toHaveAttribute('tabindex');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('suppresses click events when disabled (capture-phase)', () => {
|
|
194
|
-
TestBed.configureTestingModule({ imports: [DisabledClickHost] });
|
|
195
|
-
const fixture = TestBed.createComponent(DisabledClickHost);
|
|
196
|
-
fixture.detectChanges();
|
|
197
|
-
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
|
|
198
|
-
link.click();
|
|
199
|
-
expect(fixture.componentInstance.fired).toBe(0);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('allows click events when not disabled', () => {
|
|
203
|
-
TestBed.configureTestingModule({ imports: [DisabledClickHost] });
|
|
204
|
-
const fixture = TestBed.createComponent(DisabledClickHost);
|
|
205
|
-
fixture.componentInstance.d = false;
|
|
206
|
-
fixture.detectChanges();
|
|
207
|
-
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
|
|
208
|
-
link.addEventListener('click', (e) => e.preventDefault(), { once: true });
|
|
209
|
-
link.click();
|
|
210
|
-
expect(fixture.componentInstance.fired).toBe(1);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('suppresses Enter keydown events when disabled', () => {
|
|
214
|
-
TestBed.configureTestingModule({ imports: [DisabledKeydownHost] });
|
|
215
|
-
const fixture = TestBed.createComponent(DisabledKeydownHost);
|
|
216
|
-
fixture.detectChanges();
|
|
217
|
-
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
|
|
218
|
-
const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true });
|
|
219
|
-
link.dispatchEvent(event);
|
|
220
|
-
expect(event.defaultPrevented).toBe(true);
|
|
221
|
-
expect(fixture.componentInstance.navigated).toBe(0);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('does not suppress non-Enter keydown when disabled', () => {
|
|
225
|
-
TestBed.configureTestingModule({ imports: [DisabledKeydownHost] });
|
|
226
|
-
const fixture = TestBed.createComponent(DisabledKeydownHost);
|
|
227
|
-
fixture.detectChanges();
|
|
228
|
-
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
|
|
229
|
-
const event = new KeyboardEvent('keydown', { key: 'Tab', bubbles: true, cancelable: true });
|
|
230
|
-
link.dispatchEvent(event);
|
|
231
|
-
expect(event.defaultPrevented).toBe(false);
|
|
232
|
-
expect(fixture.componentInstance.navigated).toBe(1);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('does not suppress Enter when not disabled', () => {
|
|
236
|
-
TestBed.configureTestingModule({ imports: [DisabledKeydownHost] });
|
|
237
|
-
const fixture = TestBed.createComponent(DisabledKeydownHost);
|
|
238
|
-
fixture.componentInstance.d = false;
|
|
239
|
-
fixture.detectChanges();
|
|
240
|
-
const link = fixture.nativeElement.querySelector('a') as HTMLAnchorElement;
|
|
241
|
-
const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true });
|
|
242
|
-
link.dispatchEvent(event);
|
|
243
|
-
expect(event.defaultPrevented).toBe(false);
|
|
244
|
-
expect(fixture.componentInstance.navigated).toBe(1);
|
|
245
|
-
});
|
|
246
|
-
});
|
package/src/link/link.ts
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { DOCUMENT } from '@angular/common';
|
|
2
|
-
import {
|
|
3
|
-
Directive,
|
|
4
|
-
ElementRef,
|
|
5
|
-
afterNextRender,
|
|
6
|
-
computed,
|
|
7
|
-
effect,
|
|
8
|
-
inject,
|
|
9
|
-
input,
|
|
10
|
-
} from '@angular/core';
|
|
11
|
-
import { KjFocusRing, KjDisabled } from '../primitives';
|
|
12
|
-
import { KjVariant, KjSize, bindPresets } from '../presets';
|
|
13
|
-
import { KJ_LINK_CONFIG } from './config';
|
|
14
|
-
|
|
15
|
-
/** CSS class applied to the visually-hidden "(opens in new tab)" suffix span. */
|
|
16
|
-
const KJ_LINK_EXTERNAL_SUFFIX_CLASS = 'kj-link-external-suffix';
|
|
17
|
-
|
|
18
|
-
/** AT suffix copy. English-only for v1; i18n is a v1.1 concern (see analysis §15). */
|
|
19
|
-
const KJ_LINK_EXTERNAL_SUFFIX_TEXT = '(opens in new tab)';
|
|
20
|
-
|
|
21
|
-
/** Inline style string equivalent to `KjVisuallyHidden` — applied to the AT suffix span. */
|
|
22
|
-
const KJ_LINK_VISUALLY_HIDDEN_STYLE =
|
|
23
|
-
'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Enhances a native `<a>` (or any element used as a link) with kouji-ui
|
|
27
|
-
* presets, focus-ring and disabled-link plumbing, and external-link
|
|
28
|
-
* `rel` + AT suffix discipline.
|
|
29
|
-
*
|
|
30
|
-
* Composes `KjVariant`, `KjSize`, `KjFocusRing`, and `KjDisabled` via
|
|
31
|
-
* `hostDirectives`. Variants and sizes are configurable via
|
|
32
|
-
* `provideKjLink(…)`.
|
|
33
|
-
*
|
|
34
|
-
* Owns three behaviours that a CSS-only solution cannot deliver:
|
|
35
|
-
*
|
|
36
|
-
* 1. **External-link plumbing.** When `kjExternal` is `true` (or `undefined`
|
|
37
|
-
* and the host carries `target="_blank"`), the directive ensures `rel`
|
|
38
|
-
* contains both `noopener` and `noreferrer` (additive — preserves any
|
|
39
|
-
* consumer-supplied `rel` tokens like `nofollow`, `me`, `bookmark`) and
|
|
40
|
-
* appends a visually-hidden `(opens in new tab)` `<span>` to the host's
|
|
41
|
-
* accessible name. The suffix is suppressed when the consumer has set
|
|
42
|
-
* their own `aria-label` (the consumer is presumed to have authored the
|
|
43
|
-
* complete accessible name).
|
|
44
|
-
*
|
|
45
|
-
* 2. **Disabled-link bundle.** When `kjDisabled` is `true`, the directive
|
|
46
|
-
* sets `aria-disabled="true"`, `tabindex="-1"`, and capture-phase
|
|
47
|
-
* intercepts both `click` and `keydown.enter` to call `preventDefault()`
|
|
48
|
-
* and `stopImmediatePropagation()`. Capture phase ensures the suppression
|
|
49
|
-
* runs before any consumer-bound `(click)` handler and before Angular
|
|
50
|
-
* Router's bubble-phase click handler — so `<a kjLink routerLink="/foo"
|
|
51
|
-
* [kjDisabled]="true">` does not navigate either.
|
|
52
|
-
*
|
|
53
|
-
* 3. **Underline-mode reflection.** `kjUnderline: 'always' | 'hover' |
|
|
54
|
-
* 'none'` reflects to `[attr.data-underline]` for the components-package
|
|
55
|
-
* CSS to key off. The `kj-prose a` selector overrides only when the
|
|
56
|
-
* directive's input is left at its default — explicit consumer values
|
|
57
|
-
* win over the prose container's defaults.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```html
|
|
61
|
-
* <a kjLink href="/about">Inline link</a>
|
|
62
|
-
* <a kjLink href="https://example.com" target="_blank">External</a>
|
|
63
|
-
* <a kjLink [kjDisabled]="!ready()" routerLink="/billing">Manage billing</a>
|
|
64
|
-
* ```
|
|
65
|
-
*
|
|
66
|
-
* @doc-category Core/Navigation
|
|
67
|
-
* @doc
|
|
68
|
-
* @doc-name link
|
|
69
|
-
* @doc-description Adds disabled-link, external-link, and underline-mode handling to a native anchor.
|
|
70
|
-
* @doc-is-main
|
|
71
|
-
*/
|
|
72
|
-
@Directive({
|
|
73
|
-
selector: 'a[kjLink], [kjLink]',
|
|
74
|
-
standalone: true,
|
|
75
|
-
hostDirectives: [
|
|
76
|
-
{ directive: KjVariant, inputs: ['kjVariant'] },
|
|
77
|
-
{ directive: KjSize, inputs: ['kjSize'] },
|
|
78
|
-
KjFocusRing,
|
|
79
|
-
{ directive: KjDisabled, inputs: ['kjDisabled'] },
|
|
80
|
-
],
|
|
81
|
-
providers: [...bindPresets(KJ_LINK_CONFIG)],
|
|
82
|
-
host: {
|
|
83
|
-
'[attr.data-underline]': 'kjUnderline()',
|
|
84
|
-
'[attr.data-external]': 'isExternal() ? "true" : null',
|
|
85
|
-
'[attr.tabindex]': 'kjDisabled() ? "-1" : null',
|
|
86
|
-
'[attr.rel]': 'relAttr()',
|
|
87
|
-
},
|
|
88
|
-
})
|
|
89
|
-
export class KjLink {
|
|
90
|
-
private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
91
|
-
private readonly document = inject(DOCUMENT);
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Disables the link. Forwarded to `KjDisabled` (which reflects
|
|
95
|
-
* `aria-disabled` and `data-disabled`); the directive additionally sets
|
|
96
|
-
* `tabindex="-1"` and intercepts `click` + `keydown.enter` in the capture
|
|
97
|
-
* phase.
|
|
98
|
-
*/
|
|
99
|
-
readonly kjDisabled = input(false);
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Underline policy. `'always'` for in-content links (WCAG 1.4.1 — colour
|
|
103
|
-
* is not the only means); `'hover'` for standalone links sitting in a
|
|
104
|
-
* group of links that already signals "this is a link" via layout;
|
|
105
|
-
* `'none'` for the rare cases where the consumer wants to suppress the
|
|
106
|
-
* underline entirely (e.g. icon-text links inside breadcrumb separators).
|
|
107
|
-
* Reflects `[attr.data-underline]`.
|
|
108
|
-
*/
|
|
109
|
-
readonly kjUnderline = input<'always' | 'hover' | 'none'>('hover');
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* External-link tri-state. `undefined` (default) auto-detects from the
|
|
113
|
-
* host's `target` attribute (`target="_blank"` → external). `true` forces
|
|
114
|
-
* external treatment regardless of `target` (e.g. for cross-origin URLs
|
|
115
|
-
* served through the app's router that still warrant the indicator).
|
|
116
|
-
* `false` forces internal treatment even when `target="_blank"` is
|
|
117
|
-
* present (suppresses the icon and AT suffix).
|
|
118
|
-
*/
|
|
119
|
-
readonly kjExternal = input<boolean | undefined>(undefined);
|
|
120
|
-
|
|
121
|
-
/** Whether the host element currently carries `target="_blank"`. */
|
|
122
|
-
protected readonly hasBlankTarget = computed(() => this.targetSignal() === '_blank');
|
|
123
|
-
|
|
124
|
-
/** Computed external state — explicit input wins, else target-based auto-detect. */
|
|
125
|
-
protected readonly isExternal = computed(() => {
|
|
126
|
-
const explicit = this.kjExternal();
|
|
127
|
-
if (explicit !== undefined) return explicit;
|
|
128
|
-
return this.hasBlankTarget();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* `rel` attribute computation. Additive: existing tokens (`nofollow`,
|
|
133
|
-
* `me`, `bookmark`, …) are preserved; the directive only adds the missing
|
|
134
|
-
* `noopener` / `noreferrer` security tokens when external. When not
|
|
135
|
-
* external, the directive returns `null` so the consumer's literal `rel`
|
|
136
|
-
* attribute (read at construction) is preserved as-is.
|
|
137
|
-
*/
|
|
138
|
-
protected readonly relAttr = computed(() => {
|
|
139
|
-
if (!this.isExternal()) return this.initialRel || null;
|
|
140
|
-
const tokens = new Set<string>();
|
|
141
|
-
if (this.initialRel) {
|
|
142
|
-
for (const token of this.initialRel.split(/\s+/u)) {
|
|
143
|
-
if (token) tokens.add(token);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
tokens.add('noopener');
|
|
147
|
-
tokens.add('noreferrer');
|
|
148
|
-
return [...tokens].join(' ');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Snapshot of the host's `target` attribute. Re-read after the host
|
|
153
|
-
* renders — host bindings are evaluated synchronously on creation, so
|
|
154
|
-
* the SSR HTML carries the right `[data-external]` / `[rel]` values
|
|
155
|
-
* provided the consumer's `target` is set declaratively in the template
|
|
156
|
-
* (the common case).
|
|
157
|
-
*/
|
|
158
|
-
private readonly targetSignal = (() => {
|
|
159
|
-
const initial = this.el.nativeElement.getAttribute('target');
|
|
160
|
-
return computed(() => initial);
|
|
161
|
-
})();
|
|
162
|
-
|
|
163
|
-
/** Snapshot of the host's `rel` attribute at construction time. */
|
|
164
|
-
private readonly initialRel: string | null = this.el.nativeElement.getAttribute('rel');
|
|
165
|
-
|
|
166
|
-
/** Reference to the injected AT suffix span, if any. Cleaned up on toggle. */
|
|
167
|
-
private suffixSpan: HTMLSpanElement | null = null;
|
|
168
|
-
|
|
169
|
-
constructor() {
|
|
170
|
-
// Capture-phase native listeners: fire BEFORE Angular's template-bound
|
|
171
|
-
// bubble-phase (click) handlers and before Angular Router's bubble-phase
|
|
172
|
-
// click handler on `<a routerLink>`, so `stopImmediatePropagation()`
|
|
173
|
-
// actually prevents both navigation and any consumer (click) handlers.
|
|
174
|
-
afterNextRender(() => {
|
|
175
|
-
const node = this.el.nativeElement;
|
|
176
|
-
|
|
177
|
-
const onClick = (event: Event) => {
|
|
178
|
-
if (this.kjDisabled()) {
|
|
179
|
-
event.preventDefault();
|
|
180
|
-
event.stopImmediatePropagation();
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
const onKeydown = (event: KeyboardEvent) => {
|
|
185
|
-
if (this.kjDisabled() && event.key === 'Enter') {
|
|
186
|
-
event.preventDefault();
|
|
187
|
-
event.stopImmediatePropagation();
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
node.addEventListener('click', onClick, { capture: true });
|
|
192
|
-
node.addEventListener('keydown', onKeydown, { capture: true });
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// External-link AT suffix injection. Runs reactively so the suffix
|
|
196
|
-
// appears / disappears as `kjExternal` (or the host's `target`) changes.
|
|
197
|
-
// The effect itself is created in injection context (constructor); its
|
|
198
|
-
// first run happens after the first change-detection pass, by which
|
|
199
|
-
// point the host element has been attached to the DOM in browser
|
|
200
|
-
// contexts. Use injected DOCUMENT — prerender VMs may have no global `document`.
|
|
201
|
-
effect(() => {
|
|
202
|
-
const external = this.isExternal();
|
|
203
|
-
const doc = this.document;
|
|
204
|
-
if (!doc.createElement) return;
|
|
205
|
-
|
|
206
|
-
const node = this.el.nativeElement;
|
|
207
|
-
const consumerOwnsName = node.hasAttribute('aria-label');
|
|
208
|
-
|
|
209
|
-
if (external && !consumerOwnsName) {
|
|
210
|
-
if (!this.suffixSpan) {
|
|
211
|
-
const span = doc.createElement('span');
|
|
212
|
-
span.className = KJ_LINK_EXTERNAL_SUFFIX_CLASS;
|
|
213
|
-
span.setAttribute('style', KJ_LINK_VISUALLY_HIDDEN_STYLE);
|
|
214
|
-
// Leading space so AT reads "Documentation (opens in new tab)"
|
|
215
|
-
// not "Documentation(opens in new tab)".
|
|
216
|
-
span.textContent = ` ${KJ_LINK_EXTERNAL_SUFFIX_TEXT}`;
|
|
217
|
-
node.appendChild(span);
|
|
218
|
-
this.suffixSpan = span;
|
|
219
|
-
}
|
|
220
|
-
} else if (this.suffixSpan) {
|
|
221
|
-
this.suffixSpan.remove();
|
|
222
|
-
this.suffixSpan = null;
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|