@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.
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 +29 -21
  7. package/dist/components/button-select/ButtonSelect.d.ts +14 -4
  8. package/dist/components/button-select/ButtonSelect.js +29 -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 +24 -6
  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
@@ -0,0 +1,141 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { ChevronDown, MoreVertical } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { Button } from '../../../components/button/Button';
5
+ import { Circle } from '../../../components/circle/Circle';
6
+ import { Menu } from '../../../components/menu/Menu';
7
+ import { Popover } from '../../../components/popover/Popover';
8
+ import { useCollapsibleHeight } from '../../../hooks/useCollapsibleHeight';
9
+ import { formatDate } from '../../../utils/date/formatDate';
10
+ import styles from './TimelineRow.module.css';
11
+
12
+ function TimelineRow({
13
+ uid,
14
+ index,
15
+ item,
16
+ isLast,
17
+ isOpen,
18
+ onToggle,
19
+ shouldAnimate = true,
20
+ variant = "default",
21
+ renderMode = "lazy",
22
+ size = "md"
23
+ }) {
24
+ var _a, _b;
25
+ const isDisabled = !!item.disabled;
26
+ const isCollapsible = !!item.collapsible;
27
+ const hasContextMenu = Boolean((_a = item.contextMenuItems) == null ? void 0 : _a.length);
28
+ const buttonId = `${uid}-tl-btn-${index}`;
29
+ const panelId = `${uid}-tl-panel-${index}`;
30
+ const { innerRef, height, onTransitionEnd } = useCollapsibleHeight(
31
+ isCollapsible ? isOpen : true,
32
+ shouldAnimate
33
+ );
34
+ const [hasBeenOpened, setHasBeenOpened] = useState(isOpen);
35
+ const shouldRenderContent = !isCollapsible || renderMode === "eager" || isOpen || hasBeenOpened;
36
+ const contentClassName = [styles.content, variant === "outlined" ? styles.outlined : ""].filter(Boolean).join(" ");
37
+ function handleToggle() {
38
+ if (!isOpen) {
39
+ setHasBeenOpened(true);
40
+ }
41
+ onToggle(index);
42
+ }
43
+ return /* @__PURE__ */ jsxs(
44
+ "div",
45
+ {
46
+ className: `${styles.row} ${isDisabled ? styles.disabled : ""}`,
47
+ "data-state": isCollapsible ? isOpen ? "open" : "closed" : void 0,
48
+ children: [
49
+ /* @__PURE__ */ jsxs("div", { className: styles.markerColumn, children: [
50
+ /* @__PURE__ */ jsx("span", { className: styles.markerSlot, children: item.icon ? /* @__PURE__ */ jsx("span", { className: styles.icon, children: item.icon }) : /* @__PURE__ */ jsx(Circle, { severity: (_b = item.severity) != null ? _b : "neutral", size }) }),
51
+ !isLast ? /* @__PURE__ */ jsx("span", { className: styles.connector, "aria-hidden": "true" }) : null
52
+ ] }),
53
+ /* @__PURE__ */ jsxs("div", { className: styles.body, children: [
54
+ /* @__PURE__ */ jsxs("div", { className: `${styles.headerRow} ${hasContextMenu ? styles.headerRowWithActions : ""}`, children: [
55
+ /* @__PURE__ */ jsx("div", { className: styles.label, children: item.label }),
56
+ item.date !== void 0 ? /* @__PURE__ */ jsx("span", { className: styles.date, children: formatDate(item.date, { userFriendlyLabel: true, ...item.dateOptions }) }) : null,
57
+ isCollapsible ? /* @__PURE__ */ jsx(
58
+ "button",
59
+ {
60
+ type: "button",
61
+ id: buttonId,
62
+ className: styles.trigger,
63
+ "aria-expanded": isOpen,
64
+ "aria-controls": panelId,
65
+ "aria-label": isOpen ? "Collapse" : "Expand",
66
+ onClick: handleToggle,
67
+ disabled: isDisabled,
68
+ children: /* @__PURE__ */ jsx("span", { className: `${styles.chevron} ${isOpen ? styles.open : ""}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
69
+ }
70
+ ) : null,
71
+ hasContextMenu ? /* @__PURE__ */ jsx(TimelineRowActions, { items: item.contextMenuItems }) : null
72
+ ] }),
73
+ isCollapsible ? /* @__PURE__ */ jsx(
74
+ "div",
75
+ {
76
+ id: panelId,
77
+ role: "region",
78
+ "aria-labelledby": buttonId,
79
+ className: `${styles.panel} ${shouldAnimate ? styles.animate : styles.noAnimate}`,
80
+ style: { height },
81
+ onTransitionEnd,
82
+ children: /* @__PURE__ */ jsx("div", { ref: innerRef, className: contentClassName, children: shouldRenderContent ? item.content : null })
83
+ }
84
+ ) : /* @__PURE__ */ jsx("div", { className: contentClassName, children: item.content })
85
+ ] })
86
+ ]
87
+ }
88
+ );
89
+ }
90
+ function TimelineRowActions({ items }) {
91
+ return /* @__PURE__ */ jsx("div", { className: styles.contextMenuOverlay, children: /* @__PURE__ */ jsx(
92
+ Popover,
93
+ {
94
+ matchTriggerWidth: false,
95
+ minWidth: "180px",
96
+ trigger: (toggle, _icon, isOpen) => /* @__PURE__ */ jsx(
97
+ Button,
98
+ {
99
+ type: "button",
100
+ size: "sm",
101
+ variant: "inline",
102
+ shape: "round",
103
+ className: styles.contextMenuTrigger,
104
+ "aria-label": "Flere handlinger",
105
+ "aria-haspopup": "menu",
106
+ "aria-expanded": isOpen ? true : false,
107
+ icon: /* @__PURE__ */ jsx(MoreVertical, { size: 16 }),
108
+ onClick: toggle
109
+ }
110
+ ),
111
+ children: (close) => /* @__PURE__ */ jsx(Menu, { children: items.map(
112
+ (item, index) => item.type === "header" ? /* @__PURE__ */ jsx(Menu.Header, { children: item.label }, index) : /* @__PURE__ */ jsx(
113
+ Menu.Item,
114
+ {
115
+ active: item.active,
116
+ selected: item.selected,
117
+ disabled: item.disabled,
118
+ variant: item.variant,
119
+ children: /* @__PURE__ */ jsx(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ onClick: () => {
124
+ item.onClick();
125
+ close();
126
+ },
127
+ children: /* @__PURE__ */ jsxs("span", { className: styles.contextMenuItemLabel, children: [
128
+ item.icon,
129
+ /* @__PURE__ */ jsx("span", { className: styles.contextMenuItemText, children: item.label })
130
+ ] })
131
+ }
132
+ )
133
+ },
134
+ index
135
+ )
136
+ ) })
137
+ }
138
+ ) });
139
+ }
140
+
141
+ export { TimelineRow };
@@ -0,0 +1,195 @@
1
+ .row {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ gap: var(--tl-gap);
5
+ /* Scopes the marker column and the body to the same font-size, so their
6
+ em-based min-heights (see .markerSlot and .headerRow) agree and the
7
+ marker dot lines up with the header row instead of floating above it. */
8
+ font-size: var(--tl-font-size);
9
+ }
10
+
11
+ .markerColumn {
12
+ display: flex;
13
+ flex-direction: column;
14
+ align-items: center;
15
+ align-self: stretch;
16
+ flex: 0 0 auto;
17
+ }
18
+
19
+ .markerSlot {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ min-height: 1.5em;
24
+ flex: 0 0 auto;
25
+ }
26
+
27
+ .icon {
28
+ display: inline-flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ }
32
+
33
+ .icon svg {
34
+ width: var(--icon-size-sm);
35
+ height: var(--icon-size-sm);
36
+ }
37
+
38
+ .connector {
39
+ flex: 1 1 auto;
40
+ width: var(--border-width-thin);
41
+ min-height: var(--spacing-sm);
42
+ margin-block-start: var(--spacing-2xs);
43
+ background-color: var(--color-border-subtle);
44
+ }
45
+
46
+ .body {
47
+ flex: 1 1 auto;
48
+ min-width: 0;
49
+ padding-block-end: var(--tl-row-gap);
50
+ font-size: var(--tl-font-size);
51
+ }
52
+
53
+ .disabled {
54
+ opacity: 0.5;
55
+ }
56
+
57
+ .headerRow {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: var(--spacing-xs);
61
+ min-height: 1.5em;
62
+ position: relative;
63
+ }
64
+
65
+ .headerRowWithActions {
66
+ padding-inline-end: calc(var(--component-size-md) + 4px + var(--spacing-2xs));
67
+ }
68
+
69
+ .label {
70
+ font-weight: 600;
71
+ color: var(--color-fg-subtle);
72
+ flex: 1 1 auto;
73
+ min-width: 0;
74
+ }
75
+
76
+ .date {
77
+ font-size: var(--font-size-xs);
78
+ font-weight: 400;
79
+ color: var(--color-fg-muted);
80
+ white-space: nowrap;
81
+ flex: 0 0 auto;
82
+ }
83
+
84
+ .trigger {
85
+ all: unset;
86
+ box-sizing: border-box;
87
+ cursor: pointer;
88
+ display: inline-flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+ flex: 0 0 auto;
92
+ }
93
+
94
+ .trigger:focus-visible {
95
+ outline: none;
96
+ box-shadow: var(--focus-ring);
97
+ border-radius: var(--border-radius-sm);
98
+ }
99
+
100
+ .disabled .trigger {
101
+ cursor: not-allowed;
102
+ }
103
+
104
+ .chevron {
105
+ width: var(--icon-size-sm);
106
+ height: var(--icon-size-sm);
107
+ flex: 0 0 auto;
108
+ transition: transform var(--transition-normal) var(--ease-standard);
109
+ }
110
+
111
+ .chevron svg {
112
+ width: 100%;
113
+ height: 100%;
114
+ display: block;
115
+ }
116
+
117
+ .chevron.open {
118
+ transform: rotate(180deg);
119
+ }
120
+
121
+ .panel {
122
+ overflow: hidden;
123
+ height: 0px;
124
+ }
125
+
126
+ .animate {
127
+ transition: height var(--transition-slow) var(--ease-decelerate);
128
+ }
129
+
130
+ .noAnimate {
131
+ transition: none;
132
+ }
133
+
134
+ .content {
135
+ padding-block-start: var(--spacing-xs);
136
+ }
137
+
138
+ .content.outlined {
139
+ margin-block-start: var(--spacing-xs);
140
+ padding: var(--spacing-sm) var(--spacing-md);
141
+ border: 1px solid var(--color-border-subtle);
142
+ border-radius: var(--border-radius-md);
143
+ background-color: var(--color-bg-surface);
144
+ }
145
+
146
+ /*
147
+ * Absolutely positioned (like Table's row actions) so the kebab trigger
148
+ * never contributes to .headerRow's height - it would otherwise be much
149
+ * taller than a text line and throw off the marker dot's alignment.
150
+ */
151
+ .contextMenuOverlay {
152
+ position: absolute;
153
+ inset-block-start: 50%;
154
+ inset-inline-end: 0;
155
+ transform: translateY(-50%);
156
+ display: inline-flex;
157
+ align-items: center;
158
+ }
159
+
160
+ /*
161
+ * Popover's own wrapper divs (.container/.triggerSlot) default to
162
+ * align-items: stretch and height: 100%, meant for a trigger that should
163
+ * fill a fixed-height row. Overridden here (rather than in Popover.module.css)
164
+ * so the kebab button sizes itself instead of stretching, without affecting
165
+ * other Popover consumers.
166
+ */
167
+ .contextMenuOverlay > div,
168
+ .contextMenuOverlay > div > div {
169
+ align-items: center;
170
+ height: auto;
171
+ }
172
+
173
+ .contextMenuTrigger {
174
+ color: var(--color-fg-subtle);
175
+ min-inline-size: calc(var(--component-size-md) + 4px);
176
+ min-block-size: calc(var(--component-size-md) + 4px);
177
+ border-radius: var(--border-radius-rounded);
178
+ }
179
+
180
+ .contextMenuItemLabel {
181
+ display: inline-flex;
182
+ align-items: flex-start;
183
+ gap: var(--spacing-xs);
184
+ min-width: 0;
185
+ }
186
+
187
+ .contextMenuItemText {
188
+ min-width: 0;
189
+ }
190
+
191
+ @media (prefers-reduced-motion: reduce) {
192
+ .panel {
193
+ transition: none;
194
+ }
195
+ }
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ function useCollapsibleHeight(isOpen, shouldAnimate) {
6
+ const innerRef = react.useRef(null);
7
+ const [height, setHeight] = react.useState("0px");
8
+ const prevIsOpenRef = react.useRef(isOpen);
9
+ react.useLayoutEffect(() => {
10
+ var _a, _b;
11
+ const el = innerRef.current;
12
+ if (!el) return;
13
+ const prevIsOpen = prevIsOpenRef.current;
14
+ prevIsOpenRef.current = isOpen;
15
+ const isOpenChanged = prevIsOpen !== isOpen;
16
+ const prefersReduced = typeof window !== "undefined" && ((_b = (_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)")) == null ? void 0 : _b.matches);
17
+ if (!shouldAnimate || prefersReduced || !isOpenChanged) {
18
+ setHeight(isOpen ? "auto" : "0px");
19
+ return;
20
+ }
21
+ if (isOpen) {
22
+ setHeight("0px");
23
+ requestAnimationFrame(() => {
24
+ setHeight(`${el.scrollHeight}px`);
25
+ });
26
+ } else {
27
+ setHeight(`${el.scrollHeight}px`);
28
+ requestAnimationFrame(() => {
29
+ setHeight("0px");
30
+ });
31
+ }
32
+ }, [isOpen, shouldAnimate]);
33
+ function onTransitionEnd(e) {
34
+ if (e.propertyName !== "height") return;
35
+ if (!innerRef.current) return;
36
+ if (isOpen) setHeight("auto");
37
+ }
38
+ return { innerRef, height, onTransitionEnd };
39
+ }
40
+
41
+ exports.useCollapsibleHeight = useCollapsibleHeight;
@@ -0,0 +1,6 @@
1
+ import { type TransitionEvent } from 'react';
2
+ export declare function useCollapsibleHeight(isOpen: boolean, shouldAnimate: boolean): {
3
+ innerRef: import("react").RefObject<HTMLDivElement | null>;
4
+ height: string;
5
+ onTransitionEnd: (e: TransitionEvent<HTMLDivElement>) => void;
6
+ };
@@ -0,0 +1,39 @@
1
+ import { useRef, useState, useLayoutEffect } from 'react';
2
+
3
+ function useCollapsibleHeight(isOpen, shouldAnimate) {
4
+ const innerRef = useRef(null);
5
+ const [height, setHeight] = useState("0px");
6
+ const prevIsOpenRef = useRef(isOpen);
7
+ useLayoutEffect(() => {
8
+ var _a, _b;
9
+ const el = innerRef.current;
10
+ if (!el) return;
11
+ const prevIsOpen = prevIsOpenRef.current;
12
+ prevIsOpenRef.current = isOpen;
13
+ const isOpenChanged = prevIsOpen !== isOpen;
14
+ const prefersReduced = typeof window !== "undefined" && ((_b = (_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)")) == null ? void 0 : _b.matches);
15
+ if (!shouldAnimate || prefersReduced || !isOpenChanged) {
16
+ setHeight(isOpen ? "auto" : "0px");
17
+ return;
18
+ }
19
+ if (isOpen) {
20
+ setHeight("0px");
21
+ requestAnimationFrame(() => {
22
+ setHeight(`${el.scrollHeight}px`);
23
+ });
24
+ } else {
25
+ setHeight(`${el.scrollHeight}px`);
26
+ requestAnimationFrame(() => {
27
+ setHeight("0px");
28
+ });
29
+ }
30
+ }, [isOpen, shouldAnimate]);
31
+ function onTransitionEnd(e) {
32
+ if (e.propertyName !== "height") return;
33
+ if (!innerRef.current) return;
34
+ if (isOpen) setHeight("auto");
35
+ }
36
+ return { innerRef, height, onTransitionEnd };
37
+ }
38
+
39
+ export { useCollapsibleHeight };
@@ -16,7 +16,15 @@ function useViewportFill(ref, { bottomOffset = 0, min = 120, includeMarginTop =
16
16
  const marginTop = parseFloat(window.getComputedStyle(el).marginTop || "0") || 0;
17
17
  top -= marginTop;
18
18
  }
19
- const next = Math.max(min, Math.floor(window.innerHeight - bottomOffset - top));
19
+ let ancestorBottomSpacing = 0;
20
+ for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
21
+ const style2 = window.getComputedStyle(ancestor);
22
+ ancestorBottomSpacing += (parseFloat(style2.paddingBottom) || 0) + (parseFloat(style2.borderBottomWidth) || 0) + (parseFloat(style2.marginBottom) || 0);
23
+ }
24
+ const next = Math.max(
25
+ min,
26
+ Math.floor(window.innerHeight - bottomOffset - ancestorBottomSpacing - top)
27
+ );
20
28
  setMaxHeight((prev) => prev === next ? prev : next);
21
29
  }, [ref, bottomOffset, min, includeMarginTop]);
22
30
  const scheduleMeasure = react.useCallback(() => {
@@ -47,8 +55,8 @@ function useViewportFill(ref, { bottomOffset = 0, min = 120, includeMarginTop =
47
55
  if ("ResizeObserver" in window) {
48
56
  resizeObserver = new ResizeObserver(() => scheduleMeasure());
49
57
  resizeObserver.observe(target);
50
- if (parent && parent !== target) {
51
- resizeObserver.observe(parent);
58
+ for (let ancestor = parent; ancestor; ancestor = ancestor.parentElement) {
59
+ resizeObserver.observe(ancestor);
52
60
  }
53
61
  if (extra && extra !== target && extra !== parent) {
54
62
  resizeObserver.observe(extra);
@@ -14,7 +14,15 @@ function useViewportFill(ref, { bottomOffset = 0, min = 120, includeMarginTop =
14
14
  const marginTop = parseFloat(window.getComputedStyle(el).marginTop || "0") || 0;
15
15
  top -= marginTop;
16
16
  }
17
- const next = Math.max(min, Math.floor(window.innerHeight - bottomOffset - top));
17
+ let ancestorBottomSpacing = 0;
18
+ for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
19
+ const style2 = window.getComputedStyle(ancestor);
20
+ ancestorBottomSpacing += (parseFloat(style2.paddingBottom) || 0) + (parseFloat(style2.borderBottomWidth) || 0) + (parseFloat(style2.marginBottom) || 0);
21
+ }
22
+ const next = Math.max(
23
+ min,
24
+ Math.floor(window.innerHeight - bottomOffset - ancestorBottomSpacing - top)
25
+ );
18
26
  setMaxHeight((prev) => prev === next ? prev : next);
19
27
  }, [ref, bottomOffset, min, includeMarginTop]);
20
28
  const scheduleMeasure = useCallback(() => {
@@ -45,8 +53,8 @@ function useViewportFill(ref, { bottomOffset = 0, min = 120, includeMarginTop =
45
53
  if ("ResizeObserver" in window) {
46
54
  resizeObserver = new ResizeObserver(() => scheduleMeasure());
47
55
  resizeObserver.observe(target);
48
- if (parent && parent !== target) {
49
- resizeObserver.observe(parent);
56
+ for (let ancestor = parent; ancestor; ancestor = ancestor.parentElement) {
57
+ resizeObserver.observe(ancestor);
50
58
  }
51
59
  if (extra && extra !== target && extra !== parent) {
52
60
  resizeObserver.observe(extra);
package/dist/index.cjs CHANGED
@@ -11,6 +11,7 @@ var SectionLabel = require('./components/section-label/SectionLabel');
11
11
  var PageLayout = require('./components/page-layout/PageLayout');
12
12
  var Footer = require('./components/page-layout/components/footer/Footer');
13
13
  var Card = require('./components/card/Card');
14
+ var printSelection = require('./components/print-friendly/printSelection');
14
15
  var CardContainer = require('./components/card-container/CardContainer');
15
16
  var SkeletonLoaderItem = require('./components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem');
16
17
  var SkeletonLoader = require('./components/skeleton-loader/SkeletonLoader');
@@ -116,6 +117,12 @@ Object.keys(Card).forEach(function (k) {
116
117
  get: function () { return Card[k]; }
117
118
  });
118
119
  });
120
+ Object.keys(printSelection).forEach(function (k) {
121
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
122
+ enumerable: true,
123
+ get: function () { return printSelection[k]; }
124
+ });
125
+ });
119
126
  Object.keys(CardContainer).forEach(function (k) {
120
127
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
121
128
  enumerable: true,
package/dist/index.css CHANGED
@@ -72,6 +72,8 @@
72
72
  @import './components/table/components/column-resizer/ColumnResizer.module.css';
73
73
  @import './components/table/components/empty-state/EmptyState.module.css';
74
74
  @import './components/tabs/Tabs.module.css';
75
+ @import './components/timeline/Timeline.module.css';
76
+ @import './components/timeline/components/TimelineRow.module.css';
75
77
  @import './components/toast/Toast.module.css';
76
78
  @import './components/tree-view/TreeView.module.css';
77
79
  @import './components/user-display/UserDisplay.module.css';
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from './components/section-label/SectionLabel';
9
9
  export * from './components/page-layout/PageLayout';
10
10
  export * from './components/page-layout/components/footer/Footer';
11
11
  export * from './components/card/Card';
12
+ export * from './components/print-friendly/printSelection';
12
13
  export * from './components/card-container/CardContainer';
13
14
  export * from './components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
14
15
  export * from './components/skeleton-loader/SkeletonLoader';
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export * from './components/section-label/SectionLabel';
9
9
  export * from './components/page-layout/PageLayout';
10
10
  export * from './components/page-layout/components/footer/Footer';
11
11
  export * from './components/card/Card';
12
+ export * from './components/print-friendly/printSelection';
12
13
  export * from './components/card-container/CardContainer';
13
14
  export * from './components/skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
14
15
  export * from './components/skeleton-loader/SkeletonLoader';
@@ -99,11 +99,14 @@ body.dbc-app {
99
99
  max-width: var(--card-label-width);
100
100
  overflow: hidden;
101
101
  text-overflow: ellipsis;
102
- font-size: var(--font-size-xs);
102
+ font-size: var(--font-size-table-header);
103
103
  line-height: var(--line-height-table-header);
104
104
  color: var(--table-header-fg);
105
105
  letter-spacing: var(--letter-spacing-wide);
106
106
  font-weight: normal;
107
+ /* A bit more breathing room between the label and its value than the
108
+ shared th/td padding-inline gives on its own. */
109
+ padding-right: var(--spacing-xs);
107
110
  }
108
111
 
109
112
  .dbc-table td {
@@ -233,7 +236,7 @@ tr.dbc-table--hover:hover td:last-child {
233
236
  }
234
237
 
235
238
  .dbc-sm-text {
236
- font-size: var(--font-size-xs);
239
+ font-size: var(--font-size-sm);
237
240
  }
238
241
 
239
242
  .dbc-md-text {
@@ -379,3 +382,72 @@ tr.dbc-table--hover:hover td:last-child {
379
382
  clip: rect(0, 0, 0, 0);
380
383
  border: 0;
381
384
  }
385
+
386
+ /* ==========================================================================
387
+ Print
388
+ ========================================================================== */
389
+
390
+ @media print {
391
+ /* --- What to print (only content explicitly opted in via
392
+ printSelection()'s NOT_PRINTABLE_ATTRIBUTE gate, or a Modal) --- */
393
+ body[data-not-printable='true'] > *:not([data-not-printable='false']):not([role='dialog']) {
394
+ display: none !important;
395
+ }
396
+ body [data-not-printable='true'] {
397
+ display: none !important;
398
+ }
399
+
400
+ /* --- Page setup --- */
401
+ @page {
402
+ size: A4;
403
+ margin: 12mm;
404
+ }
405
+
406
+ html,
407
+ body {
408
+ height: auto !important; /* avoid 100vh/100% truncation */
409
+ overflow: visible !important; /* allow pagination */
410
+ }
411
+ * {
412
+ overflow: visible !important;
413
+ transform: none !important;
414
+ }
415
+
416
+ /* Modal (components/overlay/modal/Modal) portals a position:fixed,
417
+ scroll-clipped overlay directly onto body, wrapping the actual
418
+ `role="dialog"` element. It has no class or attribute of its own to
419
+ target, but it's always body's direct child that contains the dialog,
420
+ so :has() reaches it. */
421
+ body > div:has([role='dialog']) {
422
+ position: static !important;
423
+ inset: auto !important;
424
+ overflow: visible !important;
425
+ padding: 0 !important;
426
+ }
427
+
428
+ [role='dialog'] {
429
+ position: static !important;
430
+ width: 100% !important;
431
+ max-width: none !important;
432
+ height: auto !important;
433
+ max-height: none !important;
434
+ overflow: visible !important;
435
+ transform: none !important;
436
+ contain: none !important;
437
+ box-shadow: none !important;
438
+ }
439
+
440
+ /* --- Page breaks --- */
441
+ /* Use this class on any element that should start on a new page. */
442
+ .dbc-page-break-before:not(:first-of-type) {
443
+ margin-top: 0 !important;
444
+ break-before: page !important;
445
+ page-break-before: always !important; /* legacy */
446
+ }
447
+ /* Utility to discourage breaking inside certain blocks. */
448
+ .dbc-avoid-break,
449
+ [role='dialog'] {
450
+ break-inside: auto !important;
451
+ page-break-inside: auto !important;
452
+ }
453
+ }