@boostdev/design-system-components 1.2.0 → 1.2.2
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/AGENTS.md +46 -4
- package/README.md +15 -15
- package/dist/client.cjs +149 -124
- package/dist/client.css +503 -525
- package/dist/client.d.cts +71 -96
- package/dist/client.d.ts +71 -96
- package/dist/client.js +149 -124
- package/dist/index.cjs +149 -124
- package/dist/index.css +503 -525
- package/dist/index.d.cts +71 -96
- package/dist/index.d.ts +71 -96
- package/dist/index.js +149 -124
- package/dist/web-components/{chunk-6MH5UWUD.js → chunk-2FGATTGT.js} +5 -0
- package/dist/web-components/{chunk-5IPHEONG.js → chunk-6I2DBFQ7.js} +1 -0
- package/dist/web-components/{chunk-DI46Q2EA.js → chunk-OCODKRVZ.js} +57 -82
- package/dist/web-components/index.d.ts +571 -1
- package/dist/web-components/index.js +1501 -4
- package/dist/web-components/interaction/bds-collapsible.js +1 -1
- package/dist/web-components/interaction/bds-drawer.js +1 -1
- package/dist/web-components/interaction/form/bds-segmented-control.js +1 -1
- package/package.json +1 -1
- package/src/components/interaction/Command/Command.tsx +4 -3
- package/src/components/interaction/Dialog/Dialog.tsx +4 -5
- package/src/components/interaction/Drawer/Drawer.module.css +37 -62
- package/src/components/interaction/Drawer/Drawer.spec.tsx +3 -3
- package/src/components/interaction/Drawer/Drawer.tsx +25 -24
- package/src/components/interaction/DropdownMenu/DropdownMenu.tsx +4 -2
- package/src/components/interaction/Popover/Popover.tsx +4 -3
- package/src/components/interaction/Rating/Rating.tsx +4 -2
- package/src/components/interaction/form/CheckboxGroup/CheckboxGroup.tsx +4 -6
- package/src/components/interaction/form/Combobox/Combobox.tsx +4 -2
- package/src/components/interaction/form/FileInput/FileInput.tsx +4 -3
- package/src/components/interaction/form/NumberInput/NumberInput.tsx +4 -3
- package/src/components/interaction/form/RadioGroup/RadioGroup.tsx +4 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.mdx +8 -4
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.module.css +13 -6
- package/src/components/interaction/form/SegmentedControl/SegmentedControl.tsx +4 -6
- package/src/components/interaction/form/atoms/InputContainer.tsx +2 -2
- package/src/components/layout/ButtonGroup/ButtonGroup.tsx +4 -6
- package/src/components/layout/Card/Card.tsx +4 -10
- package/src/components/layout/IconWrapper/IconWrapper.tsx +4 -5
- package/src/components/layout/SectionHeader/SectionHeader.tsx +5 -4
- package/src/components/ui/Accordion/Accordion.tsx +4 -3
- package/src/components/ui/Alert/Alert.tsx +4 -3
- package/src/components/ui/Avatar/Avatar.tsx +5 -3
- package/src/components/ui/Badge/Badge.tsx +4 -5
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +4 -3
- package/src/components/ui/Calendar/Calendar.tsx +4 -4
- package/src/components/ui/Carousel/Carousel.tsx +4 -4
- package/src/components/ui/DescriptionList/DescriptionList.tsx +4 -4
- package/src/components/ui/Loading/Loading.tsx +4 -3
- package/src/components/ui/NotificationBanner/NotificationBanner.tsx +4 -2
- package/src/components/ui/Pagination/Pagination.tsx +4 -2
- package/src/components/ui/Progress/Progress.tsx +4 -2
- package/src/components/ui/ProgressCircle/ProgressCircle.tsx +4 -1
- package/src/components/ui/Separator/Separator.tsx +5 -3
- package/src/components/ui/Skeleton/Skeleton.tsx +4 -3
- package/src/components/ui/SkipLink/SkipLink.tsx +4 -5
- package/src/components/ui/Tabs/Tabs.tsx +4 -4
- package/src/components/ui/Tooltip/Tooltip.tsx +4 -2
- package/src/components/ui/Typography/Typography.tsx +4 -5
- package/src/stories/DesignSystem/Grid.mdx +2 -0
- package/src/stories/Introduction.mdx +2 -1
- package/src/web-components/index.ts +12 -0
- package/src/web-components/interaction/BdsAccordion.mdx +80 -28
- package/src/web-components/interaction/BdsAccordion.stories.tsx +67 -58
- package/src/web-components/interaction/BdsCollapsible.mdx +93 -23
- package/src/web-components/interaction/BdsCollapsible.stories.tsx +29 -48
- package/src/web-components/interaction/BdsDialog.mdx +88 -27
- package/src/web-components/interaction/BdsDialog.stories.tsx +129 -47
- package/src/web-components/interaction/BdsDrawer.mdx +85 -27
- package/src/web-components/interaction/BdsDrawer.stories.tsx +161 -31
- package/src/web-components/interaction/BdsDropdownMenu.mdx +108 -0
- package/src/web-components/interaction/BdsDropdownMenu.stories.tsx +91 -0
- package/src/web-components/interaction/BdsSkipLink.mdx +72 -16
- package/src/web-components/interaction/BdsSkipLink.stories.tsx +47 -34
- package/src/web-components/interaction/BdsTabs.mdx +97 -27
- package/src/web-components/interaction/BdsTabs.stories.tsx +69 -55
- package/src/web-components/interaction/BdsTooltip.mdx +84 -18
- package/src/web-components/interaction/BdsTooltip.stories.tsx +49 -30
- package/src/web-components/interaction/bds-collapsible.ts +1 -0
- package/src/web-components/interaction/bds-drawer.ts +59 -82
- package/src/web-components/interaction/bds-dropdown-menu-item.ts +124 -0
- package/src/web-components/interaction/bds-dropdown-menu.spec.ts +102 -0
- package/src/web-components/interaction/bds-dropdown-menu.ts +200 -0
- package/src/web-components/interaction/form/BdsCheckbox.mdx +57 -20
- package/src/web-components/interaction/form/BdsCheckbox.stories.tsx +35 -36
- package/src/web-components/interaction/form/BdsCheckboxGroup.mdx +70 -0
- package/src/web-components/interaction/form/BdsCheckboxGroup.stories.tsx +81 -0
- package/src/web-components/interaction/form/BdsCombobox.mdx +52 -33
- package/src/web-components/interaction/form/BdsCombobox.stories.tsx +55 -53
- package/src/web-components/interaction/form/BdsFileInput.mdx +59 -24
- package/src/web-components/interaction/form/BdsFileInput.stories.tsx +38 -53
- package/src/web-components/interaction/form/BdsFormInput.mdx +98 -0
- package/src/web-components/interaction/form/BdsFormInput.stories.tsx +139 -0
- package/src/web-components/interaction/form/BdsNumberInput.mdx +51 -22
- package/src/web-components/interaction/form/BdsNumberInput.stories.tsx +24 -46
- package/src/web-components/interaction/form/BdsRadio.mdx +53 -22
- package/src/web-components/interaction/form/BdsRadio.stories.tsx +19 -51
- package/src/web-components/interaction/form/BdsRadioGroup.mdx +73 -0
- package/src/web-components/interaction/form/BdsRadioGroup.stories.tsx +88 -0
- package/src/web-components/interaction/form/BdsSegmentedControl.mdx +76 -23
- package/src/web-components/interaction/form/BdsSegmentedControl.stories.tsx +64 -54
- package/src/web-components/interaction/form/BdsSelect.mdx +59 -21
- package/src/web-components/interaction/form/BdsSelect.stories.tsx +47 -56
- package/src/web-components/interaction/form/BdsSlider.mdx +55 -27
- package/src/web-components/interaction/form/BdsSlider.stories.tsx +23 -44
- package/src/web-components/interaction/form/BdsSwitch.mdx +53 -20
- package/src/web-components/interaction/form/BdsSwitch.stories.tsx +22 -37
- package/src/web-components/interaction/form/BdsTextarea.mdx +54 -22
- package/src/web-components/interaction/form/BdsTextarea.stories.tsx +35 -44
- package/src/web-components/interaction/form/bds-checkbox-group.spec.ts +55 -0
- package/src/web-components/interaction/form/bds-checkbox-group.ts +117 -0
- package/src/web-components/interaction/form/bds-form-input.spec.ts +83 -0
- package/src/web-components/interaction/form/bds-form-input.ts +268 -0
- package/src/web-components/interaction/form/bds-radio-group.spec.ts +62 -0
- package/src/web-components/interaction/form/bds-radio-group.ts +142 -0
- package/src/web-components/interaction/form/bds-segmented-control.ts +5 -0
- package/src/web-components/ui/BdsAvatar.mdx +29 -20
- package/src/web-components/ui/BdsAvatar.stories.tsx +14 -28
- package/src/web-components/ui/BdsBreadcrumb.mdx +22 -19
- package/src/web-components/ui/BdsBreadcrumb.stories.tsx +11 -21
- package/src/web-components/ui/BdsButtonGroup.mdx +69 -0
- package/src/web-components/ui/BdsButtonGroup.stories.tsx +66 -0
- package/src/web-components/ui/BdsCalendar.mdx +93 -0
- package/src/web-components/ui/BdsCalendar.stories.tsx +63 -0
- package/src/web-components/ui/BdsCard.mdx +30 -25
- package/src/web-components/ui/BdsCard.stories.tsx +19 -42
- package/src/web-components/ui/BdsCarousel.mdx +83 -0
- package/src/web-components/ui/BdsCarousel.stories.tsx +96 -0
- package/src/web-components/ui/BdsDescriptionList.mdx +13 -16
- package/src/web-components/ui/BdsDescriptionList.stories.tsx +34 -49
- package/src/web-components/ui/BdsIconWrapper.mdx +20 -20
- package/src/web-components/ui/BdsIconWrapper.stories.tsx +46 -41
- package/src/web-components/ui/BdsLink.mdx +21 -17
- package/src/web-components/ui/BdsLink.stories.tsx +19 -20
- package/src/web-components/ui/BdsLoading.mdx +9 -13
- package/src/web-components/ui/BdsLoading.stories.tsx +8 -10
- package/src/web-components/ui/BdsNotificationBanner.mdx +32 -37
- package/src/web-components/ui/BdsNotificationBanner.stories.tsx +38 -47
- package/src/web-components/ui/BdsPagination.mdx +74 -0
- package/src/web-components/ui/BdsPagination.stories.tsx +36 -0
- package/src/web-components/ui/BdsProgress.mdx +24 -17
- package/src/web-components/ui/BdsProgress.stories.tsx +55 -29
- package/src/web-components/ui/BdsProgressCircle.mdx +24 -18
- package/src/web-components/ui/BdsProgressCircle.stories.tsx +17 -29
- package/src/web-components/ui/BdsRating.mdx +22 -22
- package/src/web-components/ui/BdsRating.stories.tsx +19 -13
- package/src/web-components/ui/BdsSectionHeader.mdx +31 -28
- package/src/web-components/ui/BdsSectionHeader.stories.tsx +21 -23
- package/src/web-components/ui/BdsSeparator.mdx +22 -8
- package/src/web-components/ui/BdsSeparator.stories.tsx +27 -19
- package/src/web-components/ui/BdsSkeleton.mdx +20 -26
- package/src/web-components/ui/BdsSkeleton.stories.tsx +23 -23
- package/src/web-components/ui/BdsTable.mdx +81 -0
- package/src/web-components/ui/BdsTable.stories.tsx +83 -0
- package/src/web-components/ui/BdsTypography.mdx +20 -29
- package/src/web-components/ui/BdsTypography.stories.tsx +19 -18
- package/src/web-components/ui/bds-button-group.spec.ts +40 -0
- package/src/web-components/ui/bds-button-group.ts +78 -0
- package/src/web-components/ui/bds-calendar.spec.ts +91 -0
- package/src/web-components/ui/bds-calendar.ts +427 -0
- package/src/web-components/ui/bds-carousel.spec.ts +64 -0
- package/src/web-components/ui/bds-carousel.ts +296 -0
- package/src/web-components/ui/bds-pagination.spec.ts +67 -0
- package/src/web-components/ui/bds-pagination.ts +197 -0
- package/src/web-components/ui/bds-table.spec.ts +45 -0
- package/src/web-components/ui/bds-table.ts +96 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
|
|
1
|
+
import { HTMLAttributes, useState, useEffect, useRef, useId, KeyboardEvent, useMemo } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -17,8 +17,7 @@ export interface CommandItem {
|
|
|
17
17
|
onSelect: () => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
interface CommandProps extends WithClassName {
|
|
21
|
-
id?: string;
|
|
20
|
+
interface CommandProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
|
|
22
21
|
isOpen: boolean;
|
|
23
22
|
onOpen?: () => void;
|
|
24
23
|
onClose: () => void;
|
|
@@ -34,6 +33,7 @@ export function Command({
|
|
|
34
33
|
items,
|
|
35
34
|
placeholder = 'Search commands…',
|
|
36
35
|
className,
|
|
36
|
+
...rest
|
|
37
37
|
}: Readonly<CommandProps>) {
|
|
38
38
|
const [query, setQuery] = useState('');
|
|
39
39
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -139,6 +139,7 @@ export function Command({
|
|
|
139
139
|
|
|
140
140
|
return (
|
|
141
141
|
<dialog
|
|
142
|
+
{...rest}
|
|
142
143
|
ref={dialogRef}
|
|
143
144
|
id={dialogId}
|
|
144
145
|
className={cn(css.dialog, className)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTMLAttributes, useEffect, useId, useRef } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -9,15 +9,13 @@ import css from './Dialog.module.css';
|
|
|
9
9
|
import { cn } from '@boostdev/design-system-foundation';
|
|
10
10
|
import type { WithClassName } from '../../../types';
|
|
11
11
|
|
|
12
|
-
interface DialogProps extends WithClassName {
|
|
13
|
-
children: ReactNode;
|
|
14
|
-
id?: string;
|
|
12
|
+
interface DialogProps extends WithClassName, HTMLAttributes<HTMLDialogElement> {
|
|
15
13
|
isOpen?: boolean;
|
|
16
14
|
onOpen?: () => void;
|
|
17
15
|
onClose?: () => void;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose }: DialogProps) {
|
|
18
|
+
export function Dialog({ children, id: idProp, isOpen = false, className, onOpen, onClose, ...rest }: DialogProps) {
|
|
21
19
|
const generatedId = useId();
|
|
22
20
|
const id = idProp ?? generatedId;
|
|
23
21
|
|
|
@@ -92,6 +90,7 @@ export function Dialog({ children, id: idProp, isOpen = false, className, onOpen
|
|
|
92
90
|
|
|
93
91
|
return (
|
|
94
92
|
<dialog
|
|
93
|
+
{...rest}
|
|
95
94
|
ref={dialogRef}
|
|
96
95
|
id={id}
|
|
97
96
|
className={cn(className, css.dialog)}
|
|
@@ -1,84 +1,65 @@
|
|
|
1
1
|
@layer component {
|
|
2
2
|
.drawer {
|
|
3
3
|
position: fixed;
|
|
4
|
-
inset: 0;
|
|
4
|
+
inset-block: 0;
|
|
5
|
+
inset-inline: auto 0;
|
|
6
|
+
inline-size: min(28rem, 90vw);
|
|
7
|
+
max-inline-size: 100vw;
|
|
8
|
+
block-size: 100dvh;
|
|
9
|
+
max-block-size: 100dvh;
|
|
5
10
|
margin: 0;
|
|
6
11
|
padding: 0;
|
|
7
|
-
inline-size: 100%;
|
|
8
|
-
max-inline-size: 100%;
|
|
9
|
-
block-size: 100%;
|
|
10
|
-
max-block-size: 100%;
|
|
11
|
-
border: none;
|
|
12
|
-
background: transparent;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
transition:
|
|
15
|
-
display var(--bds-animation_transition-duration) allow-discrete,
|
|
16
|
-
overlay var(--bds-animation_transition-duration) allow-discrete;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.drawer::backdrop {
|
|
20
|
-
background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
|
|
21
|
-
backdrop-filter: blur(3px);
|
|
22
|
-
transition:
|
|
23
|
-
display var(--bds-animation_transition-duration) allow-discrete,
|
|
24
|
-
overlay var(--bds-animation_transition-duration) allow-discrete,
|
|
25
|
-
background-color var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
26
|
-
backdrop-filter var(--bds-animation_transition-duration) var(--bds-animation_easing);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@starting-style {
|
|
30
|
-
.drawer[open]::backdrop {
|
|
31
|
-
background-color: transparent;
|
|
32
|
-
backdrop-filter: blur(0);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.panel {
|
|
37
|
-
position: absolute;
|
|
38
|
-
inset-block: 0;
|
|
39
|
-
inset-inline: auto;
|
|
40
12
|
display: flex;
|
|
41
13
|
flex-direction: column;
|
|
42
|
-
inline-size: min(28rem, 90vw);
|
|
43
|
-
block-size: 100%;
|
|
44
14
|
background-color: var(--drawer_color, var(--bds-color_bg));
|
|
45
15
|
color: var(--drawer_on-color, var(--bds-color_on-bg));
|
|
16
|
+
border: none;
|
|
46
17
|
box-shadow: var(--bds-shadow_xl);
|
|
47
18
|
overflow: hidden;
|
|
48
19
|
translate: 100% 0;
|
|
49
|
-
transition:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
inset-inline-end: 0;
|
|
54
|
-
translate: 100% 0;
|
|
20
|
+
transition:
|
|
21
|
+
translate var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
22
|
+
display var(--bds-animation_transition-duration) var(--bds-animation_easing) allow-discrete,
|
|
23
|
+
overlay var(--bds-animation_transition-duration) var(--bds-animation_easing) allow-discrete;
|
|
55
24
|
}
|
|
56
25
|
|
|
57
|
-
.--side_left
|
|
58
|
-
inset-inline
|
|
26
|
+
.--side_left.drawer {
|
|
27
|
+
inset-inline: 0 auto;
|
|
59
28
|
translate: -100% 0;
|
|
60
29
|
}
|
|
61
30
|
|
|
62
|
-
.drawer[open]
|
|
31
|
+
.drawer[open] {
|
|
63
32
|
translate: 0 0;
|
|
64
33
|
}
|
|
65
34
|
|
|
66
|
-
|
|
67
|
-
|
|
35
|
+
@starting-style {
|
|
36
|
+
.drawer[open] {
|
|
37
|
+
translate: 100% 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.--side_left.drawer[open] {
|
|
41
|
+
translate: -100% 0;
|
|
42
|
+
}
|
|
68
43
|
}
|
|
69
44
|
|
|
70
|
-
.drawer
|
|
71
|
-
|
|
45
|
+
.drawer::backdrop {
|
|
46
|
+
background-color: var(--color_backdrop, rgb(0 0 0 / 50%));
|
|
47
|
+
backdrop-filter: blur(3px);
|
|
48
|
+
opacity: 0;
|
|
49
|
+
transition:
|
|
50
|
+
opacity var(--bds-animation_transition-duration) var(--bds-animation_easing),
|
|
51
|
+
display var(--bds-animation_transition-duration) var(--bds-animation_easing) allow-discrete,
|
|
52
|
+
overlay var(--bds-animation_transition-duration) var(--bds-animation_easing) allow-discrete;
|
|
72
53
|
}
|
|
73
54
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
to { translate: 0 0; }
|
|
55
|
+
.drawer[open]::backdrop {
|
|
56
|
+
opacity: 1;
|
|
77
57
|
}
|
|
78
58
|
|
|
79
|
-
@
|
|
80
|
-
|
|
81
|
-
|
|
59
|
+
@starting-style {
|
|
60
|
+
.drawer[open]::backdrop {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
}
|
|
82
63
|
}
|
|
83
64
|
|
|
84
65
|
.header {
|
|
@@ -128,14 +109,8 @@
|
|
|
128
109
|
|
|
129
110
|
@media (prefers-reduced-motion: reduce) {
|
|
130
111
|
.drawer,
|
|
131
|
-
.drawer::backdrop
|
|
132
|
-
.panel {
|
|
112
|
+
.drawer::backdrop {
|
|
133
113
|
transition: none;
|
|
134
114
|
}
|
|
135
|
-
|
|
136
|
-
.drawer[open].--side_right .panel,
|
|
137
|
-
.drawer[open].--side_left .panel {
|
|
138
|
-
animation: none;
|
|
139
|
-
}
|
|
140
115
|
}
|
|
141
116
|
}
|
|
@@ -85,13 +85,13 @@ describe('Drawer', () => {
|
|
|
85
85
|
expect(dialog).toHaveAttribute('aria-modal', 'true');
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
it('renders aria-label on the dialog when
|
|
89
|
-
render(<Drawer isOpen title="Settings"
|
|
88
|
+
it('renders aria-label on the dialog when aria-label prop is provided', () => {
|
|
89
|
+
render(<Drawer isOpen title="Settings" aria-label="Settings panel" onClose={() => {}}>Content</Drawer>);
|
|
90
90
|
const dialog = document.querySelector('dialog');
|
|
91
91
|
expect(dialog).toHaveAttribute('aria-label', 'Settings panel');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('does not render aria-label when
|
|
94
|
+
it('does not render aria-label when aria-label prop is not provided', () => {
|
|
95
95
|
render(<Drawer isOpen title="Settings" onClose={() => {}}>Content</Drawer>);
|
|
96
96
|
const dialog = document.querySelector('dialog');
|
|
97
97
|
expect(dialog).not.toHaveAttribute('aria-label');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode, useEffect, useId, useRef } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode, useEffect, useId, useRef } from 'react';
|
|
2
2
|
import { installInvokerCommandsPolyfill } from '../../../polyfill-invoker-commands';
|
|
3
3
|
|
|
4
4
|
// Install once at module load — no-op if native support is present.
|
|
@@ -8,15 +8,12 @@ import css from './Drawer.module.css';
|
|
|
8
8
|
import { cn } from '@boostdev/design-system-foundation';
|
|
9
9
|
import type { WithClassName } from '../../../types';
|
|
10
10
|
|
|
11
|
-
interface DrawerProps extends WithClassName {
|
|
12
|
-
id?: string;
|
|
11
|
+
interface DrawerProps extends WithClassName, Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
|
|
13
12
|
isOpen: boolean;
|
|
14
13
|
onOpen?: () => void;
|
|
15
14
|
onClose: () => void;
|
|
16
15
|
title: ReactNode;
|
|
17
|
-
children: ReactNode;
|
|
18
16
|
side?: 'left' | 'right';
|
|
19
|
-
ariaLabel?: string;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
export function Drawer({
|
|
@@ -27,8 +24,8 @@ export function Drawer({
|
|
|
27
24
|
title,
|
|
28
25
|
children,
|
|
29
26
|
side = 'right',
|
|
30
|
-
ariaLabel,
|
|
31
27
|
className,
|
|
28
|
+
...rest
|
|
32
29
|
}: Readonly<DrawerProps>) {
|
|
33
30
|
const generatedId = useId();
|
|
34
31
|
const id = idProp ?? generatedId;
|
|
@@ -75,7 +72,13 @@ export function Drawer({
|
|
|
75
72
|
|
|
76
73
|
// Close on backdrop click
|
|
77
74
|
const handleClick = (e: React.MouseEvent<HTMLDialogElement>) => {
|
|
78
|
-
|
|
75
|
+
const dialog = dialogRef.current;
|
|
76
|
+
if (!dialog) return;
|
|
77
|
+
const rect = dialog.getBoundingClientRect();
|
|
78
|
+
const outside =
|
|
79
|
+
e.clientX < rect.left || e.clientX > rect.right ||
|
|
80
|
+
e.clientY < rect.top || e.clientY > rect.bottom;
|
|
81
|
+
if (outside) onClose();
|
|
79
82
|
};
|
|
80
83
|
|
|
81
84
|
// Close on Escape (dialog handles it natively, but we sync state)
|
|
@@ -86,31 +89,29 @@ export function Drawer({
|
|
|
86
89
|
|
|
87
90
|
return (
|
|
88
91
|
<dialog
|
|
92
|
+
{...rest}
|
|
89
93
|
ref={dialogRef}
|
|
90
94
|
id={id}
|
|
91
95
|
className={cn(css.drawer, css[`--side_${side}`], className)}
|
|
92
|
-
aria-label={ariaLabel}
|
|
93
96
|
aria-modal="true"
|
|
94
97
|
onClick={handleClick}
|
|
95
98
|
onCancel={handleCancel}
|
|
96
99
|
>
|
|
97
|
-
<div className={css.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
</button>
|
|
111
|
-
</div>
|
|
112
|
-
<div className={css.body}>{children}</div>
|
|
100
|
+
<div className={css.header}>
|
|
101
|
+
{!!title && title}
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
className={css.closeButton}
|
|
105
|
+
commandfor={id}
|
|
106
|
+
command="close"
|
|
107
|
+
aria-label="Close drawer"
|
|
108
|
+
>
|
|
109
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
110
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M18 6L6 18M6 6l12 12" />
|
|
111
|
+
</svg>
|
|
112
|
+
</button>
|
|
113
113
|
</div>
|
|
114
|
+
<div className={css.body}>{children}</div>
|
|
114
115
|
</dialog>
|
|
115
116
|
);
|
|
116
117
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
HTMLAttributes,
|
|
2
3
|
KeyboardEvent,
|
|
3
4
|
ReactElement,
|
|
4
5
|
cloneElement,
|
|
@@ -21,7 +22,7 @@ export interface DropdownMenuItem {
|
|
|
21
22
|
separator?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
interface DropdownMenuProps extends WithClassName {
|
|
25
|
+
interface DropdownMenuProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
25
26
|
trigger: ReactElement;
|
|
26
27
|
items: DropdownMenuItem[];
|
|
27
28
|
placement?: 'bottom-start' | 'bottom-end';
|
|
@@ -32,6 +33,7 @@ export function DropdownMenu({
|
|
|
32
33
|
items,
|
|
33
34
|
placement = 'bottom-start',
|
|
34
35
|
className,
|
|
36
|
+
...rest
|
|
35
37
|
}: Readonly<DropdownMenuProps>) {
|
|
36
38
|
const [isOpen, setIsOpen] = useState(false);
|
|
37
39
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -105,7 +107,7 @@ export function DropdownMenu({
|
|
|
105
107
|
: trigger;
|
|
106
108
|
|
|
107
109
|
return (
|
|
108
|
-
<div ref={containerRef} className={cn(css.wrapper, className)}>
|
|
110
|
+
<div {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
|
|
109
111
|
{triggerEl}
|
|
110
112
|
{isOpen && (
|
|
111
113
|
<ul
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
HTMLAttributes,
|
|
2
3
|
ReactElement,
|
|
3
4
|
ReactNode,
|
|
4
5
|
cloneElement,
|
|
@@ -25,11 +26,10 @@ if (
|
|
|
25
26
|
import('@oddbird/css-anchor-positioning').catch(() => {});
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
interface PopoverProps extends WithClassName {
|
|
29
|
+
interface PopoverProps extends WithClassName, Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
|
|
29
30
|
children: ReactElement;
|
|
30
31
|
content: ReactNode;
|
|
31
32
|
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
32
|
-
'aria-label'?: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function Popover({
|
|
@@ -38,6 +38,7 @@ export function Popover({
|
|
|
38
38
|
placement = 'bottom',
|
|
39
39
|
className,
|
|
40
40
|
'aria-label': ariaLabel,
|
|
41
|
+
...rest
|
|
41
42
|
}: Readonly<PopoverProps>) {
|
|
42
43
|
const [isOpen, setIsOpen] = useState(false);
|
|
43
44
|
const containerRef = useRef<HTMLSpanElement>(null);
|
|
@@ -102,7 +103,7 @@ export function Popover({
|
|
|
102
103
|
: children;
|
|
103
104
|
|
|
104
105
|
return (
|
|
105
|
-
<span ref={containerRef} className={cn(css.wrapper, className)}>
|
|
106
|
+
<span {...rest} ref={containerRef} className={cn(css.wrapper, className)}>
|
|
106
107
|
{trigger}
|
|
107
108
|
<div
|
|
108
109
|
ref={panelRef}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
1
2
|
import css from './Rating.module.css';
|
|
2
3
|
import { cn } from '@boostdev/design-system-foundation';
|
|
3
4
|
import type { WithClassName } from '../../../types';
|
|
4
5
|
|
|
5
|
-
interface RatingProps extends WithClassName {
|
|
6
|
+
interface RatingProps extends WithClassName, HTMLAttributes<HTMLDivElement> {
|
|
6
7
|
value: number;
|
|
7
8
|
max?: number;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function Rating({ value, max = 5, className }: RatingProps) {
|
|
11
|
+
export function Rating({ value, max = 5, className, ...rest }: RatingProps) {
|
|
11
12
|
return (
|
|
12
13
|
<div
|
|
14
|
+
{...rest}
|
|
13
15
|
className={cn(css.rating, className)}
|
|
14
16
|
role="img"
|
|
15
17
|
aria-label={`${value} out of ${max} stars`}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldsetHTMLAttributes, useId } from 'react';
|
|
2
2
|
import css from './CheckboxGroup.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { cn } from '@boostdev/design-system-foundation';
|
|
5
5
|
import type { WithClassName } from '../../../../types';
|
|
6
6
|
|
|
7
|
-
export interface CheckboxGroupProps extends WithClassName {
|
|
7
|
+
export interface CheckboxGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
8
8
|
legend: string;
|
|
9
|
-
children: ReactNode;
|
|
10
9
|
error?: string;
|
|
11
10
|
hint?: string;
|
|
12
11
|
required?: boolean;
|
|
13
|
-
disabled?: boolean;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function CheckboxGroup({
|
|
@@ -19,8 +17,8 @@ export function CheckboxGroup({
|
|
|
19
17
|
error,
|
|
20
18
|
hint,
|
|
21
19
|
required,
|
|
22
|
-
disabled,
|
|
23
20
|
className,
|
|
21
|
+
...rest
|
|
24
22
|
}: Readonly<CheckboxGroupProps>) {
|
|
25
23
|
const id = useId();
|
|
26
24
|
const hintId = id + 'hint';
|
|
@@ -29,8 +27,8 @@ export function CheckboxGroup({
|
|
|
29
27
|
|
|
30
28
|
return (
|
|
31
29
|
<fieldset
|
|
30
|
+
{...rest}
|
|
32
31
|
className={cn(css.group, className)}
|
|
33
|
-
disabled={disabled}
|
|
34
32
|
aria-required={required || undefined}
|
|
35
33
|
aria-describedby={describedBy}
|
|
36
34
|
>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChangeEvent,
|
|
3
|
+
HTMLAttributes,
|
|
3
4
|
KeyboardEvent,
|
|
4
5
|
ReactNode,
|
|
5
6
|
useEffect,
|
|
@@ -21,7 +22,7 @@ export interface ComboboxOption {
|
|
|
21
22
|
disabled?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
interface ComboboxProps extends WithClassName {
|
|
25
|
+
interface ComboboxProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
25
26
|
label: ReactNode;
|
|
26
27
|
name: string;
|
|
27
28
|
options: ComboboxOption[];
|
|
@@ -44,6 +45,7 @@ export function Combobox({
|
|
|
44
45
|
error,
|
|
45
46
|
hint,
|
|
46
47
|
className,
|
|
48
|
+
...rest
|
|
47
49
|
}: Readonly<ComboboxProps>) {
|
|
48
50
|
const id = name + useId();
|
|
49
51
|
const listboxId = id + 'listbox';
|
|
@@ -122,7 +124,7 @@ export function Combobox({
|
|
|
122
124
|
};
|
|
123
125
|
|
|
124
126
|
return (
|
|
125
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
127
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
126
128
|
<Label id={id} label={label} />
|
|
127
129
|
<div ref={containerRef} className={css.inputWrapper}>
|
|
128
130
|
<input
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
|
|
1
|
+
import { HTMLAttributes, useId, useRef, useState, DragEvent, ChangeEvent } from 'react';
|
|
2
2
|
import css from './FileInput.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { InputContainer } from '../atoms/InputContainer';
|
|
5
5
|
import { cn } from '@boostdev/design-system-foundation';
|
|
6
6
|
import type { WithClassName } from '../../../../types';
|
|
7
7
|
|
|
8
|
-
interface FileInputProps extends WithClassName {
|
|
8
|
+
interface FileInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
9
9
|
label: string;
|
|
10
10
|
name: string;
|
|
11
11
|
accept?: string;
|
|
@@ -28,6 +28,7 @@ export function FileInput({
|
|
|
28
28
|
hint,
|
|
29
29
|
onChange,
|
|
30
30
|
className,
|
|
31
|
+
...rest
|
|
31
32
|
}: Readonly<FileInputProps>) {
|
|
32
33
|
const uid = name + useId();
|
|
33
34
|
const hintId = uid + 'hint';
|
|
@@ -73,7 +74,7 @@ export function FileInput({
|
|
|
73
74
|
const handleDragLeave = () => setIsDragging(false);
|
|
74
75
|
|
|
75
76
|
return (
|
|
76
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
77
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
77
78
|
<label
|
|
78
79
|
htmlFor={uid}
|
|
79
80
|
className={cn(css.dropZone, isDragging && css.isDragging, error && css.hasError, disabled && css.isDisabled)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useId, useRef, useState} from 'react';
|
|
1
|
+
import {HTMLAttributes, useId, useRef, useState} from 'react';
|
|
2
2
|
import css from './NumberInput.module.css';
|
|
3
3
|
import {Label} from '../atoms/Label';
|
|
4
4
|
import {Message} from '../atoms/Message';
|
|
@@ -6,7 +6,7 @@ import {InputContainer} from '../atoms/InputContainer';
|
|
|
6
6
|
import {cn} from '@boostdev/design-system-foundation';
|
|
7
7
|
import type { WithClassName } from '../../../../types';
|
|
8
8
|
|
|
9
|
-
interface NumberInputProps extends WithClassName {
|
|
9
|
+
interface NumberInputProps extends WithClassName, Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
10
10
|
label: string;
|
|
11
11
|
name: string;
|
|
12
12
|
value?: number;
|
|
@@ -33,6 +33,7 @@ export function NumberInput({
|
|
|
33
33
|
hint,
|
|
34
34
|
onChange,
|
|
35
35
|
className,
|
|
36
|
+
...rest
|
|
36
37
|
}: Readonly<NumberInputProps>) {
|
|
37
38
|
const uid = name + useId();
|
|
38
39
|
const hintId = uid + 'hint';
|
|
@@ -62,7 +63,7 @@ export function NumberInput({
|
|
|
62
63
|
};
|
|
63
64
|
|
|
64
65
|
return (
|
|
65
|
-
<InputContainer className={cn(css.formGroup, className)}>
|
|
66
|
+
<InputContainer {...rest} className={cn(css.formGroup, className)}>
|
|
66
67
|
<Label id={uid} label={label} />
|
|
67
68
|
<div className={css.inputRow}>
|
|
68
69
|
<button
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldsetHTMLAttributes, useId } from 'react';
|
|
2
2
|
import css from './RadioGroup.module.css';
|
|
3
3
|
import { Message } from '../atoms/Message';
|
|
4
4
|
import { cn } from '@boostdev/design-system-foundation';
|
|
5
5
|
import type { WithClassName } from '../../../../types';
|
|
6
6
|
|
|
7
|
-
export interface RadioGroupProps extends WithClassName {
|
|
7
|
+
export interface RadioGroupProps extends WithClassName, FieldsetHTMLAttributes<HTMLFieldSetElement> {
|
|
8
8
|
legend: string;
|
|
9
|
-
children: ReactNode;
|
|
10
9
|
error?: string;
|
|
11
10
|
hint?: string;
|
|
12
11
|
required?: boolean;
|
|
13
|
-
disabled?: boolean;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function RadioGroup({
|
|
@@ -19,8 +17,8 @@ export function RadioGroup({
|
|
|
19
17
|
error,
|
|
20
18
|
hint,
|
|
21
19
|
required,
|
|
22
|
-
disabled,
|
|
23
20
|
className,
|
|
21
|
+
...rest
|
|
24
22
|
}: Readonly<RadioGroupProps>) {
|
|
25
23
|
const id = useId();
|
|
26
24
|
const hintId = id + 'hint';
|
|
@@ -29,8 +27,8 @@ export function RadioGroup({
|
|
|
29
27
|
|
|
30
28
|
return (
|
|
31
29
|
<fieldset
|
|
30
|
+
{...rest}
|
|
32
31
|
className={cn(css.group, className)}
|
|
33
|
-
disabled={disabled}
|
|
34
32
|
aria-required={required || undefined}
|
|
35
33
|
aria-describedby={describedBy}
|
|
36
34
|
>
|
|
@@ -87,15 +87,19 @@ When `selectedIndex` is omitted, the component auto-detects the active item from
|
|
|
87
87
|
|
|
88
88
|
| Variant | Description |
|
|
89
89
|
|---------|-------------|
|
|
90
|
-
| `outline` (default) |
|
|
91
|
-
| `filled` |
|
|
90
|
+
| `outline` (default) | Subtle track background; active item has a `bg` background with inset-border indicator; inactive items are transparent |
|
|
91
|
+
| `filled` | Subtle track background; coloured filled thumb slides behind the active item |
|
|
92
92
|
|
|
93
93
|
## CSS variables
|
|
94
94
|
|
|
95
95
|
| Variable | Default | Description |
|
|
96
96
|
|----------|---------|-------------|
|
|
97
|
-
| `--control_track-bg` | `var(--bds-color_bg--subtle)`
|
|
98
|
-
| `--
|
|
97
|
+
| `--control_track-bg` | `var(--bds-color_bg--subtle)` | Track background colour (both variants) |
|
|
98
|
+
| `--control_item-bg` | `transparent` | Inactive item background (outline variant) |
|
|
99
|
+
| `--control_item-bg--active` | `var(--bds-color_bg)` | Active item background (outline variant) |
|
|
100
|
+
| `--control_radius` | `var(--bds-border_radius--m)` | Border radius of the outer track |
|
|
101
|
+
| `--control_thumb-bg` | `var(--bds-color_interactive)` | Filled thumb colour (filled variant) |
|
|
102
|
+
| `--control_thumb-radius` | `var(--bds-border_radius--s)` | Border radius of the thumb, indicator, and item focus ring |
|
|
99
103
|
| `--control_label-color` | `var(--bds-color_on-bg--subtle)` | Inactive item text colour |
|
|
100
104
|
| `--control_label-color--active` | `var(--bds-color_on-interactive)` (filled) / `var(--bds-color_interactive)` (outline) | Active item text colour |
|
|
101
105
|
| `--control_label-color--hover` | `var(--bds-color_on-bg)` | Hovered inactive item text colour |
|