paris 0.21.2 → 0.22.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/CHANGELOG.md +39 -0
- package/package.json +1 -1
- package/src/helpers/OpenChangeEffect.tsx +21 -0
- package/src/helpers/useControllableState.test.ts +88 -0
- package/src/helpers/useControllableState.ts +59 -0
- package/src/stories/accordionselect/AccordionSelect.test.tsx +72 -0
- package/src/stories/accordionselect/AccordionSelect.tsx +22 -12
- package/src/stories/checkbox/Checkbox.test.tsx +53 -0
- package/src/stories/checkbox/Checkbox.tsx +21 -6
- package/src/stories/combobox/Combobox.test.tsx +111 -0
- package/src/stories/combobox/Combobox.tsx +192 -137
- package/src/stories/drawer/Drawer.module.scss +56 -15
- package/src/stories/drawer/Drawer.stories.tsx +287 -109
- package/src/stories/drawer/Drawer.test.tsx +486 -11
- package/src/stories/drawer/Drawer.tsx +366 -240
- package/src/stories/drawer/DrawerActions.tsx +28 -0
- package/src/stories/drawer/DrawerBottomPanel.tsx +55 -0
- package/src/stories/drawer/DrawerContext.tsx +31 -0
- package/src/stories/drawer/DrawerPage.tsx +37 -0
- package/src/stories/drawer/DrawerPageContext.tsx +35 -0
- package/src/stories/drawer/DrawerPaginationContext.tsx +22 -0
- package/src/stories/drawer/DrawerProgressBar.tsx +72 -0
- package/src/stories/drawer/DrawerSlotContext.tsx +172 -0
- package/src/stories/drawer/DrawerTitle.tsx +35 -0
- package/src/stories/drawer/index.ts +9 -0
- package/src/stories/menu/Menu.test.tsx +43 -0
- package/src/stories/menu/Menu.tsx +13 -2
- package/src/stories/popover/Popover.tsx +8 -5
- package/src/stories/select/Select.module.scss +1 -1
- package/src/stories/select/Select.test.tsx +108 -0
- package/src/stories/select/Select.tsx +121 -92
- package/src/test/render.tsx +2 -2
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
|
|
4
4
|
import type { CSSLength } from '@ssh/csstypes';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import { motion } from 'framer-motion';
|
|
7
|
+
import {
|
|
8
|
+
Children,
|
|
9
|
+
type ComponentPropsWithoutRef,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
useCallback,
|
|
12
|
+
useEffect,
|
|
13
|
+
useMemo,
|
|
14
|
+
useRef,
|
|
15
|
+
useState,
|
|
16
|
+
} from 'react';
|
|
8
17
|
import { Button } from '../button';
|
|
9
18
|
import { ChevronLeft, ChevronRight, Close, Icon } from '../icon';
|
|
10
19
|
import type { PaginationState } from '../pagination';
|
|
@@ -13,9 +22,24 @@ import { RemoveFromDOM } from '../utility/RemoveFromDOM';
|
|
|
13
22
|
import { TextWhenString } from '../utility/TextWhenString';
|
|
14
23
|
import { VisuallyHidden } from '../utility/VisuallyHidden';
|
|
15
24
|
import styles from './Drawer.module.scss';
|
|
25
|
+
import { DrawerProvider } from './DrawerContext';
|
|
26
|
+
import { type DrawerPageProps, isDrawerPageElement } from './DrawerPage';
|
|
27
|
+
import { DrawerPageProvider } from './DrawerPageContext';
|
|
28
|
+
import { DrawerPaginationProvider } from './DrawerPaginationContext';
|
|
29
|
+
import { DrawerProgressBar, type DrawerProgressBarStyleProps } from './DrawerProgressBar';
|
|
30
|
+
import { DrawerSlotProvider, useDrawerSlotContext } from './DrawerSlotContext';
|
|
16
31
|
|
|
17
32
|
export const DrawerSizePresets = ['content', 'default', 'full', 'fullWithMargin', 'fullOnMobile'] as const;
|
|
18
33
|
|
|
34
|
+
export type DrawerPageTransition = 'none' | 'crossfade' | 'slide';
|
|
35
|
+
|
|
36
|
+
/** Extract the page ID from a child element (DrawerPage or legacy div-with-key). */
|
|
37
|
+
const getChildPageID = (child: ReactNode): string | null => {
|
|
38
|
+
if (isDrawerPageElement(child)) return (child as React.ReactElement<DrawerPageProps>).props.id;
|
|
39
|
+
if (child != null && typeof child === 'object' && 'key' in child) return child.key as string;
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
|
|
19
43
|
export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
20
44
|
/**
|
|
21
45
|
* The dialog's open state.
|
|
@@ -40,8 +64,6 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
40
64
|
*
|
|
41
65
|
* If you're hiding the title to add a custom header, you may also want to hide the close button and render your own by using the `hideCloseButton` prop.
|
|
42
66
|
*
|
|
43
|
-
* When pagination is enabled, the title is always hidden regardless of this prop.
|
|
44
|
-
*
|
|
45
67
|
* @default false
|
|
46
68
|
*/
|
|
47
69
|
hideTitle?: boolean;
|
|
@@ -51,17 +73,6 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
51
73
|
* @default false
|
|
52
74
|
*/
|
|
53
75
|
hideCloseButton?: boolean;
|
|
54
|
-
/**
|
|
55
|
-
* An optional panel that will be rendered at the bottom of the Drawer. This is useful for adding a footer to the Drawer with actions.
|
|
56
|
-
*/
|
|
57
|
-
bottomPanel?: ReactNode;
|
|
58
|
-
/**
|
|
59
|
-
* Whether the bottom panel should have default padding. Set to `false` for edge-to-edge content like dividers or multi-section layouts.
|
|
60
|
-
*
|
|
61
|
-
* @default true
|
|
62
|
-
*/
|
|
63
|
-
bottomPanelPadding?: boolean;
|
|
64
|
-
|
|
65
76
|
/**
|
|
66
77
|
* An optional area that will be rendered at the top of the Drawer next to the title. This is useful for adding actions to the Drawer. Recommended to use {@link Menu} for an action menu.
|
|
67
78
|
*/
|
|
@@ -91,9 +102,9 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
91
102
|
* const pagination = usePagination<['step1', 'step2', 'step3']>();
|
|
92
103
|
* // ...
|
|
93
104
|
* <Drawer pagination={pagination}>
|
|
94
|
-
* <
|
|
95
|
-
* <
|
|
96
|
-
* <
|
|
105
|
+
* <DrawerPage id="step1">Step 1</DrawerPage>
|
|
106
|
+
* <DrawerPage id="step2">Step 2</DrawerPage>
|
|
107
|
+
* <DrawerPage id="step3">Step 3</DrawerPage>
|
|
97
108
|
* </Drawer>
|
|
98
109
|
*```
|
|
99
110
|
*
|
|
@@ -102,6 +113,32 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
102
113
|
* @default false
|
|
103
114
|
*/
|
|
104
115
|
pagination?: PaginationState<T>;
|
|
116
|
+
/**
|
|
117
|
+
* The page transition animation style for paginated drawers.
|
|
118
|
+
*
|
|
119
|
+
* - `'none'` — instant page switch
|
|
120
|
+
* - `'crossfade'` — opacity crossfade between pages (default)
|
|
121
|
+
* - `'slide'` — direction-aware horizontal slide (Framer Motion)
|
|
122
|
+
*
|
|
123
|
+
* @default 'crossfade'
|
|
124
|
+
*/
|
|
125
|
+
pageTransition?: DrawerPageTransition;
|
|
126
|
+
/**
|
|
127
|
+
* Show a progress bar at the top of the bottom panel. Requires `pagination` to be set.
|
|
128
|
+
* The bar auto-fills based on the current page position.
|
|
129
|
+
*
|
|
130
|
+
* Pass `true` for defaults, or an object with `fill`, `track`, and `height` overrides.
|
|
131
|
+
*
|
|
132
|
+
* For explicit percentage control, use `<DrawerProgressBar value={75} />` directly instead.
|
|
133
|
+
*
|
|
134
|
+
* @default false
|
|
135
|
+
*/
|
|
136
|
+
progressBar?: boolean | DrawerProgressBarStyleProps;
|
|
137
|
+
/**
|
|
138
|
+
* Callback fired after the drawer's close animation completes.
|
|
139
|
+
* Use this to reset forms, clear state, and clean up without visual glitches.
|
|
140
|
+
*/
|
|
141
|
+
onAfterClose?: () => void;
|
|
105
142
|
/**
|
|
106
143
|
* The overlay style of the Drawer, either 'grey' or 'blur'.
|
|
107
144
|
*
|
|
@@ -122,7 +159,7 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
122
159
|
titleBarButtons?: ComponentPropsWithoutRef<'div'>;
|
|
123
160
|
content?: ComponentPropsWithoutRef<'div'>;
|
|
124
161
|
contentChildren?: ComponentPropsWithoutRef<'div'>;
|
|
125
|
-
contentChildrenChildren
|
|
162
|
+
contentChildrenChildren?: ComponentPropsWithoutRef<'div'>;
|
|
126
163
|
bottomPanelSpacer?: ComponentPropsWithoutRef<'div'>;
|
|
127
164
|
bottomPanel?: ComponentPropsWithoutRef<'div'>;
|
|
128
165
|
bottomPanelContent?: ComponentPropsWithoutRef<'div'>;
|
|
@@ -141,268 +178,357 @@ export type DrawerProps<T extends string[] | readonly string[] = string[]> = {
|
|
|
141
178
|
* ```
|
|
142
179
|
* @constructor
|
|
143
180
|
*/
|
|
144
|
-
export const Drawer = <T extends string[] | readonly string[] = string[]>({
|
|
145
|
-
isOpen = false,
|
|
181
|
+
export const Drawer = <T extends string[] | readonly string[] = string[]>(props: DrawerProps<T>) => {
|
|
182
|
+
const { isOpen = false, onClose = () => {}, onAfterClose } = props;
|
|
183
|
+
|
|
184
|
+
const handleClose = useCallback(() => onClose(false), [onClose]);
|
|
185
|
+
|
|
186
|
+
const hasBeenOpen = useRef(false);
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (isOpen) hasBeenOpen.current = true;
|
|
189
|
+
}, [isOpen]);
|
|
190
|
+
|
|
191
|
+
const handleAfterLeave = useCallback(() => {
|
|
192
|
+
if (hasBeenOpen.current) {
|
|
193
|
+
onAfterClose?.();
|
|
194
|
+
}
|
|
195
|
+
}, [onAfterClose]);
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<Transition show={isOpen} afterLeave={handleAfterLeave}>
|
|
199
|
+
<Dialog
|
|
200
|
+
as="div"
|
|
201
|
+
className={clsx(styles.root, props.overrides?.dialog?.className)}
|
|
202
|
+
onClose={onClose}
|
|
203
|
+
{...props.overrides?.dialog}
|
|
204
|
+
role="dialog"
|
|
205
|
+
>
|
|
206
|
+
<DrawerProvider close={handleClose} isOpen={isOpen}>
|
|
207
|
+
<DrawerPaginationProvider pagination={props.pagination ?? null}>
|
|
208
|
+
<DrawerSlotProvider>
|
|
209
|
+
<DrawerInner {...props} />
|
|
210
|
+
</DrawerSlotProvider>
|
|
211
|
+
</DrawerPaginationProvider>
|
|
212
|
+
</DrawerProvider>
|
|
213
|
+
</Dialog>
|
|
214
|
+
</Transition>
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/** Internal component that renders inside all providers so it can consume slot context. */
|
|
219
|
+
const DrawerInner = <T extends string[] | readonly string[] = string[]>({
|
|
146
220
|
onClose = () => {},
|
|
147
221
|
title,
|
|
148
222
|
hideTitle = false,
|
|
149
223
|
hideCloseButton = false,
|
|
150
|
-
bottomPanel,
|
|
151
|
-
bottomPanelPadding = true,
|
|
152
224
|
from = 'right',
|
|
153
225
|
size = 'default',
|
|
154
226
|
pagination,
|
|
227
|
+
pageTransition = 'crossfade',
|
|
228
|
+
progressBar,
|
|
155
229
|
overlayStyle = 'grey',
|
|
156
230
|
additionalActions,
|
|
157
231
|
children,
|
|
158
232
|
overrides,
|
|
159
233
|
}: DrawerProps<T>) => {
|
|
160
|
-
|
|
161
|
-
const xAxisDrawer = useMemo(() => ['left', 'right'].includes(from), [from]);
|
|
234
|
+
const slotContext = useDrawerSlotContext();
|
|
162
235
|
|
|
163
|
-
|
|
164
|
-
const sizeIsPreset =
|
|
236
|
+
const xAxisDrawer = from === 'left' || from === 'right';
|
|
237
|
+
const sizeIsPreset = (DrawerSizePresets as readonly string[]).includes(size);
|
|
238
|
+
const isPaginated = Boolean(pagination);
|
|
239
|
+
const hasAdditionalActions = Boolean(additionalActions);
|
|
165
240
|
|
|
166
|
-
|
|
167
|
-
const isPaginated = useMemo(() => Boolean(pagination), [pagination]);
|
|
241
|
+
const showBottomPanel = (slotContext?.hasAnyBottomPanelSlot ?? false) || (slotContext?.hasProgressBar ?? false);
|
|
168
242
|
|
|
169
|
-
|
|
243
|
+
// Sync spacer height with absolute-positioned bottom panel so content doesn't get clipped
|
|
244
|
+
const bottomPanelElRef = useRef<HTMLDivElement | null>(null);
|
|
245
|
+
const spacerRef = useRef<HTMLDivElement | null>(null);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!showBottomPanel) return;
|
|
248
|
+
const panel = bottomPanelElRef.current;
|
|
249
|
+
const spacer = spacerRef.current;
|
|
250
|
+
if (!panel || !spacer) return;
|
|
251
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
252
|
+
spacer.style.height = `${entry.contentRect.height}px`;
|
|
253
|
+
});
|
|
254
|
+
observer.observe(panel);
|
|
255
|
+
return () => observer.disconnect();
|
|
256
|
+
}, [showBottomPanel]);
|
|
170
257
|
|
|
171
|
-
const [loadedPage, setLoadedPage] = useState<string | null>(pagination?.
|
|
258
|
+
const [loadedPage, setLoadedPage] = useState<string | null>(pagination?.currentPage ?? null);
|
|
172
259
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// console.log(bottomPanelRef.current);
|
|
181
|
-
// }, [bottomPanelRef.current]);
|
|
260
|
+
const pageEntries = useMemo(() => {
|
|
261
|
+
if (!isPaginated || !pagination || !children) return null;
|
|
262
|
+
const childArray = Array.isArray(children) ? children : Children.toArray(children);
|
|
263
|
+
return childArray
|
|
264
|
+
.map((child) => ({ id: getChildPageID(child), child }))
|
|
265
|
+
.filter((entry): entry is { id: string; child: ReactNode } => entry.id !== null);
|
|
266
|
+
}, [isPaginated, pagination, children]);
|
|
182
267
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (!
|
|
187
|
-
|
|
188
|
-
|
|
268
|
+
const activePageIndex = pageEntries?.findIndex((p) => p.id === pagination?.currentPage) ?? -1;
|
|
269
|
+
|
|
270
|
+
const paginatedContent = useMemo(() => {
|
|
271
|
+
if (!pageEntries || !pagination) return null;
|
|
272
|
+
|
|
273
|
+
switch (pageTransition) {
|
|
274
|
+
case 'none':
|
|
275
|
+
return pageEntries.map((page) => {
|
|
276
|
+
const isActive = pagination.currentPage === page.id;
|
|
277
|
+
return (
|
|
278
|
+
<DrawerPageProvider key={page.id} isActive={isActive} pageID={page.id}>
|
|
279
|
+
<div
|
|
280
|
+
style={{ display: isActive ? undefined : 'none' }}
|
|
281
|
+
className={overrides?.contentChildrenChildren?.className}
|
|
282
|
+
>
|
|
283
|
+
{page.child}
|
|
284
|
+
</div>
|
|
285
|
+
</DrawerPageProvider>
|
|
286
|
+
);
|
|
287
|
+
});
|
|
189
288
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
289
|
+
case 'crossfade':
|
|
290
|
+
return pageEntries.map((page) => {
|
|
291
|
+
const isActive = pagination.currentPage === page.id && loadedPage === page.id;
|
|
292
|
+
return (
|
|
293
|
+
<DrawerPageProvider key={page.id} isActive={isActive} pageID={page.id}>
|
|
294
|
+
<Transition
|
|
295
|
+
show={isActive}
|
|
296
|
+
as="div"
|
|
297
|
+
enter={styles.paginationEnter}
|
|
298
|
+
enterFrom={styles.enterFromOpacity}
|
|
299
|
+
enterTo={styles.enterToOpacity}
|
|
300
|
+
leave={styles.paginationLeave}
|
|
301
|
+
leaveFrom={styles.leaveFromOpacity}
|
|
302
|
+
leaveTo={styles.leaveToOpacity}
|
|
303
|
+
afterLeave={() => setLoadedPage(pagination.currentPage)}
|
|
304
|
+
className={overrides?.contentChildrenChildren?.className}
|
|
305
|
+
>
|
|
306
|
+
{page.child}
|
|
307
|
+
</Transition>
|
|
308
|
+
</DrawerPageProvider>
|
|
197
309
|
);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
case 'slide':
|
|
313
|
+
return (
|
|
314
|
+
<div className={clsx(styles.pageStack, styles.pageStackClip)}>
|
|
315
|
+
{pageEntries.map((page, i) => {
|
|
316
|
+
const isActive = pagination.currentPage === page.id;
|
|
317
|
+
const offset = (i - activePageIndex) * 100;
|
|
318
|
+
return (
|
|
319
|
+
<DrawerPageProvider key={page.id} isActive={isActive} pageID={page.id}>
|
|
320
|
+
<motion.div
|
|
321
|
+
initial={false}
|
|
322
|
+
animate={{ x: `${offset}%` }}
|
|
323
|
+
transition={{
|
|
324
|
+
type: 'tween',
|
|
325
|
+
duration: 0.3,
|
|
326
|
+
ease: [0.32, 0.72, 0, 1],
|
|
327
|
+
}}
|
|
328
|
+
className={clsx(
|
|
329
|
+
styles.pageStackItem,
|
|
330
|
+
overrides?.contentChildrenChildren?.className,
|
|
331
|
+
)}
|
|
332
|
+
data-active={isActive}
|
|
333
|
+
>
|
|
334
|
+
{page.child}
|
|
335
|
+
</motion.div>
|
|
336
|
+
</DrawerPageProvider>
|
|
337
|
+
);
|
|
338
|
+
})}
|
|
339
|
+
</div>
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
default: {
|
|
343
|
+
const _exhaustive: never = pageTransition;
|
|
344
|
+
return _exhaustive;
|
|
204
345
|
}
|
|
205
346
|
}
|
|
347
|
+
}, [
|
|
348
|
+
pageEntries,
|
|
349
|
+
pagination,
|
|
350
|
+
pageTransition,
|
|
351
|
+
loadedPage,
|
|
352
|
+
activePageIndex,
|
|
353
|
+
overrides?.contentChildrenChildren?.className,
|
|
354
|
+
]);
|
|
206
355
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
356
|
+
/** Non-page children (e.g. Drawer-level DrawerBottomPanel) that should render alongside paginated content. */
|
|
357
|
+
const nonPageChildren = useMemo(() => {
|
|
358
|
+
if (!isPaginated || !children) return null;
|
|
359
|
+
const childArray = Array.isArray(children) ? children : Children.toArray(children);
|
|
360
|
+
return childArray.filter((child) => getChildPageID(child) === null);
|
|
361
|
+
}, [isPaginated, children]);
|
|
210
362
|
|
|
211
363
|
return (
|
|
212
|
-
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
364
|
+
<>
|
|
365
|
+
<div
|
|
366
|
+
className={clsx(
|
|
367
|
+
overlayStyle === 'blur' && styles.overlayBlurContainer,
|
|
368
|
+
overlayStyle === 'grey' && styles.overlayGreyContainer,
|
|
369
|
+
overrides?.overlay?.className,
|
|
370
|
+
)}
|
|
219
371
|
>
|
|
220
|
-
<
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
372
|
+
<TransitionChild
|
|
373
|
+
enter={styles.enter}
|
|
374
|
+
enterFrom={styles.enterFrom}
|
|
375
|
+
enterTo={styles.enterTo}
|
|
376
|
+
leave={styles.leave}
|
|
377
|
+
leaveFrom={styles.leaveFrom}
|
|
378
|
+
leaveTo={styles.leaveTo}
|
|
226
379
|
>
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
className={clsx(
|
|
237
|
-
styles.overlay,
|
|
238
|
-
overlayStyle === 'blur' && styles.overlayBlur,
|
|
239
|
-
overlayStyle === 'grey' && styles.overlayGrey,
|
|
240
|
-
)}
|
|
241
|
-
/>
|
|
242
|
-
</TransitionChild>
|
|
243
|
-
</div>
|
|
380
|
+
<div
|
|
381
|
+
className={clsx(
|
|
382
|
+
styles.overlay,
|
|
383
|
+
overlayStyle === 'blur' && styles.overlayBlur,
|
|
384
|
+
overlayStyle === 'grey' && styles.overlayGrey,
|
|
385
|
+
)}
|
|
386
|
+
/>
|
|
387
|
+
</TransitionChild>
|
|
388
|
+
</div>
|
|
244
389
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
390
|
+
<div
|
|
391
|
+
className={clsx(
|
|
392
|
+
styles.panelContainer,
|
|
393
|
+
styles[`from-${from}`],
|
|
394
|
+
{ [styles[`size-${size}`]]: sizeIsPreset },
|
|
395
|
+
overrides?.panelContainer?.className,
|
|
396
|
+
)}
|
|
397
|
+
style={
|
|
398
|
+
!sizeIsPreset
|
|
399
|
+
? {
|
|
400
|
+
[xAxisDrawer ? 'width' : 'height']: size,
|
|
401
|
+
...overrides?.panelContainer?.style,
|
|
402
|
+
}
|
|
403
|
+
: overrides?.panelContainer?.style
|
|
404
|
+
}
|
|
405
|
+
{...overrides?.panelContainer}
|
|
406
|
+
>
|
|
407
|
+
<TransitionChild
|
|
408
|
+
enter={styles.enter}
|
|
409
|
+
enterFrom={styles.enterFrom}
|
|
410
|
+
enterTo={styles.enterTo}
|
|
411
|
+
leave={styles.leave}
|
|
412
|
+
leaveFrom={styles.leaveFrom}
|
|
413
|
+
leaveTo={styles.leaveTo}
|
|
261
414
|
>
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
leaveTo={styles.leaveTo}
|
|
269
|
-
>
|
|
270
|
-
<DialogPanel
|
|
271
|
-
className={clsx(styles.panel, styles[`from-${from}`], overrides?.panel?.className)}
|
|
272
|
-
>
|
|
273
|
-
{/* Dialog title bar */}
|
|
274
|
-
<div className={clsx(styles.titleBar, overrides?.titleBar?.className)}>
|
|
275
|
-
<div className={clsx(styles.titleArea, overrides?.titleArea?.className)}>
|
|
276
|
-
<RemoveFromDOM
|
|
277
|
-
// Hide when pagination is not enabled.
|
|
278
|
-
when={!isPaginated}
|
|
279
|
-
>
|
|
280
|
-
<div className={clsx(styles.paginationButtons)}>
|
|
281
|
-
<Button
|
|
282
|
-
className={clsx(styles.navButton)}
|
|
283
|
-
size="medium"
|
|
284
|
-
kind="tertiary"
|
|
285
|
-
shape="circle"
|
|
286
|
-
onClick={() => pagination?.back()}
|
|
287
|
-
disabled={!pagination?.canGoBack()}
|
|
288
|
-
startEnhancer={<Icon icon={ChevronLeft} size={16} />}
|
|
289
|
-
>
|
|
290
|
-
Go to previous page in this modal
|
|
291
|
-
</Button>
|
|
292
|
-
<Button
|
|
293
|
-
className={clsx(styles.navButton)}
|
|
294
|
-
size="medium"
|
|
295
|
-
kind="tertiary"
|
|
296
|
-
shape="circle"
|
|
297
|
-
onClick={() => pagination?.forward()}
|
|
298
|
-
disabled={!pagination?.canGoForward()}
|
|
299
|
-
startEnhancer={<Icon icon={ChevronRight} size={16} />}
|
|
300
|
-
>
|
|
301
|
-
Go to next page in this modal
|
|
302
|
-
</Button>
|
|
303
|
-
</div>
|
|
304
|
-
</RemoveFromDOM>
|
|
305
|
-
<VisuallyHidden
|
|
306
|
-
// Hide when requested, or when pagination is enabled (the title isn't relevant to any specific page).
|
|
307
|
-
when={hideTitle}
|
|
308
|
-
>
|
|
309
|
-
<DialogTitle as="h2" className={styles.titleTextContainer}>
|
|
310
|
-
<TextWhenString kind="paragraphSmall" weight="medium">
|
|
311
|
-
{title}
|
|
312
|
-
</TextWhenString>
|
|
313
|
-
</DialogTitle>
|
|
314
|
-
</VisuallyHidden>
|
|
315
|
-
</div>
|
|
316
|
-
<div className={clsx(styles.titleBarButtons, overrides?.titleBarButtons?.className)}>
|
|
317
|
-
{/* Action Menu */}
|
|
318
|
-
<RemoveFromDOM when={!hasAdditionalActions}>{additionalActions}</RemoveFromDOM>
|
|
319
|
-
|
|
320
|
-
{/* Close button */}
|
|
321
|
-
<RemoveFromDOM
|
|
322
|
-
// Hide when requested, or when pagination is enabled (the page navigation bar will render its own close button).
|
|
323
|
-
when={hideCloseButton}
|
|
324
|
-
>
|
|
415
|
+
<DialogPanel className={clsx(styles.panel, styles[`from-${from}`], overrides?.panel?.className)}>
|
|
416
|
+
{/* Dialog title bar */}
|
|
417
|
+
<div className={clsx(styles.titleBar, overrides?.titleBar?.className)}>
|
|
418
|
+
<div className={clsx(styles.titleArea, overrides?.titleArea?.className)}>
|
|
419
|
+
<RemoveFromDOM when={!isPaginated}>
|
|
420
|
+
<div className={clsx(styles.paginationButtons)}>
|
|
325
421
|
<Button
|
|
422
|
+
className={clsx(styles.navButton)}
|
|
423
|
+
size="medium"
|
|
326
424
|
kind="tertiary"
|
|
327
425
|
shape="circle"
|
|
328
|
-
onClick={() =>
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
className={clsx(styles.closeButton)}
|
|
426
|
+
onClick={() => pagination?.back()}
|
|
427
|
+
disabled={!pagination?.canGoBack()}
|
|
428
|
+
startEnhancer={<Icon icon={ChevronLeft} size={16} />}
|
|
332
429
|
>
|
|
333
|
-
|
|
430
|
+
Go to previous page in this drawer
|
|
334
431
|
</Button>
|
|
335
|
-
|
|
336
|
-
|
|
432
|
+
<Button
|
|
433
|
+
className={clsx(styles.navButton)}
|
|
434
|
+
size="medium"
|
|
435
|
+
kind="tertiary"
|
|
436
|
+
shape="circle"
|
|
437
|
+
onClick={() => pagination?.forward()}
|
|
438
|
+
disabled={!pagination?.canGoForward()}
|
|
439
|
+
startEnhancer={<Icon icon={ChevronRight} size={16} />}
|
|
440
|
+
>
|
|
441
|
+
Go to next page in this drawer
|
|
442
|
+
</Button>
|
|
443
|
+
</div>
|
|
444
|
+
</RemoveFromDOM>
|
|
445
|
+
{slotContext && <div ref={slotContext.titleRef} />}
|
|
446
|
+
<VisuallyHidden when={hideTitle || (slotContext?.hasTitleSlot ?? false)}>
|
|
447
|
+
<DialogTitle as="h2" className={styles.titleTextContainer}>
|
|
448
|
+
<TextWhenString kind="paragraphSmall" weight="medium">
|
|
449
|
+
{title}
|
|
450
|
+
</TextWhenString>
|
|
451
|
+
</DialogTitle>
|
|
452
|
+
</VisuallyHidden>
|
|
453
|
+
</div>
|
|
454
|
+
<div className={clsx(styles.titleBarButtons, overrides?.titleBarButtons?.className)}>
|
|
455
|
+
{slotContext && <div ref={slotContext.actionsRef} />}
|
|
456
|
+
{!slotContext?.hasActionsSlot && (
|
|
457
|
+
<RemoveFromDOM when={!hasAdditionalActions}>{additionalActions}</RemoveFromDOM>
|
|
458
|
+
)}
|
|
459
|
+
|
|
460
|
+
{/* Close button */}
|
|
461
|
+
<RemoveFromDOM when={hideCloseButton}>
|
|
462
|
+
<Button
|
|
463
|
+
kind="tertiary"
|
|
464
|
+
shape="circle"
|
|
465
|
+
onClick={() => onClose(false)}
|
|
466
|
+
startEnhancer={<Icon icon={Close} size={20} />}
|
|
467
|
+
data-title-hidden={hideTitle}
|
|
468
|
+
className={clsx(styles.closeButton)}
|
|
469
|
+
>
|
|
470
|
+
Close drawer
|
|
471
|
+
</Button>
|
|
472
|
+
</RemoveFromDOM>
|
|
337
473
|
</div>
|
|
474
|
+
</div>
|
|
338
475
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
? children.map(
|
|
343
|
-
(child) =>
|
|
344
|
-
child &&
|
|
345
|
-
typeof child === 'object' &&
|
|
346
|
-
'key' in child && (
|
|
347
|
-
<Transition
|
|
348
|
-
show={
|
|
349
|
-
child.key === pagination?.currentPage &&
|
|
350
|
-
loadedPage === child.key
|
|
351
|
-
}
|
|
352
|
-
key={`transition_${child.key}`}
|
|
353
|
-
as="div"
|
|
354
|
-
enter={styles.paginationEnter}
|
|
355
|
-
enterFrom={styles.enterFromOpacity}
|
|
356
|
-
enterTo={styles.enterToOpacity}
|
|
357
|
-
leave={styles.paginationLeave}
|
|
358
|
-
leaveFrom={styles.leaveFromOpacity}
|
|
359
|
-
leaveTo={styles.leaveToOpacity}
|
|
360
|
-
afterLeave={() => {
|
|
361
|
-
setLoadedPage(pagination?.currentPage || null);
|
|
362
|
-
}}
|
|
363
|
-
className={clsx(
|
|
364
|
-
overrides?.contentChildrenChildren?.className,
|
|
365
|
-
)}
|
|
366
|
-
>
|
|
367
|
-
{child}
|
|
368
|
-
</Transition>
|
|
369
|
-
),
|
|
370
|
-
)
|
|
371
|
-
: children}
|
|
372
|
-
</div>
|
|
373
|
-
{bottomPanel && (
|
|
476
|
+
<div className={clsx(styles.content, overrides?.content?.className)}>
|
|
477
|
+
<div className={clsx(styles.contentChildren, overrides?.contentChildren?.className)}>
|
|
478
|
+
{isPaginated ? (
|
|
374
479
|
<>
|
|
480
|
+
{paginatedContent}
|
|
481
|
+
{nonPageChildren}
|
|
482
|
+
{progressBar && pageEntries && (
|
|
483
|
+
<DrawerProgressBar
|
|
484
|
+
steps={pageEntries.map((p) => p.id)}
|
|
485
|
+
{...(typeof progressBar === 'object' ? progressBar : undefined)}
|
|
486
|
+
/>
|
|
487
|
+
)}
|
|
488
|
+
</>
|
|
489
|
+
) : (
|
|
490
|
+
children
|
|
491
|
+
)}
|
|
492
|
+
</div>
|
|
493
|
+
{showBottomPanel && (
|
|
494
|
+
<>
|
|
495
|
+
<div
|
|
496
|
+
ref={spacerRef}
|
|
497
|
+
tabIndex={-1}
|
|
498
|
+
aria-hidden="true"
|
|
499
|
+
className={clsx(
|
|
500
|
+
styles.bottomPanelSpacer,
|
|
501
|
+
overrides?.bottomPanelSpacer?.className,
|
|
502
|
+
)}
|
|
503
|
+
/>
|
|
504
|
+
<div
|
|
505
|
+
ref={bottomPanelElRef}
|
|
506
|
+
className={clsx(styles.bottomPanel, overrides?.bottomPanel?.className)}
|
|
507
|
+
>
|
|
508
|
+
{slotContext && <div ref={slotContext.progressBarRef} />}
|
|
509
|
+
<div className={styles.glassOpacity} />
|
|
510
|
+
<div className={styles.glassBlend} />
|
|
375
511
|
<div
|
|
376
|
-
tabIndex={-1}
|
|
377
|
-
aria-hidden="true"
|
|
378
512
|
className={clsx(
|
|
379
|
-
styles.
|
|
380
|
-
|
|
381
|
-
overrides?.
|
|
513
|
+
styles.bottomPanelContent,
|
|
514
|
+
styles.noPadding,
|
|
515
|
+
overrides?.bottomPanelContent?.className,
|
|
382
516
|
)}
|
|
383
517
|
>
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
className={clsx(
|
|
391
|
-
styles.bottomPanelContent,
|
|
392
|
-
{ [styles.noPadding]: !bottomPanelPadding },
|
|
393
|
-
overrides?.bottomPanelContent?.className,
|
|
394
|
-
)}
|
|
395
|
-
>
|
|
396
|
-
{bottomPanel}
|
|
397
|
-
</div>
|
|
518
|
+
{slotContext && (
|
|
519
|
+
<div
|
|
520
|
+
ref={slotContext.bottomPanelCallbackRef}
|
|
521
|
+
className={styles.bottomPanelSlots}
|
|
522
|
+
/>
|
|
523
|
+
)}
|
|
398
524
|
</div>
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
</
|
|
403
|
-
</
|
|
404
|
-
</
|
|
405
|
-
</
|
|
406
|
-
|
|
525
|
+
</div>
|
|
526
|
+
</>
|
|
527
|
+
)}
|
|
528
|
+
</div>
|
|
529
|
+
</DialogPanel>
|
|
530
|
+
</TransitionChild>
|
|
531
|
+
</div>
|
|
532
|
+
</>
|
|
407
533
|
);
|
|
408
534
|
};
|