@dbcdk/react-components 0.0.10 → 0.0.13
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 +15 -6
- package/dist/components/card/Card.js +39 -13
- package/dist/components/card/Card.module.css +22 -28
- 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/circle/Circle.d.ts +2 -1
- package/dist/components/circle/Circle.js +2 -2
- package/dist/components/circle/Circle.module.css +6 -2
- package/dist/components/code-block/CodeBlock.js +1 -1
- package/dist/components/code-block/CodeBlock.module.css +30 -17
- package/dist/components/copy-button/CopyButton.d.ts +1 -0
- package/dist/components/copy-button/CopyButton.js +10 -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 +16 -11
- package/dist/components/filter-field/FilterField.module.css +137 -16
- package/dist/components/forms/checkbox/Checkbox.d.ts +2 -2
- package/dist/components/forms/checkbox-group/CheckboxGroup.js +1 -1
- package/dist/components/forms/checkbox-group/CheckboxGroup.module.css +1 -1
- 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 +1 -4
- package/dist/components/forms/input/Input.module.css +8 -7
- package/dist/components/forms/input-container/InputContainer.module.css +1 -1
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +1 -0
- package/dist/components/forms/select/Select.js +55 -16
- package/dist/components/hyperlink/Hyperlink.d.ts +19 -7
- package/dist/components/hyperlink/Hyperlink.js +35 -11
- package/dist/components/hyperlink/Hyperlink.module.css +50 -2
- 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 +29 -0
- package/dist/components/menu/Menu.js +61 -16
- package/dist/components/menu/Menu.module.css +73 -5
- package/dist/components/overlay/modal/Modal.module.css +4 -3
- package/dist/components/overlay/modal/provider/ModalProvider.js +1 -3
- package/dist/components/overlay/side-panel/SidePanel.js +18 -1
- package/dist/components/overlay/side-panel/SidePanel.module.css +1 -3
- package/dist/components/overlay/tooltip/useTooltipTrigger.js +4 -2
- 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/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +22 -18
- package/dist/components/sidebar/providers/SidebarProvider.d.ts +4 -1
- package/dist/components/sidebar/providers/SidebarProvider.js +66 -18
- package/dist/components/split-button/SplitButton.d.ts +1 -1
- package/dist/components/split-button/SplitButton.js +3 -1
- package/dist/components/split-button/SplitButton.module.css +4 -4
- 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/state-page/StatePage.module.css +1 -1
- 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 +8 -8
- package/dist/components/table/Table.js +37 -79
- package/dist/components/table/Table.module.css +62 -46
- package/dist/components/table/{tanstack.d.ts → TanstackTable.d.ts} +7 -3
- 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/components/table-settings/TableSettings.d.ts +13 -3
- package/dist/components/table/components/table-settings/TableSettings.js +55 -4
- 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/hooks/useTableSettings.d.ts +23 -4
- package/dist/hooks/useTableSettings.js +64 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/src/styles/styles.css +38 -23
- package/dist/styles/animation.d.ts +5 -0
- package/dist/styles/animation.js +5 -0
- package/dist/styles/styles.css +38 -23
- 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/dist/utils/localStorage.utils.d.ts +19 -0
- package/dist/utils/localStorage.utils.js +78 -0
- package/package.json +4 -4
- package/dist/components/table/tanstack.js +0 -162
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Menu.module.css */
|
|
1
2
|
.container {
|
|
2
3
|
list-style: none;
|
|
3
4
|
margin: 0;
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
display: contents;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
/* Applied to actual interactive elements (button/a/custom that forwards className) */
|
|
20
22
|
.interactive {
|
|
21
23
|
display: flex;
|
|
22
24
|
align-items: center;
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
text-decoration: none;
|
|
28
30
|
|
|
29
31
|
/* choose your density */
|
|
30
|
-
padding-block:
|
|
32
|
+
padding-block: var(--spacing-xs);
|
|
31
33
|
padding-inline: var(--spacing-md);
|
|
32
34
|
|
|
33
35
|
background: transparent;
|
|
@@ -45,29 +47,95 @@
|
|
|
45
47
|
color var(--transition-fast) var(--ease-standard);
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
/*
|
|
51
|
+
Applied to the immediate child of <li> even if it's NOT an interactive element (e.g. a <div>)
|
|
52
|
+
so that menu row styling still works for components that render a wrapper.
|
|
53
|
+
*/
|
|
54
|
+
.interactiveChild {
|
|
55
|
+
display: block;
|
|
56
|
+
inline-size: 100%;
|
|
57
|
+
border-radius: var(--border-radius-sm);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* NEW: make wrapper-children (Checkbox/Radio) look/space like menu rows */
|
|
61
|
+
.row > .interactiveChild {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
inline-size: 100%;
|
|
65
|
+
padding-block: var(--spacing-xxs);
|
|
66
|
+
padding-inline: var(--spacing-md);
|
|
67
|
+
border-radius: var(--border-radius-sm);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* NEW: let Checkbox/Radio consume full width so the hover area feels right */
|
|
71
|
+
.row > .interactiveChild > * {
|
|
72
|
+
inline-size: 100%;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* NEW: add consistent gap between control and label inside Checkbox/Radio
|
|
76
|
+
Both components use a root element with className={styles.container}.
|
|
77
|
+
Because they're CSS modules, we must target it with :global(.container). */
|
|
78
|
+
.row :global(.container) {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: var(--spacing-sm);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Hover: support both cases (interactive element, or wrapper child) */
|
|
85
|
+
.interactive:hover,
|
|
86
|
+
.row:hover > .interactiveChild {
|
|
49
87
|
background-color: var(--color-bg-hover-subtle);
|
|
50
88
|
}
|
|
51
89
|
|
|
90
|
+
/* Focus ring: support both cases */
|
|
52
91
|
.interactive:focus-visible {
|
|
53
92
|
outline: none;
|
|
54
93
|
box-shadow: var(--focus-ring);
|
|
55
94
|
}
|
|
56
95
|
|
|
96
|
+
/* If wrapper contains a focusable element, show ring when any child is focused */
|
|
97
|
+
.row:focus-within > .interactiveChild {
|
|
98
|
+
outline: none;
|
|
99
|
+
box-shadow: var(--focus-ring);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Selected/active (legacy + item variant) */
|
|
57
103
|
.active,
|
|
58
|
-
.interactive[aria-selected='true']
|
|
104
|
+
.interactive[aria-selected='true'],
|
|
105
|
+
.row > .interactiveChild.active,
|
|
106
|
+
.row > .interactiveChild[aria-selected='true'] {
|
|
59
107
|
background-color: var(--color-bg-selected);
|
|
60
108
|
color: var(--color-fg-default);
|
|
61
109
|
}
|
|
62
110
|
|
|
111
|
+
/* Checked (legacy support; kept in case any interactive element still uses aria-checked) */
|
|
112
|
+
.interactive[aria-checked='true'],
|
|
113
|
+
.row > .interactiveChild[aria-checked='true'] {
|
|
114
|
+
background-color: var(--color-bg-selected);
|
|
115
|
+
color: var(--color-fg-default);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Disabled: support both cases */
|
|
63
119
|
.interactive[aria-disabled='true'],
|
|
64
|
-
.interactive:disabled
|
|
120
|
+
.interactive:disabled,
|
|
121
|
+
.row > .interactiveChild[aria-disabled='true'] {
|
|
65
122
|
color: var(--color-disabled-fg);
|
|
66
123
|
cursor: not-allowed;
|
|
67
124
|
pointer-events: none;
|
|
68
125
|
}
|
|
69
126
|
|
|
70
|
-
|
|
127
|
+
/* Icons inside either interactive element or wrapper */
|
|
128
|
+
.interactive svg,
|
|
129
|
+
.interactiveChild svg {
|
|
71
130
|
inline-size: var(--icon-size-md);
|
|
72
131
|
block-size: var(--icon-size-md);
|
|
73
132
|
}
|
|
133
|
+
|
|
134
|
+
/* Visual separator row (used by <Menu.Separator />) */
|
|
135
|
+
.separator {
|
|
136
|
+
block-size: 1px;
|
|
137
|
+
margin-block: var(--spacing-2xs);
|
|
138
|
+
background: var(--color-border-subtle);
|
|
139
|
+
opacity: 0.8;
|
|
140
|
+
border-radius: 999px;
|
|
141
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
justify-content: center;
|
|
8
8
|
padding-top: clamp(var(--spacing-md), 12vh, 24vh);
|
|
9
9
|
padding-bottom: var(--spacing-md);
|
|
10
|
-
z-index: var(--z-modal);
|
|
10
|
+
z-index: var(--z-backdrop-modal);
|
|
11
11
|
overflow-y: auto;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -15,13 +15,14 @@
|
|
|
15
15
|
background: var(--color-bg-surface);
|
|
16
16
|
border-radius: var(--border-radius-lg);
|
|
17
17
|
min-width: 320px;
|
|
18
|
-
max-width:
|
|
18
|
+
max-width: 700px;
|
|
19
19
|
max-height: calc(100vh - (2 * var(--spacing-md)));
|
|
20
20
|
display: flex;
|
|
21
21
|
flex-direction: column;
|
|
22
22
|
box-shadow: var(--shadow-lg);
|
|
23
23
|
font-family: var(--font-family);
|
|
24
|
-
min-width:
|
|
24
|
+
min-width: 500px;
|
|
25
|
+
z-index: var(--z-modal);
|
|
25
26
|
color: var(--color-fg-default);
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -43,20 +43,18 @@ export function ModalProvider({ children }) {
|
|
|
43
43
|
label: confirmLabel,
|
|
44
44
|
onClick: () => {
|
|
45
45
|
resolvePending(true);
|
|
46
|
-
closeModal();
|
|
47
46
|
},
|
|
48
47
|
},
|
|
49
48
|
secondaryAction: {
|
|
50
49
|
label: cancelLabel,
|
|
51
50
|
onClick: () => {
|
|
52
51
|
resolvePending(false);
|
|
53
|
-
closeModal();
|
|
54
52
|
},
|
|
55
53
|
},
|
|
56
54
|
});
|
|
57
55
|
setIsOpen(true);
|
|
58
56
|
});
|
|
59
|
-
}, [
|
|
57
|
+
}, [resolvePending]);
|
|
60
58
|
const modalNode = (_jsxs(ModalContext.Provider, { value: { openModal, closeModal, confirm }, children: [children, mounted &&
|
|
61
59
|
createPortal(_jsx(Modal, { ...(config !== null && config !== void 0 ? config : {}), isOpen: isOpen, onRequestClose: handleRequestClose, children: config === null || config === void 0 ? void 0 : config.children }), document.body)] }));
|
|
62
60
|
return modalNode;
|
|
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState, } from 'react';
|
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { Button } from '../../../components/button/Button';
|
|
6
6
|
import { Headline } from '../../../components/headline/Headline';
|
|
7
|
+
import { MOTION_MS } from '../../../styles/animation';
|
|
7
8
|
import styles from './SidePanel.module.css';
|
|
8
9
|
export function SidePanel({ isOpen, onClose, children, header, headerAddition, actions, showBackdrop = true, severity, showHeaderMarker = true, width = '400px', details, detailsHeader = 'Output', detailsWidth = '420px', onCloseDetails, detailsHeaderAddition, ...props }) {
|
|
9
10
|
const [mounted, setMounted] = useState(false);
|
|
@@ -16,6 +17,21 @@ export function SidePanel({ isOpen, onClose, children, header, headerAddition, a
|
|
|
16
17
|
if (isOpen)
|
|
17
18
|
setShouldRender(true);
|
|
18
19
|
}, [isOpen]);
|
|
20
|
+
// Close on ESC key
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!isOpen)
|
|
23
|
+
return;
|
|
24
|
+
const handleKeyDown = (e) => {
|
|
25
|
+
if (e.key === 'Escape') {
|
|
26
|
+
e.stopPropagation();
|
|
27
|
+
onClose();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
31
|
+
return () => {
|
|
32
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
33
|
+
};
|
|
34
|
+
}, [isOpen, onClose]);
|
|
19
35
|
// Two-phase OPEN/CLOSE class toggle (lets CSS transitions kick in reliably)
|
|
20
36
|
useEffect(() => {
|
|
21
37
|
if (!shouldRender)
|
|
@@ -56,11 +72,12 @@ export function SidePanel({ isOpen, onClose, children, header, headerAddition, a
|
|
|
56
72
|
} })), _jsxs("div", { ref: panelRef, ...props, className: `${styles.sidePanel} ${isActive ? styles.open : ''} ${hasDetails ? styles.withDetails : styles.noDetails}`, style: {
|
|
57
73
|
'--side-panel-width': width,
|
|
58
74
|
'--details-width': detailsWidth,
|
|
75
|
+
'--panel-dur': MOTION_MS.panelSlide + 'ms',
|
|
59
76
|
}, "data-cy": "details-panel", role: "dialog", "aria-modal": "true", children: [hasDetails ? (_jsxs("aside", { className: styles.detailsCol, "data-cy": "details-panel-details", children: [_jsxs("div", { className: styles.detailsHeader, children: [_jsx("div", { className: styles.detailsTitle, children: detailsHeader }), _jsxs("div", { className: styles.detailsHeaderActions, children: [detailsHeaderAddition, onCloseDetails ? (_jsx(Button, { type: "button", size: "sm", variant: "outlined", onClick: e => {
|
|
60
77
|
e.stopPropagation();
|
|
61
78
|
onCloseDetails();
|
|
62
79
|
}, children: "Luk" })) : null] })] }), _jsx("div", { className: styles.detailsContent, children: details })] })) : null, _jsxs("section", { className: styles.mainCol, "data-cy": "details-panel-main", children: [_jsx("div", { className: styles.header, children: _jsxs("div", { className: "dbc-flex dbc-justify-between", children: [_jsx(Headline, { size: 3, disableMargin: true, severity: severity, marker: showHeaderMarker, children: header }), _jsxs("span", { className: "dbc-flex dbc-items-center dbc-gap-xs", children: [headerAddition, _jsx(Button, { type: "button", size: "sm", variant: "inline", onClick: e => {
|
|
63
80
|
e.stopPropagation();
|
|
64
81
|
onClose(e);
|
|
65
|
-
}, "aria-label": "
|
|
82
|
+
}, "aria-label": "Luk panel", "data-cy": "side-panel-close-button", children: _jsx(X, {}) })] })] }) }), _jsx("div", { className: styles.content, "data-cy": "details-panel-content", children: children }), actions && _jsx("div", { className: styles.actions, children: actions })] })] })] }), document.body);
|
|
66
83
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
.sidePanel {
|
|
2
2
|
--col-pad: var(--spacing-md);
|
|
3
3
|
|
|
4
|
-
/* Dial these for “feel” */
|
|
5
|
-
--panel-dur: 220ms;
|
|
6
4
|
--panel-ease: cubic-bezier(0.22, 1, 0.36, 1); /* smooth spring-ish without overshoot */
|
|
7
5
|
|
|
8
6
|
/* Shadow + edge */
|
|
@@ -159,7 +157,7 @@
|
|
|
159
157
|
position: fixed;
|
|
160
158
|
inset: 0;
|
|
161
159
|
background-color: rgba(0, 0, 0, 0.45);
|
|
162
|
-
z-index: var(--z-backdrop);
|
|
160
|
+
z-index: var(--z-backdrop-drawer);
|
|
163
161
|
|
|
164
162
|
opacity: 0;
|
|
165
163
|
transition: opacity var(--panel-dur) var(--panel-ease);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect, useId, useRef, useState } from 'react';
|
|
2
|
+
import { MOTION_MS } from '../../../styles/animation';
|
|
2
3
|
import { TooltipContext } from './TooltipProvider';
|
|
3
4
|
export function useTooltipTrigger(options) {
|
|
4
5
|
const ctx = useContext(TooltipContext);
|
|
@@ -45,7 +46,6 @@ export function useTooltipTrigger(options) {
|
|
|
45
46
|
}
|
|
46
47
|
show();
|
|
47
48
|
}, [isOpen, show, hide]);
|
|
48
|
-
// ✅ Only call update if THIS tooltip is the active one AND something changed
|
|
49
49
|
useEffect(() => {
|
|
50
50
|
var _a;
|
|
51
51
|
if (!isOpen)
|
|
@@ -84,7 +84,9 @@ export function useTooltipTrigger(options) {
|
|
|
84
84
|
const onFocus = () => {
|
|
85
85
|
clearTimers();
|
|
86
86
|
if (!isControlled)
|
|
87
|
-
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
setOpen(true);
|
|
89
|
+
}, MOTION_MS.tooltipOpen);
|
|
88
90
|
};
|
|
89
91
|
const onBlur = () => {
|
|
90
92
|
clearTimers();
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { PropsWithChildren, ReactNode
|
|
2
|
-
import { PageLayoutHeroProps } from './components/page-layout-hero/PageLayoutHero';
|
|
1
|
+
import type { FC, PropsWithChildren, ReactNode } from 'react';
|
|
2
|
+
import { type PageLayoutHeroProps } from './components/page-layout-hero/PageLayoutHero';
|
|
3
3
|
type Orientation = 'vertical' | 'horizontal';
|
|
4
4
|
export interface PageLayoutProps extends PropsWithChildren {
|
|
5
|
+
/**
|
|
6
|
+
* If true, PageLayout becomes a self-contained app shell (100vh) and
|
|
7
|
+
* scroll is contained inside the intended regions (sidebar/content).
|
|
8
|
+
* If false, the document scrolls normally (body scroll).
|
|
9
|
+
*/
|
|
5
10
|
containScrolling?: boolean;
|
|
6
11
|
orientation?: Orientation;
|
|
7
|
-
sidebar?: ReactNode;
|
|
8
|
-
header?: ReactNode;
|
|
9
12
|
}
|
|
10
13
|
export interface PageLayoutHeaderProps {
|
|
11
14
|
maxWidth?: boolean;
|
|
@@ -18,7 +21,16 @@ export interface PageLayoutContentProps {
|
|
|
18
21
|
export interface PageLayoutFooterProps {
|
|
19
22
|
children: ReactNode;
|
|
20
23
|
}
|
|
24
|
+
export interface PageLayoutSidebarProps {
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* Optional: if your sidebar contains navigation, you might wrap your nav in <nav>.
|
|
28
|
+
* This component will render an <aside>.
|
|
29
|
+
*/
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
}
|
|
21
32
|
export declare const PageLayout: FC<PageLayoutProps> & {
|
|
33
|
+
Sidebar: FC<PageLayoutSidebarProps>;
|
|
22
34
|
Header: FC<PageLayoutHeaderProps>;
|
|
23
35
|
Hero: FC<PageLayoutHeroProps>;
|
|
24
36
|
Content: FC<PageLayoutContentProps>;
|
|
@@ -1,38 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { PageLayoutHero } from './components/page-layout-hero/PageLayoutHero';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, isValidElement } from 'react';
|
|
3
|
+
import { PageLayoutHero, } from './components/page-layout-hero/PageLayoutHero';
|
|
4
4
|
import styles from './PageLayout.module.css';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
function getSlotName(el) {
|
|
6
|
+
var _a;
|
|
7
|
+
const t = el.type;
|
|
8
|
+
return (_a = t === null || t === void 0 ? void 0 : t.__PAGE_LAYOUT_SLOT__) !== null && _a !== void 0 ? _a : null;
|
|
9
|
+
}
|
|
10
|
+
function splitSlots(children) {
|
|
11
|
+
const slots = {};
|
|
12
|
+
const rest = [];
|
|
13
|
+
Children.forEach(children, child => {
|
|
14
|
+
if (!isValidElement(child)) {
|
|
15
|
+
if (child != null)
|
|
16
|
+
rest.push(child);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const slot = getSlotName(child);
|
|
20
|
+
if (!slot) {
|
|
21
|
+
rest.push(child);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// If multiple of same slot are provided, last wins (simple + predictable).
|
|
25
|
+
slots[slot] = child;
|
|
26
|
+
});
|
|
27
|
+
return { slots, rest };
|
|
28
|
+
}
|
|
29
|
+
/** Slot components */
|
|
30
|
+
const PageLayoutSidebar = ({ children, }) => {
|
|
31
|
+
// This returns children; layout wrapper decides how to render.
|
|
32
|
+
// We keep this as a slot marker component.
|
|
33
|
+
return _jsx(_Fragment, { children: children });
|
|
24
34
|
};
|
|
25
|
-
|
|
26
|
-
const PageLayoutHeader = ({ maxWidth =
|
|
35
|
+
PageLayoutSidebar.__PAGE_LAYOUT_SLOT__ = 'Sidebar';
|
|
36
|
+
const PageLayoutHeader = ({ maxWidth = false, children, }) => {
|
|
27
37
|
return (_jsx("div", { className: styles.headerContainer, children: _jsx("div", { className: `${styles.headerContent} ${maxWidth ? styles.maxWidth : ''}`, children: children }) }));
|
|
28
38
|
};
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
PageLayoutHeader.__PAGE_LAYOUT_SLOT__ = 'Header';
|
|
40
|
+
const PageLayoutContent = ({ maxWidth = false, children, }) => {
|
|
41
|
+
return (_jsx("div", { className: `${styles.contentInner} ${maxWidth ? styles.maxWidth : ''}`, children: children }));
|
|
31
42
|
};
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
PageLayoutContent.__PAGE_LAYOUT_SLOT__ = 'Content';
|
|
44
|
+
const PageLayoutFooter = ({ children, }) => {
|
|
45
|
+
return _jsx(_Fragment, { children: children });
|
|
46
|
+
};
|
|
47
|
+
PageLayoutFooter.__PAGE_LAYOUT_SLOT__ = 'Footer';
|
|
48
|
+
PageLayoutHero.__PAGE_LAYOUT_SLOT__ = 'Hero';
|
|
49
|
+
const PageLayoutBase = ({ children, containScrolling = false, orientation = 'vertical', }) => {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
const { slots, rest } = splitSlots(children);
|
|
52
|
+
// If no explicit <PageLayout.Content>, we’ll treat remaining children as content.
|
|
53
|
+
const content = (_a = slots.Content) !== null && _a !== void 0 ? _a : (rest.length ? _jsx(PageLayoutContent, { maxWidth: true, children: rest }) : undefined);
|
|
54
|
+
const rootClass = [
|
|
55
|
+
styles.root,
|
|
56
|
+
orientation === 'vertical' ? styles.vertical : styles.horizontal,
|
|
57
|
+
containScrolling ? styles.containScrolling : styles.documentScrolling,
|
|
58
|
+
]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ');
|
|
61
|
+
return (_jsxs("div", { className: rootClass, children: [slots.Sidebar ? (_jsx("aside", { className: styles.sidebar, "aria-label": (_b = slots.Sidebar.props) === null || _b === void 0 ? void 0 : _b.ariaLabel, children: slots.Sidebar })) : null, _jsxs("div", { className: styles.mainColumn, children: [slots.Header ? _jsx("header", { className: styles.header, children: slots.Header }) : null, slots.Hero ? _jsx("div", { className: styles.hero, children: slots.Hero }) : null, content || slots.Footer ? (_jsxs("div", { className: styles.mainScroll, children: [content ? _jsx("main", { className: styles.content, children: content }) : null, slots.Footer ? _jsx("footer", { className: styles.footer, children: slots.Footer }) : null] })) : null] })] }));
|
|
34
62
|
};
|
|
35
63
|
export const PageLayout = Object.assign(PageLayoutBase, {
|
|
64
|
+
Sidebar: PageLayoutSidebar,
|
|
36
65
|
Header: PageLayoutHeader,
|
|
37
66
|
Hero: PageLayoutHero,
|
|
38
67
|
Content: PageLayoutContent,
|
|
@@ -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 {};
|