@kouji-ui/core 0.1.1 → 0.1.3
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 +15 -7
- 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
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
// packages/core/src/primitives/list/selection.ts
|
|
2
|
-
import { Injectable, computed, effect, signal, type Signal, type WritableSignal } from '@angular/core';
|
|
3
|
-
import type { KjListItem } from './item';
|
|
4
|
-
import type {
|
|
5
|
-
KjCompareFn,
|
|
6
|
-
KjListSelectionMode,
|
|
7
|
-
KjTreeShape,
|
|
8
|
-
} from './tokens';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Selection state shared by a list-style consumer (KjSelect, KjCombobox,
|
|
12
|
-
* KjTreeSelect, KjCascadeSelect). The consumer root calls {@link bind}
|
|
13
|
-
* from its constructor to wire in its canonical signals — the model
|
|
14
|
-
* reads/writes through those signals, so there is no second source of
|
|
15
|
-
* truth and no cyclic DI dependency (the model does not inject the
|
|
16
|
-
* root; the root injects the model).
|
|
17
|
-
*
|
|
18
|
-
* **Tree topology is first-class.** When the consumer supplies a
|
|
19
|
-
* `treeShape` signal, the model uses it. Otherwise the model
|
|
20
|
-
* **auto-derives** a shape from the registered `KjListItem` parent
|
|
21
|
-
* pointers (each item injects its nearest ancestor `KjListItem` via the
|
|
22
|
-
* element injector). That makes DOM-nested clusters (cascade-select,
|
|
23
|
-
* sub-menus) tree-aware out of the box; flat data-driven clusters
|
|
24
|
-
* (tree-select) keep using the explicit shape.
|
|
25
|
-
*
|
|
26
|
-
* Modes:
|
|
27
|
-
* - `'single'` — replace + close. With a shape, branch toggles no-op
|
|
28
|
-
* (only leaves enter `value`).
|
|
29
|
-
* - `'multi'` — flat toggle in array
|
|
30
|
-
* - `'leaf'` — tree-aware multi where only leaves enter `value`
|
|
31
|
-
* - `'cascade'` — tri-state tree selection
|
|
32
|
-
*
|
|
33
|
-
* @doc-category Core/Primitives
|
|
34
|
-
*/
|
|
35
|
-
@Injectable()
|
|
36
|
-
export class KjSelectionModel<T = unknown> {
|
|
37
|
-
/**
|
|
38
|
-
* Source signals. Replaced wholesale by {@link bind} so the
|
|
39
|
-
* consumer's reactive inputs become the canonical state. `bind` MUST
|
|
40
|
-
* be called from the consumer's constructor, before any descendant
|
|
41
|
-
* `KjListItem._activate` fires.
|
|
42
|
-
*/
|
|
43
|
-
private _value: WritableSignal<T | readonly T[] | null> =
|
|
44
|
-
signal<T | readonly T[] | null>(null);
|
|
45
|
-
private _items: Signal<readonly KjListItem<unknown>[] | null> = signal(null);
|
|
46
|
-
private _modeSig: Signal<KjListSelectionMode> = signal<KjListSelectionMode>('single');
|
|
47
|
-
private _cmpSig: Signal<KjCompareFn<T>> = signal(Object.is as KjCompareFn<T>);
|
|
48
|
-
private _shapeSig: Signal<KjTreeShape<T> | null> = signal(null);
|
|
49
|
-
|
|
50
|
-
private readonly _mode = signal<KjListSelectionMode>('single');
|
|
51
|
-
private _compareBy: KjCompareFn<T> = Object.is as KjCompareFn<T>;
|
|
52
|
-
private readonly _consumerShape = signal<KjTreeShape<T> | null>(null);
|
|
53
|
-
|
|
54
|
-
constructor() {
|
|
55
|
-
// Effects always re-evaluate the bound signals so the model stays
|
|
56
|
-
// in sync whether the consumer rebinds during construction or
|
|
57
|
-
// mutates the underlying signals later. Reading inside an effect
|
|
58
|
-
// (rather than at construction time) means the model is robust to
|
|
59
|
-
// property-initializer ordering on consumer roots.
|
|
60
|
-
effect(() => this._mode.set(this._modeSig()));
|
|
61
|
-
effect(() => { this._compareBy = this._cmpSig(); });
|
|
62
|
-
effect(() => this._consumerShape.set(this._shapeSig()));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Bind consumer-owned source signals. Each is optional; unbound keys
|
|
67
|
-
* keep their previous source (defaulting to constants on first
|
|
68
|
-
* construct). Call once in the consumer's constructor before any
|
|
69
|
-
* descendant `KjListItem` activation fires.
|
|
70
|
-
*/
|
|
71
|
-
bind(sources: {
|
|
72
|
-
readonly value?: WritableSignal<T | readonly T[] | null>;
|
|
73
|
-
readonly items?: Signal<readonly KjListItem<unknown>[]>;
|
|
74
|
-
readonly mode?: Signal<KjListSelectionMode>;
|
|
75
|
-
readonly compareBy?: Signal<KjCompareFn<T>>;
|
|
76
|
-
readonly treeShape?: Signal<KjTreeShape<T> | null>;
|
|
77
|
-
}): void {
|
|
78
|
-
if (sources.value) this._value = sources.value;
|
|
79
|
-
if (sources.items) this._items = sources.items as Signal<readonly KjListItem<unknown>[] | null>;
|
|
80
|
-
if (sources.mode) this._modeSig = sources.mode;
|
|
81
|
-
if (sources.compareBy) this._cmpSig = sources.compareBy;
|
|
82
|
-
if (sources.treeShape) this._shapeSig = sources.treeShape;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Current mode. */
|
|
86
|
-
readonly mode = this._mode.asReadonly();
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Effective tree topology — consumer-supplied (`bind({ treeShape })`)
|
|
90
|
-
* when present, otherwise auto-derived from registered `KjListItem`
|
|
91
|
-
* parent pointers. Returns `null` when neither is available.
|
|
92
|
-
*/
|
|
93
|
-
readonly shape: Signal<KjTreeShape<T> | null> = computed(() => {
|
|
94
|
-
const explicit = this._consumerShape();
|
|
95
|
-
if (explicit) return explicit;
|
|
96
|
-
return this._autoShape();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Auto-derived tree shape: a map of every registered item's value to
|
|
101
|
-
* its parent value (and inverse children list), built from each
|
|
102
|
-
* `KjListItem`'s injected `parent` reference. Only useful for
|
|
103
|
-
* DOM-nested clusters (cascade-select, sub-menus); flat clusters
|
|
104
|
-
* (tree-select) bind their own shape.
|
|
105
|
-
*/
|
|
106
|
-
private readonly _autoShape: Signal<KjTreeShape<T> | null> = computed(() => {
|
|
107
|
-
const items = this._items();
|
|
108
|
-
if (!items || items.length === 0) return null;
|
|
109
|
-
const parentOf = new Map<T, T | null>();
|
|
110
|
-
const childrenOf = new Map<T, T[]>();
|
|
111
|
-
const valueByItem = new Map<KjListItem<unknown>, T>();
|
|
112
|
-
for (const it of items) {
|
|
113
|
-
const v = it.value();
|
|
114
|
-
if (v === undefined) continue;
|
|
115
|
-
valueByItem.set(it, v as T);
|
|
116
|
-
}
|
|
117
|
-
for (const it of items) {
|
|
118
|
-
const v = valueByItem.get(it);
|
|
119
|
-
if (v === undefined) continue;
|
|
120
|
-
const parentItem = it.parent;
|
|
121
|
-
const parentV = parentItem ? valueByItem.get(parentItem) ?? null : null;
|
|
122
|
-
parentOf.set(v, parentV);
|
|
123
|
-
if (parentV !== null) {
|
|
124
|
-
const arr = childrenOf.get(parentV) ?? [];
|
|
125
|
-
arr.push(v);
|
|
126
|
-
childrenOf.set(parentV, arr);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const eq = this._compareBy;
|
|
130
|
-
const findKey = (n: T): T | undefined => {
|
|
131
|
-
for (const k of parentOf.keys()) {
|
|
132
|
-
if (eq(k, n)) return k;
|
|
133
|
-
}
|
|
134
|
-
return undefined;
|
|
135
|
-
};
|
|
136
|
-
return {
|
|
137
|
-
getParent: (n: T) => {
|
|
138
|
-
const key = findKey(n);
|
|
139
|
-
return key !== undefined ? (parentOf.get(key) ?? null) : null;
|
|
140
|
-
},
|
|
141
|
-
getChildren: (n: T) => {
|
|
142
|
-
const key = findKey(n);
|
|
143
|
-
return key !== undefined ? (childrenOf.get(key) ?? []) : [];
|
|
144
|
-
},
|
|
145
|
-
isLeaf: (n: T) => {
|
|
146
|
-
const key = findKey(n);
|
|
147
|
-
if (key === undefined) return true;
|
|
148
|
-
return (childrenOf.get(key)?.length ?? 0) === 0;
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
/** Switch mode imperatively (consumers should prefer binding a signal). */
|
|
154
|
-
setMode(mode: KjListSelectionMode): void {
|
|
155
|
-
this._mode.set(mode);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/** Replace the current selection value (writes through the bound signal). */
|
|
159
|
-
setValue(v: T | readonly T[] | null): void {
|
|
160
|
-
this._value.set(v);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Override the equality function. Defaults to `Object.is`. */
|
|
164
|
-
setCompareBy(fn: KjCompareFn<T>): void {
|
|
165
|
-
this._compareBy = fn;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Provide the tree topology imperatively. Pass `null` to clear and
|
|
170
|
-
* fall back to the auto-derived shape.
|
|
171
|
-
*/
|
|
172
|
-
setTreeShape(shape: KjTreeShape<T> | null): void {
|
|
173
|
-
this._consumerShape.set(shape);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Whether `target` is a leaf in the effective shape. Returns `true`
|
|
178
|
-
* when no shape is available (flat lists — everything is a "leaf").
|
|
179
|
-
*/
|
|
180
|
-
isLeaf(target: T): boolean {
|
|
181
|
-
const s = this.shape();
|
|
182
|
-
return s ? s.isLeaf(target) : true;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Root → target value chain via the effective shape. Returns
|
|
187
|
-
* `[target]` when the target has no parent in the shape, and an
|
|
188
|
-
* empty array when no shape is available.
|
|
189
|
-
*/
|
|
190
|
-
pathTo(target: T): readonly T[] {
|
|
191
|
-
const s = this.shape();
|
|
192
|
-
if (!s) return [];
|
|
193
|
-
const reverse: T[] = [target];
|
|
194
|
-
let cur: T | null = s.getParent(target);
|
|
195
|
-
while (cur !== null) {
|
|
196
|
-
reverse.unshift(cur);
|
|
197
|
-
cur = s.getParent(cur);
|
|
198
|
-
}
|
|
199
|
-
return reverse;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Activation gate. Returns `true` when committing `target` is
|
|
204
|
-
* meaningful in the current mode + shape:
|
|
205
|
-
* - `'single'` with a shape → only leaves activate. Branches are
|
|
206
|
-
* blocked so they can serve as pure disclosure controls (open a
|
|
207
|
-
* sub-panel) without leaking a branch value into `value`.
|
|
208
|
-
* - `'single'` without a shape → any value activates.
|
|
209
|
-
* - `'multi'` → any value activates.
|
|
210
|
-
* - `'leaf'` → only leaves activate (mirrors `toggle`'s no-op).
|
|
211
|
-
* - `'cascade'` → any value activates (tri-state toggle applies).
|
|
212
|
-
*/
|
|
213
|
-
canActivate(target: T): boolean {
|
|
214
|
-
const mode = this._mode();
|
|
215
|
-
if (mode === 'multi' || mode === 'cascade') return true;
|
|
216
|
-
const s = this.shape();
|
|
217
|
-
if (!s) return true;
|
|
218
|
-
return s.isLeaf(target);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/** Whether `target` is currently in the selection (membership check). */
|
|
222
|
-
isSelected(target: T): boolean {
|
|
223
|
-
const v = this._value();
|
|
224
|
-
if (v === null) return false;
|
|
225
|
-
const mode = this._mode();
|
|
226
|
-
if (mode === 'multi' || mode === 'leaf' || mode === 'cascade') {
|
|
227
|
-
return Array.isArray(v) && v.some(x => this._compareBy(x, target));
|
|
228
|
-
}
|
|
229
|
-
return this._compareBy(v as T, target);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Tri-state for `'cascade'` mode. Walks descendants via the effective
|
|
234
|
-
* tree shape:
|
|
235
|
-
* - `'true'` — node and all leaf descendants are selected
|
|
236
|
-
* - `'mixed'` — some but not all descendants are selected
|
|
237
|
-
* - `'false'` — no descendants are selected
|
|
238
|
-
*
|
|
239
|
-
* In any other mode (or when no tree shape is set), returns `'true'`
|
|
240
|
-
* / `'false'` based on `isSelected()`.
|
|
241
|
-
*/
|
|
242
|
-
cascadeState(target: T): 'true' | 'false' | 'mixed' {
|
|
243
|
-
const shape = this.shape();
|
|
244
|
-
if (this._mode() !== 'cascade' || !shape) {
|
|
245
|
-
return this.isSelected(target) ? 'true' : 'false';
|
|
246
|
-
}
|
|
247
|
-
return this._cascadeState(target, shape);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
private _cascadeState(node: T, shape: KjTreeShape<T>): 'true' | 'false' | 'mixed' {
|
|
251
|
-
if (shape.isLeaf(node)) {
|
|
252
|
-
return this.isSelected(node) ? 'true' : 'false';
|
|
253
|
-
}
|
|
254
|
-
const children = shape.getChildren(node);
|
|
255
|
-
if (children.length === 0) {
|
|
256
|
-
return this.isSelected(node) ? 'true' : 'false';
|
|
257
|
-
}
|
|
258
|
-
let allTrue = true;
|
|
259
|
-
let anyTrueOrMixed = false;
|
|
260
|
-
for (const c of children) {
|
|
261
|
-
const s = this._cascadeState(c, shape);
|
|
262
|
-
if (s !== 'true') allTrue = false;
|
|
263
|
-
if (s !== 'false') anyTrueOrMixed = true;
|
|
264
|
-
}
|
|
265
|
-
if (allTrue) return 'true';
|
|
266
|
-
if (anyTrueOrMixed) return 'mixed';
|
|
267
|
-
return 'false';
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Toggle `target` per the current mode:
|
|
272
|
-
* - `'single'` — with a shape, branch targets no-op
|
|
273
|
-
* (`closeRequested: false`); leaves replace value and return
|
|
274
|
-
* `closeRequested: true`. Without a shape, replaces unconditionally.
|
|
275
|
-
* - `'multi'` — flat toggle in array; `closeRequested: false`
|
|
276
|
-
* - `'leaf'` — toggle only when `target` is a leaf; branches are no-ops
|
|
277
|
-
* - `'cascade'` — toggle target + all leaf descendants based on current
|
|
278
|
-
* tri-state (`'true'` deselects, `'false'`/`'mixed'` selects)
|
|
279
|
-
*/
|
|
280
|
-
toggle(target: T): { closeRequested: boolean } {
|
|
281
|
-
const mode = this._mode();
|
|
282
|
-
const shape = this.shape();
|
|
283
|
-
if (mode === 'cascade' && shape) {
|
|
284
|
-
return this._cascadeToggle(target, shape);
|
|
285
|
-
}
|
|
286
|
-
if (mode === 'leaf' && shape) {
|
|
287
|
-
if (!shape.isLeaf(target)) return { closeRequested: false };
|
|
288
|
-
return this._multiToggle(target);
|
|
289
|
-
}
|
|
290
|
-
if (mode === 'multi' || mode === 'leaf' || mode === 'cascade') {
|
|
291
|
-
return this._multiToggle(target);
|
|
292
|
-
}
|
|
293
|
-
// Single mode — gate branches when a shape is available.
|
|
294
|
-
if (shape && !shape.isLeaf(target)) return { closeRequested: false };
|
|
295
|
-
this._value.set(target);
|
|
296
|
-
return { closeRequested: true };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
private _multiToggle(target: T): { closeRequested: boolean } {
|
|
300
|
-
const current = this._value();
|
|
301
|
-
const arr = Array.isArray(current) ? [...current] : [];
|
|
302
|
-
const idx = arr.findIndex(x => this._compareBy(x, target));
|
|
303
|
-
if (idx >= 0) arr.splice(idx, 1);
|
|
304
|
-
else arr.push(target);
|
|
305
|
-
this._value.set(arr);
|
|
306
|
-
return { closeRequested: false };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private _cascadeToggle(target: T, shape: KjTreeShape<T>): { closeRequested: boolean } {
|
|
310
|
-
const currentState = this._cascadeState(target, shape);
|
|
311
|
-
const shouldSelect = currentState !== 'true';
|
|
312
|
-
|
|
313
|
-
const collectLeaves = (node: T): T[] => {
|
|
314
|
-
if (shape.isLeaf(node)) return [node];
|
|
315
|
-
const out: T[] = [];
|
|
316
|
-
for (const c of shape.getChildren(node)) out.push(...collectLeaves(c));
|
|
317
|
-
return out;
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const leaves = collectLeaves(target);
|
|
321
|
-
const current = this._value();
|
|
322
|
-
const arr = Array.isArray(current) ? [...current] : [];
|
|
323
|
-
|
|
324
|
-
for (const leaf of leaves) {
|
|
325
|
-
const idx = arr.findIndex(x => this._compareBy(x, leaf));
|
|
326
|
-
if (shouldSelect && idx < 0) arr.push(leaf);
|
|
327
|
-
else if (!shouldSelect && idx >= 0) arr.splice(idx, 1);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
this._value.set(arr);
|
|
331
|
-
return { closeRequested: false };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** Clear the selection. `single` → `null`; multi-style → `[]`. */
|
|
335
|
-
clear(): void {
|
|
336
|
-
const mode = this._mode();
|
|
337
|
-
this._value.set(mode === 'single' ? null : []);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { InjectionToken, Signal, WritableSignal } from '@angular/core';
|
|
2
|
-
import type { KjListItem } from './item';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The single contract a consumer root directive (KjSelect, KjCombobox,
|
|
6
|
-
* KjCommandPalette, …) implements + provides via
|
|
7
|
-
* `useExisting: forwardRef(() => Self)`. Primitives read whichever
|
|
8
|
-
* fields they need from this one object — items for the navigator,
|
|
9
|
-
* `value` for the selection model, `visibleItems` for filter-aware nav.
|
|
10
|
-
*/
|
|
11
|
-
export interface KjListNavigatorConfig {
|
|
12
|
-
/** All registered items in DOM order. Source of truth for nav + filter. */
|
|
13
|
-
readonly items: Signal<readonly KjListItem<unknown>[]>;
|
|
14
|
-
/** Filter-aware visible subset. Falls back to `items` when not provided. */
|
|
15
|
-
readonly visibleItems?: Signal<readonly KjListItem<unknown>[]>;
|
|
16
|
-
/**
|
|
17
|
-
* Writable signal holding the canonical selection value (typically the
|
|
18
|
-
* consumer's `model()` input). When the consumer provides
|
|
19
|
-
* `KjSelectionModel`, it reads / writes through this signal — one
|
|
20
|
-
* source of truth, no bridging effects. Optional: consumers without
|
|
21
|
-
* selection state (e.g. KjCommandPalette) omit it.
|
|
22
|
-
*/
|
|
23
|
-
readonly value?: WritableSignal<unknown | readonly unknown[] | null>;
|
|
24
|
-
/**
|
|
25
|
-
* Selection mode signal. When provided, `KjSelectionModel` follows it
|
|
26
|
-
* reactively — consumers don't need to inject the model just to call
|
|
27
|
-
* `setMode()`. Default `'single'`.
|
|
28
|
-
*/
|
|
29
|
-
readonly mode?: Signal<KjListSelectionMode>;
|
|
30
|
-
/**
|
|
31
|
-
* Equality fn signal. When provided, `KjSelectionModel` follows it
|
|
32
|
-
* reactively. Default `Object.is`.
|
|
33
|
-
*/
|
|
34
|
-
readonly compareBy?: Signal<KjCompareFn<unknown>>;
|
|
35
|
-
/**
|
|
36
|
-
* Tree topology signal. When provided, `KjSelectionModel` follows it
|
|
37
|
-
* reactively, so the consumer never needs to inject the model just
|
|
38
|
-
* to call `setTreeShape()`. Required for `'leaf'` / `'cascade'`
|
|
39
|
-
* selection modes; optional otherwise.
|
|
40
|
-
*/
|
|
41
|
-
readonly treeShape?: Signal<KjTreeShape<unknown> | null>;
|
|
42
|
-
/**
|
|
43
|
-
* Consumer-side hook invoked by `KjListItem` immediately after it
|
|
44
|
-
* toggles the shared `KjSelectionModel`. Lets the consumer do its
|
|
45
|
-
* post-selection work (close an overlay, set an input query, emit a
|
|
46
|
-
* commit event) without subscribing to every option's `activate`
|
|
47
|
-
* output — pushing the repeat pattern down into one place.
|
|
48
|
-
*
|
|
49
|
-
* `closeRequested` mirrors `KjSelectionModel.toggle()`'s return value:
|
|
50
|
-
* `true` in single mode, `false` in multi / leaf / cascade modes.
|
|
51
|
-
*/
|
|
52
|
-
afterSelect?(value: unknown, closeRequested: boolean): void;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const KJ_LIST_NAVIGATOR_CONFIG =
|
|
56
|
-
new InjectionToken<KjListNavigatorConfig>('KJ_LIST_NAVIGATOR_CONFIG');
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Focus model for a list-style cluster:
|
|
60
|
-
*
|
|
61
|
-
* - `'activedescendant'` — host element keeps DOM focus; the active
|
|
62
|
-
* option is signalled via `aria-activedescendant`. Default for
|
|
63
|
-
* listbox / combobox / command-palette.
|
|
64
|
-
* - `'roving'` — the active option *is* the focused element; focus
|
|
65
|
-
* moves between options via roving `tabindex`. Used by menus,
|
|
66
|
-
* menubars, tree-select.
|
|
67
|
-
*/
|
|
68
|
-
export type KjListFocusMode = 'activedescendant' | 'roving';
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Provided by `KjListNavigator` so child `KjListItem`s can read the
|
|
72
|
-
* current focus mode reactively and bind their own `tabindex`. Items
|
|
73
|
-
* inject this `{ optional: true }` — when absent (no navigator on the
|
|
74
|
-
* host) they fall back to `'activedescendant'` behavior.
|
|
75
|
-
*/
|
|
76
|
-
export const KJ_LIST_FOCUS_MODE =
|
|
77
|
-
new InjectionToken<Signal<KjListFocusMode>>('KJ_LIST_FOCUS_MODE');
|
|
78
|
-
|
|
79
|
-
export type KjListOrientation = 'vertical' | 'horizontal' | 'both';
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Selection modes supported by `KjSelectionModel`:
|
|
83
|
-
*
|
|
84
|
-
* - `'single'` — exactly one value (or `null`). Aria: `aria-selected="true"` on the chosen item.
|
|
85
|
-
* - `'multi'` — any combination from a flat list. Aria: `aria-selected="true|false"` per item, container `aria-multiselectable="true"`.
|
|
86
|
-
* - `'leaf'` — multi over a tree, but only leaf nodes go into `value`. Branch clicks are no-ops. Requires `setTreeShape()`. Aria: `aria-selected`.
|
|
87
|
-
* - `'cascade'` — tri-state tree selection. Toggling a branch selects/deselects all leaf descendants; the parent's state is computed from descendants (`true` / `false` / `mixed`). Requires `setTreeShape()`. Aria: `aria-checked="true|false|mixed"` per WAI-ARIA tree checkbox pattern.
|
|
88
|
-
*/
|
|
89
|
-
export type KjListSelectionMode = 'single' | 'multi' | 'leaf' | 'cascade';
|
|
90
|
-
|
|
91
|
-
export type KjFilterFn = (query: string, haystacks: readonly string[]) => number;
|
|
92
|
-
export type KjCompareFn<T> = (a: T, b: T) => boolean;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Tree topology contract used by `KjSelectionModel` in `'leaf'` and
|
|
96
|
-
* `'cascade'` modes. The consumer's root directive (typically
|
|
97
|
-
* `KjTreeSelect`) implements + provides this via `setTreeShape()`.
|
|
98
|
-
*/
|
|
99
|
-
export interface KjTreeShape<T> {
|
|
100
|
-
/** Parent of `node`, or `null` at the root. */
|
|
101
|
-
getParent(node: T): T | null;
|
|
102
|
-
/** Immediate children of `node`. Empty for leaves. */
|
|
103
|
-
getChildren(node: T): readonly T[];
|
|
104
|
-
/** `true` when `node` has no children. */
|
|
105
|
-
isLeaf(node: T): boolean;
|
|
106
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// packages/core/src/primitives/list/type-ahead.spec.ts
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
4
|
-
import { KjTypeAhead } from './type-ahead';
|
|
5
|
-
import type { KjListItem } from './item';
|
|
6
|
-
|
|
7
|
-
function item(id: string, label: string, disabled = false): KjListItem<unknown> {
|
|
8
|
-
return {
|
|
9
|
-
id,
|
|
10
|
-
label: () => label,
|
|
11
|
-
disabled: () => disabled,
|
|
12
|
-
} as unknown as KjListItem<unknown>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe('KjTypeAhead', () => {
|
|
16
|
-
let ta: KjTypeAhead;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
TestBed.configureTestingModule({ providers: [KjTypeAhead] });
|
|
19
|
-
ta = TestBed.inject(KjTypeAhead);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('returns the first item whose label starts with the typed char', () => {
|
|
23
|
-
const items = [item('1', 'Apple'), item('2', 'Banana')];
|
|
24
|
-
expect(ta.match('b', items)).toBe('2');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('buffers consecutive chars within the debounce window', () => {
|
|
28
|
-
const items = [item('1', 'Apple'), item('2', 'Apricot'), item('3', 'Avocado')];
|
|
29
|
-
expect(ta.match('a', items)).toBe('1');
|
|
30
|
-
expect(ta.match('p', items)).toBe('1');
|
|
31
|
-
expect(ta.match('r', items)).toBe('2');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('resets the buffer after the debounce window', async () => {
|
|
35
|
-
const items = [item('1', 'Apple'), item('2', 'Banana')];
|
|
36
|
-
ta.debounceMs.set(10);
|
|
37
|
-
expect(ta.match('a', items)).toBe('1');
|
|
38
|
-
await new Promise(r => setTimeout(r, 20));
|
|
39
|
-
expect(ta.match('b', items)).toBe('2');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('skips disabled items', () => {
|
|
43
|
-
const items = [item('1', 'Apple', true), item('2', 'Apricot')];
|
|
44
|
-
expect(ta.match('a', items)).toBe('2');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('returns null when nothing matches', () => {
|
|
48
|
-
const items = [item('1', 'Apple')];
|
|
49
|
-
expect(ta.match('z', items)).toBeNull();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('reset() clears the buffer', () => {
|
|
53
|
-
const items = [item('1', 'Apple'), item('2', 'Banana')];
|
|
54
|
-
ta.match('a', items);
|
|
55
|
-
ta.reset();
|
|
56
|
-
expect(ta.match('b', items)).toBe('2');
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// packages/core/src/primitives/list/type-ahead.ts
|
|
2
|
-
import { Injectable, signal } from '@angular/core';
|
|
3
|
-
import type { KjListItem } from './item';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Char-buffered type-ahead matcher used by `KjListNavigator` on
|
|
7
|
-
* single-character key presses. Buffers characters within a debounce
|
|
8
|
-
* window so users can type "ap" to jump to "Apricot" instead of just
|
|
9
|
-
* "Apple". Matches case-insensitive prefix against `KjListItem.label()`.
|
|
10
|
-
* Skips disabled items.
|
|
11
|
-
*
|
|
12
|
-
* @doc-category Core/Primitives
|
|
13
|
-
*/
|
|
14
|
-
@Injectable()
|
|
15
|
-
export class KjTypeAhead {
|
|
16
|
-
/** Debounce window (ms) that resets the buffer between key presses. */
|
|
17
|
-
readonly debounceMs = signal(500);
|
|
18
|
-
|
|
19
|
-
private _buffer = '';
|
|
20
|
-
private _lastKeyAt = 0;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Append `key` to the buffer (reset if outside debounce window), then
|
|
24
|
-
* return the id of the first non-disabled item whose label starts with
|
|
25
|
-
* the buffered prefix. Returns `null` if no item matches.
|
|
26
|
-
*/
|
|
27
|
-
match(key: string, items: readonly KjListItem<unknown>[]): string | null {
|
|
28
|
-
if (key.length !== 1) return null;
|
|
29
|
-
const now = performance.now();
|
|
30
|
-
if (now - this._lastKeyAt > this.debounceMs()) this._buffer = '';
|
|
31
|
-
this._buffer += key.toLowerCase();
|
|
32
|
-
this._lastKeyAt = now;
|
|
33
|
-
|
|
34
|
-
const needle = this._buffer;
|
|
35
|
-
const hit = items.find(
|
|
36
|
-
i => !i.disabled() && i.label().toLowerCase().startsWith(needle),
|
|
37
|
-
);
|
|
38
|
-
return hit?.id ?? null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Clear the buffer and last-key timestamp. */
|
|
42
|
-
reset(): void {
|
|
43
|
-
this._buffer = '';
|
|
44
|
-
this._lastKeyAt = 0;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Component, inject } from '@angular/core';
|
|
2
|
-
import { render } from '@testing-library/angular';
|
|
3
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
-
import { KjBackdrop } from './backdrop';
|
|
5
|
-
import { KjOverlayController } from './controller';
|
|
6
|
-
import { KJ_OVERLAY_BACKDROP_STRATEGY } from './tokens';
|
|
7
|
-
|
|
8
|
-
describe('KjBackdrop', () => {
|
|
9
|
-
it('clicking host invokes controller.close when closeOnClick=true', async () => {
|
|
10
|
-
@Component({
|
|
11
|
-
standalone: true,
|
|
12
|
-
imports: [KjBackdrop],
|
|
13
|
-
providers: [
|
|
14
|
-
KjOverlayController,
|
|
15
|
-
{ provide: KJ_OVERLAY_BACKDROP_STRATEGY, useValue: {
|
|
16
|
-
inertSiblings: true, closeOnClick: true, className: 'kj-backdrop',
|
|
17
|
-
attach() {}, onOpen() {}, onClose() {}, detach() {},
|
|
18
|
-
}},
|
|
19
|
-
],
|
|
20
|
-
template: '<kj-backdrop></kj-backdrop>',
|
|
21
|
-
})
|
|
22
|
-
class Host {
|
|
23
|
-
readonly ctrl = inject(KjOverlayController);
|
|
24
|
-
}
|
|
25
|
-
const { fixture, container } = await render(Host);
|
|
26
|
-
const closeSpy = vi.spyOn(fixture.componentInstance.ctrl, 'close');
|
|
27
|
-
const el = container.querySelector('kj-backdrop') as HTMLElement;
|
|
28
|
-
el.click();
|
|
29
|
-
expect(closeSpy).toHaveBeenCalledWith('outside');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('does not close when closeOnClick=false', async () => {
|
|
33
|
-
@Component({
|
|
34
|
-
standalone: true,
|
|
35
|
-
imports: [KjBackdrop],
|
|
36
|
-
providers: [
|
|
37
|
-
KjOverlayController,
|
|
38
|
-
{ provide: KJ_OVERLAY_BACKDROP_STRATEGY, useValue: {
|
|
39
|
-
inertSiblings: false, closeOnClick: false, className: 'kj-backdrop',
|
|
40
|
-
attach() {}, onOpen() {}, onClose() {}, detach() {},
|
|
41
|
-
}},
|
|
42
|
-
],
|
|
43
|
-
template: '<kj-backdrop></kj-backdrop>',
|
|
44
|
-
})
|
|
45
|
-
class Host {
|
|
46
|
-
readonly ctrl = inject(KjOverlayController);
|
|
47
|
-
}
|
|
48
|
-
const { fixture, container } = await render(Host);
|
|
49
|
-
const closeSpy = vi.spyOn(fixture.componentInstance.ctrl, 'close');
|
|
50
|
-
const el = container.querySelector('kj-backdrop') as HTMLElement;
|
|
51
|
-
el.click();
|
|
52
|
-
expect(closeSpy).not.toHaveBeenCalled();
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Component, ChangeDetectionStrategy, computed, inject } from '@angular/core';
|
|
2
|
-
import { KjOverlayController } from './controller';
|
|
3
|
-
import { KJ_OVERLAY_BACKDROP_STRATEGY } from './tokens';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Renders the backdrop scrim for an overlay. Reads the backdrop strategy
|
|
7
|
-
* (className, closeOnClick) and the controller from the per-overlay
|
|
8
|
-
* injector so neither needs to be passed in. Mounted by `KjOverlayBuilder`
|
|
9
|
-
* as a sibling preceding the panel inside the per-overlay wrapper.
|
|
10
|
-
*/
|
|
11
|
-
@Component({
|
|
12
|
-
selector: 'kj-backdrop',
|
|
13
|
-
standalone: true,
|
|
14
|
-
template: '',
|
|
15
|
-
host: {
|
|
16
|
-
'[class]': 'klass()',
|
|
17
|
-
'[attr.data-state]': 'state()',
|
|
18
|
-
'(click)': 'onClick($event)',
|
|
19
|
-
},
|
|
20
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
21
|
-
})
|
|
22
|
-
export class KjBackdrop {
|
|
23
|
-
private readonly controller = inject(KjOverlayController, { optional: true });
|
|
24
|
-
private readonly strategy = inject(KJ_OVERLAY_BACKDROP_STRATEGY)!;
|
|
25
|
-
readonly state = computed(() => this.controller?.state() ?? 'closed');
|
|
26
|
-
readonly klass = computed(() => (this.strategy as { className?: string }).className ?? 'kj-backdrop');
|
|
27
|
-
|
|
28
|
-
onClick(_e: MouseEvent): void {
|
|
29
|
-
if (this.strategy.closeOnClick) this.controller?.close('outside');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Component, inject } from '@angular/core';
|
|
2
|
-
import { TestBed } from '@angular/core/testing';
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
import { KjOverlayBuilder } from './builder';
|
|
5
|
-
import type { KjOverlayStrategies } from './controller';
|
|
6
|
-
import { bodyPortal } from './strategies/mount/body-portal';
|
|
7
|
-
import { viewportCentered } from './strategies/position/viewport-centered';
|
|
8
|
-
import { programmatic } from './strategies/trigger-event/programmatic';
|
|
9
|
-
|
|
10
|
-
function makeStrategies(): KjOverlayStrategies {
|
|
11
|
-
return {
|
|
12
|
-
mount: bodyPortal(),
|
|
13
|
-
position: viewportCentered(),
|
|
14
|
-
trigger: programmatic(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe('KjOverlayBuilder', () => {
|
|
19
|
-
it('create returns a controller with strategies attached', () => {
|
|
20
|
-
const builder = TestBed.inject(KjOverlayBuilder);
|
|
21
|
-
const ctrl = builder.create({ ...makeStrategies(), panelRole: 'dialog' });
|
|
22
|
-
expect(ctrl.state()).toBe('closed');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('attachComponent injects custom providers', () => {
|
|
26
|
-
@Component({ standalone: true, template: '' })
|
|
27
|
-
class Cmp {
|
|
28
|
-
readonly value = inject('TOKEN' as never);
|
|
29
|
-
}
|
|
30
|
-
const builder = TestBed.inject(KjOverlayBuilder);
|
|
31
|
-
const ctrl = builder.create({ ...makeStrategies(), panelRole: 'dialog' });
|
|
32
|
-
const ref = builder.attachComponent(ctrl, Cmp, {
|
|
33
|
-
providers: [{ provide: 'TOKEN' as never, useValue: 'hello' }],
|
|
34
|
-
});
|
|
35
|
-
expect((ref.instance as Cmp).value).toBe('hello');
|
|
36
|
-
ref.destroy();
|
|
37
|
-
});
|
|
38
|
-
});
|