@dbcdk/react-components 0.0.10 → 0.0.13
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/accordion/Accordion.d.ts +2 -2
- package/dist/components/accordion/Accordion.js +34 -41
- package/dist/components/accordion/Accordion.module.css +13 -72
- package/dist/components/accordion/components/AccordionRow.d.ts +10 -0
- package/dist/components/accordion/components/AccordionRow.js +51 -0
- package/dist/components/accordion/components/AccordionRow.module.css +82 -0
- package/dist/components/breadcrumbs/Breadcrumbs.module.css +0 -1
- package/dist/components/button/Button.module.css +7 -7
- package/dist/components/card/Card.d.ts +15 -6
- package/dist/components/card/Card.js +39 -13
- package/dist/components/card/Card.module.css +22 -28
- package/dist/components/card/components/CardMeta.d.ts +15 -0
- package/dist/components/card/components/CardMeta.js +20 -0
- package/dist/components/card/components/CardMeta.module.css +51 -0
- package/dist/components/card-container/CardContainer.js +1 -1
- package/dist/components/card-container/CardContainer.module.css +3 -1
- package/dist/components/chip/Chip.module.css +7 -2
- package/dist/components/circle/Circle.d.ts +2 -1
- package/dist/components/circle/Circle.js +2 -2
- package/dist/components/circle/Circle.module.css +6 -2
- package/dist/components/code-block/CodeBlock.js +1 -1
- package/dist/components/code-block/CodeBlock.module.css +30 -17
- package/dist/components/copy-button/CopyButton.d.ts +1 -0
- package/dist/components/copy-button/CopyButton.js +10 -2
- package/dist/components/datetime-picker/DateTimePicker.d.ts +33 -8
- package/dist/components/datetime-picker/DateTimePicker.js +119 -78
- package/dist/components/datetime-picker/DateTimePicker.module.css +2 -0
- package/dist/components/datetime-picker/dateTimeHelpers.d.ts +15 -3
- package/dist/components/datetime-picker/dateTimeHelpers.js +137 -23
- package/dist/components/filter-field/FilterField.js +16 -11
- package/dist/components/filter-field/FilterField.module.css +137 -16
- package/dist/components/forms/checkbox/Checkbox.d.ts +2 -2
- package/dist/components/forms/checkbox-group/CheckboxGroup.js +1 -1
- package/dist/components/forms/checkbox-group/CheckboxGroup.module.css +1 -1
- package/dist/components/forms/form-select/FormSelect.d.ts +35 -0
- package/dist/components/forms/form-select/FormSelect.js +86 -0
- package/dist/components/forms/form-select/FormSelect.module.css +236 -0
- package/dist/components/forms/input/Input.d.ts +0 -3
- package/dist/components/forms/input/Input.js +1 -4
- package/dist/components/forms/input/Input.module.css +8 -7
- package/dist/components/forms/input-container/InputContainer.module.css +1 -1
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
- package/dist/components/forms/select/Select.js +55 -16
- package/dist/components/hyperlink/Hyperlink.d.ts +19 -7
- package/dist/components/hyperlink/Hyperlink.js +35 -11
- package/dist/components/hyperlink/Hyperlink.module.css +50 -2
- package/dist/components/interval-select/IntervalSelect.d.ts +9 -2
- package/dist/components/interval-select/IntervalSelect.js +21 -6
- package/dist/components/menu/Menu.d.ts +29 -0
- package/dist/components/menu/Menu.js +61 -16
- package/dist/components/menu/Menu.module.css +73 -5
- package/dist/components/overlay/modal/Modal.module.css +4 -3
- package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
- package/dist/components/overlay/side-panel/SidePanel.js +18 -1
- package/dist/components/overlay/side-panel/SidePanel.module.css +1 -3
- package/dist/components/overlay/tooltip/useTooltipTrigger.js +4 -2
- package/dist/components/page-layout/PageLayout.d.ts +16 -4
- package/dist/components/page-layout/PageLayout.js +57 -28
- package/dist/components/page-layout/PageLayout.module.css +153 -33
- package/dist/components/popover/Popover.d.ts +17 -4
- package/dist/components/popover/Popover.js +147 -65
- package/dist/components/popover/Popover.module.css +5 -0
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +22 -18
- package/dist/components/sidebar/providers/SidebarProvider.d.ts +4 -1
- package/dist/components/sidebar/providers/SidebarProvider.js +66 -18
- package/dist/components/split-button/SplitButton.d.ts +1 -1
- package/dist/components/split-button/SplitButton.js +3 -1
- package/dist/components/split-button/SplitButton.module.css +4 -4
- package/dist/components/split-pane/SplitPane.d.ts +10 -24
- package/dist/components/split-pane/SplitPane.js +83 -54
- package/dist/components/split-pane/SplitPane.module.css +11 -6
- package/dist/components/split-pane/provider/SplitPaneContext.js +5 -11
- package/dist/components/state-page/StatePage.module.css +1 -1
- package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +3 -8
- package/dist/components/sticky-footer-layout/StickyFooterLayout.js +57 -20
- package/dist/components/table/Table.d.ts +8 -8
- package/dist/components/table/Table.js +37 -79
- package/dist/components/table/Table.module.css +62 -46
- package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +7 -3
- package/dist/components/table/TanstackTable.js +84 -0
- package/dist/components/table/components/column-resizer/ColumnResizer.js +1 -1
- package/dist/components/table/components/column-resizer/ColumnResizer.module.css +17 -7
- package/dist/components/table/components/table-settings/TableSettings.d.ts +13 -3
- package/dist/components/table/components/table-settings/TableSettings.js +55 -4
- package/dist/components/table/table.utils.d.ts +17 -0
- package/dist/components/table/table.utils.js +61 -0
- package/dist/components/table/tanstackTable.utils.d.ts +22 -0
- package/dist/components/table/tanstackTable.utils.js +104 -0
- package/dist/components/tabs/Tabs.d.ts +35 -12
- package/dist/components/tabs/Tabs.js +114 -26
- package/dist/components/tabs/Tabs.module.css +158 -71
- package/dist/hooks/useTableSettings.d.ts +23 -4
- package/dist/hooks/useTableSettings.js +64 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/styles/styles.css +38 -23
- package/dist/styles/animation.d.ts +5 -0
- package/dist/styles/animation.js +5 -0
- package/dist/styles/styles.css +38 -23
- package/dist/styles/themes/dbc/base.css +136 -0
- package/dist/styles/themes/dbc/dark.css +39 -202
- package/dist/styles/themes/dbc/light.css +17 -174
- package/dist/utils/localStorage.utils.d.ts +19 -0
- package/dist/utils/localStorage.utils.js +78 -0
- package/package.json +4 -4
- package/dist/components/table/tanstack.js +0 -162
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
4
4
|
import { SplitPaneProvider, useSplitPaneContext } from './provider/SplitPaneContext';
|
|
5
5
|
import styles from './SplitPane.module.css';
|
|
6
6
|
function clamp(n, min, max) {
|
|
@@ -11,68 +11,97 @@ export function SplitPane({ children, initialPrimarySize = 300, minPrimarySize =
|
|
|
11
11
|
}
|
|
12
12
|
function SplitPaneContainer({ children, showDivider, gutterSize, }) {
|
|
13
13
|
const { direction, primarySize, containerRef } = useSplitPaneContext();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const style = useMemo(() => ({
|
|
15
|
+
'--split-pane-primary-size': `${primarySize}px`,
|
|
16
|
+
'--split-pane-gutter': `${gutterSize}px`,
|
|
17
|
+
}), [primarySize, gutterSize]);
|
|
18
|
+
return (_jsx("div", { ref: containerRef, className: styles.container, "data-direction": direction, "data-divider": showDivider, style: style, children: children }));
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
-
* IMPORTANT:
|
|
21
|
-
* This component now renders ONLY the primary content (scrollable).
|
|
22
|
-
* The resizer lives in a dedicated <SplitPaneGutter /> so it never overlaps scrollbars.
|
|
23
|
-
*/
|
|
24
20
|
export function SplitPanePrimary({ children }) {
|
|
25
21
|
return _jsx("div", { className: styles.primary, children: children });
|
|
26
22
|
}
|
|
23
|
+
export function SplitPaneSecondary({ children }) {
|
|
24
|
+
return _jsx("div", { className: styles.secondary, children: children });
|
|
25
|
+
}
|
|
27
26
|
export function SplitPaneGutter() {
|
|
28
27
|
const { direction, primarySize, setPrimarySize, minPrimarySize, minSecondarySize, containerRef } = useSplitPaneContext();
|
|
29
|
-
const
|
|
28
|
+
const draggingRef = useRef(false);
|
|
29
|
+
const pointerIdRef = useRef(null);
|
|
30
30
|
const startPosRef = useRef(0);
|
|
31
31
|
const startSizeRef = useRef(primarySize);
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const maxPrimaryRef = useRef(Infinity);
|
|
33
|
+
const getClientPos = useCallback((e) => (direction === 'horizontal' ? e.clientX : e.clientY), [direction]);
|
|
34
|
+
const computeClamp = useCallback(() => {
|
|
35
|
+
const el = containerRef.current;
|
|
36
|
+
if (!el)
|
|
37
|
+
return { maxPrimary: Infinity, total: 0 };
|
|
38
|
+
const rect = el.getBoundingClientRect();
|
|
39
|
+
const total = direction === 'horizontal' ? rect.width : rect.height;
|
|
40
|
+
const maxPrimary = Math.max(minPrimarySize, total - minSecondarySize);
|
|
41
|
+
return { maxPrimary, total };
|
|
42
|
+
}, [containerRef, direction, minPrimarySize, minSecondarySize]);
|
|
43
|
+
const onPointerDown = useCallback((e) => {
|
|
44
|
+
const el = containerRef.current;
|
|
45
|
+
if (!el)
|
|
34
46
|
return;
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
const rect = el.getBoundingClientRect();
|
|
43
|
-
const total = direction === 'horizontal' ? rect.width : rect.height;
|
|
44
|
-
const clientPos = direction === 'horizontal' ? e.clientX : e.clientY;
|
|
45
|
-
const delta = clientPos - startPosRef.current;
|
|
46
|
-
// Note: total includes gutter. That's fine because max clamps against secondary min.
|
|
47
|
-
const next = startSizeRef.current + delta;
|
|
48
|
-
const maxPrimary = Math.max(minPrimarySize, total - minSecondarySize);
|
|
49
|
-
setPrimarySize(clamp(next, minPrimarySize, maxPrimary));
|
|
50
|
-
};
|
|
51
|
-
const onUp = () => {
|
|
52
|
-
if (!isDraggingRef.current)
|
|
53
|
-
return;
|
|
54
|
-
isDraggingRef.current = false;
|
|
55
|
-
document.body.style.cursor = '';
|
|
56
|
-
document.body.style.userSelect = '';
|
|
57
|
-
};
|
|
58
|
-
window.addEventListener('mousemove', onMove);
|
|
59
|
-
window.addEventListener('mouseup', onUp);
|
|
60
|
-
return () => {
|
|
61
|
-
if (window) {
|
|
62
|
-
window.removeEventListener('mousemove', onMove);
|
|
63
|
-
window.removeEventListener('mouseup', onUp);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}, [containerRef, direction, minPrimarySize, minSecondarySize, setPrimarySize]);
|
|
67
|
-
const handleMouseDown = (event) => {
|
|
68
|
-
isDraggingRef.current = true;
|
|
69
|
-
startPosRef.current = direction === 'horizontal' ? event.clientX : event.clientY;
|
|
47
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
48
|
+
const { maxPrimary } = computeClamp();
|
|
49
|
+
maxPrimaryRef.current = maxPrimary;
|
|
50
|
+
draggingRef.current = true;
|
|
51
|
+
pointerIdRef.current = e.pointerId;
|
|
52
|
+
startPosRef.current = getClientPos(e);
|
|
70
53
|
startSizeRef.current = primarySize;
|
|
71
|
-
|
|
54
|
+
// UX: prevent text selection during drag
|
|
72
55
|
document.body.style.userSelect = 'none';
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
document.body.style.cursor = direction === 'horizontal' ? 'col-resize' : 'row-resize';
|
|
57
|
+
}, [computeClamp, containerRef, direction, getClientPos, primarySize]);
|
|
58
|
+
const onPointerMove = useCallback((e) => {
|
|
59
|
+
if (!draggingRef.current)
|
|
60
|
+
return;
|
|
61
|
+
if (pointerIdRef.current !== null && e.pointerId !== pointerIdRef.current)
|
|
62
|
+
return;
|
|
63
|
+
const delta = getClientPos(e) - startPosRef.current;
|
|
64
|
+
const next = startSizeRef.current + delta;
|
|
65
|
+
const maxPrimary = maxPrimaryRef.current;
|
|
66
|
+
setPrimarySize(clamp(next, minPrimarySize, maxPrimary));
|
|
67
|
+
}, [getClientPos, minPrimarySize, setPrimarySize]);
|
|
68
|
+
const endDrag = useCallback(() => {
|
|
69
|
+
if (!draggingRef.current)
|
|
70
|
+
return;
|
|
71
|
+
draggingRef.current = false;
|
|
72
|
+
pointerIdRef.current = null;
|
|
73
|
+
document.body.style.cursor = '';
|
|
74
|
+
document.body.style.userSelect = '';
|
|
75
|
+
}, []);
|
|
76
|
+
const onPointerUp = useCallback(() => endDrag(), [endDrag]);
|
|
77
|
+
const onPointerCancel = useCallback(() => endDrag(), [endDrag]);
|
|
78
|
+
// Keyboard: arrows adjust size; Shift = bigger step; Home/End = min/max
|
|
79
|
+
const onKeyDown = useCallback((e) => {
|
|
80
|
+
const { maxPrimary } = computeClamp();
|
|
81
|
+
const step = e.shiftKey ? 32 : 8;
|
|
82
|
+
let next = null;
|
|
83
|
+
if (direction === 'horizontal') {
|
|
84
|
+
if (e.key === 'ArrowLeft')
|
|
85
|
+
next = primarySize - step;
|
|
86
|
+
if (e.key === 'ArrowRight')
|
|
87
|
+
next = primarySize + step;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (e.key === 'ArrowUp')
|
|
91
|
+
next = primarySize - step;
|
|
92
|
+
if (e.key === 'ArrowDown')
|
|
93
|
+
next = primarySize + step;
|
|
94
|
+
}
|
|
95
|
+
if (e.key === 'Home')
|
|
96
|
+
next = minPrimarySize;
|
|
97
|
+
if (e.key === 'End')
|
|
98
|
+
next = maxPrimary;
|
|
99
|
+
if (next === null)
|
|
100
|
+
return;
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
setPrimarySize(clamp(next, minPrimarySize, maxPrimary));
|
|
103
|
+
}, [computeClamp, direction, minPrimarySize, primarySize, setPrimarySize]);
|
|
104
|
+
const ariaOrientation = direction === 'horizontal' ? 'vertical' : 'horizontal';
|
|
105
|
+
const { maxPrimary } = computeClamp();
|
|
106
|
+
return (_jsx("div", { className: styles.gutter, children: _jsx("div", { className: styles.resizer, role: "separator", "aria-orientation": ariaOrientation, "aria-valuemin": Math.round(minPrimarySize), "aria-valuemax": Number.isFinite(maxPrimary) ? Math.round(maxPrimary) : undefined, "aria-valuenow": Math.round(primarySize), tabIndex: 0, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, onKeyDown: onKeyDown }) }));
|
|
78
107
|
}
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
min-inline-size: 0;
|
|
21
21
|
min-block-size: 0;
|
|
22
22
|
overflow: auto;
|
|
23
|
-
flex-direction: column;
|
|
24
23
|
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/* ===== Secondary pane ===== */
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
flex-direction: column;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/* ===== Gutter (spacing + hit area) ===== */
|
|
37
|
+
/* ===== Gutter (spacing + hit area wrapper) ===== */
|
|
38
38
|
.gutter {
|
|
39
39
|
position: relative;
|
|
40
40
|
flex: 0 0 var(--split-pane-gutter, 8px);
|
|
@@ -42,32 +42,37 @@
|
|
|
42
42
|
z-index: 1;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/* Vertical mode gutter */
|
|
46
45
|
.container[data-direction='vertical'] .gutter {
|
|
47
46
|
inline-size: 100%;
|
|
48
47
|
block-size: var(--split-pane-gutter, 8px);
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
/* ===== Resizer (interaction
|
|
50
|
+
/* ===== Resizer (interaction element) ===== */
|
|
52
51
|
.resizer {
|
|
53
52
|
position: absolute;
|
|
54
53
|
inset: 0;
|
|
55
54
|
cursor: col-resize;
|
|
56
55
|
user-select: none;
|
|
57
56
|
touch-action: none;
|
|
57
|
+
outline: none;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
.container[data-direction='vertical'] .resizer {
|
|
61
61
|
cursor: row-resize;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/* Focus ring for keyboard resizing */
|
|
65
|
+
.resizer:focus-visible {
|
|
66
|
+
box-shadow: var(--focus-ring);
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
/* ===== Divider line ===== */
|
|
65
70
|
.resizer::after {
|
|
66
71
|
content: '';
|
|
67
72
|
position: absolute;
|
|
68
73
|
inset-block: 0;
|
|
69
74
|
inset-inline: 50%;
|
|
70
|
-
inline-size: var(--border-width-
|
|
75
|
+
inline-size: var(--border-width-thin);
|
|
71
76
|
background-color: var(--color-border-subtle);
|
|
72
77
|
opacity: 0;
|
|
73
78
|
transform: translateX(-50%);
|
|
@@ -81,7 +86,7 @@
|
|
|
81
86
|
inset-inline: 0;
|
|
82
87
|
inset-block: 50%;
|
|
83
88
|
inline-size: 100%;
|
|
84
|
-
block-size: var(--border-width-
|
|
89
|
+
block-size: var(--border-width-thin);
|
|
85
90
|
transform: translateY(-50%);
|
|
86
91
|
}
|
|
87
92
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
2
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
3
|
export const SplitPaneContext = React.createContext(null);
|
|
@@ -22,7 +21,7 @@ function writeStoredSize(key, value) {
|
|
|
22
21
|
localStorage.setItem(key, String(Math.round(value)));
|
|
23
22
|
}
|
|
24
23
|
catch {
|
|
25
|
-
// ignore
|
|
24
|
+
// ignore
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
export function useSplitPaneContext() {
|
|
@@ -33,13 +32,8 @@ export function useSplitPaneContext() {
|
|
|
33
32
|
}
|
|
34
33
|
export function SplitPaneProvider({ children, direction, initialPrimarySize, minPrimarySize, minSecondarySize, storageKey, }) {
|
|
35
34
|
const containerRef = useRef(null);
|
|
36
|
-
|
|
37
|
-
* IMPORTANT (Next.js hydration):
|
|
38
|
-
* Always start with initialPrimarySize so server HTML and first client render match.
|
|
39
|
-
* Then, after hydration, read localStorage and update.
|
|
40
|
-
*/
|
|
35
|
+
// Start with initial to avoid SSR mismatch, then hydrate from storage
|
|
41
36
|
const [primarySize, setPrimarySize] = useState(initialPrimarySize);
|
|
42
|
-
// Apply persisted size AFTER hydration (prevents SSR/client mismatch warnings)
|
|
43
37
|
useEffect(() => {
|
|
44
38
|
if (!storageKey)
|
|
45
39
|
return;
|
|
@@ -48,11 +42,12 @@ export function SplitPaneProvider({ children, direction, initialPrimarySize, min
|
|
|
48
42
|
return;
|
|
49
43
|
setPrimarySize(stored);
|
|
50
44
|
}, [storageKey]);
|
|
51
|
-
// Clamp after mount / when container is measurable; re-clamp on resize
|
|
52
45
|
useEffect(() => {
|
|
53
46
|
const el = containerRef.current;
|
|
54
47
|
if (!el)
|
|
55
48
|
return;
|
|
49
|
+
if (typeof ResizeObserver === 'undefined')
|
|
50
|
+
return;
|
|
56
51
|
const clampToContainer = () => {
|
|
57
52
|
const rect = el.getBoundingClientRect();
|
|
58
53
|
const total = direction === 'horizontal' ? rect.width : rect.height;
|
|
@@ -62,11 +57,10 @@ export function SplitPaneProvider({ children, direction, initialPrimarySize, min
|
|
|
62
57
|
setPrimarySize(prev => clamp(prev, minPrimarySize, maxPrimary));
|
|
63
58
|
};
|
|
64
59
|
clampToContainer();
|
|
65
|
-
const ro = new ResizeObserver(
|
|
60
|
+
const ro = new ResizeObserver(clampToContainer);
|
|
66
61
|
ro.observe(el);
|
|
67
62
|
return () => ro.disconnect();
|
|
68
63
|
}, [direction, minPrimarySize, minSecondarySize]);
|
|
69
|
-
// Persist on change
|
|
70
64
|
useEffect(() => {
|
|
71
65
|
if (!storageKey)
|
|
72
66
|
return;
|
|
@@ -2,18 +2,13 @@ import React, { JSX } from 'react';
|
|
|
2
2
|
type StickyFooterLayoutProps = {
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
footer: React.ReactNode;
|
|
5
|
-
|
|
6
|
-
width?: number | string;
|
|
7
|
-
/** match the app chrome padding token (defaults to --spacing-md) */
|
|
5
|
+
maxWidth?: number | string;
|
|
8
6
|
chromePaddingVar?: string;
|
|
9
|
-
/** inner padding for content (defaults to "var(--spacing-md) 0") */
|
|
10
7
|
contentPadding?: string;
|
|
11
|
-
/** background used to “paint over” what scrolls behind */
|
|
12
8
|
background?: string;
|
|
13
|
-
/** extra space below content so last field doesn't sit
|
|
9
|
+
/** extra space below content so last field doesn't sit too close to footer */
|
|
14
10
|
contentBottomSpacer?: string | number;
|
|
15
|
-
/** optional classnames */
|
|
16
11
|
className?: string;
|
|
17
12
|
};
|
|
18
|
-
export declare function StickyFooterLayout({ children, footer,
|
|
13
|
+
export declare function StickyFooterLayout({ children, footer, maxWidth, chromePaddingVar, contentPadding, background, contentBottomSpacer, className, }: StickyFooterLayoutProps): JSX.Element;
|
|
19
14
|
export {};
|
|
@@ -1,27 +1,64 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
|
|
3
|
+
import { useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
+
export function StickyFooterLayout({ children, footer, maxWidth = 550, chromePaddingVar = '--spacing-md', contentPadding = '0', background = 'var(--color-bg-surface)', contentBottomSpacer = '16px', className, }) {
|
|
4
5
|
const pad = `var(${chromePaddingVar})`;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const contentColRef = useRef(null);
|
|
7
|
+
const footerOverlayRef = useRef(null);
|
|
8
|
+
const [dock, setDock] = useState({
|
|
9
|
+
left: 0,
|
|
10
|
+
width: typeof maxWidth === 'number' ? maxWidth : 550,
|
|
11
|
+
});
|
|
12
|
+
const [overlayHeight, setOverlayHeight] = useState(0);
|
|
13
|
+
// Measure the LEFT + WIDTH of the content column (for aligning footer inner)
|
|
14
|
+
useLayoutEffect(() => {
|
|
15
|
+
const el = contentColRef.current;
|
|
16
|
+
if (!el)
|
|
17
|
+
return;
|
|
18
|
+
const measure = () => {
|
|
19
|
+
const rect = el.getBoundingClientRect();
|
|
20
|
+
setDock({ left: rect.left, width: rect.width });
|
|
21
|
+
};
|
|
22
|
+
measure();
|
|
23
|
+
const ro = new ResizeObserver(measure);
|
|
24
|
+
ro.observe(el);
|
|
25
|
+
window.addEventListener('resize', measure);
|
|
26
|
+
return () => {
|
|
27
|
+
ro.disconnect();
|
|
28
|
+
window.removeEventListener('resize', measure);
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
// Measure the ACTUAL fixed footer overlay height (includes paddingBlock, borders, etc.)
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
const el = footerOverlayRef.current;
|
|
34
|
+
if (!el)
|
|
35
|
+
return;
|
|
36
|
+
const measure = () => setOverlayHeight(el.getBoundingClientRect().height);
|
|
37
|
+
measure();
|
|
38
|
+
const ro = new ResizeObserver(measure);
|
|
39
|
+
ro.observe(el);
|
|
40
|
+
return () => ro.disconnect();
|
|
41
|
+
}, []);
|
|
42
|
+
const bottomPad = `calc(${overlayHeight}px + ${String(contentBottomSpacer)})`;
|
|
43
|
+
return (_jsxs("div", { className: className, style: { background }, children: [_jsx("div", { ref: contentColRef, style: {
|
|
44
|
+
maxWidth,
|
|
45
|
+
boxSizing: 'border-box',
|
|
10
46
|
padding: contentPadding,
|
|
11
|
-
paddingBottom:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
bottom: `calc(-1 * ${pad})`,
|
|
47
|
+
paddingBottom: bottomPad,
|
|
48
|
+
}, children: children }), _jsx("div", { ref: footerOverlayRef, style: {
|
|
49
|
+
position: 'fixed',
|
|
50
|
+
left: 0,
|
|
51
|
+
right: 0,
|
|
52
|
+
bottom: 0,
|
|
18
53
|
zIndex: 10,
|
|
19
54
|
background,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
55
|
+
paddingBlock: pad,
|
|
56
|
+
boxSizing: 'border-box',
|
|
57
|
+
}, children: _jsx("div", { style: {
|
|
58
|
+
position: 'relative',
|
|
59
|
+
left: dock.left,
|
|
60
|
+
width: dock.width,
|
|
61
|
+
boxSizing: 'border-box',
|
|
62
|
+
/* you said you removed padding-inline to match form edge exactly */
|
|
63
|
+
}, className: "dbc-flex dbc-justify-end", children: footer }) })] }));
|
|
27
64
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { HTMLAttributes, JSX, ReactNode } from 'react';
|
|
3
3
|
import { Severity } from '../../constants/severity.types';
|
|
4
4
|
import { PageChangeEvent } from '../../components/pagination/Pagination';
|
|
5
5
|
import { ViewMode } from '../../hooks/useTableSettings';
|
|
6
6
|
import { TableEmptyConfig } from './components/empty-state/EmptyState';
|
|
7
|
-
|
|
7
|
+
import { SortDirection } from './table.utils';
|
|
8
8
|
export interface ColumnItem<T> {
|
|
9
9
|
id: string;
|
|
10
10
|
header: string | (() => ReactNode);
|
|
@@ -15,17 +15,17 @@ export interface ColumnItem<T> {
|
|
|
15
15
|
hidden?: boolean;
|
|
16
16
|
align?: 'left' | 'right' | 'center';
|
|
17
17
|
verticalAlign?: 'top' | 'middle' | 'bottom';
|
|
18
|
-
fitContent?: boolean;
|
|
19
18
|
allowWrap?: boolean;
|
|
20
19
|
emptyPlaceholder?: ReactNode;
|
|
21
|
-
|
|
20
|
+
canHide?: boolean;
|
|
21
|
+
severity?: any;
|
|
22
22
|
}
|
|
23
23
|
type HeaderExtrasArgs<T> = {
|
|
24
24
|
column: ColumnItem<T>;
|
|
25
25
|
index: number;
|
|
26
26
|
};
|
|
27
27
|
export type TableVariant = 'primary' | 'embedded';
|
|
28
|
-
export
|
|
28
|
+
export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTMLTableElement>, 'onClick'> & {
|
|
29
29
|
data: T[];
|
|
30
30
|
dataKey: keyof T;
|
|
31
31
|
columns: ColumnItem<T>[];
|
|
@@ -41,7 +41,7 @@ export interface TableProps<T extends Record<string, any>> {
|
|
|
41
41
|
loading?: boolean;
|
|
42
42
|
headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
|
|
43
43
|
columnStyles?: Partial<Record<string, React.CSSProperties>>;
|
|
44
|
-
|
|
44
|
+
toolbar?: ReactNode;
|
|
45
45
|
striped?: boolean;
|
|
46
46
|
fillViewport?: boolean;
|
|
47
47
|
viewportBottomOffset?: number;
|
|
@@ -58,6 +58,6 @@ export interface TableProps<T extends Record<string, any>> {
|
|
|
58
58
|
showFirstLast?: boolean;
|
|
59
59
|
viewMode?: ViewMode;
|
|
60
60
|
emptyConfig?: TableEmptyConfig;
|
|
61
|
-
}
|
|
62
|
-
export declare function Table<T extends Record<string, any>>({ data, columns, selectedRows, onRowSelect, selectionMode, onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles,
|
|
61
|
+
} & Omit<HTMLAttributes<HTMLTableElement>, 'onClick'>;
|
|
62
|
+
export declare function Table<T extends Record<string, any>>({ data, columns, selectedRows, onRowSelect, selectionMode, onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, toolbar, striped, fillViewport, viewportBottomOffset, viewportMin, viewportIncludeMarginTop, take, skip, paginationPlacement, totalItemsCount, onPageChange, loading, variant, size, getRowSeverity, showFirstLast, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, ...rest }: TableProps<T>): JSX.Element;
|
|
63
63
|
export {};
|
|
@@ -9,78 +9,48 @@ import { Pagination } from '../../components/pagination/Pagination';
|
|
|
9
9
|
import { SkeletonLoaderItem } from '../../components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
|
|
10
10
|
import { TableEmptyState } from './components/empty-state/EmptyState';
|
|
11
11
|
import styles from './Table.module.css';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
}, [onPageChange]);
|
|
17
|
-
const getColStyle = (columnId, alignment, verticalAlignment, width) => {
|
|
18
|
-
const baseStyle = columnStyles === null || columnStyles === void 0 ? void 0 : columnStyles[columnId];
|
|
19
|
-
return {
|
|
20
|
-
...(baseStyle !== null && baseStyle !== void 0 ? baseStyle : {}),
|
|
21
|
-
...(alignment === 'right' && { fontVariantNumeric: 'tabular-nums' }),
|
|
22
|
-
verticalAlign: verticalAlignment !== null && verticalAlignment !== void 0 ? verticalAlignment : 'top',
|
|
23
|
-
textAlign: alignment !== null && alignment !== void 0 ? alignment : 'left',
|
|
24
|
-
width: width !== null && width !== void 0 ? width : baseStyle === null || baseStyle === void 0 ? void 0 : baseStyle.width,
|
|
25
|
-
minWidth: width !== null && width !== void 0 ? width : baseStyle === null || baseStyle === void 0 ? void 0 : baseStyle.minWidth,
|
|
26
|
-
};
|
|
27
|
-
};
|
|
12
|
+
import { getAriaSort, getCellDisplayValue, getColumnStyle, getHeaderLabel, getNextSortDirection, getRowKey, getVisibleColumns, isModifierClick, shouldAllowWrap, shouldToggleOnKey, isActiveSort, } from './table.utils';
|
|
13
|
+
export function Table({ data, columns, selectedRows, onRowSelect, selectionMode = 'single', onSortChange, onRowClick, sortById, sortDirection, dataKey, headerExtras, columnStyles, toolbar, striped, fillViewport = false, viewportBottomOffset = 0, viewportMin = 120, viewportIncludeMarginTop = false, take, skip, paginationPlacement = 'bottom', totalItemsCount, onPageChange, loading, variant = 'primary', size = 'md', getRowSeverity, showFirstLast = false, allRowsSelected, onSelectAllRows, viewMode, emptyConfig, ...rest }) {
|
|
14
|
+
const filteredColumns = useMemo(() => getVisibleColumns(columns), [columns]);
|
|
15
|
+
const handlePageChange = useCallback((e) => onPageChange === null || onPageChange === void 0 ? void 0 : onPageChange(e), [onPageChange]);
|
|
28
16
|
const scrollRef = useRef(null);
|
|
29
17
|
const { style: viewportStyle } = useViewportFill(scrollRef, {
|
|
30
|
-
bottomOffset: viewportBottomOffset + 60,
|
|
18
|
+
bottomOffset: viewportBottomOffset + (onPageChange ? 60 : 0),
|
|
31
19
|
min: viewportMin,
|
|
32
20
|
includeMarginTop: viewportIncludeMarginTop,
|
|
33
21
|
});
|
|
34
|
-
const tableEl = (_jsxs(_Fragment, { children: [_jsxs("table", { className: `${styles.table} ${styles[variant]} ${styles[size]} ${getRowSeverity ? styles.severityTable : ''}`, children: [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
: null;
|
|
49
|
-
|
|
50
|
-
};
|
|
51
|
-
return (_jsx("th", { style: getColStyle(column.id, column.align, 'middle'), "aria-sort": ariaSort, className: `${styles.th} ${column.sortable ? styles.sortable : ''} `, onClick: e => {
|
|
52
|
-
if (!column.sortable)
|
|
53
|
-
return;
|
|
54
|
-
if (e.target instanceof HTMLElement && e.target.closest('.resizer'))
|
|
55
|
-
return;
|
|
56
|
-
toggleSort();
|
|
57
|
-
}, role: column.sortable ? 'button' : undefined, tabIndex: column.sortable ? 0 : undefined, onKeyDown: e => {
|
|
58
|
-
if (!column.sortable)
|
|
59
|
-
return;
|
|
60
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
61
|
-
e.preventDefault();
|
|
62
|
-
toggleSort();
|
|
63
|
-
}
|
|
64
|
-
}, children: _jsx("div", { className: styles.thInner, children: _jsxs("span", { children: [_jsx("span", { className: styles.thLabel, children: typeof column.header === 'function' ? column.header() : column.header }), column.sortable && (_jsxs("span", { className: styles.sortIndicator, "aria-hidden": "true", children: [isActiveSort && sortDirection === 'asc' && _jsx(ArrowUp, {}), isActiveSort && sortDirection === 'desc' && (_jsx(ArrowDown, { className: styles.descending })), !isActiveSort && (_jsx(ArrowDown, { className: `${styles.descending} ${styles.inActiveSort}` }))] })), headerExtras === null || headerExtras === void 0 ? void 0 : headerExtras({ column, index })] }) }) }, column.id));
|
|
65
|
-
})] }), headerBelowRow ? (_jsx("tr", { className: styles.headerBelowRow, children: _jsx("th", { colSpan: filteredColumns.length, children: headerBelowRow }) })) : null] }), loading && !data.length ? (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: Array.from({ length: take !== null && take !== void 0 ? take : 5 }).map((_, rowIndex) => (_jsx("tr", { children: filteredColumns.map((column, colIndex) => (_jsx("td", { style: getColStyle(column.id, column.align, 'middle', column.width), className: `${styles.tableCell} ${column.fitContent ? 'fitContent' : ''}`, children: _jsx(SkeletonLoaderItem, { height: 20, width: "100%" }) }, `${column.id}-${colIndex}`))) }, `loading-row-${rowIndex}`))) })) : (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: data === null || data === void 0 ? void 0 : data.map((row, rowIndex) => {
|
|
22
|
+
const tableEl = (_jsxs(_Fragment, { children: [toolbar ? _jsx("div", { style: { marginBottom: 12 }, children: toolbar }) : null, _jsxs("table", { ...rest, className: `${styles.table} ${styles[variant]} ${styles[size]} ${getRowSeverity ? styles.severityTable : ''}`, children: [_jsx("thead", { children: _jsxs("tr", { children: [selectedRows && onRowSelect && dataKey && (_jsx("th", { className: `${styles.th} ${styles.selectionCell}`, children: selectionMode === 'multiple' ? (_jsx(Checkbox, { size: "sm", variant: "primary", checked: allRowsSelected, onChange: checked => onSelectAllRows === null || onSelectAllRows === void 0 ? void 0 : onSelectAllRows(checked) })) : null })), filteredColumns.map((column, index) => {
|
|
23
|
+
const active = isActiveSort(sortById, column.id);
|
|
24
|
+
const ariaSort = getAriaSort(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
|
|
25
|
+
const toggleSort = () => {
|
|
26
|
+
if (!onSortChange || !column.sortable)
|
|
27
|
+
return;
|
|
28
|
+
const nextDir = getNextSortDirection(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
|
|
29
|
+
onSortChange(column, nextDir);
|
|
30
|
+
};
|
|
31
|
+
return (_jsx("th", { style: getColumnStyle(column.id, columnStyles, column.align, 'middle'), "aria-sort": ariaSort, className: `${styles.th}`, children: _jsxs("div", { className: styles.thInner, children: [column.sortable ? (_jsxs("button", { type: "button", className: styles.thButton, onClick: toggleSort, onKeyDown: e => {
|
|
32
|
+
if (shouldToggleOnKey(e.key)) {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
toggleSort();
|
|
35
|
+
}
|
|
36
|
+
}, children: [_jsx("span", { className: styles.thLabel, children: getHeaderLabel(column.header) }), _jsxs("span", { className: styles.sortIndicator, "aria-hidden": "true", children: [active && sortDirection === 'asc' && _jsx(ArrowUp, {}), active && sortDirection === 'desc' && (_jsx(ArrowDown, { className: styles.descending })), !active && (_jsx(ArrowDown, { className: `${styles.descending} ${styles.inActiveSort}` }))] })] })) : (_jsx("span", { className: styles.thLabel, children: getHeaderLabel(column.header) })), headerExtras ? (_jsx("div", { className: styles.thExtras, children: headerExtras({ column, index }) })) : null] }) }, column.id));
|
|
37
|
+
})] }) }), loading && !data.length ? (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: Array.from({ length: take !== null && take !== void 0 ? take : 5 }).map((_, rowIndex) => (_jsx("tr", { children: filteredColumns.map((column, colIndex) => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, 'middle'), className: `${styles.tableCell}`, children: _jsx(SkeletonLoaderItem, { height: 20, width: "100%" }) }, `${column.id}-${colIndex}`))) }, `loading-row-${rowIndex}`))) })) : (_jsx("tbody", { className: `${styles.tBody} ${striped ? styles.striped : ''}`, children: data === null || data === void 0 ? void 0 : data.map(row => {
|
|
66
38
|
const rowSeverity = getRowSeverity === null || getRowSeverity === void 0 ? void 0 : getRowSeverity(row);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
39
|
+
const rowId = row[dataKey];
|
|
40
|
+
const isSelected = Boolean(selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(rowId));
|
|
41
|
+
return (_jsxs("tr", { tabIndex: onRowClick ? 0 : -1, onKeyDown: e => {
|
|
42
|
+
if (!onRowClick)
|
|
43
|
+
return;
|
|
44
|
+
if (shouldToggleOnKey(e.key)) {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
onRowClick(row);
|
|
47
|
+
}
|
|
48
|
+
}, onClick: e => {
|
|
70
49
|
const canSelect = Boolean(selectedRows && onRowSelect && dataKey);
|
|
71
|
-
if (isModifierClick && canSelect) {
|
|
50
|
+
if (isModifierClick(e) && canSelect) {
|
|
72
51
|
e.preventDefault();
|
|
73
52
|
e.stopPropagation();
|
|
74
|
-
|
|
75
|
-
if (selectionMode === 'single') {
|
|
76
|
-
// In single mode, treat modifier-click as "select this row"
|
|
77
|
-
// (toggle if already selected)
|
|
78
|
-
onRowSelect(rowId, !isSelected);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// multiple mode: toggle selection
|
|
82
|
-
onRowSelect(rowId, !isSelected);
|
|
83
|
-
}
|
|
53
|
+
onRowSelect(rowId, !selectedRows.has(rowId));
|
|
84
54
|
return;
|
|
85
55
|
}
|
|
86
56
|
onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row);
|
|
@@ -88,18 +58,9 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
|
|
|
88
58
|
['--row-severity-color']: rowSeverity
|
|
89
59
|
? SeverityBgColor[rowSeverity]
|
|
90
60
|
: undefined,
|
|
91
|
-
}, className: `${onRowClick ? styles.clickableRow : ''} ${
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
(selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(row[dataKey])) ||
|
|
95
|
-
viewMode === 'comfortable'
|
|
96
|
-
? styles.allowWrap
|
|
97
|
-
: styles.nowrap} `, children: column.render
|
|
98
|
-
? column.render(row) || ((_a = column.emptyPlaceholder) !== null && _a !== void 0 ? _a : '')
|
|
99
|
-
: column.accessor
|
|
100
|
-
? row[column.accessor] || ((_b = column.emptyPlaceholder) !== null && _b !== void 0 ? _b : '')
|
|
101
|
-
: null }, column.id));
|
|
102
|
-
})] }, `tableRow-${String(row[dataKey])}-${rowIndex}`));
|
|
61
|
+
}, className: `${onRowClick ? styles.clickableRow : ''} ${isSelected ? styles.selectedRow : ''} ${rowSeverity ? styles.severity : ''}`, children: [selectedRows && onRowSelect && dataKey && (_jsx("td", { className: `${styles.selectionCell}`, onClick: e => e.stopPropagation(), children: _jsx(Checkbox, { variant: "primary", checked: selectedRows.has(rowId), size: "sm", onChange: () => onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, !selectedRows.has(rowId)) }) })), filteredColumns.map(column => (_jsx("td", { style: getColumnStyle(column.id, columnStyles, column.align, column.verticalAlign), className: `${styles.tableCell} ${shouldAllowWrap(column.allowWrap, isSelected, viewMode)
|
|
62
|
+
? styles.allowWrap
|
|
63
|
+
: styles.nowrap}`, children: getCellDisplayValue(row, column) }, column.id)))] }, getRowKey(rowId)));
|
|
103
64
|
}) }))] }), !data.length && !loading && _jsx(TableEmptyState, { config: emptyConfig })] }));
|
|
104
65
|
if (fillViewport) {
|
|
105
66
|
return (_jsxs("div", { style: {
|
|
@@ -110,10 +71,7 @@ export function Table({ data, columns, selectedRows, onRowSelect, selectionMode
|
|
|
110
71
|
position: 'relative',
|
|
111
72
|
}, children: [_jsx("div", { ref: scrollRef, style: viewportStyle, className: styles.tableScroll, children: tableEl }), onPageChange && data.length > 0 && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange, showFirstLast: showFirstLast }))] }));
|
|
112
73
|
}
|
|
113
|
-
return (_jsxs("div", { style: {
|
|
114
|
-
display: 'flex',
|
|
115
|
-
flexDirection: 'column',
|
|
116
|
-
gap: '20px',
|
|
74
|
+
return (_jsxs("div", { className: "dbc-flex dbc-flex-column dbc-gap-md", style: {
|
|
117
75
|
flexFlow: paginationPlacement === 'top' ? 'column-reverse' : 'column',
|
|
118
76
|
position: 'relative',
|
|
119
77
|
}, children: [tableEl, onPageChange && data.length > 0 && (_jsx(Pagination, { itemsCount: totalItemsCount, take: take, skip: skip, onPageChange: handlePageChange }))] }));
|