@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.
- package/dist/components/accordion/Accordion.d.ts +2 -2
- package/dist/components/accordion/Accordion.js +34 -41
- package/dist/components/accordion/Accordion.module.css +13 -72
- package/dist/components/accordion/components/AccordionRow.d.ts +10 -0
- package/dist/components/accordion/components/AccordionRow.js +51 -0
- package/dist/components/accordion/components/AccordionRow.module.css +82 -0
- package/dist/components/breadcrumbs/Breadcrumbs.module.css +0 -1
- package/dist/components/button/Button.module.css +7 -7
- package/dist/components/card/Card.d.ts +9 -18
- package/dist/components/card/Card.js +34 -23
- package/dist/components/card/Card.module.css +22 -87
- package/dist/components/card/components/CardMeta.d.ts +15 -0
- package/dist/components/card/components/CardMeta.js +20 -0
- package/dist/components/card/components/CardMeta.module.css +51 -0
- package/dist/components/card-container/CardContainer.js +1 -1
- package/dist/components/card-container/CardContainer.module.css +3 -1
- package/dist/components/chip/Chip.module.css +7 -2
- package/dist/components/datetime-picker/DateTimePicker.d.ts +33 -8
- package/dist/components/datetime-picker/DateTimePicker.js +119 -78
- package/dist/components/datetime-picker/DateTimePicker.module.css +2 -0
- package/dist/components/datetime-picker/dateTimeHelpers.d.ts +15 -3
- package/dist/components/datetime-picker/dateTimeHelpers.js +137 -23
- package/dist/components/filter-field/FilterField.js +21 -6
- package/dist/components/filter-field/FilterField.module.css +5 -5
- package/dist/components/forms/form-select/FormSelect.d.ts +35 -0
- package/dist/components/forms/form-select/FormSelect.js +86 -0
- package/dist/components/forms/form-select/FormSelect.module.css +236 -0
- package/dist/components/forms/input/Input.d.ts +0 -3
- package/dist/components/forms/input/Input.js +0 -3
- package/dist/components/forms/input/Input.module.css +7 -7
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
- package/dist/components/forms/select/Select.js +55 -16
- package/dist/components/interval-select/IntervalSelect.d.ts +9 -2
- package/dist/components/interval-select/IntervalSelect.js +21 -6
- package/dist/components/menu/Menu.d.ts +11 -14
- package/dist/components/menu/Menu.js +18 -33
- package/dist/components/menu/Menu.module.css +2 -2
- package/dist/components/overlay/modal/Modal.module.css +2 -1
- package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
- package/dist/components/overlay/side-panel/SidePanel.js +1 -1
- package/dist/components/overlay/side-panel/SidePanel.module.css +1 -1
- package/dist/components/page-layout/PageLayout.d.ts +16 -4
- package/dist/components/page-layout/PageLayout.js +57 -28
- package/dist/components/page-layout/PageLayout.module.css +153 -33
- package/dist/components/popover/Popover.d.ts +17 -4
- package/dist/components/popover/Popover.js +147 -65
- package/dist/components/popover/Popover.module.css +5 -0
- package/dist/components/split-pane/SplitPane.d.ts +10 -24
- package/dist/components/split-pane/SplitPane.js +83 -54
- package/dist/components/split-pane/SplitPane.module.css +11 -6
- package/dist/components/split-pane/provider/SplitPaneContext.js +5 -11
- package/dist/components/sticky-footer-layout/StickyFooterLayout.d.ts +3 -8
- package/dist/components/sticky-footer-layout/StickyFooterLayout.js +57 -20
- package/dist/components/table/Table.d.ts +3 -8
- package/dist/components/table/Table.js +37 -76
- package/dist/components/table/Table.module.css +45 -42
- package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +5 -12
- package/dist/components/table/TanstackTable.js +84 -0
- package/dist/components/table/components/column-resizer/ColumnResizer.js +1 -1
- package/dist/components/table/components/column-resizer/ColumnResizer.module.css +17 -7
- package/dist/components/table/table.utils.d.ts +17 -0
- package/dist/components/table/table.utils.js +61 -0
- package/dist/components/table/tanstackTable.utils.d.ts +22 -0
- package/dist/components/table/tanstackTable.utils.js +104 -0
- package/dist/components/tabs/Tabs.d.ts +35 -12
- package/dist/components/tabs/Tabs.js +114 -26
- package/dist/components/tabs/Tabs.module.css +158 -71
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/styles/styles.css +0 -1
- package/dist/styles/styles.css +0 -1
- package/dist/styles/themes/dbc/base.css +136 -0
- package/dist/styles/themes/dbc/dark.css +39 -202
- package/dist/styles/themes/dbc/light.css +17 -174
- package/package.json +4 -4
- package/dist/components/table/tanstack.js +0 -214
|
@@ -1,66 +1,186 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
10
|
-
align-items: flex-start;
|
|
26
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
11
27
|
}
|
|
12
28
|
|
|
13
|
-
.
|
|
14
|
-
|
|
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
|
-
|
|
43
|
+
-webkit-overflow-scrolling: touch;
|
|
17
44
|
}
|
|
18
45
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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: (
|
|
4
|
-
children: ((close
|
|
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
|
-
|
|
8
|
-
const
|
|
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
|
-
|
|
51
|
+
const triggerElRef = useRef(null);
|
|
52
|
+
const lastCloseReasonRef = useRef('unknown');
|
|
12
53
|
const [mounted, setMounted] = useState(false);
|
|
13
54
|
useEffect(() => setMounted(true), []);
|
|
14
|
-
const
|
|
15
|
-
|
|
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 (!
|
|
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 =
|
|
20
|
-
//
|
|
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
|
|
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;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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 (
|
|
93
|
-
|
|
151
|
+
if (!isOpen)
|
|
152
|
+
return;
|
|
153
|
+
computeAndSetPosition();
|
|
94
154
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
95
|
-
}, [
|
|
155
|
+
}, [isOpen]);
|
|
96
156
|
useEffect(() => {
|
|
97
|
-
|
|
157
|
+
var _a;
|
|
158
|
+
if (!isOpen)
|
|
159
|
+
return;
|
|
160
|
+
const content = contentRef.current;
|
|
161
|
+
if (!content)
|
|
98
162
|
return;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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(
|
|
112
|
-
document.addEventListener('
|
|
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('
|
|
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
|
-
}, [
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
minWidth
|
|
129
|
-
width:
|
|
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
|
-
|
|
133
|
-
},
|
|
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,34 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { SplitDirection } from './provider/SplitPaneContext';
|
|
3
|
-
interface SplitPaneProps {
|
|
4
|
-
children:
|
|
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):
|
|
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:
|
|
29
|
-
}):
|
|
30
|
-
export declare function SplitPaneGutter(): React.ReactNode;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): JSX.Element;
|
|
31
17
|
export declare function SplitPaneSecondary({ children }: {
|
|
32
|
-
children:
|
|
33
|
-
}):
|
|
34
|
-
export
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}): JSX.Element;
|
|
20
|
+
export declare function SplitPaneGutter(): JSX.Element;
|