@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
|
@@ -51,6 +51,7 @@ function Tabs({
|
|
|
51
51
|
onRemoveTab,
|
|
52
52
|
onAddTab,
|
|
53
53
|
addition,
|
|
54
|
+
tabListAddition,
|
|
54
55
|
loading = false,
|
|
55
56
|
children
|
|
56
57
|
}) {
|
|
@@ -143,44 +144,52 @@ function Tabs({
|
|
|
143
144
|
) : null,
|
|
144
145
|
/* @__PURE__ */ jsxs("div", { className: `${styles.tabs} ${styles[variant]} ${panelStyle ? styles.panelStyle : ""}`, children: [
|
|
145
146
|
/* @__PURE__ */ jsxs("div", { className: styles.tabList, children: [
|
|
146
|
-
/* @__PURE__ */ jsx(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
"div",
|
|
149
|
+
{
|
|
150
|
+
className: onAddTab ? styles.tabButtons : `${styles.tabButtons} ${styles.tabButtonsEnd}`,
|
|
151
|
+
role: "tablist",
|
|
152
|
+
"aria-label": header != null ? header : "Tabs",
|
|
153
|
+
children: visibleTabs.map((tab, index) => {
|
|
154
|
+
const selected = index === activeIndex;
|
|
155
|
+
const tabDomId = `${uid}-tab-${String(tab.id)}`;
|
|
156
|
+
const panelDomId = `${uid}-panel-${String(tab.id)}`;
|
|
157
|
+
return /* @__PURE__ */ jsxs("div", { className: `${styles.tab} ${selected ? styles.active : ""}`, children: [
|
|
158
|
+
/* @__PURE__ */ jsxs(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
id: tabDomId,
|
|
162
|
+
type: "button",
|
|
163
|
+
className: styles.tabButton,
|
|
164
|
+
role: "tab",
|
|
165
|
+
"aria-selected": selected,
|
|
166
|
+
"aria-controls": panelDomId,
|
|
167
|
+
tabIndex: selected ? 0 : -1,
|
|
168
|
+
disabled: tab.disabled,
|
|
169
|
+
onClick: () => setValue(tab.id),
|
|
170
|
+
onKeyDown: (e) => onKeyDownTab(e, index),
|
|
171
|
+
children: [
|
|
172
|
+
tab.headerIcon ? /* @__PURE__ */ jsx("span", { className: styles.icon, children: tab.headerIcon }) : null,
|
|
173
|
+
/* @__PURE__ */ jsx("span", { className: styles.label, children: tab.header }),
|
|
174
|
+
tab.badge ? /* @__PURE__ */ jsx("span", { className: styles.badge, children: /* @__PURE__ */ jsx(Chip, { size: "sm", children: tab.badge.toLocaleString("da-DK") }) }) : null
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
tab.removable && onRemoveTab ? /* @__PURE__ */ jsx(
|
|
179
|
+
"button",
|
|
180
|
+
{
|
|
181
|
+
type: "button",
|
|
182
|
+
className: styles.removeTabButton,
|
|
183
|
+
"aria-label": `Fjern fane ${tab.header}`,
|
|
184
|
+
disabled: tab.disabled,
|
|
185
|
+
onClick: () => onRemoveTab(tab.id, tab, index),
|
|
186
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
187
|
+
}
|
|
188
|
+
) : null
|
|
189
|
+
] }, tab.id);
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
),
|
|
184
193
|
onAddTab ? /* @__PURE__ */ jsx(
|
|
185
194
|
"button",
|
|
186
195
|
{
|
|
@@ -190,7 +199,8 @@ function Tabs({
|
|
|
190
199
|
"aria-label": "Tilf\xF8j fane",
|
|
191
200
|
children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" })
|
|
192
201
|
}
|
|
193
|
-
) : null
|
|
202
|
+
) : null,
|
|
203
|
+
tabListAddition ? /* @__PURE__ */ jsx("div", { className: styles.tabListAddition, children: tabListAddition }) : null
|
|
194
204
|
] }),
|
|
195
205
|
/* @__PURE__ */ jsx(
|
|
196
206
|
"div",
|
|
@@ -36,6 +36,15 @@
|
|
|
36
36
|
min-width: 0;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/* Extra content next to the tab buttons, pushed into the free space on the right */
|
|
40
|
+
.tabListAddition {
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--spacing-sm);
|
|
44
|
+
margin-inline-start: auto;
|
|
45
|
+
padding-inline-start: var(--spacing-md);
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
/* Individual tab wrapper */
|
|
40
49
|
.tab {
|
|
41
50
|
display: flex;
|
|
@@ -201,17 +210,26 @@
|
|
|
201
210
|
|
|
202
211
|
.filled > .tabList {
|
|
203
212
|
border-block-end: 1px solid var(--color-border-subtle);
|
|
204
|
-
background: var(--color-bg-toolbar);
|
|
205
|
-
inline-size: 100%;
|
|
206
213
|
}
|
|
207
214
|
|
|
215
|
+
/* One continuous backdrop behind the whole button row, so the curved gap
|
|
216
|
+
next to the active tab's rounded corners shows toolbar gray, not the
|
|
217
|
+
surface color behind the row. */
|
|
208
218
|
.filled > .tabList > .tabButtons {
|
|
209
219
|
display: flex;
|
|
220
|
+
background: var(--color-bg-toolbar);
|
|
221
|
+
border-start-start-radius: var(--border-radius-md);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.filled > .tabList > .tabButtons.tabButtonsEnd {
|
|
225
|
+
border-start-end-radius: var(--border-radius-md);
|
|
210
226
|
}
|
|
211
227
|
|
|
212
228
|
.filled > .tabList > .addTabButton {
|
|
229
|
+
background: var(--color-bg-toolbar);
|
|
213
230
|
border: 1px solid transparent;
|
|
214
231
|
border-block-end: 0;
|
|
232
|
+
border-start-end-radius: var(--border-radius-md);
|
|
215
233
|
}
|
|
216
234
|
|
|
217
235
|
.filled > .tabList > .addTabButton:hover {
|
|
@@ -253,7 +271,7 @@
|
|
|
253
271
|
border-block-start: none;
|
|
254
272
|
border-radius: 0 0 var(--border-radius-md) var(--border-radius-md);
|
|
255
273
|
box-shadow: var(--shadow-xs);
|
|
256
|
-
padding: var(--spacing-
|
|
274
|
+
padding: var(--spacing-md);
|
|
257
275
|
}
|
|
258
276
|
|
|
259
277
|
/* =========================
|
|
@@ -307,7 +325,7 @@
|
|
|
307
325
|
.panelStyle.outlined > .tabContent {
|
|
308
326
|
border: var(--border-width-thin) solid var(--color-border-default);
|
|
309
327
|
background: var(--color-bg-surface);
|
|
310
|
-
padding: var(--spacing-
|
|
328
|
+
padding: var(--spacing-md);
|
|
311
329
|
}
|
|
312
330
|
|
|
313
331
|
@media (max-width: 767px) {
|
|
@@ -333,8 +351,8 @@
|
|
|
333
351
|
|
|
334
352
|
.filled > .tabContent,
|
|
335
353
|
.panelStyle.outlined > .tabContent {
|
|
336
|
-
padding-block: var(--spacing-
|
|
337
|
-
padding-inline: var(--spacing-
|
|
354
|
+
padding-block: var(--spacing-sm);
|
|
355
|
+
padding-inline: var(--spacing-sm);
|
|
338
356
|
}
|
|
339
357
|
|
|
340
358
|
.panelStyle > .tabList {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var TimelineRow = require('./components/TimelineRow');
|
|
7
|
+
var styles = require('./Timeline.module.css');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var styles__default = /*#__PURE__*/_interopDefault(styles);
|
|
12
|
+
|
|
13
|
+
function uniqSorted(nums) {
|
|
14
|
+
return Array.from(new Set(nums)).sort((a, b) => a - b);
|
|
15
|
+
}
|
|
16
|
+
function collapsibleIndexesOf(items) {
|
|
17
|
+
return items.reduce((acc, item, i) => {
|
|
18
|
+
if (item.collapsible) acc.push(i);
|
|
19
|
+
return acc;
|
|
20
|
+
}, []);
|
|
21
|
+
}
|
|
22
|
+
function normalizeOpenIndexes(indexes, items) {
|
|
23
|
+
const collapsible = collapsibleIndexesOf(items);
|
|
24
|
+
if (indexes === "all") {
|
|
25
|
+
return collapsible;
|
|
26
|
+
}
|
|
27
|
+
const collapsibleSet = new Set(collapsible);
|
|
28
|
+
return uniqSorted((indexes != null ? indexes : []).filter((n) => collapsibleSet.has(n)));
|
|
29
|
+
}
|
|
30
|
+
function Timeline({
|
|
31
|
+
items,
|
|
32
|
+
size = "md",
|
|
33
|
+
variant = "default",
|
|
34
|
+
renderMode = "lazy",
|
|
35
|
+
defaultOpenIndexes = [],
|
|
36
|
+
openIndexes,
|
|
37
|
+
onOpenIndexesChange,
|
|
38
|
+
className,
|
|
39
|
+
...rest
|
|
40
|
+
}) {
|
|
41
|
+
const uid = react.useId();
|
|
42
|
+
const isControlled = openIndexes !== void 0;
|
|
43
|
+
const [hasMounted, setHasMounted] = react.useState(false);
|
|
44
|
+
react.useEffect(() => {
|
|
45
|
+
setHasMounted(true);
|
|
46
|
+
}, []);
|
|
47
|
+
const [internalOpenIndexes, setInternalOpenIndexes] = react.useState(
|
|
48
|
+
() => normalizeOpenIndexes(defaultOpenIndexes, items)
|
|
49
|
+
);
|
|
50
|
+
const currentOpenIndexes = react.useMemo(
|
|
51
|
+
() => normalizeOpenIndexes(isControlled ? openIndexes : internalOpenIndexes, items),
|
|
52
|
+
[isControlled, openIndexes, internalOpenIndexes, items]
|
|
53
|
+
);
|
|
54
|
+
const openSet = react.useMemo(() => new Set(currentOpenIndexes), [currentOpenIndexes]);
|
|
55
|
+
function commit(nextIndexes) {
|
|
56
|
+
const normalized = normalizeOpenIndexes(nextIndexes, items);
|
|
57
|
+
if (isControlled) {
|
|
58
|
+
onOpenIndexesChange == null ? void 0 : onOpenIndexesChange(normalized);
|
|
59
|
+
} else {
|
|
60
|
+
setInternalOpenIndexes(normalized);
|
|
61
|
+
onOpenIndexesChange == null ? void 0 : onOpenIndexesChange(normalized);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function toggle(index) {
|
|
65
|
+
const item = items[index];
|
|
66
|
+
if (!item || !item.collapsible || item.disabled) return;
|
|
67
|
+
const isOpen = openSet.has(index);
|
|
68
|
+
if (isOpen) {
|
|
69
|
+
commit(currentOpenIndexes.filter((i) => i !== index));
|
|
70
|
+
} else {
|
|
71
|
+
commit([...currentOpenIndexes, index]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
75
|
+
"div",
|
|
76
|
+
{
|
|
77
|
+
...rest,
|
|
78
|
+
className: [styles__default.default.container, styles__default.default[size], className].filter(Boolean).join(" "),
|
|
79
|
+
children: items.map((item, i) => {
|
|
80
|
+
var _a;
|
|
81
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
82
|
+
TimelineRow.TimelineRow,
|
|
83
|
+
{
|
|
84
|
+
uid,
|
|
85
|
+
index: i,
|
|
86
|
+
item,
|
|
87
|
+
isLast: i === items.length - 1,
|
|
88
|
+
isOpen: openSet.has(i),
|
|
89
|
+
onToggle: toggle,
|
|
90
|
+
shouldAnimate: hasMounted,
|
|
91
|
+
size,
|
|
92
|
+
variant,
|
|
93
|
+
renderMode
|
|
94
|
+
},
|
|
95
|
+
(_a = item.id) != null ? _a : i
|
|
96
|
+
);
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
exports.Timeline = Timeline;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { HTMLAttributes, JSX, ReactNode } from 'react';
|
|
2
|
+
import type { Severity } from '../../constants/severity.types';
|
|
3
|
+
import type { FormatDateOptions } from '../../utils/date/formatDate';
|
|
4
|
+
export type TimelineContextMenuActionItem = {
|
|
5
|
+
type?: 'item';
|
|
6
|
+
label: ReactNode;
|
|
7
|
+
icon?: ReactNode;
|
|
8
|
+
onClick: () => void;
|
|
9
|
+
active?: boolean;
|
|
10
|
+
selected?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
variant?: 'default' | 'danger';
|
|
13
|
+
};
|
|
14
|
+
export type TimelineContextMenuHeaderItem = {
|
|
15
|
+
type: 'header';
|
|
16
|
+
label: ReactNode;
|
|
17
|
+
};
|
|
18
|
+
export type TimelineContextMenuItem = TimelineContextMenuActionItem | TimelineContextMenuHeaderItem;
|
|
19
|
+
export interface TimelineItem {
|
|
20
|
+
id?: string;
|
|
21
|
+
label: ReactNode;
|
|
22
|
+
content: ReactNode;
|
|
23
|
+
collapsible?: boolean;
|
|
24
|
+
severity?: Severity;
|
|
25
|
+
icon?: ReactNode;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* When set, renders a muted timestamp next to the label using the
|
|
29
|
+
* component's own date formatting (see `dateOptions`) instead of the
|
|
30
|
+
* consumer building one into `label`/`content` by hand.
|
|
31
|
+
*/
|
|
32
|
+
date?: Date | string | number;
|
|
33
|
+
/** Passed through to the internal `formatDate` call for `date`. */
|
|
34
|
+
dateOptions?: FormatDateOptions;
|
|
35
|
+
/**
|
|
36
|
+
* Renders a kebab menu next to the label (same pattern as `Table`'s row
|
|
37
|
+
* actions), instead of the consumer putting action buttons directly in
|
|
38
|
+
* `label` — those inflate the header's height and throw off its
|
|
39
|
+
* alignment with the marker dot.
|
|
40
|
+
*/
|
|
41
|
+
contextMenuItems?: TimelineContextMenuItem[];
|
|
42
|
+
}
|
|
43
|
+
type Size = 'sm' | 'md' | 'lg';
|
|
44
|
+
type Variant = 'default' | 'outlined';
|
|
45
|
+
type OpenIndexes = number[] | 'all';
|
|
46
|
+
type RenderMode = 'lazy' | 'eager';
|
|
47
|
+
export interface TimelineProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
48
|
+
items: TimelineItem[];
|
|
49
|
+
size?: Size;
|
|
50
|
+
variant?: Variant;
|
|
51
|
+
renderMode?: RenderMode;
|
|
52
|
+
/** Uncontrolled default */
|
|
53
|
+
defaultOpenIndexes?: OpenIndexes;
|
|
54
|
+
/** Controlled state */
|
|
55
|
+
openIndexes?: OpenIndexes;
|
|
56
|
+
/** Change callback */
|
|
57
|
+
onOpenIndexesChange?: (indexes: number[]) => void;
|
|
58
|
+
}
|
|
59
|
+
export declare function Timeline({ items, size, variant, renderMode, defaultOpenIndexes, openIndexes, onOpenIndexesChange, className, ...rest }: TimelineProps): JSX.Element;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useId, useState, useEffect, useMemo } from 'react';
|
|
4
|
+
import { TimelineRow } from './components/TimelineRow';
|
|
5
|
+
import styles from './Timeline.module.css';
|
|
6
|
+
|
|
7
|
+
function uniqSorted(nums) {
|
|
8
|
+
return Array.from(new Set(nums)).sort((a, b) => a - b);
|
|
9
|
+
}
|
|
10
|
+
function collapsibleIndexesOf(items) {
|
|
11
|
+
return items.reduce((acc, item, i) => {
|
|
12
|
+
if (item.collapsible) acc.push(i);
|
|
13
|
+
return acc;
|
|
14
|
+
}, []);
|
|
15
|
+
}
|
|
16
|
+
function normalizeOpenIndexes(indexes, items) {
|
|
17
|
+
const collapsible = collapsibleIndexesOf(items);
|
|
18
|
+
if (indexes === "all") {
|
|
19
|
+
return collapsible;
|
|
20
|
+
}
|
|
21
|
+
const collapsibleSet = new Set(collapsible);
|
|
22
|
+
return uniqSorted((indexes != null ? indexes : []).filter((n) => collapsibleSet.has(n)));
|
|
23
|
+
}
|
|
24
|
+
function Timeline({
|
|
25
|
+
items,
|
|
26
|
+
size = "md",
|
|
27
|
+
variant = "default",
|
|
28
|
+
renderMode = "lazy",
|
|
29
|
+
defaultOpenIndexes = [],
|
|
30
|
+
openIndexes,
|
|
31
|
+
onOpenIndexesChange,
|
|
32
|
+
className,
|
|
33
|
+
...rest
|
|
34
|
+
}) {
|
|
35
|
+
const uid = useId();
|
|
36
|
+
const isControlled = openIndexes !== void 0;
|
|
37
|
+
const [hasMounted, setHasMounted] = useState(false);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
setHasMounted(true);
|
|
40
|
+
}, []);
|
|
41
|
+
const [internalOpenIndexes, setInternalOpenIndexes] = useState(
|
|
42
|
+
() => normalizeOpenIndexes(defaultOpenIndexes, items)
|
|
43
|
+
);
|
|
44
|
+
const currentOpenIndexes = useMemo(
|
|
45
|
+
() => normalizeOpenIndexes(isControlled ? openIndexes : internalOpenIndexes, items),
|
|
46
|
+
[isControlled, openIndexes, internalOpenIndexes, items]
|
|
47
|
+
);
|
|
48
|
+
const openSet = useMemo(() => new Set(currentOpenIndexes), [currentOpenIndexes]);
|
|
49
|
+
function commit(nextIndexes) {
|
|
50
|
+
const normalized = normalizeOpenIndexes(nextIndexes, items);
|
|
51
|
+
if (isControlled) {
|
|
52
|
+
onOpenIndexesChange == null ? void 0 : onOpenIndexesChange(normalized);
|
|
53
|
+
} else {
|
|
54
|
+
setInternalOpenIndexes(normalized);
|
|
55
|
+
onOpenIndexesChange == null ? void 0 : onOpenIndexesChange(normalized);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function toggle(index) {
|
|
59
|
+
const item = items[index];
|
|
60
|
+
if (!item || !item.collapsible || item.disabled) return;
|
|
61
|
+
const isOpen = openSet.has(index);
|
|
62
|
+
if (isOpen) {
|
|
63
|
+
commit(currentOpenIndexes.filter((i) => i !== index));
|
|
64
|
+
} else {
|
|
65
|
+
commit([...currentOpenIndexes, index]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
...rest,
|
|
72
|
+
className: [styles.container, styles[size], className].filter(Boolean).join(" "),
|
|
73
|
+
children: items.map((item, i) => {
|
|
74
|
+
var _a;
|
|
75
|
+
return /* @__PURE__ */ jsx(
|
|
76
|
+
TimelineRow,
|
|
77
|
+
{
|
|
78
|
+
uid,
|
|
79
|
+
index: i,
|
|
80
|
+
item,
|
|
81
|
+
isLast: i === items.length - 1,
|
|
82
|
+
isOpen: openSet.has(i),
|
|
83
|
+
onToggle: toggle,
|
|
84
|
+
shouldAnimate: hasMounted,
|
|
85
|
+
size,
|
|
86
|
+
variant,
|
|
87
|
+
renderMode
|
|
88
|
+
},
|
|
89
|
+
(_a = item.id) != null ? _a : i
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { Timeline };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.sm {
|
|
7
|
+
--tl-gap: var(--spacing-xs);
|
|
8
|
+
--tl-row-gap: var(--spacing-sm);
|
|
9
|
+
--tl-font-size: var(--font-size-sm);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.md {
|
|
13
|
+
--tl-gap: var(--spacing-sm);
|
|
14
|
+
--tl-row-gap: var(--spacing-md);
|
|
15
|
+
--tl-font-size: var(--font-size-md);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.lg {
|
|
19
|
+
--tl-gap: var(--spacing-sm);
|
|
20
|
+
--tl-row-gap: var(--spacing-lg);
|
|
21
|
+
--tl-font-size: var(--font-size-md);
|
|
22
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var lucideReact = require('lucide-react');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var Button = require('../../../components/button/Button');
|
|
7
|
+
var Circle = require('../../../components/circle/Circle');
|
|
8
|
+
var Menu = require('../../../components/menu/Menu');
|
|
9
|
+
var Popover = require('../../../components/popover/Popover');
|
|
10
|
+
var useCollapsibleHeight = require('../../../hooks/useCollapsibleHeight');
|
|
11
|
+
var formatDate = require('../../../utils/date/formatDate');
|
|
12
|
+
var styles = require('./TimelineRow.module.css');
|
|
13
|
+
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
var styles__default = /*#__PURE__*/_interopDefault(styles);
|
|
17
|
+
|
|
18
|
+
function TimelineRow({
|
|
19
|
+
uid,
|
|
20
|
+
index,
|
|
21
|
+
item,
|
|
22
|
+
isLast,
|
|
23
|
+
isOpen,
|
|
24
|
+
onToggle,
|
|
25
|
+
shouldAnimate = true,
|
|
26
|
+
variant = "default",
|
|
27
|
+
renderMode = "lazy",
|
|
28
|
+
size = "md"
|
|
29
|
+
}) {
|
|
30
|
+
var _a, _b;
|
|
31
|
+
const isDisabled = !!item.disabled;
|
|
32
|
+
const isCollapsible = !!item.collapsible;
|
|
33
|
+
const hasContextMenu = Boolean((_a = item.contextMenuItems) == null ? void 0 : _a.length);
|
|
34
|
+
const buttonId = `${uid}-tl-btn-${index}`;
|
|
35
|
+
const panelId = `${uid}-tl-panel-${index}`;
|
|
36
|
+
const { innerRef, height, onTransitionEnd } = useCollapsibleHeight.useCollapsibleHeight(
|
|
37
|
+
isCollapsible ? isOpen : true,
|
|
38
|
+
shouldAnimate
|
|
39
|
+
);
|
|
40
|
+
const [hasBeenOpened, setHasBeenOpened] = react.useState(isOpen);
|
|
41
|
+
const shouldRenderContent = !isCollapsible || renderMode === "eager" || isOpen || hasBeenOpened;
|
|
42
|
+
const contentClassName = [styles__default.default.content, variant === "outlined" ? styles__default.default.outlined : ""].filter(Boolean).join(" ");
|
|
43
|
+
function handleToggle() {
|
|
44
|
+
if (!isOpen) {
|
|
45
|
+
setHasBeenOpened(true);
|
|
46
|
+
}
|
|
47
|
+
onToggle(index);
|
|
48
|
+
}
|
|
49
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
50
|
+
"div",
|
|
51
|
+
{
|
|
52
|
+
className: `${styles__default.default.row} ${isDisabled ? styles__default.default.disabled : ""}`,
|
|
53
|
+
"data-state": isCollapsible ? isOpen ? "open" : "closed" : void 0,
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.markerColumn, children: [
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.markerSlot, children: item.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.icon, children: item.icon }) : /* @__PURE__ */ jsxRuntime.jsx(Circle.Circle, { severity: (_b = item.severity) != null ? _b : "neutral", size }) }),
|
|
57
|
+
!isLast ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.connector, "aria-hidden": "true" }) : null
|
|
58
|
+
] }),
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.body, children: [
|
|
60
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles__default.default.headerRow} ${hasContextMenu ? styles__default.default.headerRowWithActions : ""}`, children: [
|
|
61
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.label, children: item.label }),
|
|
62
|
+
item.date !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.date, children: formatDate.formatDate(item.date, { userFriendlyLabel: true, ...item.dateOptions }) }) : null,
|
|
63
|
+
isCollapsible ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
type: "button",
|
|
67
|
+
id: buttonId,
|
|
68
|
+
className: styles__default.default.trigger,
|
|
69
|
+
"aria-expanded": isOpen,
|
|
70
|
+
"aria-controls": panelId,
|
|
71
|
+
"aria-label": isOpen ? "Collapse" : "Expand",
|
|
72
|
+
onClick: handleToggle,
|
|
73
|
+
disabled: isDisabled,
|
|
74
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${styles__default.default.chevron} ${isOpen ? styles__default.default.open : ""}`, "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, {}) })
|
|
75
|
+
}
|
|
76
|
+
) : null,
|
|
77
|
+
hasContextMenu ? /* @__PURE__ */ jsxRuntime.jsx(TimelineRowActions, { items: item.contextMenuItems }) : null
|
|
78
|
+
] }),
|
|
79
|
+
isCollapsible ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
"div",
|
|
81
|
+
{
|
|
82
|
+
id: panelId,
|
|
83
|
+
role: "region",
|
|
84
|
+
"aria-labelledby": buttonId,
|
|
85
|
+
className: `${styles__default.default.panel} ${shouldAnimate ? styles__default.default.animate : styles__default.default.noAnimate}`,
|
|
86
|
+
style: { height },
|
|
87
|
+
onTransitionEnd,
|
|
88
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: innerRef, className: contentClassName, children: shouldRenderContent ? item.content : null })
|
|
89
|
+
}
|
|
90
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: contentClassName, children: item.content })
|
|
91
|
+
] })
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function TimelineRowActions({ items }) {
|
|
97
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.contextMenuOverlay, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
98
|
+
Popover.Popover,
|
|
99
|
+
{
|
|
100
|
+
matchTriggerWidth: false,
|
|
101
|
+
minWidth: "180px",
|
|
102
|
+
trigger: (toggle, _icon, isOpen) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
103
|
+
Button.Button,
|
|
104
|
+
{
|
|
105
|
+
type: "button",
|
|
106
|
+
size: "sm",
|
|
107
|
+
variant: "inline",
|
|
108
|
+
shape: "round",
|
|
109
|
+
className: styles__default.default.contextMenuTrigger,
|
|
110
|
+
"aria-label": "Flere handlinger",
|
|
111
|
+
"aria-haspopup": "menu",
|
|
112
|
+
"aria-expanded": isOpen ? true : false,
|
|
113
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, { size: 16 }),
|
|
114
|
+
onClick: toggle
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
children: (close) => /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu, { children: items.map(
|
|
118
|
+
(item, index) => item.type === "header" ? /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Header, { children: item.label }, index) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
Menu.Menu.Item,
|
|
120
|
+
{
|
|
121
|
+
active: item.active,
|
|
122
|
+
selected: item.selected,
|
|
123
|
+
disabled: item.disabled,
|
|
124
|
+
variant: item.variant,
|
|
125
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
|
+
"button",
|
|
127
|
+
{
|
|
128
|
+
type: "button",
|
|
129
|
+
onClick: () => {
|
|
130
|
+
item.onClick();
|
|
131
|
+
close();
|
|
132
|
+
},
|
|
133
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.contextMenuItemLabel, children: [
|
|
134
|
+
item.icon,
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.contextMenuItemText, children: item.label })
|
|
136
|
+
] })
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
},
|
|
140
|
+
index
|
|
141
|
+
)
|
|
142
|
+
) })
|
|
143
|
+
}
|
|
144
|
+
) });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
exports.TimelineRow = TimelineRow;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import { TimelineItem } from '../Timeline';
|
|
3
|
+
export type TimelineRowProps = {
|
|
4
|
+
uid: string;
|
|
5
|
+
index: number;
|
|
6
|
+
item: TimelineItem;
|
|
7
|
+
isLast: boolean;
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onToggle: (index: number) => void;
|
|
10
|
+
shouldAnimate?: boolean;
|
|
11
|
+
variant?: 'default' | 'outlined';
|
|
12
|
+
renderMode?: 'lazy' | 'eager';
|
|
13
|
+
size?: 'sm' | 'md' | 'lg';
|
|
14
|
+
};
|
|
15
|
+
export declare function TimelineRow({ uid, index, item, isLast, isOpen, onToggle, shouldAnimate, variant, renderMode, size, }: TimelineRowProps): JSX.Element;
|