@dbcdk/react-components 0.0.10 → 0.0.12
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/card/Card.d.ts +21 -3
- package/dist/components/card/Card.js +17 -2
- package/dist/components/card/Card.module.css +59 -0
- 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/filter-field/FilterField.js +16 -11
- package/dist/components/filter-field/FilterField.module.css +133 -12
- 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/input/Input.js +1 -1
- package/dist/components/forms/input/Input.module.css +1 -0
- package/dist/components/forms/input-container/InputContainer.module.css +1 -1
- 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/menu/Menu.d.ts +32 -0
- package/dist/components/menu/Menu.js +73 -13
- package/dist/components/menu/Menu.module.css +72 -4
- package/dist/components/overlay/modal/Modal.module.css +2 -2
- package/dist/components/overlay/side-panel/SidePanel.js +17 -0
- package/dist/components/overlay/side-panel/SidePanel.module.css +0 -2
- package/dist/components/overlay/tooltip/useTooltipTrigger.js +4 -2
- package/dist/components/popover/Popover.js +1 -1
- 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/state-page/StatePage.module.css +1 -1
- package/dist/components/table/Table.d.ts +9 -4
- package/dist/components/table/Table.js +3 -6
- package/dist/components/table/Table.module.css +18 -5
- 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/tanstack.d.ts +12 -1
- package/dist/components/table/tanstack.js +75 -23
- package/dist/hooks/useTableSettings.d.ts +23 -4
- package/dist/hooks/useTableSettings.js +64 -17
- package/dist/src/styles/styles.css +38 -22
- package/dist/styles/animation.d.ts +5 -0
- package/dist/styles/animation.js +5 -0
- package/dist/styles/styles.css +38 -22
- package/dist/utils/localStorage.utils.d.ts +19 -0
- package/dist/utils/localStorage.utils.js +78 -0
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { ReactNode, JSX, HTMLAttributes } from 'react';
|
|
1
2
|
import { JSXElementConstructor, ReactElement } from 'react';
|
|
2
|
-
import type { ReactNode, JSX } from 'react';
|
|
3
3
|
import { Severity } from '../../constants/severity.types';
|
|
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;
|
|
@@ -23,5 +23,23 @@ interface CardProps {
|
|
|
23
23
|
link?: ReactElement<any, string | JSXElementConstructor<any>>;
|
|
24
24
|
width?: 25 | 33 | 50 | 66 | 75 | 100;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
type CardMetaProps = {
|
|
27
|
+
columns?: 1 | 2 | 3;
|
|
28
|
+
className?: string;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
} & Omit<HTMLAttributes<HTMLDListElement>, 'children'>;
|
|
31
|
+
type CardMetaRowProps = {
|
|
32
|
+
label: ReactNode;
|
|
33
|
+
labelWidth?: string;
|
|
34
|
+
value: ReactNode;
|
|
35
|
+
className?: string;
|
|
36
|
+
nowrapValue?: boolean;
|
|
37
|
+
};
|
|
38
|
+
declare function CardMeta({ columns, className, children, ...rest }: CardMetaProps): JSX.Element;
|
|
39
|
+
declare function CardMetaRow({ label, value, className, nowrapValue, labelWidth, }: CardMetaRowProps): JSX.Element;
|
|
40
|
+
type CardComponent = ((props: CardProps) => JSX.Element) & {
|
|
41
|
+
Meta: typeof CardMeta;
|
|
42
|
+
MetaRow: typeof CardMetaRow;
|
|
43
|
+
};
|
|
44
|
+
export declare const Card: CardComponent;
|
|
27
45
|
export {};
|
|
@@ -4,7 +4,7 @@ import styles from './Card.module.css';
|
|
|
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
|
-
|
|
7
|
+
function CardImpl({ title, loading, variant = 'default', size = 'md', headerMarker = true, headerIcon, severity, image, imgPlacement = 'left', mediaWidth, actions, headerMeta, sectionTitle, showSectionDivider = false, children, link, width, }) {
|
|
8
8
|
const outerContainerStyle = width
|
|
9
9
|
? { ['--width']: `${width}%` }
|
|
10
10
|
: undefined;
|
|
@@ -17,6 +17,21 @@ export function Card({ title, loading, variant = 'default', size = 'md', headerM
|
|
|
17
17
|
? styles.innerImgRight
|
|
18
18
|
: styles.innerImgLeft;
|
|
19
19
|
const inner = (_jsxs("div", { className: `${styles.inner} ${innerPlacementClass}`, children: [image && (_jsx("div", { className: styles.media, style: mediaStyle, children: image })), _jsxs("div", { className: styles.content, children: [(title || headerMeta) && (_jsxs("header", { className: styles.header, children: [title && (_jsx(Headline, { severity: severity, marker: headerMarker, icon: headerIcon, size: 4, weight: 500, disableMargin: true, children: title })), headerMeta && _jsx("div", { className: styles.headerMeta, children: headerMeta })] })), loading && (_jsx("div", { className: styles.loadingList, children: Array.from({ length: 4 }).map((_, index) => (_jsxs("div", { className: styles.loadingRow, children: [_jsx(SkeletonLoaderItem, {}), _jsx(SkeletonLoaderItem, { width: "100%" })] }, index))) })), !loading && (showSectionDivider || sectionTitle) && (_jsxs("div", { className: styles.section, children: [showSectionDivider && _jsx("div", { className: styles.sectionDivider }), sectionTitle && _jsx("div", { className: styles.sectionTitle, children: sectionTitle })] })), !loading && children && _jsx("div", { className: styles.body, children: children }), !loading && actions && _jsx("div", { className: styles.actions, children: actions })] })] }));
|
|
20
|
-
|
|
20
|
+
// keep existing behavior
|
|
21
|
+
const cardWithLink = link ? _jsx(Hyperlink, { children: link }) : inner;
|
|
21
22
|
return (_jsx("div", { className: `${styles.outerContainer} ${styles[size]}`, style: outerContainerStyle, children: _jsx("div", { className: [styles.container, styles[variant]].filter(Boolean).join(' '), children: cardWithLink }) }));
|
|
22
23
|
}
|
|
24
|
+
// ---- subcomponents (declared normally) ----
|
|
25
|
+
function CardMeta({ columns = 2, className, children, ...rest }) {
|
|
26
|
+
const colsClass = columns === 1 ? styles.metaCols1 : columns === 2 ? styles.metaCols2 : styles.metaCols3;
|
|
27
|
+
return (_jsx("dl", { ...rest, className: [styles.metaGrid, colsClass, className].filter(Boolean).join(' '), children: children }));
|
|
28
|
+
}
|
|
29
|
+
function CardMetaRow({ label, value, className, nowrapValue, labelWidth, }) {
|
|
30
|
+
return (_jsxs("div", { className: [styles.metaRow, className].filter(Boolean).join(' '), style: { '--label-width': labelWidth }, children: [_jsx("dt", { className: styles.metaLabel, children: label }), _jsx("dd", { className: [styles.metaValue, nowrapValue ? styles.metaNowrap : undefined]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(' '), children: value })] }));
|
|
33
|
+
}
|
|
34
|
+
export const Card = Object.assign(CardImpl, {
|
|
35
|
+
Meta: CardMeta,
|
|
36
|
+
MetaRow: CardMetaRow,
|
|
37
|
+
});
|
|
@@ -171,3 +171,62 @@
|
|
|
171
171
|
justify-content: flex-end; /* right-align buttons */
|
|
172
172
|
gap: var(--spacing-sm);
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
/* --- Meta grid primitives (optional) --- */
|
|
176
|
+
|
|
177
|
+
.metaGrid {
|
|
178
|
+
margin: 0;
|
|
179
|
+
display: grid;
|
|
180
|
+
gap: var(--spacing-xs) var(--spacing-lg);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.metaCols1 {
|
|
184
|
+
grid-template-columns: 1fr;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.metaCols2 {
|
|
188
|
+
grid-template-columns: 1fr;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.metaCols3 {
|
|
192
|
+
grid-template-columns: 1fr;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (min-width: 640px) {
|
|
196
|
+
.metaCols2 {
|
|
197
|
+
grid-template-columns: 1fr 1fr;
|
|
198
|
+
}
|
|
199
|
+
.metaCols3 {
|
|
200
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.metaRow {
|
|
205
|
+
--label-width: 120px;
|
|
206
|
+
display: grid;
|
|
207
|
+
grid-template-columns: var(--label-width, auto) 1fr;
|
|
208
|
+
gap: var(--spacing-xs);
|
|
209
|
+
align-items: baseline;
|
|
210
|
+
min-inline-size: 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.metaLabel {
|
|
214
|
+
font-size: var(--font-size-xs);
|
|
215
|
+
color: var(--color-fg-subtle);
|
|
216
|
+
letter-spacing: var(--letter-spacing-wide);
|
|
217
|
+
text-transform: uppercase;
|
|
218
|
+
margin: 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.metaValue {
|
|
222
|
+
margin: 0;
|
|
223
|
+
font-size: var(--font-size-sm);
|
|
224
|
+
color: var(--color-fg-default);
|
|
225
|
+
font-weight: var(--font-weight-medium);
|
|
226
|
+
min-inline-size: 0;
|
|
227
|
+
word-break: break-word;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.metaNowrap {
|
|
231
|
+
white-space: nowrap;
|
|
232
|
+
}
|
|
@@ -5,8 +5,9 @@ interface CircleProps {
|
|
|
5
5
|
severity: Severity;
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
glow?: boolean;
|
|
8
|
+
muted?: boolean;
|
|
8
9
|
pulse?: boolean;
|
|
9
10
|
size?: CircleSize;
|
|
10
11
|
}
|
|
11
|
-
export declare function Circle({ severity, children, glow, pulse, size }: CircleProps): JSX.Element;
|
|
12
|
+
export declare function Circle({ severity, children, glow, muted, pulse, size, }: CircleProps): JSX.Element;
|
|
12
13
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import styles from './Circle.module.css';
|
|
3
|
-
export function Circle({ severity, children, glow, pulse, size = 'sm' }) {
|
|
4
|
-
return (_jsxs("span", { className: styles.container, children: [_jsx("span", { "data-glow": glow, "data-pulse": pulse, "data-size": size, className: `${styles.circle} ${styles[severity]}` }), children] }));
|
|
3
|
+
export function Circle({ severity, children, glow, muted, pulse, size = 'sm', }) {
|
|
4
|
+
return (_jsxs("span", { className: styles.container, children: [_jsx("span", { "data-glow": glow, "data-pulse": pulse, "data-size": size, "data-muted": muted, className: `${styles.circle} ${styles[severity]}` }), children] }));
|
|
5
5
|
}
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
block-size: var(--component-size-sm);
|
|
17
17
|
border-radius: var(--border-radius-round);
|
|
18
18
|
flex-shrink: 0;
|
|
19
|
-
|
|
20
19
|
transition:
|
|
21
20
|
background-color var(--transition-fast) var(--ease-standard),
|
|
22
|
-
box-shadow var(--transition-fast) var(--ease-standard)
|
|
21
|
+
box-shadow var(--transition-fast) var(--ease-standard),
|
|
22
|
+
opacity var(--transition-normal) var(--ease-standard);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/* Density modifier (optional) */
|
|
@@ -77,6 +77,10 @@
|
|
|
77
77
|
block-size: 18px;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
.circle[data-muted='true'] {
|
|
81
|
+
opacity: 0.4;
|
|
82
|
+
}
|
|
83
|
+
|
|
80
84
|
.circle[data-size='lg'] {
|
|
81
85
|
inline-size: 22px;
|
|
82
86
|
block-size: 22px;
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import styles from './CodeBlock.module.css';
|
|
3
3
|
import { CopyButton } from '../copy-button/CopyButton';
|
|
4
4
|
export function CodeBlock({ code, copyButton, size = 'md' }) {
|
|
5
|
-
return (_jsxs("pre", { className: `${styles.container} ${styles[size]}`, children: [copyButton && (_jsx("span", { className: styles.copyButton, children: _jsx(CopyButton, { text: code }) })), _jsx("code", { className: styles.code, children: code })] }));
|
|
5
|
+
return (_jsxs("pre", { className: `${styles.container} ${styles[size]}`, tabIndex: 0, children: [copyButton && (_jsx("span", { className: styles.copyButton, children: _jsx(CopyButton, { style: "link", text: code }) })), _jsx("code", { className: styles.code, children: code })] }));
|
|
6
6
|
}
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
.container {
|
|
2
|
-
background-color: var(--color-bg-contextual);
|
|
3
2
|
position: relative;
|
|
4
|
-
padding: var(--spacing-sm);
|
|
5
|
-
border-radius: var(--border-radius-default);
|
|
6
|
-
border: var(--border-width-thin) solid var(--color-border-default);
|
|
7
3
|
margin-block: 0;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
|
|
5
|
+
background: var(--color-bg-contextual-subtle);
|
|
6
|
+
border: var(--border-width-thin) solid var(--color-border-default);
|
|
7
|
+
border-radius: var(--border-radius-lg);
|
|
8
|
+
box-shadow: var(--shadow-xs);
|
|
9
|
+
|
|
10
|
+
padding: var(--spacing-sm);
|
|
12
11
|
padding-inline-end: calc(var(--spacing-sm) + 40px);
|
|
12
|
+
|
|
13
|
+
font-family: var(--font-family-mono);
|
|
14
|
+
line-height: var(--line-height-relaxed);
|
|
15
|
+
|
|
16
|
+
overflow-x: auto;
|
|
17
|
+
overflow-y: hidden;
|
|
18
|
+
|
|
19
|
+
/* Nice: avoids layout shift if/when scrollbars appear (supported in modern browsers) */
|
|
20
|
+
scrollbar-gutter: stable;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.container:focus-within {
|
|
24
|
+
border-color: var(--color-border-selected);
|
|
25
|
+
box-shadow: var(--shadow-xs), var(--focus-ring);
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
.container.sm {
|
|
16
29
|
padding: var(--spacing-xs);
|
|
17
|
-
padding-inline-end: calc(var(--spacing-
|
|
18
|
-
min-height: 0;
|
|
30
|
+
padding-inline-end: calc(var(--spacing-xs) + 40px);
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
.container.sm .code {
|
|
@@ -25,6 +37,7 @@
|
|
|
25
37
|
.container.md .code {
|
|
26
38
|
font-size: var(--font-size-sm);
|
|
27
39
|
}
|
|
40
|
+
|
|
28
41
|
.container.lg .code {
|
|
29
42
|
font-size: var(--font-size-base);
|
|
30
43
|
}
|
|
@@ -33,11 +46,15 @@
|
|
|
33
46
|
display: block;
|
|
34
47
|
margin: 0;
|
|
35
48
|
font-family: var(--font-family-mono);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
color: var(--color-fg-default);
|
|
50
|
+
|
|
51
|
+
/* ✅ Preserve formatting but avoid ugly breaking */
|
|
52
|
+
white-space: pre-wrap;
|
|
53
|
+
overflow-wrap: anywhere;
|
|
54
|
+
word-break: normal; /* <- not break-all */
|
|
39
55
|
}
|
|
40
56
|
|
|
57
|
+
/* Copy button stays overlayed; does not affect layout */
|
|
41
58
|
.copyButton {
|
|
42
59
|
position: absolute;
|
|
43
60
|
top: var(--spacing-xs);
|
|
@@ -54,7 +71,3 @@
|
|
|
54
71
|
opacity: 1;
|
|
55
72
|
pointer-events: auto;
|
|
56
73
|
}
|
|
57
|
-
|
|
58
|
-
.copyButton button {
|
|
59
|
-
pointer-events: auto;
|
|
60
|
-
}
|
|
@@ -4,8 +4,9 @@ import { Check, Copy } from 'lucide-react';
|
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import styles from './CopyButton.module.css';
|
|
6
6
|
import { Button } from '../button/Button';
|
|
7
|
+
import { Hyperlink } from '../hyperlink/Hyperlink';
|
|
7
8
|
export function CopyButton(props) {
|
|
8
|
-
var _a;
|
|
9
|
+
var _a, _b;
|
|
9
10
|
const { text, variant = 'outlined', size = 'sm', children, ...rest } = props;
|
|
10
11
|
const [copied, setCopied] = useState(false);
|
|
11
12
|
const handleCopy = async () => {
|
|
@@ -18,5 +19,12 @@ export function CopyButton(props) {
|
|
|
18
19
|
console.error('Failed to copy: ', err);
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
|
-
|
|
22
|
+
if (props.style === 'link') {
|
|
23
|
+
return (_jsx(Hyperlink, { asChild: true, variant: "secondary", inline: props.variant === 'inline', children: _jsxs("button", { "aria-label": children ? '' : ((_a = rest['aria-label']) !== null && _a !== void 0 ? _a : 'Kopier til udklipsholder'), onClick: e => {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
e.stopPropagation();
|
|
26
|
+
handleCopy();
|
|
27
|
+
}, className: `${styles.link} ${copied ? styles.copied : ''}`, children: [children, copied ? _jsx(Check, {}) : _jsx(Copy, {})] }) }));
|
|
28
|
+
}
|
|
29
|
+
return (_jsxs(Button, { ...rest, "aria-label": children ? '' : ((_b = rest['aria-label']) !== null && _b !== void 0 ? _b : 'Kopier til udklipsholder'), onClick: handleCopy, variant: variant, size: size, children: [_jsx("span", { className: `${styles.container} ${copied ? styles.copied : ''}`, children: copied ? _jsx(Check, {}) : _jsx(Copy, {}) }), children] }));
|
|
22
30
|
}
|
|
@@ -61,10 +61,18 @@ function OperatorDropdown({ value, onChange, operators, size = 'sm', disabled, }
|
|
|
61
61
|
return (_jsx(Menu.Item, { active: selected, children: _jsxs("button", { type: "button", onClick: () => handleSelect(op), disabled: disabled, children: [_jsx("span", { style: { width: 16, display: 'inline-flex', justifyContent: 'center' }, children: selected ? _jsx(Check, { size: 16 }) : null }), LABELS[op]] }) }, op));
|
|
62
62
|
}) }) }));
|
|
63
63
|
}
|
|
64
|
+
function isFilterActive(value) {
|
|
65
|
+
if (Array.isArray(value))
|
|
66
|
+
return value.length > 0;
|
|
67
|
+
if (typeof value === 'string')
|
|
68
|
+
return value.trim().length > 0;
|
|
69
|
+
return value != null;
|
|
70
|
+
}
|
|
64
71
|
export function FilterField({ field, control, operator, value, onChange, operators, options = [], single = true, size = 'md', label, placeholder = 'Type value…', disabled, 'data-cy': dataCy, ...inputProps }) {
|
|
65
72
|
var _a, _b;
|
|
66
73
|
const [selectedOperator, setSelectedOperator] = useState(operator);
|
|
67
74
|
const ops = useMemo(() => operators !== null && operators !== void 0 ? operators : DEFAULT_TEXT_OPERATORS, [operators]);
|
|
75
|
+
const active = isFilterActive(value);
|
|
68
76
|
// Local state ONLY for input control (to avoid URL->props lag)
|
|
69
77
|
const [localValue, setLocalValue] = useState((_a = value) !== null && _a !== void 0 ? _a : '');
|
|
70
78
|
const debounceRef = useRef(null);
|
|
@@ -92,7 +100,7 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
92
100
|
debounceRef.current = setTimeout(() => {
|
|
93
101
|
isTypingRef.current = false;
|
|
94
102
|
emit({ value: nextVal });
|
|
95
|
-
}, 250);
|
|
103
|
+
}, 250);
|
|
96
104
|
};
|
|
97
105
|
// Sync internal value when parent value changes (e.g. URL updates)
|
|
98
106
|
useEffect(() => {
|
|
@@ -100,7 +108,6 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
100
108
|
if (control !== 'input')
|
|
101
109
|
return;
|
|
102
110
|
const incoming = (_a = value) !== null && _a !== void 0 ? _a : '';
|
|
103
|
-
// don't fight the user mid-typing; once parent catches up, we allow sync again
|
|
104
111
|
if (!isTypingRef.current && incoming !== localValue) {
|
|
105
112
|
setLocalValue(incoming);
|
|
106
113
|
}
|
|
@@ -115,25 +122,23 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
115
122
|
clearTimeout(debounceRef.current);
|
|
116
123
|
};
|
|
117
124
|
}, []);
|
|
118
|
-
return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className:
|
|
125
|
+
return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className: `${styles.filterField} ${styles[size]} ${active ? styles.active : ''}`, children: [label ? _jsx("span", { className: `${styles.label} ${styles[size]}`, children: label }) : null, _jsx(OperatorDropdown, { value: selectedOperator, onChange: op => emit({ operator: op }), operators: ops, size: size, disabled: disabled }), _jsx("div", { className: `${control === 'input' ? 'dbc-flex dbc-flex-grow' : styles.valueWrapper}`, children: control === 'input' ? (_jsx(Input, { ...inputProps, value: localValue, onChange: e => {
|
|
119
126
|
const next = e.currentTarget.value;
|
|
120
127
|
isTypingRef.current = true;
|
|
121
|
-
setLocalValue(next);
|
|
122
|
-
scheduleEmitValue(next);
|
|
123
|
-
}, inputSize: size, placeholder: placeholder, width: "160px", minWidth: "120px", disabled: disabled, onClear: () => {
|
|
128
|
+
setLocalValue(next);
|
|
129
|
+
scheduleEmitValue(next);
|
|
130
|
+
}, fullWidth: true, inputSize: size, placeholder: placeholder, width: "160px", minWidth: "120px", disabled: disabled, onClear: () => {
|
|
124
131
|
isTypingRef.current = false;
|
|
125
132
|
if (debounceRef.current)
|
|
126
133
|
clearTimeout(debounceRef.current);
|
|
127
134
|
setLocalValue('');
|
|
128
|
-
emit({ value: '' });
|
|
135
|
+
emit({ value: '' });
|
|
129
136
|
} })) : single ? (_jsx(Select, { options: options, selectedValue: (_b = value) !== null && _b !== void 0 ? _b : null, onChange: v => emit({ value: v }), placeholder: placeholder, size: size, variant: "inline", onClear: () => emit({ value: '' }) })) : (_jsx(MultiSelect, { options: options, size: size, variant: "inline", selectedValues: (Array.isArray(value) ? value : []), onChange: v => {
|
|
130
137
|
const current = new Set((Array.isArray(value) ? value : []));
|
|
131
|
-
if (current.has(v))
|
|
138
|
+
if (current.has(v))
|
|
132
139
|
current.delete(v);
|
|
133
|
-
|
|
134
|
-
else {
|
|
140
|
+
else
|
|
135
141
|
current.add(v);
|
|
136
|
-
}
|
|
137
142
|
emit({ value: Array.from(current) });
|
|
138
143
|
}, onClear: () => emit({ value: [] }), children: placeholder })) })] }));
|
|
139
144
|
}
|
|
@@ -10,16 +10,22 @@
|
|
|
10
10
|
border: var(--border-width-thin) solid var(--color-border-default);
|
|
11
11
|
border-radius: var(--border-radius-default);
|
|
12
12
|
|
|
13
|
+
position: relative;
|
|
14
|
+
|
|
13
15
|
transition:
|
|
14
16
|
border-color var(--transition-fast) var(--ease-standard),
|
|
15
17
|
box-shadow var(--transition-fast) var(--ease-standard),
|
|
16
18
|
background-color var(--transition-fast) var(--ease-standard);
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
/*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
/* More comfortable active state:
|
|
22
|
+
- less "blue outline" noise
|
|
23
|
+
- slightly warmer surface hint
|
|
24
|
+
*/
|
|
25
|
+
.filterField.active {
|
|
26
|
+
border-color: color-mix(in srgb, var(--color-border-default) 75%, var(--color-border-selected));
|
|
27
|
+
background: color-mix(in srgb, var(--color-bg-surface) 96%, var(--color-bg-selected));
|
|
28
|
+
}
|
|
23
29
|
|
|
24
30
|
.filterField.sm {
|
|
25
31
|
block-size: calc(var(--component-size-sm) + var(--density));
|
|
@@ -39,6 +45,7 @@
|
|
|
39
45
|
user-select: none;
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
/* Operator trigger */
|
|
42
49
|
.filterField .operatorTrigger {
|
|
43
50
|
display: inline-flex;
|
|
44
51
|
align-items: center;
|
|
@@ -68,30 +75,139 @@
|
|
|
68
75
|
color: var(--color-disabled-fg);
|
|
69
76
|
background: var(--color-disabled-bg);
|
|
70
77
|
}
|
|
71
|
-
|
|
72
78
|
.filterField .operatorText {
|
|
73
79
|
white-space: nowrap;
|
|
74
80
|
}
|
|
75
81
|
|
|
82
|
+
/* When active, operator is less dominant (calmer + more "token"-like) */
|
|
83
|
+
.filterField.active .operatorTrigger {
|
|
84
|
+
background: transparent;
|
|
85
|
+
color: var(--color-fg-muted);
|
|
86
|
+
}
|
|
87
|
+
.filterField.active .operatorTrigger:hover {
|
|
88
|
+
background: var(--opac-bg-dark);
|
|
89
|
+
color: var(--color-fg-default);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Wrapper for the select / multiselect control */
|
|
76
93
|
.filterField .valueWrapper {
|
|
77
94
|
display: inline-flex;
|
|
78
95
|
align-items: center;
|
|
79
96
|
padding: 0;
|
|
80
97
|
height: 100%;
|
|
98
|
+
|
|
99
|
+
flex: 1;
|
|
100
|
+
min-width: 0;
|
|
81
101
|
}
|
|
82
102
|
|
|
83
|
-
|
|
103
|
+
/* Ensure the control inside can stretch/shrink */
|
|
104
|
+
.filterField .valueWrapper > * {
|
|
84
105
|
height: 100%;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
106
|
+
width: 100%;
|
|
107
|
+
min-width: 0;
|
|
88
108
|
}
|
|
89
|
-
|
|
90
|
-
.valueWrapper button {
|
|
109
|
+
.filterField .valueWrapper > * > * {
|
|
91
110
|
height: 100% !important;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Active: emphasize VALUE area (but keep it soft) */
|
|
114
|
+
.filterField.active .valueWrapper {
|
|
115
|
+
background: color-mix(in srgb, var(--color-bg-surface) 88%, var(--color-bg-selected));
|
|
116
|
+
border-left: var(--border-width-thin) solid
|
|
117
|
+
color-mix(in srgb, var(--color-border-default) 80%, var(--color-border-selected));
|
|
118
|
+
border-top-right-radius: calc(var(--border-radius-default) - 1px);
|
|
119
|
+
border-bottom-right-radius: calc(var(--border-radius-default) - 1px);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* =========================
|
|
123
|
+
TRIGGER BUTTON TARGETING
|
|
124
|
+
========================= */
|
|
125
|
+
|
|
126
|
+
/* Select trigger button */
|
|
127
|
+
.filterField .valueWrapper :global(button[data-forminput]) {
|
|
128
|
+
width: 100%;
|
|
129
|
+
height: 100%;
|
|
130
|
+
border: 0 !important;
|
|
131
|
+
|
|
132
|
+
/* slightly more breathing room than before */
|
|
133
|
+
padding-inline: calc(var(--spacing-sm) + var(--spacing-2xs)) !important;
|
|
134
|
+
|
|
135
|
+
text-align: left;
|
|
136
|
+
justify-content: flex-start;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* MultiSelect trigger button (Popover container is a div; trigger is its direct child button) */
|
|
140
|
+
.filterField .valueWrapper > div > button {
|
|
141
|
+
width: 100%;
|
|
142
|
+
height: 100%;
|
|
92
143
|
border: 0 !important;
|
|
93
|
-
padding-inline: var(--spacing-sm) !important;
|
|
144
|
+
padding-inline: calc(var(--spacing-sm) + var(--spacing-2xs)) !important;
|
|
145
|
+
|
|
146
|
+
text-align: left;
|
|
147
|
+
justify-content: flex-start;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Slight spacing between clear (×) and chevron for Select + MultiSelect
|
|
151
|
+
(feels less cramped / more intentional) */
|
|
152
|
+
.filterField .valueWrapper :global(button[data-forminput]) :global(.dbc-flex),
|
|
153
|
+
.filterField .valueWrapper > div > button {
|
|
154
|
+
column-gap: var(--spacing-xs);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Make the internal Select layout behave */
|
|
158
|
+
.filterField .valueWrapper :global(.dbc-flex) {
|
|
159
|
+
width: 100% !important;
|
|
160
|
+
min-width: 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.filterField .valueWrapper :global(.dbc-flex > span:first-child) {
|
|
164
|
+
flex: 1;
|
|
165
|
+
min-width: 0;
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
text-overflow: ellipsis;
|
|
168
|
+
white-space: nowrap;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Keep ClearButton + chevron from stretching */
|
|
172
|
+
.filterField .valueWrapper :global(.dbc-flex) > *:not(span:first-child) {
|
|
173
|
+
flex: 0 0 auto;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* For MultiSelect: label + chip should truncate nicely */
|
|
177
|
+
.filterField .valueWrapper > div > button > span:first-child {
|
|
178
|
+
display: inline-flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: var(--spacing-xxs);
|
|
181
|
+
|
|
182
|
+
flex: 1;
|
|
183
|
+
min-width: 0;
|
|
184
|
+
overflow: hidden;
|
|
185
|
+
text-overflow: ellipsis;
|
|
186
|
+
white-space: nowrap;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Emphasize chosen value text in active state (but slightly less "boldy") */
|
|
190
|
+
.filterField.active .valueWrapper input {
|
|
191
|
+
font-weight: 550;
|
|
94
192
|
}
|
|
193
|
+
.filterField.active .valueWrapper :global(button[data-forminput]),
|
|
194
|
+
.filterField.active .valueWrapper > div > button {
|
|
195
|
+
font-weight: 550 !important;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Icons calmer by default; crisp on hover/focus */
|
|
199
|
+
.filterField.active .valueWrapper svg {
|
|
200
|
+
opacity: 0.72;
|
|
201
|
+
}
|
|
202
|
+
.filterField.active .valueWrapper:hover svg,
|
|
203
|
+
.filterField.active .valueWrapper :global(button[data-forminput]):focus-visible svg,
|
|
204
|
+
.filterField.active .valueWrapper > div > button:focus-visible svg {
|
|
205
|
+
opacity: 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* =========================
|
|
209
|
+
INPUT styling
|
|
210
|
+
========================= */
|
|
95
211
|
|
|
96
212
|
.filterField input {
|
|
97
213
|
appearance: none;
|
|
@@ -103,6 +219,11 @@
|
|
|
103
219
|
inline-size: auto;
|
|
104
220
|
min-inline-size: 10ch;
|
|
105
221
|
block-size: 100%;
|
|
222
|
+
border-top-left-radius: 0;
|
|
223
|
+
border-bottom-left-radius: 0;
|
|
224
|
+
|
|
225
|
+
/* a tiny bit more comfort */
|
|
226
|
+
padding-block: calc(var(--spacing-3xs) + var(--density));
|
|
106
227
|
}
|
|
107
228
|
|
|
108
229
|
.filterField button {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { JSX } from 'react';
|
|
2
|
+
import type { JSX, ReactNode } from 'react';
|
|
3
3
|
type Variant = 'default' | 'primary' | 'outlined';
|
|
4
4
|
type Size = 'sm' | 'md' | 'lg';
|
|
5
5
|
interface CheckboxProps {
|
|
@@ -8,7 +8,7 @@ interface CheckboxProps {
|
|
|
8
8
|
variant?: Variant;
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
modified?: boolean;
|
|
11
|
-
label?:
|
|
11
|
+
label?: ReactNode;
|
|
12
12
|
size?: Size;
|
|
13
13
|
containerLabel?: string;
|
|
14
14
|
error?: string;
|
|
@@ -66,7 +66,7 @@ export function CheckboxGroup({ label, options, selectedValues, onChange, onTogg
|
|
|
66
66
|
return (_jsxs("div", { className: wrapperClassName, "data-cy": dataCy, style: { ['--checkboxgroup-action-min-width']: actionMinWidth }, children: [label && _jsx("span", { className: styles.groupLabel, children: label }), _jsx("div", { className: itemsClassName, role: "group", "aria-label": label, children: options.map(opt => {
|
|
67
67
|
const isChecked = selectedSet.has(opt.value);
|
|
68
68
|
const isDisabled = disabled || !!opt.disabled;
|
|
69
|
-
return (_jsx(Checkbox, { label: opt.label, checked: isChecked, disabled: isDisabled, variant: variant, size:
|
|
69
|
+
return (_jsx(Checkbox, { label: opt.label, checked: isChecked, disabled: isDisabled, variant: variant, size: "sm", onChange: () => {
|
|
70
70
|
if (isDisabled)
|
|
71
71
|
return;
|
|
72
72
|
toggleValue(opt.value);
|
|
@@ -23,7 +23,7 @@ function mergeRefs(...refs) {
|
|
|
23
23
|
*/
|
|
24
24
|
export const Input = forwardRef(function Input({
|
|
25
25
|
// InputContainer props
|
|
26
|
-
label, error, helpText, orientation = '
|
|
26
|
+
label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = false, required, tooltip, tooltipPlacement = 'right', modified,
|
|
27
27
|
// Input-only props
|
|
28
28
|
icon, autoFocus, minWidth, width, inputSize = 'md', variant = 'outlined', onClear, onButtonClick, buttonLabel, buttonIcon,
|
|
29
29
|
// Native input props
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
*/
|
|
81
81
|
.inputContainer[data-modified] label:not(.label) {
|
|
82
82
|
background-color: color-mix(in srgb, var(--color-status-warning-bg) 35%, transparent);
|
|
83
|
-
border-radius: var(--border-radius-
|
|
83
|
+
border-radius: var(--border-radius-default);
|
|
84
84
|
padding: 2px 6px;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
className?: string;
|
|
2
|
+
type BaseProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
5
4
|
icon?: React.ReactNode;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
className?: string;
|
|
6
|
+
/**
|
|
7
|
+
* If true, Hyperlink will NOT render <a>.
|
|
8
|
+
* Instead, it will clone its only child (e.g. Next <Link>) and apply styling/props to it.
|
|
9
|
+
*/
|
|
10
|
+
asChild?: boolean;
|
|
11
|
+
variant?: 'primary' | 'secondary';
|
|
12
|
+
inline?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type AnchorProps = BaseProps & React.AnchorHTMLAttributes<HTMLAnchorElement> & {
|
|
15
|
+
as?: 'a';
|
|
16
|
+
};
|
|
17
|
+
type ButtonProps = BaseProps & React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
18
|
+
as: 'button';
|
|
19
|
+
};
|
|
20
|
+
type HyperlinkProps = AnchorProps | ButtonProps;
|
|
21
|
+
export declare function Hyperlink(props: HyperlinkProps): React.ReactElement;
|
|
10
22
|
export {};
|