@dbcdk/react-components 0.0.184 → 0.0.186

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/client.cjs +28 -0
  2. package/dist/client.d.ts +4 -0
  3. package/dist/client.js +4 -0
  4. package/dist/components/accordion/components/AccordionRow.cjs +2 -36
  5. package/dist/components/accordion/components/AccordionRow.js +2 -36
  6. package/dist/components/button-select/ButtonSelect.cjs +30 -21
  7. package/dist/components/button-select/ButtonSelect.d.ts +8 -4
  8. package/dist/components/button-select/ButtonSelect.js +30 -21
  9. package/dist/components/page/Page.module.css +1 -0
  10. package/dist/components/pagination/Pagination.module.css +1 -0
  11. package/dist/components/panel/Panel.module.css +0 -1
  12. package/dist/components/popover/Popover.cjs +1 -1
  13. package/dist/components/popover/Popover.js +1 -1
  14. package/dist/components/print-friendly/PrintFriendlyCard.cjs +39 -0
  15. package/dist/components/print-friendly/PrintFriendlyCard.d.ts +23 -0
  16. package/dist/components/print-friendly/PrintFriendlyCard.js +37 -0
  17. package/dist/components/print-friendly/PrintFriendlyMetaBar.cjs +25 -0
  18. package/dist/components/print-friendly/PrintFriendlyMetaBar.d.ts +19 -0
  19. package/dist/components/print-friendly/PrintFriendlyMetaBar.js +23 -0
  20. package/dist/components/print-friendly/PrintFriendlyTable.cjs +51 -0
  21. package/dist/components/print-friendly/PrintFriendlyTable.d.ts +34 -0
  22. package/dist/components/print-friendly/PrintFriendlyTable.js +49 -0
  23. package/dist/components/print-friendly/components/AccumulationFooter.cjs +42 -0
  24. package/dist/components/print-friendly/components/AccumulationFooter.d.ts +9 -0
  25. package/dist/components/print-friendly/components/AccumulationFooter.js +40 -0
  26. package/dist/components/print-friendly/emptyValue.cjs +9 -0
  27. package/dist/components/print-friendly/emptyValue.d.ts +2 -0
  28. package/dist/components/print-friendly/emptyValue.js +6 -0
  29. package/dist/components/print-friendly/printSelection.cjs +14 -0
  30. package/dist/components/print-friendly/printSelection.d.ts +19 -0
  31. package/dist/components/print-friendly/printSelection.js +10 -0
  32. package/dist/components/stack/Stack.cjs +6 -5
  33. package/dist/components/stack/Stack.d.ts +2 -1
  34. package/dist/components/stack/Stack.js +6 -5
  35. package/dist/components/table/Table.cjs +20 -4
  36. package/dist/components/table/Table.js +21 -5
  37. package/dist/components/table/Table.module.css +6 -0
  38. package/dist/components/table/Table.types.d.ts +1 -0
  39. package/dist/components/table/components/TableHeader.cjs +1 -0
  40. package/dist/components/table/components/TableHeader.js +1 -0
  41. package/dist/components/tabs/Tabs.cjs +49 -39
  42. package/dist/components/tabs/Tabs.d.ts +3 -1
  43. package/dist/components/tabs/Tabs.js +49 -39
  44. package/dist/components/tabs/Tabs.module.css +43 -25
  45. package/dist/components/timeline/Timeline.cjs +102 -0
  46. package/dist/components/timeline/Timeline.d.ts +60 -0
  47. package/dist/components/timeline/Timeline.js +96 -0
  48. package/dist/components/timeline/Timeline.module.css +22 -0
  49. package/dist/components/timeline/components/TimelineRow.cjs +147 -0
  50. package/dist/components/timeline/components/TimelineRow.d.ts +15 -0
  51. package/dist/components/timeline/components/TimelineRow.js +141 -0
  52. package/dist/components/timeline/components/TimelineRow.module.css +195 -0
  53. package/dist/hooks/useCollapsibleHeight.cjs +41 -0
  54. package/dist/hooks/useCollapsibleHeight.d.ts +6 -0
  55. package/dist/hooks/useCollapsibleHeight.js +39 -0
  56. package/dist/hooks/useViewportFill.cjs +11 -3
  57. package/dist/hooks/useViewportFill.js +11 -3
  58. package/dist/index.cjs +7 -0
  59. package/dist/index.css +2 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +1 -0
  62. package/dist/styles/styles.css +74 -2
  63. package/dist/styles.css +74 -2
  64. package/dist/utils/date/formatDate.d.ts +3 -2
  65. 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("div", { className: styles.tabButtons, role: "tablist", "aria-label": header != null ? header : "Tabs", children: visibleTabs.map((tab, index) => {
147
- const selected = index === activeIndex;
148
- const tabDomId = `${uid}-tab-${String(tab.id)}`;
149
- const panelDomId = `${uid}-panel-${String(tab.id)}`;
150
- return /* @__PURE__ */ jsxs("div", { className: `${styles.tab} ${selected ? styles.active : ""}`, children: [
151
- /* @__PURE__ */ jsxs(
152
- "button",
153
- {
154
- id: tabDomId,
155
- type: "button",
156
- className: styles.tabButton,
157
- role: "tab",
158
- "aria-selected": selected,
159
- "aria-controls": panelDomId,
160
- tabIndex: selected ? 0 : -1,
161
- disabled: tab.disabled,
162
- onClick: () => setValue(tab.id),
163
- onKeyDown: (e) => onKeyDownTab(e, index),
164
- children: [
165
- tab.headerIcon ? /* @__PURE__ */ jsx("span", { className: styles.icon, children: tab.headerIcon }) : null,
166
- /* @__PURE__ */ jsx("span", { className: styles.label, children: tab.header }),
167
- tab.badge ? /* @__PURE__ */ jsx("span", { className: styles.badge, children: /* @__PURE__ */ jsx(Chip, { size: "sm", children: tab.badge.toLocaleString("da-DK") }) }) : null
168
- ]
169
- }
170
- ),
171
- tab.removable && onRemoveTab ? /* @__PURE__ */ jsx(
172
- "button",
173
- {
174
- type: "button",
175
- className: styles.removeTabButton,
176
- "aria-label": `Fjern fane ${tab.header}`,
177
- disabled: tab.disabled,
178
- onClick: () => onRemoveTab(tab.id, tab, index),
179
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
180
- }
181
- ) : null
182
- ] }, tab.id);
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;
@@ -199,26 +208,35 @@
199
208
  overflow: hidden;
200
209
  }
201
210
 
202
- .filled .tabList {
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
 
208
- .filled .tabButtons {
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. */
218
+ .filled > .tabList > .tabButtons {
209
219
  display: flex;
220
+ background: var(--color-bg-toolbar);
221
+ border-start-start-radius: var(--border-radius-md);
210
222
  }
211
223
 
212
- .filled .addTabButton {
224
+ .filled > .tabList > .tabButtons.tabButtonsEnd {
225
+ border-start-end-radius: var(--border-radius-md);
226
+ }
227
+
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
- .filled .addTabButton:hover {
235
+ .filled > .tabList > .addTabButton:hover {
218
236
  background: var(--color-bg-toolbar-hover);
219
237
  }
220
238
 
221
- .filled .tab {
239
+ .filled > .tabList > .tabButtons > .tab {
222
240
  border: 1px solid transparent;
223
241
  border-block-end: 0;
224
242
  color: var(--color-fg-muted);
@@ -229,14 +247,14 @@
229
247
  border-color var(--transition-fast) var(--ease-standard);
230
248
  }
231
249
 
232
- .filled .tab:not(.active):hover {
250
+ .filled > .tabList > .tabButtons > .tab:not(.active):hover {
233
251
  background: var(--color-bg-toolbar-hover);
234
252
  color: var(--color-fg-default);
235
253
  }
236
254
 
237
255
  /* Active tab physically overlaps the tabList separator by 1px so its
238
256
  erased bottom border seamlessly merges with the panel below. */
239
- .filled .tab.active {
257
+ .filled > .tabList > .tabButtons > .tab.active {
240
258
  background: var(--color-bg-surface);
241
259
  border-color: var(--color-border-subtle);
242
260
  border-block-end-color: var(--color-bg-surface);
@@ -247,13 +265,13 @@
247
265
  color: var(--color-fg-default);
248
266
  }
249
267
 
250
- .filled .tabContent {
268
+ .filled > .tabContent {
251
269
  background: var(--color-bg-surface);
252
270
  border: 1px solid var(--color-border-subtle);
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-lg);
274
+ padding: var(--spacing-md);
257
275
  }
258
276
 
259
277
  /* =========================
@@ -268,22 +286,22 @@
268
286
  padding-block-end: var(--spacing-xs);
269
287
  }
270
288
 
271
- .outlined .tab {
289
+ .outlined > .tabList > .tabButtons > .tab {
272
290
  color: var(--color-fg-muted);
273
291
  border-block-end: 2px solid var(--color-border-default);
274
292
  transition: color var(--transition-fast) var(--ease-standard);
275
293
  }
276
294
 
277
- .outlined .tab:not(.active):hover {
295
+ .outlined > .tabList > .tabButtons > .tab:not(.active):hover {
278
296
  color: var(--color-brand);
279
297
  }
280
298
 
281
- .outlined .tab.active {
299
+ .outlined > .tabList > .tabButtons > .tab.active {
282
300
  color: var(--color-brand);
283
301
  border-block-end-color: var(--color-brand);
284
302
  }
285
303
 
286
- .outlined .tabContent {
304
+ .outlined > .tabContent {
287
305
  background: transparent;
288
306
  padding: 0;
289
307
  }
@@ -292,22 +310,22 @@
292
310
  Panel style modifier
293
311
  ========================= */
294
312
 
295
- .panelStyle .tabList {
313
+ .panelStyle > .tabList {
296
314
  border: var(--border-width-thin) solid var(--color-border-default);
297
315
  border-block-end: 0;
298
316
  }
299
317
 
300
318
  /* When panelStyle is on, tabs look “embedded” (no bottom border indicators) */
301
- .panelStyle .tab {
319
+ .panelStyle > .tabList > .tabButtons > .tab {
302
320
  border-block-end: 0;
303
321
  }
304
322
 
305
323
  /* In filled mode, keep the filled content panel visuals (already handled above).
306
324
  In outlined mode with panelStyle, give the content a panel container too. */
307
- .panelStyle.outlined .tabContent {
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-lg);
328
+ padding: var(--spacing-md);
311
329
  }
312
330
 
313
331
  @media (max-width: 767px) {
@@ -327,17 +345,17 @@
327
345
  box-shadow: none;
328
346
  }
329
347
 
330
- .filled .tabList {
348
+ .filled > .tabList {
331
349
  inline-size: 100%;
332
350
  }
333
351
 
334
- .filled .tabContent,
335
- .panelStyle.outlined .tabContent {
336
- padding-block: var(--spacing-md);
337
- padding-inline: var(--spacing-md);
352
+ .filled > .tabContent,
353
+ .panelStyle.outlined > .tabContent {
354
+ padding-block: var(--spacing-sm);
355
+ padding-inline: var(--spacing-sm);
338
356
  }
339
357
 
340
- .panelStyle .tabList {
358
+ .panelStyle > .tabList {
341
359
  border: 0;
342
360
  }
343
361
  }
@@ -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
+ }