@a-type/ui 6.1.15 → 6.1.18
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/Calendar.module.css +5 -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 +13 -3
- 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 +25 -0
- package/dist/components/drawer/Drawer.js +102 -0
- package/dist/components/drawer/Drawer.js.map +1 -0
- package/dist/components/drawer/Drawer.module.css +182 -0
- package/dist/components/drawer/Drawer.stories.d.ts +7 -0
- package/dist/components/drawer/Drawer.stories.js +27 -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/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- 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 +2 -2
- package/src/components/button/ConfirmedButton.tsx +9 -16
- package/src/components/datePicker/Calendar.module.css +5 -1
- package/src/components/datePicker/DateRangePicker.tsx +16 -3
- 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 +181 -0
- package/src/components/drawer/Drawer.stories.tsx +68 -0
- package/src/components/drawer/Drawer.tsx +204 -0
- package/src/components/drawer/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/hooks/withContextVariant.tsx +24 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UseRenderRenderProp } from '@base-ui/react';
|
|
1
2
|
import {
|
|
2
3
|
Dialog as BaseDialog,
|
|
3
4
|
DialogCloseProps,
|
|
@@ -5,187 +6,70 @@ import {
|
|
|
5
6
|
DialogRootProps,
|
|
6
7
|
DialogTriggerProps,
|
|
7
8
|
} from '@base-ui/react/dialog';
|
|
9
|
+
import { DrawerRootProps } from '@base-ui/react/drawer';
|
|
8
10
|
import { Radio as BaseRadio } from '@base-ui/react/radio';
|
|
9
11
|
import { RadioGroup as BaseRadioGroup } from '@base-ui/react/radio-group';
|
|
10
|
-
import { useDrag } from '@use-gesture/react';
|
|
11
12
|
import clsx from 'clsx';
|
|
12
|
-
import {
|
|
13
|
-
ComponentPropsWithoutRef,
|
|
14
|
-
createContext,
|
|
15
|
-
Ref,
|
|
16
|
-
TouchEvent,
|
|
17
|
-
useCallback,
|
|
18
|
-
useContext,
|
|
19
|
-
useRef,
|
|
20
|
-
useState,
|
|
21
|
-
} from 'react';
|
|
13
|
+
import { ComponentPropsWithoutRef, Ref, useCallback, useState } from 'react';
|
|
22
14
|
import { useMediaQuery } from '../../hooks/useMediaQuery.js';
|
|
23
|
-
import useMergedRef from '../../hooks/useMergedRef.js';
|
|
24
15
|
import { withClassName } from '../../hooks/withClassName.js';
|
|
16
|
+
import { createWithContextVariant } from '../../hooks/withContextVariant.js';
|
|
25
17
|
import { Box } from '../box/Box.js';
|
|
26
18
|
import { Button } from '../button/index.js';
|
|
19
|
+
import { Drawer } from '../drawer/Drawer.js';
|
|
20
|
+
import drawerCls from '../drawer/Drawer.module.css';
|
|
27
21
|
import { Icon } from '../icon/Icon.js';
|
|
28
|
-
import { useParticles } from '../particles/index.js';
|
|
29
22
|
import menuCls from '../primitives/menus.module.css';
|
|
30
|
-
import { useConfig } from '../provider/Provider.js';
|
|
31
23
|
import { ScrollArea } from '../scrollArea/ScrollArea.js';
|
|
32
24
|
import { selectTriggerClassName } from '../select/index.js';
|
|
33
25
|
import cls from './Dialog.module.css';
|
|
34
26
|
|
|
35
|
-
const
|
|
27
|
+
const { Context: MobileDrawerContext, withContextVariant } =
|
|
28
|
+
createWithContextVariant();
|
|
36
29
|
|
|
30
|
+
// baseline Dialog-specific component implementations
|
|
31
|
+
const StyledOverlay = withClassName(BaseDialog.Backdrop, cls.overlay);
|
|
37
32
|
const StyledContent = withClassName(BaseDialog.Popup, cls.content);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
swipeY: number,
|
|
41
|
-
dy: number,
|
|
42
|
-
last: boolean,
|
|
43
|
-
close: () => void,
|
|
44
|
-
content: HTMLElement,
|
|
45
|
-
) {
|
|
46
|
-
const contentHeight = content.clientHeight;
|
|
47
|
-
|
|
48
|
-
const shouldClose = last && (swipeY === 1 || dy > contentHeight / 2);
|
|
49
|
-
if (shouldClose) {
|
|
50
|
-
close();
|
|
51
|
-
}
|
|
52
|
-
const gestureY = last ? (shouldClose ? -1000 : 0) : -Math.max(0, dy);
|
|
53
|
-
content.style.setProperty('--gesture-y', `${gestureY}px`);
|
|
54
|
-
content.style.setProperty('transition', last ? 'bottom 0.2s' : '');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// filter out gestures that are within scrollable descendants
|
|
58
|
-
// if those elements are not scrolled to the top already
|
|
59
|
-
function filterScrollables(
|
|
60
|
-
target: HTMLElement,
|
|
61
|
-
root: HTMLElement,
|
|
62
|
-
dy: number,
|
|
63
|
-
vy: number,
|
|
64
|
-
) {
|
|
65
|
-
// always allow upward swipes
|
|
66
|
-
if (vy < 0) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let cur: HTMLElement | null = target;
|
|
71
|
-
while (cur) {
|
|
72
|
-
if (cur === root) return false;
|
|
73
|
-
cur = cur.parentElement;
|
|
74
|
-
if (
|
|
75
|
-
cur &&
|
|
76
|
-
cur.scrollHeight > cur.clientHeight &&
|
|
77
|
-
cur.scrollTop !== 0 &&
|
|
78
|
-
vy >= 0
|
|
79
|
-
)
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface DialogContentProps extends DialogPopupProps {
|
|
33
|
+
export interface DialogContentProps
|
|
34
|
+
extends Omit<DialogPopupProps, 'className' | 'style' | 'render'> {
|
|
86
35
|
width?: 'sm' | 'md' | 'lg';
|
|
87
|
-
disableSheet?: boolean;
|
|
88
36
|
disableDefaultClose?: boolean;
|
|
89
37
|
/** @deprecated */
|
|
90
38
|
outerClassName?: string;
|
|
91
39
|
ref?: Ref<HTMLDivElement>;
|
|
92
40
|
innerClassName?: string;
|
|
41
|
+
className?: string;
|
|
42
|
+
style?: React.CSSProperties;
|
|
43
|
+
// minimal state overlap between drawer and dialog primitives
|
|
44
|
+
render?: UseRenderRenderProp<{ open: boolean }>;
|
|
93
45
|
}
|
|
94
|
-
|
|
95
|
-
const SWIPE_VELOCITY_THRESHOLD = 1.5;
|
|
96
|
-
|
|
97
|
-
export const Content = function Content({
|
|
46
|
+
const FunctionalContent = function Content({
|
|
98
47
|
ref,
|
|
99
48
|
children,
|
|
100
49
|
width,
|
|
101
50
|
outerClassName,
|
|
102
51
|
className,
|
|
103
|
-
disableSheet,
|
|
104
52
|
disableDefaultClose,
|
|
105
53
|
innerClassName,
|
|
106
54
|
...props
|
|
107
55
|
}: DialogContentProps) {
|
|
108
|
-
const particles = useParticles();
|
|
109
|
-
const wasOpenRef = useRef(false);
|
|
110
|
-
const openRef = useCallback(
|
|
111
|
-
(element: HTMLDivElement | null) => {
|
|
112
|
-
if (!wasOpenRef.current && element?.hasAttribute('data-open')) {
|
|
113
|
-
wasOpenRef.current = true;
|
|
114
|
-
|
|
115
|
-
const matchesSmall =
|
|
116
|
-
!disableSheet &&
|
|
117
|
-
typeof window !== 'undefined' &&
|
|
118
|
-
!window.matchMedia('(min-width:600px)').matches;
|
|
119
|
-
if (!matchesSmall) return;
|
|
120
|
-
|
|
121
|
-
setTimeout(() => {
|
|
122
|
-
particles?.addParticles(
|
|
123
|
-
particles.elementExplosion({
|
|
124
|
-
count: 20,
|
|
125
|
-
margin: 40,
|
|
126
|
-
borders: ['top'],
|
|
127
|
-
color: [
|
|
128
|
-
{
|
|
129
|
-
space: 'rgb',
|
|
130
|
-
values: [0, 0, 0],
|
|
131
|
-
opacity: 0.02,
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
space: 'rgb',
|
|
135
|
-
values: [0, 0, 0],
|
|
136
|
-
opacity: 0,
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
element,
|
|
140
|
-
startRadius: 15,
|
|
141
|
-
endRadius: 0,
|
|
142
|
-
lifespan: 1000,
|
|
143
|
-
force: 0.5,
|
|
144
|
-
drag: 0.01,
|
|
145
|
-
forceFuzz: 0.5,
|
|
146
|
-
angleFuzz: 0.1,
|
|
147
|
-
}),
|
|
148
|
-
);
|
|
149
|
-
}, 180);
|
|
150
|
-
} else if (!element?.hasAttribute('data-open')) {
|
|
151
|
-
wasOpenRef.current = false;
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
[particles, disableSheet],
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const { gestureRef, onTouchStart, onTouchMove, onTouchEnd } =
|
|
158
|
-
useDialogInherentSwipe({ disableSheet });
|
|
159
|
-
const finalRef = useMergedRef(ref, openRef, gestureRef);
|
|
160
|
-
|
|
161
|
-
const { virtualKeyboardBehavior } = useConfig();
|
|
162
|
-
|
|
163
56
|
return (
|
|
164
57
|
<BaseDialog.Portal>
|
|
165
58
|
<StyledOverlay />
|
|
166
59
|
<StyledContent
|
|
167
|
-
|
|
168
|
-
ref={finalRef}
|
|
60
|
+
ref={ref}
|
|
169
61
|
{...props}
|
|
170
|
-
onTouchStart={onTouchStart}
|
|
171
|
-
onTouchMove={onTouchMove}
|
|
172
|
-
onTouchEnd={onTouchEnd}
|
|
173
|
-
onTouchCancel={onTouchEnd}
|
|
174
|
-
data-disable-sheet={disableSheet ? 'true' : undefined}
|
|
175
|
-
data-keyboard-behavior={virtualKeyboardBehavior}
|
|
176
62
|
data-width={width}
|
|
177
63
|
className={clsx(outerClassName || className)}
|
|
178
64
|
>
|
|
179
|
-
{!disableDefaultClose &&
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
65
|
+
{!disableDefaultClose && <DialogDefaultClose />}
|
|
66
|
+
<ScrollArea
|
|
67
|
+
direction="vertical"
|
|
68
|
+
className={drawerCls.contentScrollArea}
|
|
69
|
+
>
|
|
184
70
|
<ScrollArea.Content
|
|
185
|
-
className={clsx(
|
|
186
|
-
style={{
|
|
187
|
-
minWidth: undefined,
|
|
188
|
-
}}
|
|
71
|
+
className={clsx(drawerCls.contentScrollAreaContent, innerClassName)}
|
|
72
|
+
style={{ minWidth: undefined }}
|
|
189
73
|
>
|
|
190
74
|
{children}
|
|
191
75
|
</ScrollArea.Content>
|
|
@@ -194,140 +78,7 @@ export const Content = function Content({
|
|
|
194
78
|
</BaseDialog.Portal>
|
|
195
79
|
);
|
|
196
80
|
};
|
|
197
|
-
|
|
198
|
-
function useDialogInherentSwipe({ disableSheet }: { disableSheet?: boolean }) {
|
|
199
|
-
const gestureRef = useRef<HTMLDivElement>(null);
|
|
200
|
-
|
|
201
|
-
const close = useContext(DialogCloseContext);
|
|
202
|
-
const isSmall = useMediaQuery('(max-width: 640px)');
|
|
203
|
-
|
|
204
|
-
const gestureState = useRef({
|
|
205
|
-
sy: 0,
|
|
206
|
-
dy: 0,
|
|
207
|
-
vy: 0,
|
|
208
|
-
active: false,
|
|
209
|
-
timeStamp: 0,
|
|
210
|
-
filtered: false,
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const onTouchStart = useCallback(
|
|
214
|
-
(event: TouchEvent) => {
|
|
215
|
-
if (!isSmall || disableSheet) return;
|
|
216
|
-
const touch = event.touches[0];
|
|
217
|
-
gestureState.current.sy = touch.clientY;
|
|
218
|
-
gestureState.current.timeStamp = event.timeStamp;
|
|
219
|
-
},
|
|
220
|
-
[isSmall, disableSheet],
|
|
221
|
-
);
|
|
222
|
-
const onTouchMove = useCallback(
|
|
223
|
-
(event: TouchEvent) => {
|
|
224
|
-
if (!isSmall || disableSheet) return;
|
|
225
|
-
|
|
226
|
-
const touch = event.touches[0];
|
|
227
|
-
const dy = touch.clientY - gestureState.current.sy;
|
|
228
|
-
const vy =
|
|
229
|
-
(dy - gestureState.current.dy) /
|
|
230
|
-
(event.timeStamp -
|
|
231
|
-
(gestureState.current.timeStamp ?? event.timeStamp - 1));
|
|
232
|
-
|
|
233
|
-
gestureState.current.dy = dy;
|
|
234
|
-
gestureState.current.vy = vy;
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
gestureState.current.filtered ||
|
|
238
|
-
filterScrollables(
|
|
239
|
-
event.target as HTMLElement,
|
|
240
|
-
gestureRef.current!,
|
|
241
|
-
dy,
|
|
242
|
-
vy,
|
|
243
|
-
)
|
|
244
|
-
) {
|
|
245
|
-
gestureState.current.filtered = true;
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (!gestureState.current.active) {
|
|
249
|
-
gestureState.current.active = true;
|
|
250
|
-
gestureState.current.sy = touch.clientY;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (gestureRef.current && gestureRef.current.scrollTop < 3) {
|
|
254
|
-
sheetCloseGestureLogic(
|
|
255
|
-
Math.abs(vy) > SWIPE_VELOCITY_THRESHOLD ? Math.sign(vy) : 0,
|
|
256
|
-
dy,
|
|
257
|
-
false,
|
|
258
|
-
close,
|
|
259
|
-
gestureRef.current,
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
gestureState.current.timeStamp = event.timeStamp;
|
|
264
|
-
},
|
|
265
|
-
[isSmall, disableSheet, close],
|
|
266
|
-
);
|
|
267
|
-
const onTouchEnd = useCallback(
|
|
268
|
-
(event: TouchEvent) => {
|
|
269
|
-
if (gestureState.current.active && gestureRef.current) {
|
|
270
|
-
const { vy, dy } = gestureState.current;
|
|
271
|
-
sheetCloseGestureLogic(
|
|
272
|
-
Math.abs(vy) > SWIPE_VELOCITY_THRESHOLD ? Math.sign(vy) : 0,
|
|
273
|
-
dy,
|
|
274
|
-
true,
|
|
275
|
-
close,
|
|
276
|
-
gestureRef.current,
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
gestureState.current.active = false;
|
|
280
|
-
gestureState.current.filtered = false;
|
|
281
|
-
gestureState.current.timeStamp = event.timeStamp;
|
|
282
|
-
gestureState.current.vy = 0;
|
|
283
|
-
gestureState.current.dy = 0;
|
|
284
|
-
gestureState.current.sy = 0;
|
|
285
|
-
},
|
|
286
|
-
[close],
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
return { gestureRef, onTouchStart, onTouchMove, onTouchEnd };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export function DialogSwipeHandle({
|
|
293
|
-
ref,
|
|
294
|
-
className,
|
|
295
|
-
...props
|
|
296
|
-
}: ComponentPropsWithoutRef<'div'> & {
|
|
297
|
-
ref?: React.Ref<HTMLDivElement>;
|
|
298
|
-
}) {
|
|
299
|
-
const close = useContext(DialogCloseContext);
|
|
300
|
-
const innerRef = useRef<HTMLDivElement>(null);
|
|
301
|
-
useDrag(
|
|
302
|
-
({ swipe: [, swipeY], movement: [, dy], last }) => {
|
|
303
|
-
const content = findParentDialogContent(innerRef.current);
|
|
304
|
-
if (!content) return;
|
|
305
|
-
sheetCloseGestureLogic(swipeY, dy, last, close, content);
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
target: innerRef,
|
|
309
|
-
axis: 'y',
|
|
310
|
-
},
|
|
311
|
-
);
|
|
312
|
-
const finalRef = useMergedRef(ref, innerRef);
|
|
313
|
-
return (
|
|
314
|
-
<div ref={finalRef} {...props} className={clsx(cls.swipeHandle, className)}>
|
|
315
|
-
<div className={cls.swipeHandleBar} />
|
|
316
|
-
</div>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function findParentDialogContent(
|
|
321
|
-
element: HTMLElement | null,
|
|
322
|
-
): HTMLElement | null {
|
|
323
|
-
if (!element) return null;
|
|
324
|
-
if (element.getAttribute('data-dialog-content')) return element;
|
|
325
|
-
return findParentDialogContent(element.parentElement);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const DialogCloseContext = createContext<() => void>(() => {});
|
|
329
|
-
|
|
330
|
-
export const DialogDefaultClose = function DialogDefaultClose({
|
|
81
|
+
const DialogDefaultClose = function DialogDefaultClose({
|
|
331
82
|
ref,
|
|
332
83
|
className,
|
|
333
84
|
showOnMobile,
|
|
@@ -349,55 +100,15 @@ export const DialogDefaultClose = function DialogDefaultClose({
|
|
|
349
100
|
</DialogClose>
|
|
350
101
|
);
|
|
351
102
|
};
|
|
352
|
-
|
|
353
|
-
const StyledTitle = withClassName(BaseDialog.Title, cls.title);
|
|
354
|
-
|
|
103
|
+
const StyledDialogTitle = withClassName(BaseDialog.Title, cls.title);
|
|
355
104
|
const StyledDescription = withClassName(
|
|
356
105
|
BaseDialog.Description,
|
|
357
106
|
cls.description,
|
|
358
107
|
);
|
|
359
|
-
|
|
360
|
-
// Exports
|
|
361
|
-
const DialogRoot = (props: DialogRootProps) => {
|
|
362
|
-
const [innerOpen, innerOnOpenChange] = useState(props.defaultOpen || false);
|
|
363
|
-
const open = props.open ?? innerOpen;
|
|
364
|
-
const onOpenChange = useCallback<
|
|
365
|
-
Exclude<DialogRootProps['onOpenChange'], undefined>
|
|
366
|
-
>(
|
|
367
|
-
(open, eventDetails) => {
|
|
368
|
-
innerOnOpenChange(open);
|
|
369
|
-
props.onOpenChange?.(open, eventDetails);
|
|
370
|
-
},
|
|
371
|
-
[props.onOpenChange],
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
const close = useCallback(() => {
|
|
375
|
-
onOpenChange(false, {
|
|
376
|
-
allowPropagation() {},
|
|
377
|
-
cancel() {},
|
|
378
|
-
isCanceled: false,
|
|
379
|
-
event: null as any,
|
|
380
|
-
isPropagationAllowed: true,
|
|
381
|
-
preventUnmountOnClose() {},
|
|
382
|
-
reason: 'imperative-action',
|
|
383
|
-
trigger: undefined,
|
|
384
|
-
});
|
|
385
|
-
}, [onOpenChange]);
|
|
386
|
-
|
|
387
|
-
return (
|
|
388
|
-
<DialogCloseContext.Provider value={close}>
|
|
389
|
-
<BaseDialog.Root {...props} open={open} onOpenChange={onOpenChange} />
|
|
390
|
-
</DialogCloseContext.Provider>
|
|
391
|
-
);
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
export const DialogTrigger = ({ render, ...props }: DialogTriggerProps) => (
|
|
108
|
+
const StyledTrigger = ({ render, ...props }: DialogTriggerProps) => (
|
|
395
109
|
<BaseDialog.Trigger render={render || <Button />} {...props} />
|
|
396
110
|
);
|
|
397
|
-
|
|
398
|
-
export const DialogTitle = StyledTitle;
|
|
399
|
-
export const DialogDescription = StyledDescription;
|
|
400
|
-
export const DialogClose = function DialogClose({
|
|
111
|
+
const StyledClose = function DialogClose({
|
|
401
112
|
children,
|
|
402
113
|
render,
|
|
403
114
|
...props
|
|
@@ -413,12 +124,70 @@ export const DialogClose = function DialogClose({
|
|
|
413
124
|
</BaseDialog.Close>
|
|
414
125
|
);
|
|
415
126
|
};
|
|
127
|
+
const StyledActions = withClassName(Box, cls.actions);
|
|
416
128
|
|
|
417
|
-
|
|
129
|
+
// Variant Dialog/Drawer component wrappers
|
|
130
|
+
const DialogContent = withContextVariant<DialogContentProps>(
|
|
131
|
+
Drawer.Content,
|
|
132
|
+
FunctionalContent,
|
|
133
|
+
);
|
|
134
|
+
const DialogTitle = withContextVariant(Drawer.Title, StyledDialogTitle);
|
|
135
|
+
const DialogDescription = withContextVariant(
|
|
136
|
+
Drawer.Description,
|
|
137
|
+
StyledDescription,
|
|
138
|
+
);
|
|
418
139
|
|
|
419
|
-
export
|
|
140
|
+
export interface DialogProps extends Omit<DialogRootProps, 'children'> {
|
|
141
|
+
children: React.ReactNode;
|
|
142
|
+
disableSheet?: boolean;
|
|
143
|
+
}
|
|
144
|
+
const DialogRoot = ({
|
|
145
|
+
children,
|
|
146
|
+
defaultOpen,
|
|
147
|
+
disableSheet,
|
|
148
|
+
...rootProps
|
|
149
|
+
}: DialogProps) => {
|
|
150
|
+
const [innerOpen, innerOnOpenChange] = useState(defaultOpen || false);
|
|
151
|
+
const open = rootProps.open ?? innerOpen;
|
|
152
|
+
const onOpenChange = useCallback<
|
|
153
|
+
Exclude<DialogRootProps['onOpenChange'], undefined>
|
|
154
|
+
>(
|
|
155
|
+
(open, eventDetails) => {
|
|
156
|
+
innerOnOpenChange(open);
|
|
157
|
+
rootProps.onOpenChange?.(open, eventDetails);
|
|
158
|
+
},
|
|
159
|
+
[rootProps.onOpenChange],
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const isSmall = useMediaQuery('(max-width: 600px)');
|
|
163
|
+
const useDrawer = isSmall && !disableSheet;
|
|
164
|
+
|
|
165
|
+
if (useDrawer) {
|
|
166
|
+
return (
|
|
167
|
+
<MobileDrawerContext.Provider value={true}>
|
|
168
|
+
<Drawer
|
|
169
|
+
{...rootProps}
|
|
170
|
+
open={open}
|
|
171
|
+
onOpenChange={onOpenChange as DrawerRootProps['onOpenChange']}
|
|
172
|
+
>
|
|
173
|
+
{children}
|
|
174
|
+
</Drawer>
|
|
175
|
+
</MobileDrawerContext.Provider>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
420
178
|
|
|
421
|
-
|
|
179
|
+
return (
|
|
180
|
+
<BaseDialog.Root {...rootProps} open={open} onOpenChange={onOpenChange}>
|
|
181
|
+
{children}
|
|
182
|
+
</BaseDialog.Root>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
const DialogTrigger = withContextVariant(Drawer.Trigger, StyledTrigger);
|
|
186
|
+
const DialogClose = withContextVariant(Drawer.Close, StyledClose);
|
|
187
|
+
const DialogActions = withContextVariant(Drawer.Actions, StyledActions);
|
|
188
|
+
|
|
189
|
+
// "Select" stuff - deprecate soon?
|
|
190
|
+
const DialogSelectTrigger = function DialogSelectTrigger({
|
|
422
191
|
children,
|
|
423
192
|
className,
|
|
424
193
|
...props
|
|
@@ -426,21 +195,18 @@ export const DialogSelectTrigger = function DialogSelectTrigger({
|
|
|
426
195
|
ref?: React.Ref<HTMLButtonElement>;
|
|
427
196
|
}) {
|
|
428
197
|
return (
|
|
429
|
-
<
|
|
198
|
+
<DialogTrigger
|
|
430
199
|
className={clsx(selectTriggerClassName, className)}
|
|
431
200
|
{...props}
|
|
432
201
|
>
|
|
433
202
|
<span>{children}</span>
|
|
434
203
|
<Icon name="chevron" />
|
|
435
|
-
</
|
|
204
|
+
</DialogTrigger>
|
|
436
205
|
);
|
|
437
206
|
};
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
export const DialogSelectItemRoot = withClassName(BaseRadio.Root, menuCls.item);
|
|
442
|
-
|
|
443
|
-
export const DialogSelectItem = function DialogSelectItem({
|
|
207
|
+
const DialogSelectList = withClassName(BaseRadioGroup, menuCls.itemList);
|
|
208
|
+
const DialogSelectItemRoot = withClassName(BaseRadio.Root, menuCls.item);
|
|
209
|
+
const DialogSelectItem = function DialogSelectItem({
|
|
444
210
|
children,
|
|
445
211
|
...props
|
|
446
212
|
}: ComponentPropsWithoutRef<typeof DialogSelectItemRoot> & {
|
|
@@ -458,9 +224,9 @@ export const DialogSelectItem = function DialogSelectItem({
|
|
|
458
224
|
|
|
459
225
|
export const Dialog = Object.assign(DialogRoot, {
|
|
460
226
|
Trigger: DialogTrigger,
|
|
461
|
-
Content,
|
|
462
|
-
Title:
|
|
463
|
-
Description:
|
|
227
|
+
Content: DialogContent,
|
|
228
|
+
Title: DialogTitle,
|
|
229
|
+
Description: DialogDescription,
|
|
464
230
|
Close: DialogClose,
|
|
465
231
|
Actions: DialogActions,
|
|
466
232
|
SelectTrigger: DialogSelectTrigger,
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
.overlay {
|
|
2
|
+
position: fixed;
|
|
3
|
+
inset: 0;
|
|
4
|
+
@apply --mx-shadow;
|
|
5
|
+
@apply --mx-bg(var(--m-gray-ink));
|
|
6
|
+
@apply --mx-bg-faded(5%);
|
|
7
|
+
border-top: var(--m-lw) solid var(--m-gray);
|
|
8
|
+
transition: all var(--m-dur) var(--m-ease);
|
|
9
|
+
backdrop-filter: blur(2px);
|
|
10
|
+
|
|
11
|
+
&[data-starting-style],
|
|
12
|
+
&[data-ending-style] {
|
|
13
|
+
opacity: 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.viewport {
|
|
18
|
+
--bleed: 100px;
|
|
19
|
+
|
|
20
|
+
position: fixed;
|
|
21
|
+
inset: 0;
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: flex-end;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
touch-action: none;
|
|
26
|
+
|
|
27
|
+
&::after {
|
|
28
|
+
content: '';
|
|
29
|
+
position: fixed;
|
|
30
|
+
inset-inline: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
height: var(--bleed);
|
|
33
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&[data-closed]::after {
|
|
38
|
+
opacity: 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.popup {
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
position: relative;
|
|
45
|
+
z-index: 1;
|
|
46
|
+
max-width: unset;
|
|
47
|
+
width: 100%;
|
|
48
|
+
max-height: 85svh;
|
|
49
|
+
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
align-items: stretch;
|
|
53
|
+
|
|
54
|
+
touch-action: none;
|
|
55
|
+
|
|
56
|
+
overflow: visible;
|
|
57
|
+
|
|
58
|
+
transition:
|
|
59
|
+
transform var(--m-dur) var(--m-ease),
|
|
60
|
+
opacity var(--m-dur) var(--m-ease);
|
|
61
|
+
|
|
62
|
+
--shadow-dir: -1;
|
|
63
|
+
@apply --mx-shadow;
|
|
64
|
+
--mx-shadow-value: 0 calc(var(--shadow-dir, 1) * var(--m-shadow-lg-y))
|
|
65
|
+
var(--m-shadow-lg-blur) var(--m-shadow-lg-spread) var(--m-shadow-lg-color);
|
|
66
|
+
|
|
67
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
68
|
+
@apply --mx-borderColor(var(--m-gray-heavy));
|
|
69
|
+
|
|
70
|
+
border-radius: var(--m-surface-rd) var(--m-surface-rd) 0 0;
|
|
71
|
+
border-bottom-width: 0;
|
|
72
|
+
padding-bottom: max(
|
|
73
|
+
0px,
|
|
74
|
+
calc(var(--drawer-snap-point-offset) + var(--drawer-swipe-movement-y))
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
/* Use drawer's built-in swipe movement for animation */
|
|
78
|
+
transform: translateY(
|
|
79
|
+
calc(
|
|
80
|
+
var(--drawer-swipe-movement-y, 0px) + var(--drawer-snap-point-offset, 0px)
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
will-change: transform;
|
|
84
|
+
|
|
85
|
+
/* Disable transitions while swiping so movement is immediate */
|
|
86
|
+
&[data-swiping] {
|
|
87
|
+
transition: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&[data-starting-style],
|
|
91
|
+
&[data-ending-style] {
|
|
92
|
+
transform: translateY(100%);
|
|
93
|
+
padding-bottom: 0;
|
|
94
|
+
}
|
|
95
|
+
&[data-ending-style] {
|
|
96
|
+
transition-duration: calc(var(--drawer-swipe-strength) * 400ms);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.contentScrollArea {
|
|
101
|
+
width: 100%;
|
|
102
|
+
height: 100%;
|
|
103
|
+
flex: 1 1 auto;
|
|
104
|
+
min-height: 0;
|
|
105
|
+
overscroll-behavior: contain;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.contentScrollAreaContent {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--m-sp-md);
|
|
112
|
+
padding: var(--m-surface-p);
|
|
113
|
+
overscroll-behavior: contain;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.swipeHandle {
|
|
117
|
+
position: relative;
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
width: 100%;
|
|
122
|
+
cursor: grab;
|
|
123
|
+
touch-action: none;
|
|
124
|
+
padding-block: var(--m-sp-lg);
|
|
125
|
+
user-select: none;
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.swipeHandleBar {
|
|
130
|
+
height: --fn-literal(4px);
|
|
131
|
+
width: 20%;
|
|
132
|
+
@apply --mx-bg(var(--m-gray));
|
|
133
|
+
border-radius: var(--m-rd-lg);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.close {
|
|
137
|
+
position: absolute;
|
|
138
|
+
right: var(--m-sp-sm);
|
|
139
|
+
top: var(--m-sp-sm);
|
|
140
|
+
z-index: 101;
|
|
141
|
+
display: flex;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.title {
|
|
145
|
+
@apply --mx-fg(var(--m-gray-ink));
|
|
146
|
+
font-size: var(--m-prose-primary-size);
|
|
147
|
+
font-weight: var(--m-prose-primary-weight);
|
|
148
|
+
line-height: var(--m-prose-primary-lineHeight);
|
|
149
|
+
margin-block-end: var(--m-sp-xs);
|
|
150
|
+
margin-block-start: 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.description {
|
|
154
|
+
@apply --mx-fg(var(--m-gray-heavy));
|
|
155
|
+
font-size: var(--m-prose-ambient-size);
|
|
156
|
+
line-height: var(--m-prose-ambient-lineHeight);
|
|
157
|
+
margin: 0;
|
|
158
|
+
margin-block-end: var(--m-sp-xs);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.actions {
|
|
162
|
+
position: sticky;
|
|
163
|
+
bottom: var(--m-sp-md);
|
|
164
|
+
z-index: 100;
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-direction: row;
|
|
167
|
+
flex-wrap: wrap;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: end;
|
|
170
|
+
align-self: end;
|
|
171
|
+
padding-bottom: --fn-literal(1px);
|
|
172
|
+
@apply --mx-shadow;
|
|
173
|
+
--mx-shadow-value: 0 calc(-1 * var(--m-shadow-md-y)) 16px
|
|
174
|
+
var(--m-shadow-md-spread) var(--m-gray-paper);
|
|
175
|
+
@apply --mx-bg(var(--m-surface-ambient-bg));
|
|
176
|
+
@apply --mx-bg-faded(50%);
|
|
177
|
+
margin-block-start: var(--m-sp-md);
|
|
178
|
+
gap: var(--m-sp-sm);
|
|
179
|
+
border-radius: var(--m-surface-rd);
|
|
180
|
+
transition: bottom var(--m-dur) var(--m-ease);
|
|
181
|
+
}
|