@konce-pt/react 0.12.0 → 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 +122 -64
- package/dist/accordion/accordion.d.ts +14 -6
- package/dist/accordion/accordion.js +14 -7
- package/dist/accordion/llms.txt +22 -10
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +2 -2
- package/dist/alert/llms.txt +10 -10
- package/dist/app-shell/app-shell.d.ts +14 -14
- package/dist/app-shell/app-shell.js +11 -11
- package/dist/app-shell/llms.txt +13 -13
- package/dist/auth/auth-types.d.ts +6 -6
- package/dist/auth/auth.d.ts +2 -2
- package/dist/auth/auth.js +7 -7
- package/dist/auth/llms.txt +9 -9
- package/dist/autocomplete/autocomplete.d.ts +19 -6
- package/dist/autocomplete/autocomplete.js +23 -10
- package/dist/autocomplete/llms.txt +16 -5
- package/dist/avatar/avatar.d.ts +6 -6
- package/dist/avatar/avatar.js +5 -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 +3 -3
- package/dist/avatar-group/llms.txt +8 -8
- package/dist/backdrop/backdrop.d.ts +7 -7
- package/dist/backdrop/backdrop.js +16 -16
- 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 +4 -4
- package/dist/bottom-sheet/bottom-sheet.js +2 -2
- package/dist/bottom-sheet/llms.txt +9 -9
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +3 -3
- package/dist/breadcrumb/llms.txt +3 -3
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +8 -8
- package/dist/button/llms.txt +27 -19
- package/dist/button-group/button-group.d.ts +9 -9
- package/dist/button-group/button-group.js +30 -8
- package/dist/button-group/llms.txt +20 -14
- package/dist/card/card.d.ts +8 -8
- package/dist/card/card.js +6 -6
- package/dist/card/llms.txt +20 -20
- package/dist/carousel/carousel.d.ts +6 -6
- package/dist/carousel/carousel.js +4 -4
- package/dist/carousel/llms.txt +10 -10
- package/dist/charts/chart-legend.d.ts +3 -3
- package/dist/charts/chart-legend.js +1 -1
- package/dist/charts/chart-tooltip.d.ts +3 -3
- package/dist/charts/chart-tooltip.js +1 -1
- package/dist/charts/chart.d.ts +1 -1
- package/dist/charts/chart.js +8 -8
- package/dist/charts/llms.txt +6 -6
- package/dist/charts-entry/public-api.js +2 -2
- package/dist/chat/chat.d.ts +17 -10
- package/dist/chat/chat.js +42 -18
- package/dist/chat/llms.txt +32 -17
- package/dist/chat-composer/chat-composer.d.ts +17 -7
- package/dist/chat-composer/chat-composer.js +21 -8
- 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 +9 -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 +17 -7
- 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 +3 -3
- package/dist/chat-typing/llms.txt +6 -6
- package/dist/checkbox/checkbox.d.ts +13 -6
- package/dist/checkbox/checkbox.js +26 -7
- package/dist/checkbox/llms.txt +24 -12
- package/dist/chip/chip.d.ts +4 -4
- package/dist/chip/chip.js +2 -2
- package/dist/chip/llms.txt +11 -11
- package/dist/chips-input/chips-input.d.ts +4 -4
- package/dist/chips-input/chips-input.js +35 -9
- package/dist/chips-input/llms.txt +15 -8
- package/dist/clock/clock.d.ts +4 -4
- package/dist/clock/clock.js +7 -7
- package/dist/clock/llms.txt +8 -8
- package/dist/color-picker/color-picker.d.ts +6 -6
- package/dist/color-picker/color-picker.js +3 -3
- package/dist/color-picker/llms.txt +5 -5
- package/dist/confirm/confirm-container.d.ts +3 -3
- package/dist/confirm/confirm-container.js +5 -5
- package/dist/confirm/confirm-store.d.ts +4 -4
- package/dist/confirm/confirm-store.js +7 -7
- package/dist/confirm/llms.txt +6 -6
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/context-menu.js +5 -5
- package/dist/context-menu/llms.txt +7 -7
- package/dist/data-table/data-table-types.d.ts +5 -5
- package/dist/data-table/data-table.d.ts +29 -8
- package/dist/data-table/data-table.js +340 -95
- package/dist/data-table/export-csv.d.ts +3 -3
- package/dist/data-table/export-csv.js +4 -4
- package/dist/data-table/llms.txt +127 -94
- package/dist/data-view/data-view.d.ts +13 -5
- package/dist/data-view/data-view.js +67 -6
- package/dist/data-view/llms.txt +29 -10
- package/dist/date-range/date-range.d.ts +9 -9
- package/dist/date-range/date-range.js +24 -24
- package/dist/date-range/llms.txt +20 -20
- package/dist/datepicker/datepicker.d.ts +10 -10
- package/dist/datepicker/datepicker.js +29 -29
- package/dist/datepicker/llms.txt +24 -24
- package/dist/dialog/dialog.d.ts +4 -4
- package/dist/dialog/dialog.js +4 -4
- package/dist/dialog/llms.txt +11 -11
- package/dist/divider/divider.d.ts +1 -1
- package/dist/divider/llms.txt +4 -4
- package/dist/drawer/drawer.d.ts +5 -5
- package/dist/drawer/drawer.js +2 -2
- package/dist/drawer/llms.txt +11 -11
- 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 +9 -6
- package/dist/fieldset/llms.txt +26 -9
- package/dist/file-upload/file-upload.d.ts +3 -3
- package/dist/file-upload/file-upload.js +7 -7
- package/dist/file-upload/llms.txt +7 -7
- 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 +26 -6
- package/dist/form-field/llms.txt +25 -11
- package/dist/galleria/galleria.d.ts +3 -3
- package/dist/galleria/galleria.js +5 -5
- 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 +3 -3
- package/dist/i18n/i18n-context.js +7 -7
- 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 +6 -6
- 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 +1 -1
- package/dist/image/llms.txt +13 -13
- package/dist/input/input.d.ts +18 -5
- package/dist/input/input.js +21 -6
- package/dist/input/llms.txt +25 -11
- package/dist/input-mask/input-mask.d.ts +1 -1
- package/dist/input-mask/input-mask.js +2 -2
- package/dist/input-mask/llms.txt +5 -5
- package/dist/input-number/input-number.d.ts +9 -9
- package/dist/input-number/input-number.js +11 -11
- package/dist/input-number/llms.txt +14 -14
- package/dist/input-otp/input-otp.d.ts +6 -6
- package/dist/input-otp/input-otp.js +10 -10
- package/dist/input-otp/llms.txt +9 -9
- 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 +2 -2
- package/dist/internal/mask.js +4 -4
- package/dist/internal/option-key.d.ts +2 -2
- package/dist/internal/option-key.js +2 -2
- 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 +8 -8
- package/dist/internal/password-strength.d.ts +2 -2
- package/dist/internal/password-strength.js +6 -6
- package/dist/internal/roving.d.ts +2 -2
- package/dist/internal/roving.js +3 -3
- package/dist/internal/scroll-lock.d.ts +1 -1
- package/dist/internal/scroll-lock.js +2 -2
- package/dist/internal/slot-children.d.ts +3 -3
- package/dist/internal/slot-children.js +4 -4
- package/dist/internal/slot.d.ts +3 -3
- package/dist/internal/slot.js +2 -2
- package/dist/internal/step.d.ts +3 -3
- package/dist/internal/step.js +4 -4
- package/dist/internal/tween.d.ts +5 -5
- package/dist/internal/tween.js +5 -5
- 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 +1 -1
- package/dist/internal/use-controllable-state.js +3 -3
- 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 +1 -1
- package/dist/internal/use-modal-focus.js +3 -3
- package/dist/knob/knob.d.ts +4 -4
- package/dist/knob/knob.js +14 -10
- package/dist/knob/llms.txt +17 -12
- package/dist/listbox/listbox.d.ts +6 -6
- package/dist/listbox/listbox.js +9 -9
- package/dist/listbox/llms.txt +10 -10
- 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 +25 -22
- package/dist/map/map-create-dialog.d.ts +4 -4
- package/dist/map/map-create-dialog.js +10 -10
- package/dist/map/map-hours-field.d.ts +4 -4
- package/dist/map/map-hours-field.js +7 -7
- package/dist/map/map-image-field.d.ts +2 -2
- package/dist/map/map-image-field.js +4 -4
- package/dist/map/map-legend.d.ts +4 -4
- package/dist/map/map-legend.js +2 -2
- package/dist/map/map-poi-card.d.ts +3 -3
- package/dist/map/map-poi-card.js +9 -9
- package/dist/map/map-search.d.ts +17 -6
- package/dist/map/map-search.js +21 -18
- package/dist/map/map.d.ts +21 -14
- package/dist/map/map.js +30 -29
- 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 +9 -9
- package/dist/megamenu/megamenu.d.ts +6 -6
- package/dist/megamenu/megamenu.js +10 -10
- 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.d.ts +2 -2
- package/dist/menu/menu.js +6 -6
- package/dist/menubar/llms.txt +10 -10
- package/dist/menubar/menubar.d.ts +5 -5
- package/dist/menubar/menubar.js +10 -10
- 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 +1 -1
- package/dist/motion/motion-context.d.ts +22 -0
- package/dist/motion/motion-context.js +28 -0
- package/dist/order-list/llms.txt +22 -10
- package/dist/order-list/order-list.d.ts +12 -4
- package/dist/order-list/order-list.js +104 -21
- package/dist/paginator/llms.txt +8 -8
- package/dist/paginator/paginator.js +1 -1
- package/dist/panel/llms.txt +25 -13
- package/dist/panel/panel.d.ts +11 -3
- package/dist/panel/panel.js +7 -4
- package/dist/password/llms.txt +16 -8
- package/dist/password/password.d.ts +2 -2
- package/dist/password/password.js +24 -3
- package/dist/peek/llms.txt +10 -10
- package/dist/peek/peek.d.ts +6 -6
- package/dist/peek/peek.js +7 -7
- package/dist/pick-list/llms.txt +33 -13
- package/dist/pick-list/pick-list.d.ts +12 -4
- package/dist/pick-list/pick-list.js +111 -23
- package/dist/popover/llms.txt +11 -11
- package/dist/popover/popover.d.ts +4 -4
- package/dist/popover/popover.js +5 -5
- package/dist/progress/llms.txt +17 -17
- package/dist/progress/progress.d.ts +7 -7
- package/dist/progress/progress.js +6 -6
- package/dist/public-api.d.ts +15 -3
- package/dist/public-api.js +13 -7
- package/dist/radio-group/llms.txt +13 -6
- package/dist/radio-group/radio-group.d.ts +11 -4
- package/dist/radio-group/radio-group.js +15 -7
- 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 +76 -12
- package/dist/rich-text/lists.d.ts +2 -2
- package/dist/rich-text/lists.js +7 -7
- package/dist/rich-text/llms.txt +26 -17
- package/dist/rich-text/rich-text.d.ts +12 -7
- package/dist/rich-text/rich-text.js +78 -54
- package/dist/rich-text/sanitize.d.ts +3 -3
- package/dist/rich-text/sanitize.js +12 -12
- package/dist/rich-text/tables.d.ts +3 -3
- package/dist/rich-text/tables.js +3 -3
- package/dist/roadmap/llms.txt +15 -15
- package/dist/roadmap/roadmap.d.ts +6 -6
- package/dist/roadmap/roadmap.js +19 -19
- 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 +1 -1
- package/dist/select/llms.txt +19 -12
- package/dist/select/select.d.ts +8 -8
- package/dist/select/select.js +47 -21
- package/dist/shared/types.d.ts +5 -5
- package/dist/shared/types.js +1 -1
- package/dist/sidenav/llms.txt +9 -9
- package/dist/sidenav/sidenav.d.ts +1 -1
- package/dist/sidenav/sidenav.js +1 -1
- 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 +32 -13
- package/dist/slider/slider-drag.d.ts +26 -0
- package/dist/slider/slider-drag.js +79 -0
- package/dist/slider/slider.d.ts +6 -5
- package/dist/slider/slider.js +27 -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 +1 -1
- package/dist/spinner/llms.txt +7 -7
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +1 -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 +4 -4
- package/dist/splitter/llms.txt +11 -11
- package/dist/splitter/splitter.d.ts +3 -3
- package/dist/splitter/splitter.js +5 -5
- package/dist/stepper/llms.txt +33 -14
- package/dist/stepper/stepper.d.ts +6 -6
- package/dist/stepper/stepper.js +53 -10
- package/dist/switch/llms.txt +12 -5
- package/dist/switch/switch.d.ts +10 -3
- package/dist/switch/switch.js +12 -5
- package/dist/switch-group/llms.txt +9 -9
- package/dist/switch-group/switch-group.d.ts +4 -4
- package/dist/switch-group/switch-group.js +4 -4
- package/dist/tabs/llms.txt +10 -10
- package/dist/tabs/tabs.d.ts +5 -5
- package/dist/tabs/tabs.js +8 -8
- package/dist/textarea/llms.txt +23 -12
- package/dist/textarea/textarea.d.ts +19 -6
- package/dist/textarea/textarea.js +21 -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 +47 -17
- package/dist/toast/toast-container.d.ts +9 -3
- package/dist/toast/toast-container.js +17 -9
- package/dist/toast/toast-store.d.ts +40 -4
- 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 +10 -10
- package/dist/tooltip/tooltip.d.ts +3 -3
- package/dist/tooltip/tooltip.js +18 -18
- package/dist/tree/llms.txt +25 -12
- package/dist/tree/tree-keys.d.ts +5 -5
- package/dist/tree/tree-keys.js +7 -7
- package/dist/tree/tree.d.ts +14 -7
- package/dist/tree/tree.js +53 -18
- 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 +15 -13
package/dist/select/select.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
4
|
import { foldMentionText } from '@konce-pt/mention';
|
|
5
|
+
import { flowChangeOf, prepareFlowMotion, resolveEnterMotion } from '@konce-pt/motion';
|
|
6
|
+
import { useKptMotion } from "../motion/motion-context.js";
|
|
5
7
|
import { cn } from "../internal/cn.js";
|
|
6
8
|
import { optionKey } from "../internal/option-key.js";
|
|
7
9
|
import { KptOverlay } from "../internal/overlay.js";
|
|
@@ -11,7 +13,7 @@ import { KptTypeahead, OPTION_NAV_KEYS, isTypeaheadKey, nextOptionIndex, typeahe
|
|
|
11
13
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
12
14
|
import { joinIds, useKptFieldA11y, useKptFormFieldFilled } from "../form-field/form-field-context.js";
|
|
13
15
|
import { KptIcon } from "../icon/icon.js";
|
|
14
|
-
/** Filtr opcji bez wielkości liter i diakrytyków, jak w `KptListbox`:
|
|
16
|
+
/** Filtr opcji bez wielkości liter i diakrytyków, jak w `KptListbox`: "lodz" znajduje "Łódź". */
|
|
15
17
|
function filterSelectOptions(options, text) {
|
|
16
18
|
const q = foldMentionText(text.trim());
|
|
17
19
|
return q ? options.filter((option) => foldMentionText(option.label).includes(q)) : options;
|
|
@@ -20,23 +22,23 @@ function filterSelectOptions(options, text) {
|
|
|
20
22
|
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
21
23
|
* i wielokrotny (`multiple`, chipy).
|
|
22
24
|
*
|
|
23
|
-
* Klawiatura według wzorca WAI-ARIA
|
|
24
|
-
* 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,
|
|
25
27
|
* strzałki, `Home`/`End`, `PageUp`/`PageDown` chodzą po opcjach (z pominięciem wyłączonych),
|
|
26
28
|
* wpisywane litery skaczą do pasującej opcji (bez polskich znaków), `Enter` wybiera, `Escape`
|
|
27
29
|
* zamyka. Z `filter` fokus przechodzi do pola wyszukiwania, które obsługuje te same klawisze.
|
|
28
30
|
* W trybie `multiple` `Backspace` na zamkniętym polu usuwa ostatni wybór.
|
|
29
31
|
*
|
|
30
32
|
* Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
|
|
31
|
-
* 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
|
|
32
34
|
* odczytać, tu nie ma.
|
|
33
35
|
*
|
|
34
36
|
* @example
|
|
35
|
-
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz
|
|
37
|
+
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz..." />
|
|
36
38
|
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
37
39
|
*/
|
|
38
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 }) {
|
|
39
|
-
/** `div` z rolą nie dostaje nazwy z `label[for]`
|
|
41
|
+
/** `div` z rolą nie dostaje nazwy z `label[for]` - stąd `aria-labelledby` na etykietę wrappera. */
|
|
40
42
|
const field = useKptFieldA11y(id, 'kpt-select-trigger');
|
|
41
43
|
const { t } = useKptMessages({ locale, dictionary });
|
|
42
44
|
const triggerRef = useRef(null);
|
|
@@ -71,11 +73,11 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
71
73
|
const filled = selectedOptions.length > 0;
|
|
72
74
|
useKptFormFieldFilled(filled);
|
|
73
75
|
const isSelected = (option) => selectedValues.includes(option.value);
|
|
74
|
-
/** Skrajna opcja, na której ma się otworzyć lista (`Home`/`End
|
|
76
|
+
/** Skrajna opcja, na której ma się otworzyć lista (`Home`/`End`/`ArrowUp` na zamkniętym polu). */
|
|
75
77
|
const [openEdge, setOpenEdge] = useState(null);
|
|
76
|
-
// Panel może zostać otwarty także z zewnątrz (`open`), nie tylko z pola
|
|
77
|
-
// pozycję ustawiamy więc przy każdym przejściu w stan otwarty, wzorcem
|
|
78
|
-
// w stanie
|
|
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
|
|
79
81
|
// wyboru w trybie `multiple` przy otwartym panelu nie przesuwa aktywnej opcji.
|
|
80
82
|
const [wasOpen, setWasOpen] = useState(isOpen);
|
|
81
83
|
if (wasOpen !== isOpen) {
|
|
@@ -89,7 +91,7 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
89
91
|
setOpenEdge(null);
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
|
-
// Z `filter` fokus idzie do wyszukiwarki
|
|
94
|
+
// Z `filter` fokus idzie do wyszukiwarki - klatkę później, bo `KptOverlay` mierzy panel
|
|
93
95
|
// w `visibility: hidden`.
|
|
94
96
|
useEffect(() => {
|
|
95
97
|
if (!isOpen || !filter)
|
|
@@ -108,16 +110,40 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
108
110
|
setOpen(false);
|
|
109
111
|
onTouch?.();
|
|
110
112
|
}, [isOpen, setOpen, onTouch]);
|
|
111
|
-
/** Zamknięcie z powrotem fokusu na pole
|
|
113
|
+
/** Zamknięcie z powrotem fokusu na pole - po wyborze, `Escape`, `Tab` w wyszukiwarce. */
|
|
112
114
|
const closeAndFocus = () => {
|
|
113
115
|
close();
|
|
114
116
|
triggerRef.current?.focus();
|
|
115
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
|
+
};
|
|
116
142
|
const pick = (option) => {
|
|
117
143
|
if (option.disabled)
|
|
118
144
|
return;
|
|
119
145
|
if (multiple) {
|
|
120
|
-
|
|
146
|
+
setSelected(isSelected(option) ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value]);
|
|
121
147
|
}
|
|
122
148
|
else {
|
|
123
149
|
setCurrent(option.value);
|
|
@@ -130,7 +156,7 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
130
156
|
if (found !== null)
|
|
131
157
|
setActiveIndex(found);
|
|
132
158
|
};
|
|
133
|
-
/** 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`. */
|
|
134
160
|
const onKeyDown = (event, source) => {
|
|
135
161
|
if (disabled || readOnly)
|
|
136
162
|
return;
|
|
@@ -145,7 +171,7 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
145
171
|
}
|
|
146
172
|
else if ((key === 'Backspace' || key === 'Delete') && multiple && selectedValues.length) {
|
|
147
173
|
event.preventDefault();
|
|
148
|
-
|
|
174
|
+
setSelected(selectedValues.slice(0, -1));
|
|
149
175
|
}
|
|
150
176
|
else if (isTypeaheadKey(event.nativeEvent) && !filter) {
|
|
151
177
|
setOpen(true);
|
|
@@ -158,7 +184,7 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
158
184
|
closeAndFocus();
|
|
159
185
|
}
|
|
160
186
|
else if (key === 'Tab') {
|
|
161
|
-
// 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.
|
|
162
188
|
if (source === 'search') {
|
|
163
189
|
event.preventDefault();
|
|
164
190
|
closeAndFocus();
|
|
@@ -188,15 +214,15 @@ export function KptSelect({ options = [], placeholder = '', multiple = false, fi
|
|
|
188
214
|
setAnchor(el);
|
|
189
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) => {
|
|
190
216
|
onBlur?.(event);
|
|
191
|
-
// Przy otwartym panelu fokus idzie do pola szukania
|
|
217
|
+
// Przy otwartym panelu fokus idzie do pola szukania - "dotknięcie" wyśle dopiero zamknięcie.
|
|
192
218
|
if (!isOpen)
|
|
193
219
|
onTouch?.();
|
|
194
|
-
}, ...rest, children: [_jsx("div", { className: "kpt-select__value", 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) => {
|
|
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) => {
|
|
195
221
|
event.stopPropagation();
|
|
196
|
-
|
|
222
|
+
setSelected(selectedValues.filter((v) => v !== option.value));
|
|
197
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) => {
|
|
198
224
|
setFilterText(event.target.value);
|
|
199
|
-
// Po zawężeniu aktywna jest pierwsza pasująca opcja
|
|
225
|
+
// Po zawężeniu aktywna jest pierwsza pasująca opcja - `Enter` wybiera ją od razu.
|
|
200
226
|
const narrowed = filterSelectOptions(options, event.target.value).map((o) => !!o.disabled);
|
|
201
227
|
setActiveIndex(nextOptionIndex(narrowed, -1, 'ArrowDown') ?? -1);
|
|
202
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] }) })] }));
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -6,13 +6,13 @@ 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
11
|
/**
|
|
12
12
|
* Klik w odnośnik komponentu nawigacji (`kpt-breadcrumb`, `kpt-menubar`, `kpt-megamenu`). Linki
|
|
13
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
|
|
15
|
-
* Zdarzenie leci tylko dla zwykłego kliku: Ctrl
|
|
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
16
|
* zostają natywne (nowa karta, nowe okno).
|
|
17
17
|
*/
|
|
18
18
|
export interface KptNavigateEvent<T = unknown> {
|
|
@@ -20,12 +20,12 @@ export interface KptNavigateEvent<T = unknown> {
|
|
|
20
20
|
item: T;
|
|
21
21
|
/** Adres z `href`. */
|
|
22
22
|
url: string;
|
|
23
|
-
/** Oryginalne zdarzenie
|
|
23
|
+
/** Oryginalne zdarzenie - `preventDefault()` zatrzymuje przeładowanie strony. */
|
|
24
24
|
event: MouseEvent;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
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
|
|
28
|
+
* nie ma `target` innego niż `_self`. `target` podaje wołający - w React `currentTarget`
|
|
29
29
|
* zdarzenia natywnego to korzeń aplikacji, nie kliknięty odnośnik.
|
|
30
30
|
*/
|
|
31
31
|
export declare function kptIsPlainClick(event: Pick<MouseEvent, 'button' | 'ctrlKey' | 'metaKey' | 'shiftKey' | 'altKey'>, target?: string): boolean;
|
package/dist/shared/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
3
|
+
* nie ma `target` innego niż `_self`. `target` podaje wołający - w React `currentTarget`
|
|
4
4
|
* zdarzenia natywnego to korzeń aplikacji, nie kliknięty odnośnik.
|
|
5
5
|
*/
|
|
6
6
|
export function kptIsPlainClick(event, target = '') {
|
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,7 +1,7 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
2
|
export type KptSidenavProps = ComponentPropsWithRef<'div'>;
|
|
3
3
|
/**
|
|
4
|
-
* Panel boczny (sidenav)
|
|
4
|
+
* Panel boczny (sidenav) - stylowany kontener nawigacji, zwykle podawany do slotu
|
|
5
5
|
* `sidenav` komponentu `<KptAppShell>`.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
package/dist/sidenav/sidenav.js
CHANGED
|
@@ -8,7 +8,7 @@ import { slotChildren } from "../internal/slot-children.js";
|
|
|
8
8
|
* przyklejona do dołu panelu niezależnie od długości nawigacji.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* Panel boczny (sidenav)
|
|
11
|
+
* Panel boczny (sidenav) - stylowany kontener nawigacji, zwykle podawany do slotu
|
|
12
12
|
* `sidenav` komponentu `<KptAppShell>`.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
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.
|
|
@@ -10,7 +10,7 @@ export interface KptSkeletonProps extends Omit<ComponentPropsWithRef<'span'>, 'c
|
|
|
10
10
|
/**
|
|
11
11
|
* Placeholder ładowania z animacją shimmer.
|
|
12
12
|
*
|
|
13
|
-
* Wariant `text` skraca ostatnią linię do 60%
|
|
13
|
+
* Wariant `text` skraca ostatnią linię do 60% - pełny prostokąt nie wygląda jak akapit, a to
|
|
14
14
|
* właśnie akapit ma udawać. Przy jednej linii nie ma czego udawać, więc ta ma pełną szerokość.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
@@ -3,7 +3,7 @@ import { cn } from "../internal/cn.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Placeholder ładowania z animacją shimmer.
|
|
5
5
|
*
|
|
6
|
-
* Wariant `text` skraca ostatnią linię do 60%
|
|
6
|
+
* Wariant `text` skraca ostatnią linię do 60% - pełny prostokąt nie wygląda jak akapit, a to
|
|
7
7
|
* właśnie akapit ma udawać. Przy jednej linii nie ma czego udawać, więc ta ma pełną szerokość.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
package/dist/slider/llms.txt
CHANGED
|
@@ -6,33 +6,39 @@ Import: `import { KptSlider } from '@konce-pt/react';`
|
|
|
6
6
|
## DOM
|
|
7
7
|
<!-- single -->
|
|
8
8
|
<div class="kpt-slider-host">
|
|
9
|
-
<
|
|
9
|
+
<div class="kpt-slider__track" style="--_p: 0.4">
|
|
10
|
+
<input class="kpt-slider__input" type="range">
|
|
11
|
+
<span class="kpt-slider__bar" aria-hidden="true"><span class="kpt-slider__bar-fill"></span></span>
|
|
12
|
+
<span class="kpt-slider__knob" aria-hidden="true"></span>
|
|
13
|
+
</div>
|
|
10
14
|
<span class="kpt-slider__value">40</span>
|
|
11
15
|
</div>
|
|
12
16
|
|
|
13
17
|
<!-- range -->
|
|
14
18
|
<div class="kpt-slider-host">
|
|
15
|
-
<div class="kpt-
|
|
16
|
-
<div class="kpt-slider__rail"></div>
|
|
17
|
-
<div class="kpt-slider__fill" style="left: 20%; right: 20%"></div>
|
|
19
|
+
<div class="kpt-slider__track kpt-slider__track--range" style="--_lo: 0.2; --_hi: 0.8">
|
|
18
20
|
<input class="kpt-slider__thumb kpt-slider__thumb--low" type="range" aria-label="Minimum">
|
|
19
21
|
<input class="kpt-slider__thumb kpt-slider__thumb--high" type="range" aria-label="Maximum">
|
|
22
|
+
<span class="kpt-slider__bar" aria-hidden="true"><span class="kpt-slider__bar-fill"></span></span>
|
|
23
|
+
<span class="kpt-slider__knob kpt-slider__knob--low" aria-hidden="true"></span>
|
|
24
|
+
<span class="kpt-slider__knob kpt-slider__knob--high" aria-hidden="true"></span>
|
|
20
25
|
</div>
|
|
21
|
-
<span class="kpt-slider__value">20
|
|
26
|
+
<span class="kpt-slider__value">20-80</span>
|
|
22
27
|
</div>
|
|
23
28
|
|
|
24
29
|
## Props
|
|
25
|
-
- `value` / `defaultValue` / `onValueChange`
|
|
26
|
-
- `range`: boolean
|
|
30
|
+
- `value` / `defaultValue` / `onValueChange` - `number`, or `[number, number]` in `range` mode
|
|
31
|
+
- `range`: boolean - two handles
|
|
27
32
|
- `min` (default 0), `max` (default 100), `step` (default 1)
|
|
28
|
-
- `showValue`: boolean
|
|
33
|
+
- `showValue`: boolean - prints the current value next to the slider
|
|
29
34
|
- `disabled`, `onTouch()`
|
|
30
35
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
31
36
|
|
|
32
37
|
## Native inputs all the way down
|
|
33
38
|
Both handles are native `<input type="range">` stacked on each other, so the keyboard, touch
|
|
34
|
-
gestures and screen readers work with no handling of our own
|
|
35
|
-
|
|
39
|
+
gestures and screen readers work with no handling of our own. The only custom pointer logic is the
|
|
40
|
+
press on the track next to the handles (see "Pointer"). The track, fill and handles you see are
|
|
41
|
+
decoration drawn under the transparent inputs.
|
|
36
42
|
|
|
37
43
|
## The handles cannot cross
|
|
38
44
|
Moving the low handle above the high one clamps it to the high value, and the other way round. The
|
|
@@ -43,7 +49,7 @@ pair therefore always stays ordered, so the fill never inverts.
|
|
|
43
49
|
<KptSlider range value={span} onValueChange={(v) => setSpan(v as [number, number])} showValue />
|
|
44
50
|
|
|
45
51
|
## Screen reader and keyboard
|
|
46
|
-
`suffix`: string (" zł", "%", " km")
|
|
52
|
+
`suffix`: string (" zł", "%", " km") - shown after the value and put into `aria-valuetext` ("40
|
|
47
53
|
zł"), so the unit is read too. `aria-invalid` after touch.
|
|
48
54
|
|
|
49
55
|
## Label and description
|
|
@@ -55,12 +61,25 @@ previously hard-coded and mixed-language); inside `kpt-form-field` each thumb re
|
|
|
55
61
|
Minimum" through `aria-labelledby`, without one `ariaLabel` gives "<ariaLabel>, Minimum". `id` goes
|
|
56
62
|
to the single input or the lower thumb.
|
|
57
63
|
|
|
64
|
+
## Pointer
|
|
65
|
+
The handle reacts to the pointer: it grows on hover, and while held it grows more on a spring with
|
|
66
|
+
a soft halo in the primary colour and a `grabbing` cursor. A click on the track (and a key press) glides
|
|
67
|
+
the handle and the fill to the new value instead of jumping; while dragging they follow the pointer with
|
|
68
|
+
no delay. The native `input[type=range]` stays transparent on top (keyboard, screen reader, dragging) and
|
|
69
|
+
the track, fill and handle are drawn next to it from `--_p` (0..1); `range` draws two handles from
|
|
70
|
+
`--_lo`/`--_hi`. The glide is pure CSS and drops out under "reduce motion".
|
|
71
|
+
|
|
72
|
+
In `range` mode a press on the track outside the handles - before the range, after it or inside it -
|
|
73
|
+
moves the nearest handle there, focuses it (arrow keys keep working) and keeps dragging it until
|
|
74
|
+
release. The handles themselves are dragged natively. When both handles sit in the upper half, the
|
|
75
|
+
lower one is stacked on top, so two handles pushed against `max` can still be pulled apart.
|
|
76
|
+
|
|
58
77
|
## Tokens
|
|
59
|
-
`--kpt-color-primary` (the fill and the handles), `--kpt-color-
|
|
78
|
+
`--kpt-color-primary` (the fill and the handles), `--kpt-color-surface-variant` (the track).
|
|
60
79
|
|
|
61
80
|
## Accessibility
|
|
62
81
|
Native range inputs carry their own role and value semantics. In `range` mode each handle gets an
|
|
63
82
|
`aria-label` telling the two apart, because "slider" twice would say nothing.
|
|
64
83
|
|
|
65
84
|
## Read-only and required
|
|
66
|
-
`readOnly` keeps the thumb focusable but undoes every move (`aria-readonly`). The range thumbs can no longer cross: the clamped value is written back to the native input, so the thumb stops at the other one. `slider` does not accept `aria-required`
|
|
85
|
+
`readOnly` keeps the thumb focusable but undoes every move (`aria-readonly`). The range thumbs can no longer cross: the clamped value is written back to the native input, so the thumb stops at the other one. `slider` does not accept `aria-required` - the field asterisk and the validator carry it.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oznacza tor suwaka jako przeciągany (`data-dragging`) od pierwszego ruchu wskaźnika o kilka pikseli
|
|
3
|
+
* do puszczenia. Samo kliknięcie zostawia tor bez znacznika, więc skok wartości jedzie przejściem CSS.
|
|
4
|
+
* Ta sama funkcja jest w porcie Angulara (`slider/slider-drag.ts`).
|
|
5
|
+
*/
|
|
6
|
+
export declare function kptTrackDrag(track: HTMLElement, startX: number): void;
|
|
7
|
+
/** Koniec zakresu: dolny albo górny uchwyt. */
|
|
8
|
+
export type KptRangeEnd = 'low' | 'high';
|
|
9
|
+
export interface KptRangePressOptions {
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
step: number;
|
|
13
|
+
low: number;
|
|
14
|
+
high: number;
|
|
15
|
+
/** Ustawia wybrany koniec. Pilnowanie, by uchwyty się nie minęły, zostaje w komponencie. */
|
|
16
|
+
set: (end: KptRangeEnd, value: number) => void;
|
|
17
|
+
/** Puszczenie wskaźnika. */
|
|
18
|
+
release?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wciśnięcie toru zakresu poza uchwytem. Najbliższy uchwyt skacze w to miejsce (wewnątrz zakresu
|
|
22
|
+
* też - ten bliższy), dostaje fokus, żeby dalej działały strzałki, i idzie za wskaźnikiem aż do
|
|
23
|
+
* puszczenia. Natywne suwaki tego nie zrobią: przy dwóch nałożonych inputach wskaźnik łapią tylko
|
|
24
|
+
* kciuki, a kliknięcie w tor trafiłoby w jeden z nich zawsze, niezależnie od odległości.
|
|
25
|
+
*/
|
|
26
|
+
export declare function kptRangePress(track: HTMLElement, event: PointerEvent, opts: KptRangePressOptions): void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oznacza tor suwaka jako przeciągany (`data-dragging`) od pierwszego ruchu wskaźnika o kilka pikseli
|
|
3
|
+
* do puszczenia. Samo kliknięcie zostawia tor bez znacznika, więc skok wartości jedzie przejściem CSS.
|
|
4
|
+
* Ta sama funkcja jest w porcie Angulara (`slider/slider-drag.ts`).
|
|
5
|
+
*/
|
|
6
|
+
export function kptTrackDrag(track, startX) {
|
|
7
|
+
const move = (e) => {
|
|
8
|
+
if (Math.abs(e.clientX - startX) >= 3)
|
|
9
|
+
track.setAttribute('data-dragging', '');
|
|
10
|
+
};
|
|
11
|
+
const up = () => {
|
|
12
|
+
window.removeEventListener('pointermove', move);
|
|
13
|
+
window.removeEventListener('pointerup', up);
|
|
14
|
+
window.removeEventListener('pointercancel', up);
|
|
15
|
+
track.removeAttribute('data-dragging');
|
|
16
|
+
};
|
|
17
|
+
window.addEventListener('pointermove', move);
|
|
18
|
+
window.addEventListener('pointerup', up);
|
|
19
|
+
window.addEventListener('pointercancel', up);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Wciśnięcie toru zakresu poza uchwytem. Najbliższy uchwyt skacze w to miejsce (wewnątrz zakresu
|
|
23
|
+
* też - ten bliższy), dostaje fokus, żeby dalej działały strzałki, i idzie za wskaźnikiem aż do
|
|
24
|
+
* puszczenia. Natywne suwaki tego nie zrobią: przy dwóch nałożonych inputach wskaźnik łapią tylko
|
|
25
|
+
* kciuki, a kliknięcie w tor trafiłoby w jeden z nich zawsze, niezależnie od odległości.
|
|
26
|
+
*/
|
|
27
|
+
export function kptRangePress(track, event, opts) {
|
|
28
|
+
if (event.button !== 0)
|
|
29
|
+
return;
|
|
30
|
+
// Bez tego przeglądarka zaznacza tekst w trakcie ciągnięcia i zabiera fokus z uchwytu.
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
const first = rangeValueAt(track, event.clientX, opts);
|
|
33
|
+
const end = first < opts.low ? 'low' : first > opts.high ? 'high' : first - opts.low <= opts.high - first ? 'low' : 'high';
|
|
34
|
+
let last = first;
|
|
35
|
+
opts.set(end, first);
|
|
36
|
+
track.querySelector(`.kpt-slider__thumb--${end}`)?.focus({ preventScroll: true });
|
|
37
|
+
track.setAttribute('data-pressed', end);
|
|
38
|
+
const move = (e) => {
|
|
39
|
+
const next = rangeValueAt(track, e.clientX, opts);
|
|
40
|
+
if (next === last)
|
|
41
|
+
return;
|
|
42
|
+
last = next;
|
|
43
|
+
opts.set(end, next);
|
|
44
|
+
};
|
|
45
|
+
const up = () => {
|
|
46
|
+
window.removeEventListener('pointermove', move);
|
|
47
|
+
window.removeEventListener('pointerup', up);
|
|
48
|
+
window.removeEventListener('pointercancel', up);
|
|
49
|
+
track.removeAttribute('data-pressed');
|
|
50
|
+
opts.release?.();
|
|
51
|
+
};
|
|
52
|
+
window.addEventListener('pointermove', move);
|
|
53
|
+
window.addEventListener('pointerup', up);
|
|
54
|
+
window.addEventListener('pointercancel', up);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Wartość pod wskaźnikiem, przyciągnięta do kroku tak jak natywny suwak. Środek uchwytu jeździ
|
|
58
|
+
* od połowy jego szerokości do szerokości toru minus połowa - stąd odjęcie szerokości uchwytu.
|
|
59
|
+
*/
|
|
60
|
+
function rangeValueAt(track, clientX, { min, max, step }) {
|
|
61
|
+
const rect = track.getBoundingClientRect();
|
|
62
|
+
const thumb = track.querySelector('.kpt-slider__knob')?.offsetWidth ?? 0;
|
|
63
|
+
const usable = rect.width - thumb;
|
|
64
|
+
let frac = usable > 0 ? (clientX - rect.left - thumb / 2) / usable : 0;
|
|
65
|
+
frac = Math.min(1, Math.max(0, frac));
|
|
66
|
+
if (getComputedStyle(track).direction === 'rtl')
|
|
67
|
+
frac = 1 - frac;
|
|
68
|
+
if (max <= min)
|
|
69
|
+
return min;
|
|
70
|
+
if (!(step > 0))
|
|
71
|
+
return min + frac * (max - min);
|
|
72
|
+
let steps = Math.round((frac * (max - min)) / step);
|
|
73
|
+
// Jak natywny suwak: ostatni krok nie wychodzi za `max`, gdy zakres nie dzieli się przez krok.
|
|
74
|
+
if (min + steps * step > max)
|
|
75
|
+
steps -= 1;
|
|
76
|
+
// Ułamkowy krok (0.1) zostawia ogon zmiennoprzecinkowy - przycięcie do liczby miejsc kroku.
|
|
77
|
+
const decimals = String(step).split('.')[1]?.length ?? 0;
|
|
78
|
+
return Number((min + steps * step).toFixed(decimals));
|
|
79
|
+
}
|
package/dist/slider/slider.d.ts
CHANGED
|
@@ -13,24 +13,25 @@ export interface KptSliderProps extends Omit<ComponentPropsWithRef<'div'>, 'onCh
|
|
|
13
13
|
step?: number;
|
|
14
14
|
/** Pokazuje bieżącą wartość obok suwaka. */
|
|
15
15
|
showValue?: boolean;
|
|
16
|
-
/** Jednostka za liczbą (
|
|
16
|
+
/** Jednostka za liczbą (" zł", "%", " km") - w pokazanej wartości i w `aria-valuetext`. */
|
|
17
17
|
suffix?: string;
|
|
18
18
|
invalid?: boolean;
|
|
19
19
|
touched?: boolean;
|
|
20
|
-
/** Id elementu fokusowalnego (nie hosta); bez niego generowane, w `KptFormField`
|
|
20
|
+
/** Id elementu fokusowalnego (nie hosta); bez niego generowane, w `KptFormField` - z wrappera. */
|
|
21
21
|
id?: string;
|
|
22
22
|
/** Nazwa dostępna, gdy kontrolki nie opisuje etykieta (`<label for>` albo `KptFormField`). */
|
|
23
23
|
ariaLabel?: string | null;
|
|
24
24
|
disabled?: boolean;
|
|
25
|
-
/** Tylko do odczytu
|
|
25
|
+
/** Tylko do odczytu - uchwyt można skupić, ale nie przesunąć (`aria-readonly`). */
|
|
26
26
|
readOnly?: boolean;
|
|
27
27
|
onTouch?: () => void;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Suwak wartości. W trybie `range` udostępnia dwa uchwyty, a wartością jest para `[min, max]`.
|
|
31
31
|
*
|
|
32
|
-
* Oba uchwyty to natywne `<input type="range">` leżące na sobie
|
|
33
|
-
*
|
|
32
|
+
* Oba uchwyty to natywne `<input type="range">` leżące na sobie - klawiatura, gesty dotykowe
|
|
33
|
+
* i czytniki ekranu działają bez własnej obsługi. Własna jest tylko jedna rzecz: wciśnięcie toru
|
|
34
|
+
* obok kciuków przesuwa najbliższy uchwyt (`kptRangePress`).
|
|
34
35
|
*
|
|
35
36
|
* @example
|
|
36
37
|
* <KptSlider value={volume} onValueChange={setVolume} min={0} max={100} step={5} showValue />
|