@dbcdk/react-components 0.0.185 → 0.0.187
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/client.cjs +28 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +4 -0
- package/dist/components/accordion/components/AccordionRow.cjs +2 -36
- package/dist/components/accordion/components/AccordionRow.js +2 -36
- package/dist/components/button-select/ButtonSelect.cjs +29 -21
- package/dist/components/button-select/ButtonSelect.d.ts +14 -4
- package/dist/components/button-select/ButtonSelect.js +29 -21
- package/dist/components/page/Page.module.css +1 -0
- package/dist/components/pagination/Pagination.module.css +1 -0
- package/dist/components/panel/Panel.module.css +0 -1
- package/dist/components/popover/Popover.cjs +1 -1
- package/dist/components/popover/Popover.js +1 -1
- package/dist/components/print-friendly/PrintFriendlyCard.cjs +39 -0
- package/dist/components/print-friendly/PrintFriendlyCard.d.ts +23 -0
- package/dist/components/print-friendly/PrintFriendlyCard.js +37 -0
- package/dist/components/print-friendly/PrintFriendlyMetaBar.cjs +25 -0
- package/dist/components/print-friendly/PrintFriendlyMetaBar.d.ts +19 -0
- package/dist/components/print-friendly/PrintFriendlyMetaBar.js +23 -0
- package/dist/components/print-friendly/PrintFriendlyTable.cjs +51 -0
- package/dist/components/print-friendly/PrintFriendlyTable.d.ts +34 -0
- package/dist/components/print-friendly/PrintFriendlyTable.js +49 -0
- package/dist/components/print-friendly/components/AccumulationFooter.cjs +42 -0
- package/dist/components/print-friendly/components/AccumulationFooter.d.ts +9 -0
- package/dist/components/print-friendly/components/AccumulationFooter.js +40 -0
- package/dist/components/print-friendly/emptyValue.cjs +9 -0
- package/dist/components/print-friendly/emptyValue.d.ts +2 -0
- package/dist/components/print-friendly/emptyValue.js +6 -0
- package/dist/components/print-friendly/printSelection.cjs +14 -0
- package/dist/components/print-friendly/printSelection.d.ts +19 -0
- package/dist/components/print-friendly/printSelection.js +10 -0
- package/dist/components/stack/Stack.cjs +6 -5
- package/dist/components/stack/Stack.d.ts +2 -1
- package/dist/components/stack/Stack.js +6 -5
- package/dist/components/table/Table.cjs +20 -4
- package/dist/components/table/Table.js +21 -5
- package/dist/components/table/Table.module.css +6 -0
- package/dist/components/table/Table.types.d.ts +1 -0
- package/dist/components/table/components/TableHeader.cjs +1 -0
- package/dist/components/table/components/TableHeader.js +1 -0
- package/dist/components/tabs/Tabs.cjs +49 -39
- package/dist/components/tabs/Tabs.d.ts +3 -1
- package/dist/components/tabs/Tabs.js +49 -39
- package/dist/components/tabs/Tabs.module.css +24 -6
- package/dist/components/timeline/Timeline.cjs +102 -0
- package/dist/components/timeline/Timeline.d.ts +60 -0
- package/dist/components/timeline/Timeline.js +96 -0
- package/dist/components/timeline/Timeline.module.css +22 -0
- package/dist/components/timeline/components/TimelineRow.cjs +147 -0
- package/dist/components/timeline/components/TimelineRow.d.ts +15 -0
- package/dist/components/timeline/components/TimelineRow.js +141 -0
- package/dist/components/timeline/components/TimelineRow.module.css +195 -0
- package/dist/hooks/useCollapsibleHeight.cjs +41 -0
- package/dist/hooks/useCollapsibleHeight.d.ts +6 -0
- package/dist/hooks/useCollapsibleHeight.js +39 -0
- package/dist/hooks/useViewportFill.cjs +11 -3
- package/dist/hooks/useViewportFill.js +11 -3
- package/dist/index.cjs +7 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles/styles.css +74 -2
- package/dist/styles.css +74 -2
- package/dist/utils/date/formatDate.d.ts +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { AccumulationFooter } from './components/AccumulationFooter';
|
|
5
|
+
import { isValueEmpty, EMPTY_VALUE_PLACEHOLDER } from './emptyValue';
|
|
6
|
+
|
|
7
|
+
function PrintFriendlyTable({
|
|
8
|
+
rows = [],
|
|
9
|
+
cols = [],
|
|
10
|
+
tableCy,
|
|
11
|
+
id
|
|
12
|
+
}) {
|
|
13
|
+
const safeRows = useMemo(
|
|
14
|
+
() => rows.filter((row) => row.renderEmpty !== false || !isValueEmpty(row.value)),
|
|
15
|
+
[rows]
|
|
16
|
+
);
|
|
17
|
+
return /* @__PURE__ */ jsx("section", { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("table", { "data-cy": tableCy, id, children: [
|
|
18
|
+
cols.length ? /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: cols.map((col) => /* @__PURE__ */ jsx("th", { style: { width: col.width, textAlign: col.alignment }, children: col.label }, col.key)) }) }) : /* @__PURE__ */ jsxs("colgroup", { children: [
|
|
19
|
+
/* @__PURE__ */ jsx("col", {}),
|
|
20
|
+
/* @__PURE__ */ jsx("col", {})
|
|
21
|
+
] }),
|
|
22
|
+
/* @__PURE__ */ jsx("tbody", { children: safeRows.map((row) => {
|
|
23
|
+
var _a;
|
|
24
|
+
return /* @__PURE__ */ jsx("tr", { "data-cy": row.dataCy, children: cols.length ? cols.map((col) => {
|
|
25
|
+
var _a2;
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"td",
|
|
28
|
+
{
|
|
29
|
+
style: {
|
|
30
|
+
textAlign: col.alignment,
|
|
31
|
+
whiteSpace: col.fitContent ? "nowrap" : void 0
|
|
32
|
+
},
|
|
33
|
+
children: (_a2 = row[col.key]) != null ? _a2 : EMPTY_VALUE_PLACEHOLDER
|
|
34
|
+
},
|
|
35
|
+
col.key
|
|
36
|
+
);
|
|
37
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
|
+
/* @__PURE__ */ jsxs("th", { style: { whiteSpace: "nowrap" }, scope: "row", children: [
|
|
39
|
+
row.label,
|
|
40
|
+
":"
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("td", { children: (_a = row.value) != null ? _a : EMPTY_VALUE_PLACEHOLDER })
|
|
43
|
+
] }) }, row.key);
|
|
44
|
+
}) }),
|
|
45
|
+
/* @__PURE__ */ jsx(AccumulationFooter, { rows: safeRows, cols })
|
|
46
|
+
] }) }) });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { PrintFriendlyTable };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
|
|
7
|
+
function AccumulationFooter({
|
|
8
|
+
rows = [],
|
|
9
|
+
cols = []
|
|
10
|
+
}) {
|
|
11
|
+
const accumulateKeys = react.useMemo(() => cols.filter((c) => c.accumulate).map((c) => c.key), [cols]);
|
|
12
|
+
const colTotals = react.useMemo(() => {
|
|
13
|
+
if (accumulateKeys.length === 0 || rows.length === 0) return {};
|
|
14
|
+
const totals = Object.fromEntries(accumulateKeys.map((k) => [k, 0]));
|
|
15
|
+
for (const row of rows) {
|
|
16
|
+
for (const key of accumulateKeys) {
|
|
17
|
+
const parsedNumber = Number(row[key]);
|
|
18
|
+
if (!Number.isNaN(parsedNumber)) totals[key] += parsedNumber;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return totals;
|
|
22
|
+
}, [rows, accumulateKeys]);
|
|
23
|
+
const displayFooter = accumulateKeys.length > 0;
|
|
24
|
+
if (!displayFooter) return null;
|
|
25
|
+
return /* @__PURE__ */ jsxRuntime.jsx("tfoot", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: cols.map((col, index) => {
|
|
26
|
+
const isTotal = !!col.accumulate;
|
|
27
|
+
const style = {
|
|
28
|
+
textAlign: col.alignment || void 0,
|
|
29
|
+
whiteSpace: col.fitContent ? "nowrap" : void 0,
|
|
30
|
+
fontWeight: isTotal ? 700 : void 0
|
|
31
|
+
};
|
|
32
|
+
function getCellContent() {
|
|
33
|
+
var _a;
|
|
34
|
+
if (index === 0 && !isTotal) return "I alt";
|
|
35
|
+
if (isTotal) return (_a = colTotals[col.key]) != null ? _a : null;
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { style, children: getCellContent() }, `${col.key}-footer`);
|
|
39
|
+
}) }) });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.AccumulationFooter = AccumulationFooter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type JSX } from 'react';
|
|
2
|
+
import type { PrintFriendlyTableCol, PrintFriendlyTableRow } from '../PrintFriendlyTable';
|
|
3
|
+
interface AccumulationFooterProps {
|
|
4
|
+
rows?: PrintFriendlyTableRow[];
|
|
5
|
+
cols?: PrintFriendlyTableCol[];
|
|
6
|
+
}
|
|
7
|
+
/** Renders a totals row under a PrintFriendlyTable's body, one cell per column flagged `accumulate`. */
|
|
8
|
+
export declare function AccumulationFooter({ rows, cols, }: AccumulationFooterProps): JSX.Element | null;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
function AccumulationFooter({
|
|
6
|
+
rows = [],
|
|
7
|
+
cols = []
|
|
8
|
+
}) {
|
|
9
|
+
const accumulateKeys = useMemo(() => cols.filter((c) => c.accumulate).map((c) => c.key), [cols]);
|
|
10
|
+
const colTotals = useMemo(() => {
|
|
11
|
+
if (accumulateKeys.length === 0 || rows.length === 0) return {};
|
|
12
|
+
const totals = Object.fromEntries(accumulateKeys.map((k) => [k, 0]));
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
for (const key of accumulateKeys) {
|
|
15
|
+
const parsedNumber = Number(row[key]);
|
|
16
|
+
if (!Number.isNaN(parsedNumber)) totals[key] += parsedNumber;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return totals;
|
|
20
|
+
}, [rows, accumulateKeys]);
|
|
21
|
+
const displayFooter = accumulateKeys.length > 0;
|
|
22
|
+
if (!displayFooter) return null;
|
|
23
|
+
return /* @__PURE__ */ jsx("tfoot", { children: /* @__PURE__ */ jsx("tr", { children: cols.map((col, index) => {
|
|
24
|
+
const isTotal = !!col.accumulate;
|
|
25
|
+
const style = {
|
|
26
|
+
textAlign: col.alignment || void 0,
|
|
27
|
+
whiteSpace: col.fitContent ? "nowrap" : void 0,
|
|
28
|
+
fontWeight: isTotal ? 700 : void 0
|
|
29
|
+
};
|
|
30
|
+
function getCellContent() {
|
|
31
|
+
var _a;
|
|
32
|
+
if (index === 0 && !isTotal) return "I alt";
|
|
33
|
+
if (isTotal) return (_a = colTotals[col.key]) != null ? _a : null;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return /* @__PURE__ */ jsx("td", { style, children: getCellContent() }, `${col.key}-footer`);
|
|
37
|
+
}) }) });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { AccumulationFooter };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EMPTY_VALUE_PLACEHOLDER = "(tom)";
|
|
4
|
+
function isValueEmpty(value) {
|
|
5
|
+
return value === null || value === void 0 || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.filter(Boolean).length === 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
exports.EMPTY_VALUE_PLACEHOLDER = EMPTY_VALUE_PLACEHOLDER;
|
|
9
|
+
exports.isValueEmpty = isValueEmpty;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const EMPTY_VALUE_PLACEHOLDER = "(tom)";
|
|
2
|
+
function isValueEmpty(value) {
|
|
3
|
+
return value === null || value === void 0 || typeof value === "string" && value.trim() === "" || Array.isArray(value) && value.filter(Boolean).length === 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export { EMPTY_VALUE_PLACEHOLDER, isValueEmpty };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const NOT_PRINTABLE_ATTRIBUTE_NAME = "data-not-printable";
|
|
4
|
+
const PRINTABLE_ATTRIBUTE = { [NOT_PRINTABLE_ATTRIBUTE_NAME]: false };
|
|
5
|
+
const NOT_PRINTABLE_ATTRIBUTE = { [NOT_PRINTABLE_ATTRIBUTE_NAME]: true };
|
|
6
|
+
function printSelection() {
|
|
7
|
+
window.document.body.setAttribute(NOT_PRINTABLE_ATTRIBUTE_NAME, "true");
|
|
8
|
+
window.print();
|
|
9
|
+
window.document.body.removeAttribute(NOT_PRINTABLE_ATTRIBUTE_NAME);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exports.NOT_PRINTABLE_ATTRIBUTE = NOT_PRINTABLE_ATTRIBUTE;
|
|
13
|
+
exports.PRINTABLE_ATTRIBUTE = PRINTABLE_ATTRIBUTE;
|
|
14
|
+
exports.printSelection = printSelection;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Spread onto an element to explicitly include it in a filtered print, overriding an ancestor's NOT_PRINTABLE_ATTRIBUTE. */
|
|
2
|
+
export declare const PRINTABLE_ATTRIBUTE: {
|
|
3
|
+
"data-not-printable": boolean;
|
|
4
|
+
};
|
|
5
|
+
/** Spread onto an element to exclude it (and its subtree) from a filtered print. */
|
|
6
|
+
export declare const NOT_PRINTABLE_ATTRIBUTE: {
|
|
7
|
+
"data-not-printable": boolean;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Prints only explicitly-opted-in content instead of the whole page: marks
|
|
11
|
+
* `document.body` as not-printable (hiding everything except elements
|
|
12
|
+
* carrying PRINTABLE_ATTRIBUTE, plus any `role="dialog"` element - see the
|
|
13
|
+
* @media print rules this pairs with), triggers the browser print dialog,
|
|
14
|
+
* then restores normal printing.
|
|
15
|
+
*
|
|
16
|
+
* Typical use: a "print this" button on a modal that itself carries
|
|
17
|
+
* PRINTABLE_ATTRIBUTE, so only that modal's content ends up on the page.
|
|
18
|
+
*/
|
|
19
|
+
export declare function printSelection(): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const NOT_PRINTABLE_ATTRIBUTE_NAME = "data-not-printable";
|
|
2
|
+
const PRINTABLE_ATTRIBUTE = { [NOT_PRINTABLE_ATTRIBUTE_NAME]: false };
|
|
3
|
+
const NOT_PRINTABLE_ATTRIBUTE = { [NOT_PRINTABLE_ATTRIBUTE_NAME]: true };
|
|
4
|
+
function printSelection() {
|
|
5
|
+
window.document.body.setAttribute(NOT_PRINTABLE_ATTRIBUTE_NAME, "true");
|
|
6
|
+
window.print();
|
|
7
|
+
window.document.body.removeAttribute(NOT_PRINTABLE_ATTRIBUTE_NAME);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { NOT_PRINTABLE_ATTRIBUTE, PRINTABLE_ATTRIBUTE, printSelection };
|
|
@@ -28,10 +28,11 @@ const Stack = react.forwardRef(function Stack2({
|
|
|
28
28
|
...rest
|
|
29
29
|
}, ref) {
|
|
30
30
|
const Tag = as != null ? as : "div";
|
|
31
|
-
const resolvedStyle = maxWidth != null ? {
|
|
32
|
-
...maxWidth.base != null ? { "--max-width-base": maxWidth.base } : {},
|
|
33
|
-
...maxWidth.md != null ? { "--max-width-md": maxWidth.md } : {},
|
|
34
|
-
...maxWidth.lg != null ? { "--max-width-lg": maxWidth.lg } : {},
|
|
31
|
+
const resolvedStyle = maxWidth != null || typeof grow === "number" ? {
|
|
32
|
+
...(maxWidth == null ? void 0 : maxWidth.base) != null ? { "--max-width-base": maxWidth.base } : {},
|
|
33
|
+
...(maxWidth == null ? void 0 : maxWidth.md) != null ? { "--max-width-md": maxWidth.md } : {},
|
|
34
|
+
...(maxWidth == null ? void 0 : maxWidth.lg) != null ? { "--max-width-lg": maxWidth.lg } : {},
|
|
35
|
+
...typeof grow === "number" ? { flexGrow: grow, flexBasis: 0 } : {},
|
|
35
36
|
...style
|
|
36
37
|
} : style;
|
|
37
38
|
const classes = [
|
|
@@ -40,7 +41,7 @@ const Stack = react.forwardRef(function Stack2({
|
|
|
40
41
|
gap ? `dbc-gap-${gap}` : void 0,
|
|
41
42
|
align ? `dbc-items-${align}` : void 0,
|
|
42
43
|
justify ? `dbc-justify-${justify}` : void 0,
|
|
43
|
-
grow ? "dbc-flex-grow" : void 0,
|
|
44
|
+
grow === true ? "dbc-flex-grow" : void 0,
|
|
44
45
|
wrap ? "dbc-flex-wrap" : void 0,
|
|
45
46
|
mobileDirection ? styles__default.default[`mobileDirection${mobileDirection.charAt(0).toUpperCase()}${mobileDirection.slice(1)}`] : void 0,
|
|
46
47
|
mobileGap ? styles__default.default[`mobileGap${mobileGap.charAt(0).toUpperCase()}${mobileGap.slice(1)}`] : void 0,
|
|
@@ -9,7 +9,8 @@ type StackOwnProps = {
|
|
|
9
9
|
gap?: GapSize;
|
|
10
10
|
align?: StackAlign;
|
|
11
11
|
justify?: StackJustify;
|
|
12
|
-
grow
|
|
12
|
+
/** true = flex-grow: 1. A number = flex-grow: n with flex-basis: 0, for proportional splits (e.g. grow={2} takes twice the space of grow={1}) */
|
|
13
|
+
grow?: boolean | number;
|
|
13
14
|
wrap?: boolean;
|
|
14
15
|
mobileDirection?: StackDirection;
|
|
15
16
|
mobileGap?: GapSize;
|
|
@@ -22,10 +22,11 @@ const Stack = forwardRef(function Stack2({
|
|
|
22
22
|
...rest
|
|
23
23
|
}, ref) {
|
|
24
24
|
const Tag = as != null ? as : "div";
|
|
25
|
-
const resolvedStyle = maxWidth != null ? {
|
|
26
|
-
...maxWidth.base != null ? { "--max-width-base": maxWidth.base } : {},
|
|
27
|
-
...maxWidth.md != null ? { "--max-width-md": maxWidth.md } : {},
|
|
28
|
-
...maxWidth.lg != null ? { "--max-width-lg": maxWidth.lg } : {},
|
|
25
|
+
const resolvedStyle = maxWidth != null || typeof grow === "number" ? {
|
|
26
|
+
...(maxWidth == null ? void 0 : maxWidth.base) != null ? { "--max-width-base": maxWidth.base } : {},
|
|
27
|
+
...(maxWidth == null ? void 0 : maxWidth.md) != null ? { "--max-width-md": maxWidth.md } : {},
|
|
28
|
+
...(maxWidth == null ? void 0 : maxWidth.lg) != null ? { "--max-width-lg": maxWidth.lg } : {},
|
|
29
|
+
...typeof grow === "number" ? { flexGrow: grow, flexBasis: 0 } : {},
|
|
29
30
|
...style
|
|
30
31
|
} : style;
|
|
31
32
|
const classes = [
|
|
@@ -34,7 +35,7 @@ const Stack = forwardRef(function Stack2({
|
|
|
34
35
|
gap ? `dbc-gap-${gap}` : void 0,
|
|
35
36
|
align ? `dbc-items-${align}` : void 0,
|
|
36
37
|
justify ? `dbc-justify-${justify}` : void 0,
|
|
37
|
-
grow ? "dbc-flex-grow" : void 0,
|
|
38
|
+
grow === true ? "dbc-flex-grow" : void 0,
|
|
38
39
|
wrap ? "dbc-flex-wrap" : void 0,
|
|
39
40
|
mobileDirection ? styles[`mobileDirection${mobileDirection.charAt(0).toUpperCase()}${mobileDirection.slice(1)}`] : void 0,
|
|
40
41
|
mobileGap ? styles[`mobileGap${mobileGap.charAt(0).toUpperCase()}${mobileGap.slice(1)}`] : void 0,
|
|
@@ -32,7 +32,7 @@ function Table({
|
|
|
32
32
|
size = "md",
|
|
33
33
|
viewMode,
|
|
34
34
|
fillViewport = false,
|
|
35
|
-
fillViewportBottomOffset =
|
|
35
|
+
fillViewportBottomOffset = 0,
|
|
36
36
|
containScrolling = true,
|
|
37
37
|
stickyHeader,
|
|
38
38
|
tableWidth,
|
|
@@ -67,6 +67,20 @@ function Table({
|
|
|
67
67
|
const tableRootRefWrapper = react.useRef(tableRootRef);
|
|
68
68
|
const hasSelection = Boolean(selectedRows && onRowSelect);
|
|
69
69
|
const hasPagination = Boolean(onPageChange && (loading || data.length > 0));
|
|
70
|
+
const paginationRef = react.useRef(null);
|
|
71
|
+
const [paginationHeight, setPaginationHeight] = react.useState(0);
|
|
72
|
+
react.useEffect(() => {
|
|
73
|
+
const element = paginationRef.current;
|
|
74
|
+
if (!element) return;
|
|
75
|
+
const measure = () => setPaginationHeight(element.getBoundingClientRect().height);
|
|
76
|
+
const frame = requestAnimationFrame(measure);
|
|
77
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
78
|
+
observer == null ? void 0 : observer.observe(element);
|
|
79
|
+
return () => {
|
|
80
|
+
cancelAnimationFrame(frame);
|
|
81
|
+
observer == null ? void 0 : observer.disconnect();
|
|
82
|
+
};
|
|
83
|
+
}, [hasPagination]);
|
|
70
84
|
const [expandedRows, setExpandedRows] = react.useState(() => /* @__PURE__ */ new Set());
|
|
71
85
|
const handleToggleRowExpand = react.useCallback((rowId) => {
|
|
72
86
|
setExpandedRows((prev) => {
|
|
@@ -79,12 +93,12 @@ function Table({
|
|
|
79
93
|
return next;
|
|
80
94
|
});
|
|
81
95
|
}, []);
|
|
82
|
-
const paginationOffset = hasPagination ?
|
|
83
|
-
const { style: viewportFillStyle
|
|
96
|
+
const paginationOffset = hasPagination && paginationPlacement === "bottom" ? paginationHeight : 0;
|
|
97
|
+
const { style: viewportFillStyle } = useViewportFill.useViewportFill(internalTableRootRef, {
|
|
84
98
|
bottomOffset: fillViewportBottomOffset + paginationOffset,
|
|
85
99
|
min: 0
|
|
86
100
|
});
|
|
87
|
-
const fillViewportActive = fillViewport && containScrolling
|
|
101
|
+
const fillViewportActive = fillViewport && containScrolling;
|
|
88
102
|
const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : void 0;
|
|
89
103
|
const setTableRootRef = react.useCallback((node) => {
|
|
90
104
|
internalTableRootRef.current = node;
|
|
@@ -125,6 +139,8 @@ function Table({
|
|
|
125
139
|
const paginationEl = hasPagination ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
140
|
"div",
|
|
127
141
|
{
|
|
142
|
+
ref: paginationRef,
|
|
143
|
+
"data-cy": `pagination-toolbar-${paginationPlacement}`,
|
|
128
144
|
className: table_classes.cx(
|
|
129
145
|
styles__default.default.paginationSlot,
|
|
130
146
|
paginationPlacement === "top" && styles__default.default.paginationSlotTop
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
-
import { useMemo, useId, useRef, useState, useCallback } from 'react';
|
|
3
|
+
import { useMemo, useId, useRef, useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { Pagination } from '../../components/pagination/Pagination';
|
|
5
5
|
import { SkeletonLoader } from '../../components/skeleton-loader/SkeletonLoader';
|
|
6
6
|
import { useViewportFill } from '../../hooks/useViewportFill';
|
|
@@ -26,7 +26,7 @@ function Table({
|
|
|
26
26
|
size = "md",
|
|
27
27
|
viewMode,
|
|
28
28
|
fillViewport = false,
|
|
29
|
-
fillViewportBottomOffset =
|
|
29
|
+
fillViewportBottomOffset = 0,
|
|
30
30
|
containScrolling = true,
|
|
31
31
|
stickyHeader,
|
|
32
32
|
tableWidth,
|
|
@@ -61,6 +61,20 @@ function Table({
|
|
|
61
61
|
const tableRootRefWrapper = useRef(tableRootRef);
|
|
62
62
|
const hasSelection = Boolean(selectedRows && onRowSelect);
|
|
63
63
|
const hasPagination = Boolean(onPageChange && (loading || data.length > 0));
|
|
64
|
+
const paginationRef = useRef(null);
|
|
65
|
+
const [paginationHeight, setPaginationHeight] = useState(0);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const element = paginationRef.current;
|
|
68
|
+
if (!element) return;
|
|
69
|
+
const measure = () => setPaginationHeight(element.getBoundingClientRect().height);
|
|
70
|
+
const frame = requestAnimationFrame(measure);
|
|
71
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
72
|
+
observer == null ? void 0 : observer.observe(element);
|
|
73
|
+
return () => {
|
|
74
|
+
cancelAnimationFrame(frame);
|
|
75
|
+
observer == null ? void 0 : observer.disconnect();
|
|
76
|
+
};
|
|
77
|
+
}, [hasPagination]);
|
|
64
78
|
const [expandedRows, setExpandedRows] = useState(() => /* @__PURE__ */ new Set());
|
|
65
79
|
const handleToggleRowExpand = useCallback((rowId) => {
|
|
66
80
|
setExpandedRows((prev) => {
|
|
@@ -73,12 +87,12 @@ function Table({
|
|
|
73
87
|
return next;
|
|
74
88
|
});
|
|
75
89
|
}, []);
|
|
76
|
-
const paginationOffset = hasPagination ?
|
|
77
|
-
const { style: viewportFillStyle
|
|
90
|
+
const paginationOffset = hasPagination && paginationPlacement === "bottom" ? paginationHeight : 0;
|
|
91
|
+
const { style: viewportFillStyle } = useViewportFill(internalTableRootRef, {
|
|
78
92
|
bottomOffset: fillViewportBottomOffset + paginationOffset,
|
|
79
93
|
min: 0
|
|
80
94
|
});
|
|
81
|
-
const fillViewportActive = fillViewport && containScrolling
|
|
95
|
+
const fillViewportActive = fillViewport && containScrolling;
|
|
82
96
|
const stickyTop = stickyHeader === "app-header" ? "var(--app-header-height, 60px)" : typeof stickyHeader === "number" ? `${stickyHeader}px` : void 0;
|
|
83
97
|
const setTableRootRef = useCallback((node) => {
|
|
84
98
|
internalTableRootRef.current = node;
|
|
@@ -119,6 +133,8 @@ function Table({
|
|
|
119
133
|
const paginationEl = hasPagination ? /* @__PURE__ */ jsx(
|
|
120
134
|
"div",
|
|
121
135
|
{
|
|
136
|
+
ref: paginationRef,
|
|
137
|
+
"data-cy": `pagination-toolbar-${paginationPlacement}`,
|
|
122
138
|
className: cx(
|
|
123
139
|
styles.paginationSlot,
|
|
124
140
|
paginationPlacement === "top" && styles.paginationSlotTop
|
|
@@ -120,6 +120,12 @@
|
|
|
120
120
|
z-index: 10;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/* Sticky embedded headers must cover the rows scrolling underneath. */
|
|
124
|
+
.embedded .header[data-sticky='true'] {
|
|
125
|
+
--table-component-header-bg: var(--table-row-bg);
|
|
126
|
+
background-color: var(--table-component-header-bg);
|
|
127
|
+
}
|
|
128
|
+
|
|
123
129
|
.headerRow,
|
|
124
130
|
.row {
|
|
125
131
|
position: relative;
|
|
@@ -57,6 +57,7 @@ export type TableProps<T extends Record<string, any>> = Omit<HTMLAttributes<HTML
|
|
|
57
57
|
size?: 'sm' | 'md';
|
|
58
58
|
viewMode?: ViewMode;
|
|
59
59
|
fillViewport?: boolean;
|
|
60
|
+
/** Extra space below the table, in addition to measured layout/pagination spacing. Defaults to 0. */
|
|
60
61
|
fillViewportBottomOffset?: number;
|
|
61
62
|
containScrolling?: boolean;
|
|
62
63
|
stickyHeader?: StickyHeader;
|
|
@@ -25,6 +25,7 @@ function TableHeader({
|
|
|
25
25
|
"thead",
|
|
26
26
|
{
|
|
27
27
|
className: styles__default.default.header,
|
|
28
|
+
"data-sticky": Boolean(stickyTop),
|
|
28
29
|
style: stickyTop ? { top: stickyTop, position: "sticky", zIndex: 2 } : void 0,
|
|
29
30
|
children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: styles__default.default.headerRow, children: [
|
|
30
31
|
hasSelection ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -19,6 +19,7 @@ function TableHeader({
|
|
|
19
19
|
"thead",
|
|
20
20
|
{
|
|
21
21
|
className: styles.header,
|
|
22
|
+
"data-sticky": Boolean(stickyTop),
|
|
22
23
|
style: stickyTop ? { top: stickyTop, position: "sticky", zIndex: 2 } : void 0,
|
|
23
24
|
children: /* @__PURE__ */ jsxs("tr", { className: styles.headerRow, children: [
|
|
24
25
|
hasSelection ? /* @__PURE__ */ jsx(
|
|
@@ -57,6 +57,7 @@ function Tabs({
|
|
|
57
57
|
onRemoveTab,
|
|
58
58
|
onAddTab,
|
|
59
59
|
addition,
|
|
60
|
+
tabListAddition,
|
|
60
61
|
loading = false,
|
|
61
62
|
children
|
|
62
63
|
}) {
|
|
@@ -149,44 +150,52 @@ function Tabs({
|
|
|
149
150
|
) : null,
|
|
150
151
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.tabs} ${styles__default.default[variant]} ${panelStyle ? styles__default.default.panelStyle : ""}`, children: [
|
|
151
152
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.tabList, children: [
|
|
152
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
154
|
+
"div",
|
|
155
|
+
{
|
|
156
|
+
className: onAddTab ? styles__default.default.tabButtons : `${styles__default.default.tabButtons} ${styles__default.default.tabButtonsEnd}`,
|
|
157
|
+
role: "tablist",
|
|
158
|
+
"aria-label": header != null ? header : "Tabs",
|
|
159
|
+
children: visibleTabs.map((tab, index) => {
|
|
160
|
+
const selected = index === activeIndex;
|
|
161
|
+
const tabDomId = `${uid}-tab-${String(tab.id)}`;
|
|
162
|
+
const panelDomId = `${uid}-panel-${String(tab.id)}`;
|
|
163
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.tab} ${selected ? styles__default.default.active : ""}`, children: [
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
165
|
+
"button",
|
|
166
|
+
{
|
|
167
|
+
id: tabDomId,
|
|
168
|
+
type: "button",
|
|
169
|
+
className: styles__default.default.tabButton,
|
|
170
|
+
role: "tab",
|
|
171
|
+
"aria-selected": selected,
|
|
172
|
+
"aria-controls": panelDomId,
|
|
173
|
+
tabIndex: selected ? 0 : -1,
|
|
174
|
+
disabled: tab.disabled,
|
|
175
|
+
onClick: () => setValue(tab.id),
|
|
176
|
+
onKeyDown: (e) => onKeyDownTab(e, index),
|
|
177
|
+
children: [
|
|
178
|
+
tab.headerIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, children: tab.headerIcon }) : null,
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.label, children: tab.header }),
|
|
180
|
+
tab.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.badge, children: /* @__PURE__ */ jsxRuntime.jsx(Chip.Chip, { size: "sm", children: tab.badge.toLocaleString("da-DK") }) }) : null
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
tab.removable && onRemoveTab ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
185
|
+
"button",
|
|
186
|
+
{
|
|
187
|
+
type: "button",
|
|
188
|
+
className: styles__default.default.removeTabButton,
|
|
189
|
+
"aria-label": `Fjern fane ${tab.header}`,
|
|
190
|
+
disabled: tab.disabled,
|
|
191
|
+
onClick: () => onRemoveTab(tab.id, tab, index),
|
|
192
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": "true" })
|
|
193
|
+
}
|
|
194
|
+
) : null
|
|
195
|
+
] }, tab.id);
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
),
|
|
190
199
|
onAddTab ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
191
200
|
"button",
|
|
192
201
|
{
|
|
@@ -196,7 +205,8 @@ function Tabs({
|
|
|
196
205
|
"aria-label": "Tilf\xF8j fane",
|
|
197
206
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { "aria-hidden": "true" })
|
|
198
207
|
}
|
|
199
|
-
) : null
|
|
208
|
+
) : null,
|
|
209
|
+
tabListAddition ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.tabListAddition, children: tabListAddition }) : null
|
|
200
210
|
] }),
|
|
201
211
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
202
212
|
"div",
|
|
@@ -33,6 +33,8 @@ export interface TabsProps {
|
|
|
33
33
|
/** Shows an add-tab button and notifies the caller. The caller owns creating and selecting the tab. */
|
|
34
34
|
onAddTab?: () => void;
|
|
35
35
|
addition?: ReactNode;
|
|
36
|
+
/** Extra content rendered in the tab-list row, next to the tab buttons (e.g. filters). */
|
|
37
|
+
tabListAddition?: ReactNode;
|
|
36
38
|
loading?: boolean;
|
|
37
39
|
/** Composition API */
|
|
38
40
|
children?: ReactNode;
|
|
@@ -51,7 +53,7 @@ type SlotName = 'Item';
|
|
|
51
53
|
type TabsItemComponent = ((props: TabsItemProps) => JSX.Element) & {
|
|
52
54
|
__TABS_SLOT__?: SlotName;
|
|
53
55
|
};
|
|
54
|
-
export declare function Tabs({ header, subheader, variant, panelStyle, tabs, value, defaultValue, storageKey, onValueChange, onRemoveTab, onAddTab, addition, loading, children, }: TabsProps): JSX.Element;
|
|
56
|
+
export declare function Tabs({ header, subheader, variant, panelStyle, tabs, value, defaultValue, storageKey, onValueChange, onRemoveTab, onAddTab, addition, tabListAddition, loading, children, }: TabsProps): JSX.Element;
|
|
55
57
|
export declare namespace Tabs {
|
|
56
58
|
var Item: TabsItemComponent;
|
|
57
59
|
}
|