@konce-pt/react 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -64
- package/dist/accordion/accordion.d.ts +14 -6
- package/dist/accordion/accordion.js +14 -7
- package/dist/accordion/llms.txt +22 -10
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +2 -2
- package/dist/alert/llms.txt +10 -10
- package/dist/app-shell/app-shell.d.ts +14 -14
- package/dist/app-shell/app-shell.js +11 -11
- package/dist/app-shell/llms.txt +13 -13
- package/dist/auth/auth-types.d.ts +6 -6
- package/dist/auth/auth.d.ts +2 -2
- package/dist/auth/auth.js +7 -7
- package/dist/auth/llms.txt +9 -9
- package/dist/autocomplete/autocomplete.d.ts +19 -6
- package/dist/autocomplete/autocomplete.js +23 -10
- package/dist/autocomplete/llms.txt +16 -5
- package/dist/avatar/avatar.d.ts +6 -6
- package/dist/avatar/avatar.js +5 -5
- package/dist/avatar/llms.txt +10 -10
- package/dist/avatar-group/avatar-group.d.ts +4 -4
- package/dist/avatar-group/avatar-group.js +3 -3
- package/dist/avatar-group/llms.txt +8 -8
- package/dist/backdrop/backdrop.d.ts +7 -7
- package/dist/backdrop/backdrop.js +16 -16
- package/dist/backdrop/llms.txt +27 -27
- package/dist/badge/badge.d.ts +3 -3
- package/dist/badge/badge.js +1 -1
- package/dist/badge/llms.txt +11 -11
- package/dist/bottom-sheet/bottom-sheet.d.ts +4 -4
- package/dist/bottom-sheet/bottom-sheet.js +2 -2
- package/dist/bottom-sheet/llms.txt +9 -9
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +3 -3
- package/dist/breadcrumb/llms.txt +3 -3
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +8 -8
- package/dist/button/llms.txt +27 -19
- package/dist/button-group/button-group.d.ts +9 -9
- package/dist/button-group/button-group.js +30 -8
- package/dist/button-group/llms.txt +20 -14
- package/dist/card/card.d.ts +8 -8
- package/dist/card/card.js +6 -6
- package/dist/card/llms.txt +20 -20
- package/dist/carousel/carousel.d.ts +6 -6
- package/dist/carousel/carousel.js +4 -4
- package/dist/carousel/llms.txt +10 -10
- package/dist/charts/chart-legend.d.ts +3 -3
- package/dist/charts/chart-legend.js +1 -1
- package/dist/charts/chart-tooltip.d.ts +3 -3
- package/dist/charts/chart-tooltip.js +1 -1
- package/dist/charts/chart.d.ts +1 -1
- package/dist/charts/chart.js +8 -8
- package/dist/charts/llms.txt +6 -6
- package/dist/charts-entry/public-api.js +2 -2
- package/dist/chat/chat.d.ts +17 -10
- package/dist/chat/chat.js +42 -18
- package/dist/chat/llms.txt +32 -17
- package/dist/chat-composer/chat-composer.d.ts +17 -7
- package/dist/chat-composer/chat-composer.js +21 -8
- package/dist/chat-composer/llms.txt +20 -11
- package/dist/chat-dock/chat-dock.d.ts +11 -4
- package/dist/chat-dock/chat-dock.js +9 -6
- package/dist/chat-dock/llms.txt +15 -14
- package/dist/chat-entry/public-api.js +1 -1
- package/dist/chat-message/chat-message.d.ts +12 -5
- package/dist/chat-message/chat-message.js +17 -7
- package/dist/chat-message/llms.txt +16 -15
- package/dist/chat-typing/chat-typing.d.ts +2 -2
- package/dist/chat-typing/chat-typing.js +3 -3
- package/dist/chat-typing/llms.txt +6 -6
- package/dist/checkbox/checkbox.d.ts +13 -6
- package/dist/checkbox/checkbox.js +26 -7
- package/dist/checkbox/llms.txt +24 -12
- package/dist/chip/chip.d.ts +4 -4
- package/dist/chip/chip.js +2 -2
- package/dist/chip/llms.txt +11 -11
- package/dist/chips-input/chips-input.d.ts +4 -4
- package/dist/chips-input/chips-input.js +35 -9
- package/dist/chips-input/llms.txt +15 -8
- package/dist/clock/clock.d.ts +4 -4
- package/dist/clock/clock.js +7 -7
- package/dist/clock/llms.txt +8 -8
- package/dist/color-picker/color-picker.d.ts +6 -6
- package/dist/color-picker/color-picker.js +3 -3
- package/dist/color-picker/llms.txt +5 -5
- package/dist/confirm/confirm-container.d.ts +3 -3
- package/dist/confirm/confirm-container.js +5 -5
- package/dist/confirm/confirm-store.d.ts +4 -4
- package/dist/confirm/confirm-store.js +7 -7
- package/dist/confirm/llms.txt +6 -6
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/context-menu.js +5 -5
- package/dist/context-menu/llms.txt +7 -7
- package/dist/data-table/data-table-types.d.ts +5 -5
- package/dist/data-table/data-table.d.ts +29 -8
- package/dist/data-table/data-table.js +340 -95
- package/dist/data-table/export-csv.d.ts +3 -3
- package/dist/data-table/export-csv.js +4 -4
- package/dist/data-table/llms.txt +127 -94
- package/dist/data-view/data-view.d.ts +13 -5
- package/dist/data-view/data-view.js +67 -6
- package/dist/data-view/llms.txt +29 -10
- package/dist/date-range/date-range.d.ts +9 -9
- package/dist/date-range/date-range.js +24 -24
- package/dist/date-range/llms.txt +20 -20
- package/dist/datepicker/datepicker.d.ts +10 -10
- package/dist/datepicker/datepicker.js +29 -29
- package/dist/datepicker/llms.txt +24 -24
- package/dist/dialog/dialog.d.ts +4 -4
- package/dist/dialog/dialog.js +4 -4
- package/dist/dialog/llms.txt +11 -11
- package/dist/divider/divider.d.ts +1 -1
- package/dist/divider/llms.txt +4 -4
- package/dist/drawer/drawer.d.ts +5 -5
- package/dist/drawer/drawer.js +2 -2
- package/dist/drawer/llms.txt +11 -11
- package/dist/empty/empty.d.ts +2 -2
- package/dist/empty/empty.js +1 -1
- package/dist/empty/llms.txt +9 -9
- package/dist/fab/fab.d.ts +1 -1
- package/dist/fab/fab.js +1 -1
- package/dist/fab/llms.txt +10 -6
- package/dist/fieldset/fieldset.d.ts +18 -4
- package/dist/fieldset/fieldset.js +9 -6
- package/dist/fieldset/llms.txt +26 -9
- package/dist/file-upload/file-upload.d.ts +3 -3
- package/dist/file-upload/file-upload.js +7 -7
- package/dist/file-upload/llms.txt +7 -7
- package/dist/form-field/form-field-context.d.ts +4 -4
- package/dist/form-field/form-field.d.ts +12 -4
- package/dist/form-field/form-field.js +26 -6
- package/dist/form-field/llms.txt +25 -11
- package/dist/galleria/galleria.d.ts +3 -3
- package/dist/galleria/galleria.js +5 -5
- package/dist/galleria/llms.txt +7 -7
- package/dist/grid/col.d.ts +4 -4
- package/dist/grid/col.js +2 -2
- package/dist/grid/flex.d.ts +3 -3
- package/dist/grid/flex.js +1 -1
- package/dist/grid/grid.d.ts +7 -7
- package/dist/grid/grid.js +3 -3
- package/dist/grid/layout.d.ts +2 -2
- package/dist/grid/layout.js +2 -2
- package/dist/grid/llms.txt +15 -15
- package/dist/grid-entry/public-api.js +1 -1
- package/dist/i18n/i18n-context.d.ts +3 -3
- package/dist/i18n/i18n-context.js +7 -7
- package/dist/icon/icon-registry.d.ts +3 -3
- package/dist/icon/icon-registry.js +3 -3
- package/dist/icon/icon.d.ts +4 -4
- package/dist/icon/icon.js +6 -6
- package/dist/icon/llms.txt +13 -13
- package/dist/icon-button/icon-button.d.ts +2 -2
- package/dist/icon-button/icon-button.js +1 -1
- package/dist/icon-button/llms.txt +9 -5
- package/dist/icons-entry/public-api.d.ts +2 -2
- package/dist/icons-entry/public-api.js +3 -3
- package/dist/image/image.js +1 -1
- package/dist/image/llms.txt +13 -13
- package/dist/input/input.d.ts +18 -5
- package/dist/input/input.js +21 -6
- package/dist/input/llms.txt +25 -11
- package/dist/input-mask/input-mask.d.ts +1 -1
- package/dist/input-mask/input-mask.js +2 -2
- package/dist/input-mask/llms.txt +5 -5
- package/dist/input-number/input-number.d.ts +9 -9
- package/dist/input-number/input-number.js +11 -11
- package/dist/input-number/llms.txt +14 -14
- package/dist/input-otp/input-otp.d.ts +6 -6
- package/dist/input-otp/input-otp.js +10 -10
- package/dist/input-otp/llms.txt +9 -9
- package/dist/internal/calendar-focus.d.ts +1 -1
- package/dist/internal/calendar-focus.js +3 -3
- package/dist/internal/cn.d.ts +1 -1
- package/dist/internal/mask.d.ts +2 -2
- package/dist/internal/mask.js +4 -4
- package/dist/internal/option-key.d.ts +2 -2
- package/dist/internal/option-key.js +2 -2
- package/dist/internal/option-nav.d.ts +4 -4
- package/dist/internal/option-nav.js +5 -5
- package/dist/internal/overlay-container.js +1 -1
- package/dist/internal/overlay.d.ts +3 -3
- package/dist/internal/overlay.js +8 -8
- package/dist/internal/password-strength.d.ts +2 -2
- package/dist/internal/password-strength.js +6 -6
- package/dist/internal/roving.d.ts +2 -2
- package/dist/internal/roving.js +3 -3
- package/dist/internal/scroll-lock.d.ts +1 -1
- package/dist/internal/scroll-lock.js +2 -2
- package/dist/internal/slot-children.d.ts +3 -3
- package/dist/internal/slot-children.js +4 -4
- package/dist/internal/slot.d.ts +3 -3
- package/dist/internal/slot.js +2 -2
- package/dist/internal/step.d.ts +3 -3
- package/dist/internal/step.js +4 -4
- package/dist/internal/tween.d.ts +5 -5
- package/dist/internal/tween.js +5 -5
- package/dist/internal/types.d.ts +2 -2
- package/dist/internal/use-breakpoint.d.ts +2 -2
- package/dist/internal/use-breakpoint.js +4 -4
- package/dist/internal/use-controllable-state.d.ts +1 -1
- package/dist/internal/use-controllable-state.js +3 -3
- package/dist/internal/use-id.d.ts +2 -2
- package/dist/internal/use-id.js +2 -2
- package/dist/internal/use-modal-focus.d.ts +1 -1
- package/dist/internal/use-modal-focus.js +3 -3
- package/dist/knob/knob.d.ts +4 -4
- package/dist/knob/knob.js +14 -10
- package/dist/knob/llms.txt +17 -12
- package/dist/listbox/listbox.d.ts +6 -6
- package/dist/listbox/listbox.js +9 -9
- package/dist/listbox/llms.txt +10 -10
- package/dist/loader/llms.txt +56 -0
- package/dist/loader/loader.d.ts +49 -0
- package/dist/loader/loader.js +68 -0
- package/dist/map/llms.txt +25 -22
- package/dist/map/map-create-dialog.d.ts +4 -4
- package/dist/map/map-create-dialog.js +10 -10
- package/dist/map/map-hours-field.d.ts +4 -4
- package/dist/map/map-hours-field.js +7 -7
- package/dist/map/map-image-field.d.ts +2 -2
- package/dist/map/map-image-field.js +4 -4
- package/dist/map/map-legend.d.ts +4 -4
- package/dist/map/map-legend.js +2 -2
- package/dist/map/map-poi-card.d.ts +3 -3
- package/dist/map/map-poi-card.js +9 -9
- package/dist/map/map-search.d.ts +17 -6
- package/dist/map/map-search.js +21 -18
- package/dist/map/map.d.ts +21 -14
- package/dist/map/map.js +30 -29
- package/dist/map/ports.d.ts +9 -9
- package/dist/map/ports.js +6 -6
- package/dist/map-entry/public-api.js +2 -2
- package/dist/megamenu/llms.txt +9 -9
- package/dist/megamenu/megamenu.d.ts +6 -6
- package/dist/megamenu/megamenu.js +10 -10
- package/dist/mention/llms.txt +15 -15
- package/dist/mention/mention.d.ts +5 -5
- package/dist/mention/mention.js +2 -2
- package/dist/mention/mentions.d.ts +1 -1
- package/dist/mention/mentions.js +1 -1
- package/dist/menu/llms.txt +12 -12
- package/dist/menu/menu-item.d.ts +4 -4
- package/dist/menu/menu-keys.d.ts +2 -2
- package/dist/menu/menu-keys.js +5 -5
- package/dist/menu/menu.d.ts +2 -2
- package/dist/menu/menu.js +6 -6
- package/dist/menubar/llms.txt +10 -10
- package/dist/menubar/menubar.d.ts +5 -5
- package/dist/menubar/menubar.js +10 -10
- package/dist/meter-group/llms.txt +12 -12
- package/dist/meter-group/meter-group.d.ts +2 -2
- package/dist/meter-group/meter-group.js +1 -1
- package/dist/motion/motion-context.d.ts +22 -0
- package/dist/motion/motion-context.js +28 -0
- package/dist/order-list/llms.txt +22 -10
- package/dist/order-list/order-list.d.ts +12 -4
- package/dist/order-list/order-list.js +104 -21
- package/dist/paginator/llms.txt +8 -8
- package/dist/paginator/paginator.js +1 -1
- package/dist/panel/llms.txt +25 -13
- package/dist/panel/panel.d.ts +11 -3
- package/dist/panel/panel.js +7 -4
- package/dist/password/llms.txt +16 -8
- package/dist/password/password.d.ts +2 -2
- package/dist/password/password.js +24 -3
- package/dist/peek/llms.txt +10 -10
- package/dist/peek/peek.d.ts +6 -6
- package/dist/peek/peek.js +7 -7
- package/dist/pick-list/llms.txt +33 -13
- package/dist/pick-list/pick-list.d.ts +12 -4
- package/dist/pick-list/pick-list.js +111 -23
- package/dist/popover/llms.txt +11 -11
- package/dist/popover/popover.d.ts +4 -4
- package/dist/popover/popover.js +5 -5
- package/dist/progress/llms.txt +17 -17
- package/dist/progress/progress.d.ts +7 -7
- package/dist/progress/progress.js +6 -6
- package/dist/public-api.d.ts +15 -3
- package/dist/public-api.js +13 -7
- package/dist/radio-group/llms.txt +13 -6
- package/dist/radio-group/radio-group.d.ts +11 -4
- package/dist/radio-group/radio-group.js +15 -7
- package/dist/rating/llms.txt +20 -13
- package/dist/rating/rating-pointer.d.ts +16 -0
- package/dist/rating/rating-pointer.js +26 -0
- package/dist/rating/rating.d.ts +6 -6
- package/dist/rating/rating.js +76 -12
- package/dist/rich-text/lists.d.ts +2 -2
- package/dist/rich-text/lists.js +7 -7
- package/dist/rich-text/llms.txt +26 -17
- package/dist/rich-text/rich-text.d.ts +12 -7
- package/dist/rich-text/rich-text.js +78 -54
- package/dist/rich-text/sanitize.d.ts +3 -3
- package/dist/rich-text/sanitize.js +12 -12
- package/dist/rich-text/tables.d.ts +3 -3
- package/dist/rich-text/tables.js +3 -3
- package/dist/roadmap/llms.txt +15 -15
- package/dist/roadmap/roadmap.d.ts +6 -6
- package/dist/roadmap/roadmap.js +19 -19
- package/dist/roadmap-entry/public-api.js +2 -2
- package/dist/scroll-top/llms.txt +6 -6
- package/dist/scroll-top/scroll-top.d.ts +1 -1
- package/dist/scroll-top/scroll-top.js +1 -1
- package/dist/select/llms.txt +19 -12
- package/dist/select/select.d.ts +8 -8
- package/dist/select/select.js +47 -21
- package/dist/shared/types.d.ts +5 -5
- package/dist/shared/types.js +1 -1
- package/dist/sidenav/llms.txt +9 -9
- package/dist/sidenav/sidenav.d.ts +1 -1
- package/dist/sidenav/sidenav.js +1 -1
- package/dist/skeleton/llms.txt +3 -3
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +1 -1
- package/dist/slider/llms.txt +32 -13
- package/dist/slider/slider-drag.d.ts +26 -0
- package/dist/slider/slider-drag.js +79 -0
- package/dist/slider/slider.d.ts +6 -5
- package/dist/slider/slider.js +27 -8
- package/dist/speech/llms.txt +97 -0
- package/dist/speech/speech-button.d.ts +44 -0
- package/dist/speech/speech-button.js +86 -0
- package/dist/speech/speech-wave.d.ts +19 -0
- package/dist/speech/speech-wave.js +16 -0
- package/dist/speech/use-speech-recognition.d.ts +81 -0
- package/dist/speech/use-speech-recognition.js +179 -0
- package/dist/speed-dial/llms.txt +14 -8
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/speed-dial/speed-dial.js +1 -1
- package/dist/spinner/llms.txt +7 -7
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +1 -1
- package/dist/split-button/llms.txt +21 -15
- package/dist/split-button/split-button.d.ts +2 -2
- package/dist/split-button/split-button.js +4 -4
- package/dist/splitter/llms.txt +11 -11
- package/dist/splitter/splitter.d.ts +3 -3
- package/dist/splitter/splitter.js +5 -5
- package/dist/stepper/llms.txt +33 -14
- package/dist/stepper/stepper.d.ts +6 -6
- package/dist/stepper/stepper.js +53 -10
- package/dist/switch/llms.txt +12 -5
- package/dist/switch/switch.d.ts +10 -3
- package/dist/switch/switch.js +12 -5
- package/dist/switch-group/llms.txt +9 -9
- package/dist/switch-group/switch-group.d.ts +4 -4
- package/dist/switch-group/switch-group.js +4 -4
- package/dist/tabs/llms.txt +10 -10
- package/dist/tabs/tabs.d.ts +5 -5
- package/dist/tabs/tabs.js +8 -8
- package/dist/textarea/llms.txt +23 -12
- package/dist/textarea/textarea.d.ts +19 -6
- package/dist/textarea/textarea.js +21 -7
- package/dist/timeline/llms.txt +4 -4
- package/dist/timeline/timeline.d.ts +1 -1
- package/dist/timeline/timeline.js +1 -1
- package/dist/toast/llms.txt +47 -17
- package/dist/toast/toast-container.d.ts +9 -3
- package/dist/toast/toast-container.js +17 -9
- package/dist/toast/toast-store.d.ts +40 -4
- package/dist/toast/toast-store.js +36 -1
- package/dist/toolbar/llms.txt +7 -7
- package/dist/toolbar/toolbar.d.ts +3 -3
- package/dist/toolbar/toolbar.js +2 -2
- package/dist/tooltip/llms.txt +10 -10
- package/dist/tooltip/tooltip.d.ts +3 -3
- package/dist/tooltip/tooltip.js +18 -18
- package/dist/tree/llms.txt +25 -12
- package/dist/tree/tree-keys.d.ts +5 -5
- package/dist/tree/tree-keys.js +7 -7
- package/dist/tree/tree.d.ts +14 -7
- package/dist/tree/tree.js +53 -18
- package/dist/update/llms.txt +54 -0
- package/dist/update/update.d.ts +49 -0
- package/dist/update/update.js +82 -0
- package/package.json +15 -13
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
-
import { aggregate,
|
|
4
|
+
import { aggregate, columnOffsets, createGlobalFilter, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, sortRows, toText, virtualColumnRange, virtualRange, } from '@konce-pt/table';
|
|
5
5
|
import { cn } from "../internal/cn.js";
|
|
6
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
7
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
8
8
|
import { KptButton } from "../button/button.js";
|
|
9
|
+
import { KptSpeechButton } from "../speech/speech-button.js";
|
|
10
|
+
import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
|
|
9
11
|
import { KptDatepicker } from "../datepicker/datepicker.js";
|
|
10
12
|
import { KptDateRange } from "../date-range/date-range.js";
|
|
11
13
|
import { KptIcon } from "../icon/icon.js";
|
|
@@ -18,8 +20,31 @@ import { exportTableCsv } from "./export-csv.js";
|
|
|
18
20
|
const REM_PX = 16;
|
|
19
21
|
const CHECKBOX_FROZEN_W = 3; // rem
|
|
20
22
|
const EXPANDER_FROZEN_W = 2.5; // rem
|
|
21
|
-
|
|
22
|
-
|
|
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. */
|
|
23
48
|
const OVERLAY_EDITORS = new Set(['date', 'select', 'multiselect']);
|
|
24
49
|
/** Tablice (multiselect) porównujemy po zawartości, resztę po referencji. */
|
|
25
50
|
function sameEditValue(a, b) {
|
|
@@ -29,6 +54,16 @@ function sameEditValue(a, b) {
|
|
|
29
54
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
30
55
|
return false;
|
|
31
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
|
+
}
|
|
32
67
|
const EMPTY_ROWS = [];
|
|
33
68
|
const EMPTY_FIELDS = [];
|
|
34
69
|
/**
|
|
@@ -47,7 +82,7 @@ const EMPTY_FIELDS = [];
|
|
|
47
82
|
function getValue(row, field) {
|
|
48
83
|
return row[field];
|
|
49
84
|
}
|
|
50
|
-
export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, 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, 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 }) {
|
|
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 }) {
|
|
51
86
|
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
52
87
|
const uid = useId();
|
|
53
88
|
const rowsClickable = clickableRows ?? onRowClick !== undefined;
|
|
@@ -71,13 +106,34 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
71
106
|
onChange: onHiddenFieldsChange,
|
|
72
107
|
});
|
|
73
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);
|
|
74
123
|
const [columnFilters, setColumnFilters] = useState({});
|
|
75
124
|
const [sortField, setSortField] = useState(null);
|
|
76
125
|
const [sortDir, setSortDir] = useState(null);
|
|
77
126
|
const [pageIndex, setPageIndex] = useState(0);
|
|
78
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
|
+
}
|
|
79
135
|
// Kolejność kolumn żyje w stanie (przestawianie), ale nowe `columns` z zewnątrz ją zastępują.
|
|
80
|
-
// Poprzednie `columns` trzymamy w stanie, nie w refie
|
|
136
|
+
// Poprzednie `columns` trzymamy w stanie, nie w refie - wzorzec "zmiana propsa resetuje stan"
|
|
81
137
|
// z react.dev; ref zależałby od tego, czy React nie wyrzucił renderu (StrictMode, concurrent).
|
|
82
138
|
const [order, setOrder] = useState(columns);
|
|
83
139
|
const [seenColumns, setSeenColumns] = useState(columns);
|
|
@@ -88,8 +144,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
88
144
|
/** Zmierzone szerokości kolumn zamrożonych bez szerokości liczalnej z jednostki (`%`, `ch`). */
|
|
89
145
|
const [measuredWidths, setMeasuredWidths] = useState(() => new Map());
|
|
90
146
|
const [dragField, setDragField] = useState(null);
|
|
91
|
-
|
|
92
|
-
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);
|
|
93
151
|
const [theadOffset, setTheadOffset] = useState(0);
|
|
94
152
|
const scrollRef = useRef(null);
|
|
95
153
|
const theadRef = useRef(null);
|
|
@@ -101,13 +159,19 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
101
159
|
const hasColumnFilters = columns.some((col) => col.filter);
|
|
102
160
|
const hasRowDetail = expandable && !!renderRowDetail;
|
|
103
161
|
const sameRow = (a, b) => rowKey ? getValue(a, rowKey) === getValue(b, rowKey) : a === b;
|
|
104
|
-
|
|
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]);
|
|
105
170
|
const filtered = useMemo(() => {
|
|
106
|
-
const fields = columns.map((col) => col.field);
|
|
107
171
|
let rows = data;
|
|
108
172
|
if (filterText.trim()) {
|
|
109
|
-
// Bez diakrytyków i wielkości liter
|
|
110
|
-
rows = rows.filter((row) =>
|
|
173
|
+
// Bez diakrytyków i wielkości liter - "lodz" znajduje "Łódź" (`@konce-pt/table`).
|
|
174
|
+
rows = rows.filter((row) => globalFilter(row, filterText));
|
|
111
175
|
}
|
|
112
176
|
if (hasColumnFilters) {
|
|
113
177
|
rows = rows.filter((row) => columns.every((col) => {
|
|
@@ -120,14 +184,38 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
120
184
|
}));
|
|
121
185
|
}
|
|
122
186
|
return rows;
|
|
123
|
-
}, [columnFilters, columns, data, filterText, hasColumnFilters]);
|
|
187
|
+
}, [columnFilters, columns, data, filterText, globalFilter, hasColumnFilters]);
|
|
124
188
|
const sorted = useMemo(() => {
|
|
125
189
|
if (!sortField || !sortDir)
|
|
126
190
|
return filtered;
|
|
127
|
-
// Kolator dla aktywnego języka,
|
|
128
|
-
|
|
129
|
-
return [...filtered].sort((a, b) => compare(a, b));
|
|
191
|
+
// Kolator dla aktywnego języka, klucze liczone raz na wiersz; puste komórki na końcu.
|
|
192
|
+
return sortRows(filtered, sortField, sortDir, activeLocale);
|
|
130
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
|
+
}, []);
|
|
131
219
|
const paged = useMemo(() => {
|
|
132
220
|
if (lazy)
|
|
133
221
|
return data;
|
|
@@ -149,22 +237,27 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
149
237
|
// Offset sticky drugiego wiersza nagłówka = wysokość pierwszego.
|
|
150
238
|
useLayoutEffect(() => {
|
|
151
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.
|
|
152
242
|
if (first)
|
|
153
|
-
setTheadOffset(first.
|
|
243
|
+
setTheadOffset(first.getBoundingClientRect().height);
|
|
154
244
|
}, [filterRowPosition, hasColumnFilters, visibleColumns]);
|
|
155
|
-
//
|
|
245
|
+
// Rozmiar viewportu dla wirtualizacji - także po zmianie rozmiaru kontenera bez przewinięcia.
|
|
156
246
|
useLayoutEffect(() => {
|
|
157
247
|
const el = scrollRef.current;
|
|
158
248
|
if (!el)
|
|
159
249
|
return;
|
|
160
|
-
|
|
250
|
+
const measure = () => setViewport((prev) => prev.height === el.clientHeight && prev.width === el.clientWidth
|
|
251
|
+
? prev
|
|
252
|
+
: { height: el.clientHeight, width: el.clientWidth });
|
|
253
|
+
measure();
|
|
161
254
|
if (typeof ResizeObserver === 'undefined')
|
|
162
255
|
return;
|
|
163
|
-
const observer = new ResizeObserver(
|
|
256
|
+
const observer = new ResizeObserver(measure);
|
|
164
257
|
observer.observe(el);
|
|
165
258
|
return () => observer.disconnect();
|
|
166
259
|
}, []);
|
|
167
|
-
// Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki
|
|
260
|
+
// Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki - mierzone z nagłówków.
|
|
168
261
|
// Stan zmienia się tylko przy innej wartości, więc pomiar nie zapętla renderu.
|
|
169
262
|
useLayoutEffect(() => {
|
|
170
263
|
const head = theadRef.current;
|
|
@@ -182,7 +275,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
182
275
|
}
|
|
183
276
|
setMeasuredWidths((prev) => prev.size === next.size && [...next].every(([key, value]) => prev.get(key) === value) ? prev : next);
|
|
184
277
|
}, [visibleColumns]);
|
|
185
|
-
// Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu
|
|
278
|
+
// Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu - fokus idzie za nim.
|
|
186
279
|
useLayoutEffect(() => {
|
|
187
280
|
const field = focusGrip.current;
|
|
188
281
|
if (field === null)
|
|
@@ -192,28 +285,17 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
192
285
|
?.querySelector(`th[data-field="${CSS.escape(field)}"] .kpt-data-table__grip`)
|
|
193
286
|
?.focus();
|
|
194
287
|
}, [order]);
|
|
195
|
-
// Wejście w edycję
|
|
288
|
+
// Wejście w edycję -> fokus na kontrolce edytora.
|
|
196
289
|
useEffect(() => {
|
|
197
290
|
if (!editingCell)
|
|
198
291
|
return;
|
|
199
292
|
editorHostRef.current?.querySelector('input, .kpt-select, [tabindex]')?.focus();
|
|
200
293
|
}, [editingCell]);
|
|
201
|
-
//
|
|
202
|
-
const virtualStart = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);
|
|
203
|
-
const virtualEnd = Math.min(sorted.length, virtualStart + Math.ceil(viewportHeight / rowHeight) + VIRTUAL_BUFFER * 2);
|
|
204
|
-
const virtualRows = sorted.slice(virtualStart, virtualEnd);
|
|
205
|
-
const topPad = virtualStart * rowHeight;
|
|
206
|
-
const bottomPad = (sorted.length - virtualEnd) * rowHeight;
|
|
207
|
-
const onScroll = (event) => {
|
|
208
|
-
if (!virtualScroll)
|
|
209
|
-
return;
|
|
210
|
-
const el = event.currentTarget;
|
|
211
|
-
setScrollTop(el.scrollTop);
|
|
212
|
-
setViewportHeight(el.clientHeight);
|
|
213
|
-
};
|
|
214
|
-
// ── Kolumny zamrożone ──
|
|
294
|
+
// -- Kolumny zamrożone --
|
|
215
295
|
const leadingCells = (selectable !== 'none' ? 1 : 0) + (hasRowDetail ? 1 : 0);
|
|
216
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;
|
|
217
299
|
const frozenOffsets = useMemo(() => {
|
|
218
300
|
const map = new Map();
|
|
219
301
|
let acc = leadingStickyWidth;
|
|
@@ -221,16 +303,108 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
221
303
|
if (!col.frozen)
|
|
222
304
|
break; // tylko wiodące, sąsiadujące
|
|
223
305
|
map.set(col.field, acc);
|
|
224
|
-
|
|
225
|
-
acc += cssLengthToPx(col.width, REM_PX) ?? measuredWidths.get(col.field) ?? 8 * REM_PX;
|
|
306
|
+
acc += columnPx(col, virtualScroll, defaultColumnPx, measuredWidths);
|
|
226
307
|
}
|
|
227
308
|
return map;
|
|
228
|
-
}, [leadingStickyWidth, measuredWidths, visibleColumns]);
|
|
229
|
-
|
|
230
|
-
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);
|
|
231
312
|
const expanderStickyLeft = hasFrozen ? 0 : undefined;
|
|
232
313
|
const checkboxStickyLeft = hasFrozen ? (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) : undefined;
|
|
233
|
-
//
|
|
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 --
|
|
234
408
|
const frozenLeadCount = (() => {
|
|
235
409
|
let n = 0;
|
|
236
410
|
for (const col of visibleColumns) {
|
|
@@ -259,7 +433,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
259
433
|
setOrder(cols);
|
|
260
434
|
setDragField(null);
|
|
261
435
|
};
|
|
262
|
-
/** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce
|
|
436
|
+
/** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce - alternatywa dla przeciągania. */
|
|
263
437
|
const onGripKeyDown = (field, event) => {
|
|
264
438
|
const delta = event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0;
|
|
265
439
|
if (!delta)
|
|
@@ -267,7 +441,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
267
441
|
event.preventDefault();
|
|
268
442
|
const from = visibleColumns.findIndex((col) => col.field === field);
|
|
269
443
|
const to = from + delta;
|
|
270
|
-
// Kolumny zamrożone zostają wiodące
|
|
444
|
+
// Kolumny zamrożone zostają wiodące - nie wolno ich minąć ani wyjechać poza tabelę.
|
|
271
445
|
if (from === -1 || to < frozenLeadCount || to >= visibleColumns.length)
|
|
272
446
|
return;
|
|
273
447
|
const cols = [...order];
|
|
@@ -279,10 +453,11 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
279
453
|
setMoveAnnouncement(t('dataTable.columnMoved', { column: moved.header, position: to + 1, count: visibleColumns.length }));
|
|
280
454
|
focusGrip.current = field;
|
|
281
455
|
};
|
|
282
|
-
//
|
|
456
|
+
// -- Sortowanie --
|
|
283
457
|
const toggleSort = (col) => {
|
|
284
458
|
if (!col.sortable)
|
|
285
459
|
return;
|
|
460
|
+
scrollToTop();
|
|
286
461
|
if (sortField !== col.field) {
|
|
287
462
|
setSortField(col.field);
|
|
288
463
|
setSortDir('asc');
|
|
@@ -302,10 +477,26 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
302
477
|
return 'none';
|
|
303
478
|
return sortDir === 'asc' ? 'ascending' : 'descending';
|
|
304
479
|
};
|
|
305
|
-
//
|
|
480
|
+
// -- Filtry kolumn --
|
|
306
481
|
const setColumnFilter = (field, value) => {
|
|
307
482
|
setColumnFilters((current) => ({ ...current, [field]: value }));
|
|
308
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);
|
|
309
500
|
};
|
|
310
501
|
const rangeVal = (field) => {
|
|
311
502
|
const value = columnFilters[field];
|
|
@@ -316,8 +507,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
316
507
|
const max = col.filter?.max ?? 100;
|
|
317
508
|
return columnFilters[col.field] ?? [min, max];
|
|
318
509
|
};
|
|
319
|
-
//
|
|
320
|
-
|
|
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));
|
|
321
514
|
const toggleRow = (row) => {
|
|
322
515
|
if (selectable === 'none')
|
|
323
516
|
return;
|
|
@@ -330,20 +523,24 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
330
523
|
const pageAllSelected = paged.length > 0 && paged.every((row) => isSelected(row));
|
|
331
524
|
const pageSomeSelected = paged.some((row) => isSelected(row)) && !pageAllSelected;
|
|
332
525
|
const toggleAllOnPage = () => {
|
|
333
|
-
if (pageAllSelected)
|
|
334
|
-
|
|
526
|
+
if (pageAllSelected) {
|
|
527
|
+
const pageKeys = new Set(paged.map(keyOf));
|
|
528
|
+
setPicked(picked.filter((r) => !pageKeys.has(keyOf(r))));
|
|
529
|
+
}
|
|
335
530
|
else
|
|
336
531
|
setPicked([...picked, ...paged.filter((row) => !isSelected(row))]);
|
|
337
532
|
};
|
|
338
|
-
//
|
|
339
|
-
|
|
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));
|
|
340
537
|
const toggleExpand = (row) => {
|
|
341
538
|
if (isExpanded(row))
|
|
342
539
|
setExpanded(expanded.filter((r) => !sameRow(r, row)));
|
|
343
540
|
else
|
|
344
541
|
setExpanded(multiExpand ? [...expanded, row] : [row]);
|
|
345
542
|
};
|
|
346
|
-
//
|
|
543
|
+
// -- Edycja inline --
|
|
347
544
|
const isEditable = (col) => !!col.edit || !!cellEditors?.[col.field];
|
|
348
545
|
const isEditingCell = (row, field) => !!editingCell && editingCell.field === field && sameRow(editingCell.row, row);
|
|
349
546
|
const startEdit = (row, col, event) => {
|
|
@@ -373,7 +570,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
373
570
|
/**
|
|
374
571
|
* Edytory bez panelu (text/number, własne) zatwierdza dopiero wyjście fokusu poza CAŁY edytor:
|
|
375
572
|
* `KptInputNumber` ma fokusowalne strzałki obok pola, więc reakcja na blur samej kontrolki
|
|
376
|
-
* zamykałaby edycję przy każdym kliknięciu w
|
|
573
|
+
* zamykałaby edycję przy każdym kliknięciu w "+".
|
|
377
574
|
*/
|
|
378
575
|
const onEditorFocusOut = (event) => {
|
|
379
576
|
if (editorPanelOpen)
|
|
@@ -389,7 +586,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
389
586
|
if (!open)
|
|
390
587
|
commitEdit();
|
|
391
588
|
};
|
|
392
|
-
//
|
|
589
|
+
// -- Wiersz podsumowania --
|
|
393
590
|
const summarySource = lazy ? EMPTY_ROWS : filtered;
|
|
394
591
|
const overrides = summaryValues ?? {};
|
|
395
592
|
const summaryFields = visibleColumns
|
|
@@ -397,22 +594,61 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
397
594
|
.map((col) => col.field);
|
|
398
595
|
const hasSummary = summaryFields.length > 0;
|
|
399
596
|
const summaryStartIndex = summaryFields[0] === undefined ? -1 : visibleColumns.findIndex((col) => col.field === summaryFields[0]);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
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;
|
|
413
648
|
const displayCount = virtualScroll ? sorted.length : paged.length;
|
|
414
649
|
const rowsToRender = virtualScroll ? virtualRows : paged;
|
|
415
|
-
//
|
|
650
|
+
// -- Renderowanie --
|
|
651
|
+
const hspacer = (slot) => (_jsx("td", { className: "kpt-data-table__hspacer", "aria-hidden": "true" }, slot.key));
|
|
416
652
|
const filterCell = (col) => {
|
|
417
653
|
const filter = col.filter;
|
|
418
654
|
if (!filter)
|
|
@@ -435,7 +671,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
435
671
|
return null;
|
|
436
672
|
}
|
|
437
673
|
};
|
|
438
|
-
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)))] }));
|
|
439
676
|
const editor = (row, col) => {
|
|
440
677
|
const custom = cellEditors?.[col.field];
|
|
441
678
|
if (custom) {
|
|
@@ -474,9 +711,9 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
474
711
|
return null;
|
|
475
712
|
}
|
|
476
713
|
};
|
|
477
|
-
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', rowsClickable && 'kpt-data-table__row--clickable'), style: { height: virtualScroll ? rowHeight : undefined }, tabIndex: rowsClickable ? 0 : undefined, onClick: () => onRowClick?.(row), onKeyDown: rowsClickable
|
|
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
|
|
478
715
|
? (event) => {
|
|
479
|
-
// Tylko na samym wierszu
|
|
716
|
+
// Tylko na samym wierszu - Enter w kontrolce komórki należy do niej.
|
|
480
717
|
if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' '))
|
|
481
718
|
return;
|
|
482
719
|
event.preventDefault();
|
|
@@ -490,34 +727,42 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
490
727
|
toggleRow(row);
|
|
491
728
|
}, onChange: () => {
|
|
492
729
|
// Zmianę obsługuje `onClick` (radio nie zgłasza `change` przy odznaczaniu).
|
|
493
|
-
} }) })) : null,
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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)
|
|
498
740
|
startEdit(row, col, event);
|
|
499
741
|
}, onKeyDown: (event) => {
|
|
500
742
|
if (event.key === 'Enter' && editable)
|
|
501
743
|
startEdit(row, col, event);
|
|
502
|
-
}, 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));
|
|
503
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));
|
|
504
|
-
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'), "aria-label": t('dataTable.search'), value:
|
|
505
|
-
setFilterText(event.target.value);
|
|
506
|
-
setPageIndex(0);
|
|
507
|
-
} })) : 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'), "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: caption }) : null, _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) => {
|
|
508
747
|
if (el)
|
|
509
748
|
el.indeterminate = pageSomeSelected;
|
|
510
|
-
}, "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,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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] }));
|
|
523
768
|
}
|