@dbcdk/react-components 0.0.12 → 0.0.14

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 (76) hide show
  1. package/dist/components/accordion/Accordion.d.ts +2 -2
  2. package/dist/components/accordion/Accordion.js +34 -41
  3. package/dist/components/accordion/Accordion.module.css +13 -72
  4. package/dist/components/accordion/components/AccordionRow.d.ts +10 -0
  5. package/dist/components/accordion/components/AccordionRow.js +51 -0
  6. package/dist/components/accordion/components/AccordionRow.module.css +82 -0
  7. package/dist/components/breadcrumbs/Breadcrumbs.module.css +0 -1
  8. package/dist/components/button/Button.module.css +7 -7
  9. package/dist/components/card/Card.d.ts +9 -18
  10. package/dist/components/card/Card.js +34 -23
  11. package/dist/components/card/Card.module.css +22 -87
  12. package/dist/components/card/components/CardMeta.d.ts +15 -0
  13. package/dist/components/card/components/CardMeta.js +20 -0
  14. package/dist/components/card/components/CardMeta.module.css +51 -0
  15. package/dist/components/card-container/CardContainer.js +1 -1
  16. package/dist/components/card-container/CardContainer.module.css +3 -1
  17. package/dist/components/chip/Chip.module.css +7 -2
  18. package/dist/components/datetime-picker/DateTimePicker.d.ts +33 -8
  19. package/dist/components/datetime-picker/DateTimePicker.js +119 -78
  20. package/dist/components/datetime-picker/DateTimePicker.module.css +2 -0
  21. package/dist/components/datetime-picker/dateTimeHelpers.d.ts +15 -3
  22. package/dist/components/datetime-picker/dateTimeHelpers.js +137 -23
  23. package/dist/components/filter-field/FilterField.js +21 -6
  24. package/dist/components/filter-field/FilterField.module.css +5 -5
  25. package/dist/components/forms/form-select/FormSelect.d.ts +35 -0
  26. package/dist/components/forms/form-select/FormSelect.js +86 -0
  27. package/dist/components/forms/form-select/FormSelect.module.css +236 -0
  28. package/dist/components/forms/input/Input.d.ts +0 -3
  29. package/dist/components/forms/input/Input.js +0 -3
  30. package/dist/components/forms/input/Input.module.css +7 -7
  31. package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
  32. package/dist/components/forms/select/Select.js +55 -16
  33. package/dist/components/interval-select/IntervalSelect.d.ts +9 -2
  34. package/dist/components/interval-select/IntervalSelect.js +21 -6
  35. package/dist/components/menu/Menu.d.ts +11 -14
  36. package/dist/components/menu/Menu.js +18 -33
  37. package/dist/components/menu/Menu.module.css +2 -2
  38. package/dist/components/overlay/modal/Modal.module.css +2 -1
  39. package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
  40. package/dist/components/overlay/side-panel/SidePanel.js +1 -1
  41. package/dist/components/overlay/side-panel/SidePanel.module.css +1 -1
  42. package/dist/components/page-layout/PageLayout.d.ts +16 -4
  43. package/dist/components/page-layout/PageLayout.js +57 -28
  44. package/dist/components/page-layout/PageLayout.module.css +153 -33
  45. package/dist/components/popover/Popover.d.ts +17 -4
  46. package/dist/components/popover/Popover.js +147 -65
  47. package/dist/components/popover/Popover.module.css +5 -0
  48. package/dist/components/split-pane/SplitPane.d.ts +10 -24
  49. package/dist/components/split-pane/SplitPane.js +83 -54
  50. package/dist/components/split-pane/SplitPane.module.css +11 -6
  51. package/dist/components/split-pane/provider/SplitPaneContext.js +5 -11
  52. package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +3 -8
  53. package/dist/components/sticky-footer-layout/StickyFooterLayout.js +57 -20
  54. package/dist/components/table/Table.d.ts +3 -8
  55. package/dist/components/table/Table.js +37 -76
  56. package/dist/components/table/Table.module.css +45 -42
  57. package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +5 -12
  58. package/dist/components/table/TanstackTable.js +84 -0
  59. package/dist/components/table/components/column-resizer/ColumnResizer.js +1 -1
  60. package/dist/components/table/components/column-resizer/ColumnResizer.module.css +17 -7
  61. package/dist/components/table/table.utils.d.ts +17 -0
  62. package/dist/components/table/table.utils.js +61 -0
  63. package/dist/components/table/tanstackTable.utils.d.ts +22 -0
  64. package/dist/components/table/tanstackTable.utils.js +104 -0
  65. package/dist/components/tabs/Tabs.d.ts +35 -12
  66. package/dist/components/tabs/Tabs.js +114 -26
  67. package/dist/components/tabs/Tabs.module.css +158 -71
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/src/styles/styles.css +0 -1
  71. package/dist/styles/styles.css +0 -1
  72. package/dist/styles/themes/dbc/base.css +136 -0
  73. package/dist/styles/themes/dbc/dark.css +39 -202
  74. package/dist/styles/themes/dbc/light.css +17 -174
  75. package/package.json +4 -4
  76. package/dist/components/table/tanstack.js +0 -214
@@ -1,66 +1,186 @@
1
- .container {
2
- display: flex;
3
- flex-direction: column;
4
- gap: 20px;
5
- max-width: 100vw;
1
+ /* Root shell */
2
+ .root {
3
+ width: 100%;
4
+ max-width: 100%;
5
+ display: grid;
6
+ gap: 0;
7
+ }
8
+
9
+ /* Viewport handling:
10
+ - containScrolling => app-shell: lock to viewport and manage scroll in regions
11
+ - documentScrolling => let page scroll normally
12
+ */
13
+ .containScrolling {
14
+ height: 100vh;
15
+ height: 100dvh;
16
+ overflow: hidden;
17
+ }
18
+
19
+ .documentScrolling {
20
+ min-height: 100%;
21
+ overflow: visible;
6
22
  }
7
23
 
24
+ /* Orientation layouts */
8
25
  .vertical {
9
- flex-direction: row;
10
- align-items: flex-start;
26
+ grid-template-columns: auto minmax(0, 1fr);
11
27
  }
12
28
 
13
- .container.containScrolling {
14
- height: 100vh;
29
+ .horizontal {
30
+ grid-template-columns: minmax(0, 1fr);
31
+ }
32
+
33
+ /* Sidebar */
34
+ .sidebar {
35
+ min-width: 0;
36
+ overflow: hidden;
37
+ border-right: var(--border-width-thin) solid var(--color-border-subtle);
38
+ background: var(--color-bg-surface);
39
+ }
40
+
41
+ .containScrolling .sidebar {
15
42
  overflow: auto;
16
- position: relative;
43
+ -webkit-overflow-scrolling: touch;
17
44
  }
18
45
 
19
- @media screen and (max-height: 400px) {
20
- .container {
21
- height: unset;
22
- }
46
+ /* In horizontal orientation, sidebar becomes a top block if used */
47
+ .horizontal .sidebar {
48
+ grid-column: 1 / -1;
49
+ border-right: none;
50
+ border-bottom: var(--border-width-thin) solid var(--color-border-subtle);
23
51
  }
24
52
 
25
- .headerContainer {
53
+ /* Main column:
54
+ Use flex so the "rest of space" logic works reliably.
55
+ */
56
+ .mainColumn {
57
+ min-width: 0;
26
58
  display: flex;
27
- justify-content: center;
59
+ flex-direction: column;
60
+ background: var(--color-bg-surface);
61
+ }
62
+
63
+ /* In containScrolling mode, mainColumn must fill the viewport height
64
+ so the flex "remaining space" can be computed.
65
+ */
66
+ .containScrolling .mainColumn {
67
+ height: 100%;
68
+ min-height: 0; /* important: allows children to shrink */
28
69
  }
29
70
 
30
- .container:not(.containScrolling) .headerContainer {
71
+ /* Regions */
72
+ .header {
73
+ min-width: 0;
74
+ background: var(--color-bg-surface);
75
+ border-bottom: var(--border-width-thin) solid var(--color-border-subtle);
76
+ flex: 0 0 auto;
77
+ }
78
+
79
+ /* Sticky header only when the document is scrolling (body scroll) */
80
+ .documentScrolling .header {
31
81
  position: sticky;
32
- z-index: 1000;
33
82
  top: 0;
83
+ z-index: var(--z-sticky);
34
84
  }
35
85
 
36
- .headerContent {
86
+ .hero {
87
+ min-width: 0;
88
+ background: var(--color-bg-surface);
89
+ border-bottom: var(--border-width-thin) solid var(--color-border-subtle);
90
+ flex: 0 0 auto;
91
+ }
92
+
93
+ /* Scroll wrapper: contains content + footer */
94
+ .mainScroll {
95
+ min-width: 0;
96
+ min-height: 0; /* CRITICAL for nested flex/scroll */
97
+ display: flex;
98
+ flex-direction: column;
99
+
100
+ /* This is the "take all remaining space" bit */
101
+ flex: 1 1 auto;
102
+
103
+ /* default: documentScrolling uses normal page flow */
104
+ overflow: visible;
105
+ }
106
+
107
+ /* Contained scrolling: mainScroll scrolls (includes footer) */
108
+ .containScrolling .mainScroll {
109
+ overflow: auto;
110
+ -webkit-overflow-scrolling: touch;
111
+ }
112
+
113
+ /* Content area (not a scroll container anymore) */
114
+ .content {
115
+ min-width: 0;
116
+ min-height: 0;
117
+ flex: 1 1 auto; /* take remaining space inside mainScroll */
118
+ display: flex; /* lets contentInner stretch */
119
+ flex-direction: column;
120
+
121
+ background: var(--color-bg-surface);
122
+ padding: var(--spacing-md);
123
+ overflow: visible;
124
+ }
125
+
126
+ .footer {
127
+ min-width: 0;
128
+ background: var(--color-bg-surface);
129
+ border-top: var(--border-width-thin) solid var(--color-border-subtle);
130
+
131
+ /* When there is extra space (content is short), this pushes footer to the bottom.
132
+ When content is long, footer follows content normally and is reached by scrolling.
133
+ */
134
+ margin-top: auto;
37
135
  flex: 0 0 auto;
136
+ }
137
+
138
+ /* Header slot wrappers */
139
+ .headerContainer {
140
+ display: flex;
141
+ justify-content: center;
142
+ width: 100%;
143
+ }
144
+
145
+ .headerContent {
38
146
  width: 100%;
39
147
  box-sizing: border-box;
40
148
  }
41
149
 
42
150
  .maxWidth {
43
151
  max-width: 1600px;
44
- margin: auto;
152
+ margin-inline: auto;
45
153
  width: 100%;
46
154
  box-sizing: border-box;
47
155
  }
48
156
 
49
- .content {
50
- flex: 1 1 auto;
51
- width: 100%;
52
- margin: 0 auto;
53
- box-sizing: border-box;
54
- gap: var(--spacing-xl);
55
- padding: var(--spacing-md) var(--spacing-xl);
56
- padding-top: 0;
57
- min-height: 0;
157
+ /* Content slot inner wrapper (so maxWidth works without interfering with scroll) */
158
+ .contentInner {
58
159
  display: flex;
59
160
  flex-direction: column;
60
- overflow: auto;
61
- -webkit-overflow-scrolling: touch;
161
+ gap: var(--spacing-xl);
162
+
163
+ box-sizing: border-box;
164
+ min-width: 0;
165
+
166
+ /* This is what makes your "main content" actually expand to fill */
167
+ flex: 1 1 auto;
168
+ min-height: 0;
62
169
  }
63
170
 
64
- .footer {
65
- flex: 0 0 auto;
171
+ /* Optional: for small viewport heights, avoid forced viewport locking */
172
+ @media (max-height: 400px) {
173
+ .containScrolling {
174
+ height: auto;
175
+ overflow: visible;
176
+ }
177
+
178
+ /* In this mode, avoid forcing a nested scroll region */
179
+ .containScrolling .mainScroll {
180
+ overflow: visible;
181
+ }
182
+
183
+ .containScrolling .mainColumn {
184
+ height: auto;
185
+ }
66
186
  }
@@ -1,12 +1,26 @@
1
1
  import * as React from 'react';
2
- interface PopoverProps {
3
- trigger: (event: (e: React.MouseEvent<HTMLElement> | React.FocusEvent<HTMLElement>) => void, icon: React.ReactNode) => React.ReactNode;
4
- children: ((close?: () => void) => React.ReactNode) | React.ReactNode;
2
+ export interface PopoverProps {
3
+ trigger: (toggle: (e: React.MouseEvent<HTMLElement> | React.FocusEvent<HTMLElement>) => void, icon: React.ReactNode, open?: boolean) => React.ReactNode;
4
+ children: ((close: () => void) => React.ReactNode) | React.ReactNode;
5
+ open?: boolean;
6
+ defaultOpen?: boolean;
7
+ onOpenChange?: (open: boolean) => void;
8
+ contentId?: string;
9
+ /**
10
+ * CSS length, recommended "NNpx" for predictability.
11
+ * Used as a minimum, not as a forced width unless matchTriggerWidth=true.
12
+ */
5
13
  minWidth?: string;
14
+ /**
15
+ * If true, force the overlay width to at least the trigger width.
16
+ * If false, overlay width is content-driven (calendar-friendly).
17
+ */
6
18
  matchTriggerWidth?: boolean;
7
19
  viewportPadding?: number;
8
20
  edgeBuffer?: number;
9
21
  dataCy?: string;
22
+ autoFocusContent?: boolean;
23
+ returnFocus?: boolean;
10
24
  }
11
25
  export interface PopoverHandle {
12
26
  close: () => void;
@@ -14,4 +28,3 @@ export interface PopoverHandle {
14
28
  isOpen: () => boolean;
15
29
  }
16
30
  export declare const Popover: React.ForwardRefExoticComponent<PopoverProps & React.RefAttributes<PopoverHandle>>;
17
- export {};
@@ -1,43 +1,121 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ChevronDown, ChevronUp } from 'lucide-react';
4
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
4
+ import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import styles from './Popover.module.css';
7
- export const Popover = forwardRef(function Popover({ trigger: Trigger, children, minWidth = '200px', matchTriggerWidth = true, viewportPadding = 8, edgeBuffer = 100, dataCy, }, ref) {
8
- const [pos, setPos] = useState({ top: 0, left: 0, width: 0, visible: false });
7
+ function getFocusable(container) {
8
+ const els = container.querySelectorAll([
9
+ 'a[href]',
10
+ 'button:not([disabled])',
11
+ 'input:not([disabled])',
12
+ 'select:not([disabled])',
13
+ 'textarea:not([disabled])',
14
+ '[tabindex]:not([tabindex="-1"])',
15
+ ].join(','));
16
+ return Array.from(els).filter(el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
17
+ }
18
+ function parseMinWidthPx(minWidth, elForEm) {
19
+ const v = minWidth.trim();
20
+ if (v.endsWith('px')) {
21
+ const n = Number.parseFloat(v);
22
+ return Number.isFinite(n) ? n : 0;
23
+ }
24
+ if (typeof window !== 'undefined' && v.endsWith('rem')) {
25
+ const n = Number.parseFloat(v);
26
+ if (!Number.isFinite(n))
27
+ return 0;
28
+ const rootFont = Number.parseFloat(getComputedStyle(document.documentElement).fontSize || '16');
29
+ return n * (Number.isFinite(rootFont) ? rootFont : 16);
30
+ }
31
+ if (typeof window !== 'undefined' && v.endsWith('em')) {
32
+ const n = Number.parseFloat(v);
33
+ if (!Number.isFinite(n))
34
+ return 0;
35
+ const font = elForEm ? Number.parseFloat(getComputedStyle(elForEm).fontSize || '16') : 16;
36
+ return n * (Number.isFinite(font) ? font : 16);
37
+ }
38
+ return 0;
39
+ }
40
+ export const Popover = forwardRef(function Popover({ trigger: Trigger, children, open, defaultOpen = false, onOpenChange, contentId, minWidth = '200px', matchTriggerWidth = true, viewportPadding = 8, edgeBuffer = 100, dataCy, autoFocusContent = false, returnFocus = true, }, ref) {
41
+ const internalId = useId();
42
+ const resolvedContentId = contentId !== null && contentId !== void 0 ? contentId : `popover-${internalId}`;
43
+ const isControlled = open !== undefined;
44
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
45
+ const isOpen = isControlled ? !!open : uncontrolledOpen;
46
+ const [pos, setPos] = useState({ top: 0, left: 0 });
47
+ const [positioned, setPositioned] = useState(false);
48
+ const [triggerWidth, setTriggerWidth] = useState(null);
9
49
  const containerRef = useRef(null);
10
50
  const contentRef = useRef(null);
11
- // avoid SSR/hydration mismatch
51
+ const triggerElRef = useRef(null);
52
+ const lastCloseReasonRef = useRef('unknown');
12
53
  const [mounted, setMounted] = useState(false);
13
54
  useEffect(() => setMounted(true), []);
14
- const computeAndSetPosition = useCallback((show) => {
15
- const container = containerRef.current;
55
+ const setOpen = useCallback((next) => {
56
+ if (!isControlled)
57
+ setUncontrolledOpen(next);
58
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
59
+ }, [isControlled, onOpenChange]);
60
+ const openPopover = useCallback(() => {
61
+ setPositioned(false);
62
+ setOpen(true);
63
+ }, [setOpen]);
64
+ const closePopover = useCallback((reason = 'api') => {
65
+ lastCloseReasonRef.current = reason;
66
+ setOpen(false);
67
+ }, [setOpen]);
68
+ const togglePopover = useCallback((e) => {
69
+ triggerElRef.current = e.currentTarget;
70
+ if (isOpen)
71
+ closePopover('trigger');
72
+ else
73
+ openPopover();
74
+ }, [isOpen, closePopover, openPopover]);
75
+ useImperativeHandle(ref, () => ({
76
+ close: () => closePopover('api'),
77
+ open: openPopover,
78
+ isOpen: () => isOpen,
79
+ }), [closePopover, openPopover, isOpen]);
80
+ const computeAndSetPosition = useCallback(() => {
81
+ var _a;
16
82
  const content = contentRef.current;
17
- if (!container || !content)
83
+ if (!content)
84
+ return;
85
+ const triggerEl = (_a = triggerElRef.current) !== null && _a !== void 0 ? _a : containerRef.current;
86
+ if (!triggerEl)
18
87
  return;
19
- const triggerRect = container.getBoundingClientRect();
20
- // Temporarily measure content size by forcing it into the layout.
88
+ const triggerRect = triggerEl.getBoundingClientRect();
89
+ // Only compute a forced width when requested.
90
+ let forcedWidthPx = null;
91
+ if (matchTriggerWidth) {
92
+ const minWidthPx = parseMinWidthPx(minWidth, triggerEl);
93
+ forcedWidthPx = Math.max(triggerRect.width, minWidthPx || 0);
94
+ setTriggerWidth(forcedWidthPx);
95
+ }
96
+ else {
97
+ setTriggerWidth(null);
98
+ }
99
+ // Measure height/width for collision using a temporary sizing that reflects our final sizing:
100
+ const prevHidden = content.hidden;
21
101
  const prevVis = content.style.visibility;
22
102
  const prevDisp = content.style.display;
23
103
  const prevMinWidth = content.style.minWidth;
24
104
  const prevWidth = content.style.width;
25
105
  const prevTop = content.style.top;
26
106
  const prevLeft = content.style.left;
107
+ content.hidden = false;
27
108
  content.style.visibility = 'hidden';
28
109
  content.style.display = 'block';
29
110
  content.style.top = '0px';
30
111
  content.style.left = '0px';
112
+ // Apply minWidth always; apply width only if matchTriggerWidth.
31
113
  content.style.minWidth = minWidth;
32
- content.style.width = 'auto';
33
- const minWidthPx = content.offsetWidth;
34
- const desiredWidthPx = Math.max(matchTriggerWidth ? triggerRect.width : 0, minWidthPx);
35
- // Apply desired width and re-measure final size (height may depend on width).
36
- content.style.minWidth = `${desiredWidthPx}px`;
37
- content.style.width = `${desiredWidthPx}px`;
114
+ content.style.width = forcedWidthPx != null ? `${forcedWidthPx}px` : 'auto';
38
115
  const contentWidth = content.offsetWidth;
39
116
  const contentHeight = content.offsetHeight;
40
- // Restore previous inline styles
117
+ // Restore
118
+ content.hidden = prevHidden;
41
119
  content.style.visibility = prevVis;
42
120
  content.style.display = prevDisp;
43
121
  content.style.minWidth = prevMinWidth;
@@ -58,80 +136,84 @@ export const Popover = forwardRef(function Popover({ trigger: Trigger, children,
58
136
  const placeRightOfLeftEdge = spaceRight >= contentWidth + edgeBuffer;
59
137
  const placeLeftOfRightEdge = spaceLeft >= contentWidth + edgeBuffer;
60
138
  let rawLeft;
61
- if (placeRightOfLeftEdge) {
62
- rawLeft = triggerRect.left; // align left edges
63
- }
64
- else if (placeLeftOfRightEdge) {
65
- rawLeft = triggerRect.right - contentWidth; // align right edges
66
- }
67
- else {
139
+ if (placeRightOfLeftEdge)
140
+ rawLeft = triggerRect.left;
141
+ else if (placeLeftOfRightEdge)
142
+ rawLeft = triggerRect.right - contentWidth;
143
+ else
68
144
  rawLeft = triggerRect.left + (triggerRect.width - contentWidth) / 2;
69
- }
70
145
  const clampedLeft = Math.max(viewportPadding, Math.min(rawLeft, vw - contentWidth - viewportPadding));
71
146
  const clampedTop = Math.max(viewportPadding, Math.min(rawTop, vh - contentHeight - viewportPadding));
72
- setPos({ top: clampedTop, left: clampedLeft, width: desiredWidthPx, visible: show });
147
+ setPos({ top: clampedTop, left: clampedLeft });
148
+ setPositioned(true);
73
149
  }, [edgeBuffer, viewportPadding, minWidth, matchTriggerWidth]);
74
- const openPopover = useCallback((e) => {
75
- if (pos.visible) {
76
- setPos(p => ({ ...p, visible: false }));
77
- return;
78
- }
79
- computeAndSetPosition(true);
80
- e === null || e === void 0 ? void 0 : e.stopPropagation();
81
- }, [pos.visible, computeAndSetPosition]);
82
- const closePopover = useCallback(() => {
83
- setPos(p => ({ ...p, visible: false }));
84
- }, []);
85
- useImperativeHandle(ref, () => ({
86
- close: closePopover,
87
- open: () => computeAndSetPosition(true),
88
- isOpen: () => !!pos.visible,
89
- }), [closePopover, computeAndSetPosition, pos.visible]);
90
- // Recompute position after open to account for content becoming visible / measured.
91
150
  useLayoutEffect(() => {
92
- if (pos.visible)
93
- computeAndSetPosition(true);
151
+ if (!isOpen)
152
+ return;
153
+ computeAndSetPosition();
94
154
  // eslint-disable-next-line react-hooks/exhaustive-deps
95
- }, [pos.visible]);
155
+ }, [isOpen]);
96
156
  useEffect(() => {
97
- if (!pos.visible)
157
+ var _a;
158
+ if (!isOpen)
159
+ return;
160
+ const content = contentRef.current;
161
+ if (!content)
98
162
  return;
99
- const handleClickOutside = (e) => {
100
- if (containerRef.current &&
101
- contentRef.current &&
102
- !containerRef.current.contains(e.target) &&
103
- !contentRef.current.contains(e.target)) {
104
- closePopover();
163
+ if (autoFocusContent) {
164
+ const focusables = getFocusable(content);
165
+ (_a = focusables[0]) === null || _a === void 0 ? void 0 : _a.focus();
166
+ }
167
+ const handlePointerDownCapture = (e) => {
168
+ const container = containerRef.current;
169
+ const contentEl = contentRef.current;
170
+ if (!container || !contentEl)
171
+ return;
172
+ const target = e.target;
173
+ if (!container.contains(target) && !contentEl.contains(target)) {
174
+ closePopover('outside');
105
175
  }
106
176
  };
107
177
  const handleEscape = (e) => {
108
178
  if (e.key === 'Escape')
109
- closePopover();
179
+ closePopover('escape');
110
180
  };
111
- const handleReposition = () => computeAndSetPosition(true);
112
- document.addEventListener('click', handleClickOutside);
181
+ const handleReposition = () => computeAndSetPosition();
182
+ document.addEventListener('pointerdown', handlePointerDownCapture, true);
113
183
  document.addEventListener('keydown', handleEscape, true);
114
184
  window.addEventListener('resize', handleReposition);
115
185
  window.addEventListener('scroll', handleReposition, true);
116
186
  return () => {
117
- document.removeEventListener('click', handleClickOutside);
187
+ document.removeEventListener('pointerdown', handlePointerDownCapture, true);
118
188
  document.removeEventListener('keydown', handleEscape, true);
119
189
  window.removeEventListener('resize', handleReposition);
120
190
  window.removeEventListener('scroll', handleReposition, true);
121
191
  };
122
- }, [pos.visible, closePopover, computeAndSetPosition]);
123
- return (_jsxs("div", { className: styles.container, ref: containerRef, children: [Trigger(openPopover, pos.visible ? _jsx(ChevronUp, { size: 20 }) : _jsx(ChevronDown, { size: 20 })), mounted &&
124
- createPortal(_jsx("div", { ref: contentRef, className: styles.content, style: {
192
+ }, [isOpen, closePopover, computeAndSetPosition, autoFocusContent]);
193
+ useEffect(() => {
194
+ var _a, _b;
195
+ if (isOpen)
196
+ return;
197
+ if (!returnFocus)
198
+ return;
199
+ if (lastCloseReasonRef.current === 'outside')
200
+ return;
201
+ (_b = (_a = triggerElRef.current) === null || _a === void 0 ? void 0 : _a.focus) === null || _b === void 0 ? void 0 : _b.call(_a);
202
+ }, [isOpen, returnFocus]);
203
+ const icon = isOpen ? _jsx(ChevronUp, { size: 20 }) : _jsx(ChevronDown, { size: 20 });
204
+ return (_jsxs("div", { className: styles.container, ref: containerRef, children: [Trigger(togglePopover, icon, isOpen), mounted &&
205
+ isOpen &&
206
+ createPortal(_jsx("div", { id: resolvedContentId, ref: contentRef, className: styles.content, style: {
125
207
  top: pos.top,
126
208
  left: pos.left,
127
- visibility: pos.visible ? 'visible' : 'hidden',
128
- minWidth: pos.width ? `${pos.width}px` : minWidth,
129
- width: pos.width ? `${pos.width}px` : undefined,
209
+ // Content-driven sizing by default.
210
+ minWidth,
211
+ width: triggerWidth != null ? `${triggerWidth}px` : undefined,
130
212
  maxWidth: `calc(100vw - ${viewportPadding * 2}px)`,
131
213
  maxHeight: `clamp(100px, calc(100vh - ${viewportPadding * 2}px), 400px)`,
132
- overflow: 'auto',
133
- }, role: "dialog", "aria-hidden": !pos.visible, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'popover-content', children: typeof children === 'function'
134
- ? children(closePopover)
214
+ visibility: positioned ? undefined : 'hidden',
215
+ }, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'popover-content', children: typeof children === 'function'
216
+ ? children(() => closePopover('api'))
135
217
  : children }), document.body)] }));
136
218
  });
137
219
  Popover.displayName = 'Popover';
@@ -1,5 +1,6 @@
1
1
  .container {
2
2
  position: relative;
3
+ display: inline-block;
3
4
  }
4
5
 
5
6
  .content {
@@ -13,6 +14,10 @@
13
14
  box-shadow: var(--shadow-md);
14
15
  }
15
16
 
17
+ .content[hidden] {
18
+ display: none;
19
+ }
20
+
16
21
  .content svg {
17
22
  height: 20px;
18
23
  width: 20px;
@@ -1,34 +1,20 @@
1
- import React from 'react';
2
- import { SplitDirection } from './provider/SplitPaneContext';
3
- interface SplitPaneProps {
4
- children: React.ReactNode;
1
+ import type { JSX, ReactNode } from 'react';
2
+ import type { SplitDirection } from './provider/SplitPaneContext';
3
+ export interface SplitPaneProps {
4
+ children: ReactNode;
5
5
  initialPrimarySize?: number;
6
6
  minPrimarySize?: number;
7
7
  minSecondarySize?: number;
8
8
  direction?: SplitDirection;
9
9
  showDivider?: 'hover' | 'always' | 'never';
10
- /**
11
- * Gutter size (px). This is the space between panes and contains the resizer hit area.
12
- * Example: 8 => 4px breathing room on each side if the divider line is centered.
13
- */
14
10
  gutterSize?: number;
15
- /**
16
- * If provided, primary size is persisted per key in localStorage.
17
- * Only SplitPanes sharing the same key will share size.
18
- */
19
11
  storageKey?: string;
20
12
  }
21
- export declare function SplitPane({ children, initialPrimarySize, minPrimarySize, minSecondarySize, direction, showDivider, gutterSize, storageKey, }: SplitPaneProps): React.ReactNode;
22
- /**
23
- * IMPORTANT:
24
- * This component now renders ONLY the primary content (scrollable).
25
- * The resizer lives in a dedicated <SplitPaneGutter /> so it never overlaps scrollbars.
26
- */
13
+ export declare function SplitPane({ children, initialPrimarySize, minPrimarySize, minSecondarySize, direction, showDivider, gutterSize, storageKey, }: SplitPaneProps): JSX.Element;
27
14
  export declare function SplitPanePrimary({ children }: {
28
- children: React.ReactNode;
29
- }): React.ReactNode;
30
- export declare function SplitPaneGutter(): React.ReactNode;
15
+ children: ReactNode;
16
+ }): JSX.Element;
31
17
  export declare function SplitPaneSecondary({ children }: {
32
- children: React.ReactNode;
33
- }): React.ReactNode;
34
- export {};
18
+ children: ReactNode;
19
+ }): JSX.Element;
20
+ export declare function SplitPaneGutter(): JSX.Element;