@dbcdk/react-components 0.0.12 → 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 +9 -18
- package/dist/components/card/Card.js +34 -23
- package/dist/components/card/Card.module.css +22 -87
- 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/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.module.css +5 -5
- 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 +0 -3
- package/dist/components/forms/input/Input.module.css +7 -7
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
- package/dist/components/forms/select/Select.js +55 -16
- 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 +11 -14
- package/dist/components/menu/Menu.js +18 -33
- package/dist/components/menu/Menu.module.css +2 -2
- package/dist/components/overlay/modal/Modal.module.css +2 -1
- package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
- package/dist/components/overlay/side-panel/SidePanel.js +1 -1
- package/dist/components/overlay/side-panel/SidePanel.module.css +1 -1
- 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/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/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 +3 -8
- package/dist/components/table/Table.js +37 -76
- package/dist/components/table/Table.module.css +45 -42
- package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +5 -12
- 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/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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/styles/styles.css +0 -1
- package/dist/styles/styles.css +0 -1
- 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/package.json +4 -4
- package/dist/components/table/tanstack.js +0 -214
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
.resizer {
|
|
2
|
-
display: inline-block;
|
|
3
|
-
width: 1px;
|
|
4
|
-
background-color: var(--opac-bg-default);
|
|
5
|
-
height: 60%;
|
|
6
2
|
position: absolute;
|
|
7
|
-
right:
|
|
8
|
-
top:
|
|
9
|
-
|
|
3
|
+
right: -6px; /* extend hit area into both columns */
|
|
4
|
+
top: 0;
|
|
5
|
+
height: 100%;
|
|
6
|
+
width: 12px; /* big invisible hit area */
|
|
10
7
|
cursor: col-resize;
|
|
11
8
|
user-select: none;
|
|
12
9
|
touch-action: none;
|
|
10
|
+
z-index: 20;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.resizer::after {
|
|
14
|
+
content: '';
|
|
15
|
+
position: absolute;
|
|
16
|
+
left: 50%;
|
|
17
|
+
transform: translateX(-50%);
|
|
18
|
+
width: 1px; /* visual thickness */
|
|
19
|
+
height: 60%;
|
|
20
|
+
top: 50%;
|
|
21
|
+
transform: translate(-50%, -50%);
|
|
22
|
+
background-color: var(--opac-bg-default);
|
|
13
23
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import type { ColumnItem } from './Table';
|
|
3
|
+
export type SortDirection = 'asc' | 'desc' | null;
|
|
4
|
+
export declare function getVisibleColumns<T>(columns: Array<ColumnItem<T>>): Array<ColumnItem<T>>;
|
|
5
|
+
export declare function getColumnStyle(columnId: string, columnStyles: Partial<Record<string, CSSProperties>> | undefined, alignment?: 'left' | 'right' | 'center', verticalAlignment?: 'top' | 'middle' | 'bottom'): CSSProperties;
|
|
6
|
+
export declare function getHeaderLabel(header: string | (() => ReactNode)): ReactNode;
|
|
7
|
+
export declare function isActiveSort(sortById: string | undefined, columnId: string): boolean;
|
|
8
|
+
export declare function getAriaSort(sortable: boolean | undefined, active: boolean, direction: SortDirection): 'ascending' | 'descending' | 'none';
|
|
9
|
+
export declare function getNextSortDirection(sortable: boolean | undefined, active: boolean, currentDirection: SortDirection): SortDirection;
|
|
10
|
+
export declare function shouldToggleOnKey(key: string): boolean;
|
|
11
|
+
export declare function isModifierClick(e: {
|
|
12
|
+
metaKey?: boolean;
|
|
13
|
+
ctrlKey?: boolean;
|
|
14
|
+
}): boolean;
|
|
15
|
+
export declare function shouldAllowWrap(columnAllowWrap: boolean | undefined, isRowSelected: boolean, viewMode?: 'wrapped' | string): boolean;
|
|
16
|
+
export declare function getCellDisplayValue<T extends Record<string, any>>(row: T, column: ColumnItem<T>): ReactNode;
|
|
17
|
+
export declare function getRowKey(rowId: string | number): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function getVisibleColumns(columns) {
|
|
2
|
+
return columns.filter(c => !c.hidden);
|
|
3
|
+
}
|
|
4
|
+
export function getColumnStyle(columnId, columnStyles, alignment, verticalAlignment) {
|
|
5
|
+
var _a;
|
|
6
|
+
const baseStyle = columnStyles === null || columnStyles === void 0 ? void 0 : columnStyles[columnId];
|
|
7
|
+
return {
|
|
8
|
+
...(baseStyle !== null && baseStyle !== void 0 ? baseStyle : {}),
|
|
9
|
+
...(alignment === 'right' ? { fontVariantNumeric: 'tabular-nums' } : null),
|
|
10
|
+
verticalAlign: verticalAlignment !== null && verticalAlignment !== void 0 ? verticalAlignment : 'top',
|
|
11
|
+
textAlign: alignment !== null && alignment !== void 0 ? alignment : 'left',
|
|
12
|
+
minWidth: (_a = baseStyle === null || baseStyle === void 0 ? void 0 : baseStyle.minWidth) !== null && _a !== void 0 ? _a : 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function getHeaderLabel(header) {
|
|
16
|
+
return typeof header === 'function' ? header() : header;
|
|
17
|
+
}
|
|
18
|
+
export function isActiveSort(sortById, columnId) {
|
|
19
|
+
return sortById === columnId;
|
|
20
|
+
}
|
|
21
|
+
export function getAriaSort(sortable, active, direction) {
|
|
22
|
+
if (!sortable)
|
|
23
|
+
return 'none';
|
|
24
|
+
if (!active)
|
|
25
|
+
return 'none';
|
|
26
|
+
return direction === 'asc' ? 'ascending' : 'descending';
|
|
27
|
+
}
|
|
28
|
+
export function getNextSortDirection(sortable, active, currentDirection) {
|
|
29
|
+
if (!sortable)
|
|
30
|
+
return null;
|
|
31
|
+
if (!active)
|
|
32
|
+
return 'asc';
|
|
33
|
+
if (currentDirection === 'asc')
|
|
34
|
+
return 'desc';
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
export function shouldToggleOnKey(key) {
|
|
38
|
+
return key === 'Enter' || key === ' ';
|
|
39
|
+
}
|
|
40
|
+
export function isModifierClick(e) {
|
|
41
|
+
return Boolean(e.metaKey || e.ctrlKey);
|
|
42
|
+
}
|
|
43
|
+
export function shouldAllowWrap(columnAllowWrap, isRowSelected, viewMode) {
|
|
44
|
+
return Boolean(columnAllowWrap || isRowSelected || viewMode === 'wrapped');
|
|
45
|
+
}
|
|
46
|
+
export function getCellDisplayValue(row, column) {
|
|
47
|
+
var _a;
|
|
48
|
+
const empty = (_a = column.emptyPlaceholder) !== null && _a !== void 0 ? _a : '';
|
|
49
|
+
if (column.render) {
|
|
50
|
+
const rendered = column.render(row);
|
|
51
|
+
return rendered || empty;
|
|
52
|
+
}
|
|
53
|
+
if (column.accessor) {
|
|
54
|
+
const value = row[column.accessor];
|
|
55
|
+
return value || empty;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
export function getRowKey(rowId) {
|
|
60
|
+
return `tableRow-${String(rowId)}`;
|
|
61
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ColumnDef, SortingState, VisibilityState, Column } from '@tanstack/react-table';
|
|
2
|
+
import type { CSSProperties } from 'react';
|
|
3
|
+
import type { ColumnItem } from './Table';
|
|
4
|
+
type AnyRecord = Record<string, any>;
|
|
5
|
+
export declare function getColumnId<T>(def: ColumnDef<T, any>, index: number): string;
|
|
6
|
+
export declare function buildColumnVisibilityFromVisibleIds<T>(defs: ReadonlyArray<ColumnDef<T, any>>, visibleColumnIds?: string[]): VisibilityState;
|
|
7
|
+
export declare function mapDefsToColumnItems<T extends AnyRecord>(defs: ReadonlyArray<ColumnDef<T, any>>, columnVisibility: VisibilityState): ColumnItem<T>[];
|
|
8
|
+
export declare function sortingEqual(a: SortingState, b: SortingState): boolean;
|
|
9
|
+
export declare function getSortPropsFromSorting(sorting: SortingState): {
|
|
10
|
+
sortById?: string;
|
|
11
|
+
sortDirection: 'asc' | 'desc' | null;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Builds `columnStyles` where TanStack is source of truth.
|
|
15
|
+
* Keep this util pure by passing in the leaf columns and the set of allowed IDs.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildColumnStyles(leafColumns: Array<Pick<Column<any, any>, 'id' | 'getSize' | 'columnDef'>>, allowedIds: Set<string>, defaults?: {
|
|
18
|
+
minWidth: number;
|
|
19
|
+
maxWidth: number;
|
|
20
|
+
}): Record<string, CSSProperties>;
|
|
21
|
+
export declare function columnItemsToIdSet<T>(items: Array<ColumnItem<T>>): Set<string>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export function getColumnId(def, index) {
|
|
2
|
+
const d = def;
|
|
3
|
+
if (d.id != null && String(d.id).length)
|
|
4
|
+
return String(d.id);
|
|
5
|
+
if (d.accessorKey != null && String(d.accessorKey).length)
|
|
6
|
+
return String(d.accessorKey);
|
|
7
|
+
return `col_${index}`;
|
|
8
|
+
}
|
|
9
|
+
export function buildColumnVisibilityFromVisibleIds(defs, visibleColumnIds) {
|
|
10
|
+
if (!(visibleColumnIds === null || visibleColumnIds === void 0 ? void 0 : visibleColumnIds.length))
|
|
11
|
+
return {};
|
|
12
|
+
const visible = new Set(visibleColumnIds);
|
|
13
|
+
const next = {};
|
|
14
|
+
defs.forEach((def, index) => {
|
|
15
|
+
const id = getColumnId(def, index);
|
|
16
|
+
next[id] = visible.has(id);
|
|
17
|
+
});
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
export function mapDefsToColumnItems(defs, columnVisibility) {
|
|
21
|
+
return defs.map((def, index) => {
|
|
22
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
23
|
+
const id = getColumnId(def, index);
|
|
24
|
+
const accessorKey = def.accessorKey;
|
|
25
|
+
const accessorFn = def.accessorFn;
|
|
26
|
+
const cell = def.cell;
|
|
27
|
+
let render;
|
|
28
|
+
if (typeof cell === 'function') {
|
|
29
|
+
render = (row) => cell({
|
|
30
|
+
row: { original: row },
|
|
31
|
+
getValue: () => accessorKey != null
|
|
32
|
+
? row[accessorKey]
|
|
33
|
+
: accessorFn
|
|
34
|
+
? accessorFn(row)
|
|
35
|
+
: undefined,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (accessorFn) {
|
|
39
|
+
render = (row) => accessorFn(row);
|
|
40
|
+
}
|
|
41
|
+
else if (accessorKey != null) {
|
|
42
|
+
render = (row) => row[accessorKey];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
render = () => null;
|
|
46
|
+
}
|
|
47
|
+
const isVisible = (_a = columnVisibility[id]) !== null && _a !== void 0 ? _a : true;
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
header: def.header,
|
|
51
|
+
accessor: accessorKey,
|
|
52
|
+
sortable: (_b = def.enableSorting) !== null && _b !== void 0 ? _b : !!accessorKey,
|
|
53
|
+
render,
|
|
54
|
+
hidden: !isVisible,
|
|
55
|
+
align: (_d = (_c = def.meta) === null || _c === void 0 ? void 0 : _c.align) !== null && _d !== void 0 ? _d : undefined,
|
|
56
|
+
verticalAlign: (_f = (_e = def.meta) === null || _e === void 0 ? void 0 : _e.verticalAlign) !== null && _f !== void 0 ? _f : undefined,
|
|
57
|
+
emptyPlaceholder: (_h = (_g = def.meta) === null || _g === void 0 ? void 0 : _g.emptyPlaceholder) !== null && _h !== void 0 ? _h : '-',
|
|
58
|
+
allowWrap: (_k = (_j = def.meta) === null || _j === void 0 ? void 0 : _j.allowWrap) !== null && _k !== void 0 ? _k : false,
|
|
59
|
+
severity: (_m = (_l = def.meta) === null || _l === void 0 ? void 0 : _l.severity) !== null && _m !== void 0 ? _m : undefined,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function sortingEqual(a, b) {
|
|
64
|
+
const A = a === null || a === void 0 ? void 0 : a[0];
|
|
65
|
+
const B = b === null || b === void 0 ? void 0 : b[0];
|
|
66
|
+
if (!A && !B)
|
|
67
|
+
return true;
|
|
68
|
+
if (!A || !B)
|
|
69
|
+
return false;
|
|
70
|
+
return A.id === B.id && A.desc === B.desc;
|
|
71
|
+
}
|
|
72
|
+
export function getSortPropsFromSorting(sorting) {
|
|
73
|
+
const s = sorting === null || sorting === void 0 ? void 0 : sorting[0];
|
|
74
|
+
return {
|
|
75
|
+
sortById: s === null || s === void 0 ? void 0 : s.id,
|
|
76
|
+
sortDirection: s ? (s.desc ? 'desc' : 'asc') : null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Builds `columnStyles` where TanStack is source of truth.
|
|
81
|
+
* Keep this util pure by passing in the leaf columns and the set of allowed IDs.
|
|
82
|
+
*/
|
|
83
|
+
export function buildColumnStyles(leafColumns, allowedIds, defaults = { minWidth: 80, maxWidth: 800 }) {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
const styles = {};
|
|
86
|
+
for (const c of leafColumns) {
|
|
87
|
+
const id = c.id;
|
|
88
|
+
if (!allowedIds.has(id))
|
|
89
|
+
continue;
|
|
90
|
+
const anyDef = c.columnDef;
|
|
91
|
+
styles[id] = {
|
|
92
|
+
width: c.getSize(),
|
|
93
|
+
minWidth: (_a = anyDef.minSize) !== null && _a !== void 0 ? _a : defaults.minWidth,
|
|
94
|
+
maxWidth: (_b = anyDef.maxSize) !== null && _b !== void 0 ? _b : defaults.maxWidth,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return styles;
|
|
98
|
+
}
|
|
99
|
+
export function columnItemsToIdSet(items) {
|
|
100
|
+
const s = new Set();
|
|
101
|
+
for (const c of items)
|
|
102
|
+
s.add(c.id);
|
|
103
|
+
return s;
|
|
104
|
+
}
|
|
@@ -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;
|