@dbcdk/react-components 0.0.40 → 0.0.42
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/dist/components/circle/Circle.module.css +1 -0
- package/dist/components/pagination/Pagination.js +1 -1
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +1 -1
- package/dist/components/table/Table.module.css +56 -2
- package/dist/components/table/Table.types.d.ts +1 -0
- package/dist/components/table/components/TableRow.js +3 -2
- package/dist/components/table/tanstackTable.utils.js +2 -1
- package/dist/hooks/usePagination.d.ts +6 -1
- package/dist/hooks/usePagination.js +77 -16
- package/package.json +1 -1
- package/dist/components/table/components/TableCell.d.ts +0 -9
- package/dist/components/table/components/TableCell.js +0 -7
|
@@ -6,7 +6,7 @@ import styles from './Pagination.module.css';
|
|
|
6
6
|
import { Button } from '../button/Button';
|
|
7
7
|
import { Menu } from '../menu/Menu';
|
|
8
8
|
import { Popover } from '../popover/Popover';
|
|
9
|
-
const DEFAULT_PAGE_SIZE_OPTIONS = [
|
|
9
|
+
const DEFAULT_PAGE_SIZE_OPTIONS = [20, 50, 100];
|
|
10
10
|
const NUMBER_LOCALE = 'da-DK';
|
|
11
11
|
export function Pagination({ itemsCount = 0, skip = 0, take = DEFAULT_PAGE_SIZE_OPTIONS[1], onPageChange, pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, showFirstLast = true, showGoToPage = true, }) {
|
|
12
12
|
const popoverRef = useRef(null);
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
.progressSegment[data-severity='progress'] {
|
|
69
|
-
background: color-mix(in srgb, var(--color-
|
|
69
|
+
background: color-mix(in srgb, var(--color-fg-subtle) 5%, var(--color-bg-surface));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.progressSegment[data-severity='missing'] {
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
|
|
83
83
|
.header {
|
|
84
84
|
position: relative;
|
|
85
|
-
z-index:
|
|
85
|
+
z-index: 10;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.headerRow,
|
|
@@ -148,6 +148,18 @@
|
|
|
148
148
|
line-height: 20px;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
.cell[data-vertical-align='top'] {
|
|
152
|
+
vertical-align: top;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.cell[data-vertical-align='middle'] {
|
|
156
|
+
vertical-align: middle;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.cell[data-vertical-align='bottom'] {
|
|
160
|
+
vertical-align: bottom;
|
|
161
|
+
}
|
|
162
|
+
|
|
151
163
|
.headerCell[data-align='right'],
|
|
152
164
|
.cell[data-align='right'] {
|
|
153
165
|
text-align: end;
|
|
@@ -368,17 +380,47 @@
|
|
|
368
380
|
}
|
|
369
381
|
|
|
370
382
|
.cellContent {
|
|
371
|
-
display:
|
|
383
|
+
display: flex;
|
|
372
384
|
inline-size: 100%;
|
|
373
385
|
min-width: 0;
|
|
374
386
|
max-inline-size: 100%;
|
|
375
387
|
}
|
|
376
388
|
|
|
389
|
+
/* horizontal alignment inside content wrapper */
|
|
390
|
+
.cellContent[data-align='left'] {
|
|
391
|
+
justify-content: flex-start;
|
|
392
|
+
text-align: left;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.cellContent[data-align='center'] {
|
|
396
|
+
justify-content: center;
|
|
397
|
+
text-align: center;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.cellContent[data-align='right'] {
|
|
401
|
+
justify-content: flex-end;
|
|
402
|
+
text-align: right;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/* vertical alignment inside content wrapper */
|
|
406
|
+
.cellContent[data-vertical-align='top'] {
|
|
407
|
+
align-items: flex-start;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.cellContent[data-vertical-align='middle'] {
|
|
411
|
+
align-items: center;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.cellContent[data-vertical-align='bottom'] {
|
|
415
|
+
align-items: flex-end;
|
|
416
|
+
}
|
|
417
|
+
|
|
377
418
|
.cellContent > * {
|
|
378
419
|
min-width: 0;
|
|
379
420
|
max-inline-size: 100%;
|
|
380
421
|
}
|
|
381
422
|
|
|
423
|
+
/* keep truncation + text alignment working */
|
|
382
424
|
.cellValueEllipsis {
|
|
383
425
|
display: block;
|
|
384
426
|
inline-size: 100%;
|
|
@@ -389,6 +431,18 @@
|
|
|
389
431
|
text-overflow: ellipsis;
|
|
390
432
|
}
|
|
391
433
|
|
|
434
|
+
.cellContent[data-align='left'] .cellValueEllipsis {
|
|
435
|
+
text-align: left;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.cellContent[data-align='center'] .cellValueEllipsis {
|
|
439
|
+
text-align: center;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.cellContent[data-align='right'] .cellValueEllipsis {
|
|
443
|
+
text-align: right;
|
|
444
|
+
}
|
|
445
|
+
|
|
392
446
|
.allowWrap .cellContent,
|
|
393
447
|
.allowWrap .cellValueEllipsis {
|
|
394
448
|
white-space: normal;
|
|
@@ -45,9 +45,10 @@ export function TableRow({ row, rowId, columns, selectedRows, hasSelection, sele
|
|
|
45
45
|
e.stopPropagation();
|
|
46
46
|
onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, checked);
|
|
47
47
|
} })) }) }) })) : null, columns.map(column => {
|
|
48
|
-
var _a;
|
|
48
|
+
var _a, _b, _c, _d;
|
|
49
49
|
const allowWrap = shouldAllowWrap(column.allowWrap, isSelected, viewMode);
|
|
50
|
+
const allowOverflow = column.allowOverflow;
|
|
50
51
|
const cellValue = getCellDisplayValue(row, column);
|
|
51
|
-
return (_jsx("td", { className: cx(styles.cell, allowWrap ? styles.allowWrap : styles.nowrap, column.divider === 'left' && styles.dividerLeft, column.divider === 'right' && styles.dividerRight), "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, children: allowWrap ? cellValue : _jsx("div", { className: styles.cellValueEllipsis, children: cellValue }) }) }, column.id));
|
|
52
|
+
return (_jsx("td", { className: cx(styles.cell, allowWrap ? styles.allowWrap : styles.nowrap, column.divider === 'left' && styles.dividerLeft, column.divider === 'right' && styles.dividerRight), "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-vertical-align": (_b = column.verticalAlign) !== null && _b !== void 0 ? _b : 'top', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, "data-align": (_c = column.align) !== null && _c !== void 0 ? _c : 'left', "data-vertical-align": (_d = column.verticalAlign) !== null && _d !== void 0 ? _d : 'top', children: allowWrap || allowOverflow ? (cellValue) : (_jsx("div", { className: styles.cellValueEllipsis, children: cellValue })) }) }, column.id));
|
|
52
53
|
})] }));
|
|
53
54
|
}
|
|
@@ -20,7 +20,7 @@ export function buildColumnVisibilityFromVisibleIds(defs, visibleColumnIds) {
|
|
|
20
20
|
}
|
|
21
21
|
export function mapDefsToColumnItems(defs, columnVisibility, resolvedLayout = {}) {
|
|
22
22
|
return defs.map((def, index) => {
|
|
23
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
23
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
24
24
|
const id = getColumnId(def, index);
|
|
25
25
|
const accessorKey = def.accessorKey;
|
|
26
26
|
const accessorFn = def.accessorFn;
|
|
@@ -61,6 +61,7 @@ export function mapDefsToColumnItems(defs, columnVisibility, resolvedLayout = {}
|
|
|
61
61
|
allowWrap: (_g = meta.allowWrap) !== null && _g !== void 0 ? _g : false,
|
|
62
62
|
severity: meta.severity,
|
|
63
63
|
divider: meta.divider,
|
|
64
|
+
allowOverflow: (_h = meta.allowOverflow) !== null && _h !== void 0 ? _h : false,
|
|
64
65
|
};
|
|
65
66
|
});
|
|
66
67
|
}
|
|
@@ -20,6 +20,11 @@ export interface UsePaginationProps<T> {
|
|
|
20
20
|
* Useful after filtering/sorting changes upstream.
|
|
21
21
|
*/
|
|
22
22
|
resetOnDataChange?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Optional localStorage key for persisting pagination state.
|
|
25
|
+
* Only used in uncontrolled mode.
|
|
26
|
+
*/
|
|
27
|
+
storageKey?: string;
|
|
23
28
|
}
|
|
24
29
|
export interface UsePaginationResult<T> {
|
|
25
30
|
paginatedData: T[];
|
|
@@ -30,4 +35,4 @@ export interface UsePaginationResult<T> {
|
|
|
30
35
|
page: number;
|
|
31
36
|
totalCount: number;
|
|
32
37
|
}
|
|
33
|
-
export declare function usePagination<T>({ data, skip, take, state, onStateChange, resetOnDataChange, }: UsePaginationProps<T>): UsePaginationResult<T>;
|
|
38
|
+
export declare function usePagination<T>({ data, skip, take, state, onStateChange, resetOnDataChange, storageKey, }: UsePaginationProps<T>): UsePaginationResult<T>;
|
|
@@ -3,18 +3,67 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
function clamp(n, min, max) {
|
|
4
4
|
return Math.max(min, Math.min(max, n));
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
function normalizePaginationState(next) {
|
|
7
|
+
return {
|
|
8
|
+
skip: Math.max(0, next.skip),
|
|
9
|
+
take: Math.max(1, next.take),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function safeParsePaginationState(raw) {
|
|
13
|
+
if (!raw)
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
if (typeof parsed !== 'object' ||
|
|
18
|
+
parsed == null ||
|
|
19
|
+
typeof parsed.skip !== 'number' ||
|
|
20
|
+
typeof parsed.take !== 'number') {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return normalizePaginationState({
|
|
24
|
+
skip: parsed.skip,
|
|
25
|
+
take: parsed.take,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function usePagination({ data = [], skip = 0, take = 10, state, onStateChange, resetOnDataChange = false, storageKey, }) {
|
|
7
33
|
const isControlled = state != null;
|
|
8
|
-
const [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
34
|
+
const [hydrated, setHydrated] = useState(() => !storageKey || isControlled);
|
|
35
|
+
const [uncontrolled, setUncontrolled] = useState(() => {
|
|
36
|
+
var _a;
|
|
37
|
+
const initial = normalizePaginationState({
|
|
38
|
+
skip,
|
|
39
|
+
take,
|
|
40
|
+
});
|
|
41
|
+
if (typeof window === 'undefined' || !storageKey || isControlled) {
|
|
42
|
+
return initial;
|
|
43
|
+
}
|
|
44
|
+
return (_a = safeParsePaginationState(window.localStorage.getItem(storageKey))) !== null && _a !== void 0 ? _a : initial;
|
|
45
|
+
});
|
|
46
|
+
// Hydrate from localStorage when key changes.
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (typeof window === 'undefined')
|
|
49
|
+
return;
|
|
50
|
+
if (isControlled || !storageKey) {
|
|
51
|
+
setHydrated(true);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const fallback = normalizePaginationState({ skip, take });
|
|
55
|
+
const stored = safeParsePaginationState(window.localStorage.getItem(storageKey));
|
|
56
|
+
setUncontrolled(stored !== null && stored !== void 0 ? stored : fallback);
|
|
57
|
+
setHydrated(true);
|
|
58
|
+
}, [isControlled, storageKey, skip, take]);
|
|
12
59
|
// Keep initial props in sync ONLY for uncontrolled if props change.
|
|
13
|
-
//
|
|
60
|
+
// Do not overwrite localStorage-hydrated state when storageKey is provided.
|
|
14
61
|
const didInitRef = useRef(false);
|
|
15
62
|
useEffect(() => {
|
|
16
63
|
if (isControlled)
|
|
17
64
|
return;
|
|
65
|
+
if (storageKey)
|
|
66
|
+
return;
|
|
18
67
|
if (!didInitRef.current) {
|
|
19
68
|
didInitRef.current = true;
|
|
20
69
|
return;
|
|
@@ -23,21 +72,19 @@ export function usePagination({ data = [], skip = 0, take = 10, state, onStateCh
|
|
|
23
72
|
skip: prev.skip,
|
|
24
73
|
take: Math.max(1, take),
|
|
25
74
|
}));
|
|
26
|
-
}, [isControlled, take]);
|
|
75
|
+
}, [isControlled, storageKey, take]);
|
|
27
76
|
const paginationState = (isControlled ? state : uncontrolled);
|
|
28
77
|
const totalCount = data.length;
|
|
29
78
|
const safeTake = Math.max(1, paginationState.take);
|
|
30
79
|
const maxSkip = Math.max(0, totalCount === 0 ? 0 : Math.floor((totalCount - 1) / safeTake) * safeTake);
|
|
31
80
|
const safeSkip = clamp(Math.max(0, paginationState.skip), 0, maxSkip);
|
|
32
81
|
const setPagination = useCallback((next) => {
|
|
33
|
-
const normalized =
|
|
34
|
-
|
|
35
|
-
skip: Math.max(0, next.skip),
|
|
36
|
-
};
|
|
37
|
-
if (isControlled)
|
|
82
|
+
const normalized = normalizePaginationState(next);
|
|
83
|
+
if (isControlled) {
|
|
38
84
|
onStateChange === null || onStateChange === void 0 ? void 0 : onStateChange(normalized);
|
|
39
|
-
|
|
40
|
-
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
setUncontrolled(normalized);
|
|
41
88
|
}, [isControlled, onStateChange]);
|
|
42
89
|
const onPageChange = useCallback((pageEvent) => {
|
|
43
90
|
const nextTake = Math.max(1, pageEvent.take);
|
|
@@ -51,11 +98,12 @@ export function usePagination({ data = [], skip = 0, take = 10, state, onStateCh
|
|
|
51
98
|
useEffect(() => {
|
|
52
99
|
if (!resetOnDataChange)
|
|
53
100
|
return;
|
|
101
|
+
if (!hydrated)
|
|
102
|
+
return;
|
|
54
103
|
resetPage();
|
|
55
104
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
56
|
-
}, [resetOnDataChange, data]);
|
|
105
|
+
}, [resetOnDataChange, data, hydrated]);
|
|
57
106
|
const paginatedData = useMemo(() => {
|
|
58
|
-
// Use safeSkip to avoid slicing past end if data shrinks.
|
|
59
107
|
return data.slice(safeSkip, safeSkip + safeTake);
|
|
60
108
|
}, [data, safeSkip, safeTake]);
|
|
61
109
|
const page = useMemo(() => Math.floor(safeSkip / safeTake) + 1, [safeSkip, safeTake]);
|
|
@@ -67,6 +115,19 @@ export function usePagination({ data = [], skip = 0, take = 10, state, onStateCh
|
|
|
67
115
|
setUncontrolled(prev => ({ ...prev, skip: safeSkip }));
|
|
68
116
|
}
|
|
69
117
|
}, [isControlled, safeSkip, paginationState.skip]);
|
|
118
|
+
// Persist uncontrolled state to localStorage when enabled.
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (typeof window === 'undefined')
|
|
121
|
+
return;
|
|
122
|
+
if (isControlled || !storageKey)
|
|
123
|
+
return;
|
|
124
|
+
if (!hydrated)
|
|
125
|
+
return;
|
|
126
|
+
window.localStorage.setItem(storageKey, JSON.stringify({
|
|
127
|
+
skip: paginationState.skip,
|
|
128
|
+
take: paginationState.take,
|
|
129
|
+
}));
|
|
130
|
+
}, [hydrated, isControlled, paginationState.skip, paginationState.take, storageKey]);
|
|
70
131
|
return {
|
|
71
132
|
paginatedData,
|
|
72
133
|
paginationState: { skip: safeSkip, take: safeTake },
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
type Props = {
|
|
3
|
-
align?: 'left' | 'right' | 'center';
|
|
4
|
-
divider?: 'left' | 'right';
|
|
5
|
-
allowWrap?: boolean;
|
|
6
|
-
children: ReactNode;
|
|
7
|
-
};
|
|
8
|
-
export declare function TableCell({ align, divider, allowWrap, children }: Props): ReactNode;
|
|
9
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cx } from '../table.classes';
|
|
3
|
-
import styles from '../Table.module.css';
|
|
4
|
-
export function TableCell({ align = 'left', divider, allowWrap, children }) {
|
|
5
|
-
const dividerClass = divider === 'left' ? styles.dividerLeft : divider === 'right' ? styles.dividerRight : '';
|
|
6
|
-
return (_jsx("div", { className: cx(styles.cell, allowWrap ? styles.allowWrap : styles.nowrap, dividerClass), role: "cell", "data-align": align, "data-divider": divider, children: _jsx("div", { className: styles.cellContent, children: allowWrap ? children : _jsx("div", { className: styles.cellValueEllipsis, children: children }) }) }));
|
|
7
|
-
}
|