@dbcdk/react-components 0.0.25 → 0.0.27
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/__stories__/_data/table.d.ts +1 -1
- package/dist/components/__stories__/_data/table.js +11 -5
- package/dist/components/accordion/Accordion.module.css +3 -3
- package/dist/components/accordion/components/AccordionRow.module.css +5 -1
- package/dist/components/forms/checkbox/Checkbox.js +1 -1
- package/dist/components/forms/checkbox/Checkbox.module.css +16 -3
- package/dist/components/forms/select/Select.js +1 -1
- package/dist/components/headline/Headline.module.css +15 -44
- package/dist/components/menu/Menu.js +1 -1
- package/dist/components/nav-bar/NavBar.module.css +1 -1
- package/dist/components/panel/Panel.module.css +2 -2
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +0 -1
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +1 -0
- package/dist/components/table/Table.d.ts +3 -65
- package/dist/components/table/Table.js +20 -159
- package/dist/components/table/Table.module.css +39 -6
- package/dist/components/table/Table.types.d.ts +58 -0
- package/dist/components/table/Table.types.js +1 -0
- package/dist/components/table/TanstackTable.d.ts +1 -1
- package/dist/components/table/TanstackTable.js +0 -1
- package/dist/components/table/components/TableBody.d.ts +19 -0
- package/dist/components/table/components/TableBody.js +10 -0
- package/dist/components/table/components/TableCell.d.ts +9 -0
- package/dist/components/table/components/TableCell.js +7 -0
- package/dist/components/table/components/TableHeader.d.ts +16 -0
- package/dist/components/table/components/TableHeader.js +7 -0
- package/dist/components/table/components/TableHeaderCell.d.ts +12 -0
- package/dist/components/table/components/TableHeaderCell.js +24 -0
- package/dist/components/table/components/TableLoadingBody.d.ts +10 -0
- package/dist/components/table/components/TableLoadingBody.js +10 -0
- package/dist/components/table/components/TablePagination.d.ts +0 -0
- package/dist/components/table/components/TablePagination.js +1 -0
- package/dist/components/table/components/TableRow.d.ts +19 -0
- package/dist/components/table/components/TableRow.js +38 -0
- package/dist/components/table/components/TableSelectionCell.d.ts +9 -0
- package/dist/components/table/components/TableSelectionCell.js +15 -0
- package/dist/components/table/hooks/useTableRowInteractions.d.ts +15 -0
- package/dist/components/table/hooks/useTableRowInteractions.js +25 -0
- package/dist/components/table/table.classes.d.ts +10 -0
- package/dist/components/table/table.classes.js +23 -0
- package/dist/components/table/table.utils.d.ts +7 -4
- package/dist/components/table/table.utils.js +14 -20
- package/dist/components/table/tanstackTable.utils.d.ts +1 -2
- package/dist/components/table/tanstackTable.utils.js +3 -2
- package/dist/components/tabs/Tabs.js +1 -1
- package/dist/utils/date/formatDate.d.ts +11 -3
- package/dist/utils/date/formatDate.js +35 -10
- package/package.json +1 -1
- package/dist/components/table/hooks/useAnimatedRowIds.d.ts +0 -9
- package/dist/components/table/hooks/useAnimatedRowIds.js +0 -76
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import type { PageChangeEvent } from '../../components/pagination/Pagination';
|
|
3
|
+
import type { Severity } from '../../constants/severity.types';
|
|
4
|
+
import type { ViewMode } from '../../hooks/useTableSettings';
|
|
5
|
+
export interface ColumnItem<T> {
|
|
6
|
+
id: string;
|
|
7
|
+
header: string | (() => ReactNode);
|
|
8
|
+
accessor?: keyof T;
|
|
9
|
+
sortable?: boolean;
|
|
10
|
+
sortFunction?: (a: T, b: T) => -1 | 0 | 1;
|
|
11
|
+
render?: (item: T) => ReactNode;
|
|
12
|
+
hidden?: boolean;
|
|
13
|
+
align?: 'left' | 'right' | 'center';
|
|
14
|
+
verticalAlign?: 'top' | 'middle' | 'bottom';
|
|
15
|
+
divider?: 'right' | 'left';
|
|
16
|
+
allowWrap?: boolean;
|
|
17
|
+
emptyPlaceholder?: ReactNode;
|
|
18
|
+
canHide?: boolean;
|
|
19
|
+
severity?: any;
|
|
20
|
+
}
|
|
21
|
+
export type HeaderExtrasArgs<T> = {
|
|
22
|
+
column: ColumnItem<T>;
|
|
23
|
+
index: number;
|
|
24
|
+
};
|
|
25
|
+
export type TableVariant = 'primary' | 'embedded';
|
|
26
|
+
export type SortDirection = 'asc' | 'desc' | null;
|
|
27
|
+
export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'onMouseEnter'> & {
|
|
28
|
+
data: T[];
|
|
29
|
+
dataKey: keyof T;
|
|
30
|
+
columns: ColumnItem<T>[];
|
|
31
|
+
selectedRows?: Set<number | string>;
|
|
32
|
+
selectionMode?: 'single' | 'multiple';
|
|
33
|
+
allRowsSelected?: boolean;
|
|
34
|
+
sortById?: string;
|
|
35
|
+
sortDirection?: SortDirection;
|
|
36
|
+
loading?: boolean;
|
|
37
|
+
emptyConfig?: any;
|
|
38
|
+
variant?: TableVariant;
|
|
39
|
+
size?: 'sm' | 'md';
|
|
40
|
+
viewMode?: ViewMode;
|
|
41
|
+
striped?: boolean;
|
|
42
|
+
fillViewport?: boolean;
|
|
43
|
+
gridTemplateColumns?: string;
|
|
44
|
+
toolbar?: ReactNode;
|
|
45
|
+
headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
|
|
46
|
+
take?: number;
|
|
47
|
+
skip?: number;
|
|
48
|
+
totalItemsCount?: number;
|
|
49
|
+
paginationPlacement?: 'top' | 'bottom';
|
|
50
|
+
showFirstLast?: boolean;
|
|
51
|
+
getRowSeverity?: (row: T) => Severity | undefined;
|
|
52
|
+
onRowClick?: (row: T) => void;
|
|
53
|
+
onRowMouseEnter?: (row: T) => void;
|
|
54
|
+
onRowSelect?: (rowId: number | string, isSelected: boolean) => void;
|
|
55
|
+
onSelectAllRows?: (isSelected: boolean) => void;
|
|
56
|
+
onSortChange?: (column: ColumnItem<T>, direction: SortDirection) => void;
|
|
57
|
+
onPageChange?: (e: PageChangeEvent) => void;
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ColumnDef, type SortingState } from '@tanstack/react-table';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { TableProps, TableVariant } from './Table.types';
|
|
4
4
|
import { ViewMode } from '../../hooks/useTableSettings';
|
|
5
5
|
type Filterable<T> = Array<keyof T>;
|
|
6
6
|
export type TanstackTableProps<T extends Record<string, any>> = Omit<TableProps<T>, 'columns' | 'onSortChange' | 'sortById' | 'sortDirection' | 'headerExtras' | 'gridTemplateColumns' | 'toolbar'> & {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import type { ViewMode } from '../../../hooks/useTableSettings';
|
|
3
|
+
import type { ColumnItem } from '../Table.types';
|
|
4
|
+
type Props<T extends Record<string, any>> = {
|
|
5
|
+
data: T[];
|
|
6
|
+
dataKey: keyof T;
|
|
7
|
+
columns: ColumnItem<T>[];
|
|
8
|
+
gridStyle: CSSProperties;
|
|
9
|
+
striped?: boolean;
|
|
10
|
+
selectedRows?: Set<number | string>;
|
|
11
|
+
hasSelection: boolean;
|
|
12
|
+
viewMode?: ViewMode;
|
|
13
|
+
getRowSeverity?: (row: T) => any;
|
|
14
|
+
onRowClick?: (row: T) => void;
|
|
15
|
+
onRowMouseEnter?: (row: T) => void;
|
|
16
|
+
onRowSelect?: (rowId: number | string, isSelected: boolean) => void;
|
|
17
|
+
};
|
|
18
|
+
export declare function TableBody<T extends Record<string, any>>({ data, dataKey, columns, gridStyle, striped, selectedRows, hasSelection, viewMode, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, }: Props<T>): ReactNode;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../table.classes';
|
|
3
|
+
import styles from '../Table.module.css';
|
|
4
|
+
import { TableRow } from './TableRow';
|
|
5
|
+
export function TableBody({ data, dataKey, columns, gridStyle, striped, selectedRows, hasSelection, viewMode, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, }) {
|
|
6
|
+
return (_jsx("div", { className: cx(styles.body, striped && styles.striped), role: "rowgroup", children: data.map(row => {
|
|
7
|
+
const rowId = row[dataKey];
|
|
8
|
+
return (_jsx(TableRow, { row: row, rowId: rowId, columns: columns, gridStyle: gridStyle, selectedRows: selectedRows, hasSelection: hasSelection, viewMode: viewMode, getRowSeverity: getRowSeverity, onRowClick: onRowClick, onRowMouseEnter: onRowMouseEnter, onRowSelect: onRowSelect }, `gridRow-${rowId}`));
|
|
9
|
+
}) }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
align?: 'left' | 'right' | 'center';
|
|
4
|
+
divider?: 'left' | 'right';
|
|
5
|
+
allowWrap?: boolean;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare function TableCell({ align, divider, allowWrap, children }: Props): ReactNode;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../table.classes';
|
|
3
|
+
import styles from '../Table.module.css';
|
|
4
|
+
export function TableCell({ align = 'left', divider, allowWrap, children }) {
|
|
5
|
+
const dividerClass = divider === 'left' ? styles.dividerLeft : divider === 'right' ? styles.dividerRight : '';
|
|
6
|
+
return (_jsx("div", { className: cx(styles.cell, allowWrap ? styles.allowWrap : styles.nowrap, dividerClass), role: "cell", "data-align": align, "data-divider": divider, children: _jsx("div", { className: styles.cellContent, children: allowWrap ? children : _jsx("div", { className: styles.cellValueEllipsis, children: children }) }) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React, { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import type { ColumnItem, HeaderExtrasArgs, SortDirection } from '../Table.types';
|
|
3
|
+
type Props<T> = {
|
|
4
|
+
columns: ColumnItem<T>[];
|
|
5
|
+
gridStyle: CSSProperties;
|
|
6
|
+
hasSelection: boolean;
|
|
7
|
+
selectionMode: 'single' | 'multiple';
|
|
8
|
+
allRowsSelected?: boolean;
|
|
9
|
+
onSelectAllRows?: (isSelected: boolean) => void;
|
|
10
|
+
sortById?: string;
|
|
11
|
+
sortDirection?: SortDirection;
|
|
12
|
+
onSortChange?: (column: ColumnItem<T>, direction: SortDirection) => void;
|
|
13
|
+
headerExtras?: (args: HeaderExtrasArgs<T>) => ReactNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function TableHeader<T>({ columns, gridStyle, hasSelection, selectionMode, allRowsSelected, onSelectAllRows, sortById, sortDirection, onSortChange, headerExtras, }: Props<T>): React.ReactNode;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TableHeaderCell } from './TableHeaderCell';
|
|
3
|
+
import { TableSelectionCell } from './TableSelectionCell';
|
|
4
|
+
import styles from '../Table.module.css';
|
|
5
|
+
export function TableHeader({ columns, gridStyle, hasSelection, selectionMode, allRowsSelected, onSelectAllRows, sortById, sortDirection, onSortChange, headerExtras, }) {
|
|
6
|
+
return (_jsxs("div", { className: styles.headerRow, style: gridStyle, role: "row", children: [hasSelection ? (_jsx(TableSelectionCell, { isHeader: true, multiple: selectionMode === 'multiple', checked: allRowsSelected, onToggle: checked => onSelectAllRows === null || onSelectAllRows === void 0 ? void 0 : onSelectAllRows(checked) })) : null, columns.map((column, index) => (_jsx(TableHeaderCell, { column: column, index: index, sortById: sortById, sortDirection: sortDirection, onSortChange: onSortChange, extraContent: headerExtras === null || headerExtras === void 0 ? void 0 : headerExtras({ column, index }) }, column.id)))] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ColumnItem, SortDirection } from '../Table.types';
|
|
3
|
+
type Props<T> = {
|
|
4
|
+
column: ColumnItem<T>;
|
|
5
|
+
index: number;
|
|
6
|
+
sortById?: string;
|
|
7
|
+
sortDirection?: SortDirection;
|
|
8
|
+
onSortChange?: (column: ColumnItem<T>, direction: SortDirection) => void;
|
|
9
|
+
extraContent?: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export declare function TableHeaderCell<T>({ column, index, sortById, sortDirection, onSortChange, extraContent, }: Props<T>): ReactNode;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowDown, ArrowUp } from 'lucide-react';
|
|
3
|
+
import { cx, getAlignValue, getDividerClass, getHeaderAlignClasses } from '../table.classes';
|
|
4
|
+
import styles from '../Table.module.css';
|
|
5
|
+
import { getAriaSort, getHeaderLabel, getNextSortDirection, isActiveSort, shouldToggleOnKey, } from '../table.utils';
|
|
6
|
+
export function TableHeaderCell({ column, index, sortById, sortDirection, onSortChange, extraContent, }) {
|
|
7
|
+
const active = isActiveSort(sortById, column.id);
|
|
8
|
+
const ariaSort = getAriaSort(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
|
|
9
|
+
const align = getAlignValue(column);
|
|
10
|
+
const dividerClass = getDividerClass(column);
|
|
11
|
+
const alignClasses = getHeaderAlignClasses(align);
|
|
12
|
+
const handleToggleSort = () => {
|
|
13
|
+
if (!column.sortable || !onSortChange)
|
|
14
|
+
return;
|
|
15
|
+
const nextDirection = getNextSortDirection(column.sortable, active, sortDirection !== null && sortDirection !== void 0 ? sortDirection : null);
|
|
16
|
+
onSortChange(column, nextDirection);
|
|
17
|
+
};
|
|
18
|
+
return (_jsxs("div", { className: cx(styles.headerCell, dividerClass), role: "columnheader", "aria-sort": ariaSort, "data-align": align, "data-divider": column.divider, "data-column-index": index, children: [_jsx("div", { className: alignClasses.inner, children: _jsx("div", { className: alignClasses.main, children: column.sortable ? (_jsxs("button", { type: "button", className: alignClasses.button, onClick: handleToggleSort, onKeyDown: e => {
|
|
19
|
+
if (!shouldToggleOnKey(e.key))
|
|
20
|
+
return;
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
handleToggleSort();
|
|
23
|
+
}, children: [_jsx("span", { className: alignClasses.label, 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: alignClasses.label, children: getHeaderLabel(column.header) })) }) }), extraContent != null ? _jsx("div", { className: styles.thOverlayExtras, children: extraContent }) : null] }, column.id));
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import type { ColumnItem } from '../Table.types';
|
|
3
|
+
type Props<T> = {
|
|
4
|
+
rows: number;
|
|
5
|
+
columns: ColumnItem<T>[];
|
|
6
|
+
hasSelection: boolean;
|
|
7
|
+
gridStyle: CSSProperties;
|
|
8
|
+
};
|
|
9
|
+
export declare function TableLoadingBody<T>({ rows, columns, hasSelection, gridStyle, }: Props<T>): ReactNode;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SkeletonLoaderItem } from '../../../components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
|
|
3
|
+
import { cx } from '../table.classes';
|
|
4
|
+
import styles from '../Table.module.css';
|
|
5
|
+
export function TableLoadingBody({ rows, columns, hasSelection, gridStyle, }) {
|
|
6
|
+
return (_jsx("div", { className: styles.body, role: "rowgroup", children: Array.from({ length: rows }).map((_, rowIndex) => (_jsxs("div", { className: styles.row, style: gridStyle, role: "row", children: [hasSelection ? (_jsx("div", { className: cx(styles.cell, styles.selectionCell), role: "cell" })) : null, columns.map(column => {
|
|
7
|
+
var _a;
|
|
8
|
+
return (_jsx("div", { className: cx(styles.cell, column.divider === 'left' && styles.dividerLeft, column.divider === 'right' && styles.dividerRight), role: "cell", "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, children: _jsx("div", { className: styles.cellValueEllipsis, children: _jsx(SkeletonLoaderItem, { height: 20, width: "100%" }) }) }) }, column.id));
|
|
9
|
+
})] }, `loading-row-${rowIndex}`))) }));
|
|
10
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { ViewMode } from '../../../hooks/useTableSettings';
|
|
4
|
+
import type { ColumnItem } from '../Table.types';
|
|
5
|
+
type Props<T extends Record<string, any>> = {
|
|
6
|
+
row: T;
|
|
7
|
+
rowId: string | number;
|
|
8
|
+
columns: ColumnItem<T>[];
|
|
9
|
+
gridStyle: CSSProperties;
|
|
10
|
+
selectedRows?: Set<number | string>;
|
|
11
|
+
hasSelection: boolean;
|
|
12
|
+
viewMode?: ViewMode;
|
|
13
|
+
getRowSeverity?: (row: T) => any;
|
|
14
|
+
onRowClick?: (row: T) => void;
|
|
15
|
+
onRowMouseEnter?: (row: T) => void;
|
|
16
|
+
onRowSelect?: (rowId: number | string, isSelected: boolean) => void;
|
|
17
|
+
};
|
|
18
|
+
export declare function TableRow<T extends Record<string, any>>({ row, rowId, columns, gridStyle, selectedRows, hasSelection, viewMode, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, }: Props<T>): React.ReactNode;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Checkbox } from '../../../components/forms/checkbox/Checkbox';
|
|
3
|
+
import { SeverityBgColor } from '../../../constants/severity';
|
|
4
|
+
import { useTableRowInteractions } from '../hooks/useTableRowInteractions';
|
|
5
|
+
import { cx } from '../table.classes';
|
|
6
|
+
import styles from '../Table.module.css';
|
|
7
|
+
import { getCellDisplayValue, shouldAllowWrap } from '../table.utils';
|
|
8
|
+
export function TableRow({ row, rowId, columns, gridStyle, selectedRows, hasSelection, viewMode, getRowSeverity, onRowClick, onRowMouseEnter, onRowSelect, }) {
|
|
9
|
+
var _a;
|
|
10
|
+
const isSelected = (_a = selectedRows === null || selectedRows === void 0 ? void 0 : selectedRows.has(rowId)) !== null && _a !== void 0 ? _a : false;
|
|
11
|
+
const rowSeverity = getRowSeverity === null || getRowSeverity === void 0 ? void 0 : getRowSeverity(row);
|
|
12
|
+
const canSelect = Boolean(selectedRows && onRowSelect);
|
|
13
|
+
const { handleRowClick, handleRowKeyDown } = useTableRowInteractions({
|
|
14
|
+
row,
|
|
15
|
+
rowId,
|
|
16
|
+
isSelected,
|
|
17
|
+
canSelect,
|
|
18
|
+
onRowClick,
|
|
19
|
+
onRowSelect,
|
|
20
|
+
});
|
|
21
|
+
return (_jsxs("div", { className: cx(styles.row, onRowClick && styles.clickableRow, isSelected && styles.selectedRow, rowSeverity && styles.severity), style: {
|
|
22
|
+
...gridStyle,
|
|
23
|
+
['--row-severity-color']: rowSeverity
|
|
24
|
+
? SeverityBgColor[rowSeverity]
|
|
25
|
+
: undefined,
|
|
26
|
+
}, role: "row", tabIndex: onRowClick ? 0 : -1, onKeyDown: handleRowKeyDown, onMouseEnter: () => onRowMouseEnter === null || onRowMouseEnter === void 0 ? void 0 : onRowMouseEnter(row), onClick: handleRowClick, children: [hasSelection ? (_jsx("div", { className: cx(styles.cell, styles.selectionCell), role: "cell", onClick: e => {
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, !isSelected);
|
|
29
|
+
}, children: _jsx("div", { className: styles.selectionHitArea, children: _jsx(Checkbox, { variant: "primary", checked: isSelected, size: "sm", onChange: (checked, e) => {
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, checked);
|
|
32
|
+
} }) }) })) : null, columns.map(column => {
|
|
33
|
+
var _a;
|
|
34
|
+
const allowWrap = shouldAllowWrap(column.allowWrap, isSelected, viewMode);
|
|
35
|
+
const cellValue = getCellDisplayValue(row, column);
|
|
36
|
+
return (_jsx("div", { className: cx(styles.cell, allowWrap ? styles.allowWrap : styles.nowrap, column.divider === 'left' && styles.dividerLeft, column.divider === 'right' && styles.dividerRight), role: "cell", "data-align": (_a = column.align) !== null && _a !== void 0 ? _a : 'left', "data-divider": column.divider, children: _jsx("div", { className: styles.cellContent, children: allowWrap ? cellValue : _jsx("div", { className: styles.cellValueEllipsis, children: cellValue }) }) }, column.id));
|
|
37
|
+
})] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { type JSX } from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
checked?: boolean;
|
|
4
|
+
isHeader?: boolean;
|
|
5
|
+
multiple?: boolean;
|
|
6
|
+
onToggle: (checked: boolean, e: React.MouseEvent | React.KeyboardEvent | any) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function TableSelectionCell({ checked, isHeader, multiple, onToggle }: Props): JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Checkbox } from '../../../components/forms/checkbox/Checkbox';
|
|
3
|
+
import { cx } from '../table.classes';
|
|
4
|
+
import styles from '../Table.module.css';
|
|
5
|
+
export function TableSelectionCell({ checked, isHeader, multiple, onToggle }) {
|
|
6
|
+
return (_jsx("div", { className: cx(styles.headerCell, styles.selectionCell), role: isHeader ? 'columnheader' : 'cell', onClick: e => {
|
|
7
|
+
if (isHeader && !multiple)
|
|
8
|
+
return;
|
|
9
|
+
e.stopPropagation();
|
|
10
|
+
onToggle(!checked, e);
|
|
11
|
+
}, children: isHeader && !multiple ? null : (_jsx("div", { className: styles.selectionHitArea, children: _jsx(Checkbox, { size: "sm", variant: "primary", checked: checked, onChange: (nextChecked, e) => {
|
|
12
|
+
e.stopPropagation();
|
|
13
|
+
onToggle(nextChecked, e);
|
|
14
|
+
} }) })) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { KeyboardEvent, MouseEvent } from 'react';
|
|
2
|
+
type UseTableRowInteractionsArgs<T> = {
|
|
3
|
+
row: T;
|
|
4
|
+
rowId: string | number;
|
|
5
|
+
isSelected: boolean;
|
|
6
|
+
canSelect: boolean;
|
|
7
|
+
onRowClick?: (row: T) => void;
|
|
8
|
+
onRowSelect?: (rowId: string | number, isSelected: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
type UseTableRowInteractionsReturn = {
|
|
11
|
+
handleRowClick: (e: MouseEvent<HTMLDivElement>) => void;
|
|
12
|
+
handleRowKeyDown: (e: KeyboardEvent<HTMLDivElement>) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function useTableRowInteractions<T>({ row, rowId, isSelected, canSelect, onRowClick, onRowSelect, }: UseTableRowInteractionsArgs<T>): UseTableRowInteractionsReturn;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { isModifierClick, shouldToggleOnKey } from '../table.utils';
|
|
3
|
+
export function useTableRowInteractions({ row, rowId, isSelected, canSelect, onRowClick, onRowSelect, }) {
|
|
4
|
+
const handleRowClick = useCallback((e) => {
|
|
5
|
+
if (isModifierClick(e) && canSelect) {
|
|
6
|
+
e.preventDefault();
|
|
7
|
+
e.stopPropagation();
|
|
8
|
+
onRowSelect === null || onRowSelect === void 0 ? void 0 : onRowSelect(rowId, !isSelected);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row);
|
|
12
|
+
}, [canSelect, isSelected, onRowClick, onRowSelect, row, rowId]);
|
|
13
|
+
const handleRowKeyDown = useCallback((e) => {
|
|
14
|
+
if (!onRowClick)
|
|
15
|
+
return;
|
|
16
|
+
if (!shouldToggleOnKey(e.key))
|
|
17
|
+
return;
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
onRowClick(row);
|
|
20
|
+
}, [onRowClick, row]);
|
|
21
|
+
return {
|
|
22
|
+
handleRowClick,
|
|
23
|
+
handleRowKeyDown,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ColumnItem } from './Table.types';
|
|
2
|
+
export declare function cx(...values: Array<string | false | null | undefined>): string;
|
|
3
|
+
export declare function getDividerClass<T>(column: ColumnItem<T>): string;
|
|
4
|
+
export declare function getAlignValue<T>(column: ColumnItem<T>): 'left' | 'right' | 'center';
|
|
5
|
+
export declare function getHeaderAlignClasses(align: 'left' | 'right' | 'center'): {
|
|
6
|
+
inner: string;
|
|
7
|
+
main: string;
|
|
8
|
+
button: string;
|
|
9
|
+
label: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import styles from './Table.module.css';
|
|
2
|
+
export function cx(...values) {
|
|
3
|
+
return values.filter(Boolean).join(' ');
|
|
4
|
+
}
|
|
5
|
+
export function getDividerClass(column) {
|
|
6
|
+
if (column.divider === 'left')
|
|
7
|
+
return styles.dividerLeft;
|
|
8
|
+
if (column.divider === 'right')
|
|
9
|
+
return styles.dividerRight;
|
|
10
|
+
return '';
|
|
11
|
+
}
|
|
12
|
+
export function getAlignValue(column) {
|
|
13
|
+
var _a;
|
|
14
|
+
return (_a = column.align) !== null && _a !== void 0 ? _a : 'left';
|
|
15
|
+
}
|
|
16
|
+
export function getHeaderAlignClasses(align) {
|
|
17
|
+
return {
|
|
18
|
+
inner: cx(styles.thInner, align === 'right' && styles.thInnerRight, align === 'center' && styles.thInnerCenter),
|
|
19
|
+
main: cx(styles.thMain, align === 'right' && styles.thMainRight, align === 'center' && styles.thMainCenter),
|
|
20
|
+
button: cx(styles.thButton, align === 'right' && styles.thButtonRight, align === 'center' && styles.thButtonCenter),
|
|
21
|
+
label: cx(styles.thLabel, align === 'right' && styles.thLabelRight, align === 'center' && styles.thLabelCenter),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { ColumnItem } from './Table';
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ColumnItem } from './Table.types';
|
|
3
|
+
export declare const SELECTION_COLUMN_PX: 40;
|
|
3
4
|
export type SortDirection = 'asc' | 'desc' | null;
|
|
4
5
|
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', divider?: boolean): CSSProperties;
|
|
6
6
|
export declare function getHeaderLabel(header: string | (() => ReactNode)): ReactNode;
|
|
7
7
|
export declare function isActiveSort(sortById: string | undefined, columnId: string): boolean;
|
|
8
8
|
export declare function getAriaSort(sortable: boolean | undefined, active: boolean, direction: SortDirection): 'ascending' | 'descending' | 'none';
|
|
@@ -14,4 +14,7 @@ export declare function isModifierClick(e: {
|
|
|
14
14
|
}): boolean;
|
|
15
15
|
export declare function shouldAllowWrap(columnAllowWrap: boolean | undefined, isRowSelected: boolean, viewMode?: 'wrapped' | string): boolean;
|
|
16
16
|
export declare function getCellDisplayValue<T extends Record<string, any>>(row: T, column: ColumnItem<T>): ReactNode;
|
|
17
|
-
export declare function
|
|
17
|
+
export declare function buildDefaultGridTemplate(args: {
|
|
18
|
+
hasSelection: boolean;
|
|
19
|
+
colCount: number;
|
|
20
|
+
}): string;
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
+
export const SELECTION_COLUMN_PX = 40;
|
|
1
2
|
export function getVisibleColumns(columns) {
|
|
2
|
-
return columns.filter(
|
|
3
|
-
}
|
|
4
|
-
export function getColumnStyle(columnId, columnStyles, alignment, verticalAlignment, divider) {
|
|
5
|
-
const baseStyle = columnStyles === null || columnStyles === void 0 ? void 0 : columnStyles[columnId];
|
|
6
|
-
return {
|
|
7
|
-
...(baseStyle !== null && baseStyle !== void 0 ? baseStyle : {}),
|
|
8
|
-
...(alignment === 'right' ? { fontVariantNumeric: 'tabular-nums' } : null),
|
|
9
|
-
verticalAlign: verticalAlignment !== null && verticalAlignment !== void 0 ? verticalAlignment : 'top',
|
|
10
|
-
textAlign: alignment !== null && alignment !== void 0 ? alignment : 'left',
|
|
11
|
-
...(divider ? { borderLeft: '1px solid var(--color-border-subtle)' } : null),
|
|
12
|
-
};
|
|
3
|
+
return columns.filter(column => !column.hidden);
|
|
13
4
|
}
|
|
14
5
|
export function getHeaderLabel(header) {
|
|
15
6
|
return typeof header === 'function' ? header() : header;
|
|
@@ -18,9 +9,7 @@ export function isActiveSort(sortById, columnId) {
|
|
|
18
9
|
return sortById === columnId;
|
|
19
10
|
}
|
|
20
11
|
export function getAriaSort(sortable, active, direction) {
|
|
21
|
-
if (!sortable)
|
|
22
|
-
return 'none';
|
|
23
|
-
if (!active)
|
|
12
|
+
if (!sortable || !active || !direction)
|
|
24
13
|
return 'none';
|
|
25
14
|
return direction === 'asc' ? 'ascending' : 'descending';
|
|
26
15
|
}
|
|
@@ -47,14 +36,19 @@ export function getCellDisplayValue(row, column) {
|
|
|
47
36
|
const empty = (_a = column.emptyPlaceholder) !== null && _a !== void 0 ? _a : '';
|
|
48
37
|
if (column.render) {
|
|
49
38
|
const rendered = column.render(row);
|
|
50
|
-
return rendered
|
|
39
|
+
return rendered !== null && rendered !== void 0 ? rendered : empty;
|
|
51
40
|
}
|
|
52
41
|
if (column.accessor) {
|
|
53
42
|
const value = row[column.accessor];
|
|
54
|
-
return value
|
|
43
|
+
return value !== null && value !== void 0 ? value : empty;
|
|
55
44
|
}
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
export function
|
|
59
|
-
|
|
45
|
+
return empty;
|
|
46
|
+
}
|
|
47
|
+
export function buildDefaultGridTemplate(args) {
|
|
48
|
+
const parts = [];
|
|
49
|
+
if (args.hasSelection)
|
|
50
|
+
parts.push(`${SELECTION_COLUMN_PX}px`);
|
|
51
|
+
for (let i = 0; i < args.colCount; i++)
|
|
52
|
+
parts.push('minmax(120px, 1fr)');
|
|
53
|
+
return parts.join(' ');
|
|
60
54
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ColumnDef, ColumnSizingState, SortingState, VisibilityState } from '@tanstack/react-table';
|
|
2
|
-
import
|
|
2
|
+
import { ColumnItem } from './Table.types';
|
|
3
3
|
type AnyRecord = Record<string, any>;
|
|
4
4
|
export declare function getColumnId<T>(def: ColumnDef<T, any>, index: number): string;
|
|
5
5
|
export declare function buildColumnVisibilityFromVisibleIds<T>(defs: ReadonlyArray<ColumnDef<T, any>>, visibleColumnIds?: string[]): VisibilityState;
|
|
@@ -14,7 +14,6 @@ export declare function buildDistributedGridTemplateColumns(args: {
|
|
|
14
14
|
table: any;
|
|
15
15
|
allowedIds: Set<string>;
|
|
16
16
|
hasSelection: boolean;
|
|
17
|
-
selectionPx: number;
|
|
18
17
|
defaultMinPx: number;
|
|
19
18
|
columnSizing: ColumnSizingState;
|
|
20
19
|
}): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SELECTION_COLUMN_PX } from './table.utils';
|
|
1
2
|
export function getColumnId(def, index) {
|
|
2
3
|
const d = def;
|
|
3
4
|
if (d.id != null && String(d.id).length > 0)
|
|
@@ -92,11 +93,11 @@ function clamp(value, min, max) {
|
|
|
92
93
|
}
|
|
93
94
|
export function buildDistributedGridTemplateColumns(args) {
|
|
94
95
|
var _a, _b, _c, _d;
|
|
95
|
-
const { table, allowedIds, hasSelection,
|
|
96
|
+
const { table, allowedIds, hasSelection, defaultMinPx, columnSizing } = args;
|
|
96
97
|
const parts = [];
|
|
97
98
|
const leaf = table.getVisibleLeafColumns().filter((c) => allowedIds.has(c.id));
|
|
98
99
|
if (hasSelection)
|
|
99
|
-
parts.push(`${
|
|
100
|
+
parts.push(`${SELECTION_COLUMN_PX}px`);
|
|
100
101
|
for (const c of leaf) {
|
|
101
102
|
const def = c.columnDef;
|
|
102
103
|
const meta = ((_b = ((_a = def.meta) !== null && _a !== void 0 ? _a : {})) !== null && _b !== void 0 ? _b : {});
|
|
@@ -117,7 +117,7 @@ export function Tabs({ header, variant, panelStyle = false, tabs, value, default
|
|
|
117
117
|
const selected = index === activeIndex;
|
|
118
118
|
const tabDomId = `${uid}-tab-${String(tab.id)}`;
|
|
119
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, {
|
|
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, { type: "subtle", severity: null, size: "sm", children: tab.badge.toLocaleString('da-DK') }) })) : null] }) }, tab.id));
|
|
121
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 })] })] }));
|
|
122
122
|
}
|
|
123
123
|
Tabs.Item = TabsItem;
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Formats a Date as "dd
|
|
3
|
-
*
|
|
2
|
+
* Formats a Date as "dd/mm/yyyy" or "dd/mm/yyyy hh:mm:ss".
|
|
3
|
+
* Optionally returns user-friendly Danish labels like "I dag" or "I går".
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* - formatDate(new Date(), { userFriendlyLabel: true }) => "I dag"
|
|
7
|
+
* - formatDate(new Date(), { userFriendlyLabel: true, showTime: true }) => "I dag, kl. 14:30"
|
|
8
|
+
* - formatDate(date, { showTime: true }) => "10/03/2026 14:30"
|
|
4
9
|
*
|
|
5
10
|
* @param date - The Date or date-parsable value to format
|
|
6
|
-
* @param options.
|
|
11
|
+
* @param options.showTime - If true, append time "hh:mm" or "hh:mm:ss"
|
|
12
|
+
* @param options.showSeconds - If true, append seconds when showTime is enabled
|
|
13
|
+
* @param options.userFriendlyLabel - If true, use "I dag" / "I går" when applicable
|
|
7
14
|
* @returns A formatted string
|
|
8
15
|
*/
|
|
9
16
|
export declare function formatDate(date: Date | string | number, options?: {
|
|
10
17
|
showTime?: boolean;
|
|
11
18
|
showSeconds?: boolean;
|
|
19
|
+
userFriendlyLabel?: boolean;
|
|
12
20
|
}): string;
|
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Formats a Date as "dd
|
|
3
|
-
*
|
|
2
|
+
* Formats a Date as "dd/mm/yyyy" or "dd/mm/yyyy hh:mm:ss".
|
|
3
|
+
* Optionally returns user-friendly Danish labels like "I dag" or "I går".
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* - formatDate(new Date(), { userFriendlyLabel: true }) => "I dag"
|
|
7
|
+
* - formatDate(new Date(), { userFriendlyLabel: true, showTime: true }) => "I dag, kl. 14:30"
|
|
8
|
+
* - formatDate(date, { showTime: true }) => "10/03/2026 14:30"
|
|
4
9
|
*
|
|
5
10
|
* @param date - The Date or date-parsable value to format
|
|
6
|
-
* @param options.
|
|
11
|
+
* @param options.showTime - If true, append time "hh:mm" or "hh:mm:ss"
|
|
12
|
+
* @param options.showSeconds - If true, append seconds when showTime is enabled
|
|
13
|
+
* @param options.userFriendlyLabel - If true, use "I dag" / "I går" when applicable
|
|
7
14
|
* @returns A formatted string
|
|
8
15
|
*/
|
|
9
16
|
export function formatDate(date, options = {}) {
|
|
10
17
|
const d = date instanceof Date ? date : new Date(date);
|
|
11
18
|
if (isNaN(d.getTime()))
|
|
12
19
|
return '';
|
|
20
|
+
const { showTime = false, showSeconds = false, userFriendlyLabel = false } = options;
|
|
13
21
|
const pad = (n) => n.toString().padStart(2, '0');
|
|
22
|
+
const formatTime = () => {
|
|
23
|
+
const hours = pad(d.getHours());
|
|
24
|
+
const minutes = pad(d.getMinutes());
|
|
25
|
+
if (!showSeconds)
|
|
26
|
+
return `${hours}:${minutes}`;
|
|
27
|
+
const seconds = pad(d.getSeconds());
|
|
28
|
+
return `${hours}:${minutes}:${seconds}`;
|
|
29
|
+
};
|
|
30
|
+
const isSameCalendarDay = (a, b) => a.getDate() === b.getDate() &&
|
|
31
|
+
a.getMonth() === b.getMonth() &&
|
|
32
|
+
a.getFullYear() === b.getFullYear();
|
|
33
|
+
if (userFriendlyLabel) {
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const yesterday = new Date(now);
|
|
36
|
+
yesterday.setDate(now.getDate() - 1);
|
|
37
|
+
if (isSameCalendarDay(d, now)) {
|
|
38
|
+
return showTime ? `I dag, kl. ${formatTime()}` : 'I dag';
|
|
39
|
+
}
|
|
40
|
+
if (isSameCalendarDay(d, yesterday)) {
|
|
41
|
+
return showTime ? `I går, kl. ${formatTime()}` : 'I går';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
14
44
|
const day = pad(d.getDate());
|
|
15
45
|
const month = pad(d.getMonth() + 1);
|
|
16
46
|
const year = d.getFullYear();
|
|
17
47
|
const base = `${day}/${month}/${year}`;
|
|
18
|
-
if (!
|
|
48
|
+
if (!showTime)
|
|
19
49
|
return base;
|
|
20
|
-
|
|
21
|
-
const minutes = pad(d.getMinutes());
|
|
22
|
-
if (!options.showSeconds)
|
|
23
|
-
return `${base} ${hours}:${minutes}`;
|
|
24
|
-
const seconds = pad(d.getSeconds());
|
|
25
|
-
return `${base} ${hours}:${minutes}:${seconds}`;
|
|
50
|
+
return `${base} ${formatTime()}`;
|
|
26
51
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
type RowId = string;
|
|
2
|
-
type UseAnimatedNewRowIdsArgs<T> = {
|
|
3
|
-
data: T[];
|
|
4
|
-
dataKey: keyof T;
|
|
5
|
-
enabled?: boolean;
|
|
6
|
-
animationDurationMs?: number;
|
|
7
|
-
};
|
|
8
|
-
export declare function useAnimatedNewRowIds<T extends Record<string, any>>({ data, dataKey, enabled, animationDurationMs, }: UseAnimatedNewRowIdsArgs<T>): Set<RowId>;
|
|
9
|
-
export {};
|