@dbcdk/react-components 0.0.73 → 0.0.75
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/forms/radio-buttons/RadioButtons.module.css +2 -2
- package/dist/components/headline/Headline.module.css +7 -6
- package/dist/components/json-viewer/JsonViewer.d.ts +2 -1
- package/dist/components/json-viewer/JsonViewer.js +4 -2
- package/dist/components/menu/Menu.d.ts +1 -1
- package/dist/components/menu/Menu.js +9 -1
- package/dist/components/menu/Menu.module.css +10 -2
- package/dist/components/overlay/modal/Modal.js +2 -11
- package/dist/components/overlay/modal/Modal.module.css +11 -2
- package/dist/components/popover/Popover.d.ts +1 -0
- package/dist/components/popover/Popover.js +13 -10
- package/package.json +1 -1
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
justify-content: center;
|
|
32
32
|
|
|
33
33
|
background: var(--color-bg-surface);
|
|
34
|
-
border: 2px solid
|
|
34
|
+
border: 2px solid var(--color-border-default);
|
|
35
35
|
|
|
36
36
|
transition:
|
|
37
37
|
border-color 120ms ease,
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.default {
|
|
89
|
-
border-color:
|
|
89
|
+
border-color: var(--color-border-strong);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/* Sizes */
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
display: inline-flex;
|
|
15
15
|
align-items: center;
|
|
16
16
|
gap: var(--spacing-xs);
|
|
17
|
+
margin-block: var(--spacing-xs);
|
|
17
18
|
|
|
18
19
|
/* Typography */
|
|
19
20
|
font-weight: var(--font-weight-medium, 500);
|
|
@@ -41,19 +42,19 @@
|
|
|
41
42
|
|
|
42
43
|
/* Marker variant */
|
|
43
44
|
.headline.marker {
|
|
44
|
-
padding-inline-start: calc(var(--border-width-
|
|
45
|
+
padding-inline-start: calc(var(--border-width-medium) + var(--spacing-sm));
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
.headline.marker::before {
|
|
48
49
|
content: '';
|
|
49
50
|
position: absolute;
|
|
50
|
-
inset-block:
|
|
51
|
+
inset-block-start: 50%;
|
|
52
|
+
transform: translateY(-50%);
|
|
53
|
+
block-size: 0.75em;
|
|
51
54
|
inset-inline-start: 0;
|
|
52
|
-
inline-size: var(--border-width-
|
|
55
|
+
inline-size: var(--border-width-medium);
|
|
53
56
|
background-color: var(--marker-color, var(--color-brand));
|
|
54
|
-
border-
|
|
55
|
-
border-end-start-radius: var(--border-radius-default);
|
|
56
|
-
box-shadow: inset 0 0 0 1px var(--color-border-subtle);
|
|
57
|
+
border-radius: var(--border-radius-sm);
|
|
57
58
|
pointer-events: none;
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -8,5 +8,6 @@ export interface JsonViewerProps {
|
|
|
8
8
|
searchPlaceholder?: string;
|
|
9
9
|
emptySearchText?: string;
|
|
10
10
|
className?: string;
|
|
11
|
+
maxHeight?: number | string;
|
|
11
12
|
}
|
|
12
|
-
export declare function JsonViewer({ value, defaultExpandedDepth, expandAll, searchDebounceMs, helperText, searchPlaceholder, emptySearchText, className, }: JsonViewerProps): JSX.Element;
|
|
13
|
+
export declare function JsonViewer({ value, defaultExpandedDepth, expandAll, searchDebounceMs, helperText, searchPlaceholder, emptySearchText, className, maxHeight, }: JsonViewerProps): JSX.Element;
|
|
@@ -158,7 +158,7 @@ function collectInitiallyExpandedNodeIds(value, defaultExpandedDepth, expandAll,
|
|
|
158
158
|
]));
|
|
159
159
|
return includeNode ? [nodeId, ...nested] : nested;
|
|
160
160
|
}
|
|
161
|
-
export function JsonViewer({ value, defaultExpandedDepth = 2, expandAll = false, searchDebounceMs = 180, helperText, searchPlaceholder = 'Søg i nøgler, stier og værdier', emptySearchText = 'Ingen matchende noder fundet.', className, }) {
|
|
161
|
+
export function JsonViewer({ value, defaultExpandedDepth = 2, expandAll = false, searchDebounceMs = 180, helperText, searchPlaceholder = 'Søg i nøgler, stier og værdier', emptySearchText = 'Ingen matchende noder fundet.', className, maxHeight, }) {
|
|
162
162
|
const [queryInput, setQueryInput] = useState('');
|
|
163
163
|
const [query, setQuery] = useState('');
|
|
164
164
|
const normalizedQuery = normalizeSearch(query);
|
|
@@ -214,7 +214,9 @@ export function JsonViewer({ value, defaultExpandedDepth = 2, expandAll = false,
|
|
|
214
214
|
setQuery('');
|
|
215
215
|
}, children: _jsx(X, { "aria-hidden": "true" }) })) : null] }), _jsxs("div", { className: styles.actions, children: [_jsx("button", { type: "button", className: styles.toolbarButton, onClick: handleExpandAll, disabled: Boolean(normalizedQuery), children: "Fold alle ud" }), _jsx("button", { type: "button", className: styles.toolbarButton, onClick: handleCollapseAll, disabled: Boolean(normalizedQuery), children: "Fold alle sammen" })] })] }), _jsxs("div", { className: styles.metaRow, children: [helperText ? _jsx("span", { className: styles.helperText, children: helperText }) : _jsx("span", {}), _jsx("span", { className: styles.statusText, children: normalizedQuery
|
|
216
216
|
? `${matches.size} matchende node${matches.size === 1 ? '' : 'r'}`
|
|
217
|
-
: '' })] })] }), _jsx("div", { className: styles.treePane, role: "tree", "aria-label": "JSON-visning",
|
|
217
|
+
: '' })] })] }), _jsx("div", { className: styles.treePane, role: "tree", "aria-label": "JSON-visning", style: maxHeight !== undefined
|
|
218
|
+
? { maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight }
|
|
219
|
+
: undefined, children: hasResults ? (_jsx(JsonNode, { keyName: undefined, path: [], value: safeValue, depth: 0, defaultExpandedDepth: defaultExpandedDepth, query: normalizedQuery, matches: matches, expandedNodeIds: expandedNodeIds, onToggleNode: handleToggleNode, copiedId: copiedId, onCopy: handleCopy })) : (_jsx("div", { className: styles.emptyState, children: emptySearchText })) })] }));
|
|
218
220
|
}
|
|
219
221
|
function JsonNode({ keyName, path, value, depth, defaultExpandedDepth, query, matches, expandedNodeIds, onToggleNode, copiedId, onCopy, }) {
|
|
220
222
|
const nodeId = getNodeId(path);
|
|
@@ -25,7 +25,7 @@ export interface MenuItemProps extends React.LiHTMLAttributes<HTMLLIElement> {
|
|
|
25
25
|
*/
|
|
26
26
|
itemRole?: 'menuitem' | 'option';
|
|
27
27
|
/** Adds a rounded border around the item */
|
|
28
|
-
variant?: 'default' | 'bordered';
|
|
28
|
+
variant?: 'default' | 'bordered' | 'danger';
|
|
29
29
|
}
|
|
30
30
|
export interface MenuCheckItemProps extends Omit<React.LiHTMLAttributes<HTMLLIElement>, 'onChange'> {
|
|
31
31
|
label: React.ReactNode;
|
|
@@ -90,17 +90,25 @@ const MenuItem = React.forwardRef(({ children, active, selected, disabled, class
|
|
|
90
90
|
// (We can’t reliably read parent props here without context; simplest is: caller passes itemRole on Menu.Item when needed.)
|
|
91
91
|
const resolvedRole = itemRole !== null && itemRole !== void 0 ? itemRole : 'menuitem';
|
|
92
92
|
const isBordered = variant === 'bordered';
|
|
93
|
+
const itemClassName = variant === 'danger' ? styles.itemDanger : undefined;
|
|
93
94
|
const rowClass = [styles.row, isBordered ? styles.rowBordered : '', className]
|
|
94
95
|
.filter(Boolean)
|
|
95
96
|
.join(' ');
|
|
96
97
|
if (isInteractiveEl(children)) {
|
|
97
98
|
const child = children;
|
|
98
|
-
return (_jsx("li", { ref: ref, role: "none", className: rowClass, ...liProps, children: applyMenuItemPropsToElement(child, {
|
|
99
|
+
return (_jsx("li", { ref: ref, role: "none", className: rowClass, ...liProps, children: applyMenuItemPropsToElement(child, {
|
|
100
|
+
active,
|
|
101
|
+
selected,
|
|
102
|
+
disabled,
|
|
103
|
+
role: resolvedRole,
|
|
104
|
+
className: itemClassName,
|
|
105
|
+
}) }));
|
|
99
106
|
}
|
|
100
107
|
// Fallback: wrap non-interactive children in a <button>
|
|
101
108
|
return (_jsx("li", { ref: ref, role: "none", className: rowClass, ...liProps, children: _jsx("button", { role: resolvedRole, tabIndex: 0, "aria-selected": selected || undefined, "aria-disabled": disabled || undefined, className: [
|
|
102
109
|
styles.interactive,
|
|
103
110
|
styles.item,
|
|
111
|
+
itemClassName,
|
|
104
112
|
active ? styles.active : '',
|
|
105
113
|
selected ? styles.selected : '',
|
|
106
114
|
]
|
|
@@ -149,6 +149,14 @@
|
|
|
149
149
|
color: var(--color-fg-default);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
.itemDanger {
|
|
153
|
+
color: var(--color-status-error);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.itemDanger svg {
|
|
157
|
+
color: inherit;
|
|
158
|
+
}
|
|
159
|
+
|
|
152
160
|
/* Disabled: support both cases */
|
|
153
161
|
.interactive[aria-disabled='true'],
|
|
154
162
|
.interactive:disabled,
|
|
@@ -161,8 +169,8 @@
|
|
|
161
169
|
/* Icons inside either interactive element or wrapper */
|
|
162
170
|
.interactive svg,
|
|
163
171
|
.interactiveChild svg {
|
|
164
|
-
inline-size: var(--icon-size-
|
|
165
|
-
block-size: var(--icon-size-
|
|
172
|
+
inline-size: var(--icon-size-sm);
|
|
173
|
+
block-size: var(--icon-size-sm);
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
/* Visual separator row (used by <Menu.Separator />) */
|
|
@@ -17,17 +17,8 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
|
|
|
17
17
|
onRequestCloseRef.current = onRequestClose;
|
|
18
18
|
}, [onRequestClose]);
|
|
19
19
|
useEffect(() => {
|
|
20
|
-
setMounted(true);
|
|
20
|
+
setMounted(true); // eslint-disable-line react-hooks/set-state-in-effect -- intentional: SSR portal guard, runs once after hydration
|
|
21
21
|
}, []);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
if (!isOpen)
|
|
24
|
-
return;
|
|
25
|
-
const previous = document.body.style.overflow;
|
|
26
|
-
document.body.style.overflow = 'hidden';
|
|
27
|
-
return () => {
|
|
28
|
-
document.body.style.overflow = previous;
|
|
29
|
-
};
|
|
30
|
-
}, [isOpen]);
|
|
31
22
|
// Track open transition so we only autofocus once per open
|
|
32
23
|
const wasOpenRef = useRef(false);
|
|
33
24
|
useEffect(() => {
|
|
@@ -112,7 +103,7 @@ export function Modal({ isOpen, onRequestClose, header, content, children, prima
|
|
|
112
103
|
const stopPropagation = e => {
|
|
113
104
|
e.stopPropagation();
|
|
114
105
|
};
|
|
115
|
-
const resolvedSecondaryAction = secondaryAction !== null && secondaryAction !== void 0 ? secondaryAction : (primaryAction ? { label: 'Luk', onClick:
|
|
106
|
+
const resolvedSecondaryAction = secondaryAction !== null && secondaryAction !== void 0 ? secondaryAction : (primaryAction ? { label: 'Luk', onClick: onRequestClose } : undefined);
|
|
116
107
|
const shouldRenderFooter = Boolean(primaryAction || resolvedSecondaryAction);
|
|
117
108
|
const body = children !== null && children !== void 0 ? children : content;
|
|
118
109
|
const resolvedWidth = typeof width === 'number' ? `${width}px` : typeof width === 'string' ? width : undefined;
|
|
@@ -9,8 +9,17 @@
|
|
|
9
9
|
padding: clamp(var(--spacing-sm), 10vh, var(--spacing-xl));
|
|
10
10
|
z-index: var(--z-backdrop-modal);
|
|
11
11
|
|
|
12
|
-
/*
|
|
13
|
-
|
|
12
|
+
/* Always a scroll container so overscroll-behavior can absorb wheel/touch
|
|
13
|
+
events and prevent the background page from scrolling. */
|
|
14
|
+
overflow-y: scroll;
|
|
15
|
+
overscroll-behavior: none;
|
|
16
|
+
|
|
17
|
+
/* Hide the overlay's own scrollbar — it's only there to absorb events */
|
|
18
|
+
scrollbar-width: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.overlay::-webkit-scrollbar {
|
|
22
|
+
display: none;
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
/* Default width can be overridden by --modal-width from props */
|
|
@@ -5,6 +5,16 @@ import * as React from 'react';
|
|
|
5
5
|
import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
7
|
import styles from './Popover.module.css';
|
|
8
|
+
function assignRef(ref, value) {
|
|
9
|
+
if (!ref)
|
|
10
|
+
return;
|
|
11
|
+
if (typeof ref === 'function') {
|
|
12
|
+
ref(value);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
;
|
|
16
|
+
ref.current = value;
|
|
17
|
+
}
|
|
8
18
|
function getFocusable(container) {
|
|
9
19
|
const els = container.querySelectorAll([
|
|
10
20
|
'a[href]',
|
|
@@ -38,7 +48,7 @@ function parseMinWidthPx(minWidth, elForEm) {
|
|
|
38
48
|
}
|
|
39
49
|
return 0;
|
|
40
50
|
}
|
|
41
|
-
export const Popover = forwardRef(function Popover({ trigger: Trigger, children, open, defaultOpen = false, onOpenChange, contentId, minWidth = '200px', matchTriggerWidth = true, viewportPadding = 8, edgeBuffer = 100, dataCy, fullWidth = false, fillTrigger = false, autoFocusContent = false, returnFocus = true, anchorRef, overlayRef, }, ref) {
|
|
51
|
+
export const Popover = forwardRef(function Popover({ trigger: Trigger, children, open, defaultOpen = false, onOpenChange, contentId, minWidth = '200px', matchTriggerWidth = true, viewportPadding = 8, contentMaxHeightPx = 400, edgeBuffer = 100, dataCy, fullWidth = false, fillTrigger = false, autoFocusContent = false, returnFocus = true, anchorRef, overlayRef, }, ref) {
|
|
42
52
|
const internalId = useId();
|
|
43
53
|
const resolvedContentId = contentId !== null && contentId !== void 0 ? contentId : `popover-${internalId}`;
|
|
44
54
|
const isControlled = open !== undefined;
|
|
@@ -211,14 +221,7 @@ export const Popover = forwardRef(function Popover({ trigger: Trigger, children,
|
|
|
211
221
|
}, [isOpen, returnFocus]);
|
|
212
222
|
const icon = isOpen ? _jsx(ChevronUp, { size: 20 }) : _jsx(ChevronDown, { size: 20 });
|
|
213
223
|
const setOverlayRef = React.useCallback((node) => {
|
|
214
|
-
|
|
215
|
-
return;
|
|
216
|
-
if (typeof overlayRef === 'function') {
|
|
217
|
-
overlayRef(node);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
const mutableRef = overlayRef;
|
|
221
|
-
mutableRef.current = node;
|
|
224
|
+
assignRef(overlayRef, node);
|
|
222
225
|
}, [overlayRef]);
|
|
223
226
|
return (_jsxs("div", { className: [
|
|
224
227
|
styles.container,
|
|
@@ -244,7 +247,7 @@ export const Popover = forwardRef(function Popover({ trigger: Trigger, children,
|
|
|
244
247
|
minWidth,
|
|
245
248
|
width: triggerWidth != null ? `${triggerWidth}px` : undefined,
|
|
246
249
|
maxWidth: `calc(100vw - ${viewportPadding * 2}px)`,
|
|
247
|
-
maxHeight: `clamp(100px, calc(100vh - ${viewportPadding * 2}px),
|
|
250
|
+
maxHeight: `clamp(100px, calc(100vh - ${viewportPadding * 2}px), ${contentMaxHeightPx}px)`,
|
|
248
251
|
visibility: positioned ? undefined : 'hidden',
|
|
249
252
|
}, "data-cy": dataCy !== null && dataCy !== void 0 ? dataCy : 'popover-content', children: typeof children === 'function'
|
|
250
253
|
? children(() => closePopover('api'))
|