@dbcdk/react-components 0.0.190 → 0.0.192

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 (128) hide show
  1. package/dist/client.cjs +14 -0
  2. package/dist/client.d.ts +2 -0
  3. package/dist/client.js +2 -0
  4. package/dist/components/button-select/ButtonSelect.cjs +2 -2
  5. package/dist/components/button-select/ButtonSelect.d.ts +1 -0
  6. package/dist/components/button-select/ButtonSelect.js +2 -2
  7. package/dist/components/button-select/ButtonSelect.module.css +14 -0
  8. package/dist/components/chip/Chip.module.css +25 -2
  9. package/dist/components/forms/checkbox/Checkbox.cjs +6 -1
  10. package/dist/components/forms/checkbox/Checkbox.js +6 -1
  11. package/dist/components/forms/checkbox/Checkbox.module.css +26 -0
  12. package/dist/components/forms/input/Input.module.css +93 -0
  13. package/dist/components/forms/input-container/InputContainer.module.css +0 -7
  14. package/dist/components/grid/Grid.cjs +4 -2
  15. package/dist/components/grid/Grid.d.ts +2 -2
  16. package/dist/components/grid/Grid.js +4 -2
  17. package/dist/components/json-viewer/JsonViewer.cjs +2 -1
  18. package/dist/components/json-viewer/JsonViewer.js +3 -2
  19. package/dist/components/line-viewer/LineViewer.cjs +2 -1
  20. package/dist/components/line-viewer/LineViewer.js +3 -2
  21. package/dist/components/overlay/side-panel/SidePanel.module.css +3 -1
  22. package/dist/components/page-layout/PageLayout.module.css +12 -3
  23. package/dist/components/popover/Popover.cjs +2 -1
  24. package/dist/components/popover/Popover.d.ts +8 -1
  25. package/dist/components/popover/Popover.js +2 -1
  26. package/dist/components/search-box/SearchBox.cjs +10 -7
  27. package/dist/components/search-box/SearchBox.d.ts +7 -0
  28. package/dist/components/search-box/SearchBox.js +10 -7
  29. package/dist/components/sidebar/components/sidenav-filteirng/SidenavFiltering.cjs +2 -1
  30. package/dist/components/sidebar/components/sidenav-filteirng/SidenavFiltering.js +2 -1
  31. package/dist/components/tabs/Tabs.cjs +58 -56
  32. package/dist/components/tabs/Tabs.js +58 -56
  33. package/dist/components/tabs/Tabs.module.css +32 -16
  34. package/dist/components/timeline/Timeline.d.ts +1 -0
  35. package/dist/components/timeline/components/TimelineRow.cjs +1 -1
  36. package/dist/components/timeline/components/TimelineRow.js +1 -1
  37. package/dist/components/timeline/components/TimelineRow.module.css +4 -0
  38. package/dist/components/tree-view/TreeNodeRow.cjs +9 -1
  39. package/dist/components/tree-view/TreeNodeRow.d.ts +2 -1
  40. package/dist/components/tree-view/TreeNodeRow.js +9 -1
  41. package/dist/components/tree-view/TreeView.cjs +2 -0
  42. package/dist/components/tree-view/TreeView.d.ts +1 -1
  43. package/dist/components/tree-view/TreeView.js +2 -0
  44. package/dist/components/tree-view/TreeView.module.css +8 -0
  45. package/dist/components/tree-view/types.d.ts +2 -0
  46. package/dist/components/tree-view/useTreeExpansion.cjs +12 -2
  47. package/dist/components/tree-view/useTreeExpansion.d.ts +1 -0
  48. package/dist/components/tree-view/useTreeExpansion.js +12 -2
  49. package/dist/components/xml-viewer/XmlViewer.cjs +2 -1
  50. package/dist/components/xml-viewer/XmlViewer.js +3 -2
  51. package/dist/hooks/useStickyLayout.cjs +99 -0
  52. package/dist/hooks/useStickyLayout.d.ts +22 -0
  53. package/dist/hooks/useStickyLayout.js +97 -0
  54. package/dist/hooks/useTreeViewSettings.cjs +87 -0
  55. package/dist/hooks/useTreeViewSettings.d.ts +35 -0
  56. package/dist/hooks/useTreeViewSettings.js +84 -0
  57. package/dist/index.cjs +7 -0
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.js +1 -0
  60. package/dist/styles/css-helper-classes/scrollbar.css +10 -0
  61. package/dist/styles/styles.css +1 -21
  62. package/dist/styles/themes/dbc/base.css +2 -2
  63. package/dist/styles.css +1 -21
  64. package/package.json +1 -1
  65. package/dist/styles/fonts/Roboto/OFL.txt +0 -93
  66. package/dist/styles/fonts/Roboto/README.txt +0 -118
  67. package/dist/styles/fonts/Roboto/Roboto-Black.ttf +0 -0
  68. package/dist/styles/fonts/Roboto/Roboto-BlackItalic.ttf +0 -0
  69. package/dist/styles/fonts/Roboto/Roboto-Bold.ttf +0 -0
  70. package/dist/styles/fonts/Roboto/Roboto-BoldItalic.ttf +0 -0
  71. package/dist/styles/fonts/Roboto/Roboto-ExtraBold.ttf +0 -0
  72. package/dist/styles/fonts/Roboto/Roboto-ExtraBoldItalic.ttf +0 -0
  73. package/dist/styles/fonts/Roboto/Roboto-ExtraLight.ttf +0 -0
  74. package/dist/styles/fonts/Roboto/Roboto-ExtraLightItalic.ttf +0 -0
  75. package/dist/styles/fonts/Roboto/Roboto-Italic.ttf +0 -0
  76. package/dist/styles/fonts/Roboto/Roboto-Light.ttf +0 -0
  77. package/dist/styles/fonts/Roboto/Roboto-LightItalic.ttf +0 -0
  78. package/dist/styles/fonts/Roboto/Roboto-Medium.ttf +0 -0
  79. package/dist/styles/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  80. package/dist/styles/fonts/Roboto/Roboto-Regular.ttf +0 -0
  81. package/dist/styles/fonts/Roboto/Roboto-SemiBold.ttf +0 -0
  82. package/dist/styles/fonts/Roboto/Roboto-SemiBoldItalic.ttf +0 -0
  83. package/dist/styles/fonts/Roboto/Roboto-Thin.ttf +0 -0
  84. package/dist/styles/fonts/Roboto/Roboto-ThinItalic.ttf +0 -0
  85. package/dist/styles/fonts/Roboto/Roboto_Condensed-Black.ttf +0 -0
  86. package/dist/styles/fonts/Roboto/Roboto_Condensed-BlackItalic.ttf +0 -0
  87. package/dist/styles/fonts/Roboto/Roboto_Condensed-Bold.ttf +0 -0
  88. package/dist/styles/fonts/Roboto/Roboto_Condensed-BoldItalic.ttf +0 -0
  89. package/dist/styles/fonts/Roboto/Roboto_Condensed-ExtraBold.ttf +0 -0
  90. package/dist/styles/fonts/Roboto/Roboto_Condensed-ExtraBoldItalic.ttf +0 -0
  91. package/dist/styles/fonts/Roboto/Roboto_Condensed-ExtraLight.ttf +0 -0
  92. package/dist/styles/fonts/Roboto/Roboto_Condensed-ExtraLightItalic.ttf +0 -0
  93. package/dist/styles/fonts/Roboto/Roboto_Condensed-Italic.ttf +0 -0
  94. package/dist/styles/fonts/Roboto/Roboto_Condensed-Light.ttf +0 -0
  95. package/dist/styles/fonts/Roboto/Roboto_Condensed-LightItalic.ttf +0 -0
  96. package/dist/styles/fonts/Roboto/Roboto_Condensed-Medium.ttf +0 -0
  97. package/dist/styles/fonts/Roboto/Roboto_Condensed-MediumItalic.ttf +0 -0
  98. package/dist/styles/fonts/Roboto/Roboto_Condensed-Regular.ttf +0 -0
  99. package/dist/styles/fonts/Roboto/Roboto_Condensed-SemiBold.ttf +0 -0
  100. package/dist/styles/fonts/Roboto/Roboto_Condensed-SemiBoldItalic.ttf +0 -0
  101. package/dist/styles/fonts/Roboto/Roboto_Condensed-Thin.ttf +0 -0
  102. package/dist/styles/fonts/Roboto/Roboto_Condensed-ThinItalic.ttf +0 -0
  103. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Black.ttf +0 -0
  104. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-BlackItalic.ttf +0 -0
  105. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Bold.ttf +0 -0
  106. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-BoldItalic.ttf +0 -0
  107. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-ExtraBold.ttf +0 -0
  108. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  109. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-ExtraLight.ttf +0 -0
  110. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-ExtraLightItalic.ttf +0 -0
  111. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Italic.ttf +0 -0
  112. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Light.ttf +0 -0
  113. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-LightItalic.ttf +0 -0
  114. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Medium.ttf +0 -0
  115. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-MediumItalic.ttf +0 -0
  116. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Regular.ttf +0 -0
  117. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-SemiBold.ttf +0 -0
  118. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-SemiBoldItalic.ttf +0 -0
  119. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-Thin.ttf +0 -0
  120. package/dist/styles/fonts/Roboto/Roboto_SemiCondensed-ThinItalic.ttf +0 -0
  121. package/dist/styles/fonts/Tajawal/OFL.txt +0 -93
  122. package/dist/styles/fonts/Tajawal/Tajawal-Black.ttf +0 -0
  123. package/dist/styles/fonts/Tajawal/Tajawal-Bold.ttf +0 -0
  124. package/dist/styles/fonts/Tajawal/Tajawal-ExtraBold.ttf +0 -0
  125. package/dist/styles/fonts/Tajawal/Tajawal-ExtraLight.ttf +0 -0
  126. package/dist/styles/fonts/Tajawal/Tajawal-Light.ttf +0 -0
  127. package/dist/styles/fonts/Tajawal/Tajawal-Medium.ttf +0 -0
  128. package/dist/styles/fonts/Tajawal/Tajawal-Regular.ttf +0 -0
@@ -7,6 +7,7 @@ import styles from './TreeView.module.css';
7
7
  function TreeNodeRow({
8
8
  node,
9
9
  depth,
10
+ wrapLabels,
10
11
  isExpanded,
11
12
  toggle,
12
13
  resolvedChildren,
@@ -60,7 +61,13 @@ function TreeNodeRow({
60
61
  ),
61
62
  /* @__PURE__ */ jsxs("button", { type: "button", className: styles.labelButton, onClick: () => onSelectNode == null ? void 0 : onSelectNode(node), children: [
62
63
  node.icon ? /* @__PURE__ */ jsx("span", { className: styles.icon, "aria-hidden": "true", children: node.icon }) : null,
63
- /* @__PURE__ */ jsx("span", { className: styles.label, children: renderLabel ? renderLabel(node) : node.label })
64
+ /* @__PURE__ */ jsx(
65
+ "span",
66
+ {
67
+ className: [styles.label, wrapLabels ? styles.labelWrap : styles.labelTruncate].filter(Boolean).join(" "),
68
+ children: renderLabel ? renderLabel(node) : node.label
69
+ }
70
+ )
64
71
  ] })
65
72
  ] }),
66
73
  error ? /* @__PURE__ */ jsxs("div", { className: styles.errorRow, children: [
@@ -72,6 +79,7 @@ function TreeNodeRow({
72
79
  {
73
80
  node: child,
74
81
  depth: depth + 1,
82
+ wrapLabels,
75
83
  isExpanded,
76
84
  toggle,
77
85
  resolvedChildren,
@@ -21,6 +21,7 @@ function TreeView({
21
21
  selectedId,
22
22
  onSelectNode,
23
23
  renderLabel,
24
+ wrapLabels = true,
24
25
  emptyMessage = "Intet at vise.",
25
26
  loadErrorMessage = () => "Der skete en fejl under indl\xE6sning.",
26
27
  className,
@@ -67,6 +68,7 @@ function TreeView({
67
68
  {
68
69
  node,
69
70
  depth: 0,
71
+ wrapLabels,
70
72
  isExpanded,
71
73
  toggle,
72
74
  resolvedChildren,
@@ -17,4 +17,4 @@ export { collectExpandableIds, findAncestorIds } from './utils';
17
17
  * useTreeExpansion), and it can be left uncontrolled or owned by the
18
18
  * caller via `expandedIds`/`onExpandedIdsChange`.
19
19
  */
20
- export declare function TreeView<T = unknown>({ data, loadChildren, defaultExpandedIds, expandedIds, onExpandedIdsChange, selectedId, onSelectNode, renderLabel, emptyMessage, loadErrorMessage, className, ...rest }: TreeViewProps<T>): JSX.Element;
20
+ export declare function TreeView<T = unknown>({ data, loadChildren, defaultExpandedIds, expandedIds, onExpandedIdsChange, selectedId, onSelectNode, renderLabel, wrapLabels, emptyMessage, loadErrorMessage, className, ...rest }: TreeViewProps<T>): JSX.Element;
@@ -15,6 +15,7 @@ function TreeView({
15
15
  selectedId,
16
16
  onSelectNode,
17
17
  renderLabel,
18
+ wrapLabels = true,
18
19
  emptyMessage = "Intet at vise.",
19
20
  loadErrorMessage = () => "Der skete en fejl under indl\xE6sning.",
20
21
  className,
@@ -61,6 +62,7 @@ function TreeView({
61
62
  {
62
63
  node,
63
64
  depth: 0,
65
+ wrapLabels,
64
66
  isExpanded,
65
67
  toggle,
66
68
  resolvedChildren,
@@ -108,6 +108,14 @@
108
108
 
109
109
  .label {
110
110
  min-inline-size: 0;
111
+ }
112
+
113
+ .labelWrap {
114
+ overflow-wrap: anywhere;
115
+ white-space: normal;
116
+ }
117
+
118
+ .labelTruncate {
111
119
  overflow: hidden;
112
120
  text-overflow: ellipsis;
113
121
  white-space: nowrap;
@@ -36,6 +36,8 @@ export type TreeViewProps<T = unknown> = {
36
36
  onSelectNode?: (node: TreeNode<T>) => void;
37
37
  /** Customize how a node's row content renders; defaults to `node.icon` + `node.label`. */
38
38
  renderLabel?: (node: TreeNode<T>) => ReactNode;
39
+ /** Allows node labels to wrap onto multiple lines. Set to false to truncate them to one line. */
40
+ wrapLabels?: boolean;
39
41
  emptyMessage?: ReactNode;
40
42
  /** Formats the error shown under a node whose `loadChildren` call rejected. */
41
43
  loadErrorMessage?: (error: unknown, node: TreeNode<T>) => ReactNode;
@@ -38,10 +38,20 @@ function useTreeExpansion(defaultExpandedIds = [], controlledIds, onChange) {
38
38
  (id) => expandedIds.has(id) ? collapse(id) : expand(id),
39
39
  [expandedIds, expand, collapse]
40
40
  );
41
- const expandAll = react.useCallback((ids) => commit(new Set(ids)), [commit]);
41
+ const setExpandedIds = react.useCallback((ids) => commit(new Set(ids)), [commit]);
42
+ const expandAll = react.useCallback((ids) => setExpandedIds(ids), [setExpandedIds]);
42
43
  const collapseAll = react.useCallback(() => commit(/* @__PURE__ */ new Set()), [commit]);
43
44
  const isExpanded = react.useCallback((id) => expandedIds.has(id), [expandedIds]);
44
- return { expandedIds, expand, collapse, toggle, expandAll, collapseAll, isExpanded };
45
+ return {
46
+ expandedIds,
47
+ setExpandedIds,
48
+ expand,
49
+ collapse,
50
+ toggle,
51
+ expandAll,
52
+ collapseAll,
53
+ isExpanded
54
+ };
45
55
  }
46
56
 
47
57
  exports.useTreeExpansion = useTreeExpansion;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  export declare function useTreeExpansion(defaultExpandedIds?: string[], controlledIds?: string[], onChange?: (ids: string[]) => void): {
10
10
  expandedIds: Set<string>;
11
+ setExpandedIds: (ids: string[]) => void;
11
12
  expand: (id: string) => void;
12
13
  collapse: (id: string) => void;
13
14
  toggle: (id: string) => void;
@@ -36,10 +36,20 @@ function useTreeExpansion(defaultExpandedIds = [], controlledIds, onChange) {
36
36
  (id) => expandedIds.has(id) ? collapse(id) : expand(id),
37
37
  [expandedIds, expand, collapse]
38
38
  );
39
- const expandAll = useCallback((ids) => commit(new Set(ids)), [commit]);
39
+ const setExpandedIds = useCallback((ids) => commit(new Set(ids)), [commit]);
40
+ const expandAll = useCallback((ids) => setExpandedIds(ids), [setExpandedIds]);
40
41
  const collapseAll = useCallback(() => commit(/* @__PURE__ */ new Set()), [commit]);
41
42
  const isExpanded = useCallback((id) => expandedIds.has(id), [expandedIds]);
42
- return { expandedIds, expand, collapse, toggle, expandAll, collapseAll, isExpanded };
43
+ return {
44
+ expandedIds,
45
+ setExpandedIds,
46
+ expand,
47
+ collapse,
48
+ toggle,
49
+ expandAll,
50
+ collapseAll,
51
+ isExpanded
52
+ };
43
53
  }
44
54
 
45
55
  export { useTreeExpansion };
@@ -10,6 +10,7 @@ var usePersistentState = require('../../hooks/usePersistentState');
10
10
  var tokenizeXml = require('./tokenizeXml');
11
11
  var styles = require('./XmlViewer.module.css');
12
12
  var Button = require('../button/Button');
13
+ var Icon = require('../icon/Icon');
13
14
 
14
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
16
 
@@ -88,7 +89,7 @@ function XmlViewer({
88
89
  "aria-haspopup": "menu",
89
90
  "aria-expanded": isOpen ? true : false,
90
91
  onClick: toggle,
91
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, { "aria-hidden": "true" })
92
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { customIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { "aria-hidden": "true" }) })
92
93
  }
93
94
  ),
94
95
  children: (close) => /* @__PURE__ */ jsxRuntime.jsxs(Menu.Menu, { children: [
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { TextWrap, ListTree, Code2, Sun, Moon, ClipboardCopy, MoreVertical } from 'lucide-react';
3
+ import { TextWrap, ListTree, Code2, Sun, Moon, ClipboardCopy, Settings2 } from 'lucide-react';
4
4
  import { useMemo, useRef } from 'react';
5
5
  import { Menu } from '../../components/menu/Menu';
6
6
  import { Popover } from '../../components/popover/Popover';
@@ -8,6 +8,7 @@ import { usePersistentState } from '../../hooks/usePersistentState';
8
8
  import { tokenizeXml } from './tokenizeXml';
9
9
  import styles from './XmlViewer.module.css';
10
10
  import { Button } from '../button/Button';
11
+ import { Icon } from '../icon/Icon';
11
12
 
12
13
  const TOKEN_CLASS = {
13
14
  tagName: styles.tagName,
@@ -82,7 +83,7 @@ function XmlViewer({
82
83
  "aria-haspopup": "menu",
83
84
  "aria-expanded": isOpen ? true : false,
84
85
  onClick: toggle,
85
- children: /* @__PURE__ */ jsx(MoreVertical, { "aria-hidden": "true" })
86
+ children: /* @__PURE__ */ jsx(Icon, { customIcon: /* @__PURE__ */ jsx(Settings2, { "aria-hidden": "true" }) })
86
87
  }
87
88
  ),
88
89
  children: (close) => /* @__PURE__ */ jsxs(Menu, { children: [
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+
6
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
7
+ const getScrollport = (element) => {
8
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
9
+ if (parent === document.body || parent === document.documentElement) return null;
10
+ const { overflowY } = window.getComputedStyle(parent);
11
+ if (/^(auto|scroll|hidden|overlay)$/.test(overflowY)) return parent;
12
+ }
13
+ return null;
14
+ };
15
+ function useStickyLayout(ref, {
16
+ mediaQuery,
17
+ offset = 0,
18
+ min = 0,
19
+ includeMarginTop = false,
20
+ boundaryRef,
21
+ watchRef,
22
+ bottomOffset = 0
23
+ } = {}) {
24
+ const [layout, setLayout] = react.useState(
25
+ () => ({
26
+ active: !mediaQuery,
27
+ top: Math.max(0, min, offset)
28
+ })
29
+ );
30
+ const raf = react.useRef(null);
31
+ const measure = react.useCallback(() => {
32
+ const element = ref.current;
33
+ if (typeof window === "undefined" || !element) return;
34
+ if (mediaQuery && !window.matchMedia(mediaQuery).matches) {
35
+ setLayout((prev) => prev.active ? { top: 0, active: false } : prev);
36
+ return;
37
+ }
38
+ const scrollport = getScrollport(element);
39
+ const scrollportTop = scrollport ? scrollport.getBoundingClientRect().top + scrollport.clientTop : 0;
40
+ const scrollportBottom = scrollport ? Math.min(window.innerHeight, scrollportTop + scrollport.clientHeight) : window.innerHeight;
41
+ const boundary = boundaryRef == null ? void 0 : boundaryRef.current;
42
+ const boundaryBottom = boundary && boundary !== element ? boundary.getBoundingClientRect().bottom : scrollportTop;
43
+ const marginTop = includeMarginTop ? parseFloat(window.getComputedStyle(element).marginTop) || 0 : 0;
44
+ const top = Math.max(0, min, boundaryBottom - scrollportTop + offset - marginTop);
45
+ const visibleTop = Math.max(element.getBoundingClientRect().top, scrollportTop + top);
46
+ const maxHeight = Math.max(0, Math.floor(scrollportBottom - visibleTop - bottomOffset));
47
+ setLayout(
48
+ (prev) => prev.active && prev.top === top && prev.maxHeight === maxHeight ? prev : { top, maxHeight, active: true }
49
+ );
50
+ }, [ref, boundaryRef, offset, min, includeMarginTop, bottomOffset, mediaQuery]);
51
+ const scheduleMeasure = react.useCallback(() => {
52
+ if (raf.current !== null) return;
53
+ raf.current = window.requestAnimationFrame(() => {
54
+ raf.current = null;
55
+ measure();
56
+ });
57
+ }, [measure]);
58
+ useIsomorphicLayoutEffect(() => {
59
+ measure();
60
+ }, [measure]);
61
+ react.useEffect(() => {
62
+ if (!ref.current) return;
63
+ const element = ref.current;
64
+ const media = mediaQuery ? window.matchMedia(mediaQuery) : null;
65
+ media == null ? void 0 : media.addEventListener("change", scheduleMeasure);
66
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleMeasure);
67
+ const targets = /* @__PURE__ */ new Set([element]);
68
+ for (let parent = element.parentElement; parent; parent = parent.parentElement)
69
+ targets.add(parent);
70
+ if (boundaryRef == null ? void 0 : boundaryRef.current) targets.add(boundaryRef.current);
71
+ if (watchRef == null ? void 0 : watchRef.current) targets.add(watchRef.current);
72
+ targets.forEach((target) => observer == null ? void 0 : observer.observe(target));
73
+ window.addEventListener("resize", scheduleMeasure);
74
+ document.addEventListener("scroll", scheduleMeasure, true);
75
+ document.addEventListener("transitionend", scheduleMeasure, true);
76
+ document.addEventListener("animationend", scheduleMeasure, true);
77
+ return () => {
78
+ observer == null ? void 0 : observer.disconnect();
79
+ media == null ? void 0 : media.removeEventListener("change", scheduleMeasure);
80
+ window.removeEventListener("resize", scheduleMeasure);
81
+ document.removeEventListener("scroll", scheduleMeasure, true);
82
+ document.removeEventListener("transitionend", scheduleMeasure, true);
83
+ document.removeEventListener("animationend", scheduleMeasure, true);
84
+ if (raf.current !== null) cancelAnimationFrame(raf.current);
85
+ raf.current = null;
86
+ };
87
+ }, [ref, boundaryRef, watchRef, scheduleMeasure, mediaQuery]);
88
+ const style = react.useMemo(
89
+ () => layout.active ? { position: "sticky", top: `${layout.top}px` } : {},
90
+ [layout.top, layout.active]
91
+ );
92
+ const viewportStyle = react.useMemo(
93
+ () => layout.active ? { maxHeight: layout.maxHeight, overflowY: "auto", overscrollBehavior: "contain" } : {},
94
+ [layout.maxHeight, layout.active]
95
+ );
96
+ return { top: layout.top, style, viewportStyle, recompute: measure };
97
+ }
98
+
99
+ exports.useStickyLayout = useStickyLayout;
@@ -0,0 +1,22 @@
1
+ import { type CSSProperties, type RefObject } from 'react';
2
+ type Options = {
3
+ /** Enable sticky behavior only while this media query matches. */
4
+ mediaQuery?: string;
5
+ /** Extra inset below the scrollport edge or boundary, in pixels. */
6
+ offset?: number;
7
+ min?: number;
8
+ includeMarginTop?: boolean;
9
+ /** Header whose bottom edge the sticky element must stay below. */
10
+ boundaryRef?: RefObject<HTMLElement | null>;
11
+ /** Optional additional element whose resizing affects the layout. */
12
+ watchRef?: RefObject<HTMLElement | null>;
13
+ /** Breathing room below the inner scrollable content, in pixels. */
14
+ bottomOffset?: number;
15
+ };
16
+ export declare function useStickyLayout<T extends HTMLElement>(ref: RefObject<T | null>, { mediaQuery, offset, min, includeMarginTop, boundaryRef, watchRef, bottomOffset, }?: Options): {
17
+ top: number;
18
+ style: CSSProperties;
19
+ viewportStyle: CSSProperties;
20
+ recompute: () => void;
21
+ };
22
+ export {};
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+ import { useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect } from 'react';
3
+
4
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
5
+ const getScrollport = (element) => {
6
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
7
+ if (parent === document.body || parent === document.documentElement) return null;
8
+ const { overflowY } = window.getComputedStyle(parent);
9
+ if (/^(auto|scroll|hidden|overlay)$/.test(overflowY)) return parent;
10
+ }
11
+ return null;
12
+ };
13
+ function useStickyLayout(ref, {
14
+ mediaQuery,
15
+ offset = 0,
16
+ min = 0,
17
+ includeMarginTop = false,
18
+ boundaryRef,
19
+ watchRef,
20
+ bottomOffset = 0
21
+ } = {}) {
22
+ const [layout, setLayout] = useState(
23
+ () => ({
24
+ active: !mediaQuery,
25
+ top: Math.max(0, min, offset)
26
+ })
27
+ );
28
+ const raf = useRef(null);
29
+ const measure = useCallback(() => {
30
+ const element = ref.current;
31
+ if (typeof window === "undefined" || !element) return;
32
+ if (mediaQuery && !window.matchMedia(mediaQuery).matches) {
33
+ setLayout((prev) => prev.active ? { top: 0, active: false } : prev);
34
+ return;
35
+ }
36
+ const scrollport = getScrollport(element);
37
+ const scrollportTop = scrollport ? scrollport.getBoundingClientRect().top + scrollport.clientTop : 0;
38
+ const scrollportBottom = scrollport ? Math.min(window.innerHeight, scrollportTop + scrollport.clientHeight) : window.innerHeight;
39
+ const boundary = boundaryRef == null ? void 0 : boundaryRef.current;
40
+ const boundaryBottom = boundary && boundary !== element ? boundary.getBoundingClientRect().bottom : scrollportTop;
41
+ const marginTop = includeMarginTop ? parseFloat(window.getComputedStyle(element).marginTop) || 0 : 0;
42
+ const top = Math.max(0, min, boundaryBottom - scrollportTop + offset - marginTop);
43
+ const visibleTop = Math.max(element.getBoundingClientRect().top, scrollportTop + top);
44
+ const maxHeight = Math.max(0, Math.floor(scrollportBottom - visibleTop - bottomOffset));
45
+ setLayout(
46
+ (prev) => prev.active && prev.top === top && prev.maxHeight === maxHeight ? prev : { top, maxHeight, active: true }
47
+ );
48
+ }, [ref, boundaryRef, offset, min, includeMarginTop, bottomOffset, mediaQuery]);
49
+ const scheduleMeasure = useCallback(() => {
50
+ if (raf.current !== null) return;
51
+ raf.current = window.requestAnimationFrame(() => {
52
+ raf.current = null;
53
+ measure();
54
+ });
55
+ }, [measure]);
56
+ useIsomorphicLayoutEffect(() => {
57
+ measure();
58
+ }, [measure]);
59
+ useEffect(() => {
60
+ if (!ref.current) return;
61
+ const element = ref.current;
62
+ const media = mediaQuery ? window.matchMedia(mediaQuery) : null;
63
+ media == null ? void 0 : media.addEventListener("change", scheduleMeasure);
64
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleMeasure);
65
+ const targets = /* @__PURE__ */ new Set([element]);
66
+ for (let parent = element.parentElement; parent; parent = parent.parentElement)
67
+ targets.add(parent);
68
+ if (boundaryRef == null ? void 0 : boundaryRef.current) targets.add(boundaryRef.current);
69
+ if (watchRef == null ? void 0 : watchRef.current) targets.add(watchRef.current);
70
+ targets.forEach((target) => observer == null ? void 0 : observer.observe(target));
71
+ window.addEventListener("resize", scheduleMeasure);
72
+ document.addEventListener("scroll", scheduleMeasure, true);
73
+ document.addEventListener("transitionend", scheduleMeasure, true);
74
+ document.addEventListener("animationend", scheduleMeasure, true);
75
+ return () => {
76
+ observer == null ? void 0 : observer.disconnect();
77
+ media == null ? void 0 : media.removeEventListener("change", scheduleMeasure);
78
+ window.removeEventListener("resize", scheduleMeasure);
79
+ document.removeEventListener("scroll", scheduleMeasure, true);
80
+ document.removeEventListener("transitionend", scheduleMeasure, true);
81
+ document.removeEventListener("animationend", scheduleMeasure, true);
82
+ if (raf.current !== null) cancelAnimationFrame(raf.current);
83
+ raf.current = null;
84
+ };
85
+ }, [ref, boundaryRef, watchRef, scheduleMeasure, mediaQuery]);
86
+ const style = useMemo(
87
+ () => layout.active ? { position: "sticky", top: `${layout.top}px` } : {},
88
+ [layout.top, layout.active]
89
+ );
90
+ const viewportStyle = useMemo(
91
+ () => layout.active ? { maxHeight: layout.maxHeight, overflowY: "auto", overscrollBehavior: "contain" } : {},
92
+ [layout.maxHeight, layout.active]
93
+ );
94
+ return { top: layout.top, style, viewportStyle, recompute: measure };
95
+ }
96
+
97
+ export { useStickyLayout };
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var useTreeExpansion = require('../components/tree-view/useTreeExpansion');
6
+ var utils = require('../components/tree-view/utils');
7
+ var localStorage_utils = require('../utils/localStorage.utils');
8
+
9
+ function isTreeViewSettingsState(value) {
10
+ return typeof value === "object" && value !== null && typeof value.wrapLabels === "boolean";
11
+ }
12
+ const localStorageTreeViewSettingsStorage = {
13
+ get: (key) => {
14
+ const stored = localStorage_utils.readLocalStorage(key);
15
+ return isTreeViewSettingsState(stored) ? stored : void 0;
16
+ },
17
+ set: (key, next) => {
18
+ localStorage_utils.writeLocalStorage(key, next);
19
+ }
20
+ };
21
+ function useTreeViewSettings({
22
+ data = [],
23
+ defaultExpandedIds = [],
24
+ expandedIds: controlledExpandedIds,
25
+ onExpandedIdsChange,
26
+ defaultWrapLabels = true,
27
+ wrapLabels: controlledWrapLabels,
28
+ onWrapLabelsChange,
29
+ storageKey,
30
+ storage = localStorageTreeViewSettingsStorage
31
+ }) {
32
+ const [uncontrolledWrapLabels, setUncontrolledWrapLabels] = react.useState(defaultWrapLabels);
33
+ const isWrapControlled = controlledWrapLabels !== void 0;
34
+ const wrapLabels = isWrapControlled ? controlledWrapLabels : uncontrolledWrapLabels;
35
+ react.useEffect(() => {
36
+ if (!storageKey || isWrapControlled) return;
37
+ const stored = storage.get(storageKey);
38
+ if (stored) {
39
+ setUncontrolledWrapLabels(stored.wrapLabels);
40
+ }
41
+ }, [isWrapControlled, storage, storageKey]);
42
+ const setWrapLabels = react.useCallback(
43
+ (value) => {
44
+ if (wrapLabels === value) return;
45
+ if (!isWrapControlled) {
46
+ setUncontrolledWrapLabels(value);
47
+ if (storageKey) {
48
+ storage.set(storageKey, { wrapLabels: value });
49
+ }
50
+ }
51
+ onWrapLabelsChange == null ? void 0 : onWrapLabelsChange(value);
52
+ },
53
+ [isWrapControlled, onWrapLabelsChange, storage, storageKey, wrapLabels]
54
+ );
55
+ const toggleWrapLabels = react.useCallback(() => {
56
+ setWrapLabels(!wrapLabels);
57
+ }, [setWrapLabels, wrapLabels]);
58
+ const expansion = useTreeExpansion.useTreeExpansion(defaultExpandedIds, controlledExpandedIds, onExpandedIdsChange);
59
+ const expandedIds = react.useMemo(() => Array.from(expansion.expandedIds), [expansion.expandedIds]);
60
+ const expandAll = react.useCallback(() => {
61
+ expansion.setExpandedIds(utils.collectExpandableIds(data));
62
+ }, [data, expansion]);
63
+ const treeViewProps = react.useMemo(
64
+ () => ({
65
+ expandedIds,
66
+ onExpandedIdsChange: expansion.setExpandedIds,
67
+ wrapLabels
68
+ }),
69
+ [expandedIds, expansion.setExpandedIds, wrapLabels]
70
+ );
71
+ return {
72
+ wrapLabels,
73
+ setWrapLabels,
74
+ toggleWrapLabels,
75
+ expandedIds,
76
+ setExpandedIds: expansion.setExpandedIds,
77
+ expand: expansion.expand,
78
+ collapse: expansion.collapse,
79
+ toggle: expansion.toggle,
80
+ expandAll,
81
+ collapseAll: expansion.collapseAll,
82
+ treeViewProps
83
+ };
84
+ }
85
+
86
+ exports.localStorageTreeViewSettingsStorage = localStorageTreeViewSettingsStorage;
87
+ exports.useTreeViewSettings = useTreeViewSettings;
@@ -0,0 +1,35 @@
1
+ import type { TreeNode, TreeViewProps } from '../components/tree-view/types';
2
+ type TreeViewSettingsState = {
3
+ wrapLabels: boolean;
4
+ };
5
+ export type TreeViewSettingsStorage = {
6
+ get: (key: string) => TreeViewSettingsState | undefined;
7
+ set: (key: string, next: TreeViewSettingsState) => void;
8
+ };
9
+ export declare const localStorageTreeViewSettingsStorage: TreeViewSettingsStorage;
10
+ export interface UseTreeViewSettingsOptions<T = unknown> {
11
+ data?: TreeNode<T>[];
12
+ defaultExpandedIds?: string[];
13
+ expandedIds?: string[];
14
+ onExpandedIdsChange?: (ids: string[]) => void;
15
+ defaultWrapLabels?: boolean;
16
+ wrapLabels?: boolean;
17
+ onWrapLabelsChange?: (value: boolean) => void;
18
+ storageKey?: string;
19
+ storage?: TreeViewSettingsStorage;
20
+ }
21
+ export interface UseTreeViewSettingsResult<T = unknown> {
22
+ wrapLabels: boolean;
23
+ setWrapLabels: (value: boolean) => void;
24
+ toggleWrapLabels: () => void;
25
+ expandedIds: string[];
26
+ setExpandedIds: (ids: string[]) => void;
27
+ expand: (id: string) => void;
28
+ collapse: (id: string) => void;
29
+ toggle: (id: string) => void;
30
+ expandAll: () => void;
31
+ collapseAll: () => void;
32
+ treeViewProps: Pick<TreeViewProps<T>, 'expandedIds' | 'onExpandedIdsChange' | 'wrapLabels'>;
33
+ }
34
+ export declare function useTreeViewSettings<T = unknown>({ data, defaultExpandedIds, expandedIds: controlledExpandedIds, onExpandedIdsChange, defaultWrapLabels, wrapLabels: controlledWrapLabels, onWrapLabelsChange, storageKey, storage, }: UseTreeViewSettingsOptions<T>): UseTreeViewSettingsResult<T>;
35
+ export {};
@@ -0,0 +1,84 @@
1
+ 'use client';
2
+ import { useState, useEffect, useCallback, useMemo } from 'react';
3
+ import { useTreeExpansion } from '../components/tree-view/useTreeExpansion';
4
+ import { collectExpandableIds } from '../components/tree-view/utils';
5
+ import { writeLocalStorage, readLocalStorage } from '../utils/localStorage.utils';
6
+
7
+ function isTreeViewSettingsState(value) {
8
+ return typeof value === "object" && value !== null && typeof value.wrapLabels === "boolean";
9
+ }
10
+ const localStorageTreeViewSettingsStorage = {
11
+ get: (key) => {
12
+ const stored = readLocalStorage(key);
13
+ return isTreeViewSettingsState(stored) ? stored : void 0;
14
+ },
15
+ set: (key, next) => {
16
+ writeLocalStorage(key, next);
17
+ }
18
+ };
19
+ function useTreeViewSettings({
20
+ data = [],
21
+ defaultExpandedIds = [],
22
+ expandedIds: controlledExpandedIds,
23
+ onExpandedIdsChange,
24
+ defaultWrapLabels = true,
25
+ wrapLabels: controlledWrapLabels,
26
+ onWrapLabelsChange,
27
+ storageKey,
28
+ storage = localStorageTreeViewSettingsStorage
29
+ }) {
30
+ const [uncontrolledWrapLabels, setUncontrolledWrapLabels] = useState(defaultWrapLabels);
31
+ const isWrapControlled = controlledWrapLabels !== void 0;
32
+ const wrapLabels = isWrapControlled ? controlledWrapLabels : uncontrolledWrapLabels;
33
+ useEffect(() => {
34
+ if (!storageKey || isWrapControlled) return;
35
+ const stored = storage.get(storageKey);
36
+ if (stored) {
37
+ setUncontrolledWrapLabels(stored.wrapLabels);
38
+ }
39
+ }, [isWrapControlled, storage, storageKey]);
40
+ const setWrapLabels = useCallback(
41
+ (value) => {
42
+ if (wrapLabels === value) return;
43
+ if (!isWrapControlled) {
44
+ setUncontrolledWrapLabels(value);
45
+ if (storageKey) {
46
+ storage.set(storageKey, { wrapLabels: value });
47
+ }
48
+ }
49
+ onWrapLabelsChange == null ? void 0 : onWrapLabelsChange(value);
50
+ },
51
+ [isWrapControlled, onWrapLabelsChange, storage, storageKey, wrapLabels]
52
+ );
53
+ const toggleWrapLabels = useCallback(() => {
54
+ setWrapLabels(!wrapLabels);
55
+ }, [setWrapLabels, wrapLabels]);
56
+ const expansion = useTreeExpansion(defaultExpandedIds, controlledExpandedIds, onExpandedIdsChange);
57
+ const expandedIds = useMemo(() => Array.from(expansion.expandedIds), [expansion.expandedIds]);
58
+ const expandAll = useCallback(() => {
59
+ expansion.setExpandedIds(collectExpandableIds(data));
60
+ }, [data, expansion]);
61
+ const treeViewProps = useMemo(
62
+ () => ({
63
+ expandedIds,
64
+ onExpandedIdsChange: expansion.setExpandedIds,
65
+ wrapLabels
66
+ }),
67
+ [expandedIds, expansion.setExpandedIds, wrapLabels]
68
+ );
69
+ return {
70
+ wrapLabels,
71
+ setWrapLabels,
72
+ toggleWrapLabels,
73
+ expandedIds,
74
+ setExpandedIds: expansion.setExpandedIds,
75
+ expand: expansion.expand,
76
+ collapse: expansion.collapse,
77
+ toggle: expansion.toggle,
78
+ expandAll,
79
+ collapseAll: expansion.collapseAll,
80
+ treeViewProps
81
+ };
82
+ }
83
+
84
+ export { localStorageTreeViewSettingsStorage, useTreeViewSettings };
package/dist/index.cjs CHANGED
@@ -8,6 +8,7 @@ var Icon = require('./components/icon/Icon');
8
8
  var UserDisplay = require('./components/user-display/UserDisplay');
9
9
  var Headline = require('./components/headline/Headline');
10
10
  var SectionLabel = require('./components/section-label/SectionLabel');
11
+ var useStickyLayout = require('./hooks/useStickyLayout');
11
12
  var PageLayout = require('./components/page-layout/PageLayout');
12
13
  var Footer = require('./components/page-layout/components/footer/Footer');
13
14
  var Card = require('./components/card/Card');
@@ -100,6 +101,12 @@ Object.keys(SectionLabel).forEach(function (k) {
100
101
  get: function () { return SectionLabel[k]; }
101
102
  });
102
103
  });
104
+ Object.keys(useStickyLayout).forEach(function (k) {
105
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
106
+ enumerable: true,
107
+ get: function () { return useStickyLayout[k]; }
108
+ });
109
+ });
103
110
  Object.keys(PageLayout).forEach(function (k) {
104
111
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
105
112
  enumerable: true,
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from './components/icon/Icon';
6
6
  export * from './components/user-display/UserDisplay';
7
7
  export * from './components/headline/Headline';
8
8
  export * from './components/section-label/SectionLabel';
9
+ export * from './hooks/useStickyLayout';
9
10
  export * from './components/page-layout/PageLayout';
10
11
  export * from './components/page-layout/components/footer/Footer';
11
12
  export * from './components/card/Card';
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from './components/icon/Icon';
6
6
  export * from './components/user-display/UserDisplay';
7
7
  export * from './components/headline/Headline';
8
8
  export * from './components/section-label/SectionLabel';
9
+ export * from './hooks/useStickyLayout';
9
10
  export * from './components/page-layout/PageLayout';
10
11
  export * from './components/page-layout/components/footer/Footer';
11
12
  export * from './components/card/Card';