@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
4
|
import { cn } from "../internal/cn.js";
|
|
@@ -7,21 +8,37 @@ import { useControllableState } from "../internal/use-controllable-state.js";
|
|
|
7
8
|
import { foldMentionText } from '@konce-pt/mention';
|
|
8
9
|
import { joinIds, useKptFieldA11y } from "../form-field/form-field-context.js";
|
|
9
10
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
11
|
+
import { KptSpeechButton } from "../speech/speech-button.js";
|
|
12
|
+
import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
|
|
10
13
|
/**
|
|
11
|
-
* Pole z podpowiedziami. Wartością jest wpisany tekst, nie wybrana opcja
|
|
14
|
+
* Pole z podpowiedziami. Wartością jest wpisany tekst, nie wybrana opcja - podpowiedzi
|
|
12
15
|
* skracają pisanie, ale nie ograniczają wpisu, więc pole nadaje się też do wartości spoza listy.
|
|
13
16
|
*
|
|
14
|
-
* Wzorzec WAI-ARIA
|
|
15
|
-
* `aria-activedescendant`, a liczbę podpowiedzi (albo ich brak)
|
|
16
|
-
* pomija wielkość liter i diakrytyki (
|
|
17
|
+
* Wzorzec WAI-ARIA "combobox z listą": fokus zostaje w polu, aktywną podpowiedź ogłasza
|
|
18
|
+
* `aria-activedescendant`, a liczbę podpowiedzi (albo ich brak) - region `polite`. Dopasowanie
|
|
19
|
+
* pomija wielkość liter i diakrytyki ("lodz" znajduje "Łódź").
|
|
17
20
|
*
|
|
18
21
|
* @example
|
|
19
|
-
* <KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="Miasto
|
|
22
|
+
* <KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="Miasto..." />
|
|
20
23
|
*/
|
|
21
|
-
export function KptAutocomplete({ options = [], value, defaultValue = '', onValueChange, invalid = false, touched, onTouch, disabled = false, className, onBlur, onFocus, onKeyDown, ref, id, 'aria-describedby': ariaDescribedBy, locale, dictionary, ...rest }) {
|
|
24
|
+
export function KptAutocomplete({ options = [], value, defaultValue = '', onValueChange, invalid = false, touched, onTouch, disabled = false, readOnly = false, className, onBlur, onFocus, onKeyDown, ref, id, 'aria-describedby': ariaDescribedBy, locale, dictionary, speech = false, speechLang, speechColor, speechMode = 'replace', ...rest }) {
|
|
22
25
|
const { t } = useKptMessages({ locale, dictionary });
|
|
23
26
|
const field = useKptFieldA11y(id, 'kpt-autocomplete');
|
|
24
27
|
const inputRef = useRef(null);
|
|
28
|
+
/** Kotwica nakładki w stanie (callback ref), nie `inputRef.current` czytane w renderze. */
|
|
29
|
+
const [anchor, setAnchor] = useState(null);
|
|
30
|
+
const setRefs = useMemo(() => composeRefs(inputRef, ref, setAnchor), [ref]);
|
|
31
|
+
// Podyktowana wartość wchodzi przez `onChange` pola, więc lista otwiera się jak po wpisaniu;
|
|
32
|
+
// słowa tymczasowe nie filtrują - nie ma ich w stanie. Bez `speech` kontroler nie tworzy sesji.
|
|
33
|
+
const mic = useKptSpeechRecognition({
|
|
34
|
+
enabled: speech,
|
|
35
|
+
target: inputRef,
|
|
36
|
+
lang: speechLang,
|
|
37
|
+
mode: speechMode,
|
|
38
|
+
continuous: speechMode !== 'replace',
|
|
39
|
+
disabled: disabled || readOnly,
|
|
40
|
+
});
|
|
41
|
+
const speechState = speech && mic.available ? (mic.listening ? 'listening' : 'idle') : undefined;
|
|
25
42
|
const [current, setCurrent] = useControllableState({
|
|
26
43
|
value,
|
|
27
44
|
defaultValue,
|
|
@@ -29,7 +46,7 @@ export function KptAutocomplete({ options = [], value, defaultValue = '', onValu
|
|
|
29
46
|
});
|
|
30
47
|
const [open, setOpen] = useState(false);
|
|
31
48
|
const [activeIndex, setActiveIndex] = useState(-1);
|
|
32
|
-
/** Wpisano coś od ostatniego otwarcia
|
|
49
|
+
/** Wpisano coś od ostatniego otwarcia - dopiero wtedy ogłaszamy liczbę podpowiedzi. */
|
|
33
50
|
const [typed, setTyped] = useState(false);
|
|
34
51
|
const showInvalid = invalid && (touched ?? true);
|
|
35
52
|
const filtered = useMemo(() => {
|
|
@@ -55,6 +72,9 @@ export function KptAutocomplete({ options = [], value, defaultValue = '', onValu
|
|
|
55
72
|
}, [setCurrent]);
|
|
56
73
|
const handleKeyDown = (event) => {
|
|
57
74
|
onKeyDown?.(event);
|
|
75
|
+
// Tylko do odczytu: podpowiedzi się nie otwierają, a Escape nie czyści wartości.
|
|
76
|
+
if (readOnly)
|
|
77
|
+
return;
|
|
58
78
|
if (event.key === 'ArrowDown') {
|
|
59
79
|
event.preventDefault();
|
|
60
80
|
setOpen(true);
|
|
@@ -63,7 +83,7 @@ export function KptAutocomplete({ options = [], value, defaultValue = '', onValu
|
|
|
63
83
|
else if (event.key === 'ArrowUp') {
|
|
64
84
|
event.preventDefault();
|
|
65
85
|
setOpen(true);
|
|
66
|
-
// Z pola (bez aktywnej)
|
|
86
|
+
// Z pola (bez aktywnej) `ArrowUp` idzie na ostatnią podpowiedź.
|
|
67
87
|
setActiveIndex((index) => (index <= 0 ? filtered.length - 1 : index - 1));
|
|
68
88
|
}
|
|
69
89
|
else if (event.key === 'Enter') {
|
|
@@ -86,20 +106,21 @@ export function KptAutocomplete({ options = [], value, defaultValue = '', onValu
|
|
|
86
106
|
}
|
|
87
107
|
}
|
|
88
108
|
};
|
|
89
|
-
return (_jsxs("span", { className: "kpt-autocomplete-host", children: [_jsx("input", { ref:
|
|
109
|
+
return (_jsxs("span", { className: "kpt-autocomplete-host", "data-speech": speechState, children: [_jsx("input", { ref: setRefs, className: cn('kpt-autocomplete__input', className), type: "text", id: field.id, "aria-describedby": joinIds(ariaDescribedBy, field.describedBy), role: "combobox", "aria-autocomplete": "list", autoComplete: "off", "aria-expanded": panelOpen, "aria-controls": panelOpen ? listId : undefined, "aria-activedescendant": activeOptionId, "aria-invalid": showInvalid || undefined, "aria-readonly": readOnly || undefined, disabled: disabled, readOnly: readOnly, value: current, onChange: (event) => {
|
|
90
110
|
setCurrent(event.target.value);
|
|
91
111
|
setOpen(true);
|
|
92
112
|
setTyped(true);
|
|
93
113
|
setActiveIndex(-1);
|
|
94
114
|
}, onFocus: (event) => {
|
|
95
115
|
onFocus?.(event);
|
|
96
|
-
|
|
116
|
+
if (!readOnly)
|
|
117
|
+
setOpen(true);
|
|
97
118
|
}, onBlur: (event) => {
|
|
98
119
|
onBlur?.(event);
|
|
99
120
|
setOpen(false);
|
|
100
121
|
setTyped(false);
|
|
101
122
|
onTouch?.();
|
|
102
|
-
}, onKeyDown: handleKeyDown, ...rest }), _jsx(KptOverlay, { open: panelOpen, anchor:
|
|
123
|
+
}, onKeyDown: handleKeyDown, ...rest }), speech ? (_jsx(KptSpeechButton, { listeningColor: speechColor, speech: mic, size: "sm", unsupported: "hide", disabled: disabled || readOnly, hostProps: { className: 'kpt-autocomplete__speech' } })) : null, _jsx(KptOverlay, { open: panelOpen, anchor: anchor, onClose: () => setOpen(false), matchAnchorWidth: true, withBackdrop: false, children: _jsx("ul", { className: "kpt-autocomplete__panel", role: "listbox", id: listId, "aria-label": rest['aria-label'], children: filtered.map((option, index) => (_jsx("li", { id: optionId(index), className: cn('kpt-autocomplete__option', index === activeIndex && 'is-active'), role: "option", "aria-selected": index === activeIndex,
|
|
103
124
|
// `mousedown` z zablokowanym domyślnym zachowaniem: klik w opcję nie może
|
|
104
125
|
// zabrać fokusu polu, bo `blur` zamknąłby panel przed wyborem.
|
|
105
126
|
onMouseDown: (event) => {
|
|
@@ -12,14 +12,14 @@ A text field with suggestions. Import: `import { KptAutocomplete } from '@konce-
|
|
|
12
12
|
</ul>
|
|
13
13
|
|
|
14
14
|
## Props
|
|
15
|
-
- `options`: string[]
|
|
16
|
-
- `value` / `defaultValue` / `onValueChange`
|
|
15
|
+
- `options`: string[] - the suggestions
|
|
16
|
+
- `value` / `defaultValue` / `onValueChange` - the typed text
|
|
17
17
|
- `invalid`, `touched`, `onTouch()`
|
|
18
18
|
- `className`, `ref` and every other `<input>` prop go to the input
|
|
19
19
|
|
|
20
20
|
## The value is the text, not the option
|
|
21
21
|
Whatever is typed becomes the value; the suggestions only shorten the typing. That makes the field
|
|
22
|
-
usable for values outside the list
|
|
22
|
+
usable for values outside the list - a city nobody put in `options` still submits. When the value
|
|
23
23
|
must come from the list, reach for `KptSelect` instead.
|
|
24
24
|
|
|
25
25
|
## No backdrop
|
|
@@ -33,13 +33,13 @@ ArrowDown opens the list and moves the active option, ArrowUp moves back, Enter
|
|
|
33
33
|
one, Escape closes.
|
|
34
34
|
|
|
35
35
|
## Examples
|
|
36
|
-
<KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="City
|
|
36
|
+
<KptAutocomplete options={cities} value={city} onValueChange={setCity} placeholder="City..." />
|
|
37
37
|
|
|
38
38
|
## Screen reader and keyboard
|
|
39
39
|
The WAI-ARIA combobox-with-list pattern: focus stays in the field, `aria-autocomplete="list"`,
|
|
40
40
|
`aria-controls` and `aria-activedescendant` point at the active suggestion, and a polite live region
|
|
41
41
|
says how many suggestions there are (`autocomplete.results`) or that there are none
|
|
42
|
-
(`autocomplete.noResults`). Matching ignores case and diacritics ("lodz" finds "Łódź").
|
|
42
|
+
(`autocomplete.noResults`). Matching ignores case and diacritics ("lodz" finds "Łódź"). `ArrowUp` from the
|
|
43
43
|
field goes to the last suggestion; the first `Escape` closes the list, the second clears the field.
|
|
44
44
|
|
|
45
45
|
## Label and description
|
|
@@ -54,3 +54,17 @@ added to `aria-describedby` (merged with your own). Without a wrapper, name it w
|
|
|
54
54
|
## Accessibility
|
|
55
55
|
The input is `role="combobox"` with `aria-expanded`; the panel is `role="listbox"` with
|
|
56
56
|
`role="option"` rows carrying `aria-selected`.
|
|
57
|
+
|
|
58
|
+
## Read-only and required
|
|
59
|
+
`readOnly` is the native read-only field with `aria-readonly`; the suggestions do not open and Escape does not clear the value.
|
|
60
|
+
|
|
61
|
+
## Dictation
|
|
62
|
+
- `speech`: boolean = false - a microphone at the end of the field (`KptSpeechButton`). Hidden without
|
|
63
|
+
browser support.
|
|
64
|
+
- `speechLang`: string - BCP 47 tag; without it from `KptI18nProvider`.
|
|
65
|
+
- `speechColor`: `'primary' | 'danger' | 'success' | 'warning' | 'info'` = `'primary'` - colour of "stop" and the wave while listening.
|
|
66
|
+
- `speechMode`: `'insert' | 'append' | 'replace'` = `'replace'` - the field picks one value, so a click
|
|
67
|
+
clears it and one utterance replaces it; the suggestion list opens for the spoken text like for typed
|
|
68
|
+
text. Interim words are visible but do not filter the list - only final text reaches the state.
|
|
69
|
+
|
|
70
|
+
<KptAutocomplete options={cities} value={city} onValueChange={setCity} speech />
|
package/dist/avatar/avatar.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type KptAvatarShape = 'circle' | 'square';
|
|
|
5
5
|
export interface KptAvatarProps extends Omit<ComponentPropsWithRef<'span'>, 'children'> {
|
|
6
6
|
/** Adres obrazu; ma pierwszeństwo przed inicjałami i ikoną. */
|
|
7
7
|
src?: string | null;
|
|
8
|
-
/** Imię i nazwisko
|
|
8
|
+
/** Imię i nazwisko - źródło inicjałów, `title` oraz etykiety dostępności. */
|
|
9
9
|
name?: string;
|
|
10
10
|
/** Nazwa ikony z rejestru, gdy nie ma obrazu ani nazwy. */
|
|
11
11
|
icon?: string | null;
|
|
@@ -15,17 +15,17 @@ export interface KptAvatarProps extends Omit<ComponentPropsWithRef<'span'>, 'chi
|
|
|
15
15
|
decorative?: boolean;
|
|
16
16
|
/**
|
|
17
17
|
* Propsy wrappera hosta (`.kpt-avatar-host`). To on jest elementem układu, więc klasy
|
|
18
|
-
* pozycjonujące awatar w rodzicu
|
|
18
|
+
* pozycjonujące awatar w rodzicu - jak `kpt-card__avatar` - idą właśnie tutaj.
|
|
19
19
|
*/
|
|
20
20
|
hostProps?: KptHostProps<'span'>;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Awatar
|
|
23
|
+
* Awatar - obraz, inicjały (z `name`) lub ikona.
|
|
24
24
|
*
|
|
25
|
-
* `name` trafia do natywnego `title` (podpowiedź spod kursora
|
|
25
|
+
* `name` trafia do natywnego `title` (podpowiedź spod kursora - inicjały same w sobie nie mówią,
|
|
26
26
|
* kto to jest). Bez `src` awatar dostaje dodatkowo `role="img"` i `aria-label`, a same inicjały
|
|
27
|
-
* są `aria-hidden`, żeby czytnik nie przeczytał
|
|
28
|
-
* niesie już `alt` obrazu
|
|
27
|
+
* są `aria-hidden`, żeby czytnik nie przeczytał "AK" litera po literze. Przy `src` etykietę
|
|
28
|
+
* niesie już `alt` obrazu - nie dublujemy jej.
|
|
29
29
|
*
|
|
30
30
|
* @example
|
|
31
31
|
* <KptAvatar src="/u/anna.jpg" />
|
package/dist/avatar/avatar.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
3
|
import { useState } from 'react';
|
|
3
4
|
import { cn } from "../internal/cn.js";
|
|
4
5
|
import { KptIcon } from "../icon/icon.js";
|
|
5
6
|
/**
|
|
6
|
-
* Awatar
|
|
7
|
+
* Awatar - obraz, inicjały (z `name`) lub ikona.
|
|
7
8
|
*
|
|
8
|
-
* `name` trafia do natywnego `title` (podpowiedź spod kursora
|
|
9
|
+
* `name` trafia do natywnego `title` (podpowiedź spod kursora - inicjały same w sobie nie mówią,
|
|
9
10
|
* kto to jest). Bez `src` awatar dostaje dodatkowo `role="img"` i `aria-label`, a same inicjały
|
|
10
|
-
* są `aria-hidden`, żeby czytnik nie przeczytał
|
|
11
|
-
* niesie już `alt` obrazu
|
|
11
|
+
* są `aria-hidden`, żeby czytnik nie przeczytał "AK" litera po literze. Przy `src` etykietę
|
|
12
|
+
* niesie już `alt` obrazu - nie dublujemy jej.
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
14
15
|
* <KptAvatar src="/u/anna.jpg" />
|
|
@@ -17,7 +18,7 @@ import { KptIcon } from "../icon/icon.js";
|
|
|
17
18
|
*/
|
|
18
19
|
export function KptAvatar({ src = null, name = '', icon = null, size = 'md', shape = 'circle', decorative = false, hostProps, className, ...rest }) {
|
|
19
20
|
const { className: hostClassName, ...hostRest } = hostProps ?? {};
|
|
20
|
-
// Zdjęcie, które się nie wczytało
|
|
21
|
+
// Zdjęcie, które się nie wczytało - awatar wraca wtedy do inicjałów (albo ikony). Pamiętamy,
|
|
21
22
|
// którego `src` dotyczy błąd, więc nowy adres próbuje się wczytać od nowa.
|
|
22
23
|
const [brokenSrc, setBrokenSrc] = useState(null);
|
|
23
24
|
const showImage = !!src && brokenSrc !== src;
|
package/dist/avatar/llms.txt
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
# KptAvatar (@konce-pt/react)
|
|
2
2
|
|
|
3
|
-
An avatar
|
|
3
|
+
An avatar - a photo, initials (from `name`) or an icon.
|
|
4
4
|
Import: `import { KptAvatar } from '@konce-pt/react';`
|
|
5
5
|
|
|
6
6
|
## DOM
|
|
7
7
|
<span class="kpt-avatar-host">
|
|
8
8
|
<span class="kpt-avatar" data-size="md" data-shape="circle" title="Anna Kowalska">
|
|
9
|
-
<img class="kpt-avatar__img" src="
|
|
9
|
+
<img class="kpt-avatar__img" src="..." alt="Anna Kowalska">
|
|
10
10
|
</span>
|
|
11
11
|
</span>
|
|
12
12
|
|
|
13
13
|
## Props
|
|
14
|
-
- `src`: string | null
|
|
15
|
-
- `name`: string
|
|
16
|
-
- `icon`: string | null
|
|
14
|
+
- `src`: string | null - a photo; it wins over initials and the icon
|
|
15
|
+
- `name`: string - the source of the initials, the `title` and the accessible label
|
|
16
|
+
- `icon`: string | null - a registry icon name, used when there is neither photo nor name
|
|
17
17
|
- `size`: 'sm' | 'md' | 'lg' | 'xl' (default 'md')
|
|
18
18
|
- `shape`: 'circle' | 'square' (default 'circle')
|
|
19
|
-
- `decorative`: boolean
|
|
20
|
-
- `hostProps`
|
|
19
|
+
- `decorative`: boolean - hides the avatar from screen readers (the name is written next to it)
|
|
20
|
+
- `hostProps` - props for the `.kpt-avatar-host` wrapper
|
|
21
21
|
- `className`, `ref` and every other `<span>` prop go to the inner `.kpt-avatar`
|
|
22
22
|
|
|
23
23
|
## Fallback order
|
|
24
|
-
`src`
|
|
24
|
+
`src` -> `name` (up to two initials, uppercased) -> `icon`. A photo that fails to load drops to the
|
|
25
25
|
next step by itself, and a new `src` is tried again. With none of them the avatar renders as an
|
|
26
26
|
empty shape, which is a valid placeholder.
|
|
27
27
|
|
|
28
28
|
## Positioning inside a parent
|
|
29
|
-
The host is the layout element, so classes that place the avatar in its parent go to `hostProps`
|
|
29
|
+
The host is the layout element, so classes that place the avatar in its parent go to `hostProps` -
|
|
30
30
|
the overlapping avatar on a card is `hostProps={{ className: 'kpt-card__avatar' }}`.
|
|
31
31
|
|
|
32
32
|
## Examples
|
|
@@ -42,5 +42,5 @@ the overlapping avatar on a card is `hostProps={{ className: 'kpt-card__avatar'
|
|
|
42
42
|
`name` goes into the native `title` (initials alone say nothing). With a photo the name is the
|
|
43
43
|
image's `alt`; without one the avatar is `role="img"` with `aria-label`, and the initials are
|
|
44
44
|
`aria-hidden`. A photo that fails to load falls back to the initials (or the icon); a new `src` is
|
|
45
|
-
tried again. `decorative` removes the avatar from the accessibility tree
|
|
45
|
+
tried again. `decorative` removes the avatar from the accessibility tree - use it when the name is
|
|
46
46
|
already written next to it.
|
|
@@ -8,15 +8,15 @@ export interface KptAvatarItem {
|
|
|
8
8
|
}
|
|
9
9
|
export interface KptAvatarGroupProps extends ComponentPropsWithRef<'div'>, KptI18nOverrides {
|
|
10
10
|
avatars?: readonly KptAvatarItem[];
|
|
11
|
-
/** Ile awatarów pokazać, zanim pojawi się
|
|
11
|
+
/** Ile awatarów pokazać, zanim pojawi się "+N". */
|
|
12
12
|
max?: number;
|
|
13
13
|
size?: KptAvatarSize;
|
|
14
|
-
/** Nazwa grupy dla czytników ekranu, np.
|
|
14
|
+
/** Nazwa grupy dla czytników ekranu, np. "Członkowie projektu"; bez niej grupa nie ma roli. */
|
|
15
15
|
label?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Grupa nakładających się awatarów z licznikiem
|
|
19
|
-
* dziedziczy jego `title` z imieniem
|
|
18
|
+
* Grupa nakładających się awatarów z licznikiem "+N". Buduje na `KptAvatar`, więc każdy element
|
|
19
|
+
* dziedziczy jego `title` z imieniem - inicjały same w sobie nie mówią, kto to jest.
|
|
20
20
|
*
|
|
21
21
|
* @example
|
|
22
22
|
* <KptAvatarGroup avatars={team} max={4} />
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { cn } from "../internal/cn.js";
|
|
3
4
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
4
5
|
import { KptAvatar } from "../avatar/avatar.js";
|
|
5
6
|
/**
|
|
6
|
-
* Grupa nakładających się awatarów z licznikiem
|
|
7
|
-
* dziedziczy jego `title` z imieniem
|
|
7
|
+
* Grupa nakładających się awatarów z licznikiem "+N". Buduje na `KptAvatar`, więc każdy element
|
|
8
|
+
* dziedziczy jego `title` z imieniem - inicjały same w sobie nie mówią, kto to jest.
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
10
11
|
* <KptAvatarGroup avatars={team} max={4} />
|
|
@@ -13,7 +14,7 @@ export function KptAvatarGroup({ avatars = [], max = 4, size = 'md', label = '',
|
|
|
13
14
|
const { t } = useKptMessages({ locale, dictionary });
|
|
14
15
|
const shown = avatars.slice(0, max);
|
|
15
16
|
const overflow = Math.max(0, avatars.length - max);
|
|
16
|
-
// Sam
|
|
17
|
+
// Sam "+2" nic czytnikowi nie mówi - licznik ma nazwę "i 2 więcej" i podpowiedź z imionami.
|
|
17
18
|
const hiddenNames = avatars
|
|
18
19
|
.slice(max)
|
|
19
20
|
.map((avatar) => avatar.name)
|
|
@@ -6,22 +6,22 @@ Import: `import { KptAvatarGroup } from '@konce-pt/react';`
|
|
|
6
6
|
## DOM
|
|
7
7
|
<div class="kpt-avatar-group-host">
|
|
8
8
|
<div class="kpt-avatar-group" data-size="lg" role="group" aria-label="Project members">
|
|
9
|
-
<span class="kpt-avatar-group__item"
|
|
9
|
+
<span class="kpt-avatar-group__item">...KptAvatar...</span>...
|
|
10
10
|
<span class="kpt-avatar-group__item kpt-avatar-group__more" role="img" data-size="lg"
|
|
11
11
|
aria-label="2 more" title="Marta, Tomasz"><span aria-hidden="true">+2</span></span>
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
15
|
## Props
|
|
16
|
-
- `avatars`: `KptAvatarItem[]`
|
|
17
|
-
- `max`: number (default 4)
|
|
16
|
+
- `avatars`: `KptAvatarItem[]` - `{ name?, src?, icon? }`
|
|
17
|
+
- `max`: number (default 4) - how many to show before "+N"
|
|
18
18
|
- `size`: `'sm' | 'md' | 'lg' | 'xl'`
|
|
19
|
-
- `label`: string
|
|
19
|
+
- `label`: string - the group's name; with it the row is `role="group"`
|
|
20
20
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
21
21
|
|
|
22
22
|
## One list, three fallbacks
|
|
23
|
-
Each entry falls back in order: `src` (a photo)
|
|
24
|
-
some people have a photo therefore needs no branching at the call site
|
|
23
|
+
Each entry falls back in order: `src` (a photo) -> `name` (initials) -> `icon`. A team where only
|
|
24
|
+
some people have a photo therefore needs no branching at the call site - which is exactly the
|
|
25
25
|
common case.
|
|
26
26
|
|
|
27
27
|
## The name survives the overlap
|
|
@@ -36,6 +36,6 @@ Every avatar keeps the `title` from `KptAvatar`, so hovering tells you who it is
|
|
|
36
36
|
The overlap ring uses `--kpt-color-surface`, the "+N" chip `--kpt-color-muted`.
|
|
37
37
|
|
|
38
38
|
## Accessibility
|
|
39
|
-
Every avatar keeps its own name. With `label` the row is `role="group"` with that name. The
|
|
40
|
-
counter is `role="img"` named from `avatarGroup.more` (`{count}`
|
|
39
|
+
Every avatar keeps its own name. With `label` the row is `role="group"` with that name. The "+N"
|
|
40
|
+
counter is `role="img"` named from `avatarGroup.more` (`{count}` - "2 more"), with the hidden names
|
|
41
41
|
in its `title`. The overlap uses `margin-inline-start`, so it mirrors in RTL.
|
|
@@ -8,22 +8,22 @@ export interface KptBackdropProps extends ComponentPropsWithRef<'div'> {
|
|
|
8
8
|
background?: string;
|
|
9
9
|
/** Tempo; 1 to spokojne tempo obu efektów, 0 zatrzymuje obraz. */
|
|
10
10
|
speed?: number;
|
|
11
|
-
/** Ziarno losowania
|
|
11
|
+
/** Ziarno losowania - ta sama wartość daje ten sam układ kwadratów i ten sam przebieg wstęgi. */
|
|
12
12
|
seed?: number;
|
|
13
13
|
/** Zatrzymuje animację, zostawiając ostatnią klatkę. */
|
|
14
14
|
paused?: boolean;
|
|
15
15
|
/**
|
|
16
16
|
* Przenosi barwy wpisane na sztywno na powierzchnię bieżącego motywu: czerń dobrana do białego
|
|
17
|
-
* tła staje się bielą, gdy tło jest ciemne. Rusza wyłącznie jasność
|
|
17
|
+
* tła staje się bielą, gdy tło jest ciemne. Rusza wyłącznie jasność - odcień i nasycenie
|
|
18
18
|
* zostają, więc barwa marki nie zmienia się w inną barwę.
|
|
19
19
|
*
|
|
20
20
|
* Dotyczy `color`, `colors` i `background`, ale tylko wtedy, gdy podano je literałem. Prop pusty
|
|
21
|
-
* bierze token, a zapis z `var()` sam po token sięga
|
|
21
|
+
* bierze token, a zapis z `var()` sam po token sięga - jedno i drugie już idzie za motywem
|
|
22
22
|
* i adaptacja odwróciłaby je drugi raz.
|
|
23
23
|
*/
|
|
24
24
|
adaptive?: boolean;
|
|
25
25
|
/**
|
|
26
|
-
* `signal-grid`: kolor kwadratów
|
|
26
|
+
* `signal-grid`: kolor kwadratów - dowolny zapis CSS, także sięgający po tokeny, np.
|
|
27
27
|
* `color-mix(in oklab, #5b8af2, var(--kpt-color-surface) 30%)`. Taki zapis przelicza się przy
|
|
28
28
|
* zmianie motywu; literał zostaje literałem. Puste bierze `--kpt-color-primary`.
|
|
29
29
|
*/
|
|
@@ -34,14 +34,14 @@ export interface KptBackdropProps extends ComponentPropsWithRef<'div'> {
|
|
|
34
34
|
cell?: number;
|
|
35
35
|
/** `signal-grid`: odstęp między kwadratami (px). */
|
|
36
36
|
gap?: number;
|
|
37
|
-
/** `signal-grid`: udział zapalonych komórek przy kotwicy, 0
|
|
37
|
+
/** `signal-grid`: udział zapalonych komórek przy kotwicy, 0-1. */
|
|
38
38
|
density?: number;
|
|
39
39
|
/** `signal-grid`: jak szybko siatka gaśnie ku przeciwnej krawędzi. */
|
|
40
40
|
falloff?: number;
|
|
41
41
|
/**
|
|
42
|
-
* `wave`: rampa barw, po której przesuwa się wstęga
|
|
42
|
+
* `wave`: rampa barw, po której przesuwa się wstęga - każdy przystanek dowolnym zapisem CSS.
|
|
43
43
|
* Zamknięta w pętlę, więc ostatnia barwa wraca do pierwszej. Pusta bierze `--kpt-color-primary`
|
|
44
|
-
* i dwie barwy wyprowadzone z niego obrotem odcienia
|
|
44
|
+
* i dwie barwy wyprowadzone z niego obrotem odcienia - paleta biblioteki jest neutralna, więc
|
|
45
45
|
* domyślna wstęga jest jednobarwna i przygaszona. Po kolory sięga się tu, nie do tokenów
|
|
46
46
|
* statusowych. Zapis może sięgać po tokeny i wtedy idzie za motywem. Przystanek, którego
|
|
47
47
|
* przeglądarka nie uznaje za kolor, wypada z rampy; gdy wypadną wszystkie, zostaje rampa
|
|
@@ -57,23 +57,4 @@ export interface KptBackdropProps extends ComponentPropsWithRef<'div'> {
|
|
|
57
57
|
/** Propsy warstwy z treścią (`.kpt-backdrop__content`). */
|
|
58
58
|
contentProps?: ComponentPropsWithRef<'div'>;
|
|
59
59
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Dekoracyjna warstwa tła dla sekcji: komponent obejmuje treść i maluje pod nią animowany canvas.
|
|
62
|
-
* Sięga się po niego tam, gdzie płaska powierzchnia wygląda na niedokończoną, a wideo albo duży
|
|
63
|
-
* obraz kosztowałyby transfer i nie poszłyby za motywem.
|
|
64
|
-
*
|
|
65
|
-
* Komponent jest opakowaniem: canvas leży pod spodem, a dzieci idą warstwę wyżej i zachowują się
|
|
66
|
-
* zwyczajnie. Rysowanie stoi, gdy nie ma czego oglądać — sekcja poza ekranem, karta w tle,
|
|
67
|
-
* `paused`, `speed` równe zeru albo ograniczony ruch w systemie; wtedy zostaje jedna statyczna
|
|
68
|
-
* klatka zamiast pustki, bo efekt jest częścią kompozycji, nie ozdobą.
|
|
69
|
-
*
|
|
70
|
-
* Rysowanie i cała matematyka siedzą w `@konce-pt/backdrop`, wspólnym z portem Angulara —
|
|
71
|
-
* ten komponent to sam cykl życia.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* <KptBackdrop effect="wave" rotation={18} colors={['#5b8af2', '#a86df2', '#f25b9e']}>
|
|
75
|
-
* <h1>Build your app from the first prompt</h1>
|
|
76
|
-
* <KptButton>Get started</KptButton>
|
|
77
|
-
* </KptBackdrop>
|
|
78
|
-
*/
|
|
79
60
|
export declare function KptBackdrop({ effect, background, speed, seed, paused, adaptive, color, anchor, cell, gap, density, falloff, colors, rotation, band, amplitude, contentProps, className, style, children, ...rest }: Readonly<KptBackdropProps>): import("react").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
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, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
4
|
import { KPT_BACKDROP_MAX_DPR, KPT_BACKDROP_MAX_STEP, KPT_SIGNAL_GRID_CELL, KPT_SIGNAL_GRID_DENSITY, KPT_SIGNAL_GRID_FALLOFF, KPT_SIGNAL_GRID_GAP, KPT_WAVE_AMPLITUDE, KPT_WAVE_BAND, KPT_WAVE_ROTATION, adaptBackdropValue, createBackdropRenderer, observeBackdropTheme, readBackdropInk, readBackdropLead, readBackdropSurface, resolveBackdropColors, } from '@konce-pt/backdrop';
|
|
4
5
|
import { cn } from "../internal/cn.js";
|
|
5
6
|
import { isDevBuild } from "../internal/dev.js";
|
|
@@ -10,11 +11,11 @@ const EMPTY = [];
|
|
|
10
11
|
* obraz kosztowałyby transfer i nie poszłyby za motywem.
|
|
11
12
|
*
|
|
12
13
|
* Komponent jest opakowaniem: canvas leży pod spodem, a dzieci idą warstwę wyżej i zachowują się
|
|
13
|
-
* zwyczajnie. Rysowanie stoi, gdy nie ma czego oglądać
|
|
14
|
+
* zwyczajnie. Rysowanie stoi, gdy nie ma czego oglądać - sekcja poza ekranem, karta w tle,
|
|
14
15
|
* `paused`, `speed` równe zeru albo ograniczony ruch w systemie; wtedy zostaje jedna statyczna
|
|
15
16
|
* klatka zamiast pustki, bo efekt jest częścią kompozycji, nie ozdobą.
|
|
16
17
|
*
|
|
17
|
-
* Rysowanie i cała matematyka siedzą w `@konce-pt/backdrop`, wspólnym z portem Angulara
|
|
18
|
+
* Rysowanie i cała matematyka siedzą w `@konce-pt/backdrop`, wspólnym z portem Angulara -
|
|
18
19
|
* ten komponent to sam cykl życia.
|
|
19
20
|
*
|
|
20
21
|
* @example
|
|
@@ -23,29 +24,39 @@ const EMPTY = [];
|
|
|
23
24
|
* <KptButton>Get started</KptButton>
|
|
24
25
|
* </KptBackdrop>
|
|
25
26
|
*/
|
|
27
|
+
const REDUCED_MOTION = '(prefers-reduced-motion: reduce)';
|
|
28
|
+
/** `prefers-reduced-motion` jako zewnętrzny magazyn - bez `setState` w efekcie i bez mignięcia ruchu. */
|
|
29
|
+
function subscribeReducedMotion(onChange) {
|
|
30
|
+
if (typeof matchMedia === 'undefined')
|
|
31
|
+
return () => undefined;
|
|
32
|
+
const query = matchMedia(REDUCED_MOTION);
|
|
33
|
+
query.addEventListener('change', onChange);
|
|
34
|
+
return () => query.removeEventListener('change', onChange);
|
|
35
|
+
}
|
|
36
|
+
function readReducedMotion() {
|
|
37
|
+
return typeof matchMedia !== 'undefined' && matchMedia(REDUCED_MOTION).matches;
|
|
38
|
+
}
|
|
26
39
|
export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1, seed = 0, paused = false, adaptive = false, color = '', anchor = 'right', cell = KPT_SIGNAL_GRID_CELL, gap = KPT_SIGNAL_GRID_GAP, density = KPT_SIGNAL_GRID_DENSITY, falloff = KPT_SIGNAL_GRID_FALLOFF, colors = EMPTY, rotation = KPT_WAVE_ROTATION, band = KPT_WAVE_BAND, amplitude = KPT_WAVE_AMPLITUDE, contentProps, className, style, children, ...rest }) {
|
|
27
40
|
const host = useRef(null);
|
|
28
41
|
const canvas = useRef(null);
|
|
29
42
|
const context = useRef(null);
|
|
30
43
|
const renderer = useRef(null);
|
|
31
44
|
const size = useRef({ width: 0, height: 0 });
|
|
32
|
-
/** Zegar animacji w sekundach
|
|
45
|
+
/** Zegar animacji w sekundach - w refie, żeby przebudowa renderera nie cofała obrazu. */
|
|
33
46
|
const clock = useRef(0);
|
|
34
|
-
/** Kolor wiodący, z którym zbudowany jest bieżący renderer
|
|
47
|
+
/** Kolor wiodący, z którym zbudowany jest bieżący renderer - punkt odniesienia dla zmiany motywu. */
|
|
35
48
|
const lead = useRef('');
|
|
36
|
-
/** Ostatnio zgłoszony zestaw odrzuconych barw
|
|
49
|
+
/** Ostatnio zgłoszony zestaw odrzuconych barw - żeby nie powtarzać tego samego ostrzeżenia. */
|
|
37
50
|
const reported = useRef('');
|
|
38
|
-
/** Tło po adaptacji. Puste znaczy
|
|
51
|
+
/** Tło po adaptacji. Puste znaczy "nie ma czego podmieniać" i wtedy liczy się samo `background`. */
|
|
39
52
|
const [adapted, setAdapted] = useState('');
|
|
40
53
|
/**
|
|
41
|
-
* Ton i kolor treści dobrane do malowanego tła. Puste znaczy
|
|
54
|
+
* Ton i kolor treści dobrane do malowanego tła. Puste znaczy "sekcja nie ma własnego tła"
|
|
42
55
|
* i wtedy treść zostaje przy `--kpt-color-on-surface` ze stylu.
|
|
43
56
|
*/
|
|
44
57
|
const [ink, setInk] = useState({ tone: '', color: '' });
|
|
45
|
-
const rate = useRef(speed);
|
|
46
|
-
rate.current = speed;
|
|
47
58
|
const [onScreen, setOnScreen] = useState(true);
|
|
48
|
-
const
|
|
59
|
+
const reduced = useSyncExternalStore(subscribeReducedMotion, readReducedMotion, () => false);
|
|
49
60
|
/** Licznik zmian motywu; podbicie każe przeliczyć kolory z tokenów na nowo. */
|
|
50
61
|
const [theme, setTheme] = useState(0);
|
|
51
62
|
const paint = useCallback(() => {
|
|
@@ -70,10 +81,10 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
70
81
|
// Tablica barw przychodzi nowa przy każdym renderze rodzica, więc do zależności idzie jej treść.
|
|
71
82
|
const ramp = colors.join('|');
|
|
72
83
|
const list = useMemo(() => (ramp === '' ? EMPTY : ramp.split('|')), [ramp]);
|
|
73
|
-
// Każda zmiana wejść buduje renderer od nowa
|
|
84
|
+
// Każda zmiana wejść buduje renderer od nowa - zamiast doklejać ścieżki "co się zmieniło".
|
|
74
85
|
useEffect(() => {
|
|
75
86
|
// Kolory rozwiązuje się na canvasie, nie na hoście: sonda `var()` pisze inline style, a nasłuch
|
|
76
|
-
// motywu ogląda `style` hosta i jego przodków
|
|
87
|
+
// motywu ogląda `style` hosta i jego przodków - zapis na hoście zapętliłby przebudowy.
|
|
77
88
|
const scope = canvas.current;
|
|
78
89
|
if (!scope)
|
|
79
90
|
return undefined;
|
|
@@ -82,10 +93,10 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
82
93
|
lead.current = resolved.color;
|
|
83
94
|
const painted = adaptive ? adaptBackdropValue(background, readBackdropSurface(scope)) : background;
|
|
84
95
|
setAdapted(adaptive ? painted : '');
|
|
85
|
-
// Ton liczymy z tła, które sekcja naprawdę pomaluje
|
|
96
|
+
// Ton liczymy z tła, które sekcja naprawdę pomaluje - czyli już po adaptacji.
|
|
86
97
|
setInk(readBackdropInk(scope, painted));
|
|
87
98
|
// Efekt chodzi przy każdej zmianie wejść, więc ciągnięcie suwaka nad wadliwą rampą zasypałoby
|
|
88
|
-
// konsolę tym samym zdaniem
|
|
99
|
+
// konsolę tym samym zdaniem - zgłaszamy dopiero zestaw inny niż poprzednio.
|
|
89
100
|
const rejected = resolved.invalid.join(', ');
|
|
90
101
|
if (rejected !== reported.current) {
|
|
91
102
|
reported.current = rejected;
|
|
@@ -115,7 +126,7 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
115
126
|
renderer.current = null;
|
|
116
127
|
};
|
|
117
128
|
}, [effect, color, list, anchor, cell, gap, density, falloff, rotation, band, amplitude, seed, adaptive, background, theme, measure, paint]);
|
|
118
|
-
// Sam rozmiar nie zmienia ustawień, więc renderer zostaje
|
|
129
|
+
// Sam rozmiar nie zmienia ustawień, więc renderer zostaje - przelicza tylko geometrię.
|
|
119
130
|
useEffect(() => {
|
|
120
131
|
const element = canvas.current;
|
|
121
132
|
if (!element)
|
|
@@ -146,14 +157,7 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
146
157
|
document.removeEventListener('visibilitychange', onTabSwitch);
|
|
147
158
|
};
|
|
148
159
|
}, []);
|
|
149
|
-
|
|
150
|
-
const query = matchMedia('(prefers-reduced-motion: reduce)');
|
|
151
|
-
setReduced(query.matches);
|
|
152
|
-
const onChange = (event) => setReduced(event.matches);
|
|
153
|
-
query.addEventListener('change', onChange);
|
|
154
|
-
return () => query.removeEventListener('change', onChange);
|
|
155
|
-
}, []);
|
|
156
|
-
// Zmiana motywu przestawia tokeny, a kolory renderera są już wyliczone — trzeba je odczytać
|
|
160
|
+
// Zmiana motywu przestawia tokeny, a kolory renderera są już wyliczone - trzeba je odczytać
|
|
157
161
|
// na nowo. Nasłuch trafia też w zmiany niezwiązane z barwą, więc licznik podbija się dopiero
|
|
158
162
|
// wtedy, gdy odczyt naprawdę dał inny kolor.
|
|
159
163
|
useEffect(() => {
|
|
@@ -164,7 +168,7 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
164
168
|
const scope = canvas.current;
|
|
165
169
|
if (!scope)
|
|
166
170
|
return;
|
|
167
|
-
// Ten sam rachunek co w efekcie budującym
|
|
171
|
+
// Ten sam rachunek co w efekcie budującym - inaczej strażnik porównywałby wartość sprzed
|
|
168
172
|
// adaptacji z zapamiętaną po adaptacji i podbijałby licznik bez przerwy.
|
|
169
173
|
const next = adaptive ? adaptBackdropValue(color, readBackdropSurface(scope)) : color;
|
|
170
174
|
if (readBackdropLead(scope, next) === lead.current)
|
|
@@ -180,8 +184,9 @@ export function KptBackdrop({ effect = 'signal-grid', background = '', speed = 1
|
|
|
180
184
|
const tick = (now) => {
|
|
181
185
|
const step = last === 0 ? 0 : Math.min(now - last, KPT_BACKDROP_MAX_STEP);
|
|
182
186
|
last = now;
|
|
183
|
-
// Prędkość wchodzi w zegar, nie w fazę
|
|
184
|
-
|
|
187
|
+
// Prędkość wchodzi w zegar, nie w fazę - suwak przyspiesza obraz zamiast go przeskakiwać. Pętla
|
|
188
|
+
// restartuje się przy zmianie `speed` (zależność efektu), a zegar żyje w refie, więc nic nie skacze.
|
|
189
|
+
clock.current += (step / 1000) * speed;
|
|
185
190
|
paint();
|
|
186
191
|
frame = requestAnimationFrame(tick);
|
|
187
192
|
};
|