@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,10 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
import { aggregate, isEmptyFilterValue, matchesColumnFilter, toText, } from '@konce-pt/table';
|
|
3
|
+
import { useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { aggregate, columnOffsets, createGlobalFilter, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, sortRows, toText, virtualColumnRange, virtualRange, } from '@konce-pt/table';
|
|
4
5
|
import { cn } from "../internal/cn.js";
|
|
5
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
6
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
7
8
|
import { KptButton } from "../button/button.js";
|
|
9
|
+
import { KptSpeechButton } from "../speech/speech-button.js";
|
|
10
|
+
import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
|
|
8
11
|
import { KptDatepicker } from "../datepicker/datepicker.js";
|
|
9
12
|
import { KptDateRange } from "../date-range/date-range.js";
|
|
10
13
|
import { KptIcon } from "../icon/icon.js";
|
|
@@ -17,8 +20,31 @@ import { exportTableCsv } from "./export-csv.js";
|
|
|
17
20
|
const REM_PX = 16;
|
|
18
21
|
const CHECKBOX_FROZEN_W = 3; // rem
|
|
19
22
|
const EXPANDER_FROZEN_W = 2.5; // rem
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/** Wiersze dorenderowane nad i pod widocznym oknem. */
|
|
24
|
+
const ROW_OVERSCAN = 6;
|
|
25
|
+
/** Kolumny dorenderowane po obu stronach widocznego okna. */
|
|
26
|
+
const COLUMN_OVERSCAN = 2;
|
|
27
|
+
/** Powyżej tylu wierszy filtr globalny czeka na przerwę w pisaniu, zamiast liczyć co znak. */
|
|
28
|
+
const FILTER_DEBOUNCE_ROWS = 2000;
|
|
29
|
+
const FILTER_DEBOUNCE_MS = 150;
|
|
30
|
+
/** Kawałek rozgrzewania indeksu wyszukiwania na jedno wywołanie w czasie bezczynności. */
|
|
31
|
+
const WARMUP_SLICE_MS = 8;
|
|
32
|
+
function scheduleIdle(fn) {
|
|
33
|
+
return typeof requestIdleCallback === 'function' ? { idle: requestIdleCallback(fn) } : { timer: setTimeout(fn, 16) };
|
|
34
|
+
}
|
|
35
|
+
function cancelIdle(handle) {
|
|
36
|
+
if (!handle)
|
|
37
|
+
return;
|
|
38
|
+
if ('idle' in handle)
|
|
39
|
+
cancelIdleCallback(handle.idle);
|
|
40
|
+
else
|
|
41
|
+
clearTimeout(handle.timer);
|
|
42
|
+
}
|
|
43
|
+
/** Okna równe, gdy obejmują te same elementy - przewinięcie w obrębie okna nie budzi renderu. */
|
|
44
|
+
function sameWindow(a, b) {
|
|
45
|
+
return a.start === b.start && a.end === b.end && a.before === b.before && a.after === b.after;
|
|
46
|
+
}
|
|
47
|
+
/** Edytory z panelem w nakładce - otwierane od razu i zatwierdzane na zamknięciu panelu. */
|
|
22
48
|
const OVERLAY_EDITORS = new Set(['date', 'select', 'multiselect']);
|
|
23
49
|
/** Tablice (multiselect) porównujemy po zawartości, resztę po referencji. */
|
|
24
50
|
function sameEditValue(a, b) {
|
|
@@ -28,6 +54,16 @@ function sameEditValue(a, b) {
|
|
|
28
54
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
29
55
|
return false;
|
|
30
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Szerokość kolumny w px: z jednostki (`px`/`rem`/`em`), a gdy się nie da - w trybie wirtualnym
|
|
59
|
+
* `columnWidth`, poza nim pomiar z wyrenderowanego nagłówka, a do pierwszego pomiaru 8 rem.
|
|
60
|
+
*/
|
|
61
|
+
function columnPx(col, virtual, defaultPx, measured) {
|
|
62
|
+
const fromUnit = cssLengthToPx(col.width, REM_PX);
|
|
63
|
+
if (fromUnit !== null)
|
|
64
|
+
return fromUnit;
|
|
65
|
+
return virtual ? defaultPx : (measured.get(col.field) ?? 8 * REM_PX);
|
|
66
|
+
}
|
|
31
67
|
const EMPTY_ROWS = [];
|
|
32
68
|
const EMPTY_FIELDS = [];
|
|
33
69
|
/**
|
|
@@ -46,8 +82,14 @@ const EMPTY_FIELDS = [];
|
|
|
46
82
|
function getValue(row, field) {
|
|
47
83
|
return row[field];
|
|
48
84
|
}
|
|
49
|
-
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, rowHeight = 41, reorderable = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
|
|
50
|
-
const { t } = useKptMessages({ locale, dictionary });
|
|
85
|
+
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterSpeech = false, filterSpeechLang, filterSpeechColor, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, ariaLabel = null, caption = null, clickableRows, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, columnWidth = '10rem', rowHeight = 41, reorderable = false, frozenSystemColumns = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
|
|
86
|
+
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
87
|
+
const uid = useId();
|
|
88
|
+
const rowsClickable = clickableRows ?? onRowClick !== undefined;
|
|
89
|
+
/** Ogłoszenie po przestawieniu kolumny z klawiatury. */
|
|
90
|
+
const [moveAnnouncement, setMoveAnnouncement] = useState('');
|
|
91
|
+
/** Pole kolumny, której uchwyt ma dostać fokus po przerysowaniu (ustawiane w zdarzeniu, czytane w efekcie). */
|
|
92
|
+
const focusGrip = useRef(null);
|
|
51
93
|
const [picked, setPicked] = useControllableState({
|
|
52
94
|
value: selection,
|
|
53
95
|
defaultValue: defaultSelection,
|
|
@@ -58,28 +100,54 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
58
100
|
defaultValue: defaultExpandedRows,
|
|
59
101
|
onChange: onExpandedRowsChange,
|
|
60
102
|
});
|
|
61
|
-
const [hidden
|
|
103
|
+
const [hidden] = useControllableState({
|
|
62
104
|
value: hiddenFields,
|
|
63
105
|
defaultValue: defaultHiddenFields,
|
|
64
106
|
onChange: onHiddenFieldsChange,
|
|
65
107
|
});
|
|
66
108
|
const [filterText, setFilterText] = useState('');
|
|
109
|
+
/** Treść pola wyszukiwania - przy dużych zbiorach wyprzedza `filterText` o czas debounce. */
|
|
110
|
+
const [filterInput, setFilterInput] = useState('');
|
|
111
|
+
// Filtr głosem: pole czyści się po kliknięciu, a fraza wchodzi przez `onChange` jak wpisana -
|
|
112
|
+
// z tym samym opóźnieniem dla dużych danych. Bez `filterSpeech` kontroler nie tworzy sesji.
|
|
113
|
+
const filterRef = useRef(null);
|
|
114
|
+
const filterMic = useKptSpeechRecognition({
|
|
115
|
+
enabled: filterable && filterSpeech,
|
|
116
|
+
target: filterRef,
|
|
117
|
+
lang: filterSpeechLang,
|
|
118
|
+
mode: 'replace',
|
|
119
|
+
continuous: false,
|
|
120
|
+
});
|
|
121
|
+
const filterSpeechState = filterable && filterSpeech && filterMic.available ? (filterMic.listening ? 'listening' : 'idle') : undefined;
|
|
122
|
+
const filterTimer = useRef(undefined);
|
|
67
123
|
const [columnFilters, setColumnFilters] = useState({});
|
|
68
124
|
const [sortField, setSortField] = useState(null);
|
|
69
125
|
const [sortDir, setSortDir] = useState(null);
|
|
70
126
|
const [pageIndex, setPageIndex] = useState(0);
|
|
71
127
|
const [pageSizeState, setPageSizeState] = useState(pageSize || 10);
|
|
128
|
+
// Nowy `pageSize` od rodzica zastępuje rozmiar wybrany w paginatorze - jak `linkedSignal` w Angularze.
|
|
129
|
+
const [seenPageSize, setSeenPageSize] = useState(pageSize);
|
|
130
|
+
if (seenPageSize !== pageSize) {
|
|
131
|
+
setSeenPageSize(pageSize);
|
|
132
|
+
setPageSizeState(pageSize || 10);
|
|
133
|
+
setPageIndex(0);
|
|
134
|
+
}
|
|
72
135
|
// Kolejność kolumn żyje w stanie (przestawianie), ale nowe `columns` z zewnątrz ją zastępują.
|
|
136
|
+
// Poprzednie `columns` trzymamy w stanie, nie w refie - wzorzec "zmiana propsa resetuje stan"
|
|
137
|
+
// z react.dev; ref zależałby od tego, czy React nie wyrzucił renderu (StrictMode, concurrent).
|
|
73
138
|
const [order, setOrder] = useState(columns);
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
setOrder(columns);
|
|
139
|
+
const [seenColumns, setSeenColumns] = useState(columns);
|
|
140
|
+
if (seenColumns !== columns) {
|
|
141
|
+
setSeenColumns(columns);
|
|
142
|
+
setOrder(columns);
|
|
79
143
|
}
|
|
144
|
+
/** Zmierzone szerokości kolumn zamrożonych bez szerokości liczalnej z jednostki (`%`, `ch`). */
|
|
145
|
+
const [measuredWidths, setMeasuredWidths] = useState(() => new Map());
|
|
80
146
|
const [dragField, setDragField] = useState(null);
|
|
81
|
-
|
|
82
|
-
const [
|
|
147
|
+
/** Przewinięcie zapisane wtedy, gdy zmienia okno wierszy albo kolumn - nie przy każdym pikselu. */
|
|
148
|
+
const [scroll, setScroll] = useState({ top: 0, left: 0 });
|
|
149
|
+
const [viewport, setViewport] = useState({ height: 400, width: 1200 });
|
|
150
|
+
const scrollFrame = useRef(0);
|
|
83
151
|
const [theadOffset, setTheadOffset] = useState(0);
|
|
84
152
|
const scrollRef = useRef(null);
|
|
85
153
|
const theadRef = useRef(null);
|
|
@@ -91,12 +159,19 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
91
159
|
const hasColumnFilters = columns.some((col) => col.filter);
|
|
92
160
|
const hasRowDetail = expandable && !!renderRowDetail;
|
|
93
161
|
const sameRow = (a, b) => rowKey ? getValue(a, rowKey) === getValue(b, rowKey) : a === b;
|
|
94
|
-
|
|
162
|
+
/** Tożsamość wiersza: wartość `rowKey` albo sam wiersz - klucz zbiorów zaznaczenia i rozwinięcia. */
|
|
163
|
+
const keyOf = (row) => (rowKey ? getValue(row, rowKey) : row);
|
|
164
|
+
// -- Potok danych --
|
|
165
|
+
/**
|
|
166
|
+
* Filtr globalny z pamięcią złożonego tekstu wiersza (`@konce-pt/table`) - nowy przy zmianie
|
|
167
|
+
* kolumn, więc kolejne znaki frazy nie składają od nowa 20 000 x 100 komórek.
|
|
168
|
+
*/
|
|
169
|
+
const globalFilter = useMemo(() => createGlobalFilter(columns.map((col) => col.field)), [columns]);
|
|
95
170
|
const filtered = useMemo(() => {
|
|
96
|
-
const query = filterText.trim().toLowerCase();
|
|
97
171
|
let rows = data;
|
|
98
|
-
if (
|
|
99
|
-
|
|
172
|
+
if (filterText.trim()) {
|
|
173
|
+
// Bez diakrytyków i wielkości liter - "lodz" znajduje "Łódź" (`@konce-pt/table`).
|
|
174
|
+
rows = rows.filter((row) => globalFilter(row, filterText));
|
|
100
175
|
}
|
|
101
176
|
if (hasColumnFilters) {
|
|
102
177
|
rows = rows.filter((row) => columns.every((col) => {
|
|
@@ -109,24 +184,38 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
109
184
|
}));
|
|
110
185
|
}
|
|
111
186
|
return rows;
|
|
112
|
-
}, [columnFilters, columns, data, filterText, hasColumnFilters]);
|
|
187
|
+
}, [columnFilters, columns, data, filterText, globalFilter, hasColumnFilters]);
|
|
113
188
|
const sorted = useMemo(() => {
|
|
114
189
|
if (!sortField || !sortDir)
|
|
115
190
|
return filtered;
|
|
116
|
-
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
191
|
+
// Kolator dla aktywnego języka, klucze liczone raz na wiersz; puste komórki na końcu.
|
|
192
|
+
return sortRows(filtered, sortField, sortDir, activeLocale);
|
|
193
|
+
}, [activeLocale, filtered, sortDir, sortField]);
|
|
194
|
+
// Duży zbiór z wyszukiwaniem: tekst wierszy składamy w tle, kawałkami po kilka ms w czasie
|
|
195
|
+
// bezczynności - inaczej pierwszy znak frazy blokowałby stronę na cały zbiór naraz.
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!filterable || lazy)
|
|
198
|
+
return;
|
|
199
|
+
const rows = data;
|
|
200
|
+
if (rows.length <= FILTER_DEBOUNCE_ROWS)
|
|
201
|
+
return;
|
|
202
|
+
let index = 0;
|
|
203
|
+
let handle = null;
|
|
204
|
+
const step = () => {
|
|
205
|
+
const until = performance.now() + WARMUP_SLICE_MS;
|
|
206
|
+
while (index < rows.length && performance.now() < until)
|
|
207
|
+
globalFilter.prepare(rows[index++]);
|
|
208
|
+
if (index < rows.length)
|
|
209
|
+
handle = scheduleIdle(step);
|
|
210
|
+
};
|
|
211
|
+
handle = scheduleIdle(step);
|
|
212
|
+
return () => cancelIdle(handle);
|
|
213
|
+
}, [data, filterable, globalFilter, lazy]);
|
|
214
|
+
// Odmontowanie: porzucona klatka odczytu przewinięcia i czekający debounce filtra.
|
|
215
|
+
useEffect(() => () => {
|
|
216
|
+
cancelAnimationFrame(scrollFrame.current);
|
|
217
|
+
clearTimeout(filterTimer.current);
|
|
218
|
+
}, []);
|
|
130
219
|
const paged = useMemo(() => {
|
|
131
220
|
if (lazy)
|
|
132
221
|
return data;
|
|
@@ -136,56 +225,77 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
136
225
|
return sorted.slice(start, start + pageSizeState);
|
|
137
226
|
}, [data, lazy, pageIndex, pageSize, pageSizeState, sorted]);
|
|
138
227
|
const filteredCount = lazy ? (totalRecords ?? data.length) : filtered.length;
|
|
139
|
-
// Tryb lazy: każda zmiana stanu to prośba do rodzica o dane.
|
|
228
|
+
// Tryb lazy: każda zmiana stanu to prośba do rodzica o dane. `onLazyLoad` czytamy przez
|
|
229
|
+
// `useEffectEvent`: nowa referencja funkcji przy każdym renderze rodzica nie może restartować
|
|
230
|
+
// efektu, bo wywołałaby pętlę żądań.
|
|
231
|
+
const requestLazyLoad = useEffectEvent((state) => onLazyLoad?.(state));
|
|
140
232
|
useEffect(() => {
|
|
141
233
|
if (!lazy)
|
|
142
234
|
return;
|
|
143
|
-
|
|
144
|
-
// `onLazyLoad` celowo poza zależnościami: nowa referencja funkcji przy każdym renderze
|
|
145
|
-
// rodzica wywołałaby pętlę żądań.
|
|
146
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
235
|
+
requestLazyLoad({ pageIndex, pageSize: pageSizeState, sortField, sortDir, filter: filterText, columnFilters });
|
|
147
236
|
}, [lazy, pageIndex, pageSizeState, sortField, sortDir, filterText, columnFilters]);
|
|
148
237
|
// Offset sticky drugiego wiersza nagłówka = wysokość pierwszego.
|
|
149
238
|
useLayoutEffect(() => {
|
|
150
239
|
const first = theadRef.current?.rows?.[0];
|
|
240
|
+
// Dokładna wysokość, nie `offsetHeight`: ten zaokrągla (46,5 -> 47) i przy skali ekranu innej
|
|
241
|
+
// niż 100% między wierszami nagłówka zostawała szpara, przez którą prześwitywały wiersze.
|
|
151
242
|
if (first)
|
|
152
|
-
setTheadOffset(first.
|
|
243
|
+
setTheadOffset(first.getBoundingClientRect().height);
|
|
153
244
|
}, [filterRowPosition, hasColumnFilters, visibleColumns]);
|
|
245
|
+
// Rozmiar viewportu dla wirtualizacji - także po zmianie rozmiaru kontenera bez przewinięcia.
|
|
154
246
|
useLayoutEffect(() => {
|
|
155
247
|
const el = scrollRef.current;
|
|
156
|
-
if (el)
|
|
157
|
-
|
|
248
|
+
if (!el)
|
|
249
|
+
return;
|
|
250
|
+
const measure = () => setViewport((prev) => prev.height === el.clientHeight && prev.width === el.clientWidth
|
|
251
|
+
? prev
|
|
252
|
+
: { height: el.clientHeight, width: el.clientWidth });
|
|
253
|
+
measure();
|
|
254
|
+
if (typeof ResizeObserver === 'undefined')
|
|
255
|
+
return;
|
|
256
|
+
const observer = new ResizeObserver(measure);
|
|
257
|
+
observer.observe(el);
|
|
258
|
+
return () => observer.disconnect();
|
|
158
259
|
}, []);
|
|
159
|
-
//
|
|
260
|
+
// Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki - mierzone z nagłówków.
|
|
261
|
+
// Stan zmienia się tylko przy innej wartości, więc pomiar nie zapętla renderu.
|
|
262
|
+
useLayoutEffect(() => {
|
|
263
|
+
const head = theadRef.current;
|
|
264
|
+
if (!head)
|
|
265
|
+
return;
|
|
266
|
+
const next = new Map();
|
|
267
|
+
for (const col of visibleColumns) {
|
|
268
|
+
if (!col.frozen)
|
|
269
|
+
break;
|
|
270
|
+
if (cssLengthToPx(col.width, REM_PX) !== null)
|
|
271
|
+
continue;
|
|
272
|
+
const th = head.querySelector(`th[data-field="${CSS.escape(col.field)}"]`);
|
|
273
|
+
if (th)
|
|
274
|
+
next.set(col.field, th.offsetWidth);
|
|
275
|
+
}
|
|
276
|
+
setMeasuredWidths((prev) => prev.size === next.size && [...next].every(([key, value]) => prev.get(key) === value) ? prev : next);
|
|
277
|
+
}, [visibleColumns]);
|
|
278
|
+
// Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu - fokus idzie za nim.
|
|
279
|
+
useLayoutEffect(() => {
|
|
280
|
+
const field = focusGrip.current;
|
|
281
|
+
if (field === null)
|
|
282
|
+
return;
|
|
283
|
+
focusGrip.current = null;
|
|
284
|
+
theadRef.current
|
|
285
|
+
?.querySelector(`th[data-field="${CSS.escape(field)}"] .kpt-data-table__grip`)
|
|
286
|
+
?.focus();
|
|
287
|
+
}, [order]);
|
|
288
|
+
// Wejście w edycję -> fokus na kontrolce edytora.
|
|
160
289
|
useEffect(() => {
|
|
161
290
|
if (!editingCell)
|
|
162
291
|
return;
|
|
163
292
|
editorHostRef.current?.querySelector('input, .kpt-select, [tabindex]')?.focus();
|
|
164
293
|
}, [editingCell]);
|
|
165
|
-
//
|
|
166
|
-
const virtualStart = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);
|
|
167
|
-
const virtualEnd = Math.min(sorted.length, virtualStart + Math.ceil(viewportHeight / rowHeight) + VIRTUAL_BUFFER * 2);
|
|
168
|
-
const virtualRows = sorted.slice(virtualStart, virtualEnd);
|
|
169
|
-
const topPad = virtualStart * rowHeight;
|
|
170
|
-
const bottomPad = (sorted.length - virtualEnd) * rowHeight;
|
|
171
|
-
const onScroll = (event) => {
|
|
172
|
-
if (!virtualScroll)
|
|
173
|
-
return;
|
|
174
|
-
const el = event.currentTarget;
|
|
175
|
-
setScrollTop(el.scrollTop);
|
|
176
|
-
setViewportHeight(el.clientHeight);
|
|
177
|
-
};
|
|
178
|
-
// ── Kolumny zamrożone ──
|
|
179
|
-
const parseWidthPx = (width) => {
|
|
180
|
-
if (!width)
|
|
181
|
-
return 8 * REM_PX;
|
|
182
|
-
const n = Number.parseFloat(width);
|
|
183
|
-
if (Number.isNaN(n))
|
|
184
|
-
return 8 * REM_PX;
|
|
185
|
-
return width.trim().endsWith('px') ? n : n * REM_PX;
|
|
186
|
-
};
|
|
294
|
+
// -- Kolumny zamrożone --
|
|
187
295
|
const leadingCells = (selectable !== 'none' ? 1 : 0) + (hasRowDetail ? 1 : 0);
|
|
188
296
|
const leadingStickyWidth = (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) + (selectable !== 'none' ? CHECKBOX_FROZEN_W * REM_PX : 0);
|
|
297
|
+
/** `columnWidth` w px; nieliczalna jednostka wraca do 10 rem. */
|
|
298
|
+
const defaultColumnPx = cssLengthToPx(columnWidth, REM_PX) ?? 10 * REM_PX;
|
|
189
299
|
const frozenOffsets = useMemo(() => {
|
|
190
300
|
const map = new Map();
|
|
191
301
|
let acc = leadingStickyWidth;
|
|
@@ -193,15 +303,108 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
193
303
|
if (!col.frozen)
|
|
194
304
|
break; // tylko wiodące, sąsiadujące
|
|
195
305
|
map.set(col.field, acc);
|
|
196
|
-
acc +=
|
|
306
|
+
acc += columnPx(col, virtualScroll, defaultColumnPx, measuredWidths);
|
|
197
307
|
}
|
|
198
308
|
return map;
|
|
199
|
-
}, [leadingStickyWidth, visibleColumns]);
|
|
200
|
-
|
|
201
|
-
const
|
|
309
|
+
}, [defaultColumnPx, leadingStickyWidth, measuredWidths, virtualScroll, visibleColumns]);
|
|
310
|
+
/** Czy cokolwiek jest przyklejone do lewej - kolumny `frozen` albo same systemowe. */
|
|
311
|
+
const hasFrozen = frozenOffsets.size > 0 || (frozenSystemColumns && leadingCells > 0);
|
|
202
312
|
const expanderStickyLeft = hasFrozen ? 0 : undefined;
|
|
203
313
|
const checkboxStickyLeft = hasFrozen ? (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) : undefined;
|
|
204
|
-
//
|
|
314
|
+
// -- Kolumny do renderowania (metadane + okno wirtualizacji) --
|
|
315
|
+
/** Widoczne kolumny z cechami policzonymi raz - nie przy każdej komórce każdego wiersza. */
|
|
316
|
+
const columnMeta = useMemo(() => visibleColumns.map((col, index) => ({
|
|
317
|
+
col,
|
|
318
|
+
index,
|
|
319
|
+
editable: !!col.edit || !!cellEditors?.[col.field],
|
|
320
|
+
frozen: frozenOffsets.has(col.field),
|
|
321
|
+
left: frozenOffsets.get(col.field),
|
|
322
|
+
align: col.align ?? 'start',
|
|
323
|
+
width: columnPx(col, virtualScroll, defaultColumnPx, measuredWidths),
|
|
324
|
+
})), [cellEditors, defaultColumnPx, frozenOffsets, measuredWidths, virtualScroll, visibleColumns]);
|
|
325
|
+
/** Kolumny przewijalne (za wiodącymi zamrożonymi) i ich sumy prefiksowe szerokości. */
|
|
326
|
+
const scrollable = useMemo(() => {
|
|
327
|
+
const pinned = columnMeta.filter((m) => m.frozen);
|
|
328
|
+
const rest = columnMeta.slice(pinned.length);
|
|
329
|
+
// Kolumny systemowe zasłaniają lewą krawędź okna tylko wtedy, gdy są przyklejone.
|
|
330
|
+
const leading = hasFrozen ? leadingStickyWidth : 0;
|
|
331
|
+
const pinnedWidth = leading + pinned.reduce((sum, m) => sum + m.width, 0);
|
|
332
|
+
return { pinned, rest, pinnedWidth, offsets: columnOffsets(rest.map((m) => m.width)) };
|
|
333
|
+
}, [columnMeta, hasFrozen, leadingStickyWidth]);
|
|
334
|
+
// -- Wirtualizacja --
|
|
335
|
+
const rowWindowAt = (top) => virtualRange({
|
|
336
|
+
offset: top,
|
|
337
|
+
viewport: viewport.height,
|
|
338
|
+
itemSize: rowHeight,
|
|
339
|
+
count: sorted.length,
|
|
340
|
+
overscan: ROW_OVERSCAN,
|
|
341
|
+
});
|
|
342
|
+
const columnWindowAt = (left) => virtualScroll
|
|
343
|
+
? virtualColumnRange({
|
|
344
|
+
offsets: scrollable.offsets,
|
|
345
|
+
scrollLeft: left,
|
|
346
|
+
viewport: viewport.width,
|
|
347
|
+
overscan: COLUMN_OVERSCAN,
|
|
348
|
+
pinnedWidth: scrollable.pinnedWidth,
|
|
349
|
+
})
|
|
350
|
+
: { start: 0, end: scrollable.rest.length, before: 0, after: 0 };
|
|
351
|
+
const rowWindow = rowWindowAt(scroll.top);
|
|
352
|
+
const columnWindow = columnWindowAt(scroll.left);
|
|
353
|
+
const virtualRows = sorted.slice(rowWindow.start, rowWindow.end);
|
|
354
|
+
const topPad = rowWindow.before;
|
|
355
|
+
const bottomPad = rowWindow.after;
|
|
356
|
+
// Odczyt przewinięcia w `requestAnimationFrame` porównuje okna z tymi z ostatniego renderu -
|
|
357
|
+
// obie strony muszą widzieć bieżące dane, więc odświeżamy je po każdym renderze.
|
|
358
|
+
const windowsAt = useRef(null);
|
|
359
|
+
const shownWindows = useRef(null);
|
|
360
|
+
useLayoutEffect(() => {
|
|
361
|
+
windowsAt.current = (top, left) => ({ row: rowWindowAt(top), col: columnWindowAt(left) });
|
|
362
|
+
shownWindows.current = { row: rowWindow, col: columnWindow };
|
|
363
|
+
});
|
|
364
|
+
/** Jedna klatka na odczyt przewinięcia, a render tylko wtedy, gdy zmienia się okno wierszy lub kolumn. */
|
|
365
|
+
const onScroll = (event) => {
|
|
366
|
+
if (!virtualScroll || scrollFrame.current)
|
|
367
|
+
return;
|
|
368
|
+
const el = event.currentTarget;
|
|
369
|
+
scrollFrame.current = requestAnimationFrame(() => {
|
|
370
|
+
scrollFrame.current = 0;
|
|
371
|
+
const top = el.scrollTop;
|
|
372
|
+
const left = el.scrollLeft;
|
|
373
|
+
const next = windowsAt.current?.(top, left);
|
|
374
|
+
const shown = shownWindows.current;
|
|
375
|
+
if (next && shown && sameWindow(next.row, shown.row) && sameWindow(next.col, shown.col))
|
|
376
|
+
return;
|
|
377
|
+
setScroll({ top, left });
|
|
378
|
+
});
|
|
379
|
+
};
|
|
380
|
+
/** Nowy wynik filtra lub sortowania zaczyna się od góry - stare przewinięcie wskazywałoby w środek innego zbioru. */
|
|
381
|
+
const scrollToTop = () => {
|
|
382
|
+
const el = scrollRef.current;
|
|
383
|
+
if (virtualScroll && el)
|
|
384
|
+
el.scrollTop = 0;
|
|
385
|
+
};
|
|
386
|
+
/** Pozycje wiersza: kolumny zamrożone, dystansownik, okno kolumn, dystansownik. */
|
|
387
|
+
const slots = scrollable.pinned.map((m) => ({ key: m.col.field, meta: m, width: m.width, upTo: m.index }));
|
|
388
|
+
{
|
|
389
|
+
const { rest } = scrollable;
|
|
390
|
+
const { start, end, before, after } = columnWindow;
|
|
391
|
+
const firstIndex = rest[start]?.index ?? scrollable.pinned.length;
|
|
392
|
+
if (before > 0)
|
|
393
|
+
slots.push({ key: '\u0000before', meta: null, width: before, upTo: firstIndex });
|
|
394
|
+
for (let i = start; i < end; i++)
|
|
395
|
+
slots.push({ key: rest[i].col.field, meta: rest[i], width: rest[i].width, upTo: rest[i].index });
|
|
396
|
+
if (after > 0)
|
|
397
|
+
slots.push({ key: '\u0000after', meta: null, width: after, upTo: columnMeta.length });
|
|
398
|
+
}
|
|
399
|
+
/** Pełna szerokość tabeli w trybie wirtualnym (`table-layout: fixed`). */
|
|
400
|
+
const tableWidth = virtualScroll
|
|
401
|
+
? scrollable.pinnedWidth + scrollable.offsets[scrollable.offsets.length - 1]
|
|
402
|
+
: undefined;
|
|
403
|
+
/** Liczba wierszy nagłówka (etykiety + ewentualnie filtry) - przesunięcie `aria-rowindex`. */
|
|
404
|
+
const headerRowCount = hasColumnFilters ? 2 : 1;
|
|
405
|
+
/** `aria-colindex` komórki kolumny (od 1, z kolumnami systemowymi) - tylko w trybie wirtualnym. */
|
|
406
|
+
const colIndex = (meta) => virtualScroll ? leadingCells + meta.index + 1 : undefined;
|
|
407
|
+
// -- Przestawianie kolumn --
|
|
205
408
|
const frozenLeadCount = (() => {
|
|
206
409
|
let n = 0;
|
|
207
410
|
for (const col of visibleColumns) {
|
|
@@ -230,10 +433,31 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
230
433
|
setOrder(cols);
|
|
231
434
|
setDragField(null);
|
|
232
435
|
};
|
|
233
|
-
|
|
436
|
+
/** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce - alternatywa dla przeciągania. */
|
|
437
|
+
const onGripKeyDown = (field, event) => {
|
|
438
|
+
const delta = event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0;
|
|
439
|
+
if (!delta)
|
|
440
|
+
return;
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
const from = visibleColumns.findIndex((col) => col.field === field);
|
|
443
|
+
const to = from + delta;
|
|
444
|
+
// Kolumny zamrożone zostają wiodące - nie wolno ich minąć ani wyjechać poza tabelę.
|
|
445
|
+
if (from === -1 || to < frozenLeadCount || to >= visibleColumns.length)
|
|
446
|
+
return;
|
|
447
|
+
const cols = [...order];
|
|
448
|
+
const fromIdx = cols.findIndex((col) => col.field === field);
|
|
449
|
+
const toIdx = cols.findIndex((col) => col.field === visibleColumns[to].field);
|
|
450
|
+
const [moved] = cols.splice(fromIdx, 1);
|
|
451
|
+
cols.splice(toIdx, 0, moved);
|
|
452
|
+
setOrder(cols);
|
|
453
|
+
setMoveAnnouncement(t('dataTable.columnMoved', { column: moved.header, position: to + 1, count: visibleColumns.length }));
|
|
454
|
+
focusGrip.current = field;
|
|
455
|
+
};
|
|
456
|
+
// -- Sortowanie --
|
|
234
457
|
const toggleSort = (col) => {
|
|
235
458
|
if (!col.sortable)
|
|
236
459
|
return;
|
|
460
|
+
scrollToTop();
|
|
237
461
|
if (sortField !== col.field) {
|
|
238
462
|
setSortField(col.field);
|
|
239
463
|
setSortDir('asc');
|
|
@@ -253,10 +477,26 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
253
477
|
return 'none';
|
|
254
478
|
return sortDir === 'asc' ? 'ascending' : 'descending';
|
|
255
479
|
};
|
|
256
|
-
//
|
|
480
|
+
// -- Filtry kolumn --
|
|
257
481
|
const setColumnFilter = (field, value) => {
|
|
258
482
|
setColumnFilters((current) => ({ ...current, [field]: value }));
|
|
259
483
|
setPageIndex(0);
|
|
484
|
+
scrollToTop();
|
|
485
|
+
};
|
|
486
|
+
// -- Filtr globalny --
|
|
487
|
+
const applyFilter = (value) => {
|
|
488
|
+
setFilterText(value);
|
|
489
|
+
setPageIndex(0);
|
|
490
|
+
scrollToTop();
|
|
491
|
+
};
|
|
492
|
+
/** Przy dużych zbiorach filtr czeka na przerwę w pisaniu; pole pokazuje tekst od razu. */
|
|
493
|
+
const onFilterInput = (value) => {
|
|
494
|
+
setFilterInput(value);
|
|
495
|
+
clearTimeout(filterTimer.current);
|
|
496
|
+
if (data.length > FILTER_DEBOUNCE_ROWS)
|
|
497
|
+
filterTimer.current = setTimeout(() => applyFilter(value), FILTER_DEBOUNCE_MS);
|
|
498
|
+
else
|
|
499
|
+
applyFilter(value);
|
|
260
500
|
};
|
|
261
501
|
const rangeVal = (field) => {
|
|
262
502
|
const value = columnFilters[field];
|
|
@@ -267,8 +507,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
267
507
|
const max = col.filter?.max ?? 100;
|
|
268
508
|
return columnFilters[col.field] ?? [min, max];
|
|
269
509
|
};
|
|
270
|
-
//
|
|
271
|
-
|
|
510
|
+
// -- Zaznaczanie --
|
|
511
|
+
/** Klucze zaznaczonych wierszy - przy "zaznacz wszystko" na 20 000 wierszach `some()` dawałoby O(n^2). */
|
|
512
|
+
const selectedKeys = useMemo(() => new Set(picked.map((r) => (rowKey ? getValue(r, rowKey) : r))), [picked, rowKey]);
|
|
513
|
+
const isSelected = (row) => selectedKeys.has(keyOf(row));
|
|
272
514
|
const toggleRow = (row) => {
|
|
273
515
|
if (selectable === 'none')
|
|
274
516
|
return;
|
|
@@ -281,20 +523,24 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
281
523
|
const pageAllSelected = paged.length > 0 && paged.every((row) => isSelected(row));
|
|
282
524
|
const pageSomeSelected = paged.some((row) => isSelected(row)) && !pageAllSelected;
|
|
283
525
|
const toggleAllOnPage = () => {
|
|
284
|
-
if (pageAllSelected)
|
|
285
|
-
|
|
526
|
+
if (pageAllSelected) {
|
|
527
|
+
const pageKeys = new Set(paged.map(keyOf));
|
|
528
|
+
setPicked(picked.filter((r) => !pageKeys.has(keyOf(r))));
|
|
529
|
+
}
|
|
286
530
|
else
|
|
287
531
|
setPicked([...picked, ...paged.filter((row) => !isSelected(row))]);
|
|
288
532
|
};
|
|
289
|
-
//
|
|
290
|
-
|
|
533
|
+
// -- Rozwijane wiersze --
|
|
534
|
+
/** Klucze rozwiniętych wierszy - `isExpanded` w O(1) zamiast przeszukiwania listy dla każdego wiersza. */
|
|
535
|
+
const expandedKeys = useMemo(() => new Set(expanded.map((r) => (rowKey ? getValue(r, rowKey) : r))), [expanded, rowKey]);
|
|
536
|
+
const isExpanded = (row) => expandedKeys.has(keyOf(row));
|
|
291
537
|
const toggleExpand = (row) => {
|
|
292
538
|
if (isExpanded(row))
|
|
293
539
|
setExpanded(expanded.filter((r) => !sameRow(r, row)));
|
|
294
540
|
else
|
|
295
541
|
setExpanded(multiExpand ? [...expanded, row] : [row]);
|
|
296
542
|
};
|
|
297
|
-
//
|
|
543
|
+
// -- Edycja inline --
|
|
298
544
|
const isEditable = (col) => !!col.edit || !!cellEditors?.[col.field];
|
|
299
545
|
const isEditingCell = (row, field) => !!editingCell && editingCell.field === field && sameRow(editingCell.row, row);
|
|
300
546
|
const startEdit = (row, col, event) => {
|
|
@@ -324,7 +570,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
324
570
|
/**
|
|
325
571
|
* Edytory bez panelu (text/number, własne) zatwierdza dopiero wyjście fokusu poza CAŁY edytor:
|
|
326
572
|
* `KptInputNumber` ma fokusowalne strzałki obok pola, więc reakcja na blur samej kontrolki
|
|
327
|
-
* zamykałaby edycję przy każdym kliknięciu w
|
|
573
|
+
* zamykałaby edycję przy każdym kliknięciu w "+".
|
|
328
574
|
*/
|
|
329
575
|
const onEditorFocusOut = (event) => {
|
|
330
576
|
if (editorPanelOpen)
|
|
@@ -340,7 +586,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
340
586
|
if (!open)
|
|
341
587
|
commitEdit();
|
|
342
588
|
};
|
|
343
|
-
//
|
|
589
|
+
// -- Wiersz podsumowania --
|
|
344
590
|
const summarySource = lazy ? EMPTY_ROWS : filtered;
|
|
345
591
|
const overrides = summaryValues ?? {};
|
|
346
592
|
const summaryFields = visibleColumns
|
|
@@ -348,22 +594,61 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
348
594
|
.map((col) => col.field);
|
|
349
595
|
const hasSummary = summaryFields.length > 0;
|
|
350
596
|
const summaryStartIndex = summaryFields[0] === undefined ? -1 : visibleColumns.findIndex((col) => col.field === summaryFields[0]);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
597
|
+
/**
|
|
598
|
+
* Komórki wiersza podsumowania: etykieta scala kolumny systemowe, kolumny przed pierwszą
|
|
599
|
+
* podsumowywaną i dystansownik, o ile ten też stoi przed nią; reszta jak w `slots`.
|
|
600
|
+
* Przy kolumnach zamrożonych etykieta kończy się na ich granicy - jest przyklejona do lewej,
|
|
601
|
+
* więc scalona z kolumnami przewijanymi zasłaniałaby je przy przewijaniu w poziomie, a jej
|
|
602
|
+
* szerokość skakałaby przy każdym przestawieniu kolumny. Kolumny za granicą dostają puste komórki.
|
|
603
|
+
* Gdy przyklejone są same kolumny systemowe (`frozenSystemColumns`), są za wąskie na etykietę:
|
|
604
|
+
* dostają pustą przyklejoną komórkę (`systemSpan`), a etykieta staje za nimi, nad kolumnami
|
|
605
|
+
* przed pierwszą liczoną, i przewija się razem z nimi.
|
|
606
|
+
* `labelSpan` równe zero (pierwsza widoczna kolumna jest liczona) = etykieta nie ma się gdzie
|
|
607
|
+
* zmieścić i nie renderuje się wcale.
|
|
608
|
+
*/
|
|
609
|
+
const summaryLayout = (() => {
|
|
610
|
+
const isBeforeStart = (slot) => slot.meta ? slot.meta.index < summaryStartIndex : slot.upTo <= summaryStartIndex;
|
|
611
|
+
let frozenSpan = 0;
|
|
612
|
+
const restSlots = [];
|
|
613
|
+
for (const slot of slots) {
|
|
614
|
+
const fitsLabel = !hasFrozen || slot.meta?.frozen === true;
|
|
615
|
+
if (restSlots.length === 0 && isBeforeStart(slot) && fitsLabel)
|
|
616
|
+
frozenSpan++;
|
|
617
|
+
else
|
|
618
|
+
restSlots.push(slot);
|
|
619
|
+
}
|
|
620
|
+
if (hasFrozen && frozenSpan === 0 && leadingCells > 0) {
|
|
621
|
+
let free = 0;
|
|
622
|
+
while (free < restSlots.length && isBeforeStart(restSlots[free]))
|
|
623
|
+
free++;
|
|
624
|
+
if (free > 0)
|
|
625
|
+
return { systemSpan: leadingCells, labelSpan: free, labelSticky: false, slots: restSlots.slice(free) };
|
|
626
|
+
}
|
|
627
|
+
return { systemSpan: 0, labelSpan: leadingCells + frozenSpan, labelSticky: hasFrozen, slots: restSlots };
|
|
628
|
+
})();
|
|
629
|
+
/** Gotowe teksty komórek podsumowania (pole -> tekst) - agregacja całego zbioru raz, nie przy każdym renderze. */
|
|
630
|
+
const summaryTexts = useMemo(() => {
|
|
631
|
+
const values = summaryValues ?? {};
|
|
632
|
+
const format = (raw, fmt) => (fmt ? fmt(raw) : toText(raw));
|
|
633
|
+
const map = new Map();
|
|
634
|
+
for (const col of visibleColumns) {
|
|
635
|
+
if (col.field in values)
|
|
636
|
+
map.set(col.field, format(values[col.field], col.summary?.format));
|
|
637
|
+
else if (col.summary) {
|
|
638
|
+
const raw = typeof col.summary.fn === 'function'
|
|
639
|
+
? col.summary.fn(summarySource)
|
|
640
|
+
: aggregate(summarySource, col.field, col.summary.fn);
|
|
641
|
+
map.set(col.field, format(raw, col.summary.format));
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return map;
|
|
645
|
+
}, [summarySource, summaryValues, visibleColumns]);
|
|
646
|
+
/** Komórki faktycznie renderowane w wierszu (w trybie wirtualnym okno kolumn + dystansowniki). */
|
|
647
|
+
const colspan = slots.length + leadingCells;
|
|
364
648
|
const displayCount = virtualScroll ? sorted.length : paged.length;
|
|
365
649
|
const rowsToRender = virtualScroll ? virtualRows : paged;
|
|
366
|
-
//
|
|
650
|
+
// -- Renderowanie --
|
|
651
|
+
const hspacer = (slot) => (_jsx("td", { className: "kpt-data-table__hspacer", "aria-hidden": "true" }, slot.key));
|
|
367
652
|
const filterCell = (col) => {
|
|
368
653
|
const filter = col.filter;
|
|
369
654
|
if (!filter)
|
|
@@ -386,7 +671,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
386
671
|
return null;
|
|
387
672
|
}
|
|
388
673
|
};
|
|
389
|
-
const
|
|
674
|
+
const filterRowIndex = filterRowPosition === 'top' ? 1 : 2;
|
|
675
|
+
const filterRow = (_jsxs("tr", { className: "kpt-data-table__filter-row", "aria-rowindex": virtualScroll ? filterRowIndex : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable !== 'none' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => slot.meta ? (_jsx("th", { style: { width: virtualScroll ? undefined : slot.meta.col.width, left: slot.meta.left }, "data-align": slot.meta.align, "aria-colindex": colIndex(slot.meta), className: cn(slot.meta.frozen && 'kpt-data-table__cell--frozen'), children: filterCell(slot.meta.col) }, slot.key)) : (hspacer(slot)))] }));
|
|
390
676
|
const editor = (row, col) => {
|
|
391
677
|
const custom = cellEditors?.[col.field];
|
|
392
678
|
if (custom) {
|
|
@@ -425,37 +711,58 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
425
711
|
return null;
|
|
426
712
|
}
|
|
427
713
|
};
|
|
428
|
-
const renderRow = (row, index) => (_jsxs("tbody", { style: { display: 'contents' }, children: [_jsxs("tr", { className: cn(isSelected(row) && 'kpt-data-table__row--selected', hasRowDetail && isExpanded(row) && 'kpt-data-table__row--expanded'
|
|
714
|
+
const renderRow = (row, index, ariaRowIndex) => (_jsxs("tbody", { style: { display: 'contents' }, children: [_jsxs("tr", { "aria-rowindex": ariaRowIndex, className: cn(isSelected(row) && 'kpt-data-table__row--selected', hasRowDetail && isExpanded(row) && 'kpt-data-table__row--expanded', rowsClickable && 'kpt-data-table__row--clickable'), style: { height: virtualScroll ? rowHeight : undefined }, tabIndex: rowsClickable ? 0 : undefined, onClick: () => onRowClick?.(row), onKeyDown: rowsClickable
|
|
715
|
+
? (event) => {
|
|
716
|
+
// Tylko na samym wierszu - Enter w kontrolce komórki należy do niej.
|
|
717
|
+
if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' '))
|
|
718
|
+
return;
|
|
719
|
+
event.preventDefault();
|
|
720
|
+
onRowClick?.(row);
|
|
721
|
+
}
|
|
722
|
+
: undefined, children: [hasRowDetail ? (_jsx("td", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft }, children: _jsx("button", { type: "button", className: cn('kpt-data-table__expander-btn', isExpanded(row) && 'kpt-data-table__expander-btn--open'), "aria-expanded": isExpanded(row), "aria-label": t('dataTable.expandRow'), onClick: (event) => {
|
|
429
723
|
event.stopPropagation();
|
|
430
724
|
toggleExpand(row);
|
|
431
|
-
}, children: _jsx(KptIcon, { name: "chevron-right" }) }) })) : null, selectable !== 'none' ? (_jsx("td", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type:
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
725
|
+
}, children: _jsx(KptIcon, { name: "chevron-right" }) }) })) : null, selectable !== 'none' ? (_jsx("td", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: selectable === 'single' ? 'radio' : 'checkbox', name: selectable === 'single' ? `${uid}-select` : undefined, checked: isSelected(row), "aria-label": t('dataTable.selectRow'), onClick: (event) => {
|
|
726
|
+
event.stopPropagation();
|
|
727
|
+
toggleRow(row);
|
|
728
|
+
}, onChange: () => {
|
|
729
|
+
// Zmianę obsługuje `onClick` (radio nie zgłasza `change` przy odznaczaniu).
|
|
730
|
+
} }) })) : null, slots.map((slot) => {
|
|
731
|
+
const m = slot.meta;
|
|
732
|
+
if (!m)
|
|
733
|
+
return hspacer(slot);
|
|
734
|
+
const col = m.col;
|
|
735
|
+
// Edytor sprawdzamy tylko, gdy jakakolwiek komórka jest w edycji - reszta to odczyt gotowych cech kolumny.
|
|
736
|
+
const editing = m.editable && editingCell !== null && isEditingCell(row, col.field);
|
|
737
|
+
const editable = m.editable && !editing;
|
|
738
|
+
return (_jsx("td", { "data-align": m.align, style: { left: m.left }, "aria-colindex": colIndex(m), className: cn(m.frozen && 'kpt-data-table__cell--frozen', editable && 'kpt-data-table__cell--editable'), tabIndex: editable ? 0 : undefined, "aria-describedby": editable ? `${uid}-edit-${m.index}` : undefined, onClick: (event) => {
|
|
739
|
+
if (m.editable)
|
|
436
740
|
startEdit(row, col, event);
|
|
437
741
|
}, onKeyDown: (event) => {
|
|
438
742
|
if (event.key === 'Enter' && editable)
|
|
439
743
|
startEdit(row, col, event);
|
|
440
|
-
}, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) },
|
|
744
|
+
}, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) }, slot.key));
|
|
441
745
|
})] }), hasRowDetail && isExpanded(row) && !virtualScroll ? (_jsx("tr", { className: "kpt-data-table__detail-row", children: _jsx("td", { className: "kpt-data-table__detail", colSpan: colspan, children: renderRowDetail?.(row) }) })) : null] }, rowKey ? String(getValue(row, rowKey)) : index));
|
|
442
|
-
return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsx("input", { className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), value:
|
|
443
|
-
setFilterText(event.target.value);
|
|
444
|
-
setPageIndex(0);
|
|
445
|
-
} })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), children: [_jsxs("thead", { ref: theadRef, children: [hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
|
|
746
|
+
return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsxs("div", { className: "kpt-data-table__search", "data-speech": filterSpeechState, children: [_jsx("input", { ref: filterRef, className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), "aria-label": t('dataTable.search'), value: filterInput, onChange: (event) => onFilterInput(event.target.value) }), filterSpeech ? (_jsx(KptSpeechButton, { listeningColor: filterSpeechColor, speech: filterMic, size: "sm", unsupported: "hide", hostProps: { className: 'kpt-data-table__speech' } })) : null] })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), style: { width: tableWidth }, "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, "aria-rowcount": virtualScroll ? headerRowCount + sorted.length + (hasSummary ? 1 : 0) : undefined, "aria-colcount": virtualScroll ? leadingCells + visibleColumns.length : undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: _jsx("span", { className: "kpt-data-table__caption-text", children: caption }) }) : null, virtualScroll ? (_jsxs("colgroup", { children: [hasRowDetail ? _jsx("col", { className: "kpt-data-table__col-expander" }) : null, selectable !== 'none' ? _jsx("col", { className: "kpt-data-table__col-checkbox" }) : null, slots.map((slot) => (_jsx("col", { style: { width: slot.width } }, slot.key)))] })) : null, _jsxs("thead", { ref: theadRef, children: [hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { "aria-rowindex": virtualScroll ? (hasColumnFilters && filterRowPosition === 'top' ? 2 : 1) : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
|
|
446
747
|
if (el)
|
|
447
748
|
el.indeterminate = pageSomeSelected;
|
|
448
|
-
}, "aria-label": t('dataTable.selectAll'), onChange: toggleAllOnPage }) })) : selectable === 'single' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null,
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
749
|
+
}, "aria-label": t('dataTable.selectAll'), onChange: toggleAllOnPage }) })) : selectable === 'single' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => {
|
|
750
|
+
if (!slot.meta)
|
|
751
|
+
return hspacer(slot);
|
|
752
|
+
const m = slot.meta;
|
|
753
|
+
const col = m.col;
|
|
754
|
+
return (_jsxs("th", { draggable: isColDraggable(col) || undefined, style: { width: virtualScroll ? undefined : col.width, left: m.left }, "data-align": m.align, "aria-colindex": colIndex(m), className: cn(m.frozen && 'kpt-data-table__cell--frozen', col.sortable && 'kpt-data-table__th--sortable', isColDraggable(col) && 'kpt-data-table__th--draggable', dragField !== null && dragField !== col.field && !col.frozen && 'kpt-data-table__th--dragover'), "data-field": col.field, "aria-sort": col.sortable ? ariaSort(col) : undefined, onDragStart: (event) => {
|
|
755
|
+
if (col.frozen)
|
|
756
|
+
return;
|
|
757
|
+
setDragField(col.field);
|
|
758
|
+
event.dataTransfer.setData('text/plain', col.field);
|
|
759
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
760
|
+
}, onDragOver: (event) => {
|
|
761
|
+
if (!reorderable)
|
|
762
|
+
return;
|
|
763
|
+
const visIndex = visibleColumns.findIndex((c) => c.field === col.field);
|
|
764
|
+
if (dragField !== null && visIndex >= frozenLeadCount)
|
|
765
|
+
event.preventDefault();
|
|
766
|
+
}, onDrop: (event) => reorderable && onColDrop(col.field, event), onDragEnd: () => setDragField(null), children: [isColDraggable(col) ? (_jsx("button", { type: "button", className: "kpt-data-table__grip", "aria-label": t('dataTable.moveColumn', { column: col.header }), "aria-keyshortcuts": "ArrowLeft ArrowRight", onKeyDown: (event) => onGripKeyDown(col.field, event), children: _jsx(KptIcon, { name: "grip-vertical" }) })) : null, col.sortable ? (_jsxs("button", { type: "button", className: "kpt-data-table__sort-btn", onClick: () => toggleSort(col), children: [_jsx("span", { children: col.header }), _jsx(KptIcon, { className: cn('kpt-data-table__sort', ariaSort(col) !== 'none' && 'kpt-data-table__sort--active'), name: sortField !== col.field || !sortDir ? 'selector' : sortDir === 'asc' ? 'chevron-up' : 'chevron-down' })] })) : (_jsx("span", { children: col.header }))] }, slot.key));
|
|
767
|
+
})] }), hasColumnFilters && filterRowPosition === 'bottom' ? filterRow : null] }), loading ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: t('dataTable.loading') }) }) })) : displayCount === 0 ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: emptyMessage ?? t('dataTable.empty') }) }) })) : (_jsxs(_Fragment, { children: [virtualScroll && topPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: topPad } }) }) })) : null, rowsToRender.map((row, index) => renderRow(row, index, virtualScroll ? headerRowCount + rowWindow.start + index + 1 : undefined)), virtualScroll && bottomPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: bottomPad } }) }) })) : null] })), hasSummary && !loading && displayCount > 0 ? (_jsx("tfoot", { children: _jsxs("tr", { className: "kpt-data-table__summary", "aria-rowindex": virtualScroll ? headerRowCount + sorted.length + 1 : undefined, children: [summaryLayout.systemSpan > 0 ? (_jsx("td", { className: "kpt-data-table__cell--frozen", colSpan: summaryLayout.systemSpan, style: { left: 0 } })) : null, summaryLayout.labelSpan > 0 ? (_jsx("th", { className: cn('kpt-data-table__summary-label', summaryLayout.labelSticky && 'kpt-data-table__cell--frozen'), colSpan: summaryLayout.labelSpan, style: { left: summaryLayout.labelSticky ? 0 : undefined }, children: summaryLabel ?? t('dataTable.total') })) : null, summaryLayout.slots.map((slot) => slot.meta ? (_jsx("td", { "data-align": slot.meta.col.align ?? 'end', style: { width: virtualScroll ? undefined : slot.meta.col.width, left: slot.meta.left }, "aria-colindex": colIndex(slot.meta), className: cn(slot.meta.frozen && 'kpt-data-table__cell--frozen'), children: summaryTexts.get(slot.meta.col.field) ?? '' }, slot.key)) : (hspacer(slot)))] }) })) : null] }) }), _jsx("div", { className: "kpt-data-table__live", "aria-live": "polite", children: moveAnnouncement }), _jsx("div", { hidden: true, children: visibleColumns.map((col, colIndex) => isEditable(col) ? (_jsx("span", { id: `${uid}-edit-${colIndex}`, children: t('dataTable.editCell', { column: col.header }) }, col.field)) : null) }), pageSize > 0 && !virtualScroll ? (_jsxs("div", { className: "kpt-data-table__pager", children: [paginatorActions, _jsx(KptPaginator, { length: filteredCount, pageIndex: pageIndex, onPageIndexChange: setPageIndex, pageSize: pageSizeState, onPageSizeChange: setPageSizeState, pageSizeOptions: pageSizeOptions })] })) : null] }));
|
|
461
768
|
}
|