@robr0/design-system 0.2.0 → 0.4.0

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 (99) hide show
  1. package/README.md +33 -28
  2. package/components/Accordion/Accordion.js +0 -1
  3. package/components/AgentStatus/AgentStatus.css +436 -0
  4. package/components/AgentStatus/AgentStatus.d.ts +37 -0
  5. package/components/AgentStatus/AgentStatus.js +53 -0
  6. package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
  7. package/components/AgentStatus/AgentStatusPatterns.js +20 -0
  8. package/components/AlertDialog/AlertDialog.css +5 -5
  9. package/components/Avatar/Avatar.js +1 -0
  10. package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
  11. package/components/Breadcrumb/Breadcrumb.js +3 -2
  12. package/components/Button/Button.css +18 -0
  13. package/components/Button/Button.d.ts +20 -2
  14. package/components/Button/Button.js +13 -3
  15. package/components/ButtonGroup/ButtonGroup.js +2 -9
  16. package/components/Card/Card.css +6 -0
  17. package/components/Checkbox/Checkbox.d.ts +17 -3
  18. package/components/CircularButton/CircularButton.css +11 -0
  19. package/components/CircularButton/CircularButton.d.ts +15 -3
  20. package/components/CircularButton/CircularButton.js +19 -6
  21. package/components/CodeBlock/CodeBlock.js +10 -1
  22. package/components/ColorPicker/ColorPicker.css +278 -0
  23. package/components/ColorPicker/ColorPicker.d.ts +50 -0
  24. package/components/ColorPicker/ColorPicker.js +338 -0
  25. package/components/Combobox/Combobox.css +0 -36
  26. package/components/Combobox/Combobox.d.ts +10 -2
  27. package/components/Combobox/Combobox.js +90 -82
  28. package/components/CommandPalette/CommandPalette.css +1 -15
  29. package/components/CommandPalette/CommandPalette.js +6 -5
  30. package/components/ContextMenu/ContextMenu.css +258 -0
  31. package/components/ContextMenu/ContextMenu.d.ts +26 -0
  32. package/components/ContextMenu/ContextMenu.js +350 -0
  33. package/components/DateInput/DateInput.css +0 -36
  34. package/components/DateInput/DateInput.d.ts +5 -1
  35. package/components/DateInput/DateInput.js +37 -33
  36. package/components/DatePicker/DatePicker.js +45 -34
  37. package/components/Dialog/Dialog.css +5 -5
  38. package/components/Dialog/Dialog.js +5 -4
  39. package/components/Drawer/Drawer.js +7 -2
  40. package/components/Dropdown/Dropdown.css +2 -40
  41. package/components/Dropdown/Dropdown.d.ts +10 -2
  42. package/components/Dropdown/Dropdown.js +119 -109
  43. package/components/DropdownMenu/DropdownMenu.css +2 -4
  44. package/components/DropdownMenu/DropdownMenu.js +10 -7
  45. package/components/Field/Field.css +80 -0
  46. package/components/Field/Field.d.ts +50 -0
  47. package/components/Field/Field.js +58 -0
  48. package/components/Field/FieldContext.d.ts +42 -0
  49. package/components/Field/FieldContext.js +7 -0
  50. package/components/FileInput/FileInput.css +0 -36
  51. package/components/FileInput/FileInput.d.ts +5 -1
  52. package/components/FileInput/FileInput.js +111 -103
  53. package/components/Input/Input.css +0 -43
  54. package/components/Input/Input.d.ts +5 -1
  55. package/components/Input/Input.js +51 -46
  56. package/components/Kbd/Kbd.css +28 -0
  57. package/components/Kbd/Kbd.d.ts +19 -0
  58. package/components/Kbd/Kbd.js +14 -0
  59. package/components/LinkList/LinkList.d.ts +1 -0
  60. package/components/Popover/Popover.d.ts +0 -6
  61. package/components/Popover/Popover.js +22 -16
  62. package/components/ProgressBar/ProgressBar.js +1 -1
  63. package/components/RadioButton/RadioButton.d.ts +17 -3
  64. package/components/Reasoning/Reasoning.css +152 -0
  65. package/components/Reasoning/Reasoning.d.ts +36 -0
  66. package/components/Reasoning/Reasoning.js +69 -0
  67. package/components/SelectionCard/SelectionCard.css +6 -2
  68. package/components/SelectionCard/SelectionCard.d.ts +5 -1
  69. package/components/Skeleton/Skeleton.css +2 -0
  70. package/components/Slider/Slider.d.ts +5 -1
  71. package/components/Spinner/Spinner.css +11 -0
  72. package/components/Spinner/Spinner.d.ts +2 -2
  73. package/components/Swatch/Swatch.css +52 -0
  74. package/components/Swatch/Swatch.d.ts +34 -0
  75. package/components/Swatch/Swatch.js +44 -0
  76. package/components/Table/Table.css +19 -0
  77. package/components/Table/Table.js +1 -1
  78. package/components/Textarea/Textarea.css +0 -43
  79. package/components/Textarea/Textarea.d.ts +5 -1
  80. package/components/Textarea/Textarea.js +37 -35
  81. package/components/Timeline/Timeline.d.ts +2 -0
  82. package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
  83. package/components/ToggleSwitch/ToggleSwitch.css +7 -2
  84. package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
  85. package/components/ToolCall/ToolCall.css +213 -0
  86. package/components/ToolCall/ToolCall.d.ts +43 -0
  87. package/components/ToolCall/ToolCall.js +89 -0
  88. package/components/registry.d.ts +47 -10
  89. package/components/registry.js +5 -1
  90. package/components/registry.json +523 -56
  91. package/components/registry.json.d.ts +523 -56
  92. package/components/registry.json.js +4 -1
  93. package/index.d.ts +12 -1
  94. package/index.js +24 -1
  95. package/package.json +8 -4
  96. package/tokens/registry.json +1 -0
  97. package/tokens/registry.json.d.ts +1 -0
  98. package/tokens/registry.json.js +1 -1
  99. package/tokens/tokens-motion.css +3 -1
@@ -0,0 +1,350 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React, { useState, useRef, useCallback, useEffect, useLayoutEffect } from "react";
3
+ import "./ContextMenu.css";
4
+ import "../../fonts/material-symbols.css";
5
+ function collectItems(entries) {
6
+ const result = [];
7
+ for (const entry of entries) {
8
+ if (entry.type === "separator") continue;
9
+ if (entry.type === "group") {
10
+ result.push(...collectItems(entry.items));
11
+ } else {
12
+ result.push(entry);
13
+ }
14
+ }
15
+ return result;
16
+ }
17
+ const SubMenuItem = ({ item, baseClass, focused, size, onActivate }) => {
18
+ const [subOpen, setSubOpen] = useState(false);
19
+ const timeoutRef = useRef(null);
20
+ const handleMouseEnter = () => {
21
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
22
+ setSubOpen(true);
23
+ };
24
+ const handleMouseLeave = () => {
25
+ timeoutRef.current = setTimeout(() => setSubOpen(false), 150);
26
+ };
27
+ useEffect(() => {
28
+ return () => {
29
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
30
+ };
31
+ }, []);
32
+ useEffect(() => {
33
+ if (focused && item.children && item.children.length > 0) {
34
+ setSubOpen(true);
35
+ }
36
+ }, [focused, item.children]);
37
+ const classes = [
38
+ `${baseClass}__item`,
39
+ focused ? `${baseClass}__item--focused` : "",
40
+ item.disabled ? `${baseClass}__item--disabled` : "",
41
+ item.destructive ? `${baseClass}__item--destructive` : "",
42
+ `${baseClass}__item--has-submenu`
43
+ ].filter(Boolean).join(" ");
44
+ return /* @__PURE__ */ jsxs(
45
+ "li",
46
+ {
47
+ className: classes,
48
+ role: "menuitem",
49
+ "aria-haspopup": "menu",
50
+ "aria-expanded": subOpen,
51
+ "aria-disabled": item.disabled || void 0,
52
+ onMouseEnter: handleMouseEnter,
53
+ onMouseLeave: handleMouseLeave,
54
+ onClick: (e) => {
55
+ e.stopPropagation();
56
+ if (!item.disabled) setSubOpen((prev) => !prev);
57
+ },
58
+ children: [
59
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__item-label`, children: [
60
+ item.icon && /* @__PURE__ */ jsx("span", { className: `${baseClass}__item-icon material-symbols-rounded`, "aria-hidden": "true", children: item.icon }),
61
+ item.label
62
+ ] }),
63
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__submenu-icon material-symbols-rounded`, "aria-hidden": "true", children: "chevron_right" }),
64
+ subOpen && item.children && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__panel ${baseClass}__submenu`, role: "menu", children: item.children.map((entry, index) => /* @__PURE__ */ jsx(
65
+ SubEntry,
66
+ {
67
+ entry,
68
+ baseClass,
69
+ size,
70
+ onActivate
71
+ },
72
+ index
73
+ )) })
74
+ ]
75
+ }
76
+ );
77
+ };
78
+ const SubEntry = ({
79
+ entry,
80
+ baseClass,
81
+ size,
82
+ onActivate
83
+ }) => {
84
+ if (entry.type === "separator") {
85
+ return /* @__PURE__ */ jsx("li", { className: `${baseClass}__separator`, role: "separator" });
86
+ }
87
+ if (entry.type === "group") {
88
+ return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__group`, role: "none", children: [
89
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__group-label`, role: "presentation", children: entry.label }),
90
+ /* @__PURE__ */ jsx("ul", { className: `${baseClass}__group-list`, role: "group", "aria-label": entry.label, children: entry.items.map((subEntry, subIndex) => /* @__PURE__ */ jsx(
91
+ SubEntry,
92
+ {
93
+ entry: subEntry,
94
+ baseClass,
95
+ size,
96
+ onActivate
97
+ },
98
+ subIndex
99
+ )) })
100
+ ] });
101
+ }
102
+ if (entry.children && entry.children.length > 0) {
103
+ return /* @__PURE__ */ jsx(
104
+ SubMenuItem,
105
+ {
106
+ item: entry,
107
+ baseClass,
108
+ focused: false,
109
+ size,
110
+ onActivate
111
+ }
112
+ );
113
+ }
114
+ const itemClasses = [
115
+ `${baseClass}__item`,
116
+ entry.disabled ? `${baseClass}__item--disabled` : "",
117
+ entry.destructive ? `${baseClass}__item--destructive` : ""
118
+ ].filter(Boolean).join(" ");
119
+ return /* @__PURE__ */ jsxs(
120
+ "li",
121
+ {
122
+ className: itemClasses,
123
+ role: "menuitem",
124
+ "aria-disabled": entry.disabled || void 0,
125
+ onClick: (e) => {
126
+ e.stopPropagation();
127
+ if (!entry.disabled) {
128
+ entry.onClick?.();
129
+ onActivate();
130
+ }
131
+ },
132
+ children: [
133
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__item-label`, children: [
134
+ entry.icon && /* @__PURE__ */ jsx("span", { className: `${baseClass}__item-icon material-symbols-rounded`, "aria-hidden": "true", children: entry.icon }),
135
+ entry.label
136
+ ] }),
137
+ entry.shortcut && /* @__PURE__ */ jsx("span", { className: `${baseClass}__shortcut`, children: entry.shortcut })
138
+ ]
139
+ }
140
+ );
141
+ };
142
+ const ContextMenu = React.forwardRef(
143
+ ({
144
+ items,
145
+ size = "default",
146
+ ariaLabel,
147
+ className = "",
148
+ children,
149
+ onContextMenu,
150
+ ...rest
151
+ }, ref) => {
152
+ const [position, setPosition] = useState(null);
153
+ const [focusedIndex, setFocusedIndex] = useState(-1);
154
+ const rootRef = useRef(null);
155
+ const panelRef = useRef(null);
156
+ const baseClass = "ds-context-menu";
157
+ const classes = [baseClass, className].filter(Boolean).join(" ");
158
+ const isOpen = position !== null;
159
+ const flatItems = collectItems(items);
160
+ const setRootRef = (node) => {
161
+ rootRef.current = node;
162
+ if (typeof ref === "function") ref(node);
163
+ else if (ref) ref.current = node;
164
+ };
165
+ const handleClose = useCallback(() => {
166
+ setPosition(null);
167
+ setFocusedIndex(-1);
168
+ }, []);
169
+ const handleContextMenu = (e) => {
170
+ onContextMenu?.(e);
171
+ e.preventDefault();
172
+ let x = e.clientX;
173
+ let y = e.clientY;
174
+ if (x === 0 && y === 0 && rootRef.current) {
175
+ const rect = rootRef.current.getBoundingClientRect();
176
+ x = rect.left + rect.width / 2;
177
+ y = rect.top + rect.height / 2;
178
+ }
179
+ setPosition({ x, y });
180
+ setFocusedIndex(-1);
181
+ };
182
+ useEffect(() => {
183
+ if (!isOpen) return;
184
+ const handler = (e) => {
185
+ if (panelRef.current && !panelRef.current.contains(e.target)) {
186
+ handleClose();
187
+ }
188
+ };
189
+ document.addEventListener("mousedown", handler);
190
+ return () => document.removeEventListener("mousedown", handler);
191
+ }, [isOpen, handleClose]);
192
+ useEffect(() => {
193
+ if (!isOpen) return;
194
+ const handler = () => handleClose();
195
+ window.addEventListener("scroll", handler, true);
196
+ window.addEventListener("resize", handler);
197
+ return () => {
198
+ window.removeEventListener("scroll", handler, true);
199
+ window.removeEventListener("resize", handler);
200
+ };
201
+ }, [isOpen, handleClose]);
202
+ useLayoutEffect(() => {
203
+ if (!isOpen || !panelRef.current || !position) return;
204
+ const panel = panelRef.current;
205
+ const rect = panel.getBoundingClientRect();
206
+ const margin = 8;
207
+ let { x, y } = position;
208
+ if (x + rect.width + margin > window.innerWidth) {
209
+ x = Math.max(margin, window.innerWidth - rect.width - margin);
210
+ }
211
+ if (y + rect.height + margin > window.innerHeight) {
212
+ y = Math.max(margin, window.innerHeight - rect.height - margin);
213
+ }
214
+ if (x !== position.x || y !== position.y) {
215
+ setPosition({ x, y });
216
+ return;
217
+ }
218
+ panel.focus({ preventScroll: true });
219
+ }, [isOpen, position]);
220
+ const handleKeyDown = (e) => {
221
+ switch (e.key) {
222
+ case "Enter":
223
+ case " ": {
224
+ e.preventDefault();
225
+ if (focusedIndex >= 0) {
226
+ const item = flatItems[focusedIndex];
227
+ if (item && !item.disabled && !item.children) {
228
+ item.onClick?.();
229
+ handleClose();
230
+ }
231
+ }
232
+ break;
233
+ }
234
+ case "ArrowDown":
235
+ e.preventDefault();
236
+ setFocusedIndex((prev) => {
237
+ let next = prev + 1;
238
+ while (next < flatItems.length && flatItems[next].disabled) next++;
239
+ return next < flatItems.length ? next : prev;
240
+ });
241
+ break;
242
+ case "ArrowUp":
243
+ e.preventDefault();
244
+ setFocusedIndex((prev) => {
245
+ let next = prev < 0 ? flatItems.length - 1 : prev - 1;
246
+ while (next >= 0 && flatItems[next].disabled) next--;
247
+ return next >= 0 ? next : prev;
248
+ });
249
+ break;
250
+ case "Escape":
251
+ case "Tab":
252
+ e.preventDefault();
253
+ handleClose();
254
+ break;
255
+ }
256
+ };
257
+ const renderEntry = (entry, index, itemCounter2) => {
258
+ if (entry.type === "separator") {
259
+ return /* @__PURE__ */ jsx("li", { className: `${baseClass}__separator`, role: "separator" }, `sep-${index}`);
260
+ }
261
+ if (entry.type === "group") {
262
+ return /* @__PURE__ */ jsxs("li", { className: `${baseClass}__group`, role: "none", children: [
263
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__group-label`, role: "presentation", children: entry.label }),
264
+ /* @__PURE__ */ jsx("ul", { className: `${baseClass}__group-list`, role: "group", "aria-label": entry.label, children: entry.items.map(
265
+ (subEntry, subIndex) => renderEntry(subEntry, subIndex, itemCounter2)
266
+ ) })
267
+ ] }, `group-${index}`);
268
+ }
269
+ const myIndex = itemCounter2.count;
270
+ itemCounter2.count++;
271
+ const isFocused = myIndex === focusedIndex;
272
+ if (entry.children && entry.children.length > 0) {
273
+ return /* @__PURE__ */ jsx(
274
+ SubMenuItem,
275
+ {
276
+ item: entry,
277
+ baseClass,
278
+ focused: isFocused,
279
+ size,
280
+ onActivate: handleClose
281
+ },
282
+ `submenu-${index}`
283
+ );
284
+ }
285
+ const itemClasses = [
286
+ `${baseClass}__item`,
287
+ isFocused ? `${baseClass}__item--focused` : "",
288
+ entry.disabled ? `${baseClass}__item--disabled` : "",
289
+ entry.destructive ? `${baseClass}__item--destructive` : ""
290
+ ].filter(Boolean).join(" ");
291
+ return /* @__PURE__ */ jsxs(
292
+ "li",
293
+ {
294
+ className: itemClasses,
295
+ role: "menuitem",
296
+ "aria-disabled": entry.disabled || void 0,
297
+ onClick: (e) => {
298
+ e.stopPropagation();
299
+ if (!entry.disabled) {
300
+ entry.onClick?.();
301
+ handleClose();
302
+ }
303
+ },
304
+ children: [
305
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__item-label`, children: [
306
+ entry.icon && /* @__PURE__ */ jsx(
307
+ "span",
308
+ {
309
+ className: `${baseClass}__item-icon material-symbols-rounded`,
310
+ "aria-hidden": "true",
311
+ children: entry.icon
312
+ }
313
+ ),
314
+ entry.label
315
+ ] }),
316
+ entry.shortcut && /* @__PURE__ */ jsx("span", { className: `${baseClass}__shortcut`, children: entry.shortcut })
317
+ ]
318
+ },
319
+ `item-${index}`
320
+ );
321
+ };
322
+ const panelClasses = [
323
+ `${baseClass}__panel`,
324
+ size === "compact" ? `${baseClass}__panel--compact` : ""
325
+ ].filter(Boolean).join(" ");
326
+ const itemCounter = { count: 0 };
327
+ return /* @__PURE__ */ jsxs("div", { ...rest, ref: setRootRef, className: classes, onContextMenu: handleContextMenu, children: [
328
+ children,
329
+ isOpen && /* @__PURE__ */ jsx(
330
+ "ul",
331
+ {
332
+ className: panelClasses,
333
+ role: "menu",
334
+ "aria-label": ariaLabel,
335
+ tabIndex: -1,
336
+ ref: (el) => {
337
+ panelRef.current = el;
338
+ },
339
+ style: { left: position.x, top: position.y },
340
+ onKeyDown: handleKeyDown,
341
+ children: items.map((entry, index) => renderEntry(entry, index, itemCounter))
342
+ }
343
+ )
344
+ ] });
345
+ }
346
+ );
347
+ ContextMenu.displayName = "ContextMenu";
348
+ export {
349
+ ContextMenu
350
+ };
@@ -14,19 +14,6 @@
14
14
  LABEL
15
15
  ============================================ */
16
16
 
17
- .ds-date-input__label {
18
- font-family: var(--font-paragraph-em-family);
19
- font-size: var(--font-paragraph-em-size);
20
- font-weight: var(--font-paragraph-em-weight);
21
- line-height: var(--font-paragraph-em-line-height);
22
- letter-spacing: var(--font-paragraph-em-letter-spacing);
23
- color: var(--color-text-primary);
24
- }
25
-
26
- .ds-date-input__required {
27
- color: var(--color-core-accent-coral);
28
- }
29
-
30
17
  /* ============================================
31
18
  FIELD WRAPPER
32
19
  ============================================ */
@@ -108,25 +95,10 @@
108
95
  --icon-size: var(--icon-size-sm);
109
96
  }
110
97
 
111
- .ds-date-input--compact .ds-date-input__label {
112
- font-size: var(--font-paragraph-sm-size);
113
- line-height: var(--font-paragraph-sm-line-height);
114
- }
115
-
116
98
  /* ============================================
117
99
  HELPER TEXT
118
100
  ============================================ */
119
101
 
120
- .ds-date-input__helper {
121
- font-family: var(--font-paragraph-sm-family);
122
- font-size: var(--font-paragraph-sm-size);
123
- font-weight: var(--font-paragraph-sm-weight);
124
- line-height: var(--font-paragraph-sm-line-height);
125
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
126
- color: var(--color-text-tertiary);
127
- margin: 0;
128
- }
129
-
130
102
  /* ============================================
131
103
  ERROR STATE
132
104
  ============================================ */
@@ -139,18 +111,10 @@
139
111
  border-color: var(--color-status-error-border);
140
112
  }
141
113
 
142
- .ds-date-input--error .ds-date-input__helper {
143
- color: var(--color-status-error-border);
144
- }
145
-
146
114
  /* ============================================
147
115
  DISABLED STATE
148
116
  ============================================ */
149
117
 
150
- .ds-date-input--disabled .ds-date-input__label {
151
- color: var(--color-input-text-disabled);
152
- }
153
-
154
118
  .ds-date-input--disabled .ds-date-input__field {
155
119
  background-color: var(--color-input-bg-disabled);
156
120
  border-color: var(--color-input-border-disabled);
@@ -23,7 +23,11 @@ type DateInputOwnProps = {
23
23
  onValueChange?: (value: string) => void;
24
24
  /** Additional CSS classes — applied to the wrapper, not the <input> */
25
25
  className?: string;
26
- /** @deprecated Pass the native `aria-label` attribute instead. */
26
+ /**
27
+ * Legacy accessible-name prop.
28
+ *
29
+ * @deprecated Pass the native `aria-label` attribute instead.
30
+ */
27
31
  ariaLabel?: string;
28
32
  };
29
33
  export interface DateInputProps extends DateInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof DateInputOwnProps | 'type'> {
@@ -1,5 +1,6 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
+ import { Field } from "../Field/Field.js";
3
4
  import "./DateInput.css";
4
5
  import "../../fonts/material-symbols.css";
5
6
  const DateInput = React.forwardRef(
@@ -35,40 +36,43 @@ const DateInput = React.forwardRef(
35
36
  onValueChange?.(e.target.value);
36
37
  };
37
38
  const inputId = id || name || label?.toLowerCase().replace(/\s+/g, "-");
38
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
39
- label && /* @__PURE__ */ jsxs("label", { className: `${baseClass}__label`, htmlFor: inputId, children: [
39
+ return /* @__PURE__ */ jsx(
40
+ Field,
41
+ {
42
+ className: classes,
40
43
  label,
41
- required && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__required`, "aria-hidden": "true", children: [
42
- " ",
43
- "*"
44
+ helperText,
45
+ error,
46
+ required,
47
+ disabled,
48
+ size,
49
+ id: inputId,
50
+ children: /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field-wrapper`, children: [
51
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: "calendar_today" }),
52
+ /* @__PURE__ */ jsx(
53
+ "input",
54
+ {
55
+ ...rest,
56
+ ref,
57
+ className: `${baseClass}__field`,
58
+ type: "date",
59
+ id: inputId,
60
+ name,
61
+ value,
62
+ placeholder,
63
+ disabled,
64
+ required,
65
+ min,
66
+ max,
67
+ "aria-label": ariaLabel || rest["aria-label"] || label,
68
+ "aria-invalid": error,
69
+ "aria-describedby": helperText ? `${inputId}-helper` : rest["aria-describedby"],
70
+ onChange: handleChange
71
+ }
72
+ )
44
73
  ] })
45
- ] }),
46
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field-wrapper`, children: [
47
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: "calendar_today" }),
48
- /* @__PURE__ */ jsx(
49
- "input",
50
- {
51
- ...rest,
52
- ref,
53
- className: `${baseClass}__field`,
54
- type: "date",
55
- id: inputId,
56
- name,
57
- value,
58
- placeholder,
59
- disabled,
60
- required,
61
- min,
62
- max,
63
- "aria-label": ariaLabel || rest["aria-label"] || label,
64
- "aria-invalid": error,
65
- "aria-describedby": helperText ? `${inputId}-helper` : rest["aria-describedby"],
66
- onChange: handleChange
67
- }
68
- )
69
- ] }),
70
- helperText && /* @__PURE__ */ jsx("p", { className: `${baseClass}__helper`, id: `${inputId}-helper`, children: helperText })
71
- ] });
74
+ }
75
+ );
72
76
  }
73
77
  );
74
78
  DateInput.displayName = "DateInput";
@@ -125,40 +125,51 @@ const DatePicker = React.forwardRef(
125
125
  }
126
126
  )
127
127
  ] }),
128
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__grid`, role: "grid", children: [
129
- DAYS.map((day) => /* @__PURE__ */ jsx("div", { className: `${baseClass}__day-header`, role: "columnheader", children: day }, day)),
130
- cells.map((cell, idx) => {
131
- const isSelected = selectedDate && isSameDay(cell.date, selectedDate);
132
- const isToday = isSameDay(cell.date, today);
133
- const isCellDisabled = isDateDisabled(cell.date);
134
- const cellClasses = [
135
- `${baseClass}__cell`,
136
- !cell.inMonth ? `${baseClass}__cell--outside` : "",
137
- isSelected ? `${baseClass}__cell--selected` : "",
138
- isToday && !isSelected ? `${baseClass}__cell--today` : "",
139
- isCellDisabled ? `${baseClass}__cell--disabled` : ""
140
- ].filter(Boolean).join(" ");
141
- return /* @__PURE__ */ jsx(
142
- "button",
143
- {
144
- type: "button",
145
- className: cellClasses,
146
- onClick: () => handleSelect(cell),
147
- disabled: isCellDisabled,
148
- "aria-label": cell.date.toLocaleDateString(void 0, {
149
- weekday: "long",
150
- year: "numeric",
151
- month: "long",
152
- day: "numeric"
153
- }),
154
- "aria-selected": isSelected || void 0,
155
- role: "gridcell",
156
- children: cell.day
157
- },
158
- idx
159
- );
160
- })
161
- ] })
128
+ /* @__PURE__ */ jsxs(
129
+ "div",
130
+ {
131
+ className: `${baseClass}__grid`,
132
+ role: "group",
133
+ "aria-label": `${MONTHS[viewMonth]} ${viewYear}`,
134
+ children: [
135
+ DAYS.map((day) => (
136
+ // Each cell's aria-label already carries the full weekday name, so
137
+ // the visual column headers are redundant noise for a screen reader.
138
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__day-header`, "aria-hidden": "true", children: day }, day)
139
+ )),
140
+ cells.map((cell, idx) => {
141
+ const isSelected = selectedDate && isSameDay(cell.date, selectedDate);
142
+ const isToday = isSameDay(cell.date, today);
143
+ const isCellDisabled = isDateDisabled(cell.date);
144
+ const cellClasses = [
145
+ `${baseClass}__cell`,
146
+ !cell.inMonth ? `${baseClass}__cell--outside` : "",
147
+ isSelected ? `${baseClass}__cell--selected` : "",
148
+ isToday && !isSelected ? `${baseClass}__cell--today` : "",
149
+ isCellDisabled ? `${baseClass}__cell--disabled` : ""
150
+ ].filter(Boolean).join(" ");
151
+ return /* @__PURE__ */ jsx(
152
+ "button",
153
+ {
154
+ type: "button",
155
+ className: cellClasses,
156
+ onClick: () => handleSelect(cell),
157
+ disabled: isCellDisabled,
158
+ "aria-label": `${cell.date.toLocaleDateString(void 0, {
159
+ weekday: "long",
160
+ year: "numeric",
161
+ month: "long",
162
+ day: "numeric"
163
+ })}${isSelected ? ", selected" : ""}`,
164
+ "aria-current": isToday ? "date" : void 0,
165
+ children: cell.day
166
+ },
167
+ idx
168
+ );
169
+ })
170
+ ]
171
+ }
172
+ )
162
173
  ] });
163
174
  }
164
175
  );
@@ -88,11 +88,11 @@
88
88
  }
89
89
 
90
90
  .ds-dialog__title {
91
- font-family: var(--font-heading-6-family);
92
- font-size: var(--font-heading-6-size);
93
- font-weight: var(--font-heading-6-weight);
94
- line-height: var(--font-heading-6-line-height);
95
- letter-spacing: var(--font-heading-6-letter-spacing);
91
+ font-family: var(--font-heading-3-family);
92
+ font-size: var(--font-heading-3-size);
93
+ font-weight: var(--font-heading-3-weight);
94
+ line-height: var(--font-heading-3-line-height);
95
+ letter-spacing: var(--font-heading-3-letter-spacing);
96
96
  color: var(--color-text-primary);
97
97
  margin: 0;
98
98
  }
@@ -42,9 +42,7 @@ const Dialog = React.forwardRef(
42
42
  if (!open) return;
43
43
  const panel = panelRef.current;
44
44
  if (panel) {
45
- const first = panel.querySelector(
46
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
47
- );
45
+ const first = panel.querySelector("button, [href], input, select, textarea") ?? panel.querySelector('[tabindex]:not([tabindex="-1"])');
48
46
  (first ?? panel).focus();
49
47
  }
50
48
  const handleKeyDown = (e) => {
@@ -133,7 +131,10 @@ const Dialog = React.forwardRef(
133
131
  }
134
132
  )
135
133
  ] }),
136
- children && /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children }),
134
+ children && // Scrollable when content is long; without a tab stop a keyboard
135
+ // user cannot reach that scroll. The initial-focus query below
136
+ // still prefers a real control, so this does not steal focus.
137
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, tabIndex: 0, children }),
137
138
  footer && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer`, children: footer })
138
139
  ]
139
140
  }
@@ -44,7 +44,9 @@ const Drawer = React.forwardRef(
44
44
  if (!open) return;
45
45
  const panel = panelRef.current;
46
46
  if (panel) {
47
- const first = panel.querySelector(FOCUSABLE);
47
+ const first = panel.querySelector(
48
+ "button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled])"
49
+ ) ?? panel.querySelector(FOCUSABLE);
48
50
  (first ?? panel).focus();
49
51
  }
50
52
  const handleKeyDown = (e) => {
@@ -132,7 +134,10 @@ const Drawer = React.forwardRef(
132
134
  }
133
135
  )
134
136
  ] }),
135
- children && /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children }),
137
+ children && // Scrollable when content is long; without a tab stop a keyboard
138
+ // user cannot reach that scroll. The initial-focus query below
139
+ // still prefers a real control, so this does not steal focus.
140
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, tabIndex: 0, children }),
136
141
  footer && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer`, children: footer })
137
142
  ]
138
143
  }