@dbcdk/react-components 0.0.19 → 0.0.21
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/button/Button.module.css +21 -10
- package/dist/components/chip/Chip.d.ts +3 -2
- package/dist/components/chip/Chip.js +2 -1
- package/dist/components/chip/Chip.module.css +107 -67
- package/dist/components/code-block/CodeBlock.js +28 -19
- package/dist/components/code-block/CodeBlock.module.css +69 -67
- package/dist/components/filter-field/FilterField.d.ts +2 -1
- package/dist/components/filter-field/FilterField.js +4 -9
- package/dist/components/filter-field/FilterField.module.css +203 -152
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +2 -3
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +3 -5
- package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.module.css +13 -13
- package/dist/components/forms/input/Input.d.ts +1 -1
- package/dist/components/forms/input/Input.js +3 -9
- package/dist/components/forms/input/Input.module.css +106 -17
- package/dist/components/hyperlink/Hyperlink.module.css +14 -3
- package/dist/components/interval-select/IntervalSelect.js +2 -2
- package/dist/components/overlay/modal/Modal.d.ts +2 -1
- package/dist/components/overlay/modal/Modal.js +6 -6
- package/dist/components/overlay/modal/Modal.module.css +52 -19
- package/dist/components/popover/Popover.module.css +4 -1
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts +4 -2
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.js +17 -19
- package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +128 -20
- package/dist/components/sidebar/Sidebar.d.ts +2 -1
- package/dist/components/sidebar/Sidebar.js +2 -2
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +2 -3
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +2 -4
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +2 -1
- package/dist/components/table/Table.d.ts +6 -6
- package/dist/components/table/Table.js +153 -78
- package/dist/components/table/Table.module.css +335 -171
- package/dist/components/table/TanstackTable.d.ts +1 -1
- package/dist/components/table/TanstackTable.js +14 -12
- package/dist/components/table/table.utils.d.ts +1 -1
- package/dist/components/table/table.utils.js +2 -3
- package/dist/components/table/tanstackTable.utils.d.ts +9 -10
- package/dist/components/table/tanstackTable.utils.js +50 -30
- package/dist/hooks/useViewportFill.d.ts +6 -5
- package/dist/hooks/useViewportFill.js +43 -41
- package/dist/src/styles/styles.css +9 -3
- package/dist/styles/styles.css +9 -3
- package/dist/styles/themes/dbc/base.css +0 -3
- package/dist/styles/themes/dbc/dark.css +44 -12
- package/dist/styles/themes/dbc/light.css +33 -7
- package/package.json +1 -1
|
@@ -69,20 +69,16 @@ function isFilterActive(value) {
|
|
|
69
69
|
return value != null;
|
|
70
70
|
}
|
|
71
71
|
const VALUELESS_OPERATORS = ['isEmpty', 'isNotEmpty'];
|
|
72
|
-
export function FilterField({ field, control, operator, value, onChange, operators, options = [], single = true, size = 'md', label, placeholder = 'Type value…', disabled, 'data-cy': dataCy, width, ...inputProps }) {
|
|
72
|
+
export function FilterField({ field, control, operator, value, onChange, operators, options = [], single = true, size = 'md', variant = 'surface', label, placeholder = 'Type value…', disabled, 'data-cy': dataCy, width, ...inputProps }) {
|
|
73
73
|
var _a, _b;
|
|
74
74
|
const ops = useMemo(() => operators !== null && operators !== void 0 ? operators : DEFAULT_TEXT_OPERATORS, [operators]);
|
|
75
|
-
// internal operator state (source of truth for UI)
|
|
76
75
|
const [selectedOperator, setSelectedOperator] = useState(operator);
|
|
77
|
-
// "active" based on value only (as requested)
|
|
78
76
|
const active = isFilterActive(value);
|
|
79
|
-
// Overwrite internal operator if parent sends a new one
|
|
80
77
|
useEffect(() => {
|
|
81
78
|
if (ops.includes(operator)) {
|
|
82
79
|
setSelectedOperator(operator);
|
|
83
80
|
}
|
|
84
81
|
}, [operator, ops]);
|
|
85
|
-
// Local state ONLY for input control (to avoid URL->props lag)
|
|
86
82
|
const [localValue, setLocalValue] = useState((_a = value) !== null && _a !== void 0 ? _a : '');
|
|
87
83
|
const debounceRef = useRef(null);
|
|
88
84
|
const isTypingRef = useRef(false);
|
|
@@ -90,7 +86,6 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
90
86
|
var _a, _b;
|
|
91
87
|
const nextOperator = (_a = next.operator) !== null && _a !== void 0 ? _a : selectedOperator;
|
|
92
88
|
const nextValue = (_b = next.value) !== null && _b !== void 0 ? _b : value;
|
|
93
|
-
// Always keep internal operator in sync when user picks one
|
|
94
89
|
if (next.operator)
|
|
95
90
|
setSelectedOperator(nextOperator);
|
|
96
91
|
onChange({
|
|
@@ -117,7 +112,6 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
117
112
|
emit({ value: nextVal });
|
|
118
113
|
}, 250);
|
|
119
114
|
};
|
|
120
|
-
// Sync internal value when parent value changes (e.g. URL updates)
|
|
121
115
|
useEffect(() => {
|
|
122
116
|
var _a;
|
|
123
117
|
if (control !== 'input')
|
|
@@ -130,14 +124,15 @@ export function FilterField({ field, control, operator, value, onChange, operato
|
|
|
130
124
|
isTypingRef.current = false;
|
|
131
125
|
}
|
|
132
126
|
}, [value, control, localValue]);
|
|
133
|
-
// Cleanup debounce on unmount
|
|
134
127
|
useEffect(() => {
|
|
135
128
|
return () => {
|
|
136
129
|
if (debounceRef.current)
|
|
137
130
|
clearTimeout(debounceRef.current);
|
|
138
131
|
};
|
|
139
132
|
}, []);
|
|
140
|
-
return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className:
|
|
133
|
+
return (_jsxs("div", { ...(dataCy ? { 'data-cy': dataCy } : {}), className: [styles.filterField, styles[size], styles[variant], active ? styles.active : '']
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join(' '), children: [label ? _jsx("span", { className: `${styles.label} ${styles[size]}`, children: label }) : null, _jsx(OperatorDropdown, { value: selectedOperator, onChange: handleOperatorChange, operators: ops, size: size, disabled: disabled }), _jsx("div", { className: `${control === 'input' ? 'dbc-flex dbc-flex-grow' : styles.valueWrapper}`, style: { width }, children: control === 'input' ? (_jsx(Input, { variant: "embedded", ...inputProps, value: localValue, onChange: e => {
|
|
141
136
|
const next = e.currentTarget.value;
|
|
142
137
|
isTypingRef.current = true;
|
|
143
138
|
setLocalValue(next);
|
|
@@ -2,38 +2,157 @@
|
|
|
2
2
|
display: inline-flex;
|
|
3
3
|
align-items: stretch;
|
|
4
4
|
gap: 0;
|
|
5
|
+
position: relative;
|
|
6
|
+
z-index: 0;
|
|
5
7
|
font-size: var(--font-size-sm);
|
|
6
8
|
font-family: var(--font-family);
|
|
7
|
-
background: var(--color-bg-surface);
|
|
8
9
|
color: var(--color-fg-default);
|
|
9
|
-
|
|
10
|
-
border: var(--border-width-thin) solid var(--color-border-default);
|
|
10
|
+
border: 1px solid transparent;
|
|
11
11
|
border-radius: var(--border-radius-default);
|
|
12
|
-
|
|
13
|
-
position: relative;
|
|
14
|
-
|
|
12
|
+
overflow: visible;
|
|
15
13
|
transition:
|
|
14
|
+
background-color var(--transition-fast) var(--ease-standard),
|
|
16
15
|
border-color var(--transition-fast) var(--ease-standard),
|
|
17
16
|
box-shadow var(--transition-fast) var(--ease-standard),
|
|
18
|
-
|
|
17
|
+
color var(--transition-fast) var(--ease-standard);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* =========================
|
|
21
|
+
VARIANTS
|
|
22
|
+
========================= */
|
|
23
|
+
|
|
24
|
+
.filterField.surface {
|
|
25
|
+
background: var(--color-bg-surface);
|
|
26
|
+
border-color: var(--color-border-subtle);
|
|
27
|
+
box-shadow: var(--shadow-xs);
|
|
28
|
+
--filter-operator-bg: var(--color-bg-surface);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.filterField.surface:hover {
|
|
32
|
+
border-color: var(--color-border-default);
|
|
33
|
+
box-shadow: var(--shadow-sm);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.filterField.surface.active {
|
|
37
|
+
border-color: var(--color-border-default);
|
|
38
|
+
box-shadow: var(--shadow-sm);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.filterField.outlined {
|
|
42
|
+
background: var(--color-bg-surface);
|
|
43
|
+
border-color: var(--color-border-subtle);
|
|
44
|
+
box-shadow: none;
|
|
45
|
+
--filter-operator-bg: var(--color-bg-surface);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.filterField.outlined:hover {
|
|
49
|
+
border-color: var(--color-border-default);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.filterField.outlined.active {
|
|
53
|
+
border-color: var(--color-border-default);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.filterField.subtle {
|
|
57
|
+
background: var(--color-bg-toolbar);
|
|
58
|
+
border-color: transparent;
|
|
59
|
+
box-shadow: inset 0 0 0 1px transparent;
|
|
60
|
+
--filter-operator-bg: color-mix(in srgb, var(--color-fg-default) 4%, var(--color-bg-toolbar));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.filterField.subtle:hover {
|
|
64
|
+
background: var(--color-bg-surface-strong);
|
|
65
|
+
--filter-operator-bg: color-mix(
|
|
66
|
+
in srgb,
|
|
67
|
+
var(--color-fg-default) 6%,
|
|
68
|
+
var(--color-bg-surface-strong)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.filterField.subtle.active {
|
|
73
|
+
background: var(--color-bg-surface-strong);
|
|
74
|
+
--filter-operator-bg: color-mix(
|
|
75
|
+
in srgb,
|
|
76
|
+
var(--color-fg-default) 5%,
|
|
77
|
+
var(--color-bg-surface-strong)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* =========================
|
|
82
|
+
ACTIVE INDICATOR
|
|
83
|
+
========================= */
|
|
84
|
+
|
|
85
|
+
.filterField.active::before {
|
|
86
|
+
content: '';
|
|
87
|
+
position: absolute;
|
|
88
|
+
inset-inline-start: 0;
|
|
89
|
+
top: 1px;
|
|
90
|
+
bottom: 1px;
|
|
91
|
+
width: 3px;
|
|
92
|
+
border-top-left-radius: inherit;
|
|
93
|
+
border-bottom-left-radius: inherit;
|
|
94
|
+
background: var(--color-border-selected);
|
|
95
|
+
pointer-events: none;
|
|
96
|
+
z-index: 2;
|
|
19
97
|
}
|
|
20
98
|
|
|
21
|
-
/*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.filterField
|
|
26
|
-
|
|
27
|
-
|
|
99
|
+
/* =========================
|
|
100
|
+
FOCUS
|
|
101
|
+
========================= */
|
|
102
|
+
|
|
103
|
+
.filterField:focus-within {
|
|
104
|
+
z-index: 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.filterField.surface:focus-within,
|
|
108
|
+
.filterField.outlined:focus-within,
|
|
109
|
+
.filterField.subtle:focus-within {
|
|
110
|
+
border-color: var(--color-border-selected);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* subtle focus without inner outline */
|
|
114
|
+
.filterField.surface:focus-within {
|
|
115
|
+
box-shadow: var(--shadow-sm);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.filterField.outlined:focus-within,
|
|
119
|
+
.filterField.subtle:focus-within {
|
|
120
|
+
box-shadow: none;
|
|
28
121
|
}
|
|
29
122
|
|
|
123
|
+
/* stronger focus when filter is active */
|
|
124
|
+
|
|
125
|
+
.filterField.surface.active:focus-within {
|
|
126
|
+
box-shadow:
|
|
127
|
+
var(--shadow-sm),
|
|
128
|
+
inset 0 0 0 1px var(--color-border-selected);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.filterField.outlined.active:focus-within,
|
|
132
|
+
.filterField.subtle.active:focus-within {
|
|
133
|
+
box-shadow: inset 0 0 0 1px var(--color-border-selected);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* =========================
|
|
137
|
+
SIZES
|
|
138
|
+
========================= */
|
|
139
|
+
|
|
30
140
|
.filterField.sm {
|
|
31
141
|
block-size: var(--component-size-sm);
|
|
32
142
|
}
|
|
143
|
+
|
|
33
144
|
.filterField.md {
|
|
34
145
|
block-size: var(--component-size-md);
|
|
35
146
|
}
|
|
36
147
|
|
|
148
|
+
.filterField.lg {
|
|
149
|
+
block-size: var(--component-size-lg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* =========================
|
|
153
|
+
LABEL
|
|
154
|
+
========================= */
|
|
155
|
+
|
|
37
156
|
.filterField .label {
|
|
38
157
|
display: inline-flex;
|
|
39
158
|
align-items: center;
|
|
@@ -45,196 +164,128 @@
|
|
|
45
164
|
user-select: none;
|
|
46
165
|
}
|
|
47
166
|
|
|
48
|
-
/*
|
|
167
|
+
/* =========================
|
|
168
|
+
OPERATOR
|
|
169
|
+
========================= */
|
|
170
|
+
|
|
49
171
|
.filterField .operatorTrigger {
|
|
50
172
|
display: inline-flex;
|
|
51
173
|
align-items: center;
|
|
52
174
|
justify-content: center;
|
|
175
|
+
gap: var(--spacing-xxs);
|
|
53
176
|
height: 100%;
|
|
54
177
|
padding-block: var(--spacing-2xs);
|
|
55
178
|
padding-inline: var(--spacing-sm);
|
|
56
|
-
|
|
179
|
+
|
|
57
180
|
color: var(--color-fg-default);
|
|
181
|
+
background: transparent;
|
|
58
182
|
border: 0;
|
|
59
|
-
border-radius: 0;
|
|
60
183
|
cursor: pointer;
|
|
184
|
+
position: relative;
|
|
185
|
+
z-index: 0;
|
|
186
|
+
font-weight: var(--font-weight-default);
|
|
187
|
+
|
|
61
188
|
transition:
|
|
62
189
|
background-color var(--transition-fast) var(--ease-standard),
|
|
63
190
|
color var(--transition-fast) var(--ease-standard);
|
|
64
191
|
}
|
|
65
|
-
|
|
66
|
-
|
|
192
|
+
|
|
193
|
+
/* inset operator background */
|
|
194
|
+
|
|
195
|
+
.filterField .operatorTrigger::after {
|
|
196
|
+
content: '';
|
|
197
|
+
position: absolute;
|
|
198
|
+
inset: 1px 0;
|
|
199
|
+
background: var(--filter-operator-bg, transparent);
|
|
200
|
+
pointer-events: none;
|
|
201
|
+
z-index: 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.filterField .operatorTrigger > * {
|
|
205
|
+
position: relative;
|
|
206
|
+
z-index: 1;
|
|
67
207
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
208
|
+
|
|
209
|
+
/* =========================
|
|
210
|
+
SEPARATORS
|
|
211
|
+
========================= */
|
|
212
|
+
|
|
213
|
+
.filterField .operatorTrigger::before {
|
|
214
|
+
content: '';
|
|
215
|
+
position: absolute;
|
|
216
|
+
inset-inline-start: 0;
|
|
217
|
+
top: 8px;
|
|
218
|
+
bottom: 8px;
|
|
219
|
+
width: 1px;
|
|
220
|
+
background: var(--color-border-subtle);
|
|
221
|
+
pointer-events: none;
|
|
71
222
|
}
|
|
223
|
+
|
|
224
|
+
/* =========================
|
|
225
|
+
DISABLED
|
|
226
|
+
========================= */
|
|
227
|
+
|
|
72
228
|
.filterField .operatorTrigger:disabled,
|
|
73
229
|
.filterField .operatorTrigger[aria-disabled='true'] {
|
|
74
230
|
cursor: not-allowed;
|
|
75
231
|
color: var(--color-disabled-fg);
|
|
76
|
-
background: var(--color-disabled-bg);
|
|
77
|
-
}
|
|
78
|
-
.filterField .operatorText {
|
|
79
|
-
white-space: nowrap;
|
|
80
232
|
}
|
|
81
233
|
|
|
82
|
-
|
|
83
|
-
.filterField.
|
|
84
|
-
|
|
85
|
-
color: var(--color-fg-muted);
|
|
234
|
+
.filterField.surface .operatorTrigger:disabled,
|
|
235
|
+
.filterField.outlined .operatorTrigger:disabled {
|
|
236
|
+
--filter-operator-bg: var(--color-disabled-bg);
|
|
86
237
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
238
|
+
|
|
239
|
+
.filterField.subtle .operatorTrigger:disabled {
|
|
240
|
+
--filter-operator-bg: color-mix(
|
|
241
|
+
in srgb,
|
|
242
|
+
var(--color-fg-default) 3%,
|
|
243
|
+
var(--color-bg-surface-subtle)
|
|
244
|
+
);
|
|
90
245
|
}
|
|
91
246
|
|
|
92
|
-
/*
|
|
247
|
+
/* =========================
|
|
248
|
+
VALUE WRAPPER
|
|
249
|
+
========================= */
|
|
250
|
+
|
|
93
251
|
.filterField .valueWrapper {
|
|
94
252
|
display: inline-flex;
|
|
95
253
|
align-items: center;
|
|
96
254
|
padding: 0;
|
|
97
255
|
height: 100%;
|
|
98
|
-
|
|
99
256
|
flex: 1;
|
|
100
257
|
min-width: 0;
|
|
258
|
+
position: relative;
|
|
101
259
|
}
|
|
102
260
|
|
|
103
|
-
/* Ensure the control inside can stretch/shrink */
|
|
104
261
|
.filterField .valueWrapper > * {
|
|
105
262
|
height: 100%;
|
|
106
263
|
width: 100%;
|
|
107
264
|
min-width: 0;
|
|
108
265
|
}
|
|
109
|
-
.filterField .valueWrapper > * > * {
|
|
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
266
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
267
|
+
.filterField .valueWrapper > div {
|
|
268
|
+
display: flex;
|
|
269
|
+
align-items: stretch;
|
|
129
270
|
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
271
|
width: 100%;
|
|
142
|
-
height: 100%;
|
|
143
|
-
border: 0 !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
272
|
min-width: 0;
|
|
184
|
-
overflow: hidden;
|
|
185
|
-
text-overflow: ellipsis;
|
|
186
|
-
white-space: nowrap;
|
|
187
273
|
}
|
|
188
274
|
|
|
189
|
-
/*
|
|
190
|
-
.filterField.active .valueWrapper input {
|
|
191
|
-
font-weight: 550;
|
|
192
|
-
}
|
|
193
|
-
.filterField.active .valueWrapper :global(button[data-forminput]),
|
|
194
|
-
.filterField.active .valueWrapper > div > button {
|
|
195
|
-
font-weight: 550 !important;
|
|
196
|
-
}
|
|
275
|
+
/* subtle highlight when active */
|
|
197
276
|
|
|
198
|
-
|
|
199
|
-
|
|
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;
|
|
277
|
+
.filterField.active .valueWrapper {
|
|
278
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-border-selected) 18%, transparent);
|
|
206
279
|
}
|
|
207
280
|
|
|
208
281
|
/* =========================
|
|
209
|
-
|
|
282
|
+
TEXT
|
|
210
283
|
========================= */
|
|
211
284
|
|
|
212
|
-
.filterField
|
|
213
|
-
|
|
214
|
-
border: 0;
|
|
215
|
-
outline: none;
|
|
216
|
-
background: transparent;
|
|
217
|
-
color: var(--color-fg-default);
|
|
218
|
-
font: inherit;
|
|
219
|
-
inline-size: auto;
|
|
220
|
-
min-inline-size: 10ch;
|
|
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: var(--spacing-3xs);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.filterField button {
|
|
230
|
-
border-radius: 0;
|
|
285
|
+
.filterField .operatorText {
|
|
286
|
+
white-space: nowrap;
|
|
231
287
|
}
|
|
232
288
|
|
|
233
289
|
.filterField input::placeholder {
|
|
234
|
-
color: var(--color-fg-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.filterField input:disabled {
|
|
238
|
-
color: var(--color-disabled-fg);
|
|
239
|
-
cursor: not-allowed;
|
|
290
|
+
color: var(--color-fg-subtle);
|
|
240
291
|
}
|
|
@@ -6,7 +6,6 @@ interface ChipToggleOption {
|
|
|
6
6
|
icon?: React.ReactNode;
|
|
7
7
|
}
|
|
8
8
|
interface ChipMultiToggleProps {
|
|
9
|
-
/** Optional group label (e.g. "Fejlede i") */
|
|
10
9
|
label?: string;
|
|
11
10
|
options: ChipToggleOption[];
|
|
12
11
|
selectedValues: string[];
|
|
@@ -17,11 +16,11 @@ interface ChipMultiToggleProps {
|
|
|
17
16
|
unselectedSeverity?: 'neutral' | 'info' | 'success' | 'warning' | 'danger' | null;
|
|
18
17
|
fullWidth?: boolean;
|
|
19
18
|
disabled?: boolean;
|
|
20
|
-
showSelectedIcon?: boolean;
|
|
21
19
|
showAllOption?: boolean;
|
|
22
20
|
allLabel?: string;
|
|
23
21
|
allIcon?: React.ReactNode;
|
|
24
22
|
dataCy?: string;
|
|
23
|
+
type?: 'default' | 'outlined' | 'subtle' | 'rounded';
|
|
25
24
|
/**
|
|
26
25
|
* If true, clicking "All" toggles between:
|
|
27
26
|
* - All (no filtering) => []
|
|
@@ -34,5 +33,5 @@ interface ChipMultiToggleProps {
|
|
|
34
33
|
*/
|
|
35
34
|
noneValue?: string;
|
|
36
35
|
}
|
|
37
|
-
export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size,
|
|
36
|
+
export declare function ChipMultiToggle({ label, options, selectedValues, onChange, onToggle, size, fullWidth, disabled, showAllOption, allLabel, allIcon, allTogglesNone, noneValue, type, dataCy, }: ChipMultiToggleProps): JSX.Element;
|
|
38
37
|
export {};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Check } from 'lucide-react';
|
|
4
3
|
import React from 'react';
|
|
5
4
|
import { Chip } from '../../../components/chip/Chip';
|
|
6
5
|
import styles from './ChipMultiToggle.module.css';
|
|
7
|
-
export function ChipMultiToggle({ label, options, selectedValues = [], onChange, onToggle, size = 'sm',
|
|
6
|
+
export function ChipMultiToggle({ label, options, selectedValues = [], onChange, onToggle, size = 'sm', fullWidth = false, disabled = false, showAllOption = false, allLabel = 'Alle', allIcon, allTogglesNone = false, noneValue = '__none__', type = 'subtle', dataCy, }) {
|
|
8
7
|
const selectedSet = React.useMemo(() => new Set(selectedValues), [selectedValues]);
|
|
9
8
|
const isNoneSelected = allTogglesNone && selectedSet.has(noneValue);
|
|
10
9
|
const isAllSelected = showAllOption && !isNoneSelected && selectedSet.size === 0;
|
|
@@ -28,7 +27,6 @@ export function ChipMultiToggle({ label, options, selectedValues = [], onChange,
|
|
|
28
27
|
if (disabled)
|
|
29
28
|
return;
|
|
30
29
|
if (!allTogglesNone) {
|
|
31
|
-
// Classic "All" = clear selection
|
|
32
30
|
onChange([]);
|
|
33
31
|
return;
|
|
34
32
|
}
|
|
@@ -46,9 +44,9 @@ export function ChipMultiToggle({ label, options, selectedValues = [], onChange,
|
|
|
46
44
|
onChange([]);
|
|
47
45
|
}
|
|
48
46
|
};
|
|
49
|
-
return (_jsxs("div", { className: `${styles.wrapper} ${fullWidth ? styles.fullWidth : ''}`, "data-cy": dataCy, children: [label && _jsx("span", { className: styles.label, children: label }), _jsxs("div", { className: styles.container, children: [showAllOption && (_jsx("button", { type: "button", className: styles.chipButton, onClick: toggleAll, "aria-pressed": isAllSelected || isNoneSelected, disabled: disabled, children: _jsx(Chip, { size: size, severity:
|
|
47
|
+
return (_jsxs("div", { className: `${styles.wrapper} ${fullWidth ? styles.fullWidth : ''}`, "data-cy": dataCy, children: [label && _jsx("span", { className: styles.label, children: label }), _jsxs("div", { className: styles.container, children: [showAllOption && (_jsx("button", { type: "button", className: styles.chipButton, onClick: toggleAll, "aria-pressed": isAllSelected || isNoneSelected, disabled: disabled, children: _jsx(Chip, { type: type, size: size, severity: null, customIcon: allIcon, selected: isAllSelected || isNoneSelected, children: isNoneSelected ? `${allLabel} (ingen)` : allLabel }) }, "__all__")), options.map(opt => {
|
|
50
48
|
// If none-state is active, everything else should look unselected
|
|
51
49
|
const isSelected = !isNoneSelected && selectedSet.has(opt.value);
|
|
52
|
-
return (_jsx("button", { type: "button", className: styles.chipButton, onClick: () => toggleValue(opt.value), "aria-pressed": isSelected, disabled: disabled, children: _jsx(Chip, { size: size,
|
|
50
|
+
return (_jsx("button", { type: "button", className: styles.chipButton, onClick: () => toggleValue(opt.value), "aria-pressed": isSelected, disabled: disabled, children: _jsx(Chip, { size: size, customIcon: opt.icon, severity: null, type: type, selected: isSelected, children: opt.label }) }, opt.value));
|
|
53
51
|
})] })] }));
|
|
54
52
|
}
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
display: flex;
|
|
5
5
|
align-items: center;
|
|
6
6
|
flex-wrap: wrap;
|
|
7
|
-
|
|
8
|
-
/* use theme spacing tokens */
|
|
9
7
|
gap: var(--spacing-xs);
|
|
10
8
|
}
|
|
11
9
|
|
|
@@ -15,10 +13,7 @@
|
|
|
15
13
|
|
|
16
14
|
.label {
|
|
17
15
|
font-weight: var(--font-weight-medium);
|
|
18
|
-
|
|
19
|
-
/* theme uses --color-fg-muted / --color-fg-subtle (not --color-text-muted) */
|
|
20
16
|
color: var(--color-fg-muted);
|
|
21
|
-
|
|
22
17
|
white-space: nowrap;
|
|
23
18
|
line-height: var(--line-height-tight);
|
|
24
19
|
font-size: var(--font-size-sm);
|
|
@@ -28,8 +23,6 @@
|
|
|
28
23
|
display: flex;
|
|
29
24
|
flex-wrap: wrap;
|
|
30
25
|
align-items: center;
|
|
31
|
-
|
|
32
|
-
/* use theme spacing token */
|
|
33
26
|
gap: var(--spacing-xxs);
|
|
34
27
|
}
|
|
35
28
|
|
|
@@ -38,23 +31,30 @@
|
|
|
38
31
|
border: 0;
|
|
39
32
|
background: transparent;
|
|
40
33
|
cursor: pointer;
|
|
41
|
-
|
|
42
|
-
/* avoid default button styles on some browsers */
|
|
43
34
|
font: inherit;
|
|
44
35
|
color: inherit;
|
|
45
36
|
}
|
|
46
37
|
|
|
47
38
|
.chipButton:disabled {
|
|
48
39
|
cursor: not-allowed;
|
|
49
|
-
|
|
50
|
-
/* align with theme disabled */
|
|
51
40
|
opacity: 1;
|
|
52
41
|
color: var(--color-disabled-fg);
|
|
53
42
|
}
|
|
54
43
|
|
|
55
|
-
/* Focus ring should use theme focus tokens */
|
|
56
44
|
.chipButton:focus-visible {
|
|
57
45
|
outline: none;
|
|
58
|
-
box-shadow:
|
|
46
|
+
box-shadow: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.chipButton:focus-visible > * {
|
|
50
|
+
border-color: var(--color-border-selected);
|
|
51
|
+
box-shadow: inset 0 0 0 1px var(--color-border-selected);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.chipButton:focus-visible > .rounded {
|
|
59
55
|
border-radius: var(--border-radius-rounded);
|
|
60
56
|
}
|
|
57
|
+
|
|
58
|
+
.chipButton:focus-visible > .default {
|
|
59
|
+
border-radius: var(--border-radius-default);
|
|
60
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { Size } from '../../../types/sizes.types';
|
|
3
3
|
import type { InputContainerProps } from '../input-container/InputContainer';
|
|
4
|
-
export type InputVariant = 'outlined' | '
|
|
4
|
+
export type InputVariant = 'outlined' | 'surface' | 'subtle' | 'standalone' | 'embedded';
|
|
5
5
|
export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'width'> & Omit<InputContainerProps, 'children' | 'htmlFor' | 'tooltip' | 'tooltipPlacement'> & {
|
|
6
6
|
icon?: React.ReactNode;
|
|
7
7
|
autoFocus?: boolean;
|
|
@@ -18,13 +18,7 @@ function mergeRefs(...refs) {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
export const Input = forwardRef(function Input({
|
|
22
|
-
// InputContainer props
|
|
23
|
-
label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = false, required, tooltip, tooltipPlacement = 'right', modified,
|
|
24
|
-
// Input-only props
|
|
25
|
-
icon, autoFocus, minWidth, width, inputSize = 'md', variant = 'outlined', onClear, onButtonClick, buttonLabel, buttonIcon,
|
|
26
|
-
// Native input props
|
|
27
|
-
id, style, className, ...inputProps }, ref) {
|
|
21
|
+
export const Input = forwardRef(function Input({ label, error, helpText, orientation = 'vertical', labelWidth = '160px', fullWidth = false, required, tooltip, tooltipPlacement = 'right', modified, icon, autoFocus, minWidth, width, inputSize = 'md', variant = 'outlined', onClear, onButtonClick, buttonLabel, buttonIcon, id, style, className, ...inputProps }, ref) {
|
|
28
22
|
const inputRef = useRef(null);
|
|
29
23
|
const reactId = useId();
|
|
30
24
|
const inputId = id !== null && id !== void 0 ? id : `input-${reactId}`;
|
|
@@ -39,12 +33,12 @@ id, style, className, ...inputProps }, ref) {
|
|
|
39
33
|
...(minWidth ? { ['--input-min-width']: minWidth } : null),
|
|
40
34
|
...(width ? { ['--input-width']: width } : null),
|
|
41
35
|
};
|
|
42
|
-
// Anchor tooltip to the stable "field" wrapper (input + icon + clear button)
|
|
43
36
|
const { triggerProps } = useTooltipTrigger({
|
|
44
37
|
content: tooltip,
|
|
45
38
|
placement: tooltipPlacement,
|
|
46
39
|
offset: 8,
|
|
47
40
|
});
|
|
41
|
+
const trailingButtonVariant = variant === 'outlined' || variant === 'standalone' ? 'outlined' : 'default';
|
|
48
42
|
return (_jsx(InputContainer, { label: label, htmlFor: inputId, fullWidth: fullWidth, error: error, helpText: helpText, orientation: orientation, labelWidth: labelWidth, required: required, modified: modified, children: _jsxs("div", { style: rootStyle, className: [
|
|
49
43
|
styles.container,
|
|
50
44
|
fullWidth ? styles.fullWidth : '',
|
|
@@ -60,6 +54,6 @@ id, style, className, ...inputProps }, ref) {
|
|
|
60
54
|
styles[variant],
|
|
61
55
|
]
|
|
62
56
|
.filter(Boolean)
|
|
63
|
-
.join(' ') }), onClear && inputProps.value && _jsx(ClearButton, { onClick: onClear, absolute: true })] }), hasButton && (_jsxs(Button, { onClick: onButtonClick, className: styles.trailingButton, type: "button", children: [buttonIcon !== null && buttonIcon !== void 0 ? buttonIcon : null, buttonLabel !== null && buttonLabel !== void 0 ? buttonLabel : null] }))] }) }));
|
|
57
|
+
.join(' ') }), onClear && inputProps.value && _jsx(ClearButton, { onClick: onClear, absolute: true })] }), hasButton && (_jsxs(Button, { onClick: onButtonClick, className: styles.trailingButton, type: "button", variant: trailingButtonVariant, children: [buttonIcon !== null && buttonIcon !== void 0 ? buttonIcon : null, buttonLabel !== null && buttonLabel !== void 0 ? buttonLabel : null] }))] }) }));
|
|
64
58
|
});
|
|
65
59
|
Input.displayName = 'Input';
|