@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,639 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Directive,
|
|
3
|
-
ElementRef,
|
|
4
|
-
EventEmitter,
|
|
5
|
-
Output,
|
|
6
|
-
Signal,
|
|
7
|
-
afterNextRender,
|
|
8
|
-
booleanAttribute,
|
|
9
|
-
computed,
|
|
10
|
-
inject,
|
|
11
|
-
input,
|
|
12
|
-
signal,
|
|
13
|
-
} from '@angular/core';
|
|
14
|
-
import {
|
|
15
|
-
KJ_FILE_UPLOAD,
|
|
16
|
-
KJ_FILE_UPLOAD_ITEM,
|
|
17
|
-
KjFileUploadAggregateStatus,
|
|
18
|
-
KjFileUploadContext,
|
|
19
|
-
KjFileUploadItemContext,
|
|
20
|
-
} from './file-upload.context';
|
|
21
|
-
import {
|
|
22
|
-
KJ_FILE_UPLOAD_DEFAULT_MESSAGES,
|
|
23
|
-
KjFileRejection,
|
|
24
|
-
KjFileStatus,
|
|
25
|
-
KjFileUploadValidationMessages,
|
|
26
|
-
KjUploadableFile,
|
|
27
|
-
} from './file-upload.types';
|
|
28
|
-
|
|
29
|
-
let kjFileUploadIdCounter = 0;
|
|
30
|
-
function nextFileId(): string {
|
|
31
|
-
const cryptoLike = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;
|
|
32
|
-
if (cryptoLike?.randomUUID) return `kjf-${cryptoLike.randomUUID().slice(0, 8)}`;
|
|
33
|
-
return `kjf-${(++kjFileUploadIdCounter).toString(36)}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validates a single `File` against the configured `accept` / `maxSize`
|
|
38
|
-
* constraints. Returns the rejection reason, or `null` if the file passes.
|
|
39
|
-
*
|
|
40
|
-
* `accept` matching follows the WHATWG file picker rules: a comma-separated
|
|
41
|
-
* list of MIME types (`image/*`, `application/pdf`) or extensions
|
|
42
|
-
* (`.pdf`, `.heic`). When `file.type` is empty (browsers don't report a MIME
|
|
43
|
-
* type for some extensions, e.g. `.heic`), we fall back to extension
|
|
44
|
-
* matching against `file.name`.
|
|
45
|
-
*
|
|
46
|
-
* @internal
|
|
47
|
-
*/
|
|
48
|
-
export function kjFileMatchesAccept(file: File, accept: string | undefined): boolean {
|
|
49
|
-
if (!accept) return true;
|
|
50
|
-
const tokens = accept
|
|
51
|
-
.split(',')
|
|
52
|
-
.map((t) => t.trim().toLowerCase())
|
|
53
|
-
.filter(Boolean);
|
|
54
|
-
if (!tokens.length) return true;
|
|
55
|
-
const type = file.type.toLowerCase();
|
|
56
|
-
const name = file.name.toLowerCase();
|
|
57
|
-
for (const tok of tokens) {
|
|
58
|
-
if (tok.startsWith('.')) {
|
|
59
|
-
if (name.endsWith(tok)) return true;
|
|
60
|
-
} else if (tok.endsWith('/*')) {
|
|
61
|
-
const prefix = tok.slice(0, -1); // keep the trailing slash
|
|
62
|
-
if (type.startsWith(prefix)) return true;
|
|
63
|
-
} else if (type && tok === type) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Root file-upload directive. Owns the file list, drag state, and selection
|
|
72
|
-
* validation (`accept` / `maxSize` / `maxFiles`). Provides `KJ_FILE_UPLOAD`
|
|
73
|
-
* for child directives (trigger, drop-zone, list, item).
|
|
74
|
-
*
|
|
75
|
-
* The transport is the consumer's job — `KjFileUpload` ships the *state
|
|
76
|
-
* machine* and emits structured events. Examples call `setFileStatus` from
|
|
77
|
-
* a simulated upload to drive `KjProgressBar` per row.
|
|
78
|
-
*
|
|
79
|
-
* The directive owns a visually-hidden `aria-live="polite"` span (mounted
|
|
80
|
-
* as a child of the host on first render) that announces file additions,
|
|
81
|
-
* removals, completions, and errors to screen readers (WCAG 4.1.3). The
|
|
82
|
-
* announcement node is intentionally NOT the host element itself — clearing
|
|
83
|
-
* `host.textContent` to retrigger SR readout would also wipe out the
|
|
84
|
-
* projected drop-zone / list children, so we keep the live region as a
|
|
85
|
-
* dedicated sibling.
|
|
86
|
-
*
|
|
87
|
-
* @example
|
|
88
|
-
* ```html
|
|
89
|
-
* <div kjFileUpload kjAccept="image/*" [kjMaxSize]="5_000_000">
|
|
90
|
-
* <button kjFileUploadTrigger>Choose files</button>
|
|
91
|
-
* <div kjFileUploadDropzone>Drop files here</div>
|
|
92
|
-
* <ul kjFileUploadList>
|
|
93
|
-
* @for (f of upload.files(); track f.id) {
|
|
94
|
-
* <li kjFileUploadItem [kjFile]="f">{{ f.file.name }}</li>
|
|
95
|
-
* }
|
|
96
|
-
* </ul>
|
|
97
|
-
* </div>
|
|
98
|
-
* ```
|
|
99
|
-
* @doc-category Core/Data input
|
|
100
|
-
* @doc
|
|
101
|
-
* @doc-name file-upload
|
|
102
|
-
* @doc-description Manages file selection, drag state, and validation while you control the upload transport.
|
|
103
|
-
* @doc-is-main
|
|
104
|
-
*/
|
|
105
|
-
@Directive({
|
|
106
|
-
selector: '[kjFileUpload]',
|
|
107
|
-
standalone: true,
|
|
108
|
-
exportAs: 'kjFileUpload',
|
|
109
|
-
providers: [{ provide: KJ_FILE_UPLOAD, useExisting: KjFileUpload }],
|
|
110
|
-
host: {
|
|
111
|
-
'[attr.data-disabled]': 'kjDisabled() ? "" : null',
|
|
112
|
-
'[attr.data-drag-active]': 'dragActive() ? "" : null',
|
|
113
|
-
'[attr.data-status]': 'aggregateStatus()',
|
|
114
|
-
},
|
|
115
|
-
})
|
|
116
|
-
export class KjFileUpload implements KjFileUploadContext {
|
|
117
|
-
private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
118
|
-
private liveRegionEl: HTMLElement | null = null;
|
|
119
|
-
|
|
120
|
-
/** MIME / extension list for the file picker and drop validation. */
|
|
121
|
-
readonly kjAccept = input<string | undefined>(undefined);
|
|
122
|
-
|
|
123
|
-
/** Whether multiple files may be selected. Defaults to `true` (advanced flavour). */
|
|
124
|
-
readonly kjMultiple = input(true, { transform: booleanAttribute });
|
|
125
|
-
|
|
126
|
-
/** Maximum bytes per file. Files over the cap are rejected at selection. */
|
|
127
|
-
readonly kjMaxSize = input<number | undefined>(undefined);
|
|
128
|
-
|
|
129
|
-
/** Maximum total file count across the list. Excess files are rejected. */
|
|
130
|
-
readonly kjMaxFiles = input<number | undefined>(undefined);
|
|
131
|
-
|
|
132
|
-
/** Disables the upload — drop-zone is `aria-disabled`, the trigger is inert. */
|
|
133
|
-
readonly kjDisabled = input(false, { transform: booleanAttribute });
|
|
134
|
-
|
|
135
|
-
/** Per-rejection-reason override messages. */
|
|
136
|
-
readonly kjValidationMessages = input<Partial<KjFileUploadValidationMessages>>({});
|
|
137
|
-
|
|
138
|
-
/** Emits the survivors after validation each time the user adds files. */
|
|
139
|
-
@Output() readonly kjSelect = new EventEmitter<File[]>();
|
|
140
|
-
|
|
141
|
-
/** Emits the rejection list whenever validation filters files out. */
|
|
142
|
-
@Output() readonly kjReject = new EventEmitter<KjFileRejection[]>();
|
|
143
|
-
|
|
144
|
-
/** Emits the `KjUploadableFile` for each row the user removes. */
|
|
145
|
-
@Output() readonly kjRemove = new EventEmitter<KjUploadableFile>();
|
|
146
|
-
|
|
147
|
-
private readonly _files = signal<readonly KjUploadableFile[]>([]);
|
|
148
|
-
/** Current file list (in selection order). */
|
|
149
|
-
readonly files: Signal<readonly KjUploadableFile[]> = this._files.asReadonly();
|
|
150
|
-
|
|
151
|
-
private readonly _dragActive = signal(false);
|
|
152
|
-
/** True while at least one drag operation is currently over the drop-zone host. */
|
|
153
|
-
readonly dragActive: Signal<boolean> = this._dragActive.asReadonly();
|
|
154
|
-
|
|
155
|
-
/** Read-only mirror of the disabled flag for context consumers. */
|
|
156
|
-
readonly disabled: Signal<boolean> = this.kjDisabled;
|
|
157
|
-
readonly multiple: Signal<boolean> = this.kjMultiple;
|
|
158
|
-
readonly accept: Signal<string | undefined> = this.kjAccept;
|
|
159
|
-
readonly maxSize: Signal<number | undefined> = this.kjMaxSize;
|
|
160
|
-
readonly maxFiles: Signal<number | undefined> = this.kjMaxFiles;
|
|
161
|
-
|
|
162
|
-
/** Aggregate status across the file list. */
|
|
163
|
-
readonly aggregateStatus = computed<KjFileUploadAggregateStatus>(() => {
|
|
164
|
-
const list = this._files();
|
|
165
|
-
if (!list.length) return 'idle';
|
|
166
|
-
let hasUploading = false;
|
|
167
|
-
let hasPending = false;
|
|
168
|
-
let hasError = false;
|
|
169
|
-
let allDone = true;
|
|
170
|
-
for (const f of list) {
|
|
171
|
-
if (f.status === 'uploading') hasUploading = true;
|
|
172
|
-
if (f.status === 'pending' || f.status === 'preparing') hasPending = true;
|
|
173
|
-
if (f.status === 'error') hasError = true;
|
|
174
|
-
if (f.status !== 'done') allDone = false;
|
|
175
|
-
}
|
|
176
|
-
if (hasUploading) return 'uploading';
|
|
177
|
-
if (allDone) return 'complete';
|
|
178
|
-
if (hasError && !hasPending) return 'partial-error';
|
|
179
|
-
return 'pending';
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
/** @internal Drag-enter / drag-leave counter — flips `_dragActive` cleanly across nested children. */
|
|
183
|
-
private dragDepth = 0;
|
|
184
|
-
|
|
185
|
-
/** @internal Set by the trigger / drop-zone so `openPicker()` can click the right input. */
|
|
186
|
-
private pickerInput: HTMLInputElement | null = null;
|
|
187
|
-
|
|
188
|
-
/** @internal Trigger / drop-zone register their hidden file input here. */
|
|
189
|
-
registerPickerInput(el: HTMLInputElement): void {
|
|
190
|
-
this.pickerInput = el;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/** Opens the native file picker by clicking the registered hidden input. */
|
|
194
|
-
openPicker(): void {
|
|
195
|
-
if (this.kjDisabled()) return;
|
|
196
|
-
this.pickerInput?.click();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Adds candidate files (from drop, programmatic add, or the picker's
|
|
201
|
-
* `change` event). Validates each against `accept` / `maxSize`, then
|
|
202
|
-
* applies the `kjMaxFiles` cap to the survivors. Mints ids, appends to
|
|
203
|
-
* the list, fires `kjSelect` / `kjReject`, and announces.
|
|
204
|
-
*/
|
|
205
|
-
addFiles(incoming: readonly File[] | FileList): void {
|
|
206
|
-
if (this.kjDisabled()) return;
|
|
207
|
-
const candidates: File[] = Array.from(incoming);
|
|
208
|
-
if (!candidates.length) return;
|
|
209
|
-
|
|
210
|
-
const accept = this.kjAccept();
|
|
211
|
-
const maxSize = this.kjMaxSize();
|
|
212
|
-
const maxFiles = this.kjMaxFiles();
|
|
213
|
-
const messages = this.resolvedMessages();
|
|
214
|
-
|
|
215
|
-
const survivors: File[] = [];
|
|
216
|
-
const rejections: KjFileRejection[] = [];
|
|
217
|
-
|
|
218
|
-
for (const file of candidates) {
|
|
219
|
-
if (accept && !kjFileMatchesAccept(file, accept)) {
|
|
220
|
-
rejections.push({ file, reason: 'type' });
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (maxSize !== undefined && file.size > maxSize) {
|
|
224
|
-
rejections.push({ file, reason: 'size' });
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
survivors.push(file);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// In single mode, the most recent pick replaces any existing file. Cap
|
|
231
|
-
// the survivors to 1 (rejecting any overflow with reason 'count'), then
|
|
232
|
-
// clear the existing list so the survivor takes its place.
|
|
233
|
-
if (!this.kjMultiple()) {
|
|
234
|
-
if (survivors.length > 1) {
|
|
235
|
-
const overflow = survivors.splice(1);
|
|
236
|
-
for (const f of overflow) rejections.push({ file: f, reason: 'count' });
|
|
237
|
-
}
|
|
238
|
-
if (survivors.length) this._files.set([]);
|
|
239
|
-
} else if (maxFiles !== undefined) {
|
|
240
|
-
// Multi mode: the explicit `kjMaxFiles` cap applies to current + new.
|
|
241
|
-
const current = this._files().length;
|
|
242
|
-
if (current + survivors.length > maxFiles) {
|
|
243
|
-
const allowed = Math.max(0, maxFiles - current);
|
|
244
|
-
const overflow = survivors.splice(allowed);
|
|
245
|
-
for (const f of overflow) rejections.push({ file: f, reason: 'count' });
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (survivors.length) {
|
|
250
|
-
const next: KjUploadableFile[] = survivors.map((f) => ({
|
|
251
|
-
id: nextFileId(),
|
|
252
|
-
file: f,
|
|
253
|
-
status: 'pending' as const,
|
|
254
|
-
progress: null,
|
|
255
|
-
}));
|
|
256
|
-
this._files.update((list) => [...list, ...next]);
|
|
257
|
-
this.kjSelect.emit(survivors);
|
|
258
|
-
this.announce(
|
|
259
|
-
survivors.length === 1
|
|
260
|
-
? `${survivors[0].name} added`
|
|
261
|
-
: `${survivors.length} files added`,
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (rejections.length) {
|
|
266
|
-
this.kjReject.emit(rejections);
|
|
267
|
-
const reason = rejections[0].reason;
|
|
268
|
-
const msg = messages[reason];
|
|
269
|
-
this.announce(
|
|
270
|
-
rejections.length === 1
|
|
271
|
-
? `${rejections[0].file.name} rejected: ${msg}`
|
|
272
|
-
: `${rejections.length} files rejected: ${msg}`,
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/** Remove a file by id. Cancels in-flight uploads, emits `kjRemove`. */
|
|
278
|
-
remove(id: string): void {
|
|
279
|
-
const target = this._files().find((f) => f.id === id);
|
|
280
|
-
if (!target) return;
|
|
281
|
-
this._files.update((list) => list.filter((f) => f.id !== id));
|
|
282
|
-
this.kjRemove.emit(target);
|
|
283
|
-
this.announce(`${target.file.name} removed`);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/** Clear the entire file list. */
|
|
287
|
-
clear(): void {
|
|
288
|
-
if (!this._files().length) return;
|
|
289
|
-
this._files.set([]);
|
|
290
|
-
this.announce('File list cleared');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** Reset a row to `pending` so the consumer (or simulator) can re-run it. */
|
|
294
|
-
retry(id: string): void {
|
|
295
|
-
this.patchFile(id, { status: 'pending', progress: null, error: undefined });
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/** Cancel an in-flight upload — sets `status: 'cancelled'`, clears progress. */
|
|
299
|
-
cancel(id: string): void {
|
|
300
|
-
this.patchFile(id, { status: 'cancelled', progress: null });
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Update a row's status / progress / error. Examples drive this from a
|
|
305
|
-
* simulated upload; consumers wiring `HttpClient` call it from their
|
|
306
|
-
* progress / success / error subscriptions.
|
|
307
|
-
*/
|
|
308
|
-
setFileStatus(id: string, status: KjFileStatus, progress?: number | null, error?: string): void {
|
|
309
|
-
this.patchFile(id, {
|
|
310
|
-
status,
|
|
311
|
-
progress: progress === undefined ? null : progress,
|
|
312
|
-
error,
|
|
313
|
-
});
|
|
314
|
-
if (status === 'done') {
|
|
315
|
-
const f = this._files().find((x) => x.id === id);
|
|
316
|
-
if (f) this.announce(`${f.file.name} uploaded`);
|
|
317
|
-
} else if (status === 'error') {
|
|
318
|
-
const f = this._files().find((x) => x.id === id);
|
|
319
|
-
if (f) this.announce(`${f.file.name} failed: ${error ?? 'upload error'}`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// ─── Drag state plumbing (called by KjFileUploadDropzone) ───
|
|
324
|
-
|
|
325
|
-
/** @internal */
|
|
326
|
-
onDragEnter(event: Event): void {
|
|
327
|
-
if (this.kjDisabled()) return;
|
|
328
|
-
if (!this.eventCarriesFiles(event)) return;
|
|
329
|
-
event.preventDefault();
|
|
330
|
-
this.dragDepth++;
|
|
331
|
-
this._dragActive.set(true);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/** @internal */
|
|
335
|
-
onDragOver(event: Event): void {
|
|
336
|
-
if (this.kjDisabled()) return;
|
|
337
|
-
if (!this.eventCarriesFiles(event)) return;
|
|
338
|
-
event.preventDefault();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/** @internal */
|
|
342
|
-
onDragLeave(event: Event): void {
|
|
343
|
-
if (this.kjDisabled()) return;
|
|
344
|
-
event.preventDefault();
|
|
345
|
-
this.dragDepth = Math.max(0, this.dragDepth - 1);
|
|
346
|
-
if (this.dragDepth === 0) this._dragActive.set(false);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/** @internal */
|
|
350
|
-
onDrop(event: Event): void {
|
|
351
|
-
event.preventDefault();
|
|
352
|
-
this.dragDepth = 0;
|
|
353
|
-
this._dragActive.set(false);
|
|
354
|
-
if (this.kjDisabled()) return;
|
|
355
|
-
const files = (event as DragEvent).dataTransfer?.files;
|
|
356
|
-
if (!files || files.length === 0) return;
|
|
357
|
-
this.addFiles(files);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/** @internal */
|
|
361
|
-
onPickerChange(event: Event): void {
|
|
362
|
-
const target = event.target as HTMLInputElement;
|
|
363
|
-
if (target.files) this.addFiles(target.files);
|
|
364
|
-
// Reset so picking the same file twice fires another change event.
|
|
365
|
-
target.value = '';
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private eventCarriesFiles(event: Event): boolean {
|
|
369
|
-
const types = (event as DragEvent).dataTransfer?.types;
|
|
370
|
-
if (!types) return false;
|
|
371
|
-
for (let i = 0; i < types.length; i++) {
|
|
372
|
-
if (types[i] === 'Files') return true;
|
|
373
|
-
}
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
private patchFile(id: string, patch: Partial<KjUploadableFile>): void {
|
|
378
|
-
this._files.update((list) =>
|
|
379
|
-
list.map((f) => (f.id === id ? { ...f, ...patch } : f)),
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
private resolvedMessages(): KjFileUploadValidationMessages {
|
|
384
|
-
return { ...KJ_FILE_UPLOAD_DEFAULT_MESSAGES, ...this.kjValidationMessages() };
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private announce(message: string): void {
|
|
388
|
-
const el = this.liveRegionEl;
|
|
389
|
-
if (!el) return;
|
|
390
|
-
// Clear-then-set with a microtask gap so screen readers detect the change
|
|
391
|
-
// even when consecutive announcements share a prefix.
|
|
392
|
-
el.textContent = '';
|
|
393
|
-
setTimeout(() => {
|
|
394
|
-
if (this.liveRegionEl) this.liveRegionEl.textContent = message;
|
|
395
|
-
}, 50);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
constructor() {
|
|
399
|
-
afterNextRender(() => {
|
|
400
|
-
if (typeof document === 'undefined') return;
|
|
401
|
-
const span = document.createElement('span');
|
|
402
|
-
span.setAttribute('aria-live', 'polite');
|
|
403
|
-
span.setAttribute('aria-atomic', 'true');
|
|
404
|
-
span.style.cssText =
|
|
405
|
-
'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
|
|
406
|
-
this.hostEl.nativeElement.appendChild(span);
|
|
407
|
-
this.liveRegionEl = span;
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* The "browse" button — when clicked, opens the native file picker. Owns the
|
|
414
|
-
* hidden `<input type="file">` so consumers don't have to render one
|
|
415
|
-
* themselves. Mirrors the `KjButton` keyboard contract (Enter / Space click
|
|
416
|
-
* the host element).
|
|
417
|
-
*
|
|
418
|
-
* Use on a `<button>` (recommended, native keyboard semantics) or any
|
|
419
|
-
* focusable element. The directive does **not** force `role="button"` — wrap
|
|
420
|
-
* a real `<button>` and let the browser handle it.
|
|
421
|
-
*
|
|
422
|
-
* @example
|
|
423
|
-
* ```html
|
|
424
|
-
* <button kjFileUploadTrigger>Choose files</button>
|
|
425
|
-
* ```
|
|
426
|
-
* @doc-category Core/Data input
|
|
427
|
-
* @doc
|
|
428
|
-
* @doc-name file-upload
|
|
429
|
-
*/
|
|
430
|
-
@Directive({
|
|
431
|
-
selector: '[kjFileUploadTrigger]',
|
|
432
|
-
standalone: true,
|
|
433
|
-
host: {
|
|
434
|
-
'type': 'button',
|
|
435
|
-
'[attr.aria-disabled]': 'ctx.disabled() ? "true" : null',
|
|
436
|
-
'[attr.data-disabled]': 'ctx.disabled() ? "" : null',
|
|
437
|
-
'(click)': 'onClick($event)',
|
|
438
|
-
},
|
|
439
|
-
})
|
|
440
|
-
export class KjFileUploadTrigger {
|
|
441
|
-
/** @internal */
|
|
442
|
-
readonly ctx = inject(KJ_FILE_UPLOAD);
|
|
443
|
-
private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
|
|
444
|
-
private hidden!: HTMLInputElement;
|
|
445
|
-
|
|
446
|
-
constructor() {
|
|
447
|
-
// Mint and own a hidden `<input type="file">` parked in the trigger's
|
|
448
|
-
// host element. Visually hidden (not [hidden] attribute) so it stays in
|
|
449
|
-
// the a11y tree and programmatic .click() opens the picker reliably
|
|
450
|
-
// across browsers (analysis open-question #2).
|
|
451
|
-
if (typeof document !== 'undefined') {
|
|
452
|
-
this.hidden = document.createElement('input');
|
|
453
|
-
this.hidden.type = 'file';
|
|
454
|
-
this.hidden.tabIndex = -1;
|
|
455
|
-
this.hidden.setAttribute('aria-hidden', 'true');
|
|
456
|
-
this.hidden.style.cssText =
|
|
457
|
-
'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;';
|
|
458
|
-
this.hidden.addEventListener('change', (event) => this.ctx.onPickerChange(event));
|
|
459
|
-
this.syncAttrs();
|
|
460
|
-
// Mirror dynamic accept / multiple onto the hidden input.
|
|
461
|
-
// computed-via-effect would also work; for a deliberately small surface
|
|
462
|
-
// we re-sync on every click (cheap).
|
|
463
|
-
this.el.nativeElement.appendChild(this.hidden);
|
|
464
|
-
this.ctx.registerPickerInput(this.hidden);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/** @internal */
|
|
469
|
-
onClick(event: Event): void {
|
|
470
|
-
if (this.ctx.disabled()) {
|
|
471
|
-
event.preventDefault();
|
|
472
|
-
event.stopImmediatePropagation();
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
this.syncAttrs();
|
|
476
|
-
this.hidden?.click();
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
private syncAttrs(): void {
|
|
480
|
-
if (!this.hidden) return;
|
|
481
|
-
const accept = this.ctx.accept();
|
|
482
|
-
if (accept) this.hidden.setAttribute('accept', accept);
|
|
483
|
-
else this.hidden.removeAttribute('accept');
|
|
484
|
-
if (this.ctx.multiple()) this.hidden.setAttribute('multiple', '');
|
|
485
|
-
else this.hidden.removeAttribute('multiple');
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Drag-and-drop surface. `role="button"`, `tabindex="0"`, Enter/Space open
|
|
491
|
-
* the picker. Reflects `data-drag-active` while files are over the host so
|
|
492
|
-
* theme CSS can style the highlighted state.
|
|
493
|
-
*
|
|
494
|
-
* The drop-zone wires drag enter / over / leave / drop into the root's
|
|
495
|
-
* counter (so nested children don't churn `data-drag-active`). The host is
|
|
496
|
-
* a generic `<div>`-shaped element so consumers can project arbitrary
|
|
497
|
-
* content (thumbnail grid, instructional text, etc.) — analysis decision
|
|
498
|
-
* #3 in `file-upload.md`.
|
|
499
|
-
*
|
|
500
|
-
* @example
|
|
501
|
-
* ```html
|
|
502
|
-
* <div kjFileUploadDropzone>
|
|
503
|
-
* Drag files here, or click to browse
|
|
504
|
-
* </div>
|
|
505
|
-
* ```
|
|
506
|
-
* @doc-category Core/Data input
|
|
507
|
-
* @doc
|
|
508
|
-
* @doc-name file-upload
|
|
509
|
-
*/
|
|
510
|
-
@Directive({
|
|
511
|
-
selector: '[kjFileUploadDropzone]',
|
|
512
|
-
standalone: true,
|
|
513
|
-
host: {
|
|
514
|
-
'role': 'button',
|
|
515
|
-
'tabindex': '0',
|
|
516
|
-
'[attr.aria-label]': 'kjLabel()',
|
|
517
|
-
'[attr.aria-disabled]': 'ctx.disabled() ? "true" : null',
|
|
518
|
-
'[attr.data-drag-active]': 'ctx.dragActive() ? "" : null',
|
|
519
|
-
'[attr.data-disabled]': 'ctx.disabled() ? "" : null',
|
|
520
|
-
'(dragenter)': 'ctx.onDragEnter($event)',
|
|
521
|
-
'(dragover)': 'ctx.onDragOver($event)',
|
|
522
|
-
'(dragleave)': 'ctx.onDragLeave($event)',
|
|
523
|
-
'(drop)': 'ctx.onDrop($event)',
|
|
524
|
-
'(click)': 'onClick($event)',
|
|
525
|
-
'(keydown.enter)': 'onActivate($event)',
|
|
526
|
-
'(keydown.space)': 'onActivate($event)',
|
|
527
|
-
},
|
|
528
|
-
})
|
|
529
|
-
export class KjFileUploadDropzone {
|
|
530
|
-
/** @internal */
|
|
531
|
-
readonly ctx = inject(KJ_FILE_UPLOAD);
|
|
532
|
-
|
|
533
|
-
/** Accessible name for the drop-zone. */
|
|
534
|
-
readonly kjLabel = input<string>('Drag files here, or click to browse');
|
|
535
|
-
|
|
536
|
-
/** @internal */
|
|
537
|
-
onClick(event: Event): void {
|
|
538
|
-
if (this.ctx.disabled()) {
|
|
539
|
-
event.preventDefault();
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
this.ctx.openPicker();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/** @internal */
|
|
546
|
-
onActivate(event: Event): void {
|
|
547
|
-
event.preventDefault();
|
|
548
|
-
if (this.ctx.disabled()) return;
|
|
549
|
-
this.ctx.openPicker();
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* File list container. Sets `role="list"` (idempotent on a `<ul>`, required
|
|
555
|
-
* on a `<div>`) so the per-row `role="listitem"` semantics chain correctly
|
|
556
|
-
* for screen readers (WCAG 1.3.1).
|
|
557
|
-
*
|
|
558
|
-
* The directive is structural-light — the consumer iterates `ctx.files()`
|
|
559
|
-
* with `@for` and renders rows via `[kjFileUploadItem]` — so list-shape
|
|
560
|
-
* (table / definition list / virtualised) stays the consumer's call.
|
|
561
|
-
*
|
|
562
|
-
* @example
|
|
563
|
-
* ```html
|
|
564
|
-
* <ul kjFileUploadList>
|
|
565
|
-
* @for (f of upload.files(); track f.id) {
|
|
566
|
-
* <li kjFileUploadItem [kjFile]="f">{{ f.file.name }}</li>
|
|
567
|
-
* }
|
|
568
|
-
* </ul>
|
|
569
|
-
* ```
|
|
570
|
-
* @doc-category Core/Data input
|
|
571
|
-
* @doc
|
|
572
|
-
* @doc-name file-upload
|
|
573
|
-
*/
|
|
574
|
-
@Directive({
|
|
575
|
-
selector: '[kjFileUploadList]',
|
|
576
|
-
standalone: true,
|
|
577
|
-
host: {
|
|
578
|
-
'role': 'list',
|
|
579
|
-
},
|
|
580
|
-
})
|
|
581
|
-
export class KjFileUploadList {
|
|
582
|
-
/** @internal */
|
|
583
|
-
readonly ctx = inject(KJ_FILE_UPLOAD);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Per-file row directive. Provides `KJ_FILE_UPLOAD_ITEM` so per-row buttons
|
|
588
|
-
* (retry / cancel / remove) and a per-row `KjProgressBar` can read
|
|
589
|
-
* `file()` / `progress()` / `status()` from context.
|
|
590
|
-
*
|
|
591
|
-
* Reflects `data-status` so theme CSS can flip the row's tint (red border
|
|
592
|
-
* for `error`, dimmed for `done`, etc.).
|
|
593
|
-
*
|
|
594
|
-
* @example
|
|
595
|
-
* ```html
|
|
596
|
-
* <li kjFileUploadItem [kjFile]="f">
|
|
597
|
-
* <span>{{ f.file.name }}</span>
|
|
598
|
-
* <button (click)="item.remove()">Remove</button>
|
|
599
|
-
* </li>
|
|
600
|
-
* ```
|
|
601
|
-
* @doc-category Core/Data input
|
|
602
|
-
* @doc
|
|
603
|
-
* @doc-name file-upload
|
|
604
|
-
*/
|
|
605
|
-
@Directive({
|
|
606
|
-
selector: '[kjFileUploadItem]',
|
|
607
|
-
standalone: true,
|
|
608
|
-
exportAs: 'kjFileUploadItem',
|
|
609
|
-
providers: [{ provide: KJ_FILE_UPLOAD_ITEM, useExisting: KjFileUploadItem }],
|
|
610
|
-
host: {
|
|
611
|
-
'role': 'listitem',
|
|
612
|
-
'[attr.data-status]': 'status()',
|
|
613
|
-
},
|
|
614
|
-
})
|
|
615
|
-
export class KjFileUploadItem implements KjFileUploadItemContext {
|
|
616
|
-
/** @internal */
|
|
617
|
-
readonly ctx = inject(KJ_FILE_UPLOAD);
|
|
618
|
-
|
|
619
|
-
/** The `KjUploadableFile` for this row. Required. */
|
|
620
|
-
readonly kjFile = input.required<KjUploadableFile>();
|
|
621
|
-
|
|
622
|
-
/** Read-only mirror for context consumers. */
|
|
623
|
-
readonly file: Signal<KjUploadableFile> = this.kjFile;
|
|
624
|
-
|
|
625
|
-
readonly status: Signal<KjFileStatus> = computed(() => this.kjFile().status);
|
|
626
|
-
readonly progress: Signal<number | null> = computed(() => this.kjFile().progress);
|
|
627
|
-
|
|
628
|
-
retry(): void {
|
|
629
|
-
this.ctx.retry(this.kjFile().id);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
cancel(): void {
|
|
633
|
-
this.ctx.cancel(this.kjFile().id);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
remove(): void {
|
|
637
|
-
this.ctx.remove(this.kjFile().id);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-file lifecycle status. Set by the directive as files move through the
|
|
3
|
-
* state machine; read by templates and per-row controls (retry / cancel /
|
|
4
|
-
* remove gate on this).
|
|
5
|
-
*/
|
|
6
|
-
export type KjFileStatus =
|
|
7
|
-
| 'preparing'
|
|
8
|
-
| 'pending'
|
|
9
|
-
| 'uploading'
|
|
10
|
-
| 'done'
|
|
11
|
-
| 'error'
|
|
12
|
-
| 'cancelled';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The kouji envelope around a native `File`. `KjFileUpload` mints `id` (a
|
|
16
|
-
* stable string) on selection so consumers can `track` per-row by identity.
|
|
17
|
-
*
|
|
18
|
-
* `progress` is `null` when the file hasn't started yet *or* the upload is
|
|
19
|
-
* indeterminate; once the consumer reports `progress` events the directive
|
|
20
|
-
* fills in `0..100`.
|
|
21
|
-
*/
|
|
22
|
-
export interface KjUploadableFile {
|
|
23
|
-
/** Stable id, minted by `KjFileUpload`. Survives retry. */
|
|
24
|
-
readonly id: string;
|
|
25
|
-
/** The native `File`. */
|
|
26
|
-
readonly file: File;
|
|
27
|
-
/** Current per-file status. */
|
|
28
|
-
readonly status: KjFileStatus;
|
|
29
|
-
/** `0..100` once the upload reports progress; `null` otherwise. */
|
|
30
|
-
readonly progress: number | null;
|
|
31
|
-
/** Human-readable error message (set when `status === 'error'`). */
|
|
32
|
-
readonly error?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Reason a candidate file was rejected at selection time. Drives both the
|
|
37
|
-
* built-in validation messages and the structured `kjReject` output the
|
|
38
|
-
* consumer can map to a hint / toast.
|
|
39
|
-
*/
|
|
40
|
-
export type KjFileRejectReason = 'size' | 'type' | 'count';
|
|
41
|
-
|
|
42
|
-
/** A file that was filtered out at selection. */
|
|
43
|
-
export interface KjFileRejection {
|
|
44
|
-
readonly file: File;
|
|
45
|
-
readonly reason: KjFileRejectReason;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Built-in validation messages (English, locale-neutral). Override per-instance via `kjValidationMessages`. */
|
|
49
|
-
export interface KjFileUploadValidationMessages {
|
|
50
|
-
size: string;
|
|
51
|
-
type: string;
|
|
52
|
-
count: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const KJ_FILE_UPLOAD_DEFAULT_MESSAGES: KjFileUploadValidationMessages = {
|
|
56
|
-
size: 'File too large',
|
|
57
|
-
type: 'File type not allowed',
|
|
58
|
-
count: 'Too many files',
|
|
59
|
-
};
|