@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,22 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
export type TabId = string | number;
|
|
2
3
|
export type TabItem = {
|
|
3
4
|
header: string;
|
|
4
|
-
id:
|
|
5
|
-
headerIcon?:
|
|
6
|
-
content:
|
|
5
|
+
id: TabId;
|
|
6
|
+
headerIcon?: ReactNode;
|
|
7
|
+
content: ReactNode;
|
|
7
8
|
disabled?: boolean;
|
|
8
9
|
hidden?: boolean;
|
|
9
10
|
badge?: number;
|
|
10
11
|
};
|
|
11
|
-
|
|
12
|
+
type TabsVariant = 'filled' | 'outlined';
|
|
13
|
+
export interface TabsProps {
|
|
12
14
|
header?: string;
|
|
13
|
-
variant:
|
|
15
|
+
variant: TabsVariant;
|
|
14
16
|
panelStyle?: boolean;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
/** Data-driven API */
|
|
18
|
+
tabs?: TabItem[];
|
|
19
|
+
/** Controlled */
|
|
20
|
+
value?: TabId;
|
|
21
|
+
/** Uncontrolled initial */
|
|
22
|
+
defaultValue?: TabId;
|
|
23
|
+
onValueChange?: (id: TabId, tab: TabItem, index: number) => void;
|
|
24
|
+
addition?: ReactNode;
|
|
25
|
+
/** Composition API */
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
export interface TabsItemProps {
|
|
29
|
+
id: TabId;
|
|
30
|
+
header: string;
|
|
31
|
+
headerIcon?: ReactNode;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
hidden?: boolean;
|
|
34
|
+
badge?: number;
|
|
35
|
+
children?: ReactNode;
|
|
36
|
+
}
|
|
37
|
+
type SlotName = 'Item';
|
|
38
|
+
type TabsItemComponent = ((props: TabsItemProps) => JSX.Element) & {
|
|
39
|
+
__TABS_SLOT__?: SlotName;
|
|
40
|
+
};
|
|
41
|
+
export declare function Tabs({ header, variant, panelStyle, tabs, value, defaultValue, onValueChange, addition, children, }: TabsProps): JSX.Element;
|
|
42
|
+
export declare namespace Tabs {
|
|
43
|
+
var Item: TabsItemComponent;
|
|
20
44
|
}
|
|
21
|
-
export declare function Tabs({ variant, header, tabs, activeId, onTabChange, manuallySetActiveTab, addition, panelStyle, }: TabsProps): React.ReactNode;
|
|
22
45
|
export {};
|
|
@@ -1,35 +1,123 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, isValidElement, useCallback, useEffect, useId, useMemo, useState, } from 'react';
|
|
4
3
|
import styles from './Tabs.module.css';
|
|
5
4
|
import { Chip } from '../chip/Chip';
|
|
6
5
|
import { Headline } from '../headline/Headline';
|
|
7
|
-
|
|
6
|
+
const TabsItem = (_props) => {
|
|
7
|
+
// This never renders directly; parent consumes it.
|
|
8
|
+
return _jsx(_Fragment, {});
|
|
9
|
+
};
|
|
10
|
+
TabsItem.__TABS_SLOT__ = 'Item';
|
|
11
|
+
function getFirstEnabledId(items) {
|
|
8
12
|
var _a;
|
|
9
|
-
|
|
13
|
+
return (_a = items.find(t => !t.hidden && !t.disabled)) === null || _a === void 0 ? void 0 : _a.id;
|
|
14
|
+
}
|
|
15
|
+
function normalizeFromChildren(children) {
|
|
16
|
+
const items = [];
|
|
17
|
+
Children.forEach(children, child => {
|
|
18
|
+
if (!isValidElement(child))
|
|
19
|
+
return;
|
|
20
|
+
const t = child.type;
|
|
21
|
+
if ((t === null || t === void 0 ? void 0 : t.__TABS_SLOT__) !== 'Item')
|
|
22
|
+
return;
|
|
23
|
+
const props = child.props;
|
|
24
|
+
items.push({
|
|
25
|
+
id: props.id,
|
|
26
|
+
header: props.header,
|
|
27
|
+
headerIcon: props.headerIcon,
|
|
28
|
+
disabled: props.disabled,
|
|
29
|
+
hidden: props.hidden,
|
|
30
|
+
badge: props.badge,
|
|
31
|
+
content: props.children,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return items;
|
|
35
|
+
}
|
|
36
|
+
export function Tabs({ header, variant, panelStyle = false, tabs, value, defaultValue, onValueChange, addition, children, }) {
|
|
37
|
+
const uid = useId();
|
|
38
|
+
// Data API wins if provided; otherwise parse <Tabs.Item>
|
|
39
|
+
const sourceTabs = useMemo(() => {
|
|
40
|
+
if (tabs && tabs.length)
|
|
41
|
+
return tabs;
|
|
42
|
+
return normalizeFromChildren(children);
|
|
43
|
+
}, [tabs, children]);
|
|
44
|
+
const visibleTabs = useMemo(() => sourceTabs.filter(t => !t.hidden), [sourceTabs]);
|
|
45
|
+
const isControlled = value !== undefined;
|
|
46
|
+
const [internalValue, setInternalValue] = useState(() => {
|
|
47
|
+
return defaultValue !== null && defaultValue !== void 0 ? defaultValue : getFirstEnabledId(visibleTabs);
|
|
48
|
+
});
|
|
49
|
+
const currentValue = isControlled ? value : internalValue;
|
|
50
|
+
const activeIndex = useMemo(() => {
|
|
51
|
+
if (!visibleTabs.length)
|
|
52
|
+
return -1;
|
|
53
|
+
const idx = visibleTabs.findIndex(t => t.id === currentValue);
|
|
54
|
+
return idx >= 0 ? idx : 0;
|
|
55
|
+
}, [visibleTabs, currentValue]);
|
|
56
|
+
const activeTab = activeIndex >= 0 ? visibleTabs[activeIndex] : undefined;
|
|
57
|
+
const setValue = useCallback((nextId) => {
|
|
58
|
+
const idx = visibleTabs.findIndex(t => t.id === nextId);
|
|
59
|
+
const tab = idx >= 0 ? visibleTabs[idx] : undefined;
|
|
60
|
+
if (!tab || tab.disabled)
|
|
61
|
+
return;
|
|
62
|
+
if (!isControlled)
|
|
63
|
+
setInternalValue(nextId);
|
|
64
|
+
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(nextId, tab, idx);
|
|
65
|
+
}, [visibleTabs, isControlled, onValueChange]);
|
|
66
|
+
// If current tab disappears (hidden/removed) or becomes disabled, recover to first enabled.
|
|
10
67
|
useEffect(() => {
|
|
11
|
-
if (
|
|
12
|
-
|
|
68
|
+
if (!visibleTabs.length)
|
|
69
|
+
return;
|
|
70
|
+
const current = currentValue;
|
|
71
|
+
const stillValid = visibleTabs.some(t => t.id === current && !t.disabled);
|
|
72
|
+
if (stillValid)
|
|
73
|
+
return;
|
|
74
|
+
const next = getFirstEnabledId(visibleTabs);
|
|
75
|
+
if (next === undefined)
|
|
76
|
+
return;
|
|
77
|
+
setValue(next);
|
|
78
|
+
// setValue already calls onValueChange; fine.
|
|
79
|
+
// For controlled usage, parent should update `value` accordingly.
|
|
80
|
+
// If parent doesn't, it will remain "stuck" by design.
|
|
81
|
+
}, [visibleTabs, currentValue, setValue]);
|
|
82
|
+
// Keyboard: roving tabindex + select-on-arrow
|
|
83
|
+
const onKeyDownTab = useCallback((e, index) => {
|
|
84
|
+
var _a;
|
|
85
|
+
const enabled = visibleTabs.filter(t => !t.disabled);
|
|
86
|
+
if (!enabled.length)
|
|
87
|
+
return;
|
|
88
|
+
// Map current index -> enabled index
|
|
89
|
+
const currentId = (_a = visibleTabs[index]) === null || _a === void 0 ? void 0 : _a.id;
|
|
90
|
+
const currentEnabledIndex = enabled.findIndex(t => t.id === currentId);
|
|
91
|
+
const focusAndSelect = (enabledIndex) => {
|
|
92
|
+
const nextTab = enabled[enabledIndex];
|
|
93
|
+
if (!nextTab)
|
|
94
|
+
return;
|
|
95
|
+
const btn = document.getElementById(`${uid}-tab-${String(nextTab.id)}`);
|
|
96
|
+
btn === null || btn === void 0 ? void 0 : btn.focus();
|
|
97
|
+
setValue(nextTab.id);
|
|
98
|
+
};
|
|
99
|
+
if (e.key === 'ArrowRight') {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
focusAndSelect((currentEnabledIndex + 1) % enabled.length);
|
|
13
102
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}, [setCurrentId]);
|
|
18
|
-
const handleTabChange = useCallback((index, tab) => {
|
|
19
|
-
if (!manuallySetActiveTab) {
|
|
20
|
-
setActiveTab(tab.id);
|
|
103
|
+
else if (e.key === 'ArrowLeft') {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
focusAndSelect((currentEnabledIndex - 1 + enabled.length) % enabled.length);
|
|
21
106
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (activeIndex === -1 && filteredTabs.length > 0) {
|
|
31
|
-
setActiveTab(filteredTabs[0].id);
|
|
107
|
+
else if (e.key === 'Home') {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
focusAndSelect(0);
|
|
110
|
+
}
|
|
111
|
+
else if (e.key === 'End') {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
focusAndSelect(enabled.length - 1);
|
|
32
114
|
}
|
|
33
|
-
}, [
|
|
34
|
-
return (_jsxs("div", { className:
|
|
115
|
+
}, [uid, visibleTabs, setValue]);
|
|
116
|
+
return (_jsxs("div", { className: styles.root, children: [header ? (_jsxs("div", { className: styles.headerContainer, children: [_jsx(Headline, { disableMargin: true, size: 2, children: header }), addition] })) : null, _jsxs("div", { className: `${styles.tabs} ${styles[variant]} ${panelStyle ? styles.panelStyle : ''}`, children: [_jsx("div", { className: styles.tabList, role: "tablist", "aria-label": header !== null && header !== void 0 ? header : 'Tabs', children: visibleTabs.map((tab, index) => {
|
|
117
|
+
const selected = index === activeIndex;
|
|
118
|
+
const tabDomId = `${uid}-tab-${String(tab.id)}`;
|
|
119
|
+
const panelDomId = `${uid}-panel-${String(tab.id)}`;
|
|
120
|
+
return (_jsx("div", { className: `${styles.tab} ${selected ? styles.active : ''}`, children: _jsxs("button", { id: tabDomId, type: "button", className: styles.tabButton, role: "tab", "aria-selected": selected, "aria-controls": panelDomId, tabIndex: selected ? 0 : -1, disabled: tab.disabled, onClick: () => setValue(tab.id), onKeyDown: e => onKeyDownTab(e, index), children: [tab.headerIcon ? _jsx("span", { className: styles.icon, children: tab.headerIcon }) : null, _jsx("span", { className: styles.label, children: tab.header }), tab.badge !== undefined && tab.badge > 0 ? (_jsx("span", { className: styles.badge, children: _jsx(Chip, { severity: "info", size: "sm", children: tab.badge.toLocaleString('da-DK') }) })) : null] }) }, tab.id));
|
|
121
|
+
}) }), _jsx("div", { id: activeTab ? `${uid}-panel-${String(activeTab.id)}` : undefined, role: "tabpanel", "aria-labelledby": activeTab ? `${uid}-tab-${String(activeTab.id)}` : undefined, className: styles.tabContent, children: activeTab === null || activeTab === void 0 ? void 0 : activeTab.content })] })] }));
|
|
35
122
|
}
|
|
123
|
+
Tabs.Item = TabsItem;
|
|
@@ -1,113 +1,200 @@
|
|
|
1
|
-
.
|
|
1
|
+
.root {
|
|
2
2
|
display: flex;
|
|
3
3
|
flex-direction: column;
|
|
4
|
-
|
|
5
|
-
gap: var(--spacing-xl);
|
|
6
|
-
overflow: hidden;
|
|
4
|
+
min-width: 0;
|
|
7
5
|
}
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
.tabs.filled {
|
|
17
|
-
gap: 0;
|
|
18
|
-
}
|
|
19
|
-
.tabs.filled .tabList {
|
|
20
|
-
border-start-start-radius: var(--border-radius-default);
|
|
21
|
-
border-start-end-radius: var(--border-radius-default);
|
|
22
|
-
inline-size: fit-content;
|
|
23
|
-
}
|
|
24
|
-
.tabs.filled .tab {
|
|
25
|
-
border: var(--border-width-thin) solid transparent;
|
|
26
|
-
color: var(--color-fg-muted);
|
|
27
|
-
font-size: var(--font-size-sm);
|
|
28
|
-
z-index: 3;
|
|
29
|
-
transition:
|
|
30
|
-
background-color var(--transition-fast) var(--ease-standard),
|
|
31
|
-
color var(--transition-fast) var(--ease-standard);
|
|
32
|
-
border-start-start-radius: var(--border-radius-default);
|
|
33
|
-
border-start-end-radius: var(--border-radius-default);
|
|
34
|
-
}
|
|
35
|
-
.tabs.filled .tab:not(.active):hover {
|
|
36
|
-
color: var(--color-brand);
|
|
7
|
+
.headerContainer {
|
|
8
|
+
display: flex;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
align-items: center;
|
|
11
|
+
padding-block: var(--spacing-lg);
|
|
12
|
+
gap: var(--spacing-md);
|
|
37
13
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
14
|
+
|
|
15
|
+
/* Outer wrapper for tablist + content */
|
|
16
|
+
.tabs {
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
42
19
|
flex-grow: 1;
|
|
43
|
-
overflow:
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
min-height: 0;
|
|
44
22
|
}
|
|
45
23
|
|
|
46
|
-
/*
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
24
|
+
/* Tablist */
|
|
25
|
+
.tabList {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
min-width: 0;
|
|
50
29
|
}
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
|
|
31
|
+
/* Individual tab wrapper */
|
|
32
|
+
.tab {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
border-radius: 0;
|
|
36
|
+
border-block-end: 2px solid var(--color-border-default);
|
|
53
37
|
}
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
|
|
39
|
+
.active {
|
|
40
|
+
border-block-end-color: var(--color-brand);
|
|
56
41
|
}
|
|
57
42
|
|
|
43
|
+
/* Button */
|
|
58
44
|
.tabButton {
|
|
59
|
-
text-transform: var(--tab-label-transform);
|
|
60
45
|
font-size: var(--font-size-sm);
|
|
61
46
|
color: inherit;
|
|
47
|
+
|
|
62
48
|
display: inline-flex;
|
|
63
|
-
white-space: nowrap;
|
|
64
|
-
background: none;
|
|
65
49
|
align-items: center;
|
|
66
50
|
justify-content: center;
|
|
67
51
|
gap: var(--spacing-xs);
|
|
52
|
+
|
|
53
|
+
white-space: nowrap;
|
|
54
|
+
|
|
55
|
+
background: none;
|
|
68
56
|
border: 0;
|
|
69
57
|
border-radius: 0;
|
|
70
|
-
|
|
58
|
+
|
|
59
|
+
padding-block: var(--spacing-sm);
|
|
71
60
|
padding-inline: var(--spacing-md);
|
|
61
|
+
|
|
72
62
|
cursor: pointer;
|
|
63
|
+
|
|
73
64
|
transition:
|
|
74
|
-
border var(--transition-normal) var(--ease-standard),
|
|
75
65
|
color var(--transition-fast) var(--ease-standard),
|
|
76
|
-
background-color var(--transition-fast) var(--ease-standard)
|
|
66
|
+
background-color var(--transition-fast) var(--ease-standard),
|
|
67
|
+
border-color var(--transition-fast) var(--ease-standard);
|
|
77
68
|
}
|
|
69
|
+
|
|
78
70
|
.tabButton:focus-visible {
|
|
79
71
|
outline: none;
|
|
80
72
|
box-shadow: var(--focus-ring);
|
|
81
73
|
}
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
flex-wrap: wrap;
|
|
75
|
+
.tabButton:disabled {
|
|
76
|
+
cursor: not-allowed;
|
|
77
|
+
color: var(--color-disabled-fg);
|
|
87
78
|
}
|
|
88
79
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
/* Icon + label */
|
|
81
|
+
.icon {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
color: inherit;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.icon svg {
|
|
88
|
+
inline-size: var(--icon-size-md);
|
|
89
|
+
block-size: var(--icon-size-md);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.label {
|
|
93
|
+
display: inline-block;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.badge {
|
|
97
|
+
display: inline-flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Content panel */
|
|
102
|
+
.tabContent {
|
|
103
|
+
flex: 1 1 auto;
|
|
104
|
+
min-height: 0;
|
|
105
|
+
overflow: auto;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* =========================
|
|
109
|
+
Filled variant
|
|
110
|
+
========================= */
|
|
111
|
+
|
|
112
|
+
.filled {
|
|
113
|
+
gap: 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.filled .tabList {
|
|
117
|
+
border-start-start-radius: var(--border-radius-default);
|
|
118
|
+
border-start-end-radius: var(--border-radius-default);
|
|
119
|
+
inline-size: fit-content;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.filled .tab {
|
|
123
|
+
border: var(--border-width-thin) solid transparent;
|
|
124
|
+
border-block-end: 0;
|
|
125
|
+
color: var(--color-fg-muted);
|
|
126
|
+
z-index: 3;
|
|
127
|
+
border-start-start-radius: var(--border-radius-default);
|
|
128
|
+
border-start-end-radius: var(--border-radius-default);
|
|
129
|
+
transition:
|
|
130
|
+
background-color var(--transition-fast) var(--ease-standard),
|
|
131
|
+
color var(--transition-fast) var(--ease-standard),
|
|
132
|
+
border-color var(--transition-fast) var(--ease-standard);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.filled .tab:not(.active):hover {
|
|
136
|
+
color: var(--color-brand);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.filled .tab.active {
|
|
140
|
+
background: var(--opac-bg-brand);
|
|
141
|
+
color: var(--color-brand);
|
|
142
|
+
border-color: var(--opac-bg-dark);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.filled .tabContent {
|
|
146
|
+
border: var(--border-width-thin) solid var(--opac-bg-dark);
|
|
147
|
+
background: var(--color-bg-surface);
|
|
148
|
+
padding: var(--spacing-lg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* =========================
|
|
152
|
+
Outlined variant
|
|
153
|
+
========================= */
|
|
154
|
+
|
|
155
|
+
.outlined {
|
|
156
|
+
gap: var(--spacing-md);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.outlined .tab {
|
|
160
|
+
color: var(--color-fg-muted);
|
|
93
161
|
border-block-end: 2px solid var(--color-border-default);
|
|
162
|
+
transition: color var(--transition-fast) var(--ease-standard);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.outlined .tab:not(.active):hover {
|
|
166
|
+
color: var(--color-brand);
|
|
94
167
|
}
|
|
95
|
-
|
|
168
|
+
|
|
169
|
+
.outlined .tab.active {
|
|
170
|
+
color: var(--color-brand);
|
|
96
171
|
border-block-end-color: var(--color-brand);
|
|
97
172
|
}
|
|
98
173
|
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
174
|
+
.outlined .tabContent {
|
|
175
|
+
/* plain panel by default */
|
|
176
|
+
background: transparent;
|
|
177
|
+
padding: 0;
|
|
102
178
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
179
|
+
|
|
180
|
+
/* =========================
|
|
181
|
+
Panel style modifier
|
|
182
|
+
========================= */
|
|
183
|
+
|
|
184
|
+
.panelStyle .tabList {
|
|
185
|
+
border: var(--border-width-thin) solid var(--color-border-default);
|
|
186
|
+
border-block-end: 0;
|
|
106
187
|
}
|
|
107
188
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
189
|
+
/* When panelStyle is on, tabs look “embedded” (no bottom border indicators) */
|
|
190
|
+
.panelStyle .tab {
|
|
191
|
+
border-block-end: 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* In filled mode, keep the filled content panel visuals (already handled above).
|
|
195
|
+
In outlined mode with panelStyle, give the content a panel container too. */
|
|
196
|
+
.panelStyle.outlined .tabContent {
|
|
197
|
+
border: var(--border-width-thin) solid var(--color-border-default);
|
|
198
|
+
background: var(--color-bg-surface);
|
|
199
|
+
padding: var(--spacing-lg);
|
|
113
200
|
}
|
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { ColumnDef } from '@tanstack/react-table';
|
|
2
|
+
export type ViewMode = 'compact' | 'wrapped';
|
|
3
|
+
export type TableSettingsState = {
|
|
3
4
|
viewMode: ViewMode;
|
|
4
|
-
|
|
5
|
+
visibleColumnIds: string[];
|
|
6
|
+
};
|
|
7
|
+
export type TableSettingsStorage = {
|
|
8
|
+
get: (key: string) => TableSettingsState | undefined;
|
|
9
|
+
set: (key: string, next: TableSettingsState) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare const localStorageTableSettingsStorage: TableSettingsStorage;
|
|
12
|
+
interface UseTableSettingsOptions {
|
|
13
|
+
storageKey: string;
|
|
14
|
+
tableColumns?: ColumnDef<any>[];
|
|
15
|
+
defaultViewMode?: ViewMode;
|
|
16
|
+
defaultVisibleColumnIds?: string[];
|
|
17
|
+
storage?: TableSettingsStorage;
|
|
5
18
|
}
|
|
6
|
-
export declare function useTableSettings(storageKey
|
|
19
|
+
export declare function useTableSettings({ storageKey, tableColumns, defaultViewMode, defaultVisibleColumnIds, storage, }: UseTableSettingsOptions): {
|
|
20
|
+
viewMode: ViewMode;
|
|
21
|
+
toggleViewMode: () => void;
|
|
22
|
+
setViewMode: (mode: ViewMode) => void;
|
|
23
|
+
visibleColumnIds: string[];
|
|
24
|
+
setVisibleColumnIds: (ids: string[]) => void;
|
|
25
|
+
};
|
|
7
26
|
export {};
|
|
@@ -1,24 +1,71 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
|
|
4
|
+
function getDefaultVisibleIds(tableColumns) {
|
|
5
|
+
var _a;
|
|
6
|
+
return ((_a = tableColumns === null || tableColumns === void 0 ? void 0 : tableColumns.filter(c => { var _a; return !((_a = c.meta) === null || _a === void 0 ? void 0 : _a.hidden); }).map(c => c.id).filter(Boolean)) !== null && _a !== void 0 ? _a : []);
|
|
7
|
+
}
|
|
8
|
+
function mergeDefaults(stored, defaults) {
|
|
9
|
+
const viewMode = (stored === null || stored === void 0 ? void 0 : stored.viewMode) === 'compact' || (stored === null || stored === void 0 ? void 0 : stored.viewMode) === 'wrapped'
|
|
10
|
+
? stored.viewMode
|
|
11
|
+
: defaults.viewMode;
|
|
12
|
+
const visibleColumnIds = Array.isArray(stored === null || stored === void 0 ? void 0 : stored.visibleColumnIds) && stored.visibleColumnIds.length > 0
|
|
13
|
+
? stored.visibleColumnIds
|
|
14
|
+
: defaults.visibleColumnIds;
|
|
15
|
+
return { viewMode, visibleColumnIds };
|
|
16
|
+
}
|
|
17
|
+
export const localStorageTableSettingsStorage = {
|
|
18
|
+
get: key => {
|
|
19
|
+
const v = readLocalStorage(key);
|
|
20
|
+
return v && typeof v === 'object' ? v : undefined;
|
|
21
|
+
},
|
|
22
|
+
set: (key, next) => {
|
|
23
|
+
writeLocalStorage(key, next);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export function useTableSettings({ storageKey, tableColumns, defaultViewMode = 'compact', defaultVisibleColumnIds, storage = localStorageTableSettingsStorage, }) {
|
|
27
|
+
const defaults = useMemo(() => {
|
|
28
|
+
return {
|
|
29
|
+
viewMode: defaultViewMode,
|
|
30
|
+
visibleColumnIds: defaultVisibleColumnIds !== null && defaultVisibleColumnIds !== void 0 ? defaultVisibleColumnIds : getDefaultVisibleIds(tableColumns),
|
|
31
|
+
};
|
|
32
|
+
}, [defaultViewMode, defaultVisibleColumnIds, tableColumns]);
|
|
33
|
+
const [state, setState] = useState(defaults);
|
|
5
34
|
useEffect(() => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
35
|
+
const stored = storage.get(storageKey);
|
|
36
|
+
const next = mergeDefaults(stored, defaults);
|
|
37
|
+
setState(next);
|
|
38
|
+
}, [storageKey, storage, defaults]);
|
|
39
|
+
const persist = useCallback((next) => storage.set(storageKey, next), [storage, storageKey]);
|
|
40
|
+
const setViewMode = useCallback((mode) => {
|
|
41
|
+
setState(prev => {
|
|
42
|
+
if (prev.viewMode === mode)
|
|
43
|
+
return prev;
|
|
44
|
+
const next = { ...prev, viewMode: mode };
|
|
45
|
+
persist(next);
|
|
46
|
+
return next;
|
|
47
|
+
});
|
|
48
|
+
}, [persist]);
|
|
13
49
|
const toggleViewMode = useCallback(() => {
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
50
|
+
setState(prev => {
|
|
51
|
+
const nextMode = prev.viewMode === 'wrapped' ? 'compact' : 'wrapped';
|
|
52
|
+
const next = { ...prev, viewMode: nextMode };
|
|
53
|
+
persist(next);
|
|
54
|
+
return next;
|
|
55
|
+
});
|
|
56
|
+
}, [persist]);
|
|
57
|
+
const setVisibleColumnIds = useCallback((ids) => {
|
|
58
|
+
setState(prev => {
|
|
59
|
+
const next = { ...prev, visibleColumnIds: ids };
|
|
60
|
+
persist(next);
|
|
61
|
+
return next;
|
|
62
|
+
});
|
|
63
|
+
}, [persist]);
|
|
20
64
|
return {
|
|
21
|
-
viewMode,
|
|
65
|
+
viewMode: state.viewMode,
|
|
22
66
|
toggleViewMode,
|
|
67
|
+
setViewMode,
|
|
68
|
+
visibleColumnIds: state.visibleColumnIds,
|
|
69
|
+
setVisibleColumnIds,
|
|
23
70
|
};
|
|
24
71
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export * from './hooks/useTableSettings';
|
|
|
53
53
|
export * from './components/table/components/table-settings/TableSettings';
|
|
54
54
|
export * from './components/filtering/chip-multi-toggle/ChipMultiToggle';
|
|
55
55
|
export * from './components/forms/checkbox-group/CheckboxGroup';
|
|
56
|
-
export * from './components/table/
|
|
56
|
+
export * from './components/table/TanstackTable';
|
|
57
57
|
export * from './components/overlay/side-panel/SidePanel';
|
|
58
58
|
export * from './components/overlay/side-panel/useSidePanel';
|
|
59
59
|
export * from './components/forms/input-container/InputContainer';
|
package/dist/index.js
CHANGED
|
@@ -53,7 +53,7 @@ export * from './hooks/useTableSettings';
|
|
|
53
53
|
export * from './components/table/components/table-settings/TableSettings';
|
|
54
54
|
export * from './components/filtering/chip-multi-toggle/ChipMultiToggle';
|
|
55
55
|
export * from './components/forms/checkbox-group/CheckboxGroup';
|
|
56
|
-
export * from './components/table/
|
|
56
|
+
export * from './components/table/TanstackTable';
|
|
57
57
|
export * from './components/overlay/side-panel/SidePanel';
|
|
58
58
|
export * from './components/overlay/side-panel/useSidePanel';
|
|
59
59
|
export * from './components/forms/input-container/InputContainer';
|