@konce-pt/react 0.11.2 → 0.14.0
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/README.md +128 -64
- package/dist/accordion/accordion.d.ts +14 -6
- package/dist/accordion/accordion.js +25 -17
- package/dist/accordion/llms.txt +22 -10
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +3 -2
- package/dist/alert/llms.txt +10 -9
- package/dist/app-shell/app-shell.d.ts +14 -14
- package/dist/app-shell/app-shell.js +22 -15
- package/dist/app-shell/llms.txt +16 -15
- package/dist/auth/auth-types.d.ts +6 -6
- package/dist/auth/auth.d.ts +2 -2
- package/dist/auth/auth.js +24 -15
- package/dist/auth/llms.txt +12 -11
- package/dist/autocomplete/autocomplete.d.ts +19 -6
- package/dist/autocomplete/autocomplete.js +32 -11
- package/dist/autocomplete/llms.txt +19 -5
- package/dist/avatar/avatar.d.ts +6 -6
- package/dist/avatar/avatar.js +6 -5
- package/dist/avatar/llms.txt +10 -10
- package/dist/avatar-group/avatar-group.d.ts +4 -4
- package/dist/avatar-group/avatar-group.js +4 -3
- package/dist/avatar-group/llms.txt +8 -8
- package/dist/backdrop/backdrop.d.ts +7 -26
- package/dist/backdrop/backdrop.js +32 -27
- package/dist/backdrop/llms.txt +27 -27
- package/dist/badge/badge.d.ts +3 -3
- package/dist/badge/badge.js +1 -1
- package/dist/badge/llms.txt +11 -11
- package/dist/bottom-sheet/bottom-sheet.d.ts +8 -3
- package/dist/bottom-sheet/bottom-sheet.js +5 -4
- package/dist/bottom-sheet/llms.txt +11 -8
- package/dist/breadcrumb/breadcrumb.d.ts +8 -2
- package/dist/breadcrumb/breadcrumb.js +11 -3
- package/dist/breadcrumb/llms.txt +10 -2
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +10 -9
- package/dist/button/llms.txt +27 -19
- package/dist/button-group/button-group.d.ts +18 -5
- package/dist/button-group/button-group.js +54 -6
- package/dist/button-group/llms.txt +27 -18
- package/dist/card/card.d.ts +8 -8
- package/dist/card/card.js +10 -9
- package/dist/card/llms.txt +20 -20
- package/dist/carousel/carousel.d.ts +6 -6
- package/dist/carousel/carousel.js +8 -5
- package/dist/carousel/llms.txt +10 -10
- package/dist/charts/chart-legend.d.ts +3 -3
- package/dist/charts/chart-legend.js +2 -1
- package/dist/charts/chart-tooltip.d.ts +3 -3
- package/dist/charts/chart-tooltip.js +1 -1
- package/dist/charts/chart.d.ts +4 -3
- package/dist/charts/chart.js +18 -10
- package/dist/charts/llms.txt +10 -7
- package/dist/charts-entry/public-api.js +2 -2
- package/dist/chat/chat.d.ts +17 -10
- package/dist/chat/chat.js +77 -23
- package/dist/chat/llms.txt +39 -15
- package/dist/chat-composer/chat-composer.d.ts +20 -7
- package/dist/chat-composer/chat-composer.js +29 -7
- package/dist/chat-composer/llms.txt +20 -11
- package/dist/chat-dock/chat-dock.d.ts +11 -4
- package/dist/chat-dock/chat-dock.js +10 -6
- package/dist/chat-dock/llms.txt +15 -14
- package/dist/chat-entry/public-api.js +1 -1
- package/dist/chat-message/chat-message.d.ts +12 -5
- package/dist/chat-message/chat-message.js +19 -8
- package/dist/chat-message/llms.txt +16 -15
- package/dist/chat-typing/chat-typing.d.ts +2 -2
- package/dist/chat-typing/chat-typing.js +4 -3
- package/dist/chat-typing/llms.txt +6 -6
- package/dist/checkbox/checkbox.d.ts +13 -6
- package/dist/checkbox/checkbox.js +31 -5
- package/dist/checkbox/llms.txt +26 -11
- package/dist/chip/chip.d.ts +4 -4
- package/dist/chip/chip.js +3 -2
- package/dist/chip/llms.txt +11 -11
- package/dist/chips-input/chips-input.d.ts +6 -4
- package/dist/chips-input/chips-input.js +47 -12
- package/dist/chips-input/llms.txt +18 -8
- package/dist/clock/clock.d.ts +11 -6
- package/dist/clock/clock.js +38 -8
- package/dist/clock/llms.txt +12 -8
- package/dist/color-picker/color-picker.d.ts +9 -5
- package/dist/color-picker/color-picker.js +37 -18
- package/dist/color-picker/llms.txt +8 -5
- package/dist/confirm/confirm-container.d.ts +3 -3
- package/dist/confirm/confirm-container.js +6 -5
- package/dist/confirm/confirm-store.d.ts +4 -4
- package/dist/confirm/confirm-store.js +11 -5
- package/dist/confirm/llms.txt +8 -7
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/context-menu.js +37 -14
- package/dist/context-menu/llms.txt +10 -7
- package/dist/data-table/data-table-types.d.ts +5 -5
- package/dist/data-table/data-table.d.ts +37 -5
- package/dist/data-table/data-table.js +426 -119
- package/dist/data-table/export-csv.d.ts +3 -3
- package/dist/data-table/export-csv.js +8 -10
- package/dist/data-table/llms.txt +127 -79
- package/dist/data-view/data-view.d.ts +13 -5
- package/dist/data-view/data-view.js +68 -6
- package/dist/data-view/llms.txt +29 -10
- package/dist/date-range/date-range.d.ts +20 -8
- package/dist/date-range/date-range.js +86 -63
- package/dist/date-range/llms.txt +32 -18
- package/dist/datepicker/datepicker.d.ts +22 -8
- package/dist/datepicker/datepicker.js +103 -71
- package/dist/datepicker/llms.txt +36 -22
- package/dist/dialog/dialog.d.ts +4 -4
- package/dist/dialog/dialog.js +32 -11
- package/dist/dialog/llms.txt +13 -10
- package/dist/divider/divider.d.ts +7 -1
- package/dist/divider/divider.js +3 -2
- package/dist/divider/llms.txt +4 -3
- package/dist/drawer/drawer.d.ts +9 -4
- package/dist/drawer/drawer.js +5 -4
- package/dist/drawer/llms.txt +13 -10
- package/dist/empty/empty.d.ts +2 -2
- package/dist/empty/empty.js +1 -1
- package/dist/empty/llms.txt +9 -9
- package/dist/fab/fab.d.ts +1 -1
- package/dist/fab/fab.js +1 -1
- package/dist/fab/llms.txt +10 -6
- package/dist/fieldset/fieldset.d.ts +18 -4
- package/dist/fieldset/fieldset.js +12 -6
- package/dist/fieldset/llms.txt +26 -9
- package/dist/file-upload/file-upload.d.ts +8 -2
- package/dist/file-upload/file-upload.js +22 -11
- package/dist/file-upload/llms.txt +12 -9
- package/dist/form-field/form-field-context.d.ts +4 -4
- package/dist/form-field/form-field.d.ts +12 -4
- package/dist/form-field/form-field.js +27 -6
- package/dist/form-field/llms.txt +25 -11
- package/dist/galleria/galleria.d.ts +3 -3
- package/dist/galleria/galleria.js +9 -6
- package/dist/galleria/llms.txt +7 -7
- package/dist/grid/col.d.ts +4 -4
- package/dist/grid/col.js +2 -2
- package/dist/grid/flex.d.ts +3 -3
- package/dist/grid/flex.js +1 -1
- package/dist/grid/grid.d.ts +7 -7
- package/dist/grid/grid.js +3 -3
- package/dist/grid/layout.d.ts +2 -2
- package/dist/grid/layout.js +2 -2
- package/dist/grid/llms.txt +15 -15
- package/dist/grid-entry/public-api.js +1 -1
- package/dist/i18n/i18n-context.d.ts +6 -6
- package/dist/i18n/i18n-context.js +27 -5
- package/dist/icon/icon-registry.d.ts +3 -3
- package/dist/icon/icon-registry.js +3 -3
- package/dist/icon/icon.d.ts +4 -4
- package/dist/icon/icon.js +10 -7
- package/dist/icon/llms.txt +13 -13
- package/dist/icon-button/icon-button.d.ts +2 -2
- package/dist/icon-button/icon-button.js +1 -1
- package/dist/icon-button/llms.txt +9 -5
- package/dist/icons-entry/public-api.d.ts +2 -2
- package/dist/icons-entry/public-api.js +3 -3
- package/dist/image/image.js +5 -5
- package/dist/image/llms.txt +13 -13
- package/dist/input/input.d.ts +18 -5
- package/dist/input/input.js +22 -6
- package/dist/input/llms.txt +25 -11
- package/dist/input-mask/input-mask.d.ts +7 -2
- package/dist/input-mask/input-mask.js +17 -43
- package/dist/input-mask/llms.txt +8 -5
- package/dist/input-number/input-number.d.ts +11 -9
- package/dist/input-number/input-number.js +25 -21
- package/dist/input-number/llms.txt +17 -14
- package/dist/input-otp/input-otp.d.ts +18 -5
- package/dist/input-otp/input-otp.js +48 -24
- package/dist/input-otp/llms.txt +18 -7
- package/dist/internal/calendar-focus.d.ts +1 -1
- package/dist/internal/calendar-focus.js +3 -3
- package/dist/internal/cn.d.ts +1 -1
- package/dist/internal/mask.d.ts +14 -0
- package/dist/internal/mask.js +62 -0
- package/dist/internal/number-group.d.ts +22 -0
- package/dist/internal/number-group.js +53 -0
- package/dist/internal/option-key.d.ts +6 -0
- package/dist/internal/option-key.js +8 -0
- package/dist/internal/option-nav.d.ts +4 -4
- package/dist/internal/option-nav.js +5 -5
- package/dist/internal/overlay-container.js +1 -1
- package/dist/internal/overlay.d.ts +3 -3
- package/dist/internal/overlay.js +40 -10
- package/dist/internal/password-strength.d.ts +6 -0
- package/dist/internal/password-strength.js +74 -0
- package/dist/internal/roving.d.ts +2 -2
- package/dist/internal/roving.js +3 -3
- package/dist/internal/scroll-lock.d.ts +6 -0
- package/dist/internal/scroll-lock.js +35 -0
- package/dist/internal/slot-children.d.ts +13 -0
- package/dist/internal/slot-children.js +31 -0
- package/dist/internal/slot.d.ts +3 -3
- package/dist/internal/slot.js +2 -2
- package/dist/internal/step.d.ts +12 -0
- package/dist/internal/step.js +31 -0
- package/dist/internal/tween.d.ts +5 -5
- package/dist/internal/tween.js +7 -8
- package/dist/internal/types.d.ts +2 -2
- package/dist/internal/use-breakpoint.d.ts +2 -2
- package/dist/internal/use-breakpoint.js +4 -4
- package/dist/internal/use-controllable-state.d.ts +5 -0
- package/dist/internal/use-controllable-state.js +25 -4
- package/dist/internal/use-id.d.ts +2 -2
- package/dist/internal/use-id.js +2 -2
- package/dist/internal/use-modal-focus.d.ts +2 -1
- package/dist/internal/use-modal-focus.js +14 -3
- package/dist/knob/knob.d.ts +7 -4
- package/dist/knob/knob.js +27 -22
- package/dist/knob/llms.txt +18 -11
- package/dist/listbox/listbox.d.ts +8 -6
- package/dist/listbox/listbox.js +37 -17
- package/dist/listbox/llms.txt +12 -9
- package/dist/loader/llms.txt +56 -0
- package/dist/loader/loader.d.ts +49 -0
- package/dist/loader/loader.js +68 -0
- package/dist/map/llms.txt +30 -23
- package/dist/map/map-create-dialog.d.ts +4 -4
- package/dist/map/map-create-dialog.js +36 -28
- package/dist/map/map-hours-field.d.ts +4 -4
- package/dist/map/map-hours-field.js +8 -7
- package/dist/map/map-image-field.d.ts +2 -2
- package/dist/map/map-image-field.js +6 -5
- package/dist/map/map-legend.d.ts +4 -4
- package/dist/map/map-legend.js +3 -2
- package/dist/map/map-poi-card.d.ts +3 -3
- package/dist/map/map-poi-card.js +10 -9
- package/dist/map/map-search.d.ts +17 -6
- package/dist/map/map-search.js +70 -43
- package/dist/map/map.d.ts +25 -13
- package/dist/map/map.js +86 -57
- package/dist/map/ports.d.ts +9 -9
- package/dist/map/ports.js +6 -6
- package/dist/map-entry/public-api.js +2 -2
- package/dist/megamenu/llms.txt +18 -7
- package/dist/megamenu/megamenu.d.ts +12 -6
- package/dist/megamenu/megamenu.js +23 -14
- package/dist/mention/llms.txt +15 -15
- package/dist/mention/mention.d.ts +5 -5
- package/dist/mention/mention.js +2 -2
- package/dist/mention/mentions.d.ts +1 -1
- package/dist/mention/mentions.js +1 -1
- package/dist/menu/llms.txt +12 -12
- package/dist/menu/menu-item.d.ts +4 -4
- package/dist/menu/menu-keys.d.ts +2 -2
- package/dist/menu/menu-keys.js +5 -5
- package/dist/menu/menu-rows.js +1 -0
- package/dist/menu/menu.d.ts +2 -2
- package/dist/menu/menu.js +15 -8
- package/dist/menubar/llms.txt +19 -8
- package/dist/menubar/menubar.d.ts +11 -5
- package/dist/menubar/menubar.js +23 -11
- package/dist/meter-group/llms.txt +12 -12
- package/dist/meter-group/meter-group.d.ts +2 -2
- package/dist/meter-group/meter-group.js +2 -1
- package/dist/motion/motion-context.d.ts +22 -0
- package/dist/motion/motion-context.js +28 -0
- package/dist/order-list/llms.txt +24 -9
- package/dist/order-list/order-list.d.ts +12 -4
- package/dist/order-list/order-list.js +123 -25
- package/dist/paginator/llms.txt +8 -8
- package/dist/paginator/paginator.js +7 -4
- package/dist/panel/llms.txt +25 -13
- package/dist/panel/panel.d.ts +11 -3
- package/dist/panel/panel.js +12 -7
- package/dist/password/llms.txt +19 -7
- package/dist/password/password.d.ts +2 -2
- package/dist/password/password.js +27 -16
- package/dist/peek/llms.txt +11 -11
- package/dist/peek/peek.d.ts +6 -6
- package/dist/peek/peek.js +41 -19
- package/dist/pick-list/llms.txt +35 -12
- package/dist/pick-list/pick-list.d.ts +12 -4
- package/dist/pick-list/pick-list.js +128 -24
- package/dist/popover/llms.txt +13 -10
- package/dist/popover/popover.d.ts +6 -4
- package/dist/popover/popover.js +14 -7
- package/dist/progress/llms.txt +17 -17
- package/dist/progress/progress.d.ts +7 -7
- package/dist/progress/progress.js +7 -6
- package/dist/public-api.d.ts +16 -4
- package/dist/public-api.js +13 -7
- package/dist/radio-group/llms.txt +16 -6
- package/dist/radio-group/radio-group.d.ts +12 -3
- package/dist/radio-group/radio-group.js +23 -4
- package/dist/rating/llms.txt +20 -13
- package/dist/rating/rating-pointer.d.ts +16 -0
- package/dist/rating/rating-pointer.js +26 -0
- package/dist/rating/rating.d.ts +6 -6
- package/dist/rating/rating.js +77 -12
- package/dist/rich-text/lists.d.ts +2 -2
- package/dist/rich-text/lists.js +7 -7
- package/dist/rich-text/llms.txt +35 -12
- package/dist/rich-text/rich-text.d.ts +13 -14
- package/dist/rich-text/rich-text.js +311 -142
- package/dist/rich-text/sanitize.d.ts +11 -2
- package/dist/rich-text/sanitize.js +80 -32
- package/dist/rich-text/tables.d.ts +3 -3
- package/dist/rich-text/tables.js +3 -3
- package/dist/roadmap/llms.txt +23 -17
- package/dist/roadmap/roadmap.d.ts +6 -6
- package/dist/roadmap/roadmap.js +102 -25
- package/dist/roadmap-entry/public-api.js +2 -2
- package/dist/scroll-top/llms.txt +6 -6
- package/dist/scroll-top/scroll-top.d.ts +1 -1
- package/dist/scroll-top/scroll-top.js +2 -1
- package/dist/select/llms.txt +22 -12
- package/dist/select/select.d.ts +10 -8
- package/dist/select/select.js +87 -47
- package/dist/shared/types.d.ts +22 -1
- package/dist/shared/types.js +9 -1
- package/dist/sidenav/llms.txt +9 -9
- package/dist/sidenav/sidenav.d.ts +2 -3
- package/dist/sidenav/sidenav.js +5 -4
- package/dist/skeleton/llms.txt +3 -3
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +1 -1
- package/dist/slider/llms.txt +34 -12
- package/dist/slider/slider-drag.d.ts +26 -0
- package/dist/slider/slider-drag.js +79 -0
- package/dist/slider/slider.d.ts +8 -5
- package/dist/slider/slider.js +38 -8
- package/dist/speech/llms.txt +97 -0
- package/dist/speech/speech-button.d.ts +44 -0
- package/dist/speech/speech-button.js +86 -0
- package/dist/speech/speech-wave.d.ts +19 -0
- package/dist/speech/speech-wave.js +16 -0
- package/dist/speech/use-speech-recognition.d.ts +81 -0
- package/dist/speech/use-speech-recognition.js +179 -0
- package/dist/speed-dial/llms.txt +14 -8
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/speed-dial/speed-dial.js +3 -2
- package/dist/spinner/llms.txt +7 -7
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +2 -1
- package/dist/split-button/llms.txt +21 -15
- package/dist/split-button/split-button.d.ts +2 -2
- package/dist/split-button/split-button.js +13 -6
- package/dist/splitter/llms.txt +11 -11
- package/dist/splitter/splitter.d.ts +3 -3
- package/dist/splitter/splitter.js +25 -10
- package/dist/stepper/llms.txt +35 -13
- package/dist/stepper/stepper.d.ts +6 -6
- package/dist/stepper/stepper.js +60 -13
- package/dist/switch/llms.txt +15 -5
- package/dist/switch/switch.d.ts +10 -3
- package/dist/switch/switch.js +18 -4
- package/dist/switch-group/llms.txt +11 -8
- package/dist/switch-group/switch-group.d.ts +6 -4
- package/dist/switch-group/switch-group.js +9 -7
- package/dist/tabs/llms.txt +12 -9
- package/dist/tabs/tabs.d.ts +5 -5
- package/dist/tabs/tabs.js +36 -20
- package/dist/textarea/llms.txt +23 -12
- package/dist/textarea/textarea.d.ts +19 -6
- package/dist/textarea/textarea.js +22 -7
- package/dist/timeline/llms.txt +4 -4
- package/dist/timeline/timeline.d.ts +1 -1
- package/dist/timeline/timeline.js +1 -1
- package/dist/toast/llms.txt +50 -17
- package/dist/toast/toast-container.d.ts +9 -3
- package/dist/toast/toast-container.js +18 -9
- package/dist/toast/toast-store.d.ts +42 -2
- package/dist/toast/toast-store.js +36 -1
- package/dist/toolbar/llms.txt +7 -7
- package/dist/toolbar/toolbar.d.ts +3 -3
- package/dist/toolbar/toolbar.js +2 -2
- package/dist/tooltip/llms.txt +14 -12
- package/dist/tooltip/tooltip.d.ts +5 -4
- package/dist/tooltip/tooltip.js +100 -41
- package/dist/tree/llms.txt +25 -11
- package/dist/tree/tree-keys.d.ts +5 -5
- package/dist/tree/tree-keys.js +7 -7
- package/dist/tree/tree.d.ts +17 -5
- package/dist/tree/tree.js +62 -20
- package/dist/update/llms.txt +54 -0
- package/dist/update/update.d.ts +49 -0
- package/dist/update/update.js +82 -0
- package/package.json +18 -15
package/dist/scroll-top/llms.txt
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# KptScrollTop (@konce-pt/react)
|
|
2
2
|
|
|
3
|
-
A "back to top" button
|
|
3
|
+
A "back to top" button - appears once the scroll passes `threshold` px.
|
|
4
4
|
Import: `import { KptScrollTop } from '@konce-pt/react';`
|
|
5
5
|
|
|
6
6
|
## DOM
|
|
7
7
|
<div class="kpt-scroll-top-host">
|
|
8
|
-
<button class="kpt-scroll-top is-visible" tabindex="0" aria-label="Scroll to top"
|
|
8
|
+
<button class="kpt-scroll-top is-visible" tabindex="0" aria-label="Scroll to top">...</button>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
11
|
## Props
|
|
12
12
|
- `threshold`: number (px, default 300)
|
|
13
|
-
- `target`: string | null
|
|
13
|
+
- `target`: string | null - a selector for the scrolled element; the window by default
|
|
14
14
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
15
15
|
|
|
16
16
|
## `target` is not optional in a shell
|
|
17
|
-
Whenever the app scrolls a container rather than the page
|
|
17
|
+
Whenever the app scrolls a container rather than the page - an `KptAppShell` layout, for instance -
|
|
18
18
|
the window never scrolls and the button would never appear. Point it at the container:
|
|
19
19
|
`target=".kpt-app-shell__content"`.
|
|
20
20
|
|
|
@@ -24,7 +24,7 @@ component, and on the server it does not exist at all. The listener is removed o
|
|
|
24
24
|
position is read once on mount, because the page may already be scrolled (arriving at an anchor).
|
|
25
25
|
|
|
26
26
|
## The hidden button stays in the DOM
|
|
27
|
-
It fades in and out, so hiding it is `tabindex="-1"` plus the CSS class, not a conditional render
|
|
27
|
+
It fades in and out, so hiding it is `tabindex="-1"` plus the CSS class, not a conditional render -
|
|
28
28
|
otherwise the animation would have nothing to run on.
|
|
29
29
|
|
|
30
30
|
## Examples
|
|
@@ -42,4 +42,4 @@ A real `<button>` labelled from `scrollTop.label`; while hidden it is `visibilit
|
|
|
42
42
|
out of the tab order and the accessibility tree. It sits in the bottom-end corner
|
|
43
43
|
(`inset-inline-end`), so it moves to the left in RTL. With `prefers-reduced-motion` the scroll is a
|
|
44
44
|
jump instead of smooth and the button appears without a transition. Focus stays on the button after
|
|
45
|
-
the jump
|
|
45
|
+
the jump - move it to your page heading if that matters.
|
|
@@ -7,7 +7,7 @@ export interface KptScrollTopProps extends ComponentPropsWithRef<'div'>, KptI18n
|
|
|
7
7
|
target?: string | null;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Przycisk
|
|
10
|
+
* Przycisk "do góry" - pojawia się po przewinięciu ponad `threshold` px.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* <KptScrollTop threshold={300} />
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { useEffect, useState } from 'react';
|
|
3
4
|
import { cn } from "../internal/cn.js";
|
|
4
5
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
5
6
|
import { KptIcon } from "../icon/icon.js";
|
|
6
7
|
/**
|
|
7
|
-
* Przycisk
|
|
8
|
+
* Przycisk "do góry" - pojawia się po przewinięciu ponad `threshold` px.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* <KptScrollTop threshold={300} />
|
package/dist/select/llms.txt
CHANGED
|
@@ -6,21 +6,21 @@ A select on its own panel. Variants: single, with a search box (`filter`) and mu
|
|
|
6
6
|
## DOM
|
|
7
7
|
<span class="kpt-select-host">
|
|
8
8
|
<div class="kpt-select" role="combobox" tabindex="0" aria-expanded="false">
|
|
9
|
-
<div class="kpt-select__value"><span class="kpt-select__placeholder">Choose
|
|
10
|
-
<span class="kpt-icon-host kpt-select__chevron"
|
|
9
|
+
<div class="kpt-select__value"><span class="kpt-select__placeholder">Choose...</span></div>
|
|
10
|
+
<span class="kpt-icon-host kpt-select__chevron">...</span>
|
|
11
11
|
</div>
|
|
12
12
|
</span>
|
|
13
13
|
<!-- while open, in a portal on document.body: -->
|
|
14
|
-
<div class="kpt-select__panel"><ul class="kpt-select__list" role="listbox"
|
|
14
|
+
<div class="kpt-select__panel"><ul class="kpt-select__list" role="listbox">...</ul></div>
|
|
15
15
|
|
|
16
16
|
## Props
|
|
17
|
-
- `options`: `KptOption<T>[]`
|
|
18
|
-
- `value` / `defaultValue` / `onValueChange`
|
|
19
|
-
- `open` / `defaultOpen` / `onOpenChange`
|
|
17
|
+
- `options`: `KptOption<T>[]` - `{ label, value, disabled? }`
|
|
18
|
+
- `value` / `defaultValue` / `onValueChange` - a single `T`, or `T[]` in `multiple` mode
|
|
19
|
+
- `open` / `defaultOpen` / `onOpenChange` - the panel state, so the list can be opened from outside
|
|
20
20
|
- `placeholder`, `multiple`, `filter`, `disabled`, `invalid`, `touched`
|
|
21
|
-
- `onTouch()`
|
|
21
|
+
- `onTouch()` - fires when the panel closes or focus leaves the closed trigger
|
|
22
22
|
- `aria-label` (through the rest props) names the trigger when no `<label for>` describes it
|
|
23
|
-
- `locale`, `dictionary`
|
|
23
|
+
- `locale`, `dictionary` - i18n overrides for this component only
|
|
24
24
|
- `className`, `ref` and every other `<div>` prop go to the trigger
|
|
25
25
|
|
|
26
26
|
## The panel is a portal
|
|
@@ -35,20 +35,20 @@ through `aria-activedescendant` (with `filter`, focus moves to the search field)
|
|
|
35
35
|
ArrowUp, Enter, Space, Home and End open the list, typing letters opens it on the first match, and
|
|
36
36
|
in `multiple` mode Backspace/Delete removes the last chip. Open: ArrowUp/ArrowDown move by one,
|
|
37
37
|
Home/End to the ends, PageUp/PageDown by ten, skipping disabled options without wrapping; Enter (and
|
|
38
|
-
Space on the trigger) picks
|
|
38
|
+
Space on the trigger) picks - a single select closes and returns focus to the trigger; typing jumps
|
|
39
39
|
to the next match, diacritics ignored; Escape and Tab close. Hovering an option makes it active, so
|
|
40
40
|
the mouse and the keyboard share one highlight, and clicking it does not take focus from the trigger.
|
|
41
41
|
|
|
42
42
|
## Inside KptFormField
|
|
43
43
|
There is no native field for the wrapper to read, so the select reports its filled state with
|
|
44
|
-
`useKptFormFieldFilled`
|
|
44
|
+
`useKptFormFieldFilled` - the float label then floats over the chosen value.
|
|
45
45
|
|
|
46
46
|
## i18n
|
|
47
47
|
`select.search` (the search placeholder), `select.noResults` (the empty list),
|
|
48
48
|
`select.removeChip` (the chip's remove button).
|
|
49
49
|
|
|
50
50
|
## Examples
|
|
51
|
-
<KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Choose
|
|
51
|
+
<KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Choose..." />
|
|
52
52
|
<KptSelect options={roles} value={role} onValueChange={setRole} filter />
|
|
53
53
|
<KptSelect options={tags} value={tags} onValueChange={setTags} multiple filter />
|
|
54
54
|
|
|
@@ -56,9 +56,16 @@ There is no native field for the wrapper to read, so the select reports its fill
|
|
|
56
56
|
`id` goes to the focusable element, not the host; without one it comes from `KptFormField` or is
|
|
57
57
|
generated. A `div` with a role is not named by `label[for]`, so inside `KptFormField` it gets
|
|
58
58
|
`aria-labelledby` pointing at the wrapper's label (unless `aria-label` is set), and the hint or
|
|
59
|
-
error is added to `aria-describedby`. New: `id` and `required` (
|
|
59
|
+
error is added to `aria-describedby`. New: `id` and `required` (-> `aria-required`); the list gets
|
|
60
60
|
the same name as the trigger.
|
|
61
61
|
|
|
62
|
+
## Motion
|
|
63
|
+
With `multiple`, a chip of a newly picked option pops in at its place (chips follow the order of
|
|
64
|
+
`options`), a removed one - by its "x", by unpicking it in the list or by `Backspace` - fades and shrinks in
|
|
65
|
+
place while the rest slide over on a spring. Several changes at once (e.g. a new value set from code) go
|
|
66
|
+
without motion. Follows `enter` from `KptMotionProvider` (`spring` / `fade` / `none`); `enabled: false` and the
|
|
67
|
+
system "reduce motion" setting always win.
|
|
68
|
+
|
|
62
69
|
## Tokens
|
|
63
70
|
`--kpt-form-field-{bg,text,placeholder,border,border-hover,border-focus,border-error,radius}`,
|
|
64
71
|
`--kpt-color-surface-raised` and `--kpt-elevation-3` for the panel, `--kpt-z-overlay` for the layer.
|
|
@@ -68,3 +75,6 @@ The trigger is `role="combobox"` with `aria-haspopup="listbox"`, `aria-expanded`
|
|
|
68
75
|
and `aria-activedescendant`; the list is `role="listbox"` with `aria-multiselectable`, and each
|
|
69
76
|
option is `role="option"` with `aria-selected` and `aria-disabled`. Chip remove buttons sit outside
|
|
70
77
|
the Tab order and are named with the option label.
|
|
78
|
+
|
|
79
|
+
## Read-only and required
|
|
80
|
+
`readOnly` shows the choice, but the panel does not open and the chips have no remove button (`aria-readonly`). The filter ignores case and diacritics ("lodz" finds "Łódź"), as in `listbox`. "No results" sits next to the list, not inside it (a listbox may contain only options).
|
package/dist/select/select.d.ts
CHANGED
|
@@ -4,22 +4,24 @@ import type { KptOption } from '../shared/types.ts';
|
|
|
4
4
|
export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'children' | 'onChange' | 'defaultValue'>, KptI18nOverrides {
|
|
5
5
|
options?: readonly KptOption<T>[];
|
|
6
6
|
placeholder?: string;
|
|
7
|
-
/** Wybór wielokrotny
|
|
7
|
+
/** Wybór wielokrotny - wartością jest tablica, a wybrane opcje pokazują się jako chipy. */
|
|
8
8
|
multiple?: boolean;
|
|
9
9
|
/** Pole wyszukiwania nad listą. */
|
|
10
10
|
filter?: boolean;
|
|
11
|
-
/** Id wyzwalacza; bez niego generowane, w `KptFormField`
|
|
11
|
+
/** Id wyzwalacza; bez niego generowane, w `KptFormField` - z wrappera. */
|
|
12
12
|
id?: string;
|
|
13
13
|
/** Oznacza pole jako wymagane atrybutem `aria-required`. */
|
|
14
14
|
required?: boolean;
|
|
15
15
|
disabled?: boolean;
|
|
16
|
+
/** Tylko do odczytu - wybór widać, panel się nie otwiera (`aria-readonly`). */
|
|
17
|
+
readOnly?: boolean;
|
|
16
18
|
invalid?: boolean;
|
|
17
19
|
touched?: boolean;
|
|
18
20
|
/** Wartość: pojedyncza (T) albo tablica (T[]) w trybie `multiple`. */
|
|
19
21
|
value?: T | T[] | null;
|
|
20
22
|
defaultValue?: T | T[] | null;
|
|
21
23
|
onValueChange?: (value: T | T[] | null) => void;
|
|
22
|
-
/** Stan panelu
|
|
24
|
+
/** Stan panelu - pozwala otworzyć listę z zewnątrz i wiedzieć, kiedy user skończył wybierać. */
|
|
23
25
|
open?: boolean;
|
|
24
26
|
defaultOpen?: boolean;
|
|
25
27
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -30,19 +32,19 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
30
32
|
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
31
33
|
* i wielokrotny (`multiple`, chipy).
|
|
32
34
|
*
|
|
33
|
-
* Klawiatura według wzorca WAI-ARIA
|
|
34
|
-
* idzie przez `aria-activedescendant`.
|
|
35
|
+
* Klawiatura według wzorca WAI-ARIA "select-only combobox": fokus zostaje na polu, a aktywna opcja
|
|
36
|
+
* idzie przez `aria-activedescendant`. `ArrowDown`/`ArrowUp`/`Enter`/`Spacja` otwierają listę na wybranej opcji,
|
|
35
37
|
* strzałki, `Home`/`End`, `PageUp`/`PageDown` chodzą po opcjach (z pominięciem wyłączonych),
|
|
36
38
|
* wpisywane litery skaczą do pasującej opcji (bez polskich znaków), `Enter` wybiera, `Escape`
|
|
37
39
|
* zamyka. Z `filter` fokus przechodzi do pola wyszukiwania, które obsługuje te same klawisze.
|
|
38
40
|
* W trybie `multiple` `Backspace` na zamkniętym polu usuwa ostatni wybór.
|
|
39
41
|
*
|
|
40
42
|
* Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
|
|
41
|
-
* Float Label unosi się nad wybraną wartością
|
|
43
|
+
* Float Label unosi się nad wybraną wartością - natywnego pola, z którego wrapper mógłby to
|
|
42
44
|
* odczytać, tu nie ma.
|
|
43
45
|
*
|
|
44
46
|
* @example
|
|
45
|
-
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz
|
|
47
|
+
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz..." />
|
|
46
48
|
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
47
49
|
*/
|
|
48
|
-
export declare function KptSelect<T>({ options, placeholder, multiple, filter, disabled, invalid, touched, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }: Readonly<KptSelectProps<T>>): import("react").JSX.Element;
|
|
50
|
+
export declare function KptSelect<T>({ options, placeholder, multiple, filter, disabled, readOnly, invalid, touched, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }: Readonly<KptSelectProps<T>>): import("react").JSX.Element;
|
package/dist/select/select.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { foldMentionText } from '@konce-pt/mention';
|
|
5
|
+
import { flowChangeOf, prepareFlowMotion, resolveEnterMotion } from '@konce-pt/motion';
|
|
6
|
+
import { useKptMotion } from "../motion/motion-context.js";
|
|
3
7
|
import { cn } from "../internal/cn.js";
|
|
8
|
+
import { optionKey } from "../internal/option-key.js";
|
|
4
9
|
import { KptOverlay } from "../internal/overlay.js";
|
|
5
10
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
6
11
|
import { useKptId } from "../internal/use-id.js";
|
|
@@ -8,30 +13,37 @@ import { KptTypeahead, OPTION_NAV_KEYS, isTypeaheadKey, nextOptionIndex, typeahe
|
|
|
8
13
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
9
14
|
import { joinIds, useKptFieldA11y, useKptFormFieldFilled } from "../form-field/form-field-context.js";
|
|
10
15
|
import { KptIcon } from "../icon/icon.js";
|
|
16
|
+
/** Filtr opcji bez wielkości liter i diakrytyków, jak w `KptListbox`: "lodz" znajduje "Łódź". */
|
|
17
|
+
function filterSelectOptions(options, text) {
|
|
18
|
+
const q = foldMentionText(text.trim());
|
|
19
|
+
return q ? options.filter((option) => foldMentionText(option.label).includes(q)) : options;
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
13
23
|
* i wielokrotny (`multiple`, chipy).
|
|
14
24
|
*
|
|
15
|
-
* Klawiatura według wzorca WAI-ARIA
|
|
16
|
-
* idzie przez `aria-activedescendant`.
|
|
25
|
+
* Klawiatura według wzorca WAI-ARIA "select-only combobox": fokus zostaje na polu, a aktywna opcja
|
|
26
|
+
* idzie przez `aria-activedescendant`. `ArrowDown`/`ArrowUp`/`Enter`/`Spacja` otwierają listę na wybranej opcji,
|
|
17
27
|
* strzałki, `Home`/`End`, `PageUp`/`PageDown` chodzą po opcjach (z pominięciem wyłączonych),
|
|
18
28
|
* wpisywane litery skaczą do pasującej opcji (bez polskich znaków), `Enter` wybiera, `Escape`
|
|
19
29
|
* zamyka. Z `filter` fokus przechodzi do pola wyszukiwania, które obsługuje te same klawisze.
|
|
20
30
|
* W trybie `multiple` `Backspace` na zamkniętym polu usuwa ostatni wybór.
|
|
21
31
|
*
|
|
22
32
|
* Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
|
|
23
|
-
* Float Label unosi się nad wybraną wartością
|
|
33
|
+
* Float Label unosi się nad wybraną wartością - natywnego pola, z którego wrapper mógłby to
|
|
24
34
|
* odczytać, tu nie ma.
|
|
25
35
|
*
|
|
26
36
|
* @example
|
|
27
|
-
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz
|
|
37
|
+
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz..." />
|
|
28
38
|
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
29
39
|
*/
|
|
30
|
-
export function KptSelect({ options = [], placeholder = '', multiple = false, filter = false, disabled = false, invalid = false, touched, value, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required = false, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }) {
|
|
31
|
-
/** `div` z rolą nie dostaje nazwy z `label[for]`
|
|
40
|
+
export function KptSelect({ options = [], placeholder = '', multiple = false, filter = false, disabled = false, readOnly = false, invalid = false, touched, value, defaultValue = null, onValueChange, open, defaultOpen = false, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required = false, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }) {
|
|
41
|
+
/** `div` z rolą nie dostaje nazwy z `label[for]` - stąd `aria-labelledby` na etykietę wrappera. */
|
|
32
42
|
const field = useKptFieldA11y(id, 'kpt-select-trigger');
|
|
33
43
|
const { t } = useKptMessages({ locale, dictionary });
|
|
34
44
|
const triggerRef = useRef(null);
|
|
45
|
+
/** Kotwica nakładki w stanie (callback ref), nie `triggerRef.current` czytane w renderze. */
|
|
46
|
+
const [anchor, setAnchor] = useState(null);
|
|
35
47
|
const searchRef = useRef(null);
|
|
36
48
|
const typeahead = useRef(new KptTypeahead());
|
|
37
49
|
const uid = useKptId(undefined, 'kpt-select');
|
|
@@ -52,35 +64,41 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
52
64
|
const showInvalid = invalid && (touched ?? true);
|
|
53
65
|
const selectedValues = useMemo(() => {
|
|
54
66
|
if (multiple)
|
|
55
|
-
return Array.isArray(current) ? current : [];
|
|
67
|
+
return Array.isArray(current) ? (current) : [];
|
|
56
68
|
return current == null ? [] : [current];
|
|
57
69
|
}, [current, multiple]);
|
|
58
70
|
const selectedOptions = useMemo(() => options.filter((option) => selectedValues.includes(option.value)), [options, selectedValues]);
|
|
59
|
-
const
|
|
60
|
-
const q = text.trim().toLowerCase();
|
|
61
|
-
return q ? options.filter((option) => option.label.toLowerCase().includes(q)) : options;
|
|
62
|
-
};
|
|
63
|
-
const filteredOptions = useMemo(() => filterOptions(filterText), [options, filterText]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
71
|
+
const filteredOptions = useMemo(() => filterSelectOptions(options, filterText), [options, filterText]);
|
|
64
72
|
const disabledMap = filteredOptions.map((option) => !!option.disabled);
|
|
65
73
|
const filled = selectedOptions.length > 0;
|
|
66
74
|
useKptFormFieldFilled(filled);
|
|
67
75
|
const isSelected = (option) => selectedValues.includes(option.value);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
//
|
|
76
|
+
/** Skrajna opcja, na której ma się otworzyć lista (`Home`/`End`/`ArrowUp` na zamkniętym polu). */
|
|
77
|
+
const [openEdge, setOpenEdge] = useState(null);
|
|
78
|
+
// Panel może zostać otwarty także z zewnątrz (`open`), nie tylko z pola - filtr i aktywną
|
|
79
|
+
// pozycję ustawiamy więc przy każdym przejściu w stan otwarty, wzorcem "poprzednia wartość
|
|
80
|
+
// w stanie" (w tym samym renderze, bez efektu). Lista otwiera się na wybranej opcji; zmiana
|
|
81
|
+
// wyboru w trybie `multiple` przy otwartym panelu nie przesuwa aktywnej opcji.
|
|
82
|
+
const [wasOpen, setWasOpen] = useState(isOpen);
|
|
83
|
+
if (wasOpen !== isOpen) {
|
|
84
|
+
setWasOpen(isOpen);
|
|
85
|
+
if (isOpen) {
|
|
86
|
+
setFilterText('');
|
|
87
|
+
const disabledAll = options.map((o) => !!o.disabled);
|
|
88
|
+
const selected = options.findIndex((option) => selectedValues.includes(option.value));
|
|
89
|
+
const firstEnabled = nextOptionIndex(disabledAll, -1, 'ArrowDown') ?? -1;
|
|
90
|
+
setActiveIndex(openEdge ? (nextOptionIndex(disabledAll, -1, openEdge) ?? -1) : selected >= 0 ? selected : firstEnabled);
|
|
91
|
+
setOpenEdge(null);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Z `filter` fokus idzie do wyszukiwarki - klatkę później, bo `KptOverlay` mierzy panel
|
|
95
|
+
// w `visibility: hidden`.
|
|
72
96
|
useEffect(() => {
|
|
73
|
-
if (!isOpen)
|
|
74
|
-
return;
|
|
75
|
-
setFilterText('');
|
|
76
|
-
const selected = options.findIndex((option) => selectedValues.includes(option.value));
|
|
77
|
-
const firstEnabled = nextOptionIndex(options.map((o) => !!o.disabled), -1, 'ArrowDown') ?? -1;
|
|
78
|
-
setActiveIndex(selected >= 0 ? selected : firstEnabled);
|
|
79
|
-
if (!filter)
|
|
97
|
+
if (!isOpen || !filter)
|
|
80
98
|
return;
|
|
81
99
|
const frame = requestAnimationFrame(() => searchRef.current?.focus());
|
|
82
100
|
return () => cancelAnimationFrame(frame);
|
|
83
|
-
}, [isOpen]);
|
|
101
|
+
}, [isOpen, filter]);
|
|
84
102
|
// Aktywna opcja zawsze w polu widzenia listy.
|
|
85
103
|
useEffect(() => {
|
|
86
104
|
if (isOpen && activeIndex >= 0)
|
|
@@ -92,16 +110,40 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
92
110
|
setOpen(false);
|
|
93
111
|
onTouch?.();
|
|
94
112
|
}, [isOpen, setOpen, onTouch]);
|
|
95
|
-
/** Zamknięcie z powrotem fokusu na pole
|
|
113
|
+
/** Zamknięcie z powrotem fokusu na pole - po wyborze, `Escape`, `Tab` w wyszukiwarce. */
|
|
96
114
|
const closeAndFocus = () => {
|
|
97
115
|
close();
|
|
98
116
|
triggerRef.current?.focus();
|
|
99
117
|
};
|
|
118
|
+
const enterMotion = resolveEnterMotion(undefined, useKptMotion());
|
|
119
|
+
const valueRef = useRef(null);
|
|
120
|
+
/** Ruch chipów przygotowany przed zmianą wyboru, odtwarzany po jej wyrenderowaniu. */
|
|
121
|
+
const pendingFlow = useRef(null);
|
|
122
|
+
const chipElements = () => Array.from(valueRef.current?.querySelectorAll('.kpt-select__chip') ?? []);
|
|
123
|
+
// Przed malowaniem - inaczej mignęłaby klatka z nowym układem, zanim FLIP cofnie chipy.
|
|
124
|
+
useLayoutEffect(() => {
|
|
125
|
+
const play = pendingFlow.current;
|
|
126
|
+
pendingFlow.current = null;
|
|
127
|
+
play?.(chipElements());
|
|
128
|
+
});
|
|
129
|
+
/**
|
|
130
|
+
* Nowy wybór w trybie `multiple` z ruchem chipów: dodany wskakuje, usunięty gaśnie, reszta się
|
|
131
|
+
* dosuwa (`prepareFlowMotion`, za `enter` z `KptMotionProvider`). Chipy stoją w kolejności opcji,
|
|
132
|
+
* więc pozycję zmiany liczymy z list przed i po; kilka zmian naraz idzie bez ruchu.
|
|
133
|
+
*/
|
|
134
|
+
const setSelected = (next) => {
|
|
135
|
+
const box = valueRef.current;
|
|
136
|
+
const order = (values) => options.filter((o) => values.includes(o.value)).map((o) => o.value);
|
|
137
|
+
const change = flowChangeOf(order(selectedValues), order(next));
|
|
138
|
+
if (enterMotion !== 'none' && box && change)
|
|
139
|
+
pendingFlow.current = prepareFlowMotion(box, chipElements(), change, enterMotion);
|
|
140
|
+
setCurrent(next);
|
|
141
|
+
};
|
|
100
142
|
const pick = (option) => {
|
|
101
143
|
if (option.disabled)
|
|
102
144
|
return;
|
|
103
145
|
if (multiple) {
|
|
104
|
-
|
|
146
|
+
setSelected(isSelected(option) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value]);
|
|
105
147
|
}
|
|
106
148
|
else {
|
|
107
149
|
setCurrent(option.value);
|
|
@@ -114,27 +156,22 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
114
156
|
if (found !== null)
|
|
115
157
|
setActiveIndex(found);
|
|
116
158
|
};
|
|
117
|
-
/** Jeden obsługiwacz dla pola i dla wyszukiwarki w panelu
|
|
159
|
+
/** Jeden obsługiwacz dla pola i dla wyszukiwarki w panelu - różnią się tylko `Spacją` i `Tab`. */
|
|
118
160
|
const onKeyDown = (event, source) => {
|
|
119
|
-
if (disabled)
|
|
161
|
+
if (disabled || readOnly)
|
|
120
162
|
return;
|
|
121
163
|
const key = event.key;
|
|
122
164
|
if (!isOpen) {
|
|
123
165
|
if (['ArrowDown', 'ArrowUp', 'Enter', ' ', 'Home', 'End'].includes(key)) {
|
|
124
166
|
event.preventDefault();
|
|
167
|
+
// Skrajne klawisze otwierają listę na pierwszej albo ostatniej opcji zamiast na wybranej.
|
|
168
|
+
if (key === 'ArrowUp' || key === 'End' || key === 'Home')
|
|
169
|
+
setOpenEdge(key === 'Home' ? 'Home' : 'End');
|
|
125
170
|
setOpen(true);
|
|
126
|
-
if (key === 'ArrowUp' || key === 'End' || key === 'Home') {
|
|
127
|
-
// Efekt otwarcia ustawia wybraną opcję; skrajne klawisze nadpisują to klatkę później.
|
|
128
|
-
const target = nextOptionIndex(disabledMap, -1, key === 'Home' ? 'Home' : 'End');
|
|
129
|
-
requestAnimationFrame(() => {
|
|
130
|
-
if (target !== null)
|
|
131
|
-
setActiveIndex(target);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
171
|
}
|
|
135
172
|
else if ((key === 'Backspace' || key === 'Delete') && multiple && selectedValues.length) {
|
|
136
173
|
event.preventDefault();
|
|
137
|
-
|
|
174
|
+
setSelected(selectedValues.slice(0, -1));
|
|
138
175
|
}
|
|
139
176
|
else if (isTypeaheadKey(event.nativeEvent) && !filter) {
|
|
140
177
|
setOpen(true);
|
|
@@ -147,7 +184,7 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
147
184
|
closeAndFocus();
|
|
148
185
|
}
|
|
149
186
|
else if (key === 'Tab') {
|
|
150
|
-
// Z wyszukiwarki (w portalu na końcu strony) Tab uciekłby w próżnię
|
|
187
|
+
// Z wyszukiwarki (w portalu na końcu strony) Tab uciekłby w próżnię - wracamy na pole.
|
|
151
188
|
if (source === 'search') {
|
|
152
189
|
event.preventDefault();
|
|
153
190
|
closeAndFocus();
|
|
@@ -172,18 +209,21 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
172
209
|
jumpTo(key);
|
|
173
210
|
}
|
|
174
211
|
};
|
|
175
|
-
return (_jsxs("span", { className: "kpt-select-host", children: [_jsxs("div", { ref:
|
|
212
|
+
return (_jsxs("span", { className: "kpt-select-host", children: [_jsxs("div", { ref: (el) => {
|
|
213
|
+
triggerRef.current = el;
|
|
214
|
+
setAnchor(el);
|
|
215
|
+
}, className: cn('kpt-select', isOpen && 'kpt-select--open', disabled && 'kpt-select--disabled', readOnly && 'kpt-select--readonly', showInvalid && 'kpt-select--invalid', className), role: "combobox", id: field.id, tabIndex: 0, "aria-haspopup": "listbox", "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : field.labelledBy, "aria-describedby": joinIds(ariaDescribedBy, field.describedBy), "aria-required": required || undefined, "aria-readonly": readOnly || undefined, "aria-expanded": isOpen, "aria-controls": isOpen ? listId : undefined, "aria-activedescendant": isOpen && !filter && activeIndex >= 0 ? optionId(activeIndex) : undefined, "aria-disabled": disabled || undefined, "aria-invalid": showInvalid || undefined, onClick: () => !disabled && !readOnly && setOpen(!isOpen), onKeyDown: (event) => onKeyDown(event, 'trigger'), onBlur: (event) => {
|
|
176
216
|
onBlur?.(event);
|
|
177
|
-
// Przy otwartym panelu fokus idzie do pola szukania
|
|
217
|
+
// Przy otwartym panelu fokus idzie do pola szukania - "dotknięcie" wyśle dopiero zamknięcie.
|
|
178
218
|
if (!isOpen)
|
|
179
219
|
onTouch?.();
|
|
180
|
-
}, ...rest, children: [_jsx("div", { className: "kpt-select__value", children: multiple && selectedOptions.length ? (selectedOptions.map((option) => (_jsxs("span", { className: "kpt-select__chip", children: [option.label, _jsx("button", { type: "button", className: "kpt-select__chip-remove", tabIndex: -1, "aria-label": `${t('select.removeChip')}: ${option.label}`, onClick: (event) => {
|
|
220
|
+
}, ...rest, children: [_jsx("div", { className: "kpt-select__value", ref: valueRef, children: multiple && selectedOptions.length ? (selectedOptions.map((option, index) => (_jsxs("span", { className: "kpt-select__chip", children: [option.label, readOnly ? null : (_jsx("button", { type: "button", className: "kpt-select__chip-remove", tabIndex: -1, "aria-label": `${t('select.removeChip')}: ${option.label}`, onClick: (event) => {
|
|
181
221
|
event.stopPropagation();
|
|
182
|
-
|
|
183
|
-
}, children: _jsx(KptIcon, { name: "x" }) })] },
|
|
222
|
+
setSelected(selectedValues.filter((v) => v !== option.value));
|
|
223
|
+
}, children: _jsx(KptIcon, { name: "x" }) }))] }, optionKey(option.value, index))))) : !multiple && filled ? (_jsx("span", { className: "kpt-select__single", children: selectedOptions[0]?.label })) : (_jsx("span", { className: "kpt-select__placeholder", children: placeholder })) }), _jsx(KptIcon, { className: "kpt-select__chevron", name: "chevron-down" })] }), _jsx(KptOverlay, { open: isOpen, anchor: anchor, onClose: close, minAnchorWidth: true, children: _jsxs("div", { className: "kpt-select__panel", children: [filter ? (_jsxs("div", { className: "kpt-select__search", children: [_jsx(KptIcon, { name: "search" }), _jsx("input", { ref: searchRef, type: "text", className: "kpt-select__search-input", role: "combobox", "aria-autocomplete": "list", "aria-expanded": true, "aria-controls": listId, "aria-activedescendant": activeIndex >= 0 ? optionId(activeIndex) : undefined, "aria-label": t('select.search'), value: filterText, placeholder: t('select.search'), onKeyDown: (event) => onKeyDown(event, 'search'), onChange: (event) => {
|
|
184
224
|
setFilterText(event.target.value);
|
|
185
|
-
// Po zawężeniu aktywna jest pierwsza pasująca opcja
|
|
186
|
-
const narrowed =
|
|
225
|
+
// Po zawężeniu aktywna jest pierwsza pasująca opcja - `Enter` wybiera ją od razu.
|
|
226
|
+
const narrowed = filterSelectOptions(options, event.target.value).map((o) => !!o.disabled);
|
|
187
227
|
setActiveIndex(nextOptionIndex(narrowed, -1, 'ArrowDown') ?? -1);
|
|
188
|
-
} })] })) : null, _jsx("ul", { className: "kpt-select__list", role: "listbox", id: listId, "aria-multiselectable": multiple, "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : field.labelledBy, children: filteredOptions.
|
|
228
|
+
} })] })) : null, _jsx("ul", { className: "kpt-select__list", role: "listbox", id: listId, "aria-multiselectable": multiple, "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : field.labelledBy, children: filteredOptions.map((option, index) => (_jsxs("li", { id: optionId(index), className: cn('kpt-select__option', isSelected(option) && 'kpt-select__option--selected', index === activeIndex && 'kpt-select__option--active', option.disabled && 'kpt-select__option--disabled'), role: "option", "aria-selected": isSelected(option), "aria-disabled": option.disabled || undefined, onMouseDown: (event) => event.preventDefault(), onClick: () => pick(option), onMouseEnter: () => setActiveIndex(index), children: [multiple ? (_jsx("span", { className: cn('kpt-select__check', isSelected(option) && 'is-on'), children: _jsx(KptIcon, { name: "check" }) })) : null, _jsx("span", { className: "kpt-select__option-label", children: option.label }), !multiple && isSelected(option) ? (_jsx(KptIcon, { className: "kpt-select__option-tick", name: "check" })) : null] }, optionKey(option.value, index)))) }), filteredOptions.length === 0 ? (_jsx("div", { className: "kpt-select__empty", role: "status", children: t('select.noResults') })) : null] }) })] }));
|
|
189
229
|
}
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -6,5 +6,26 @@ export interface KptOption<T = unknown> {
|
|
|
6
6
|
/** Nazwa ikony z rejestru. Renderuje ją dziś wyłącznie `kpt-button-group`. */
|
|
7
7
|
icon?: string;
|
|
8
8
|
}
|
|
9
|
-
/** Wariant komunikatu
|
|
9
|
+
/** Wariant komunikatu - wspólny dla alertu i toastów. */
|
|
10
10
|
export type KptAlertVariant = 'info' | 'success' | 'warning' | 'danger';
|
|
11
|
+
/**
|
|
12
|
+
* Klik w odnośnik komponentu nawigacji (`kpt-breadcrumb`, `kpt-menubar`, `kpt-megamenu`). Linki
|
|
13
|
+
* są zwykłym `<a href>`, więc bez routera klik przeładowuje stronę. Aplikacja z routerem łapie to
|
|
14
|
+
* zdarzenie, woła `event.preventDefault()` i nawiguje sama - biblioteka nie zależy od żadnego routera.
|
|
15
|
+
* Zdarzenie leci tylko dla zwykłego kliku: Ctrl/Cmd/Shift/środkowy przycisk i `target="_blank"`
|
|
16
|
+
* zostają natywne (nowa karta, nowe okno).
|
|
17
|
+
*/
|
|
18
|
+
export interface KptNavigateEvent<T = unknown> {
|
|
19
|
+
/** Pozycja, w którą kliknięto. */
|
|
20
|
+
item: T;
|
|
21
|
+
/** Adres z `href`. */
|
|
22
|
+
url: string;
|
|
23
|
+
/** Oryginalne zdarzenie - `preventDefault()` zatrzymuje przeładowanie strony. */
|
|
24
|
+
event: MouseEvent;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Czy klik to zwykła nawigacja w tej samej karcie: lewy przycisk, bez modyfikatorów, a odnośnik
|
|
28
|
+
* nie ma `target` innego niż `_self`. `target` podaje wołający - w React `currentTarget`
|
|
29
|
+
* zdarzenia natywnego to korzeń aplikacji, nie kliknięty odnośnik.
|
|
30
|
+
*/
|
|
31
|
+
export declare function kptIsPlainClick(event: Pick<MouseEvent, 'button' | 'ctrlKey' | 'metaKey' | 'shiftKey' | 'altKey'>, target?: string): boolean;
|
package/dist/shared/types.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Czy klik to zwykła nawigacja w tej samej karcie: lewy przycisk, bez modyfikatorów, a odnośnik
|
|
3
|
+
* nie ma `target` innego niż `_self`. `target` podaje wołający - w React `currentTarget`
|
|
4
|
+
* zdarzenia natywnego to korzeń aplikacji, nie kliknięty odnośnik.
|
|
5
|
+
*/
|
|
6
|
+
export function kptIsPlainClick(event, target = '') {
|
|
7
|
+
const modified = event.ctrlKey || event.metaKey || event.shiftKey || event.altKey;
|
|
8
|
+
return event.button === 0 && !modified && (target === '' || target === '_self');
|
|
9
|
+
}
|
package/dist/sidenav/llms.txt
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# KptSidenav (@konce-pt/react)
|
|
2
2
|
|
|
3
|
-
The side panel
|
|
3
|
+
The side panel - a styled navigation container, usually handed to the `sidenav` slot of
|
|
4
4
|
`<KptAppShell>`. Import: `import { KptSidenav } from '@konce-pt/react';`
|
|
5
5
|
|
|
6
6
|
## DOM
|
|
7
7
|
<div class="kpt-sidenav">
|
|
8
|
-
<div class="kpt-sidenav__scroll"
|
|
9
|
-
<div kptsidenavfooter=""
|
|
8
|
+
<div class="kpt-sidenav__scroll">...children...</div>
|
|
9
|
+
<div kptsidenavfooter="">...footer...</div>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
## Props
|
|
13
13
|
- `className`, `ref` and every other `<div>` prop go to the panel root
|
|
14
14
|
|
|
15
15
|
## Sub-components
|
|
16
|
-
- `KptSidenav.Footer`
|
|
16
|
+
- `KptSidenav.Footer` - lifted OUT of the scrolling area, so it stays pinned to the bottom of the
|
|
17
17
|
panel however long the navigation gets (a user block, an app version, a theme switch). It carries
|
|
18
18
|
the `kptsidenavfooter` attribute the shared SCSS targets.
|
|
19
19
|
|
|
20
20
|
Default children land in the scrolling area. Plain `<a>` elements inside it are styled by the
|
|
21
|
-
stylesheet already
|
|
21
|
+
stylesheet already - padding, radius, a hover background - so a simple link list needs no classes.
|
|
22
22
|
|
|
23
23
|
## Examples
|
|
24
24
|
<KptSidenav>
|
|
@@ -27,7 +27,7 @@ stylesheet already — padding, radius, a hover background — so a simple link
|
|
|
27
27
|
<KptSidenav.Footer>v0.7.12</KptSidenav.Footer>
|
|
28
28
|
</KptSidenav>
|
|
29
29
|
|
|
30
|
-
<KptAppShell sidenav={<KptSidenav
|
|
30
|
+
<KptAppShell sidenav={<KptSidenav>...</KptSidenav>} toolbar={...}>...</KptAppShell>
|
|
31
31
|
|
|
32
32
|
## Rail mode
|
|
33
33
|
When `<KptAppShell sidenavRailBreakpoint>` is on, a closed panel narrows instead of hiding. Mark the
|
|
@@ -41,13 +41,13 @@ the shell will clip them the accessible way (visually hidden, still in the acces
|
|
|
41
41
|
|
|
42
42
|
Both render the lowercase attribute the shared SCSS targets (`kptnavlabel` / `kptnavsection`);
|
|
43
43
|
attribute names in CSS selectors are case-insensitive in HTML, so one rule serves both ports. Use
|
|
44
|
-
the components rather than writing the attribute by hand
|
|
44
|
+
the components rather than writing the attribute by hand - JSX rejects an unknown camelCase
|
|
45
45
|
attribute, and a hand-written one is easy to misspell.
|
|
46
46
|
|
|
47
47
|
## Links
|
|
48
48
|
Links in the scroll area are `display: flex` with a gap, so an icon and a label never glue together
|
|
49
49
|
even when the template compiler drops the whitespace between them. A link with
|
|
50
|
-
`aria-current="page"` gets the muted background and a medium weight
|
|
50
|
+
`aria-current="page"` gets the muted background and a medium weight - mark the current page
|
|
51
51
|
yourself, the component does not know which one it is.
|
|
52
52
|
|
|
53
53
|
## Tokens
|
|
@@ -55,5 +55,5 @@ yourself, the component does not know which one it is.
|
|
|
55
55
|
`--kpt-sidenav-rail-width`, which are read by `KptAppShell`.
|
|
56
56
|
|
|
57
57
|
## Accessibility
|
|
58
|
-
The panel itself is a plain container
|
|
58
|
+
The panel itself is a plain container - the `role="navigation"` and the label live on the shell's
|
|
59
59
|
`<aside>` around it, so a sidenav used outside `KptAppShell` needs its own landmark.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
-
export
|
|
3
|
-
}
|
|
2
|
+
export type KptSidenavProps = ComponentPropsWithRef<'div'>;
|
|
4
3
|
/**
|
|
5
|
-
* Panel boczny (sidenav)
|
|
4
|
+
* Panel boczny (sidenav) - stylowany kontener nawigacji, zwykle podawany do slotu
|
|
6
5
|
* `sidenav` komponentu `<KptAppShell>`.
|
|
7
6
|
*
|
|
8
7
|
* @example
|
package/dist/sidenav/sidenav.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { isValidElement } from 'react';
|
|
3
3
|
import { cn } from "../internal/cn.js";
|
|
4
|
+
import { slotChildren } from "../internal/slot-children.js";
|
|
4
5
|
/*
|
|
5
6
|
* Panel odtwarza projekcję z Angulara: treść domyślna ląduje w obszarze przewijanym,
|
|
6
7
|
* a `<KptSidenav.Footer>` jest wynoszona POZA ten obszar. Dzięki temu stopka zostaje
|
|
7
8
|
* przyklejona do dołu panelu niezależnie od długości nawigacji.
|
|
8
9
|
*/
|
|
9
10
|
/**
|
|
10
|
-
* Panel boczny (sidenav)
|
|
11
|
+
* Panel boczny (sidenav) - stylowany kontener nawigacji, zwykle podawany do slotu
|
|
11
12
|
* `sidenav` komponentu `<KptAppShell>`.
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
@@ -20,14 +21,14 @@ import { cn } from "../internal/cn.js";
|
|
|
20
21
|
export function KptSidenav({ className, children, ...rest }) {
|
|
21
22
|
const scroll = [];
|
|
22
23
|
const footers = [];
|
|
23
|
-
|
|
24
|
+
for (const child of slotChildren(children)) {
|
|
24
25
|
if (isValidElement(child) && child.type?.kptSidenavSlot === 'footer') {
|
|
25
26
|
footers.push(child);
|
|
26
27
|
}
|
|
27
28
|
else {
|
|
28
29
|
scroll.push(child);
|
|
29
30
|
}
|
|
30
|
-
}
|
|
31
|
+
}
|
|
31
32
|
return (_jsxs("div", { className: cn('kpt-sidenav', className), ...rest, children: [_jsx("div", { className: "kpt-sidenav__scroll", children: scroll }), footers] }));
|
|
32
33
|
}
|
|
33
34
|
/**
|
package/dist/skeleton/llms.txt
CHANGED
|
@@ -12,13 +12,13 @@ Import: `import { KptSkeleton } from '@konce-pt/react';`
|
|
|
12
12
|
## Props
|
|
13
13
|
- `variant`: 'text' | 'circle' | 'rect' (default 'rect')
|
|
14
14
|
- `width`: string (default '100%'), `height`: string (default '1rem')
|
|
15
|
-
- `lines`: number (default 3)
|
|
15
|
+
- `lines`: number (default 3) - the number of lines in the `text` variant
|
|
16
16
|
- `className`, `ref` and every other `<span>` prop go to the host
|
|
17
17
|
|
|
18
18
|
## The last line is shorter
|
|
19
19
|
In the `text` variant the last line is 60% wide. A stack of full-width rectangles does not read as
|
|
20
20
|
a paragraph, and a paragraph is exactly what it stands in for. A single line (`lines={1}`) keeps
|
|
21
|
-
the full `width`
|
|
21
|
+
the full `width` - there is no paragraph to imitate.
|
|
22
22
|
|
|
23
23
|
## Examples
|
|
24
24
|
<KptSkeleton variant="text" lines={3} />
|
|
@@ -29,6 +29,6 @@ the full `width` — there is no paragraph to imitate.
|
|
|
29
29
|
The base `--kpt-color-muted`; the shimmer is a lighter band mixed with `--kpt-color-surface`; corners `--kpt-radius-md` (blocks) / `--kpt-radius-sm` (text lines).
|
|
30
30
|
|
|
31
31
|
## Accessibility
|
|
32
|
-
The host is `aria-hidden="true"`
|
|
32
|
+
The host is `aria-hidden="true"` - a placeholder has nothing to announce, and reading out empty
|
|
33
33
|
boxes only adds noise. Announce the wait with `KptSpinner` or `KptProgress` next to it when it
|
|
34
34
|
matters.
|