@duro-app/ui 5.11.0 → 5.13.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/dist/Table-Bms2ftt3.js +1694 -0
- package/dist/Table-Bms2ftt3.js.map +1 -0
- package/dist/compiler-runtime-BuugS4D0.js.map +1 -1
- package/dist/components/Alert/Alert.d.ts.map +1 -1
- package/dist/components/AppShell/native.css.d.ts +53 -0
- package/dist/components/AppShell/native.css.d.ts.map +1 -0
- package/dist/components/Button/Button.d.ts +2 -1
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/styles.css.d.ts +1 -0
- package/dist/components/Button/styles.css.d.ts.map +1 -1
- package/dist/components/Callout/Callout.d.ts.map +1 -1
- package/dist/components/Callout/styles.css.d.ts +3 -0
- package/dist/components/Callout/styles.css.d.ts.map +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -1
- package/dist/components/DetailPanel/DetailPanel.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.d.ts +2 -17
- package/dist/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/components/Dialog/Dialog.stories.d.ts +4 -0
- package/dist/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/components/Dialog/native.css.d.ts +82 -0
- package/dist/components/Dialog/native.css.d.ts.map +1 -0
- package/dist/components/Dialog/parts.css.d.ts +82 -0
- package/dist/components/Dialog/parts.css.d.ts.map +1 -0
- package/dist/components/Dialog/parts.d.ts +35 -0
- package/dist/components/Dialog/parts.d.ts.map +1 -0
- package/dist/components/Dialog/styles.css.d.ts +1 -74
- package/dist/components/Dialog/styles.css.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.d.ts +7 -35
- package/dist/components/Drawer/Drawer.d.ts.map +1 -1
- package/dist/components/Drawer/Drawer.meta.d.ts.map +1 -1
- package/dist/components/Drawer/DrawerScroll.stories.d.ts.map +1 -1
- package/dist/components/Drawer/DrawerSnap.stories.d.ts +20 -0
- package/dist/components/Drawer/DrawerSnap.stories.d.ts.map +1 -0
- package/dist/components/Drawer/native.css.d.ts +55 -0
- package/dist/components/Drawer/native.css.d.ts.map +1 -0
- package/dist/components/Drawer/parts.css.d.ts +96 -0
- package/dist/components/Drawer/parts.css.d.ts.map +1 -0
- package/dist/components/Drawer/parts.d.ts +56 -0
- package/dist/components/Drawer/parts.d.ts.map +1 -0
- package/dist/components/Drawer/sheetPhysics.d.ts +56 -0
- package/dist/components/Drawer/sheetPhysics.d.ts.map +1 -0
- package/dist/components/Drawer/styles.css.d.ts +15 -58
- package/dist/components/Drawer/styles.css.d.ts.map +1 -1
- package/dist/components/Drawer/touchInput.d.ts +35 -0
- package/dist/components/Drawer/touchInput.d.ts.map +1 -0
- package/dist/components/Drawer/useDrawerRoot.d.ts +33 -0
- package/dist/components/Drawer/useDrawerRoot.d.ts.map +1 -0
- package/dist/components/Drawer/useSheetHeights.d.ts +7 -0
- package/dist/components/Drawer/useSheetHeights.d.ts.map +1 -0
- package/dist/components/Drawer/useSwipeDismiss.d.ts +13 -2
- package/dist/components/Drawer/useSwipeDismiss.d.ts.map +1 -1
- package/dist/components/Grid/styles.css.d.ts +1 -0
- package/dist/components/Grid/styles.css.d.ts.map +1 -1
- package/dist/components/Icon/Icon.d.ts +3 -9
- package/dist/components/Icon/Icon.d.ts.map +1 -1
- package/dist/components/Icon/geometry.d.ts +60 -0
- package/dist/components/Icon/geometry.d.ts.map +1 -0
- package/dist/components/Icon/types.d.ts +15 -0
- package/dist/components/Icon/types.d.ts.map +1 -0
- package/dist/components/Input/Input.d.ts +5 -1
- package/dist/components/Input/Input.d.ts.map +1 -1
- package/dist/components/Input/styles.css.d.ts +1 -0
- package/dist/components/Input/styles.css.d.ts.map +1 -1
- package/dist/components/LinkButton/styles.css.d.ts +1 -0
- package/dist/components/LinkButton/styles.css.d.ts.map +1 -1
- package/dist/components/Menu/Menu.d.ts +7 -1
- package/dist/components/Menu/Menu.d.ts.map +1 -1
- package/dist/components/Menu/native.css.d.ts +14 -0
- package/dist/components/Menu/native.css.d.ts.map +1 -0
- package/dist/components/Menu/useMenuOpen.d.ts +16 -0
- package/dist/components/Menu/useMenuOpen.d.ts.map +1 -0
- package/dist/components/Menu/useMenuRoot.d.ts +3 -6
- package/dist/components/Menu/useMenuRoot.d.ts.map +1 -1
- package/dist/components/Meter/styles.css.d.ts +1 -0
- package/dist/components/Meter/styles.css.d.ts.map +1 -1
- package/dist/components/PageShell/styles.css.d.ts +2 -0
- package/dist/components/PageShell/styles.css.d.ts.map +1 -1
- package/dist/components/Popover/native.css.d.ts +11 -0
- package/dist/components/Popover/native.css.d.ts.map +1 -0
- package/dist/components/RadioGroup/RadioGroup.d.ts +1 -17
- package/dist/components/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.stories.d.ts +3 -0
- package/dist/components/RadioGroup/RadioGroup.stories.d.ts.map +1 -1
- package/dist/components/RadioGroup/group.d.ts +31 -0
- package/dist/components/RadioGroup/group.d.ts.map +1 -0
- package/dist/components/RadioGroup/styles.css.d.ts +10 -0
- package/dist/components/RadioGroup/styles.css.d.ts.map +1 -1
- package/dist/components/ScrollArea/styles.css.d.ts +1 -0
- package/dist/components/ScrollArea/styles.css.d.ts.map +1 -1
- package/dist/components/SideNav/Chevron.d.ts +4 -0
- package/dist/components/SideNav/Chevron.d.ts.map +1 -0
- package/dist/components/SideNav/SideNav.d.ts.map +1 -1
- package/dist/components/SideNav/styles.css.d.ts +6 -0
- package/dist/components/SideNav/styles.css.d.ts.map +1 -1
- package/dist/components/SideNav/types.d.ts +6 -0
- package/dist/components/SideNav/types.d.ts.map +1 -0
- package/dist/components/Spinner/Spinner.d.ts +2 -6
- package/dist/components/Spinner/Spinner.d.ts.map +1 -1
- package/dist/components/Spinner/Spinner.types.d.ts +6 -0
- package/dist/components/Spinner/Spinner.types.d.ts.map +1 -0
- package/dist/components/Spinner/ring.css.d.ts +39 -0
- package/dist/components/Spinner/ring.css.d.ts.map +1 -0
- package/dist/components/Spinner/styles.css.d.ts +1 -28
- package/dist/components/Spinner/styles.css.d.ts.map +1 -1
- package/dist/components/SplitPane/SplitPane.d.ts +38 -0
- package/dist/components/SplitPane/SplitPane.d.ts.map +1 -0
- package/dist/components/SplitPane/SplitPane.meta.d.ts +3 -0
- package/dist/components/SplitPane/SplitPane.meta.d.ts.map +1 -0
- package/dist/components/SplitPane/SplitPane.stories.d.ts +17 -0
- package/dist/components/SplitPane/SplitPane.stories.d.ts.map +1 -0
- package/dist/components/SplitPane/styles.css.d.ts +78 -0
- package/dist/components/SplitPane/styles.css.d.ts.map +1 -0
- package/dist/components/StatusIcon/StatusIcon.d.ts.map +1 -1
- package/dist/components/StatusIcon/styles.css.d.ts +3 -0
- package/dist/components/StatusIcon/styles.css.d.ts.map +1 -1
- package/dist/components/Switch/Switch.d.ts +2 -18
- package/dist/components/Switch/Switch.d.ts.map +1 -1
- package/dist/components/Switch/Switch.types.d.ts +18 -0
- package/dist/components/Switch/Switch.types.d.ts.map +1 -0
- package/dist/components/TableOfContents/styles.css.d.ts +1 -0
- package/dist/components/TableOfContents/styles.css.d.ts.map +1 -1
- package/dist/components/Tabs/TabLayout.d.ts +7 -0
- package/dist/components/Tabs/TabLayout.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/components/Tabs/indicator.d.ts +28 -0
- package/dist/components/Tabs/indicator.d.ts.map +1 -0
- package/dist/components/Tabs/styles.css.d.ts +4 -0
- package/dist/components/Tabs/styles.css.d.ts.map +1 -1
- package/dist/components/Tabs/useTabIndicator.d.ts +4 -0
- package/dist/components/Tabs/useTabIndicator.d.ts.map +1 -0
- package/dist/components/Tabs/useTabKeys.d.ts +13 -0
- package/dist/components/Tabs/useTabKeys.d.ts.map +1 -0
- package/dist/components/Tag/RemoveGlyph.d.ts +4 -0
- package/dist/components/Tag/RemoveGlyph.d.ts.map +1 -0
- package/dist/components/Tag/Tag.d.ts +2 -24
- package/dist/components/Tag/Tag.d.ts.map +1 -1
- package/dist/components/Tag/styles.css.d.ts +8 -0
- package/dist/components/Tag/styles.css.d.ts.map +1 -1
- package/dist/components/Tag/types.d.ts +39 -0
- package/dist/components/Tag/types.d.ts.map +1 -0
- package/dist/components/Textarea/styles.css.d.ts +1 -0
- package/dist/components/Textarea/styles.css.d.ts.map +1 -1
- package/dist/components/Toast/ToastProvider.d.ts +2 -21
- package/dist/components/Toast/ToastProvider.d.ts.map +1 -1
- package/dist/components/Toast/native.css.d.ts +81 -0
- package/dist/components/Toast/native.css.d.ts.map +1 -0
- package/dist/components/Toast/store.d.ts +47 -0
- package/dist/components/Toast/store.d.ts.map +1 -0
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/styles.css.d.ts +11 -2
- package/dist/components/Toggle/styles.css.d.ts.map +1 -1
- package/dist/components/Tooltip/TooltipHint.d.ts +9 -0
- package/dist/components/Tooltip/TooltipHint.d.ts.map +1 -0
- package/dist/components/Tree/Tree.d.ts.map +1 -1
- package/dist/components/Tree/styles.css.d.ts +6 -0
- package/dist/components/Tree/styles.css.d.ts.map +1 -1
- package/dist/components/Tree/useTreeKeyboard.d.ts +19 -0
- package/dist/components/Tree/useTreeKeyboard.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.d.ts +33 -0
- package/dist/components/VirtualList/VirtualList.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.meta.d.ts +3 -0
- package/dist/components/VirtualList/VirtualList.meta.d.ts.map +1 -0
- package/dist/components/VirtualList/VirtualList.stories.d.ts +15 -0
- package/dist/components/VirtualList/VirtualList.stories.d.ts.map +1 -0
- package/dist/components/VirtualList/styles.css.d.ts +28 -0
- package/dist/components/VirtualList/styles.css.d.ts.map +1 -0
- package/dist/components/VirtualTable/styles.css.d.ts +6 -0
- package/dist/components/VirtualTable/styles.css.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6202 -4794
- package/dist/index.js.map +1 -1
- package/dist/shared/ButtonHost.d.ts +11 -0
- package/dist/shared/ButtonHost.d.ts.map +1 -0
- package/dist/shared/portal/hostRegistry.d.ts +33 -0
- package/dist/shared/portal/hostRegistry.d.ts.map +1 -0
- package/dist/shared/pressableChild.d.ts +14 -0
- package/dist/shared/pressableChild.d.ts.map +1 -0
- package/dist/shared/useOverlayOpenState.d.ts +40 -0
- package/dist/shared/useOverlayOpenState.d.ts.map +1 -0
- package/dist/table.js +141 -135
- package/dist/table.js.map +1 -1
- package/package.json +34 -4
- package/src/components/Alert/Alert.tsx +14 -2
- package/src/components/AppShell/AppShell.meta.ts +1 -1
- package/src/components/AppShell/AppShell.native.tsx +174 -0
- package/src/components/AppShell/native.css.ts +56 -0
- package/src/components/Button/Button.tsx +6 -5
- package/src/components/Button/styles.css.ts +2 -0
- package/src/components/Callout/Callout.tsx +17 -3
- package/src/components/Callout/styles.css.ts +5 -0
- package/src/components/Checkbox/Checkbox.native.tsx +67 -0
- package/src/components/Checkbox/Checkbox.tsx +1 -50
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +3 -0
- package/src/components/DetailPanel/DetailPanel.tsx +9 -45
- package/src/components/Dialog/Dialog.native.tsx +296 -0
- package/src/components/Dialog/Dialog.stories.tsx +71 -0
- package/src/components/Dialog/Dialog.tsx +41 -130
- package/src/components/Dialog/native.css.ts +78 -0
- package/src/components/Dialog/parts.css.ts +101 -0
- package/src/components/Dialog/parts.tsx +61 -0
- package/src/components/Dialog/styles.css.ts +2 -89
- package/src/components/Drawer/Drawer.meta.ts +2 -1
- package/src/components/Drawer/Drawer.native.tsx +469 -0
- package/src/components/Drawer/Drawer.tsx +63 -179
- package/src/components/Drawer/DrawerScroll.stories.tsx +4 -54
- package/src/components/Drawer/DrawerSnap.stories.tsx +205 -0
- package/src/components/Drawer/native.css.ts +58 -0
- package/src/components/Drawer/parts.css.ts +125 -0
- package/src/components/Drawer/parts.tsx +110 -0
- package/src/components/Drawer/sheetPhysics.ts +103 -0
- package/src/components/Drawer/styles.css.ts +19 -74
- package/src/components/Drawer/touchInput.ts +81 -0
- package/src/components/Drawer/useDrawerRoot.ts +90 -0
- package/src/components/Drawer/useSheetHeights.ts +42 -0
- package/src/components/Drawer/useSwipeDismiss.ts +146 -23
- package/src/components/Grid/styles.css.ts +2 -0
- package/src/components/Icon/Icon.native.tsx +54 -0
- package/src/components/Icon/Icon.tsx +19 -430
- package/src/components/Icon/geometry.ts +456 -0
- package/src/components/Icon/types.ts +74 -0
- package/src/components/Input/Input.tsx +8 -0
- package/src/components/Input/styles.css.ts +2 -0
- package/src/components/LinkButton/styles.css.ts +2 -0
- package/src/components/Menu/Menu.meta.ts +1 -1
- package/src/components/Menu/Menu.native.tsx +268 -0
- package/src/components/Menu/Menu.tsx +11 -2
- package/src/components/Menu/native.css.ts +18 -0
- package/src/components/Menu/useMenuOpen.ts +33 -0
- package/src/components/Menu/useMenuRoot.ts +7 -26
- package/src/components/Meter/styles.css.ts +2 -0
- package/src/components/PageShell/styles.css.ts +4 -0
- package/src/components/Popover/Popover.meta.ts +1 -1
- package/src/components/Popover/Popover.native.tsx +205 -0
- package/src/components/Popover/native.css.ts +13 -0
- package/src/components/RadioGroup/RadioGroup.meta.ts +1 -1
- package/src/components/RadioGroup/RadioGroup.native.tsx +48 -0
- package/src/components/RadioGroup/RadioGroup.stories.tsx +64 -1
- package/src/components/RadioGroup/RadioGroup.tsx +8 -83
- package/src/components/RadioGroup/group.tsx +85 -0
- package/src/components/RadioGroup/styles.css.ts +13 -0
- package/src/components/ScrollArea/styles.css.ts +2 -0
- package/src/components/SideNav/Chevron.native.tsx +20 -0
- package/src/components/SideNav/Chevron.tsx +20 -0
- package/src/components/SideNav/SideNav.meta.ts +1 -1
- package/src/components/SideNav/SideNav.tsx +57 -41
- package/src/components/SideNav/styles.css.ts +8 -0
- package/src/components/SideNav/types.ts +6 -0
- package/src/components/Spinner/Spinner.native.tsx +37 -0
- package/src/components/Spinner/Spinner.tsx +6 -15
- package/src/components/Spinner/Spinner.types.ts +6 -0
- package/src/components/Spinner/ring.css.ts +45 -0
- package/src/components/Spinner/styles.css.ts +2 -32
- package/src/components/SplitPane/SplitPane.meta.ts +40 -0
- package/src/components/SplitPane/SplitPane.native.tsx +47 -0
- package/src/components/SplitPane/SplitPane.stories.tsx +148 -0
- package/src/components/SplitPane/SplitPane.tsx +218 -0
- package/src/components/SplitPane/styles.css.ts +81 -0
- package/src/components/StatusIcon/StatusIcon.tsx +13 -2
- package/src/components/StatusIcon/styles.css.ts +5 -0
- package/src/components/Switch/Switch.native.tsx +45 -0
- package/src/components/Switch/Switch.tsx +22 -44
- package/src/components/Switch/Switch.types.ts +18 -0
- package/src/components/TableOfContents/styles.css.ts +2 -0
- package/src/components/Tabs/TabLayout.native.tsx +9 -0
- package/src/components/Tabs/TabLayout.tsx +6 -0
- package/src/components/Tabs/Tabs.meta.ts +1 -1
- package/src/components/Tabs/Tabs.tsx +38 -136
- package/src/components/Tabs/indicator.ts +36 -0
- package/src/components/Tabs/styles.css.ts +7 -0
- package/src/components/Tabs/useTabIndicator.native.ts +35 -0
- package/src/components/Tabs/useTabIndicator.ts +49 -0
- package/src/components/Tabs/useTabKeys.native.ts +2 -0
- package/src/components/Tabs/useTabKeys.ts +99 -0
- package/src/components/Tag/RemoveGlyph.native.tsx +17 -0
- package/src/components/Tag/RemoveGlyph.tsx +10 -0
- package/src/components/Tag/Tag.meta.ts +1 -1
- package/src/components/Tag/Tag.native.tsx +70 -0
- package/src/components/Tag/Tag.tsx +9 -37
- package/src/components/Tag/styles.css.ts +9 -0
- package/src/components/Tag/types.ts +43 -0
- package/src/components/Textarea/styles.css.ts +2 -0
- package/src/components/ThemeProvider/ThemeProvider.native.tsx +70 -10
- package/src/components/Toast/Toast.meta.ts +1 -1
- package/src/components/Toast/ToastProvider.native.tsx +226 -0
- package/src/components/Toast/ToastProvider.tsx +22 -84
- package/src/components/Toast/native.css.ts +78 -0
- package/src/components/Toast/store.ts +96 -0
- package/src/components/Toggle/Toggle.tsx +5 -3
- package/src/components/Toggle/styles.css.ts +14 -2
- package/src/components/Tooltip/Tooltip.meta.ts +1 -1
- package/src/components/Tooltip/Tooltip.native.tsx +36 -0
- package/src/components/Tooltip/TooltipHint.ts +13 -0
- package/src/components/Tree/Tree.meta.ts +1 -1
- package/src/components/Tree/Tree.tsx +23 -135
- package/src/components/Tree/styles.css.ts +6 -0
- package/src/components/Tree/useTreeKeyboard.native.ts +5 -0
- package/src/components/Tree/useTreeKeyboard.ts +152 -0
- package/src/components/VirtualList/VirtualList.meta.ts +39 -0
- package/src/components/VirtualList/VirtualList.stories.tsx +95 -0
- package/src/components/VirtualList/VirtualList.tsx +103 -0
- package/src/components/VirtualList/styles.css.ts +31 -0
- package/src/components/VirtualTable/styles.css.ts +12 -0
- package/src/hooks/useContainerBelow.native.ts +30 -0
- package/src/index.native.ts +61 -1
- package/src/index.ts +8 -1
- package/src/native-guard/globals.d.ts +10 -0
- package/src/native-guard/react-dom.d.ts +3 -0
- package/src/shared/ButtonHost.native.tsx +70 -0
- package/src/shared/ButtonHost.tsx +12 -0
- package/src/shared/accessibilityFocus.native.ts +23 -0
- package/src/shared/portal/AnchoredOverlay.native.tsx +117 -0
- package/src/shared/portal/OverlayModal.native.tsx +51 -0
- package/src/shared/portal/PortalHost.native.tsx +102 -0
- package/src/shared/portal/hostRegistry.ts +76 -0
- package/src/shared/portal/themeScope.native.ts +18 -0
- package/src/shared/pressableChild.ts +33 -0
- package/src/shared/safeArea.native.ts +22 -0
- package/src/shared/useKeyboardHeight.native.ts +18 -0
- package/src/shared/useOverlayOpenState.ts +128 -0
- package/src/shared/useScreenReaderEnabled.native.ts +19 -0
- package/dist/Table-ColfpgKV.js +0 -1552
- package/dist/Table-ColfpgKV.js.map +0 -1
- package/dist/components/ThemeProvider/ThemeProvider.native.d.ts +0 -10
- package/dist/components/ThemeProvider/ThemeProvider.native.d.ts.map +0 -1
- package/dist/index.native.d.ts +0 -33
- package/dist/index.native.d.ts.map +0 -1
- package/dist/platform.native.d.ts +0 -2
- package/dist/platform.native.d.ts.map +0 -1
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import {createPortal} from 'react-dom'
|
|
2
|
-
import {
|
|
3
|
-
type ReactNode,
|
|
4
|
-
type RefObject,
|
|
5
|
-
createContext,
|
|
6
|
-
useContext,
|
|
7
|
-
useState,
|
|
8
|
-
useEffect,
|
|
9
|
-
useCallback,
|
|
10
|
-
useId,
|
|
11
|
-
useRef,
|
|
12
|
-
} from 'react'
|
|
2
|
+
import {type ReactNode, useState, useEffect, useCallback} from 'react'
|
|
13
3
|
import {html} from 'react-strict-dom'
|
|
14
|
-
import
|
|
4
|
+
import type {DurationToken} from '@duro-app/tokens/keys'
|
|
15
5
|
import {styles} from './styles.css'
|
|
16
6
|
import {usePortalMount} from '../ThemeProvider/ThemeProvider'
|
|
17
7
|
import {ControlContextBoundary} from '../Toolbar/ControlContextBoundary'
|
|
18
8
|
import {ModalContext} from '../../shared/ModalContext'
|
|
19
9
|
import {useSwipeDismiss} from './useSwipeDismiss'
|
|
10
|
+
import {useSheetHeights} from './useSheetHeights'
|
|
11
|
+
import {cycleDetent} from './sheetPhysics'
|
|
12
|
+
import {partStyles} from './parts.css'
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_GRABBER_LABELS,
|
|
15
|
+
Description,
|
|
16
|
+
DrawerContext,
|
|
17
|
+
Footer,
|
|
18
|
+
Header,
|
|
19
|
+
Title,
|
|
20
|
+
useDrawer,
|
|
21
|
+
type DrawerSize,
|
|
22
|
+
type GrabberLabels,
|
|
23
|
+
} from './parts'
|
|
24
|
+
import {useDrawerRoot, type DrawerRootProps} from './useDrawerRoot'
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export type DrawerAnchor = 'right' | 'left' | 'bottom'
|
|
24
|
-
export type DrawerSize = 'sm' | 'md' | 'lg'
|
|
26
|
+
export type {DrawerAnchor, DrawerSize, SnapPoint} from './parts'
|
|
25
27
|
|
|
26
28
|
const closeDurationMap = {
|
|
27
29
|
instant: styles.closeDurationInstant,
|
|
@@ -33,113 +35,15 @@ const closeDurationMap = {
|
|
|
33
35
|
slow: styles.closeDurationSlow,
|
|
34
36
|
} as const satisfies Record<DurationToken, unknown>
|
|
35
37
|
|
|
36
|
-
// --- Context ---
|
|
37
|
-
|
|
38
|
-
interface DrawerContextValue {
|
|
39
|
-
open: boolean
|
|
40
|
-
closing: boolean
|
|
41
|
-
closeDuration: DurationToken
|
|
42
|
-
anchor: DrawerAnchor
|
|
43
|
-
dismissable: boolean
|
|
44
|
-
swipeDismiss: boolean
|
|
45
|
-
requestOpen: () => void
|
|
46
|
-
requestClose: () => void
|
|
47
|
-
/** Close immediately without CSS animation (used by swipe dismiss which handles its own exit animation) */
|
|
48
|
-
requestCloseImmediate: () => void
|
|
49
|
-
titleId: string
|
|
50
|
-
descriptionId: string
|
|
51
|
-
panelRef: RefObject<HTMLDivElement | null>
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const DrawerContext = createContext<DrawerContextValue | null>(null)
|
|
55
|
-
|
|
56
|
-
function useDrawer() {
|
|
57
|
-
const ctx = useContext(DrawerContext)
|
|
58
|
-
if (!ctx) throw new Error('Drawer compound components must be used within Drawer.Root')
|
|
59
|
-
return ctx
|
|
60
|
-
}
|
|
61
|
-
|
|
62
38
|
// --- Root ---
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
open?: boolean
|
|
68
|
-
/** Default open state (uncontrolled) */
|
|
69
|
-
defaultOpen?: boolean
|
|
70
|
-
/** Called when drawer open state changes */
|
|
71
|
-
onOpenChange?: (open: boolean) => void
|
|
72
|
-
/** Which edge the drawer slides from. Default: 'right' */
|
|
73
|
-
anchor?: DrawerAnchor
|
|
74
|
-
/** Whether clicking backdrop or pressing Escape closes the drawer. Default: true */
|
|
75
|
-
dismissable?: boolean
|
|
76
|
-
/** Whether swipe gesture dismisses the drawer. Default: true */
|
|
77
|
-
swipeDismiss?: boolean
|
|
78
|
-
/** Motion token for the close animation. Default: 'fast' (150ms) */
|
|
79
|
-
closeAnimationDuration?: DurationToken
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function Root({
|
|
83
|
-
children,
|
|
84
|
-
open: controlledOpen,
|
|
85
|
-
defaultOpen = false,
|
|
86
|
-
onOpenChange,
|
|
87
|
-
anchor = 'right',
|
|
88
|
-
dismissable = true,
|
|
89
|
-
swipeDismiss = true,
|
|
90
|
-
closeAnimationDuration = 'fast',
|
|
91
|
-
}: RootProps) {
|
|
92
|
-
const isControlled = controlledOpen !== undefined
|
|
93
|
-
const [internalOpen, setInternalOpen] = useState(defaultOpen)
|
|
94
|
-
const [closing, setClosing] = useState(false)
|
|
95
|
-
const closingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
96
|
-
const panelRef = useRef<HTMLDivElement>(null)
|
|
97
|
-
|
|
98
|
-
const isOpen = isControlled ? controlledOpen : internalOpen
|
|
99
|
-
|
|
100
|
-
const titleId = useId()
|
|
101
|
-
const descriptionId = useId()
|
|
102
|
-
|
|
103
|
-
const requestOpen = useCallback(() => {
|
|
104
|
-
if (closingTimerRef.current) {
|
|
105
|
-
clearTimeout(closingTimerRef.current)
|
|
106
|
-
closingTimerRef.current = null
|
|
107
|
-
}
|
|
108
|
-
setClosing(false)
|
|
109
|
-
if (!isControlled) setInternalOpen(true)
|
|
110
|
-
onOpenChange?.(true)
|
|
111
|
-
}, [isControlled, onOpenChange])
|
|
112
|
-
|
|
113
|
-
const requestClose = useCallback(() => {
|
|
114
|
-
setClosing(true)
|
|
115
|
-
closingTimerRef.current = setTimeout(() => {
|
|
116
|
-
setClosing(false)
|
|
117
|
-
closingTimerRef.current = null
|
|
118
|
-
if (!isControlled) setInternalOpen(false)
|
|
119
|
-
onOpenChange?.(false)
|
|
120
|
-
}, DURATION_MS[closeAnimationDuration])
|
|
121
|
-
}, [isControlled, onOpenChange, closeAnimationDuration])
|
|
122
|
-
|
|
123
|
-
const requestCloseImmediate = useCallback(() => {
|
|
124
|
-
if (closingTimerRef.current) {
|
|
125
|
-
clearTimeout(closingTimerRef.current)
|
|
126
|
-
closingTimerRef.current = null
|
|
127
|
-
}
|
|
128
|
-
setClosing(false)
|
|
129
|
-
if (!isControlled) setInternalOpen(false)
|
|
130
|
-
onOpenChange?.(false)
|
|
131
|
-
}, [isControlled, onOpenChange])
|
|
132
|
-
|
|
133
|
-
// Clean up timer on unmount
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
return () => {
|
|
136
|
-
if (closingTimerRef.current) clearTimeout(closingTimerRef.current)
|
|
137
|
-
}
|
|
138
|
-
}, [])
|
|
40
|
+
function Root({children, ...props}: DrawerRootProps) {
|
|
41
|
+
const ctx = useDrawerRoot(props)
|
|
42
|
+
const {open: present, dismissable, requestClose} = ctx
|
|
139
43
|
|
|
140
44
|
// Handle Escape key
|
|
141
45
|
useEffect(() => {
|
|
142
|
-
if (!
|
|
46
|
+
if (!present) return
|
|
143
47
|
if (!dismissable) return
|
|
144
48
|
|
|
145
49
|
function onKeyDown(e: KeyboardEvent) {
|
|
@@ -150,39 +54,20 @@ function Root({
|
|
|
150
54
|
}
|
|
151
55
|
document.addEventListener('keydown', onKeyDown)
|
|
152
56
|
return () => document.removeEventListener('keydown', onKeyDown)
|
|
153
|
-
}, [
|
|
57
|
+
}, [present, dismissable, requestClose])
|
|
154
58
|
|
|
155
59
|
// Lock body scroll when open
|
|
156
60
|
useEffect(() => {
|
|
157
|
-
if (
|
|
61
|
+
if (present) {
|
|
158
62
|
const prev = document.body.style.overflow
|
|
159
63
|
document.body.style.overflow = 'hidden'
|
|
160
64
|
return () => {
|
|
161
65
|
document.body.style.overflow = prev
|
|
162
66
|
}
|
|
163
67
|
}
|
|
164
|
-
}, [
|
|
68
|
+
}, [present])
|
|
165
69
|
|
|
166
|
-
return
|
|
167
|
-
<DrawerContext.Provider
|
|
168
|
-
value={{
|
|
169
|
-
open: isOpen || closing,
|
|
170
|
-
closing,
|
|
171
|
-
closeDuration: closeAnimationDuration,
|
|
172
|
-
anchor,
|
|
173
|
-
dismissable,
|
|
174
|
-
swipeDismiss,
|
|
175
|
-
requestOpen,
|
|
176
|
-
requestClose,
|
|
177
|
-
requestCloseImmediate,
|
|
178
|
-
titleId,
|
|
179
|
-
descriptionId,
|
|
180
|
-
panelRef,
|
|
181
|
-
}}
|
|
182
|
-
>
|
|
183
|
-
{children}
|
|
184
|
-
</DrawerContext.Provider>
|
|
185
|
-
)
|
|
70
|
+
return <DrawerContext.Provider value={ctx}>{children}</DrawerContext.Provider>
|
|
186
71
|
}
|
|
187
72
|
|
|
188
73
|
// --- Trigger ---
|
|
@@ -191,7 +76,7 @@ function Trigger({children}: {children: ReactNode}) {
|
|
|
191
76
|
const {requestOpen} = useDrawer()
|
|
192
77
|
|
|
193
78
|
return (
|
|
194
|
-
<html.div onClick={requestOpen} style={
|
|
79
|
+
<html.div onClick={requestOpen} style={partStyles.inlineWrapper}>
|
|
195
80
|
{children}
|
|
196
81
|
</html.div>
|
|
197
82
|
)
|
|
@@ -202,6 +87,8 @@ function Trigger({children}: {children: ReactNode}) {
|
|
|
202
87
|
interface PortalProps {
|
|
203
88
|
children: ReactNode
|
|
204
89
|
size?: DrawerSize
|
|
90
|
+
/** The grabber's accessible names, by what a press does next. Snap sheets only */
|
|
91
|
+
grabberLabels?: GrabberLabels
|
|
205
92
|
}
|
|
206
93
|
|
|
207
94
|
// Slide animation maps
|
|
@@ -223,7 +110,7 @@ const viewportAnchorMap = {
|
|
|
223
110
|
bottom: styles.viewportBottom,
|
|
224
111
|
} as const
|
|
225
112
|
|
|
226
|
-
function Portal({children, size = 'md'}: PortalProps) {
|
|
113
|
+
function Portal({children, size = 'md', grabberLabels = DEFAULT_GRABBER_LABELS}: PortalProps) {
|
|
227
114
|
const {
|
|
228
115
|
open,
|
|
229
116
|
closing,
|
|
@@ -236,8 +123,14 @@ function Portal({children, size = 'md'}: PortalProps) {
|
|
|
236
123
|
titleId,
|
|
237
124
|
descriptionId,
|
|
238
125
|
panelRef,
|
|
126
|
+
snapPoints,
|
|
127
|
+
snapIndex,
|
|
128
|
+
setSnapIndex,
|
|
239
129
|
} = useDrawer()
|
|
240
130
|
const mount = usePortalMount()
|
|
131
|
+
const snapping = snapPoints.length > 0
|
|
132
|
+
const heights = useSheetHeights(snapPoints, snapping && open)
|
|
133
|
+
const sized = snapping && heights.length === snapPoints.length
|
|
241
134
|
// The shown panel element, tracked as state so swipe listeners follow it
|
|
242
135
|
// when the portal mount arrives and the panel is remounted into it.
|
|
243
136
|
const [panel, setPanel] = useState<HTMLDivElement | null>(null)
|
|
@@ -261,6 +154,7 @@ function Portal({children, size = 'md'}: PortalProps) {
|
|
|
261
154
|
enabled: swipeEnabled && open && !closing,
|
|
262
155
|
onDismiss: requestCloseImmediate,
|
|
263
156
|
panel,
|
|
157
|
+
snap: sized ? {heights, index: snapIndex, onSnap: setSnapIndex} : null,
|
|
264
158
|
})
|
|
265
159
|
|
|
266
160
|
if (!open) return null
|
|
@@ -294,12 +188,29 @@ function Portal({children, size = 'md'}: PortalProps) {
|
|
|
294
188
|
isHorizontal && styles.panelHorizontal,
|
|
295
189
|
isHorizontal && styles[size],
|
|
296
190
|
!isHorizontal && styles.panelVertical,
|
|
191
|
+
sized && styles.panelSnap,
|
|
192
|
+
sized && styles.sheetHeight(heights[snapIndex]),
|
|
297
193
|
anchor === 'left' && styles.panelLeftBorder,
|
|
298
194
|
closing && slideOutMap[anchor],
|
|
299
195
|
closing && closeDurationMap[closeDuration],
|
|
300
196
|
!closing && slideInMap[anchor],
|
|
301
197
|
]}
|
|
302
198
|
>
|
|
199
|
+
{snapping && (
|
|
200
|
+
<html.button
|
|
201
|
+
type="button"
|
|
202
|
+
data-drawer-grabber=""
|
|
203
|
+
aria-label={
|
|
204
|
+
cycleDetent(snapIndex, snapPoints.length) > snapIndex
|
|
205
|
+
? grabberLabels.expand
|
|
206
|
+
: grabberLabels.collapse
|
|
207
|
+
}
|
|
208
|
+
onClick={() => setSnapIndex(cycleDetent(snapIndex, snapPoints.length))}
|
|
209
|
+
style={[partStyles.grabberHit, styles.grabberHitWeb]}
|
|
210
|
+
>
|
|
211
|
+
<html.span style={partStyles.grabber} />
|
|
212
|
+
</html.button>
|
|
213
|
+
)}
|
|
303
214
|
<ModalContext.Provider value={true}>
|
|
304
215
|
<ControlContextBoundary>{children}</ControlContextBoundary>
|
|
305
216
|
</ModalContext.Provider>
|
|
@@ -316,46 +227,19 @@ function Portal({children, size = 'md'}: PortalProps) {
|
|
|
316
227
|
return mount ? createPortal(node, mount) : node
|
|
317
228
|
}
|
|
318
229
|
|
|
319
|
-
// ---
|
|
320
|
-
|
|
321
|
-
function Header({children}: {children: ReactNode}) {
|
|
322
|
-
return <html.div style={styles.header}>{children}</html.div>
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// --- Title ---
|
|
326
|
-
|
|
327
|
-
function Title({children}: {children: ReactNode}) {
|
|
328
|
-
const {titleId} = useDrawer()
|
|
329
|
-
return (
|
|
330
|
-
<html.h2 id={titleId} style={styles.title}>
|
|
331
|
-
{children}
|
|
332
|
-
</html.h2>
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// --- Description ---
|
|
230
|
+
// --- Body ---
|
|
337
231
|
|
|
338
|
-
function
|
|
339
|
-
const {
|
|
232
|
+
function Body({children}: {children: ReactNode}) {
|
|
233
|
+
const {snapPoints} = useDrawer()
|
|
234
|
+
// A snap sheet's Body is its scroll viewport: a drag there scrolls it, and
|
|
235
|
+
// moves the sheet only from the top edge (or upward, below the largest detent).
|
|
340
236
|
return (
|
|
341
|
-
<html.
|
|
237
|
+
<html.div style={styles.body} data-duro-scroll={snapPoints.length > 0 ? '' : undefined}>
|
|
342
238
|
{children}
|
|
343
|
-
</html.
|
|
239
|
+
</html.div>
|
|
344
240
|
)
|
|
345
241
|
}
|
|
346
242
|
|
|
347
|
-
// --- Body ---
|
|
348
|
-
|
|
349
|
-
function Body({children}: {children: ReactNode}) {
|
|
350
|
-
return <html.div style={styles.body}>{children}</html.div>
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// --- Footer ---
|
|
354
|
-
|
|
355
|
-
function Footer({children}: {children: ReactNode}) {
|
|
356
|
-
return <html.div style={styles.footer}>{children}</html.div>
|
|
357
|
-
}
|
|
358
|
-
|
|
359
243
|
// --- Close ---
|
|
360
244
|
|
|
361
245
|
interface CloseProps {
|
|
@@ -368,14 +252,14 @@ function Close({children, 'aria-label': ariaLabel = 'Close'}: CloseProps) {
|
|
|
368
252
|
|
|
369
253
|
if (children) {
|
|
370
254
|
return (
|
|
371
|
-
<html.div onClick={requestClose} style={
|
|
255
|
+
<html.div onClick={requestClose} style={partStyles.inlineWrapper}>
|
|
372
256
|
{children}
|
|
373
257
|
</html.div>
|
|
374
258
|
)
|
|
375
259
|
}
|
|
376
260
|
|
|
377
261
|
return (
|
|
378
|
-
<html.button onClick={requestClose} aria-label={ariaLabel} style={
|
|
262
|
+
<html.button onClick={requestClose} aria-label={ariaLabel} style={partStyles.closeButton}>
|
|
379
263
|
<svg width={16} height={16} viewBox="0 0 16 16" fill="none">
|
|
380
264
|
<path
|
|
381
265
|
d="M4 4l8 8M12 4l-8 8"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type {Meta, StoryObj} from '@storybook/react'
|
|
2
2
|
import {expect, waitFor} from 'storybook/test'
|
|
3
|
-
import {cdp, page} from 'vitest/browser'
|
|
4
3
|
import {Drawer} from './Drawer'
|
|
5
4
|
import {ToggleGroup} from '../ToggleGroup/ToggleGroup'
|
|
6
5
|
import {Toggle} from '../Toggle/Toggle'
|
|
7
6
|
import {Icon, type IconName} from '../Icon'
|
|
8
7
|
import {SIZES_PX} from '@duro-app/tokens/keys'
|
|
8
|
+
import {cdpSend, pause, setViewport, toPage, touchDrag, type Point} from './touchInput'
|
|
9
9
|
|
|
10
10
|
const meta: Meta = {
|
|
11
11
|
title: 'Components/Drawer/Scroll',
|
|
@@ -87,56 +87,6 @@ function Scene({pressed}: {pressed?: string} = {}) {
|
|
|
87
87
|
)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
type Point = {x: number; y: number}
|
|
91
|
-
|
|
92
|
-
/** `CDPSession` is empty until the provider's typings load; `send` exists at runtime. */
|
|
93
|
-
const cdpSend = (method: string, params: object) =>
|
|
94
|
-
(cdp() as unknown as {send: (method: string, params: object) => Promise<unknown>}).send(
|
|
95
|
-
method,
|
|
96
|
-
params,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
const pause = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* CDP input lands in top-level page coordinates; the story runs in an iframe
|
|
103
|
-
* the test runner offsets and may scale to fit. Map a point up every frame.
|
|
104
|
-
*/
|
|
105
|
-
function toPage(p: Point): Point {
|
|
106
|
-
let {x, y} = p
|
|
107
|
-
let win: Window = window
|
|
108
|
-
while (win.frameElement && win.parent !== win) {
|
|
109
|
-
const rect = win.frameElement.getBoundingClientRect()
|
|
110
|
-
const scale = rect.width / win.innerWidth
|
|
111
|
-
x = rect.left + x * scale
|
|
112
|
-
y = rect.top + y * scale
|
|
113
|
-
win = win.parent
|
|
114
|
-
}
|
|
115
|
-
return {x, y}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* A page-coordinate touch point with a 1px radius: Chrome's touch adjustment
|
|
120
|
-
* snaps a fat default touch onto the nearest control, which would turn every
|
|
121
|
-
* drag in the list into a drag on a toggle.
|
|
122
|
-
*/
|
|
123
|
-
const touchAt = (p: Point) => ({...toPage(p), radiusX: 1, radiusY: 1})
|
|
124
|
-
|
|
125
|
-
/** Real touch drag through CDP; synthetic pointer events don't scroll. */
|
|
126
|
-
async function touchDrag(from: Point, to: Point) {
|
|
127
|
-
const steps = 12
|
|
128
|
-
const send = (type: string, touchPoints: Point[]) =>
|
|
129
|
-
cdpSend('Input.dispatchTouchEvent', {type, touchPoints: touchPoints.map(touchAt)})
|
|
130
|
-
await send('touchStart', [from])
|
|
131
|
-
for (let i = 1; i <= steps; i++) {
|
|
132
|
-
await send('touchMove', [
|
|
133
|
-
{x: from.x + ((to.x - from.x) * i) / steps, y: from.y + ((to.y - from.y) * i) / steps},
|
|
134
|
-
])
|
|
135
|
-
await pause(16)
|
|
136
|
-
}
|
|
137
|
-
await send('touchEnd', [])
|
|
138
|
-
}
|
|
139
|
-
|
|
140
90
|
const getViewport = () => document.querySelector<HTMLElement>('[data-duro-scroll]')!
|
|
141
91
|
const getDialog = () => document.querySelector('[role="dialog"]')
|
|
142
92
|
|
|
@@ -158,7 +108,7 @@ function freePoint(el: HTMLElement, fy: number): Point {
|
|
|
158
108
|
}
|
|
159
109
|
|
|
160
110
|
async function setup() {
|
|
161
|
-
await
|
|
111
|
+
await setViewport(390, 844)
|
|
162
112
|
await cdpSend('Emulation.setTouchEmulationEnabled', {enabled: true, maxTouchPoints: 1})
|
|
163
113
|
await waitFor(() => expect(getViewport()).toBeTruthy())
|
|
164
114
|
const viewport = getViewport()
|
|
@@ -242,7 +192,7 @@ export const TapOnToggleStillPresses: Story = {
|
|
|
242
192
|
export const MountKeepsTheDrawerStill: Story = {
|
|
243
193
|
// size the page before the render: a drawer mounts at its final width
|
|
244
194
|
beforeEach: async () => {
|
|
245
|
-
await
|
|
195
|
+
await setViewport(390, 844)
|
|
246
196
|
},
|
|
247
197
|
render: () => <Scene pressed={ICON_NAMES[39]} />,
|
|
248
198
|
play: async () => {
|
|
@@ -278,7 +228,7 @@ export const CoarsePointerRows: Story = {
|
|
|
278
228
|
),
|
|
279
229
|
play: async () => {
|
|
280
230
|
// phone width, so 42 icons overflow three rows
|
|
281
|
-
await
|
|
231
|
+
await setViewport(390, 844)
|
|
282
232
|
await cdpSend('Emulation.setTouchEmulationEnabled', {enabled: true, maxTouchPoints: 1})
|
|
283
233
|
await waitFor(() => expect(matchMedia('(pointer: coarse)').matches).toBe(true))
|
|
284
234
|
const viewport = getViewport()
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type {Meta, StoryObj} from '@storybook/react'
|
|
2
|
+
import {expect, fn, userEvent, waitFor} from 'storybook/test'
|
|
3
|
+
import {Drawer, type SnapPoint} from './Drawer'
|
|
4
|
+
import {Button} from '../Button/Button'
|
|
5
|
+
import {List} from '../List/List'
|
|
6
|
+
import {cdpSend, centre, pause, setViewport, touchDrag, type Point} from './touchInput'
|
|
7
|
+
|
|
8
|
+
const meta: Meta = {
|
|
9
|
+
title: 'Components/Drawer/Snap points',
|
|
10
|
+
parameters: {layout: 'fullscreen'},
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default meta
|
|
14
|
+
type Story = StoryObj
|
|
15
|
+
|
|
16
|
+
const SESSIONS = Array.from({length: 40}, (_, i) => `Session ${i + 1}`)
|
|
17
|
+
|
|
18
|
+
interface SheetArgs {
|
|
19
|
+
snapPoints: SnapPoint[]
|
|
20
|
+
defaultSnapPoint: SnapPoint
|
|
21
|
+
titleAlign: 'start' | 'center'
|
|
22
|
+
onOpenChange?: (open: boolean) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEFAULT_ARGS: SheetArgs = {
|
|
26
|
+
snapPoints: ['medium', 'large'],
|
|
27
|
+
defaultSnapPoint: 'medium',
|
|
28
|
+
titleAlign: 'center',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The picked B0 direction (docs/mockups/native-sheet/Done.dc.html): grabber, centred title, a text Done. */
|
|
32
|
+
function Sheet(props: Partial<SheetArgs>) {
|
|
33
|
+
const {snapPoints, defaultSnapPoint, titleAlign, onOpenChange} = {...DEFAULT_ARGS, ...props}
|
|
34
|
+
return (
|
|
35
|
+
<Drawer.Root
|
|
36
|
+
anchor="bottom"
|
|
37
|
+
snapPoints={snapPoints}
|
|
38
|
+
defaultSnapPoint={defaultSnapPoint}
|
|
39
|
+
onOpenChange={onOpenChange}
|
|
40
|
+
defaultOpen
|
|
41
|
+
>
|
|
42
|
+
<Drawer.Portal>
|
|
43
|
+
<Drawer.Header titleAlign={titleAlign}>
|
|
44
|
+
<Drawer.Title>Move to project</Drawer.Title>
|
|
45
|
+
<Drawer.Close>
|
|
46
|
+
<Button variant="link">Done</Button>
|
|
47
|
+
</Drawer.Close>
|
|
48
|
+
</Drawer.Header>
|
|
49
|
+
<Drawer.Body>
|
|
50
|
+
<List.Root aria-label="Sessions">
|
|
51
|
+
{SESSIONS.map((s) => (
|
|
52
|
+
<List.Item key={s}>
|
|
53
|
+
<List.Text>{s}</List.Text>
|
|
54
|
+
</List.Item>
|
|
55
|
+
))}
|
|
56
|
+
</List.Root>
|
|
57
|
+
</Drawer.Body>
|
|
58
|
+
</Drawer.Portal>
|
|
59
|
+
</Drawer.Root>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const SnapSheet: StoryObj<SheetArgs> = {
|
|
64
|
+
args: {...DEFAULT_ARGS, onOpenChange: fn()},
|
|
65
|
+
argTypes: {
|
|
66
|
+
snapPoints: {
|
|
67
|
+
control: 'check',
|
|
68
|
+
options: ['medium', 'large'],
|
|
69
|
+
description: "The detents: 'medium' half the window, 'large' the window less its top inset",
|
|
70
|
+
},
|
|
71
|
+
defaultSnapPoint: {
|
|
72
|
+
control: 'inline-radio',
|
|
73
|
+
options: ['medium', 'large'],
|
|
74
|
+
description: 'The detent the sheet opens at',
|
|
75
|
+
},
|
|
76
|
+
titleAlign: {
|
|
77
|
+
control: 'inline-radio',
|
|
78
|
+
options: ['start', 'center'],
|
|
79
|
+
description: 'Drawer.Header: the title at the start, or centred with the close at the end',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
// Keyed by the opening detent: it seeds state on mount, so a change remounts the sheet.
|
|
83
|
+
render: (args) => (
|
|
84
|
+
<Sheet
|
|
85
|
+
key={args.defaultSnapPoint}
|
|
86
|
+
{...args}
|
|
87
|
+
snapPoints={args.snapPoints.length > 0 ? args.snapPoints : ['medium']}
|
|
88
|
+
/>
|
|
89
|
+
),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const getDialog = () => document.querySelector<HTMLElement>('[role="dialog"]')
|
|
93
|
+
const getGrabber = () => document.querySelector<HTMLElement>('[data-drawer-grabber]')!
|
|
94
|
+
const getBody = () => document.querySelector<HTMLElement>('[role="dialog"] [data-duro-scroll]')!
|
|
95
|
+
const sheetHeight = () => getDialog()!.getBoundingClientRect().height
|
|
96
|
+
|
|
97
|
+
const WINDOW_H = 844
|
|
98
|
+
const MEDIUM = WINDOW_H / 2
|
|
99
|
+
const LARGE = WINDOW_H // no top safe-area inset in a desktop browser
|
|
100
|
+
|
|
101
|
+
async function setup() {
|
|
102
|
+
await setViewport(390, WINDOW_H)
|
|
103
|
+
await cdpSend('Emulation.setTouchEmulationEnabled', {enabled: true, maxTouchPoints: 1})
|
|
104
|
+
await waitFor(() => expect(getDialog()).not.toBeNull())
|
|
105
|
+
// let the slide-in finish
|
|
106
|
+
await pause(500)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const near = (actual: number, expected: number) =>
|
|
110
|
+
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(2)
|
|
111
|
+
|
|
112
|
+
async function dragGrabber(dy: number) {
|
|
113
|
+
const from: Point = centre(getGrabber())
|
|
114
|
+
await touchDrag(from, {x: from.x, y: from.y + dy})
|
|
115
|
+
await pause(400)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// One drag per story: a test that drags three times can outlast the runner's
|
|
119
|
+
// timeout on a loaded machine.
|
|
120
|
+
export const DragUpGrowsToLarge: Story = {
|
|
121
|
+
render: () => <Sheet />,
|
|
122
|
+
play: async () => {
|
|
123
|
+
await setup()
|
|
124
|
+
await waitFor(() => near(sheetHeight(), MEDIUM))
|
|
125
|
+
await dragGrabber(-300)
|
|
126
|
+
await waitFor(() => near(sheetHeight(), LARGE))
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const DragDownShrinksToMedium: Story = {
|
|
131
|
+
render: () => <Sheet defaultSnapPoint="large" />,
|
|
132
|
+
play: async () => {
|
|
133
|
+
await setup()
|
|
134
|
+
await waitFor(() => near(sheetHeight(), LARGE))
|
|
135
|
+
await dragGrabber(300)
|
|
136
|
+
await waitFor(() => near(sheetHeight(), MEDIUM))
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const DragDownBelowMediumDismisses: Story = {
|
|
141
|
+
render: () => <Sheet />,
|
|
142
|
+
play: async () => {
|
|
143
|
+
await setup()
|
|
144
|
+
await waitFor(() => near(sheetHeight(), MEDIUM))
|
|
145
|
+
await dragGrabber(300)
|
|
146
|
+
await waitFor(() => expect(getDialog()).toBeNull())
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const SlowShortDragSnapsBack: Story = {
|
|
151
|
+
render: () => <Sheet />,
|
|
152
|
+
play: async () => {
|
|
153
|
+
await setup()
|
|
154
|
+
const from = centre(getGrabber())
|
|
155
|
+
// 60px over 20 moves (about 0.2 px/ms): well under the fling speed, well
|
|
156
|
+
// short of the next detent
|
|
157
|
+
await touchDrag(from, {x: from.x, y: from.y - 60}, 20)
|
|
158
|
+
await pause(400)
|
|
159
|
+
await waitFor(() => near(sheetHeight(), MEDIUM))
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const GrabberTapCyclesDetents: Story = {
|
|
164
|
+
render: () => <Sheet />,
|
|
165
|
+
play: async () => {
|
|
166
|
+
await setup()
|
|
167
|
+
const grabber = getGrabber()
|
|
168
|
+
await expect(grabber).toHaveAccessibleName('Expand sheet')
|
|
169
|
+
await userEvent.click(grabber)
|
|
170
|
+
await waitFor(() => near(sheetHeight(), LARGE))
|
|
171
|
+
await expect(grabber).toHaveAccessibleName('Shrink sheet')
|
|
172
|
+
await userEvent.click(grabber)
|
|
173
|
+
await waitFor(() => near(sheetHeight(), MEDIUM))
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const DragUpOnContentGrowsThenScrolls: Story = {
|
|
178
|
+
render: () => <Sheet />,
|
|
179
|
+
play: async () => {
|
|
180
|
+
await setup()
|
|
181
|
+
const from = centre(getBody())
|
|
182
|
+
// At medium, the content's upward drag grows the sheet …
|
|
183
|
+
await touchDrag(from, {x: from.x, y: from.y - 250})
|
|
184
|
+
await pause(400)
|
|
185
|
+
await waitFor(() => near(sheetHeight(), LARGE))
|
|
186
|
+
await expect(getBody().scrollTop).toBe(0)
|
|
187
|
+
// … at the largest detent it scrolls the list.
|
|
188
|
+
const again = centre(getBody())
|
|
189
|
+
await touchDrag(again, {x: again.x, y: again.y - 120})
|
|
190
|
+
await pause(300)
|
|
191
|
+
await expect(getBody().scrollTop).toBeGreaterThan(0)
|
|
192
|
+
near(sheetHeight(), LARGE)
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const DoneCloses: Story = {
|
|
197
|
+
render: () => <Sheet />,
|
|
198
|
+
play: async () => {
|
|
199
|
+
await setup()
|
|
200
|
+
await userEvent.click(
|
|
201
|
+
document.querySelector('[role="dialog"] button:not([data-drawer-grabber])')!,
|
|
202
|
+
)
|
|
203
|
+
await waitFor(() => expect(getDialog()).toBeNull())
|
|
204
|
+
},
|
|
205
|
+
}
|