@dbcdk/react-components 0.0.179 → 0.0.181

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 (29) hide show
  1. package/README.md +4 -3
  2. package/dist/components/clear-button/ClearButton.cjs +7 -2
  3. package/dist/components/clear-button/ClearButton.d.ts +3 -1
  4. package/dist/components/clear-button/ClearButton.js +7 -2
  5. package/dist/components/clear-button/ClearButton.module.css +9 -0
  6. package/dist/components/forms/input/Input.cjs +8 -1
  7. package/dist/components/forms/input/Input.js +8 -1
  8. package/dist/components/forms/multi-select/MultiSelect.cjs +7 -1
  9. package/dist/components/forms/multi-select/MultiSelect.js +7 -1
  10. package/dist/components/forms/select/Select.cjs +7 -1
  11. package/dist/components/forms/select/Select.js +7 -1
  12. package/dist/components/interval-select/IntervalSelect.cjs +7 -1
  13. package/dist/components/interval-select/IntervalSelect.js +7 -1
  14. package/dist/components/json-viewer/JsonViewer.cjs +196 -83
  15. package/dist/components/json-viewer/JsonViewer.d.ts +1 -1
  16. package/dist/components/json-viewer/JsonViewer.js +197 -84
  17. package/dist/components/json-viewer/JsonViewer.module.css +37 -44
  18. package/dist/components/json-viewer/types.d.ts +8 -0
  19. package/dist/components/search-box/SearchBox.cjs +9 -1
  20. package/dist/components/search-box/SearchBox.d.ts +3 -3
  21. package/dist/components/search-box/SearchBox.js +9 -1
  22. package/dist/components/tabs/Tabs.cjs +50 -21
  23. package/dist/components/tabs/Tabs.d.ts +7 -1
  24. package/dist/components/tabs/Tabs.js +50 -21
  25. package/dist/components/tabs/Tabs.module.css +91 -11
  26. package/dist/hooks/useGetActiveTheme.cjs +2 -1
  27. package/dist/hooks/useGetActiveTheme.d.ts +1 -0
  28. package/dist/hooks/useGetActiveTheme.js +2 -1
  29. package/package.json +1 -1
@@ -8,6 +8,7 @@ export type TabItem = {
8
8
  disabled?: boolean;
9
9
  hidden?: boolean;
10
10
  badge?: number | string;
11
+ removable?: boolean;
11
12
  };
12
13
  type TabsVariant = 'filled' | 'outlined';
13
14
  export interface TabsProps {
@@ -27,6 +28,10 @@ export interface TabsProps {
27
28
  */
28
29
  storageKey?: string;
29
30
  onValueChange?: (id: TabId, tab: TabItem, index: number) => void;
31
+ /** Called when a removable tab's remove button is pressed. The caller owns removing the tab. */
32
+ onRemoveTab?: (id: TabId, tab: TabItem, index: number) => void;
33
+ /** Shows an add-tab button and notifies the caller. The caller owns creating and selecting the tab. */
34
+ onAddTab?: () => void;
30
35
  addition?: ReactNode;
31
36
  loading?: boolean;
32
37
  /** Composition API */
@@ -39,13 +44,14 @@ export interface TabsItemProps {
39
44
  disabled?: boolean;
40
45
  hidden?: boolean;
41
46
  badge?: number;
47
+ removable?: boolean;
42
48
  children?: ReactNode;
43
49
  }
44
50
  type SlotName = 'Item';
45
51
  type TabsItemComponent = ((props: TabsItemProps) => JSX.Element) & {
46
52
  __TABS_SLOT__?: SlotName;
47
53
  };
48
- export declare function Tabs({ header, subheader, variant, panelStyle, tabs, value, defaultValue, storageKey, onValueChange, addition, loading, children, }: TabsProps): JSX.Element;
54
+ export declare function Tabs({ header, subheader, variant, panelStyle, tabs, value, defaultValue, storageKey, onValueChange, onRemoveTab, onAddTab, addition, loading, children, }: TabsProps): JSX.Element;
49
55
  export declare namespace Tabs {
50
56
  var Item: TabsItemComponent;
51
57
  }
@@ -1,4 +1,5 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { X, Plus } from 'lucide-react';
2
3
  import { useId, useMemo, useCallback, useEffect, Children, isValidElement } from 'react';
3
4
  import { usePersistentState } from '../../hooks/usePersistentState';
4
5
  import styles from './Tabs.module.css';
@@ -31,6 +32,7 @@ function normalizeFromChildren(children) {
31
32
  disabled: props.disabled,
32
33
  hidden: props.hidden,
33
34
  badge: props.badge,
35
+ removable: props.removable,
34
36
  content: props.children
35
37
  });
36
38
  });
@@ -46,6 +48,8 @@ function Tabs({
46
48
  defaultValue,
47
49
  storageKey,
48
50
  onValueChange,
51
+ onRemoveTab,
52
+ onAddTab,
49
53
  addition,
50
54
  loading = false,
51
55
  children
@@ -138,31 +142,56 @@ function Tabs({
138
142
  }
139
143
  ) : null,
140
144
  /* @__PURE__ */ jsxs("div", { className: `${styles.tabs} ${styles[variant]} ${panelStyle ? styles.panelStyle : ""}`, children: [
141
- /* @__PURE__ */ jsx("div", { className: styles.tabList, role: "tablist", "aria-label": header != null ? header : "Tabs", children: visibleTabs.map((tab, index) => {
142
- const selected = index === activeIndex;
143
- const tabDomId = `${uid}-tab-${String(tab.id)}`;
144
- const panelDomId = `${uid}-panel-${String(tab.id)}`;
145
- return /* @__PURE__ */ jsx("div", { className: `${styles.tab} ${selected ? styles.active : ""}`, children: /* @__PURE__ */ jsxs(
145
+ /* @__PURE__ */ jsxs("div", { className: styles.tabList, children: [
146
+ /* @__PURE__ */ jsx("div", { className: styles.tabButtons, role: "tablist", "aria-label": header != null ? header : "Tabs", children: visibleTabs.map((tab, index) => {
147
+ const selected = index === activeIndex;
148
+ const tabDomId = `${uid}-tab-${String(tab.id)}`;
149
+ const panelDomId = `${uid}-panel-${String(tab.id)}`;
150
+ return /* @__PURE__ */ jsxs("div", { className: `${styles.tab} ${selected ? styles.active : ""}`, children: [
151
+ /* @__PURE__ */ jsxs(
152
+ "button",
153
+ {
154
+ id: tabDomId,
155
+ type: "button",
156
+ className: styles.tabButton,
157
+ role: "tab",
158
+ "aria-selected": selected,
159
+ "aria-controls": panelDomId,
160
+ tabIndex: selected ? 0 : -1,
161
+ disabled: tab.disabled,
162
+ onClick: () => setValue(tab.id),
163
+ onKeyDown: (e) => onKeyDownTab(e, index),
164
+ children: [
165
+ tab.headerIcon ? /* @__PURE__ */ jsx("span", { className: styles.icon, children: tab.headerIcon }) : null,
166
+ /* @__PURE__ */ jsx("span", { className: styles.label, children: tab.header }),
167
+ tab.badge ? /* @__PURE__ */ jsx("span", { className: styles.badge, children: /* @__PURE__ */ jsx(Chip, { size: "sm", children: tab.badge.toLocaleString("da-DK") }) }) : null
168
+ ]
169
+ }
170
+ ),
171
+ tab.removable && onRemoveTab ? /* @__PURE__ */ jsx(
172
+ "button",
173
+ {
174
+ type: "button",
175
+ className: styles.removeTabButton,
176
+ "aria-label": `Fjern fane ${tab.header}`,
177
+ disabled: tab.disabled,
178
+ onClick: () => onRemoveTab(tab.id, tab, index),
179
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
180
+ }
181
+ ) : null
182
+ ] }, tab.id);
183
+ }) }),
184
+ onAddTab ? /* @__PURE__ */ jsx(
146
185
  "button",
147
186
  {
148
- id: tabDomId,
149
187
  type: "button",
150
- className: styles.tabButton,
151
- role: "tab",
152
- "aria-selected": selected,
153
- "aria-controls": panelDomId,
154
- tabIndex: selected ? 0 : -1,
155
- disabled: tab.disabled,
156
- onClick: () => setValue(tab.id),
157
- onKeyDown: (e) => onKeyDownTab(e, index),
158
- children: [
159
- tab.headerIcon ? /* @__PURE__ */ jsx("span", { className: styles.icon, children: tab.headerIcon }) : null,
160
- /* @__PURE__ */ jsx("span", { className: styles.label, children: tab.header }),
161
- tab.badge ? /* @__PURE__ */ jsx("span", { className: styles.badge, children: /* @__PURE__ */ jsx(Chip, { size: "sm", children: tab.badge.toLocaleString("da-DK") }) }) : null
162
- ]
188
+ className: styles.addTabButton,
189
+ onClick: onAddTab,
190
+ "aria-label": "Tilf\xF8j fane",
191
+ children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" })
163
192
  }
164
- ) }, tab.id);
165
- }) }),
193
+ ) : null
194
+ ] }),
166
195
  /* @__PURE__ */ jsx(
167
196
  "div",
168
197
  {
@@ -26,6 +26,11 @@
26
26
 
27
27
  /* Tablist */
28
28
  .tabList {
29
+ display: flex;
30
+ min-width: 0;
31
+ }
32
+
33
+ .tabButtons {
29
34
  display: flex;
30
35
  flex-wrap: wrap;
31
36
  min-width: 0;
@@ -34,7 +39,7 @@
34
39
  /* Individual tab wrapper */
35
40
  .tab {
36
41
  display: flex;
37
- flex-direction: column;
42
+ align-items: stretch;
38
43
  border-radius: 0;
39
44
  border-block-end: 2px solid var(--color-border-default);
40
45
  }
@@ -82,6 +87,68 @@
82
87
  color: var(--color-disabled-fg);
83
88
  }
84
89
 
90
+ .removeTabButton {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ align-self: stretch;
95
+
96
+ color: inherit;
97
+ background: none;
98
+ border: 0;
99
+ border-radius: 0;
100
+ padding-inline: var(--spacing-xs);
101
+ cursor: pointer;
102
+ }
103
+
104
+ .removeTabButton:hover {
105
+ color: var(--color-brand);
106
+ }
107
+
108
+ .removeTabButton:focus-visible {
109
+ outline: none;
110
+ box-shadow: var(--focus-ring);
111
+ }
112
+
113
+ .removeTabButton:disabled {
114
+ cursor: not-allowed;
115
+ color: var(--color-disabled-fg);
116
+ }
117
+
118
+ .removeTabButton svg {
119
+ inline-size: var(--icon-size-sm);
120
+ block-size: var(--icon-size-sm);
121
+ }
122
+
123
+ .addTabButton {
124
+ display: inline-flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ align-self: stretch;
128
+
129
+ color: var(--color-fg-muted);
130
+ background: none;
131
+ border: 0;
132
+ border-radius: 0;
133
+ padding-inline: var(--spacing-md);
134
+ cursor: pointer;
135
+ }
136
+
137
+ .addTabButton:hover {
138
+ color: var(--color-fg-default);
139
+ background: var(--color-bg-hover-subtle);
140
+ }
141
+
142
+ .addTabButton:focus-visible {
143
+ outline: none;
144
+ box-shadow: var(--focus-ring);
145
+ }
146
+
147
+ .addTabButton svg {
148
+ inline-size: var(--icon-size-sm);
149
+ block-size: var(--icon-size-sm);
150
+ }
151
+
85
152
  /* Icon + label */
86
153
  .icon {
87
154
  display: inline-flex;
@@ -125,7 +192,7 @@
125
192
  gap: 0;
126
193
  }
127
194
 
128
- /* Outer container: layout only, no visible border — tab list is borderless */
195
+ /* Filled tabs mimic a browser tab strip: a muted toolbar with a raised active page. */
129
196
  .tabs.filled {
130
197
  border-radius: var(--border-radius-md);
131
198
  background: var(--color-bg-surface);
@@ -134,12 +201,26 @@
134
201
 
135
202
  .filled .tabList {
136
203
  border-block-end: 1px solid var(--color-border-subtle);
137
- background: var(--color-bg-surface);
204
+ background: var(--color-bg-toolbar);
138
205
  inline-size: 100%;
139
206
  }
140
207
 
208
+ .filled .tabButtons {
209
+ display: flex;
210
+ }
211
+
212
+ .filled .addTabButton {
213
+ border: 1px solid transparent;
214
+ border-block-end: 0;
215
+ }
216
+
217
+ .filled .addTabButton:hover {
218
+ background: var(--color-bg-toolbar-hover);
219
+ }
220
+
141
221
  .filled .tab {
142
222
  border: 1px solid transparent;
223
+ border-block-end: 0;
143
224
  color: var(--color-fg-muted);
144
225
  position: relative;
145
226
  transition:
@@ -149,22 +230,21 @@
149
230
  }
150
231
 
151
232
  .filled .tab:not(.active):hover {
152
- color: var(--color-brand);
233
+ background: var(--color-bg-toolbar-hover);
234
+ color: var(--color-fg-default);
153
235
  }
154
236
 
155
237
  /* Active tab physically overlaps the tabList separator by 1px so its
156
- erased bottom border seamlessly merges with the panel below.
157
- No top border — the outer container already provides the top edge. */
238
+ erased bottom border seamlessly merges with the panel below. */
158
239
  .filled .tab.active {
159
- background: var(--opac-bg-brand);
160
- border-inline-color: var(--color-border-subtle);
161
- border-block-start-color: var(--color-border-subtle);
162
- border-block-end-color: var(--opac-bg-brand);
240
+ background: var(--color-bg-surface);
241
+ border-color: var(--color-border-subtle);
242
+ border-block-end-color: var(--color-bg-surface);
163
243
  border-start-start-radius: var(--border-radius-md);
164
244
  border-start-end-radius: var(--border-radius-md);
165
245
  margin-block-end: -1px;
166
246
  z-index: 1;
167
- color: var(--color-brand);
247
+ color: var(--color-fg-default);
168
248
  }
169
249
 
170
250
  .filled .tabContent {
@@ -7,7 +7,8 @@ var useTheme = require('./useTheme');
7
7
  function useGetActiveTheme() {
8
8
  const { theme } = useTheme.useTheme();
9
9
  const { colorScheme } = useColorScheme.useColorScheme();
10
- return { theme, colorScheme };
10
+ const themeKey = theme && colorScheme ? `${theme}-${colorScheme}` : null;
11
+ return { theme, colorScheme, themeKey };
11
12
  }
12
13
 
13
14
  exports.useGetActiveTheme = useGetActiveTheme;
@@ -4,5 +4,6 @@ export type { ColorScheme, ThemeId };
4
4
  export interface UseGetActiveThemeResult {
5
5
  theme: ThemeId | null;
6
6
  colorScheme: ColorScheme | null;
7
+ themeKey: string | null;
7
8
  }
8
9
  export declare function useGetActiveTheme(): UseGetActiveThemeResult;
@@ -5,7 +5,8 @@ import { useTheme } from './useTheme';
5
5
  function useGetActiveTheme() {
6
6
  const { theme } = useTheme();
7
7
  const { colorScheme } = useColorScheme();
8
- return { theme, colorScheme };
8
+ const themeKey = theme && colorScheme ? `${theme}-${colorScheme}` : null;
9
+ return { theme, colorScheme, themeKey };
9
10
  }
10
11
 
11
12
  export { useGetActiveTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbcdk/react-components",
3
- "version": "0.0.179",
3
+ "version": "0.0.181",
4
4
  "description": "Reusable React components for DBC projects",
5
5
  "license": "ISC",
6
6
  "author": "",