@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.
Files changed (106) hide show
  1. package/dist/components/accordion/Accordion.d.ts +2 -2
  2. package/dist/components/accordion/Accordion.js +34 -41
  3. package/dist/components/accordion/Accordion.module.css +13 -72
  4. package/dist/components/accordion/components/AccordionRow.d.ts +10 -0
  5. package/dist/components/accordion/components/AccordionRow.js +51 -0
  6. package/dist/components/accordion/components/AccordionRow.module.css +82 -0
  7. package/dist/components/breadcrumbs/Breadcrumbs.module.css +0 -1
  8. package/dist/components/button/Button.module.css +7 -7
  9. package/dist/components/card/Card.d.ts +15 -6
  10. package/dist/components/card/Card.js +39 -13
  11. package/dist/components/card/Card.module.css +22 -28
  12. package/dist/components/card/components/CardMeta.d.ts +15 -0
  13. package/dist/components/card/components/CardMeta.js +20 -0
  14. package/dist/components/card/components/CardMeta.module.css +51 -0
  15. package/dist/components/card-container/CardContainer.js +1 -1
  16. package/dist/components/card-container/CardContainer.module.css +3 -1
  17. package/dist/components/chip/Chip.module.css +7 -2
  18. package/dist/components/circle/Circle.d.ts +2 -1
  19. package/dist/components/circle/Circle.js +2 -2
  20. package/dist/components/circle/Circle.module.css +6 -2
  21. package/dist/components/code-block/CodeBlock.js +1 -1
  22. package/dist/components/code-block/CodeBlock.module.css +30 -17
  23. package/dist/components/copy-button/CopyButton.d.ts +1 -0
  24. package/dist/components/copy-button/CopyButton.js +10 -2
  25. package/dist/components/datetime-picker/DateTimePicker.d.ts +33 -8
  26. package/dist/components/datetime-picker/DateTimePicker.js +119 -78
  27. package/dist/components/datetime-picker/DateTimePicker.module.css +2 -0
  28. package/dist/components/datetime-picker/dateTimeHelpers.d.ts +15 -3
  29. package/dist/components/datetime-picker/dateTimeHelpers.js +137 -23
  30. package/dist/components/filter-field/FilterField.js +16 -11
  31. package/dist/components/filter-field/FilterField.module.css +137 -16
  32. package/dist/components/forms/checkbox/Checkbox.d.ts +2 -2
  33. package/dist/components/forms/checkbox-group/CheckboxGroup.js +1 -1
  34. package/dist/components/forms/checkbox-group/CheckboxGroup.module.css +1 -1
  35. package/dist/components/forms/form-select/FormSelect.d.ts +35 -0
  36. package/dist/components/forms/form-select/FormSelect.js +86 -0
  37. package/dist/components/forms/form-select/FormSelect.module.css +236 -0
  38. package/dist/components/forms/input/Input.d.ts +0 -3
  39. package/dist/components/forms/input/Input.js +1 -4
  40. package/dist/components/forms/input/Input.module.css +8 -7
  41. package/dist/components/forms/input-container/InputContainer.module.css +1 -1
  42. package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
  43. package/dist/components/forms/select/Select.js +55 -16
  44. package/dist/components/hyperlink/Hyperlink.d.ts +19 -7
  45. package/dist/components/hyperlink/Hyperlink.js +35 -11
  46. package/dist/components/hyperlink/Hyperlink.module.css +50 -2
  47. package/dist/components/interval-select/IntervalSelect.d.ts +9 -2
  48. package/dist/components/interval-select/IntervalSelect.js +21 -6
  49. package/dist/components/menu/Menu.d.ts +29 -0
  50. package/dist/components/menu/Menu.js +61 -16
  51. package/dist/components/menu/Menu.module.css +73 -5
  52. package/dist/components/overlay/modal/Modal.module.css +4 -3
  53. package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
  54. package/dist/components/overlay/side-panel/SidePanel.js +18 -1
  55. package/dist/components/overlay/side-panel/SidePanel.module.css +1 -3
  56. package/dist/components/overlay/tooltip/useTooltipTrigger.js +4 -2
  57. package/dist/components/page-layout/PageLayout.d.ts +16 -4
  58. package/dist/components/page-layout/PageLayout.js +57 -28
  59. package/dist/components/page-layout/PageLayout.module.css +153 -33
  60. package/dist/components/popover/Popover.d.ts +17 -4
  61. package/dist/components/popover/Popover.js +147 -65
  62. package/dist/components/popover/Popover.module.css +5 -0
  63. package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +22 -18
  64. package/dist/components/sidebar/providers/SidebarProvider.d.ts +4 -1
  65. package/dist/components/sidebar/providers/SidebarProvider.js +66 -18
  66. package/dist/components/split-button/SplitButton.d.ts +1 -1
  67. package/dist/components/split-button/SplitButton.js +3 -1
  68. package/dist/components/split-button/SplitButton.module.css +4 -4
  69. package/dist/components/split-pane/SplitPane.d.ts +10 -24
  70. package/dist/components/split-pane/SplitPane.js +83 -54
  71. package/dist/components/split-pane/SplitPane.module.css +11 -6
  72. package/dist/components/split-pane/provider/SplitPaneContext.js +5 -11
  73. package/dist/components/state-page/StatePage.module.css +1 -1
  74. package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +3 -8
  75. package/dist/components/sticky-footer-layout/StickyFooterLayout.js +57 -20
  76. package/dist/components/table/Table.d.ts +8 -8
  77. package/dist/components/table/Table.js +37 -79
  78. package/dist/components/table/Table.module.css +62 -46
  79. package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +7 -3
  80. package/dist/components/table/TanstackTable.js +84 -0
  81. package/dist/components/table/components/column-resizer/ColumnResizer.js +1 -1
  82. package/dist/components/table/components/column-resizer/ColumnResizer.module.css +17 -7
  83. package/dist/components/table/components/table-settings/TableSettings.d.ts +13 -3
  84. package/dist/components/table/components/table-settings/TableSettings.js +55 -4
  85. package/dist/components/table/table.utils.d.ts +17 -0
  86. package/dist/components/table/table.utils.js +61 -0
  87. package/dist/components/table/tanstackTable.utils.d.ts +22 -0
  88. package/dist/components/table/tanstackTable.utils.js +104 -0
  89. package/dist/components/tabs/Tabs.d.ts +35 -12
  90. package/dist/components/tabs/Tabs.js +114 -26
  91. package/dist/components/tabs/Tabs.module.css +158 -71
  92. package/dist/hooks/useTableSettings.d.ts +23 -4
  93. package/dist/hooks/useTableSettings.js +64 -17
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.js +1 -1
  96. package/dist/src/styles/styles.css +38 -23
  97. package/dist/styles/animation.d.ts +5 -0
  98. package/dist/styles/animation.js +5 -0
  99. package/dist/styles/styles.css +38 -23
  100. package/dist/styles/themes/dbc/base.css +136 -0
  101. package/dist/styles/themes/dbc/dark.css +39 -202
  102. package/dist/styles/themes/dbc/light.css +17 -174
  103. package/dist/utils/localStorage.utils.d.ts +19 -0
  104. package/dist/utils/localStorage.utils.js +78 -0
  105. package/package.json +4 -4
  106. package/dist/components/table/tanstack.js +0 -162
@@ -1,22 +1,45 @@
1
- import React from 'react';
1
+ import type { JSX, ReactNode } from 'react';
2
+ export type TabId = string | number;
2
3
  export type TabItem = {
3
4
  header: string;
4
- id: string | number;
5
- headerIcon?: React.ReactNode;
6
- content: React.ReactNode;
5
+ id: TabId;
6
+ headerIcon?: ReactNode;
7
+ content: ReactNode;
7
8
  disabled?: boolean;
8
9
  hidden?: boolean;
9
10
  badge?: number;
10
11
  };
11
- interface TabsProps {
12
+ type TabsVariant = 'filled' | 'outlined';
13
+ export interface TabsProps {
12
14
  header?: string;
13
- variant: 'filled' | 'outlined';
15
+ variant: TabsVariant;
14
16
  panelStyle?: boolean;
15
- tabs: TabItem[];
16
- activeId?: number | string;
17
- onTabChange?: (index: number, tabItem: TabItem) => void;
18
- manuallySetActiveTab?: boolean;
19
- addition?: React.ReactNode;
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
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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
- export function Tabs({ variant, header, tabs, activeId, onTabChange, manuallySetActiveTab, addition, panelStyle, }) {
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
- const [currentId, setCurrentId] = useState(activeId);
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 (activeId !== undefined) {
12
- setCurrentId(activeId);
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
- }, [activeId]);
15
- const setActiveTab = useCallback((id) => {
16
- setCurrentId(id);
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
- onTabChange === null || onTabChange === void 0 ? void 0 : onTabChange(index, tab);
23
- }, [onTabChange, manuallySetActiveTab, setActiveTab]);
24
- const filteredTabs = useMemo(() => tabs.filter(tab => !tab.hidden), [tabs]);
25
- const activeIndex = useMemo(() => {
26
- var _a, _b;
27
- return (_a = filteredTabs.findIndex(tab => tab.id === currentId)) !== null && _a !== void 0 ? _a : (_b = filteredTabs[0]) === null || _b === void 0 ? void 0 : _b.id;
28
- }, [currentId, filteredTabs]);
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
- }, [activeIndex, filteredTabs, setActiveTab]);
34
- return (_jsxs("div", { className: "grow-container", children: [header && (_jsxs("div", { className: styles.headerContainer, children: [_jsx(Headline, { disableMargin: true, size: 2, children: header }), addition] })), _jsxs("div", { className: `${styles.tabs} ${styles[variant]} ${panelStyle ? styles.panelStyle : ''}`, children: [_jsx("div", { className: styles.tabList, children: filteredTabs.map((tab, index) => (_jsx("div", { "data-index": index, className: `${styles.tab} ${activeIndex === index ? styles.active : ''}`, children: _jsxs("button", { className: `${styles.tabButton}`, onClick: () => handleTabChange(index, tab), disabled: tab.disabled, children: [_jsx("span", { className: styles.icon, children: tab.headerIcon }), _jsx("span", { className: styles.header, children: tab.header }), tab.badge !== undefined && tab.badge > 0 && (_jsxs("span", { className: styles.badge, children: [' ', _jsx(Chip, { severity: "info", size: "sm", children: tab.badge.toLocaleString('da-DK') })] }))] }) }, index))) }), _jsx("div", { className: `${styles.tabContent} scrollable`, children: (_a = filteredTabs[activeIndex]) === null || _a === void 0 ? void 0 : _a.content })] })] }));
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
- .tabs {
1
+ .root {
2
2
  display: flex;
3
3
  flex-direction: column;
4
- flex-grow: 1;
5
- gap: var(--spacing-xl);
6
- overflow: hidden;
4
+ min-width: 0;
7
5
  }
8
6
 
9
- /* ========== Filled variant ========== */
10
- .tabs.filled .tab.active {
11
- background: var(--opac-bg-brand);
12
- color: var(--color-brand);
13
- border: var(--border-width-thin) solid var(--opac-bg-dark);
14
- border-block-end: 0;
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
- .tabs.filled .tabContent {
39
- border: var(--border-width-thin) solid var(--opac-bg-dark);
40
- background: var(--color-bg-surface);
41
- padding: var(--spacing-lg);
14
+
15
+ /* Outer wrapper for tablist + content */
16
+ .tabs {
17
+ display: flex;
18
+ flex-direction: column;
42
19
  flex-grow: 1;
43
- overflow: auto;
20
+ overflow: hidden;
21
+ min-height: 0;
44
22
  }
45
23
 
46
- /* ========== Panel style variant ========== */
47
- .panelStyle .tabList {
48
- border: var(--border-width-thin) solid var(--color-border-default);
49
- border-block-end: 0;
24
+ /* Tablist */
25
+ .tabList {
26
+ display: flex;
27
+ flex-wrap: wrap;
28
+ min-width: 0;
50
29
  }
51
- .panelStyle .tab.active {
52
- border: 0 !important;
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
- .panelStyle .tab {
55
- border: 0 !important;
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
- padding-block: calc(var(--spacing-sm) + var(--density));
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
- /* ========== Tablist and tabs ========== */
84
- .tabList {
85
- display: flex;
86
- flex-wrap: wrap;
75
+ .tabButton:disabled {
76
+ cursor: not-allowed;
77
+ color: var(--color-disabled-fg);
87
78
  }
88
79
 
89
- .tab {
90
- display: flex;
91
- flex-direction: column;
92
- border-radius: 0;
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
- .tab.active {
168
+
169
+ .outlined .tab.active {
170
+ color: var(--color-brand);
96
171
  border-block-end-color: var(--color-brand);
97
172
  }
98
173
 
99
- .tab .icon {
100
- display: contents;
101
- color: inherit;
174
+ .outlined .tabContent {
175
+ /* plain panel by default */
176
+ background: transparent;
177
+ padding: 0;
102
178
  }
103
- .tab .icon svg {
104
- inline-size: var(--icon-size-md);
105
- block-size: var(--icon-size-md);
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
- .headerContainer {
109
- display: flex;
110
- justify-content: space-between;
111
- align-items: center;
112
- padding-block: var(--spacing-lg);
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
- export type ViewMode = 'compact' | 'comfortable';
2
- interface TableSettings {
1
+ import { ColumnDef } from '@tanstack/react-table';
2
+ export type ViewMode = 'compact' | 'wrapped';
3
+ export type TableSettingsState = {
3
4
  viewMode: ViewMode;
4
- toggleViewMode: () => void;
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?: string): TableSettings;
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
- export function useTableSettings(storageKey) {
4
- const [viewMode, setViewMode] = useState('compact');
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
- if (typeof window !== 'undefined' && storageKey) {
7
- const storedMode = window.localStorage.getItem(`dbc-table-settings:${storageKey}`);
8
- if (storedMode === 'compact' || storedMode === 'comfortable') {
9
- setViewMode(storedMode);
10
- }
11
- }
12
- }, [storageKey]);
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
- if (typeof window !== 'undefined' && storageKey) {
15
- const newMode = viewMode === 'comfortable' ? 'compact' : 'comfortable';
16
- window.localStorage.setItem(`dbc-table-settings:${storageKey}`, newMode);
17
- }
18
- setViewMode(prevMode => (prevMode === 'comfortable' ? 'compact' : 'comfortable'));
19
- }, [storageKey, viewMode]);
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/tanstack';
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/tanstack';
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';