@konce-pt/react 0.11.1 → 0.12.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 +8 -2
- package/dist/accordion/accordion.js +11 -10
- package/dist/alert/alert.js +1 -0
- package/dist/alert/llms.txt +1 -0
- package/dist/app-shell/app-shell.js +12 -5
- package/dist/app-shell/llms.txt +5 -4
- package/dist/auth/auth.js +17 -8
- package/dist/auth/llms.txt +3 -2
- package/dist/autocomplete/autocomplete.d.ts +1 -1
- package/dist/autocomplete/autocomplete.js +12 -4
- package/dist/autocomplete/llms.txt +3 -0
- package/dist/avatar/avatar.js +1 -0
- package/dist/avatar-group/avatar-group.js +1 -0
- package/dist/backdrop/backdrop.d.ts +0 -19
- package/dist/backdrop/backdrop.js +18 -13
- package/dist/bottom-sheet/bottom-sheet.d.ts +6 -1
- package/dist/bottom-sheet/bottom-sheet.js +3 -2
- package/dist/bottom-sheet/llms.txt +3 -0
- package/dist/breadcrumb/breadcrumb.d.ts +7 -1
- package/dist/breadcrumb/breadcrumb.js +10 -2
- package/dist/breadcrumb/llms.txt +8 -0
- package/dist/button/button.js +2 -1
- package/dist/button-group/button-group.d.ts +15 -2
- package/dist/button-group/button-group.js +31 -4
- package/dist/button-group/llms.txt +9 -6
- package/dist/card/card.js +4 -3
- package/dist/carousel/carousel.js +4 -1
- package/dist/charts/chart-legend.js +1 -0
- package/dist/charts/chart.d.ts +3 -2
- package/dist/charts/chart.js +12 -4
- package/dist/charts/llms.txt +6 -3
- package/dist/chat/chat.js +39 -9
- package/dist/chat/llms.txt +9 -0
- package/dist/chat-composer/chat-composer.d.ts +5 -2
- package/dist/chat-composer/chat-composer.js +10 -1
- package/dist/chat-dock/chat-dock.js +1 -0
- package/dist/chat-message/chat-message.js +2 -1
- package/dist/chat-typing/chat-typing.js +1 -0
- package/dist/checkbox/checkbox.d.ts +1 -1
- package/dist/checkbox/checkbox.js +9 -2
- package/dist/checkbox/llms.txt +3 -0
- package/dist/chip/chip.js +1 -0
- package/dist/chips-input/chips-input.d.ts +3 -1
- package/dist/chips-input/chips-input.js +14 -5
- package/dist/chips-input/llms.txt +3 -0
- package/dist/clock/clock.d.ts +9 -4
- package/dist/clock/clock.js +35 -5
- package/dist/clock/llms.txt +6 -2
- package/dist/color-picker/color-picker.d.ts +5 -1
- package/dist/color-picker/color-picker.js +36 -17
- package/dist/color-picker/llms.txt +3 -0
- package/dist/confirm/confirm-container.js +1 -0
- package/dist/confirm/confirm-store.js +6 -0
- package/dist/confirm/llms.txt +2 -1
- package/dist/context-menu/context-menu.js +35 -12
- package/dist/context-menu/llms.txt +3 -0
- package/dist/data-table/data-table.d.ts +12 -1
- package/dist/data-table/data-table.js +113 -51
- package/dist/data-table/export-csv.d.ts +2 -2
- package/dist/data-table/export-csv.js +7 -9
- package/dist/data-table/llms.txt +19 -4
- package/dist/data-view/data-view.js +1 -0
- package/dist/date-range/date-range.d.ts +16 -4
- package/dist/date-range/date-range.js +69 -46
- package/dist/date-range/llms.txt +17 -3
- package/dist/datepicker/datepicker.d.ts +17 -3
- package/dist/datepicker/datepicker.js +86 -54
- package/dist/datepicker/llms.txt +17 -3
- package/dist/dialog/dialog.js +29 -8
- package/dist/dialog/llms.txt +3 -0
- package/dist/divider/divider.d.ts +7 -1
- package/dist/divider/divider.js +3 -2
- package/dist/divider/llms.txt +1 -0
- package/dist/drawer/drawer.d.ts +6 -1
- package/dist/drawer/drawer.js +3 -2
- package/dist/drawer/llms.txt +3 -0
- package/dist/fieldset/fieldset.js +4 -1
- package/dist/file-upload/file-upload.d.ts +7 -1
- package/dist/file-upload/file-upload.js +18 -7
- package/dist/file-upload/llms.txt +6 -3
- package/dist/form-field/form-field.js +1 -0
- package/dist/galleria/galleria.js +4 -1
- package/dist/i18n/i18n-context.d.ts +3 -3
- package/dist/i18n/i18n-context.js +25 -3
- package/dist/icon/icon.js +4 -1
- package/dist/image/image.js +4 -4
- package/dist/input/input.js +1 -0
- package/dist/input-mask/input-mask.d.ts +7 -2
- package/dist/input-mask/input-mask.js +15 -41
- package/dist/input-mask/llms.txt +3 -0
- package/dist/input-number/input-number.d.ts +3 -1
- package/dist/input-number/input-number.js +15 -11
- package/dist/input-number/llms.txt +5 -2
- package/dist/input-otp/input-otp.d.ts +16 -3
- package/dist/input-otp/input-otp.js +43 -19
- package/dist/input-otp/llms.txt +11 -0
- 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/overlay.js +37 -7
- package/dist/internal/password-strength.d.ts +6 -0
- package/dist/internal/password-strength.js +74 -0
- 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/step.d.ts +12 -0
- package/dist/internal/step.js +31 -0
- package/dist/internal/tween.js +2 -3
- package/dist/internal/use-controllable-state.d.ts +5 -0
- package/dist/internal/use-controllable-state.js +25 -4
- package/dist/internal/use-modal-focus.d.ts +1 -0
- package/dist/internal/use-modal-focus.js +12 -1
- package/dist/knob/knob.d.ts +5 -2
- package/dist/knob/knob.js +15 -14
- package/dist/knob/llms.txt +4 -2
- package/dist/listbox/listbox.d.ts +3 -1
- package/dist/listbox/listbox.js +30 -10
- package/dist/listbox/llms.txt +3 -0
- package/dist/map/llms.txt +5 -1
- package/dist/map/map-create-dialog.js +30 -22
- package/dist/map/map-hours-field.js +1 -0
- package/dist/map/map-image-field.js +2 -1
- package/dist/map/map-legend.js +1 -0
- package/dist/map/map-poi-card.js +2 -1
- package/dist/map/map-search.js +54 -30
- package/dist/map/map.d.ts +6 -1
- package/dist/map/map.js +62 -34
- package/dist/megamenu/llms.txt +11 -0
- package/dist/megamenu/megamenu.d.ts +7 -1
- package/dist/megamenu/megamenu.js +15 -6
- package/dist/menu/menu-rows.js +1 -0
- package/dist/menu/menu.js +10 -3
- package/dist/menubar/llms.txt +11 -0
- package/dist/menubar/menubar.d.ts +7 -1
- package/dist/menubar/menubar.js +15 -3
- package/dist/meter-group/meter-group.js +1 -0
- package/dist/order-list/llms.txt +3 -0
- package/dist/order-list/order-list.js +21 -6
- package/dist/paginator/paginator.js +7 -4
- package/dist/panel/panel.js +5 -3
- package/dist/password/llms.txt +6 -2
- package/dist/password/password.js +4 -14
- package/dist/peek/llms.txt +1 -1
- package/dist/peek/peek.js +37 -15
- package/dist/pick-list/llms.txt +3 -0
- package/dist/pick-list/pick-list.js +19 -3
- package/dist/popover/llms.txt +3 -0
- package/dist/popover/popover.d.ts +3 -1
- package/dist/popover/popover.js +10 -3
- package/dist/progress/progress.js +1 -0
- package/dist/public-api.d.ts +1 -1
- package/dist/radio-group/llms.txt +3 -0
- package/dist/radio-group/radio-group.d.ts +3 -1
- package/dist/radio-group/radio-group.js +13 -2
- package/dist/rating/rating.js +1 -0
- package/dist/rich-text/llms.txt +14 -0
- package/dist/rich-text/rich-text.d.ts +3 -9
- package/dist/rich-text/rich-text.js +241 -96
- package/dist/rich-text/sanitize.d.ts +9 -0
- package/dist/rich-text/sanitize.js +77 -29
- package/dist/roadmap/llms.txt +9 -3
- package/dist/roadmap/roadmap.js +88 -11
- package/dist/scroll-top/scroll-top.js +1 -0
- package/dist/select/llms.txt +3 -0
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +46 -32
- package/dist/shared/types.d.ts +21 -0
- package/dist/shared/types.js +9 -1
- package/dist/sidenav/sidenav.d.ts +1 -2
- package/dist/sidenav/sidenav.js +4 -3
- package/dist/slider/llms.txt +3 -0
- package/dist/slider/slider.d.ts +3 -1
- package/dist/slider/slider.js +15 -4
- package/dist/speed-dial/speed-dial.js +2 -1
- package/dist/spinner/spinner.js +1 -0
- package/dist/split-button/split-button.js +10 -3
- package/dist/splitter/splitter.js +23 -8
- package/dist/stepper/llms.txt +3 -0
- package/dist/stepper/stepper.js +9 -5
- package/dist/switch/llms.txt +3 -0
- package/dist/switch/switch.d.ts +1 -1
- package/dist/switch/switch.js +9 -2
- package/dist/switch-group/llms.txt +3 -0
- package/dist/switch-group/switch-group.d.ts +3 -1
- package/dist/switch-group/switch-group.js +5 -3
- package/dist/tabs/llms.txt +3 -0
- package/dist/tabs/tabs.js +30 -14
- package/dist/textarea/textarea.js +1 -0
- package/dist/toast/llms.txt +3 -0
- package/dist/toast/toast-container.js +1 -0
- package/dist/toast/toast-store.d.ts +5 -1
- package/dist/toast/toast-store.js +1 -1
- package/dist/tooltip/llms.txt +5 -3
- package/dist/tooltip/tooltip.d.ts +3 -2
- package/dist/tooltip/tooltip.js +148 -55
- package/dist/tree/llms.txt +1 -0
- package/dist/tree/tree.d.ts +6 -1
- package/dist/tree/tree.js +14 -7
- package/package.json +15 -14
|
@@ -1,6 +1,7 @@
|
|
|
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, createRowComparator, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, matchesGlobalFilter, toText, } 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";
|
|
@@ -46,8 +47,14 @@ const EMPTY_FIELDS = [];
|
|
|
46
47
|
function getValue(row, field) {
|
|
47
48
|
return row[field];
|
|
48
49
|
}
|
|
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 });
|
|
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 }) {
|
|
51
|
+
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
52
|
+
const uid = useId();
|
|
53
|
+
const rowsClickable = clickableRows ?? onRowClick !== undefined;
|
|
54
|
+
/** Ogłoszenie po przestawieniu kolumny z klawiatury. */
|
|
55
|
+
const [moveAnnouncement, setMoveAnnouncement] = useState('');
|
|
56
|
+
/** Pole kolumny, której uchwyt ma dostać fokus po przerysowaniu (ustawiane w zdarzeniu, czytane w efekcie). */
|
|
57
|
+
const focusGrip = useRef(null);
|
|
51
58
|
const [picked, setPicked] = useControllableState({
|
|
52
59
|
value: selection,
|
|
53
60
|
defaultValue: defaultSelection,
|
|
@@ -58,7 +65,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
58
65
|
defaultValue: defaultExpandedRows,
|
|
59
66
|
onChange: onExpandedRowsChange,
|
|
60
67
|
});
|
|
61
|
-
const [hidden
|
|
68
|
+
const [hidden] = useControllableState({
|
|
62
69
|
value: hiddenFields,
|
|
63
70
|
defaultValue: defaultHiddenFields,
|
|
64
71
|
onChange: onHiddenFieldsChange,
|
|
@@ -70,13 +77,16 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
70
77
|
const [pageIndex, setPageIndex] = useState(0);
|
|
71
78
|
const [pageSizeState, setPageSizeState] = useState(pageSize || 10);
|
|
72
79
|
// Kolejność kolumn żyje w stanie (przestawianie), ale nowe `columns` z zewnątrz ją zastępują.
|
|
80
|
+
// Poprzednie `columns` trzymamy w stanie, nie w refie — wzorzec „zmiana propsa resetuje stan”
|
|
81
|
+
// z react.dev; ref zależałby od tego, czy React nie wyrzucił renderu (StrictMode, concurrent).
|
|
73
82
|
const [order, setOrder] = useState(columns);
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
setOrder(columns);
|
|
83
|
+
const [seenColumns, setSeenColumns] = useState(columns);
|
|
84
|
+
if (seenColumns !== columns) {
|
|
85
|
+
setSeenColumns(columns);
|
|
86
|
+
setOrder(columns);
|
|
79
87
|
}
|
|
88
|
+
/** Zmierzone szerokości kolumn zamrożonych bez szerokości liczalnej z jednostki (`%`, `ch`). */
|
|
89
|
+
const [measuredWidths, setMeasuredWidths] = useState(() => new Map());
|
|
80
90
|
const [dragField, setDragField] = useState(null);
|
|
81
91
|
const [scrollTop, setScrollTop] = useState(0);
|
|
82
92
|
const [viewportHeight, setViewportHeight] = useState(400);
|
|
@@ -93,10 +103,11 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
93
103
|
const sameRow = (a, b) => rowKey ? getValue(a, rowKey) === getValue(b, rowKey) : a === b;
|
|
94
104
|
// ── Potok danych ──
|
|
95
105
|
const filtered = useMemo(() => {
|
|
96
|
-
const
|
|
106
|
+
const fields = columns.map((col) => col.field);
|
|
97
107
|
let rows = data;
|
|
98
|
-
if (
|
|
99
|
-
|
|
108
|
+
if (filterText.trim()) {
|
|
109
|
+
// Bez diakrytyków i wielkości liter — „lodz” znajduje „Łódź” (`@konce-pt/table`).
|
|
110
|
+
rows = rows.filter((row) => matchesGlobalFilter(row, fields, filterText));
|
|
100
111
|
}
|
|
101
112
|
if (hasColumnFilters) {
|
|
102
113
|
rows = rows.filter((row) => columns.every((col) => {
|
|
@@ -113,20 +124,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
113
124
|
const sorted = useMemo(() => {
|
|
114
125
|
if (!sortField || !sortDir)
|
|
115
126
|
return filtered;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Puste komórki lądują na końcu niezależnie od kierunku — sortowanie ma pokazywać dane.
|
|
121
|
-
if (av == null)
|
|
122
|
-
return 1;
|
|
123
|
-
if (bv == null)
|
|
124
|
-
return -1;
|
|
125
|
-
if (typeof av === 'number' && typeof bv === 'number')
|
|
126
|
-
return (av - bv) * sign;
|
|
127
|
-
return toText(av).localeCompare(toText(bv)) * sign;
|
|
128
|
-
});
|
|
129
|
-
}, [filtered, sortDir, sortField]);
|
|
127
|
+
// Kolator dla aktywnego języka, tworzony raz na sortowanie; puste komórki na końcu.
|
|
128
|
+
const compare = createRowComparator(sortField, sortDir, activeLocale);
|
|
129
|
+
return [...filtered].sort((a, b) => compare(a, b));
|
|
130
|
+
}, [activeLocale, filtered, sortDir, sortField]);
|
|
130
131
|
const paged = useMemo(() => {
|
|
131
132
|
if (lazy)
|
|
132
133
|
return data;
|
|
@@ -136,14 +137,14 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
136
137
|
return sorted.slice(start, start + pageSizeState);
|
|
137
138
|
}, [data, lazy, pageIndex, pageSize, pageSizeState, sorted]);
|
|
138
139
|
const filteredCount = lazy ? (totalRecords ?? data.length) : filtered.length;
|
|
139
|
-
// Tryb lazy: każda zmiana stanu to prośba do rodzica o dane.
|
|
140
|
+
// Tryb lazy: każda zmiana stanu to prośba do rodzica o dane. `onLazyLoad` czytamy przez
|
|
141
|
+
// `useEffectEvent`: nowa referencja funkcji przy każdym renderze rodzica nie może restartować
|
|
142
|
+
// efektu, bo wywołałaby pętlę żądań.
|
|
143
|
+
const requestLazyLoad = useEffectEvent((state) => onLazyLoad?.(state));
|
|
140
144
|
useEffect(() => {
|
|
141
145
|
if (!lazy)
|
|
142
146
|
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
|
|
147
|
+
requestLazyLoad({ pageIndex, pageSize: pageSizeState, sortField, sortDir, filter: filterText, columnFilters });
|
|
147
148
|
}, [lazy, pageIndex, pageSizeState, sortField, sortDir, filterText, columnFilters]);
|
|
148
149
|
// Offset sticky drugiego wiersza nagłówka = wysokość pierwszego.
|
|
149
150
|
useLayoutEffect(() => {
|
|
@@ -151,11 +152,46 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
151
152
|
if (first)
|
|
152
153
|
setTheadOffset(first.offsetHeight);
|
|
153
154
|
}, [filterRowPosition, hasColumnFilters, visibleColumns]);
|
|
155
|
+
// Wysokość viewportu dla wirtualizacji — także po zmianie rozmiaru kontenera bez przewinięcia.
|
|
154
156
|
useLayoutEffect(() => {
|
|
155
157
|
const el = scrollRef.current;
|
|
156
|
-
if (el)
|
|
157
|
-
|
|
158
|
+
if (!el)
|
|
159
|
+
return;
|
|
160
|
+
setViewportHeight(el.clientHeight);
|
|
161
|
+
if (typeof ResizeObserver === 'undefined')
|
|
162
|
+
return;
|
|
163
|
+
const observer = new ResizeObserver(() => setViewportHeight(el.clientHeight));
|
|
164
|
+
observer.observe(el);
|
|
165
|
+
return () => observer.disconnect();
|
|
158
166
|
}, []);
|
|
167
|
+
// Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki — mierzone z nagłówków.
|
|
168
|
+
// Stan zmienia się tylko przy innej wartości, więc pomiar nie zapętla renderu.
|
|
169
|
+
useLayoutEffect(() => {
|
|
170
|
+
const head = theadRef.current;
|
|
171
|
+
if (!head)
|
|
172
|
+
return;
|
|
173
|
+
const next = new Map();
|
|
174
|
+
for (const col of visibleColumns) {
|
|
175
|
+
if (!col.frozen)
|
|
176
|
+
break;
|
|
177
|
+
if (cssLengthToPx(col.width, REM_PX) !== null)
|
|
178
|
+
continue;
|
|
179
|
+
const th = head.querySelector(`th[data-field="${CSS.escape(col.field)}"]`);
|
|
180
|
+
if (th)
|
|
181
|
+
next.set(col.field, th.offsetWidth);
|
|
182
|
+
}
|
|
183
|
+
setMeasuredWidths((prev) => prev.size === next.size && [...next].every(([key, value]) => prev.get(key) === value) ? prev : next);
|
|
184
|
+
}, [visibleColumns]);
|
|
185
|
+
// Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu — fokus idzie za nim.
|
|
186
|
+
useLayoutEffect(() => {
|
|
187
|
+
const field = focusGrip.current;
|
|
188
|
+
if (field === null)
|
|
189
|
+
return;
|
|
190
|
+
focusGrip.current = null;
|
|
191
|
+
theadRef.current
|
|
192
|
+
?.querySelector(`th[data-field="${CSS.escape(field)}"] .kpt-data-table__grip`)
|
|
193
|
+
?.focus();
|
|
194
|
+
}, [order]);
|
|
159
195
|
// Wejście w edycję → fokus na kontrolce edytora.
|
|
160
196
|
useEffect(() => {
|
|
161
197
|
if (!editingCell)
|
|
@@ -176,14 +212,6 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
176
212
|
setViewportHeight(el.clientHeight);
|
|
177
213
|
};
|
|
178
214
|
// ── 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
|
-
};
|
|
187
215
|
const leadingCells = (selectable !== 'none' ? 1 : 0) + (hasRowDetail ? 1 : 0);
|
|
188
216
|
const leadingStickyWidth = (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) + (selectable !== 'none' ? CHECKBOX_FROZEN_W * REM_PX : 0);
|
|
189
217
|
const frozenOffsets = useMemo(() => {
|
|
@@ -193,10 +221,11 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
193
221
|
if (!col.frozen)
|
|
194
222
|
break; // tylko wiodące, sąsiadujące
|
|
195
223
|
map.set(col.field, acc);
|
|
196
|
-
|
|
224
|
+
// Z jednostki, potem z pomiaru, a do pierwszego pomiaru 8 rem.
|
|
225
|
+
acc += cssLengthToPx(col.width, REM_PX) ?? measuredWidths.get(col.field) ?? 8 * REM_PX;
|
|
197
226
|
}
|
|
198
227
|
return map;
|
|
199
|
-
}, [leadingStickyWidth, visibleColumns]);
|
|
228
|
+
}, [leadingStickyWidth, measuredWidths, visibleColumns]);
|
|
200
229
|
const hasFrozen = frozenOffsets.size > 0;
|
|
201
230
|
const frozenLeft = (field) => frozenOffsets.get(field);
|
|
202
231
|
const expanderStickyLeft = hasFrozen ? 0 : undefined;
|
|
@@ -230,6 +259,26 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
230
259
|
setOrder(cols);
|
|
231
260
|
setDragField(null);
|
|
232
261
|
};
|
|
262
|
+
/** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce — alternatywa dla przeciągania. */
|
|
263
|
+
const onGripKeyDown = (field, event) => {
|
|
264
|
+
const delta = event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0;
|
|
265
|
+
if (!delta)
|
|
266
|
+
return;
|
|
267
|
+
event.preventDefault();
|
|
268
|
+
const from = visibleColumns.findIndex((col) => col.field === field);
|
|
269
|
+
const to = from + delta;
|
|
270
|
+
// Kolumny zamrożone zostają wiodące — nie wolno ich minąć ani wyjechać poza tabelę.
|
|
271
|
+
if (from === -1 || to < frozenLeadCount || to >= visibleColumns.length)
|
|
272
|
+
return;
|
|
273
|
+
const cols = [...order];
|
|
274
|
+
const fromIdx = cols.findIndex((col) => col.field === field);
|
|
275
|
+
const toIdx = cols.findIndex((col) => col.field === visibleColumns[to].field);
|
|
276
|
+
const [moved] = cols.splice(fromIdx, 1);
|
|
277
|
+
cols.splice(toIdx, 0, moved);
|
|
278
|
+
setOrder(cols);
|
|
279
|
+
setMoveAnnouncement(t('dataTable.columnMoved', { column: moved.header, position: to + 1, count: visibleColumns.length }));
|
|
280
|
+
focusGrip.current = field;
|
|
281
|
+
};
|
|
233
282
|
// ── Sortowanie ──
|
|
234
283
|
const toggleSort = (col) => {
|
|
235
284
|
if (!col.sortable)
|
|
@@ -425,13 +474,26 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
425
474
|
return null;
|
|
426
475
|
}
|
|
427
476
|
};
|
|
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'
|
|
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
|
|
478
|
+
? (event) => {
|
|
479
|
+
// Tylko na samym wierszu — Enter w kontrolce komórki należy do niej.
|
|
480
|
+
if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' '))
|
|
481
|
+
return;
|
|
482
|
+
event.preventDefault();
|
|
483
|
+
onRowClick?.(row);
|
|
484
|
+
}
|
|
485
|
+
: 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
486
|
event.stopPropagation();
|
|
430
487
|
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:
|
|
488
|
+
}, 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) => {
|
|
489
|
+
event.stopPropagation();
|
|
490
|
+
toggleRow(row);
|
|
491
|
+
}, onChange: () => {
|
|
492
|
+
// Zmianę obsługuje `onClick` (radio nie zgłasza `change` przy odznaczaniu).
|
|
493
|
+
} }) })) : null, visibleColumns.map((col, colIndex) => {
|
|
432
494
|
const editing = isEditingCell(row, col.field);
|
|
433
495
|
const editable = isEditable(col) && !editing;
|
|
434
|
-
return (_jsx("td", { "data-align": col.align ?? 'start', style: { left: frozenLeft(col.field) }, className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen', editable && 'kpt-data-table__cell--editable'), tabIndex: editable ? 0 : undefined, "aria-
|
|
496
|
+
return (_jsx("td", { "data-align": col.align ?? 'start', style: { left: frozenLeft(col.field) }, className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen', editable && 'kpt-data-table__cell--editable'), tabIndex: editable ? 0 : undefined, "aria-describedby": editable ? `${uid}-edit-${colIndex}` : undefined, onClick: (event) => {
|
|
435
497
|
if (isEditable(col))
|
|
436
498
|
startEdit(row, col, event);
|
|
437
499
|
}, onKeyDown: (event) => {
|
|
@@ -439,13 +501,13 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
439
501
|
startEdit(row, col, event);
|
|
440
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) }, col.field));
|
|
441
503
|
})] }), 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: filterText, onChange: (event) => {
|
|
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: filterText, onChange: (event) => {
|
|
443
505
|
setFilterText(event.target.value);
|
|
444
506
|
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) => {
|
|
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) => {
|
|
446
508
|
if (el)
|
|
447
509
|
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, visibleColumns.map((col) => (_jsxs("th", { draggable: isColDraggable(col) || undefined, style: { width: col.width, left: frozenLeft(col.field) }, "data-align": col.align ?? 'start', className: cn(frozenOffsets.has(col.field) && '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'), "
|
|
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, visibleColumns.map((col) => (_jsxs("th", { draggable: isColDraggable(col) || undefined, style: { width: col.width, left: frozenLeft(col.field) }, "data-align": col.align ?? 'start', className: cn(frozenOffsets.has(col.field) && '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) => {
|
|
449
511
|
if (col.frozen)
|
|
450
512
|
return;
|
|
451
513
|
setDragField(col.field);
|
|
@@ -457,5 +519,5 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
|
|
|
457
519
|
const visIndex = visibleColumns.findIndex((c) => c.field === col.field);
|
|
458
520
|
if (dragField !== null && visIndex >= frozenLeadCount)
|
|
459
521
|
event.preventDefault();
|
|
460
|
-
}, onDrop: (event) => reorderable && onColDrop(col.field, event), onDragEnd: () => setDragField(null), children: [isColDraggable(col) ? (_jsx(
|
|
522
|
+
}, 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 }))] }, col.field)))] }), 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 && 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", children: [summaryLabelSpan > 0 ? (_jsx("th", { className: cn('kpt-data-table__summary-label', hasFrozen && 'kpt-data-table__cell--frozen'), colSpan: summaryLabelSpan, style: { left: hasFrozen ? 0 : undefined }, children: summaryLabel ?? t('dataTable.total') })) : null, visibleColumns.map((col, index) => index >= summaryStartIndex ? (_jsx("td", { "data-align": col.align ?? 'end', style: { width: col.width, left: frozenLeft(col.field) }, className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen'), children: summaryText(col) }, col.field)) : null)] }) })) : 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
523
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { KptColumn } from './data-table-types.ts';
|
|
2
2
|
/**
|
|
3
|
-
* Zrzuca wiersze do pliku CSV (separator
|
|
4
|
-
* funkcja jest wyeksportowana, bo w Reakcie nie ma odpowiednika metody publicznej komponentu
|
|
3
|
+
* Zrzuca wiersze do pliku CSV (separator `;`, końce wierszy CRLF, BOM — patrz `buildCsv`).
|
|
4
|
+
* Tabela woła to samo z przycisku „Eksport CSV"; funkcja jest wyeksportowana, bo w Reakcie nie ma odpowiednika metody publicznej komponentu
|
|
5
5
|
* — eksport z kodu aplikacji wywołuje się właśnie tak.
|
|
6
6
|
*/
|
|
7
7
|
export declare function exportTableCsv(columns: readonly KptColumn[], rows: readonly Record<string, unknown>[], filename?: string): void;
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildCsv } from '@konce-pt/table';
|
|
2
2
|
/**
|
|
3
|
-
* Zrzuca wiersze do pliku CSV (separator
|
|
4
|
-
* funkcja jest wyeksportowana, bo w Reakcie nie ma odpowiednika metody publicznej komponentu
|
|
3
|
+
* Zrzuca wiersze do pliku CSV (separator `;`, końce wierszy CRLF, BOM — patrz `buildCsv`).
|
|
4
|
+
* Tabela woła to samo z przycisku „Eksport CSV"; funkcja jest wyeksportowana, bo w Reakcie nie ma odpowiednika metody publicznej komponentu
|
|
5
5
|
* — eksport z kodu aplikacji wywołuje się właśnie tak.
|
|
6
6
|
*/
|
|
7
7
|
export function exportTableCsv(columns, rows, filename = 'export.csv') {
|
|
8
|
-
const
|
|
9
|
-
const body = rows.map((row) => columns.map((col) => escapeCsvCell(row[col.field])).join(';')).join('\n');
|
|
10
|
-
// BOM — bez niego Excel czyta UTF-8 jako stronę kodową systemu i psuje polskie znaki.
|
|
11
|
-
const csv = `${header}\n${body}`;
|
|
12
|
-
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
|
8
|
+
const blob = new Blob([buildCsv(columns, rows)], { type: 'text/csv;charset=utf-8;' });
|
|
13
9
|
const url = URL.createObjectURL(blob);
|
|
14
10
|
const link = document.createElement('a');
|
|
15
11
|
link.href = url;
|
|
16
12
|
link.download = filename;
|
|
17
13
|
link.click();
|
|
18
|
-
|
|
14
|
+
// Część przeglądarek zaczyna pobieranie asynchronicznie — natychmiastowe unieważnienie
|
|
15
|
+
// adresu potrafi je przerwać.
|
|
16
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
19
17
|
}
|
package/dist/data-table/llms.txt
CHANGED
|
@@ -7,7 +7,9 @@ Import: `import { KptDataTable, type KptColumn } from '@konce-pt/react';`
|
|
|
7
7
|
|
|
8
8
|
## The column model (KptColumn)
|
|
9
9
|
`{ field, header, sortable?, align?, width?, frozen?, filter?, edit?, summary? }`
|
|
10
|
-
`frozen` columns (leading, adjacent) stick during horizontal scrolling
|
|
10
|
+
`frozen` columns (leading, adjacent) stick during horizontal scrolling. Their offsets come from `width`:
|
|
11
|
+
`px`/`rem`/`em` are computed up front, other units (`%`, `ch`) and a missing width are measured from the
|
|
12
|
+
rendered header.
|
|
11
13
|
|
|
12
14
|
- `filter`: `KptColumnFilter` from `@konce-pt/table` — `text`, `number`, `select` (with `multiple`),
|
|
13
15
|
`numberRange`, `slider`, `dateRange`. Any column with a `filter` renders the filter row.
|
|
@@ -21,9 +23,19 @@ Import: `import { KptDataTable, type KptColumn } from '@konce-pt/react';`
|
|
|
21
23
|
`rowKey`, `scrollHeight`, `virtualScroll`, `rowHeight` (41), `reorderable`, `exportable`,
|
|
22
24
|
`exportFilename`, `lazy`, `totalRecords`, `expandable`, `multiExpand`, `summaryLabel`,
|
|
23
25
|
`summaryValues`
|
|
26
|
+
- `ariaLabel` (the table's name without a visible caption), `caption` (a visible `<caption>`),
|
|
27
|
+
`clickableRows` (rows focusable and activated with Enter/Space; on by default when `onRowClick` is set)
|
|
24
28
|
- Controllable state: `selection`, `expandedRows`, `hiddenFields` (each with `default*` and
|
|
25
29
|
`on*Change`)
|
|
26
|
-
- Events: `onRowClick`, `onLazyLoad
|
|
30
|
+
- Events: `onRowClick`, `onLazyLoad` (also once on start, for the first page), `onCellEdit`
|
|
31
|
+
|
|
32
|
+
## Keyboard
|
|
33
|
+
- A sortable header is a button: Tab to it, Enter/Space cycles asc → desc → none; `aria-sort` is on the `<th>`.
|
|
34
|
+
- With `reorderable` the grip is a button: `←`/`→` move the column by one place; the new position is
|
|
35
|
+
announced and focus follows the grip.
|
|
36
|
+
- An editable cell is reachable with Tab and opens with Enter; its name stays its value and
|
|
37
|
+
"Edit: {column}" is its description.
|
|
38
|
+
- `selectable="single"` renders radios (one group per table).
|
|
27
39
|
|
|
28
40
|
## Templates are render props
|
|
29
41
|
- `cells`: `Record<field, (row, value) => ReactNode>` — the equivalent of `kptCell`
|
|
@@ -33,7 +45,9 @@ Import: `import { KptDataTable, type KptColumn } from '@konce-pt/react';`
|
|
|
33
45
|
- `paginatorActions` — the slot to the left of the paginator (a column-toggle button, say)
|
|
34
46
|
|
|
35
47
|
## The logic lives in a package
|
|
36
|
-
Filter matching
|
|
48
|
+
Filter matching (the global filter and the text filter ignore case and diacritics: "lodz" finds
|
|
49
|
+
"Łódź"), sorting (`Intl.Collator` for the active language, natural numbers, empty cells last),
|
|
50
|
+
aggregations, the CSV file (`;`, CRLF, BOM) and cell-to-text come from `@konce-pt/table` — pure
|
|
37
51
|
functions with `node --test` coverage, shared with the Angular port. What stays here is the state
|
|
38
52
|
and the markup.
|
|
39
53
|
|
|
@@ -68,7 +82,8 @@ them while the search box does. With `lazy` the table only holds one page, so it
|
|
|
68
82
|
## i18n
|
|
69
83
|
The toolbar, the states and the filter labels come from the `dataTable` namespace (`search`,
|
|
70
84
|
`exportCsv`, `loading`, `empty`, `total`, `selectAll`, `selectRow`, `expandRow`, `editCell`,
|
|
71
|
-
`filter` / `filterMin` / `filterMax` with `{column}`, `all`, `min`, `max`, `dateRange`
|
|
85
|
+
`filter` / `filterMin` / `filterMax` with `{column}`, `all`, `min`, `max`, `dateRange`, `moveColumn`,
|
|
86
|
+
`columnMoved`).
|
|
72
87
|
|
|
73
88
|
## Tokens
|
|
74
89
|
`--kpt-table-{bg,fg,border,radius,header-bg,header-fg,row-hover,row-selected,cell-padding}`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
-
import { type KptDateRangeMode, type KptDateRangeValue } from '@konce-pt/datetime';
|
|
2
|
+
import { type KptDateDisplayFormat, type KptFirstDayOfWeek, type KptDateRangeMode, type KptDateRangeValue } from '@konce-pt/datetime';
|
|
3
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
4
4
|
export type { KptDateRangeMode, KptDateRangeValue };
|
|
5
5
|
export interface KptDateRangeProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'defaultValue'>, KptI18nOverrides {
|
|
@@ -12,7 +12,13 @@ export interface KptDateRangeProps extends Omit<ComponentPropsWithRef<'div'>, 'o
|
|
|
12
12
|
hourFormat?: 12 | 24;
|
|
13
13
|
minuteStep?: number;
|
|
14
14
|
placeholder?: string;
|
|
15
|
-
|
|
15
|
+
/** Zapis dat: `'pl'` (domyślnie), `'iso'` albo `'locale'` (`Intl` dla aktywnego języka). */
|
|
16
|
+
displayFormat?: KptDateDisplayFormat;
|
|
17
|
+
/** Pierwszy dzień tygodnia (1 = poniedziałek … 7 = niedziela); `null` = wg aktywnego języka. */
|
|
18
|
+
firstDayOfWeek?: KptFirstDayOfWeek | null;
|
|
19
|
+
/** Stan panelu — pozwala otworzyć go z zewnątrz i wiedzieć, kiedy wybór się skończył. */
|
|
20
|
+
open?: boolean;
|
|
21
|
+
onOpenChange?: (open: boolean) => void;
|
|
16
22
|
/** Najwcześniejsza dozwolona data 'RRRR-MM-DD' (włącznie). */
|
|
17
23
|
minDate?: string | null;
|
|
18
24
|
/** Najpóźniejsza dozwolona data 'RRRR-MM-DD' (włącznie). */
|
|
@@ -22,6 +28,10 @@ export interface KptDateRangeProps extends Omit<ComponentPropsWithRef<'div'>, 'o
|
|
|
22
28
|
/** Id pola (nie hosta); bez niego generowane, w `KptFormField` — z wrappera. */
|
|
23
29
|
id?: string;
|
|
24
30
|
disabled?: boolean;
|
|
31
|
+
/** Tylko do odczytu — wartość widać, panel się nie otwiera (`aria-readonly`). */
|
|
32
|
+
readOnly?: boolean;
|
|
33
|
+
/** Pole wymagane — `aria-required`. */
|
|
34
|
+
required?: boolean;
|
|
25
35
|
invalid?: boolean;
|
|
26
36
|
touched?: boolean;
|
|
27
37
|
onTouch?: () => void;
|
|
@@ -34,10 +44,12 @@ export interface KptDateRangeProps extends Omit<ComponentPropsWithRef<'div'>, 'o
|
|
|
34
44
|
* Klawiatura jak w `KptDatepicker`: `Enter`/`Spacja`/`↓` otwierają panel z fokusem na początku
|
|
35
45
|
* zakresu (albo dziś), strzałki, `Home`/`End` i `PageUp`/`PageDown` (z `Shift` — rok) chodzą po
|
|
36
46
|
* dniach przez wszystkie panele, `Tab` krąży w panelu, `Escape` i wybór końca oddają fokus polu.
|
|
37
|
-
* Krańce i środek zakresu są w nazwie dnia („…, początek zakresu”), a wybór
|
|
47
|
+
* Krańce i środek zakresu są w nazwie dnia („…, początek zakresu”), a wybór i przewinięcie miesiąca
|
|
48
|
+
* ogłasza region `polite`. Pole to `role="combobox"` z `aria-haspopup="dialog"`, więc niesie
|
|
49
|
+
* `aria-required`, `aria-readonly` i `aria-invalid`. Tydzień zaczyna się wg aktywnego języka.
|
|
38
50
|
*
|
|
39
51
|
* @example
|
|
40
52
|
* <KptDateRange value={period} onValueChange={setPeriod} visibleMonths={2} />
|
|
41
53
|
* <KptDateRange mode="datetime" hourFormat={12} value={span} onValueChange={setSpan} />
|
|
42
54
|
*/
|
|
43
|
-
export declare function KptDateRange({ value, defaultValue, onValueChange, mode, visibleMonths, hourFormat, minuteStep, placeholder, displayFormat, minDate, maxDate, ariaLabel, id, disabled, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptDateRangeProps>): import("react").JSX.Element;
|
|
55
|
+
export declare function KptDateRange({ value, defaultValue, onValueChange, mode, visibleMonths, hourFormat, minuteStep, placeholder, displayFormat, firstDayOfWeek, open, onOpenChange, minDate, maxDate, ariaLabel, id, disabled, readOnly, required, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptDateRangeProps>): import("react").JSX.Element;
|