@a-type/ui 6.1.16 → 6.1.19
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/dist/components/button/ConfirmedButton.js +2 -2
- package/dist/components/button/ConfirmedButton.js.map +1 -1
- package/dist/components/datePicker/DatePicker.d.ts +1 -1
- package/dist/components/datePicker/DatePicker.stories.d.ts +1 -1
- package/dist/components/datePicker/DateRangePicker.d.ts +1 -1
- package/dist/components/datePicker/DateRangePicker.js +15 -1
- package/dist/components/datePicker/DateRangePicker.js.map +1 -1
- package/dist/components/dialog/Dialog.d.ts +20 -34
- package/dist/components/dialog/Dialog.js +47 -208
- package/dist/components/dialog/Dialog.js.map +1 -1
- package/dist/components/dialog/Dialog.module.css +10 -82
- package/dist/components/dialog/Dialog.stories.js +8 -7
- package/dist/components/dialog/Dialog.stories.js.map +1 -1
- package/dist/components/drawer/Drawer.d.ts +24 -0
- package/dist/components/drawer/Drawer.js +101 -0
- package/dist/components/drawer/Drawer.js.map +1 -0
- package/dist/components/drawer/Drawer.module.css +236 -0
- package/dist/components/drawer/Drawer.stories.d.ts +7 -0
- package/dist/components/drawer/Drawer.stories.js +28 -0
- package/dist/components/drawer/Drawer.stories.js.map +1 -0
- package/dist/components/drawer/index.d.ts +1 -0
- package/dist/components/drawer/index.js +2 -0
- package/dist/components/drawer/index.js.map +1 -0
- package/dist/components/globalErrorFallback/GlobalErrorFallback.d.ts +1 -1
- package/dist/components/globalErrorFallback/GlobalErrorFallback.js +2 -1
- package/dist/components/globalErrorFallback/GlobalErrorFallback.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/scrollArea/ScrollArea.module.css +1 -0
- package/dist/hooks/withContextVariant.d.ts +8 -0
- package/dist/hooks/withContextVariant.js +21 -0
- package/dist/hooks/withContextVariant.js.map +1 -0
- package/package.json +3 -2
- package/src/components/button/ConfirmedButton.tsx +9 -16
- package/src/components/datePicker/DateRangePicker.tsx +18 -1
- package/src/components/dialog/Dialog.module.css +10 -82
- package/src/components/dialog/Dialog.stories.tsx +45 -51
- package/src/components/dialog/Dialog.tsx +98 -332
- package/src/components/drawer/Drawer.module.css +235 -0
- package/src/components/drawer/Drawer.stories.tsx +109 -0
- package/src/components/drawer/Drawer.tsx +189 -0
- package/src/components/drawer/index.ts +1 -0
- package/src/components/globalErrorFallback/GlobalErrorFallback.tsx +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/scrollArea/ScrollArea.module.css +1 -0
- package/src/hooks/withContextVariant.tsx +24 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
.overlay {
|
|
2
|
+
--bleed: 3rem;
|
|
3
|
+
--swipe-opacity: 0.2;
|
|
4
|
+
|
|
5
|
+
position: fixed;
|
|
6
|
+
min-height: 100dvh;
|
|
7
|
+
inset: 0;
|
|
8
|
+
|
|
9
|
+
@apply --mx-bg(var(--m-gray-ink));
|
|
10
|
+
opacity: calc(
|
|
11
|
+
0.2 + var(--swipe-opacity) * (1 - var(--drawer-swipe-progress, 0))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
border-top: var(--m-lw) solid var(--m-gray);
|
|
15
|
+
|
|
16
|
+
transition: all var(--m-dur-long) var(--m-ease);
|
|
17
|
+
|
|
18
|
+
backdrop-filter: blur(2px);
|
|
19
|
+
|
|
20
|
+
&[data-starting-style],
|
|
21
|
+
&[data-ending-style] {
|
|
22
|
+
opacity: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&[data-swiping] {
|
|
26
|
+
transition-duration: 0ms;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&[data-ending-style] {
|
|
30
|
+
transition-duration: calc(var(--drawer-swipe-strength) * var(--m-dur-long));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.viewport {
|
|
35
|
+
--bleed: 3rem;
|
|
36
|
+
|
|
37
|
+
position: fixed;
|
|
38
|
+
inset: 0;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: flex-end;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
touch-action: none;
|
|
43
|
+
|
|
44
|
+
&::after {
|
|
45
|
+
content: '';
|
|
46
|
+
position: fixed;
|
|
47
|
+
inset-inline: 0;
|
|
48
|
+
bottom: 0;
|
|
49
|
+
height: var(--bleed);
|
|
50
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
51
|
+
pointer-events: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&[data-closed]::after {
|
|
55
|
+
opacity: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* While swiping, the popup's own bleed covers the bottom edge, so hide the
|
|
59
|
+
fixed fill — otherwise it paints white over the backdrop once the popup is
|
|
60
|
+
dragged far enough down. */
|
|
61
|
+
&:has([data-swiping])::after {
|
|
62
|
+
opacity: 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.popup {
|
|
67
|
+
--bleed: 3rem;
|
|
68
|
+
--top-gap: 1rem;
|
|
69
|
+
|
|
70
|
+
box-sizing: border-box;
|
|
71
|
+
position: relative;
|
|
72
|
+
z-index: 1;
|
|
73
|
+
max-width: unset;
|
|
74
|
+
width: 100%;
|
|
75
|
+
max-height: calc(100% - var(--top-gap) + var(--bleed));
|
|
76
|
+
height: calc(100% - var(--top-gap) + var(--bleed));
|
|
77
|
+
margin-bottom: calc(-1 * var(--bleed));
|
|
78
|
+
padding-bottom: max(
|
|
79
|
+
0px,
|
|
80
|
+
calc(var(--drawer-snap-point-offset) + var(--drawer-swipe-movement-y))
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
align-items: stretch;
|
|
86
|
+
|
|
87
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
88
|
+
@apply --mx-borderColor(var(--m-gray-heavy));
|
|
89
|
+
|
|
90
|
+
touch-action: none;
|
|
91
|
+
|
|
92
|
+
overflow: visible;
|
|
93
|
+
|
|
94
|
+
transition:
|
|
95
|
+
transform var(--m-dur-long) var(--m-ease),
|
|
96
|
+
box-shadow var(--m-dur-long) var(--m-ease),
|
|
97
|
+
padding-bottom var(--m-dur-long) var(--m-ease);
|
|
98
|
+
|
|
99
|
+
--shadow-dir: -1;
|
|
100
|
+
@apply --mx-shadow;
|
|
101
|
+
--mx-shadow-value: 0 calc(var(--shadow-dir, 1) * var(--m-shadow-lg-y))
|
|
102
|
+
var(--m-shadow-lg-blur) var(--m-shadow-lg-spread) var(--m-shadow-lg-color);
|
|
103
|
+
|
|
104
|
+
border-radius: var(--m-surface-rd) var(--m-surface-rd) 0 0;
|
|
105
|
+
border-bottom-width: 0;
|
|
106
|
+
|
|
107
|
+
transform: translateY(
|
|
108
|
+
calc(
|
|
109
|
+
var(--drawer-swipe-movement-y) + var(--drawer-snap-point-offset) -
|
|
110
|
+
var(--bleed)
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
will-change: transform;
|
|
114
|
+
|
|
115
|
+
/* Disable transitions while swiping so movement is immediate */
|
|
116
|
+
&[data-swiping] {
|
|
117
|
+
transition: none;
|
|
118
|
+
user-select: none;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&[data-starting-style],
|
|
122
|
+
&[data-ending-style] {
|
|
123
|
+
transform: translateY(calc(100% - var(--bleed)));
|
|
124
|
+
padding-bottom: 0;
|
|
125
|
+
box-shadow: 0 0 0 transparent;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&[data-ending-style] {
|
|
129
|
+
transition-duration: calc(var(--drawer-swipe-strength) * var(--m-dur-long));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&::after {
|
|
133
|
+
content: '';
|
|
134
|
+
position: absolute;
|
|
135
|
+
inset-inline: 0;
|
|
136
|
+
top: 100%;
|
|
137
|
+
height: var(--bleed);
|
|
138
|
+
background-color: inherit;
|
|
139
|
+
pointer-events: none;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.contentScrollArea {
|
|
144
|
+
flex: 1 1 auto;
|
|
145
|
+
min-height: 0;
|
|
146
|
+
overscroll-behavior: contain;
|
|
147
|
+
touch-action: auto;
|
|
148
|
+
overflow-y: auto;
|
|
149
|
+
padding: var(--m-surface-p);
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
gap: var(--m-sp-md);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.swipeHandle {
|
|
156
|
+
position: relative;
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
width: 100%;
|
|
161
|
+
cursor: grab;
|
|
162
|
+
touch-action: none;
|
|
163
|
+
padding-block: var(--m-sp-lg);
|
|
164
|
+
user-select: none;
|
|
165
|
+
flex-shrink: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.swipeHandleBar {
|
|
169
|
+
height: --fn-literal(4px);
|
|
170
|
+
width: 20%;
|
|
171
|
+
@apply --mx-bg(var(--m-gray));
|
|
172
|
+
border-radius: var(--m-rd-lg);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.close {
|
|
176
|
+
position: absolute;
|
|
177
|
+
right: var(--m-sp-sm);
|
|
178
|
+
top: var(--m-sp-sm);
|
|
179
|
+
z-index: 101;
|
|
180
|
+
display: flex;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.title {
|
|
184
|
+
@apply --mx-fg(var(--m-gray-ink));
|
|
185
|
+
font-size: var(--m-prose-primary-size);
|
|
186
|
+
font-weight: var(--m-prose-primary-weight);
|
|
187
|
+
line-height: var(--m-prose-primary-lineHeight);
|
|
188
|
+
margin-block-end: var(--m-sp-xs);
|
|
189
|
+
margin-block-start: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.description {
|
|
193
|
+
@apply --mx-fg(var(--m-gray-heavy));
|
|
194
|
+
font-size: var(--m-prose-ambient-size);
|
|
195
|
+
line-height: var(--m-prose-ambient-lineHeight);
|
|
196
|
+
margin: 0;
|
|
197
|
+
margin-block-end: var(--m-sp-xs);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.actions {
|
|
201
|
+
position: sticky;
|
|
202
|
+
bottom: 0;
|
|
203
|
+
z-index: 100;
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: row;
|
|
206
|
+
flex-wrap: wrap;
|
|
207
|
+
align-items: center;
|
|
208
|
+
justify-content: end;
|
|
209
|
+
align-self: end;
|
|
210
|
+
@apply --mx-shadow;
|
|
211
|
+
--mx-shadow-value: 0 calc(-1 * var(--m-shadow-md-y)) 16px
|
|
212
|
+
var(--m-shadow-md-spread) var(--m-gray-paper);
|
|
213
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
214
|
+
@apply --mx-bg-faded(50%);
|
|
215
|
+
margin-block-start: var(--m-sp-md);
|
|
216
|
+
gap: var(--m-sp-sm);
|
|
217
|
+
border-radius: var(--m-surface-rd);
|
|
218
|
+
transition: bottom var(--m-dur) var(--m-ease);
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
Hide the actions when the keyboard is open.
|
|
222
|
+
Done by moving them downward behind it.
|
|
223
|
+
This both fixes a bug with the positioning, and
|
|
224
|
+
gives the user more room to see.
|
|
225
|
+
*/
|
|
226
|
+
transform: translateY(calc(2 * var(--drawer-keyboard-inset, 0px)));
|
|
227
|
+
/*
|
|
228
|
+
Same but with modern CSS if()
|
|
229
|
+
*/
|
|
230
|
+
@supports (
|
|
231
|
+
display: if(style(--drawer-keyboard-inset > 0px): none; else: flex;)
|
|
232
|
+
) {
|
|
233
|
+
display: if(style(--drawer-keyboard-inset > 0px): none; else: flex;);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Button } from '../button/index.js';
|
|
3
|
+
import { Input } from '../input/Input.js';
|
|
4
|
+
import { H1, P } from '../typography/index.js';
|
|
5
|
+
import { Drawer } from './Drawer.js';
|
|
6
|
+
|
|
7
|
+
const meta: any = {
|
|
8
|
+
title: 'Components/Drawer',
|
|
9
|
+
component: Drawer,
|
|
10
|
+
argTypes: {},
|
|
11
|
+
parameters: {
|
|
12
|
+
controls: { expanded: true },
|
|
13
|
+
},
|
|
14
|
+
} satisfies Meta<typeof Drawer>;
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof Drawer>;
|
|
19
|
+
|
|
20
|
+
function DummyContent() {
|
|
21
|
+
return (
|
|
22
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
23
|
+
<H1>Some content</H1>
|
|
24
|
+
<P>
|
|
25
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
26
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
27
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
28
|
+
</P>
|
|
29
|
+
<Input placeholder="Type something..." />
|
|
30
|
+
<P>
|
|
31
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
32
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
33
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
34
|
+
</P>
|
|
35
|
+
<Input placeholder="Type something..." />
|
|
36
|
+
<P>
|
|
37
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
38
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
39
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi. Lorem
|
|
40
|
+
ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at porttitor
|
|
41
|
+
sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula accumsan,
|
|
42
|
+
sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
43
|
+
</P>
|
|
44
|
+
<Input placeholder="Type something..." />
|
|
45
|
+
<P>
|
|
46
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
47
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
48
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi. Lorem
|
|
49
|
+
ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at porttitor
|
|
50
|
+
sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula accumsan,
|
|
51
|
+
sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
52
|
+
</P>
|
|
53
|
+
<Input placeholder="Type something..." />
|
|
54
|
+
<P>
|
|
55
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
56
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
57
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi. Lorem
|
|
58
|
+
ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at porttitor
|
|
59
|
+
sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula accumsan,
|
|
60
|
+
sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
61
|
+
</P>
|
|
62
|
+
<Input placeholder="Type something..." />
|
|
63
|
+
<P>
|
|
64
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at
|
|
65
|
+
porttitor sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula
|
|
66
|
+
accumsan, sit amet ullamcorper nunc ultricies. Nulla facilisi. Lorem
|
|
67
|
+
ipsum dolor sit amet, consectetur adipiscing elit. Aliquam at porttitor
|
|
68
|
+
sem. Aliquam erat volutpat. Donec fermentum tortor eget ligula accumsan,
|
|
69
|
+
sit amet ullamcorper nunc ultricies. Nulla facilisi.
|
|
70
|
+
</P>
|
|
71
|
+
<Input placeholder="Type something..." />
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const Default: Story = {
|
|
77
|
+
args: {
|
|
78
|
+
children: (
|
|
79
|
+
<>
|
|
80
|
+
<Drawer.Trigger render={<Button />}>Open</Drawer.Trigger>
|
|
81
|
+
<Drawer.Content>
|
|
82
|
+
<Drawer.Title>Hello world</Drawer.Title>
|
|
83
|
+
<DummyContent />
|
|
84
|
+
<Drawer.Actions>
|
|
85
|
+
<Drawer.Close />
|
|
86
|
+
<Button emphasis="primary">Accept</Button>
|
|
87
|
+
</Drawer.Actions>
|
|
88
|
+
</Drawer.Content>
|
|
89
|
+
</>
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const NoDefaultClose: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
children: (
|
|
97
|
+
<>
|
|
98
|
+
<Drawer.Trigger render={<Button />}>Open</Drawer.Trigger>
|
|
99
|
+
<Drawer.Content disableDefaultClose>
|
|
100
|
+
<Drawer.Title>No default close</Drawer.Title>
|
|
101
|
+
<DummyContent />
|
|
102
|
+
<Drawer.Actions>
|
|
103
|
+
<Drawer.Close />
|
|
104
|
+
</Drawer.Actions>
|
|
105
|
+
</Drawer.Content>
|
|
106
|
+
</>
|
|
107
|
+
),
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Drawer as BaseDrawer,
|
|
3
|
+
DrawerCloseProps,
|
|
4
|
+
DrawerPopupProps,
|
|
5
|
+
DrawerRootProps,
|
|
6
|
+
DrawerTriggerProps,
|
|
7
|
+
} from '@base-ui/react/drawer';
|
|
8
|
+
import clsx from 'clsx';
|
|
9
|
+
import { ComponentPropsWithoutRef, Ref, useCallback, useRef } from 'react';
|
|
10
|
+
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
11
|
+
import { withClassName } from '../../hooks/withClassName.js';
|
|
12
|
+
import { Box } from '../box/Box.js';
|
|
13
|
+
import { Button } from '../button/index.js';
|
|
14
|
+
import { Icon } from '../icon/Icon.js';
|
|
15
|
+
import { useParticles } from '../particles/ParticleContext.js';
|
|
16
|
+
import cls from './Drawer.module.css';
|
|
17
|
+
|
|
18
|
+
const DrawerBackdrop = withClassName(BaseDrawer.Backdrop, cls.overlay);
|
|
19
|
+
|
|
20
|
+
const StyledPopup = withClassName(BaseDrawer.Popup, cls.popup);
|
|
21
|
+
|
|
22
|
+
export interface DrawerContentProps extends DrawerPopupProps {
|
|
23
|
+
disableDefaultClose?: boolean;
|
|
24
|
+
ref?: Ref<HTMLDivElement>;
|
|
25
|
+
innerClassName?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DrawerContent = function DrawerContent({
|
|
29
|
+
ref,
|
|
30
|
+
children,
|
|
31
|
+
className,
|
|
32
|
+
disableDefaultClose,
|
|
33
|
+
innerClassName,
|
|
34
|
+
...props
|
|
35
|
+
}: DrawerContentProps) {
|
|
36
|
+
const particles = useParticles();
|
|
37
|
+
const wasOpenRef = useRef(false);
|
|
38
|
+
|
|
39
|
+
const openRef = useCallback(
|
|
40
|
+
(element: HTMLDivElement | null) => {
|
|
41
|
+
if (!wasOpenRef.current && element?.hasAttribute('data-open')) {
|
|
42
|
+
wasOpenRef.current = true;
|
|
43
|
+
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
particles?.addParticles(
|
|
46
|
+
particles.elementExplosion({
|
|
47
|
+
count: 20,
|
|
48
|
+
margin: 40,
|
|
49
|
+
borders: ['top'],
|
|
50
|
+
color: [
|
|
51
|
+
{
|
|
52
|
+
space: 'rgb',
|
|
53
|
+
values: [0, 0, 0],
|
|
54
|
+
opacity: 0.02,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
space: 'rgb',
|
|
58
|
+
values: [0, 0, 0],
|
|
59
|
+
opacity: 0,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
element,
|
|
63
|
+
startRadius: 15,
|
|
64
|
+
endRadius: 0,
|
|
65
|
+
lifespan: 1000,
|
|
66
|
+
force: 0.5,
|
|
67
|
+
drag: 0.01,
|
|
68
|
+
forceFuzz: 0.5,
|
|
69
|
+
angleFuzz: 0.1,
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
}, 180);
|
|
73
|
+
} else if (!element?.hasAttribute('data-open')) {
|
|
74
|
+
wasOpenRef.current = false;
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
[particles],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const finalRef = useMergedRef(ref, openRef);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<BaseDrawer.VirtualKeyboardProvider>
|
|
84
|
+
<BaseDrawer.Portal>
|
|
85
|
+
<DrawerBackdrop data-role="backdrop" />
|
|
86
|
+
<BaseDrawer.Viewport className={cls.viewport}>
|
|
87
|
+
<StyledPopup ref={finalRef} {...props} className={className}>
|
|
88
|
+
{!disableDefaultClose && <DrawerDefaultClose />}
|
|
89
|
+
<DrawerSwipeHandle />
|
|
90
|
+
<div className={clsx(cls.contentScrollArea, innerClassName)}>
|
|
91
|
+
{children}
|
|
92
|
+
</div>
|
|
93
|
+
</StyledPopup>
|
|
94
|
+
</BaseDrawer.Viewport>
|
|
95
|
+
</BaseDrawer.Portal>
|
|
96
|
+
</BaseDrawer.VirtualKeyboardProvider>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function DrawerSwipeHandle({
|
|
101
|
+
ref,
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: ComponentPropsWithoutRef<'div'> & {
|
|
105
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
106
|
+
}) {
|
|
107
|
+
return (
|
|
108
|
+
<div ref={ref} {...props} className={clsx(cls.swipeHandle, className)}>
|
|
109
|
+
<div className={cls.swipeHandleBar} />
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function DrawerDefaultClose({
|
|
115
|
+
ref,
|
|
116
|
+
className,
|
|
117
|
+
...props
|
|
118
|
+
}: DrawerCloseProps & {
|
|
119
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
120
|
+
}) {
|
|
121
|
+
return (
|
|
122
|
+
<DrawerClose
|
|
123
|
+
className={clsx(cls.close, className)}
|
|
124
|
+
aria-label="Close drawer"
|
|
125
|
+
ref={ref}
|
|
126
|
+
{...props}
|
|
127
|
+
render={<Button emphasis="ghost" size="small" />}
|
|
128
|
+
>
|
|
129
|
+
<Icon name="x" />
|
|
130
|
+
</DrawerClose>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const DrawerTitle = withClassName(BaseDrawer.Title, cls.title);
|
|
135
|
+
const DrawerDescription = withClassName(
|
|
136
|
+
BaseDrawer.Description,
|
|
137
|
+
cls.description,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// TODO: support swipeDirection
|
|
141
|
+
export interface DrawerProps extends Omit<DrawerRootProps, 'swipeDirection'> {}
|
|
142
|
+
const defaultSnapPoints = [0.75, 1];
|
|
143
|
+
const DrawerRoot = ({
|
|
144
|
+
children,
|
|
145
|
+
snapPoints = defaultSnapPoints,
|
|
146
|
+
...props
|
|
147
|
+
}: DrawerProps) => {
|
|
148
|
+
return (
|
|
149
|
+
<BaseDrawer.Root swipeDirection="down" snapPoints={snapPoints} {...props}>
|
|
150
|
+
{children}
|
|
151
|
+
</BaseDrawer.Root>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const DrawerTrigger = ({ render, ...props }: DrawerTriggerProps) => (
|
|
156
|
+
<BaseDrawer.Trigger render={render || <Button />} {...props} />
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
function DrawerClose({
|
|
160
|
+
children,
|
|
161
|
+
render,
|
|
162
|
+
...props
|
|
163
|
+
}: DrawerCloseProps & {
|
|
164
|
+
ref?: React.Ref<HTMLButtonElement>;
|
|
165
|
+
}) {
|
|
166
|
+
return (
|
|
167
|
+
<BaseDrawer.Close
|
|
168
|
+
render={render ?? <Button emphasis="default" />}
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
{children ?? 'Close'}
|
|
172
|
+
</BaseDrawer.Close>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const DrawerActions = withClassName(Box, cls.actions);
|
|
177
|
+
|
|
178
|
+
export const Drawer = Object.assign(DrawerRoot, {
|
|
179
|
+
Trigger: DrawerTrigger,
|
|
180
|
+
Content: DrawerContent,
|
|
181
|
+
Title: DrawerTitle,
|
|
182
|
+
Description: DrawerDescription,
|
|
183
|
+
Close: DrawerClose,
|
|
184
|
+
Actions: DrawerActions,
|
|
185
|
+
Unstyled: BaseDrawer as DrawerParts,
|
|
186
|
+
createHandle: BaseDrawer.createHandle,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export type DrawerParts = typeof BaseDrawer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Drawer.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Box, BoxProps, Heading, P } from '@a-type/ui';
|
|
2
1
|
import { useLocalStorage } from '../../hooks.js';
|
|
2
|
+
import { Box, type BoxProps } from '../box/Box.js';
|
|
3
|
+
import { Heading, P } from '../typography/typography.js';
|
|
3
4
|
|
|
4
5
|
export function GlobalErrorFallback({ children, ...rest }: BoxProps) {
|
|
5
6
|
const hadRecentError = useHadRecentError();
|
package/src/components/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from './combobox/Combobox.js';
|
|
|
14
14
|
export * from './contextMenu/contextMenu.js';
|
|
15
15
|
export * from './datePicker/index.js';
|
|
16
16
|
export * from './dialog/index.js';
|
|
17
|
+
export * from './drawer/index.js';
|
|
17
18
|
export * from './divider/index.js';
|
|
18
19
|
export * from './dropdownMenu/index.js';
|
|
19
20
|
export * from './editableText/EditableText.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a context and HOC which swaps between two components
|
|
5
|
+
* based on a contextual value
|
|
6
|
+
*/
|
|
7
|
+
export function createWithContextVariant() {
|
|
8
|
+
const Context = createContext(false);
|
|
9
|
+
function withContextVariant<P extends object>(
|
|
10
|
+
TrueComponent: React.ComponentType<P>,
|
|
11
|
+
FalseComponent: React.ComponentType<P>,
|
|
12
|
+
) {
|
|
13
|
+
return function WithContextVariant(props: P) {
|
|
14
|
+
const contextValue = useContext(Context);
|
|
15
|
+
const Component = contextValue ? TrueComponent : FalseComponent;
|
|
16
|
+
return <Component {...props} />;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
Context,
|
|
22
|
+
withContextVariant,
|
|
23
|
+
};
|
|
24
|
+
}
|