@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.
Files changed (46) hide show
  1. package/dist/components/button/Button.module.css +21 -10
  2. package/dist/components/chip/Chip.d.ts +3 -2
  3. package/dist/components/chip/Chip.js +2 -1
  4. package/dist/components/chip/Chip.module.css +107 -67
  5. package/dist/components/code-block/CodeBlock.js +28 -19
  6. package/dist/components/code-block/CodeBlock.module.css +69 -67
  7. package/dist/components/filter-field/FilterField.d.ts +2 -1
  8. package/dist/components/filter-field/FilterField.js +4 -9
  9. package/dist/components/filter-field/FilterField.module.css +203 -152
  10. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.d.ts +2 -3
  11. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.js +3 -5
  12. package/dist/components/filtering/chip-multi-toggle/ChipMultiToggle.module.css +13 -13
  13. package/dist/components/forms/input/Input.d.ts +1 -1
  14. package/dist/components/forms/input/Input.js +3 -9
  15. package/dist/components/forms/input/Input.module.css +106 -17
  16. package/dist/components/hyperlink/Hyperlink.module.css +14 -3
  17. package/dist/components/interval-select/IntervalSelect.js +2 -2
  18. package/dist/components/overlay/modal/Modal.d.ts +2 -1
  19. package/dist/components/overlay/modal/Modal.js +6 -6
  20. package/dist/components/overlay/modal/Modal.module.css +52 -19
  21. package/dist/components/popover/Popover.module.css +4 -1
  22. package/dist/components/segmented-progress-bar/SegmentedProgressBar.d.ts +4 -2
  23. package/dist/components/segmented-progress-bar/SegmentedProgressBar.js +17 -19
  24. package/dist/components/segmented-progress-bar/SegmentedProgressBar.module.css +128 -20
  25. package/dist/components/sidebar/Sidebar.d.ts +2 -1
  26. package/dist/components/sidebar/Sidebar.js +2 -2
  27. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.d.ts +2 -3
  28. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.js +2 -4
  29. package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +2 -1
  30. package/dist/components/table/Table.d.ts +6 -6
  31. package/dist/components/table/Table.js +153 -78
  32. package/dist/components/table/Table.module.css +335 -171
  33. package/dist/components/table/TanstackTable.d.ts +1 -1
  34. package/dist/components/table/TanstackTable.js +14 -12
  35. package/dist/components/table/table.utils.d.ts +1 -1
  36. package/dist/components/table/table.utils.js +2 -3
  37. package/dist/components/table/tanstackTable.utils.d.ts +9 -10
  38. package/dist/components/table/tanstackTable.utils.js +50 -30
  39. package/dist/hooks/useViewportFill.d.ts +6 -5
  40. package/dist/hooks/useViewportFill.js +43 -41
  41. package/dist/src/styles/styles.css +9 -3
  42. package/dist/styles/styles.css +9 -3
  43. package/dist/styles/themes/dbc/base.css +0 -3
  44. package/dist/styles/themes/dbc/dark.css +44 -12
  45. package/dist/styles/themes/dbc/light.css +33 -7
  46. 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: `${styles.filterField} ${styles[size]} ${active ? styles.active : ''}`, 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, { ...inputProps, value: localValue, onChange: e => {
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
- background-color var(--transition-fast) var(--ease-standard);
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
- /* 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));
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
- /* Operator trigger */
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
- background: var(--opac-bg-default);
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
- .filterField .operatorTrigger:hover {
66
- background: var(--opac-bg-dark);
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
- .filterField .operatorTrigger:focus-visible {
69
- outline: none;
70
- box-shadow: var(--focus-ring);
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
- /* When active, operator is less dominant (calmer + more "token"-like) */
83
- .filterField.active .operatorTrigger {
84
- background: transparent;
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
- .filterField.active .operatorTrigger:hover {
88
- background: var(--opac-bg-dark);
89
- color: var(--color-fg-default);
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
- /* Wrapper for the select / multiselect control */
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
- /* Select trigger button */
127
- .filterField .valueWrapper :global(button[data-forminput]) {
128
- width: 100%;
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
- /* Emphasize chosen value text in active state (but slightly less "boldy") */
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
- /* 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;
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
- INPUT styling
282
+ TEXT
210
283
  ========================= */
211
284
 
212
- .filterField input {
213
- appearance: none;
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-muted);
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, selectedSeverity, unselectedSeverity, fullWidth, disabled, showAllOption, allLabel, allIcon, showSelectedIcon, allTogglesNone, noneValue, dataCy, }: ChipMultiToggleProps): JSX.Element;
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', selectedSeverity = 'info', unselectedSeverity = null, fullWidth = false, disabled = false, showAllOption = false, allLabel = 'Alle', allIcon, showSelectedIcon = false, allTogglesNone = false, noneValue = '__none__', dataCy, }) {
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: (isAllSelected || isNoneSelected ? selectedSeverity : unselectedSeverity), customIcon: allIcon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: isNoneSelected ? `${allLabel} (ingen)` : allLabel }) }, "__all__")), options.map(opt => {
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, severity: (isSelected ? selectedSeverity : unselectedSeverity), customIcon: showSelectedIcon ? isSelected ? _jsx(Check, {}) : null : opt.icon, type: unselectedSeverity === null ? 'outlined' : 'rounded', children: opt.label }) }, opt.value));
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: var(--focus-ring);
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' | 'filled' | 'standalone';
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';