@robr0/design-system 0.1.0 → 0.3.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.
- package/README.md +13 -9
- package/components/Accordion/Accordion.js +0 -1
- package/components/Avatar/Avatar.js +1 -0
- package/components/Breadcrumb/Breadcrumb.css +9 -1
- package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
- package/components/Breadcrumb/Breadcrumb.js +3 -2
- package/components/Button/Button.css +18 -0
- package/components/Button/Button.d.ts +38 -16
- package/components/Button/Button.js +76 -58
- package/components/Card/Card.css +6 -0
- package/components/Card/Card.d.ts +17 -5
- package/components/Card/Card.js +101 -80
- package/components/Checkbox/Checkbox.d.ts +24 -12
- package/components/Checkbox/Checkbox.js +99 -85
- package/components/CircularButton/CircularButton.css +11 -0
- package/components/CircularButton/CircularButton.d.ts +7 -1
- package/components/CircularButton/CircularButton.js +15 -4
- package/components/CodeBlock/CodeBlock.js +10 -1
- package/components/ColorPicker/ColorPicker.css +278 -0
- package/components/ColorPicker/ColorPicker.d.ts +50 -0
- package/components/ColorPicker/ColorPicker.js +338 -0
- package/components/Combobox/Combobox.css +0 -36
- package/components/Combobox/Combobox.d.ts +19 -7
- package/components/Combobox/Combobox.js +106 -89
- package/components/CommandPalette/CommandPalette.css +1 -15
- package/components/CommandPalette/CommandPalette.js +6 -5
- package/components/ContextMenu/ContextMenu.css +262 -0
- package/components/ContextMenu/ContextMenu.d.ts +26 -0
- package/components/ContextMenu/ContextMenu.js +350 -0
- package/components/DateInput/DateInput.css +0 -36
- package/components/DateInput/DateInput.d.ts +18 -21
- package/components/DateInput/DateInput.js +76 -64
- package/components/DatePicker/DatePicker.d.ts +9 -2
- package/components/DatePicker/DatePicker.js +137 -128
- package/components/Dialog/Dialog.d.ts +15 -4
- package/components/Dialog/Dialog.js +136 -118
- package/components/Drawer/Drawer.d.ts +15 -4
- package/components/Drawer/Drawer.js +138 -119
- package/components/Dropdown/Dropdown.css +0 -36
- package/components/Dropdown/Dropdown.d.ts +24 -8
- package/components/Dropdown/Dropdown.js +219 -169
- package/components/DropdownMenu/DropdownMenu.js +10 -7
- package/components/Field/Field.css +80 -0
- package/components/Field/Field.d.ts +50 -0
- package/components/Field/Field.js +58 -0
- package/components/Field/FieldContext.d.ts +42 -0
- package/components/Field/FieldContext.js +7 -0
- package/components/FileInput/FileInput.css +0 -36
- package/components/FileInput/FileInput.d.ts +13 -17
- package/components/FileInput/FileInput.js +168 -141
- package/components/Input/Input.css +0 -43
- package/components/Input/Input.d.ts +23 -22
- package/components/Input/Input.js +93 -66
- package/components/Kbd/Kbd.css +28 -0
- package/components/Kbd/Kbd.d.ts +19 -0
- package/components/Kbd/Kbd.js +14 -0
- package/components/Popover/Popover.d.ts +0 -6
- package/components/Popover/Popover.js +22 -16
- package/components/ProgressBar/ProgressBar.js +1 -1
- package/components/RadioButton/RadioButton.d.ts +26 -13
- package/components/RadioButton/RadioButton.js +95 -70
- package/components/SelectionCard/SelectionCard.css +6 -2
- package/components/SelectionCard/SelectionCard.d.ts +10 -4
- package/components/SelectionCard/SelectionCard.js +72 -56
- package/components/Skeleton/Skeleton.css +2 -0
- package/components/Slider/Slider.d.ts +19 -10
- package/components/Slider/Slider.js +50 -40
- package/components/Spinner/Spinner.css +11 -0
- package/components/Spinner/Spinner.d.ts +2 -2
- package/components/Swatch/Swatch.css +52 -0
- package/components/Swatch/Swatch.d.ts +34 -0
- package/components/Swatch/Swatch.js +44 -0
- package/components/Table/Table.css +19 -0
- package/components/Table/Table.d.ts +10 -2
- package/components/Table/Table.js +58 -58
- package/components/Textarea/Textarea.css +0 -43
- package/components/Textarea/Textarea.d.ts +21 -22
- package/components/Textarea/Textarea.js +78 -67
- package/components/ToggleGroup/ToggleGroup.d.ts +18 -8
- package/components/ToggleGroup/ToggleGroup.js +61 -44
- package/components/ToggleSwitch/ToggleSwitch.css +7 -2
- package/components/ToggleSwitch/ToggleSwitch.d.ts +17 -9
- package/components/ToggleSwitch/ToggleSwitch.js +57 -44
- package/components/registry.d.ts +47 -10
- package/components/registry.js +5 -1
- package/components/registry.json +498 -56
- package/components/registry.json.d.ts +498 -56
- package/components/registry.json.js +4 -1
- package/index.d.ts +8 -1
- package/index.js +16 -1
- package/package.json +1 -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);
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
|
|
2
|
+
/** Props owned by DateInput itself — everything else falls through to the <input>. */
|
|
3
|
+
type DateInputOwnProps = {
|
|
3
4
|
/** Input label text */
|
|
4
5
|
label?: string;
|
|
5
6
|
/** Current value (YYYY-MM-DD format) */
|
|
6
7
|
value?: string;
|
|
7
|
-
/**
|
|
8
|
-
placeholder?: string;
|
|
9
|
-
/** Component size */
|
|
8
|
+
/** Component size (not the native character-width `size` attribute) */
|
|
10
9
|
size?: 'default' | 'compact';
|
|
11
|
-
/** Whether the input is disabled */
|
|
12
|
-
disabled?: boolean;
|
|
13
|
-
/** Whether the input is required */
|
|
14
|
-
required?: boolean;
|
|
15
10
|
/** Error state */
|
|
16
11
|
error?: boolean;
|
|
17
12
|
/** Helper or error message */
|
|
@@ -20,24 +15,26 @@ export interface DateInputProps {
|
|
|
20
15
|
min?: string;
|
|
21
16
|
/** Maximum selectable date (YYYY-MM-DD) */
|
|
22
17
|
max?: string;
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
/** Additional CSS classes */
|
|
18
|
+
/**
|
|
19
|
+
* Convenience callback receiving the value directly.
|
|
20
|
+
* Fires alongside `onChange`, which keeps the standard React event signature
|
|
21
|
+
* so form libraries work unmodified.
|
|
22
|
+
*/
|
|
23
|
+
onValueChange?: (value: string) => void;
|
|
24
|
+
/** Additional CSS classes — applied to the wrapper, not the <input> */
|
|
30
25
|
className?: string;
|
|
31
|
-
/**
|
|
26
|
+
/** @deprecated Pass the native `aria-label` attribute instead. */
|
|
32
27
|
ariaLabel?: string;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
/** HTML id attribute */
|
|
36
|
-
id?: string;
|
|
28
|
+
};
|
|
29
|
+
export interface DateInputProps extends DateInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof DateInputOwnProps | 'type'> {
|
|
37
30
|
}
|
|
38
31
|
/**
|
|
39
32
|
* Date input component with native date picker.
|
|
40
33
|
* Uses the browser's built-in date input with custom styling,
|
|
41
34
|
* calendar icon, label, and validation states.
|
|
35
|
+
*
|
|
36
|
+
* Forwards a ref to the underlying `<input type="date">` and spreads
|
|
37
|
+
* unrecognised props onto it.
|
|
42
38
|
*/
|
|
43
|
-
export declare const DateInput:
|
|
39
|
+
export declare const DateInput: React.ForwardRefExoticComponent<DateInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
40
|
+
export {};
|
|
@@ -1,69 +1,81 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import "react";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
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
|
-
const DateInput = (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
6
|
+
const DateInput = React.forwardRef(
|
|
7
|
+
({
|
|
8
|
+
label,
|
|
9
|
+
value,
|
|
10
|
+
placeholder,
|
|
11
|
+
size = "default",
|
|
12
|
+
disabled = false,
|
|
13
|
+
required = false,
|
|
14
|
+
error = false,
|
|
15
|
+
helperText,
|
|
16
|
+
min,
|
|
17
|
+
max,
|
|
18
|
+
onChange,
|
|
19
|
+
onValueChange,
|
|
20
|
+
className = "",
|
|
21
|
+
ariaLabel,
|
|
22
|
+
name,
|
|
23
|
+
id,
|
|
24
|
+
...rest
|
|
25
|
+
}, ref) => {
|
|
26
|
+
const baseClass = "ds-date-input";
|
|
27
|
+
const classes = [
|
|
28
|
+
baseClass,
|
|
29
|
+
`${baseClass}--${size}`,
|
|
30
|
+
error ? `${baseClass}--error` : "",
|
|
31
|
+
disabled ? `${baseClass}--disabled` : "",
|
|
32
|
+
className
|
|
33
|
+
].filter(Boolean).join(" ");
|
|
34
|
+
const handleChange = (e) => {
|
|
35
|
+
onChange?.(e);
|
|
36
|
+
onValueChange?.(e.target.value);
|
|
37
|
+
};
|
|
38
|
+
const inputId = id || name || label?.toLowerCase().replace(/\s+/g, "-");
|
|
39
|
+
return /* @__PURE__ */ jsx(
|
|
40
|
+
Field,
|
|
41
|
+
{
|
|
42
|
+
className: classes,
|
|
43
|
+
label,
|
|
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
|
+
)
|
|
73
|
+
] })
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
DateInput.displayName = "DateInput";
|
|
67
79
|
export {
|
|
68
80
|
DateInput
|
|
69
81
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Props owned by DatePicker itself — everything else falls through to the wrapper. */
|
|
3
|
+
type DatePickerOwnProps = {
|
|
2
4
|
/** Currently selected date (YYYY-MM-DD) */
|
|
3
5
|
value?: string;
|
|
4
6
|
/** Component size */
|
|
@@ -13,10 +15,15 @@ export interface DatePickerProps {
|
|
|
13
15
|
onDateSelect?: (date: string) => void;
|
|
14
16
|
/** Additional CSS classes */
|
|
15
17
|
className?: string;
|
|
18
|
+
};
|
|
19
|
+
export interface DatePickerProps extends DatePickerOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof DatePickerOwnProps> {
|
|
16
20
|
}
|
|
17
21
|
/**
|
|
18
22
|
* Inline date picker calendar component.
|
|
19
23
|
* Displays a full month grid with navigation,
|
|
20
24
|
* day headers, and selectable date cells.
|
|
25
|
+
*
|
|
26
|
+
* Forwards a ref to the wrapping element and spreads unrecognised props onto it.
|
|
21
27
|
*/
|
|
22
|
-
export declare const DatePicker:
|
|
28
|
+
export declare const DatePicker: React.ForwardRefExoticComponent<DatePickerProps & React.RefAttributes<HTMLDivElement>>;
|
|
29
|
+
export {};
|