@robr0/design-system 0.1.0 → 0.2.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 +6 -4
- package/components/Breadcrumb/Breadcrumb.css +9 -1
- package/components/Button/Button.d.ts +32 -16
- package/components/Button/Button.js +66 -58
- 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/Combobox/Combobox.d.ts +19 -7
- package/components/Combobox/Combobox.js +18 -9
- package/components/DateInput/DateInput.d.ts +18 -21
- package/components/DateInput/DateInput.js +71 -63
- package/components/DatePicker/DatePicker.d.ts +9 -2
- package/components/DatePicker/DatePicker.js +127 -129
- package/components/Dialog/Dialog.d.ts +15 -4
- package/components/Dialog/Dialog.js +133 -116
- package/components/Drawer/Drawer.d.ts +15 -4
- package/components/Drawer/Drawer.js +133 -119
- package/components/Dropdown/Dropdown.d.ts +24 -8
- package/components/Dropdown/Dropdown.js +226 -186
- package/components/FileInput/FileInput.d.ts +13 -17
- package/components/FileInput/FileInput.js +177 -158
- package/components/Input/Input.d.ts +23 -22
- package/components/Input/Input.js +87 -65
- package/components/RadioButton/RadioButton.d.ts +26 -13
- package/components/RadioButton/RadioButton.js +95 -70
- package/components/SelectionCard/SelectionCard.d.ts +10 -4
- package/components/SelectionCard/SelectionCard.js +72 -56
- package/components/Slider/Slider.d.ts +19 -10
- package/components/Slider/Slider.js +50 -40
- package/components/Table/Table.d.ts +10 -2
- package/components/Table/Table.js +58 -58
- package/components/Textarea/Textarea.d.ts +21 -22
- package/components/Textarea/Textarea.js +77 -68
- package/components/ToggleGroup/ToggleGroup.d.ts +18 -8
- package/components/ToggleGroup/ToggleGroup.js +61 -44
- package/components/ToggleSwitch/ToggleSwitch.d.ts +17 -9
- package/components/ToggleSwitch/ToggleSwitch.js +57 -44
- package/package.json +1 -1
|
@@ -1,134 +1,148 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useId, useSyncExternalStore, useEffect } from "react";
|
|
2
|
+
import React, { useRef, useId, useSyncExternalStore, useEffect } from "react";
|
|
3
3
|
import ReactDOM from "react-dom";
|
|
4
4
|
import "./Drawer.css";
|
|
5
5
|
import "../../fonts/material-symbols.css";
|
|
6
6
|
const emptySubscribe = () => () => {
|
|
7
7
|
};
|
|
8
8
|
const FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
9
|
-
const Drawer = (
|
|
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
|
-
|
|
9
|
+
const Drawer = React.forwardRef(
|
|
10
|
+
({
|
|
11
|
+
open,
|
|
12
|
+
onOpenChange,
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
children,
|
|
16
|
+
footer,
|
|
17
|
+
side = "right",
|
|
18
|
+
size = "md",
|
|
19
|
+
dismissible = true,
|
|
20
|
+
className = "",
|
|
21
|
+
...rest
|
|
22
|
+
}, ref) => {
|
|
23
|
+
const panelRef = useRef(null);
|
|
24
|
+
const previousFocusRef = useRef(null);
|
|
25
|
+
const titleId = useId();
|
|
26
|
+
const descId = useId();
|
|
27
|
+
const baseClass = "ds-drawer";
|
|
28
|
+
const setPanelRef = (node) => {
|
|
29
|
+
panelRef.current = node;
|
|
30
|
+
if (typeof ref === "function") ref(node);
|
|
31
|
+
else if (ref) ref.current = node;
|
|
32
|
+
};
|
|
33
|
+
const mounted = useSyncExternalStore(
|
|
34
|
+
emptySubscribe,
|
|
35
|
+
() => true,
|
|
36
|
+
() => false
|
|
37
|
+
);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (open) {
|
|
40
|
+
previousFocusRef.current = document.activeElement;
|
|
41
|
+
}
|
|
42
|
+
}, [open]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!open) return;
|
|
45
|
+
const panel = panelRef.current;
|
|
46
|
+
if (panel) {
|
|
47
|
+
const first = panel.querySelector(FOCUSABLE);
|
|
48
|
+
(first ?? panel).focus();
|
|
43
49
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const last = focusable[focusable.length - 1];
|
|
49
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
50
|
-
e.preventDefault();
|
|
51
|
-
last.focus();
|
|
52
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
53
|
-
e.preventDefault();
|
|
54
|
-
first.focus();
|
|
50
|
+
const handleKeyDown = (e) => {
|
|
51
|
+
if (e.key === "Escape" && dismissible) {
|
|
52
|
+
onOpenChange(false);
|
|
53
|
+
return;
|
|
55
54
|
}
|
|
55
|
+
if (e.key === "Tab" && panel) {
|
|
56
|
+
const focusable = panel.querySelectorAll(FOCUSABLE);
|
|
57
|
+
if (focusable.length === 0) return;
|
|
58
|
+
const first = focusable[0];
|
|
59
|
+
const last = focusable[focusable.length - 1];
|
|
60
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
last.focus();
|
|
63
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
first.focus();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
70
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
71
|
+
}, [open, dismissible, onOpenChange]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!open && previousFocusRef.current) {
|
|
74
|
+
previousFocusRef.current.focus();
|
|
75
|
+
previousFocusRef.current = null;
|
|
56
76
|
}
|
|
77
|
+
}, [open]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (open) {
|
|
80
|
+
document.body.style.overflow = "hidden";
|
|
81
|
+
} else {
|
|
82
|
+
document.body.style.overflow = "";
|
|
83
|
+
}
|
|
84
|
+
return () => {
|
|
85
|
+
document.body.style.overflow = "";
|
|
86
|
+
};
|
|
87
|
+
}, [open]);
|
|
88
|
+
const handleScrimClick = () => {
|
|
89
|
+
if (dismissible) onOpenChange(false);
|
|
57
90
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__heading`, children: [
|
|
102
|
-
/* @__PURE__ */ jsx("h2", { className: `${baseClass}__title`, id: titleId, children: title }),
|
|
103
|
-
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, id: descId, children: description })
|
|
91
|
+
const classes = [
|
|
92
|
+
baseClass,
|
|
93
|
+
`${baseClass}--${side}`,
|
|
94
|
+
`${baseClass}--${size}`,
|
|
95
|
+
open ? `${baseClass}--open` : "",
|
|
96
|
+
className
|
|
97
|
+
].filter(Boolean).join(" ");
|
|
98
|
+
const drawer = /* @__PURE__ */ jsxs("div", { className: classes, children: [
|
|
99
|
+
/* @__PURE__ */ jsx("div", { className: `${baseClass}__scrim`, onClick: handleScrimClick }),
|
|
100
|
+
/* @__PURE__ */ jsxs(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
...rest,
|
|
104
|
+
className: `${baseClass}__panel`,
|
|
105
|
+
ref: setPanelRef,
|
|
106
|
+
role: "dialog",
|
|
107
|
+
"aria-modal": "true",
|
|
108
|
+
"aria-labelledby": titleId,
|
|
109
|
+
"aria-describedby": description ? descId : rest["aria-describedby"],
|
|
110
|
+
tabIndex: -1,
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__header`, children: [
|
|
113
|
+
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__heading`, children: [
|
|
114
|
+
/* @__PURE__ */ jsx("h2", { className: `${baseClass}__title`, id: titleId, children: title }),
|
|
115
|
+
description && /* @__PURE__ */ jsx("p", { className: `${baseClass}__description`, id: descId, children: description })
|
|
116
|
+
] }),
|
|
117
|
+
dismissible && /* @__PURE__ */ jsx(
|
|
118
|
+
"button",
|
|
119
|
+
{
|
|
120
|
+
type: "button",
|
|
121
|
+
className: `${baseClass}__close`,
|
|
122
|
+
onClick: () => onOpenChange(false),
|
|
123
|
+
"aria-label": "Close drawer",
|
|
124
|
+
children: /* @__PURE__ */ jsx(
|
|
125
|
+
"span",
|
|
126
|
+
{
|
|
127
|
+
className: `${baseClass}__close-icon material-symbols-rounded`,
|
|
128
|
+
"aria-hidden": "true",
|
|
129
|
+
children: "close"
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
)
|
|
104
134
|
] }),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"aria-hidden": "true",
|
|
117
|
-
children: "close"
|
|
118
|
-
}
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
] }),
|
|
123
|
-
children && /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children }),
|
|
124
|
-
footer && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer`, children: footer })
|
|
125
|
-
]
|
|
126
|
-
}
|
|
127
|
-
)
|
|
128
|
-
] });
|
|
129
|
-
if (!mounted) return null;
|
|
130
|
-
return ReactDOM.createPortal(drawer, document.body);
|
|
131
|
-
};
|
|
135
|
+
children && /* @__PURE__ */ jsx("div", { className: `${baseClass}__body`, children }),
|
|
136
|
+
footer && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer`, children: footer })
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
] });
|
|
141
|
+
if (!mounted) return null;
|
|
142
|
+
return ReactDOM.createPortal(drawer, document.body);
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
Drawer.displayName = "Drawer";
|
|
132
146
|
export {
|
|
133
147
|
Drawer
|
|
134
148
|
};
|
|
@@ -13,7 +13,8 @@ export interface DropdownOptionGroup {
|
|
|
13
13
|
/** Options within the group */
|
|
14
14
|
options: DropdownOption[];
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
/** Props owned by Dropdown itself — everything else falls through to the wrapper. */
|
|
17
|
+
type DropdownOwnProps = {
|
|
17
18
|
/** Dropdown label text */
|
|
18
19
|
label?: string;
|
|
19
20
|
/** Placeholder when no value selected */
|
|
@@ -34,15 +35,30 @@ export interface DropdownProps {
|
|
|
34
35
|
error?: boolean;
|
|
35
36
|
/** Helper or error message */
|
|
36
37
|
helperText?: string;
|
|
37
|
-
/**
|
|
38
|
-
|
|
38
|
+
/** Called with the newly selected value */
|
|
39
|
+
onValueChange?: (value: string) => void;
|
|
39
40
|
/** Additional CSS classes */
|
|
40
41
|
className?: string;
|
|
41
|
-
/**
|
|
42
|
+
/** @deprecated Use `onValueChange` instead. */
|
|
43
|
+
onChange?: (value: string) => void;
|
|
44
|
+
/** @deprecated Pass the native `aria-label` attribute instead. */
|
|
42
45
|
ariaLabel?: string;
|
|
43
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Used only as a fallback for deriving the element id (`id || name || label`).
|
|
48
|
+
*
|
|
49
|
+
* Note: Dropdown renders a `<div role="combobox">`, not a native `<select>`,
|
|
50
|
+
* so `name` does **not** make it participate in native form submission.
|
|
51
|
+
*/
|
|
44
52
|
name?: string;
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
};
|
|
54
|
+
export interface DropdownProps extends DropdownOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof DropdownOwnProps> {
|
|
47
55
|
}
|
|
48
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Static single-select dropdown. Renders a `<div role="combobox">` paired with a
|
|
58
|
+
* `role="listbox"` menu; supports flat and grouped options with full keyboard
|
|
59
|
+
* navigation.
|
|
60
|
+
*
|
|
61
|
+
* Forwards a ref to the wrapping element and spreads unrecognised props onto it.
|
|
62
|
+
*/
|
|
63
|
+
export declare const Dropdown: React.ForwardRefExoticComponent<DropdownProps & React.RefAttributes<HTMLDivElement>>;
|
|
64
|
+
export {};
|