@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,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { Severity } from '../../constants/severity.types';
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { Severity } from '../../constants/severity.types';
|
|
3
3
|
export interface AccordionItem {
|
|
4
4
|
header: string;
|
|
5
5
|
headerIcon?: ReactNode;
|
|
@@ -1,42 +1,43 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useId, useMemo, useState } from 'react';
|
|
4
4
|
import styles from './Accordion.module.css';
|
|
5
|
-
import {
|
|
5
|
+
import { AccordionRow } from './components/AccordionRow';
|
|
6
6
|
function uniqSorted(nums) {
|
|
7
7
|
return Array.from(new Set(nums)).sort((a, b) => a - b);
|
|
8
8
|
}
|
|
9
|
+
function normalizeMultiple(indexes) {
|
|
10
|
+
return uniqSorted((indexes !== null && indexes !== void 0 ? indexes : []).filter(n => Number.isFinite(n)));
|
|
11
|
+
}
|
|
9
12
|
export function Accordion({ items, mode = 'single', size = 'md', defaultOpenIndex = null, defaultOpenIndexes = [], openIndex, openIndexes, onOpenIndexChange, onOpenIndexesChange, }) {
|
|
10
13
|
const uid = useId();
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const [
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
? uniqSorted(openIndexes)
|
|
19
|
-
: internalOpenIndexes
|
|
20
|
-
: [], [mode, isControlledMultiple, openIndexes, internalOpenIndexes]);
|
|
21
|
-
const openSet = useMemo(() => new Set(mode === 'single'
|
|
22
|
-
? currentOpenIndex !== null
|
|
23
|
-
? [currentOpenIndex]
|
|
24
|
-
: []
|
|
25
|
-
: currentOpenIndexes), [mode, currentOpenIndex, currentOpenIndexes]);
|
|
26
|
-
function setSingle(next) {
|
|
27
|
-
if (isControlledSingle)
|
|
28
|
-
onOpenIndexChange === null || onOpenIndexChange === void 0 ? void 0 : onOpenIndexChange(next);
|
|
29
|
-
else {
|
|
30
|
-
setInternalOpenIndex(next);
|
|
31
|
-
onOpenIndexChange === null || onOpenIndexChange === void 0 ? void 0 : onOpenIndexChange(next);
|
|
14
|
+
const isControlled = mode === 'single' ? openIndex !== undefined : openIndexes !== undefined;
|
|
15
|
+
const [internalSingle, setInternalSingle] = useState(mode === 'single' ? defaultOpenIndex : null);
|
|
16
|
+
const [internalMultiple, setInternalMultiple] = useState(mode === 'multiple' ? normalizeMultiple(defaultOpenIndexes) : []);
|
|
17
|
+
const currentOpenIndexes = useMemo(() => {
|
|
18
|
+
if (mode === 'single') {
|
|
19
|
+
const current = isControlled ? (openIndex !== null && openIndex !== void 0 ? openIndex : null) : internalSingle;
|
|
20
|
+
return current === null ? [] : [current];
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
return isControlled ? normalizeMultiple(openIndexes) : internalMultiple;
|
|
23
|
+
}, [mode, isControlled, openIndex, openIndexes, internalSingle, internalMultiple]);
|
|
24
|
+
const openSet = useMemo(() => new Set(currentOpenIndexes), [currentOpenIndexes]);
|
|
25
|
+
function commit(nextIndexes) {
|
|
26
|
+
if (mode === 'single') {
|
|
27
|
+
const next = nextIndexes.length ? nextIndexes[0] : null;
|
|
28
|
+
if (isControlled)
|
|
29
|
+
onOpenIndexChange === null || onOpenIndexChange === void 0 ? void 0 : onOpenIndexChange(next);
|
|
30
|
+
else {
|
|
31
|
+
setInternalSingle(next);
|
|
32
|
+
onOpenIndexChange === null || onOpenIndexChange === void 0 ? void 0 : onOpenIndexChange(next);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const normalized = uniqSorted(nextIndexes);
|
|
37
|
+
if (isControlled)
|
|
37
38
|
onOpenIndexesChange === null || onOpenIndexesChange === void 0 ? void 0 : onOpenIndexesChange(normalized);
|
|
38
39
|
else {
|
|
39
|
-
|
|
40
|
+
setInternalMultiple(normalized);
|
|
40
41
|
onOpenIndexesChange === null || onOpenIndexesChange === void 0 ? void 0 : onOpenIndexesChange(normalized);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -44,23 +45,15 @@ export function Accordion({ items, mode = 'single', size = 'md', defaultOpenInde
|
|
|
44
45
|
const item = items[index];
|
|
45
46
|
if (!item || item.disabled)
|
|
46
47
|
return;
|
|
48
|
+
const isOpen = openSet.has(index);
|
|
47
49
|
if (mode === 'single') {
|
|
48
|
-
|
|
49
|
-
setSingle(isOpen ? null : index);
|
|
50
|
+
commit(isOpen ? [] : [index]);
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
|
-
// multiple
|
|
53
|
-
const isOpen = openSet.has(index);
|
|
54
53
|
if (isOpen)
|
|
55
|
-
|
|
54
|
+
commit(currentOpenIndexes.filter(i => i !== index));
|
|
56
55
|
else
|
|
57
|
-
|
|
56
|
+
commit([...currentOpenIndexes, index]);
|
|
58
57
|
}
|
|
59
|
-
return (_jsx("div", { className: `${styles.container} ${styles[size]}`, children: items.map((item, i) => {
|
|
60
|
-
const isOpen = openSet.has(i);
|
|
61
|
-
const isDisabled = !!item.disabled;
|
|
62
|
-
const buttonId = `${uid}-acc-btn-${i}`;
|
|
63
|
-
const panelId = `${uid}-acc-panel-${i}`;
|
|
64
|
-
return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => toggle(i), disabled: isDisabled, children: [_jsx("span", { className: styles.title, children: _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, allowWrap: isOpen, children: item.header }) }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx("svg", { viewBox: "0 0 20 20", focusable: "false", children: _jsx("path", { d: "M5.5 7.5L10 12l4.5-4.5", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })] }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: styles.panel, "data-open": isOpen ? 'true' : 'false', children: _jsx("div", { className: styles.content, children: item.children }) })] }, i));
|
|
65
|
-
}) }));
|
|
58
|
+
return (_jsx("div", { className: `${styles.container} ${styles[size]}`, children: items.map((item, i) => (_jsx(AccordionRow, { uid: uid, index: i, item: item, isOpen: openSet.has(i), onToggle: toggle }, i))) }));
|
|
66
59
|
}
|
|
@@ -8,80 +8,21 @@
|
|
|
8
8
|
gap: var(--spacing-xxs);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
align-items: center;
|
|
17
|
-
justify-content: space-between;
|
|
18
|
-
gap: var(--spacing-sm);
|
|
19
|
-
cursor: pointer;
|
|
20
|
-
user-select: none;
|
|
21
|
-
padding: var(--spacing-xs) var(--spacing-md);
|
|
22
|
-
background: var(--color-bg-contextual);
|
|
23
|
-
|
|
24
|
-
/* IMPORTANT: allow flex children to actually shrink */
|
|
25
|
-
min-width: 0;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.trigger:focus-visible {
|
|
29
|
-
outline: none;
|
|
30
|
-
box-shadow: var(--focus-ring);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.disabled .trigger {
|
|
34
|
-
cursor: not-allowed;
|
|
35
|
-
color: var(--color-disabled-fg);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.title {
|
|
39
|
-
/* IMPORTANT: this is the shrinking area that contains Headline */
|
|
40
|
-
display: flex;
|
|
41
|
-
align-items: center;
|
|
42
|
-
min-width: 0;
|
|
43
|
-
flex: 1 1 auto;
|
|
44
|
-
|
|
45
|
-
/* ensures any overflow is clipped so ellipsis can show */
|
|
46
|
-
overflow: hidden;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.chevron {
|
|
50
|
-
width: var(--icon-size-md);
|
|
51
|
-
height: var(--icon-size-md);
|
|
52
|
-
flex: 0 0 auto;
|
|
53
|
-
transition: transform var(--transition-normal) var(--ease-standard);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.open .chevron {
|
|
57
|
-
transform: rotate(180deg);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/* Collapsible panel using max-height */
|
|
61
|
-
.panel {
|
|
62
|
-
overflow: hidden;
|
|
63
|
-
max-height: 0;
|
|
64
|
-
transition: max-height var(--transition-slow) var(--ease-decelerate);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.panel[data-open='true'] {
|
|
68
|
-
max-height: 999px;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.content {
|
|
72
|
-
padding: var(--spacing-md) 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Sizes */
|
|
76
|
-
.sm .trigger,
|
|
77
|
-
.sm .content {
|
|
78
|
-
padding: var(--spacing-sm) var(--spacing-md);
|
|
11
|
+
/* Size variables (consumed by AccordionRow) */
|
|
12
|
+
.sm {
|
|
13
|
+
--acc-trigger-py: var(--spacing-sm);
|
|
14
|
+
--acc-trigger-px: var(--spacing-md);
|
|
15
|
+
--acc-content-py: var(--spacing-sm);
|
|
79
16
|
}
|
|
80
17
|
|
|
81
|
-
.md
|
|
82
|
-
|
|
18
|
+
.md {
|
|
19
|
+
--acc-trigger-py: var(--spacing-sm);
|
|
20
|
+
--acc-trigger-px: var(--spacing-md);
|
|
21
|
+
--acc-content-py: var(--spacing-md);
|
|
83
22
|
}
|
|
84
23
|
|
|
85
|
-
.lg
|
|
86
|
-
|
|
24
|
+
.lg {
|
|
25
|
+
--acc-trigger-py: var(--spacing-md);
|
|
26
|
+
--acc-trigger-px: var(--spacing-md);
|
|
27
|
+
--acc-content-py: var(--spacing-md);
|
|
87
28
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import { AccordionItem } from '../Accordion';
|
|
3
|
+
export type AccordionRowProps = {
|
|
4
|
+
uid: string;
|
|
5
|
+
index: number;
|
|
6
|
+
item: AccordionItem;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onToggle: (index: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function AccordionRow({ uid, index, item, isOpen, onToggle, }: AccordionRowProps): JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown } from 'lucide-react';
|
|
3
|
+
import { useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
+
import { Headline } from '../../../components/headline/Headline';
|
|
5
|
+
import styles from './AccordionRow.module.css';
|
|
6
|
+
function useCollapsibleHeight(isOpen) {
|
|
7
|
+
const innerRef = useRef(null);
|
|
8
|
+
const [height, setHeight] = useState('0px');
|
|
9
|
+
useLayoutEffect(() => {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const el = innerRef.current;
|
|
12
|
+
if (!el)
|
|
13
|
+
return;
|
|
14
|
+
const prefersReduced = typeof window !== 'undefined' &&
|
|
15
|
+
((_b = (_a = window.matchMedia) === null || _a === void 0 ? void 0 : _a.call(window, '(prefers-reduced-motion: reduce)')) === null || _b === void 0 ? void 0 : _b.matches);
|
|
16
|
+
if (prefersReduced) {
|
|
17
|
+
setHeight(isOpen ? 'auto' : '0px');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (isOpen) {
|
|
21
|
+
// 0 -> scrollHeight
|
|
22
|
+
setHeight('0px');
|
|
23
|
+
requestAnimationFrame(() => {
|
|
24
|
+
setHeight(`${el.scrollHeight}px`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// lock -> 0 (important when currently 'auto')
|
|
29
|
+
setHeight(`${el.scrollHeight}px`);
|
|
30
|
+
requestAnimationFrame(() => {
|
|
31
|
+
setHeight('0px');
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}, [isOpen]);
|
|
35
|
+
function onTransitionEnd(e) {
|
|
36
|
+
if (e.propertyName !== 'height')
|
|
37
|
+
return;
|
|
38
|
+
if (!innerRef.current)
|
|
39
|
+
return;
|
|
40
|
+
if (isOpen)
|
|
41
|
+
setHeight('auto');
|
|
42
|
+
}
|
|
43
|
+
return { innerRef, height, onTransitionEnd };
|
|
44
|
+
}
|
|
45
|
+
export function AccordionRow({ uid, index, item, isOpen, onToggle, }) {
|
|
46
|
+
const isDisabled = !!item.disabled;
|
|
47
|
+
const buttonId = `${uid}-acc-btn-${index}`;
|
|
48
|
+
const panelId = `${uid}-acc-panel-${index}`;
|
|
49
|
+
const { innerRef, height, onTransitionEnd } = useCollapsibleHeight(isOpen);
|
|
50
|
+
return (_jsxs("section", { className: `${styles.item} ${isOpen ? styles.open : ''} ${isDisabled ? styles.disabled : ''}`, children: [_jsx("h3", { className: styles.heading, children: _jsxs("button", { type: "button", id: buttonId, className: styles.trigger, "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => onToggle(index), disabled: isDisabled, children: [_jsxs("span", { className: styles.title, children: [item.headerIcon ? _jsx("span", { className: styles.icon, children: item.headerIcon }) : null, _jsx(Headline, { disableMargin: true, size: 4, weight: 500, severity: item.severity, allowWrap: isOpen, children: item.header })] }), _jsx("span", { className: styles.chevron, "aria-hidden": "true", children: _jsx(ChevronDown, {}) })] }) }), _jsx("div", { id: panelId, role: "region", "aria-labelledby": buttonId, className: styles.panel, style: { height }, onTransitionEnd: onTransitionEnd, children: _jsx("div", { ref: innerRef, className: styles.content, children: item.children }) })] }));
|
|
51
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
.heading {
|
|
2
|
+
margin: 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.trigger {
|
|
6
|
+
all: unset;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
|
|
9
|
+
width: 100%;
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
gap: var(--spacing-sm);
|
|
14
|
+
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
user-select: none;
|
|
17
|
+
|
|
18
|
+
padding: var(--acc-trigger-py) var(--acc-trigger-px);
|
|
19
|
+
background: var(--color-bg-contextual);
|
|
20
|
+
|
|
21
|
+
min-width: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.trigger:focus-visible {
|
|
25
|
+
outline: none;
|
|
26
|
+
box-shadow: var(--focus-ring);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.disabled .trigger {
|
|
30
|
+
cursor: not-allowed;
|
|
31
|
+
color: var(--color-disabled-fg);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.title {
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: var(--spacing-xs);
|
|
38
|
+
|
|
39
|
+
min-width: 0;
|
|
40
|
+
flex: 1 1 auto;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.icon {
|
|
45
|
+
flex: 0 0 auto;
|
|
46
|
+
display: inline-flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.chevron {
|
|
51
|
+
width: var(--icon-size-md);
|
|
52
|
+
height: var(--icon-size-md);
|
|
53
|
+
flex: 0 0 auto;
|
|
54
|
+
transition: transform var(--transition-normal) var(--ease-standard);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.chevron svg {
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
display: block;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.open .chevron {
|
|
64
|
+
transform: rotate(180deg);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.panel {
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
height: 0px;
|
|
70
|
+
transition: height var(--transition-slow) var(--ease-decelerate);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Padding inside so scrollHeight includes it */
|
|
74
|
+
.content {
|
|
75
|
+
padding: var(--acc-content-py) var(--acc-trigger-px);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@media (prefers-reduced-motion: reduce) {
|
|
79
|
+
.panel {
|
|
80
|
+
transition: none;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
line-height: 1;
|
|
14
14
|
|
|
15
15
|
height: var(--component-size-md);
|
|
16
|
-
min-block-size:
|
|
16
|
+
min-block-size: var(--component-size-md);
|
|
17
17
|
|
|
18
|
-
padding-inline: var(--
|
|
19
|
-
padding-block:
|
|
18
|
+
padding-inline: var(--spacing-sm);
|
|
19
|
+
padding-block: var(--spacing-xs);
|
|
20
20
|
|
|
21
21
|
border-width: var(--border-width-thin, 1px);
|
|
22
22
|
border-style: solid;
|
|
@@ -86,19 +86,19 @@
|
|
|
86
86
|
|
|
87
87
|
.button.xs {
|
|
88
88
|
height: var(--component-size-xs);
|
|
89
|
-
min-block-size:
|
|
89
|
+
min-block-size: var(--component-size-sm);
|
|
90
90
|
padding-inline: var(--spacing-sm);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
.button.sm {
|
|
94
94
|
height: var(--component-size-sm);
|
|
95
|
-
min-block-size:
|
|
95
|
+
min-block-size: var(--component-size-sm);
|
|
96
96
|
padding-inline: var(--spacing-sm);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
.button.xs {
|
|
100
100
|
height: var(--component-size-xs);
|
|
101
|
-
min-block-size:
|
|
101
|
+
min-block-size: var(--component-size-xs);
|
|
102
102
|
padding-inline: var(--spacing-xs);
|
|
103
103
|
font-size: var(--font-size-xs);
|
|
104
104
|
}
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
|
|
111
111
|
.button.lg {
|
|
112
112
|
height: var(--component-size-lg);
|
|
113
|
-
min-block-size:
|
|
113
|
+
min-block-size: var(--component-size-lg);
|
|
114
114
|
padding-inline: var(--spacing-lg);
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { Severity } from '../../constants/severity.types';
|
|
3
|
+
import { CardMeta, CardMetaRow } from './components/CardMeta';
|
|
4
4
|
type CardVariant = 'default' | 'subtle' | 'strong';
|
|
5
5
|
type CardSize = 'sm' | 'md' | 'lg';
|
|
6
6
|
type CardImagePlacement = 'left' | 'right' | 'top';
|
|
7
|
-
interface CardProps {
|
|
7
|
+
export interface CardProps {
|
|
8
8
|
title?: string;
|
|
9
9
|
loading?: boolean;
|
|
10
10
|
variant?: CardVariant;
|
|
@@ -20,8 +20,17 @@ interface CardProps {
|
|
|
20
20
|
sectionTitle?: string;
|
|
21
21
|
showSectionDivider?: boolean;
|
|
22
22
|
children?: ReactNode;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Keep current behavior: if provided, Card becomes "linked".
|
|
25
|
+
* NOTE: this assumes your Hyperlink component can render the passed element correctly.
|
|
26
|
+
* If Hyperlink expects an <a>, pass <a href="...">...</a> or whatever your existing pattern is.
|
|
27
|
+
*/
|
|
28
|
+
link?: ReactNode;
|
|
24
29
|
width?: 25 | 33 | 50 | 66 | 75 | 100;
|
|
25
30
|
}
|
|
26
|
-
|
|
31
|
+
type CardComponent = ((props: CardProps) => JSX.Element) & {
|
|
32
|
+
Meta: typeof CardMeta;
|
|
33
|
+
MetaRow: typeof CardMetaRow;
|
|
34
|
+
};
|
|
35
|
+
export declare const Card: CardComponent;
|
|
27
36
|
export {};
|
|
@@ -1,22 +1,48 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import styles from './Card.module.css';
|
|
3
|
+
import { CardMeta, CardMetaRow } from './components/CardMeta';
|
|
4
4
|
import { Headline } from '../headline/Headline';
|
|
5
5
|
import { Hyperlink } from '../hyperlink/Hyperlink';
|
|
6
6
|
import { SkeletonLoaderItem } from '../skeleton-loader/skeleton-loader-item/SkeletonLoaderItem';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function getInnerPlacementClass(imgPlacement, s) {
|
|
8
|
+
switch (imgPlacement) {
|
|
9
|
+
case 'top':
|
|
10
|
+
return s.innerImgTop;
|
|
11
|
+
case 'right':
|
|
12
|
+
return s.innerImgRight;
|
|
13
|
+
case 'left':
|
|
14
|
+
default:
|
|
15
|
+
return s.innerImgLeft;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getVariantClass(variant, s) {
|
|
19
|
+
switch (variant) {
|
|
20
|
+
case 'subtle':
|
|
21
|
+
return s.variantSubtle;
|
|
22
|
+
case 'strong':
|
|
23
|
+
return s.variantStrong;
|
|
24
|
+
case 'default':
|
|
25
|
+
default:
|
|
26
|
+
return s.variantDefault;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function CardImpl({ title, loading = false, variant = 'default', size = 'md', headerMarker = true, headerIcon, severity, image, imgPlacement = 'left', mediaWidth, actions, headerMeta, sectionTitle, showSectionDivider = false, children, link, width, }) {
|
|
30
|
+
const outerStyle = width ? { ['--width']: `${width}%` } : undefined;
|
|
11
31
|
const mediaStyle = mediaWidth
|
|
12
32
|
? { ['--card-media-width']: `${mediaWidth}px` }
|
|
13
33
|
: undefined;
|
|
14
|
-
const innerPlacementClass = imgPlacement
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
34
|
+
const innerPlacementClass = getInnerPlacementClass(imgPlacement, styles);
|
|
35
|
+
const variantClass = getVariantClass(variant, styles);
|
|
36
|
+
const hasHeader = !!title || !!headerMeta;
|
|
37
|
+
const showSection = !loading && (showSectionDivider || !!sectionTitle);
|
|
38
|
+
const showBody = !loading && !!children;
|
|
39
|
+
const showActions = !loading && !!actions;
|
|
40
|
+
const inner = (_jsxs("div", { className: `${styles.inner} ${innerPlacementClass}`, children: [image ? (_jsx("div", { className: styles.media, style: mediaStyle, children: image })) : null, _jsxs("div", { className: styles.content, children: [hasHeader ? (_jsxs("header", { className: styles.header, children: [title ? (_jsx(Headline, { severity: severity, marker: headerMarker, icon: headerIcon, size: 4, weight: 500, disableMargin: true, children: title })) : null, headerMeta ? _jsx("div", { className: styles.headerMeta, children: headerMeta }) : null] })) : null, loading ? (_jsx("div", { className: styles.loadingList, "aria-busy": "true", "aria-live": "polite", children: Array.from({ length: 4 }, (_, index) => (_jsxs("div", { className: styles.loadingRow, children: [_jsx(SkeletonLoaderItem, {}), _jsx(SkeletonLoaderItem, { width: "100%" })] }, index))) })) : null, showSection ? (_jsxs("div", { className: styles.section, children: [showSectionDivider ? _jsx("div", { className: styles.sectionDivider }) : null, sectionTitle ? _jsx("div", { className: styles.sectionTitle, children: sectionTitle }) : null] })) : null, showBody ? _jsx("div", { className: styles.body, children: children }) : null, showActions ? _jsx("div", { className: styles.actions, children: actions }) : null] })] }));
|
|
41
|
+
// keep existing behavior
|
|
42
|
+
const cardContent = link ? _jsx(Hyperlink, { children: link }) : inner;
|
|
43
|
+
return (_jsx("div", { className: `${styles.outerContainer} ${styles[size]}`, style: outerStyle, children: _jsx("div", { className: `${styles.container} ${variantClass}`, children: cardContent }) }));
|
|
22
44
|
}
|
|
45
|
+
export const Card = Object.assign(CardImpl, {
|
|
46
|
+
Meta: CardMeta,
|
|
47
|
+
MetaRow: CardMetaRow,
|
|
48
|
+
});
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
border-radius: var(--border-radius-md);
|
|
13
13
|
border: var(--border-width-thin) solid var(--color-border-subtle);
|
|
14
14
|
box-shadow: var(--shadow-xs);
|
|
15
|
-
padding-block: 0;
|
|
16
15
|
transition:
|
|
17
16
|
color var(--transition-fast) var(--ease-standard),
|
|
18
17
|
box-shadow var(--transition-fast) var(--ease-standard),
|
|
@@ -24,33 +23,31 @@
|
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
/* BACKGROUND VARIANTS */
|
|
27
|
-
.
|
|
26
|
+
.variantDefault {
|
|
28
27
|
background-color: var(--card-bg-default, var(--color-bg-surface));
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
.
|
|
30
|
+
.variantSubtle {
|
|
32
31
|
background-color: var(--card-bg-subtle, var(--color-bg-surface-subtle));
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
.
|
|
34
|
+
.variantStrong {
|
|
36
35
|
background-color: var(--card-bg-strong, var(--color-bg-surface-strong));
|
|
37
36
|
color: var(--card-fg-on-strong, var(--color-fg-on-strong));
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
/* SIZE VARIANTS */
|
|
41
|
-
.sm
|
|
42
|
-
|
|
43
|
-
gap: var(--spacing-md);
|
|
39
|
+
/* SIZE VARIANTS (define vars once; inner uses them) */
|
|
40
|
+
.sm {
|
|
41
|
+
--card-pad: var(--spacing-md);
|
|
42
|
+
--card-gap: var(--spacing-md);
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
gap: var(--spacing-lg);
|
|
44
|
+
.md {
|
|
45
|
+
--card-pad: var(--spacing-lg);
|
|
46
|
+
--card-gap: var(--spacing-lg);
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
gap: var(--spacing-xl);
|
|
48
|
+
.lg {
|
|
49
|
+
--card-pad: var(--spacing-xl);
|
|
50
|
+
--card-gap: var(--spacing-xl);
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
/* INNER LAYOUT */
|
|
@@ -58,6 +55,8 @@
|
|
|
58
55
|
margin-inline: auto;
|
|
59
56
|
display: flex;
|
|
60
57
|
align-items: flex-start;
|
|
58
|
+
padding: var(--card-pad);
|
|
59
|
+
gap: var(--card-gap);
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
/* Media placement */
|
|
@@ -103,8 +102,8 @@
|
|
|
103
102
|
|
|
104
103
|
/* CONTENT */
|
|
105
104
|
.content {
|
|
106
|
-
flex: 1 1 auto;
|
|
107
|
-
min-inline-size: 0;
|
|
105
|
+
flex: 1 1 auto;
|
|
106
|
+
min-inline-size: 0;
|
|
108
107
|
display: flex;
|
|
109
108
|
flex-direction: column;
|
|
110
109
|
gap: var(--spacing-md);
|
|
@@ -118,11 +117,6 @@
|
|
|
118
117
|
gap: var(--spacing-sm);
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
.value {
|
|
122
|
-
font-weight: 600;
|
|
123
|
-
white-space: nowrap;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
120
|
.headerMeta {
|
|
127
121
|
display: flex;
|
|
128
122
|
align-items: center;
|
|
@@ -156,18 +150,18 @@
|
|
|
156
150
|
}
|
|
157
151
|
|
|
158
152
|
.sectionTitle {
|
|
159
|
-
font-weight:
|
|
153
|
+
font-weight: var(--font-weight-semibold);
|
|
160
154
|
}
|
|
161
155
|
|
|
162
156
|
/* BODY */
|
|
163
157
|
.body {
|
|
164
|
-
/*
|
|
158
|
+
/* neutral container */
|
|
165
159
|
}
|
|
166
160
|
|
|
167
|
-
/* ACTIONS
|
|
161
|
+
/* ACTIONS */
|
|
168
162
|
.actions {
|
|
169
|
-
margin-top: auto;
|
|
163
|
+
margin-top: auto;
|
|
170
164
|
display: flex;
|
|
171
|
-
justify-content: flex-end;
|
|
165
|
+
justify-content: flex-end;
|
|
172
166
|
gap: var(--spacing-sm);
|
|
173
167
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes, JSX, ReactNode } from 'react';
|
|
2
|
+
export type CardMetaProps = {
|
|
3
|
+
columns?: 1 | 2 | 3;
|
|
4
|
+
className?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
} & Omit<HTMLAttributes<HTMLDListElement>, 'children'>;
|
|
7
|
+
export type CardMetaRowProps = {
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
value: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
nowrapValue?: boolean;
|
|
12
|
+
labelWidth?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function CardMeta({ columns, className, children, ...rest }: CardMetaProps): JSX.Element;
|
|
15
|
+
export declare function CardMetaRow({ label, value, className, nowrapValue, labelWidth, }: CardMetaRowProps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import styles from './CardMeta.module.css';
|
|
3
|
+
function getColsClass(columns) {
|
|
4
|
+
switch (columns) {
|
|
5
|
+
case 1:
|
|
6
|
+
return styles.cols1;
|
|
7
|
+
case 3:
|
|
8
|
+
return styles.cols3;
|
|
9
|
+
case 2:
|
|
10
|
+
default:
|
|
11
|
+
return styles.cols2;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function CardMeta({ columns = 2, className, children, ...rest }) {
|
|
15
|
+
const colsClass = getColsClass(columns);
|
|
16
|
+
return (_jsx("dl", { ...rest, className: [styles.grid, colsClass, className].filter(Boolean).join(' '), children: children }));
|
|
17
|
+
}
|
|
18
|
+
export function CardMetaRow({ label, value, className, nowrapValue, labelWidth, }) {
|
|
19
|
+
return (_jsxs("div", { className: [styles.row, className].filter(Boolean).join(' '), style: labelWidth ? { ['--label-width']: labelWidth } : undefined, children: [_jsx("dt", { className: styles.label, children: label }), _jsx("dd", { className: [styles.value, nowrapValue ? styles.nowrap : ''].filter(Boolean).join(' '), children: value })] }));
|
|
20
|
+
}
|