react-panel-layout 0.6.1 → 0.7.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/FloatingWindow-Bw2djgpz.js +1542 -0
- package/dist/FloatingWindow-Bw2djgpz.js.map +1 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs +2 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs.map +1 -0
- package/dist/GridLayout-B4aCqSyd.js +947 -0
- package/dist/{GridLayout-UWNxXw77.js.map → GridLayout-B4aCqSyd.js.map} +1 -1
- package/dist/GridLayout-DNOClFzz.cjs +2 -0
- package/dist/{GridLayout-DKTg_N61.cjs.map → GridLayout-DNOClFzz.cjs.map} +1 -1
- package/dist/PanelSystem-B8Igvnb2.cjs +3 -0
- package/dist/PanelSystem-B8Igvnb2.cjs.map +1 -0
- package/dist/{PanelSystem-BqUzNtf2.js → PanelSystem-DDUSFjXD.js} +208 -247
- package/dist/PanelSystem-DDUSFjXD.js.map +1 -0
- package/dist/components/window/Drawer.d.ts +1 -0
- package/dist/components/window/DrawerRevealContext.d.ts +61 -0
- package/dist/components/window/drawerRevealAnimationUtils.d.ts +212 -0
- package/dist/components/window/drawerStyles.d.ts +5 -0
- package/dist/components/window/useDrawerSwipeTransform.d.ts +8 -2
- package/dist/components/window/useDrawerTransform.d.ts +68 -0
- package/dist/components/window/useRevealDrawerTransform.d.ts +56 -0
- package/dist/config.cjs +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +8 -7
- package/dist/config.js.map +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +4 -4
- package/dist/modules/drawer/drawerStateMachine.d.ts +168 -0
- package/dist/modules/drawer/revealDrawerConstants.d.ts +33 -0
- package/dist/modules/drawer/revealDrawerStateMachine.d.ts +146 -0
- package/dist/modules/drawer/strategies/index.d.ts +8 -0
- package/dist/modules/drawer/strategies/overlayStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/revealStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/types.d.ts +116 -0
- package/dist/panels.cjs +1 -1
- package/dist/panels.js +1 -1
- package/dist/stack.cjs +1 -1
- package/dist/stack.cjs.map +1 -1
- package/dist/stack.js +306 -347
- package/dist/stack.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs +2 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs.map +1 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js +394 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js.map +1 -0
- package/dist/window.cjs +1 -1
- package/dist/window.js +1 -1
- package/package.json +1 -1
- package/src/components/gesture/SwipeSafeZone.tsx +1 -0
- package/src/components/grid/GridLayout.tsx +110 -38
- package/src/components/window/Drawer.tsx +114 -10
- package/src/components/window/DrawerLayers.tsx +48 -15
- package/src/components/window/DrawerRevealContext.spec.ts +20 -0
- package/src/components/window/DrawerRevealContext.tsx +99 -0
- package/src/components/window/drawerRevealAnimationUtils.spec.ts +375 -0
- package/src/components/window/drawerRevealAnimationUtils.ts +415 -0
- package/src/components/window/drawerStyles.spec.ts +39 -0
- package/src/components/window/drawerStyles.ts +24 -0
- package/src/components/window/useDrawerSwipeTransform.ts +28 -90
- package/src/components/window/useDrawerTransform.ts +505 -0
- package/src/components/window/useRevealDrawerTransform.spec.ts +1936 -0
- package/src/components/window/useRevealDrawerTransform.ts +105 -0
- package/src/demo/components/FullscreenDemoPage.tsx +47 -0
- package/src/demo/fullscreenRoutes.tsx +32 -0
- package/src/demo/index.tsx +5 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +23 -8
- package/src/demo/pages/Drawer/components/DrawerBasics.module.css +6 -1
- package/src/demo/pages/Drawer/components/DrawerBasics.tsx +14 -4
- package/src/demo/pages/Drawer/components/DrawerReveal.module.css +157 -0
- package/src/demo/pages/Drawer/components/DrawerReveal.tsx +128 -0
- package/src/demo/pages/Drawer/reveal/index.tsx +17 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/index.tsx +135 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/styles.module.css +233 -0
- package/src/demo/pages/Stack/components/StackBasics.spec.tsx +56 -52
- package/src/demo/pages/Stack/components/StackTablet.spec.tsx +39 -49
- package/src/demo/routes.tsx +2 -0
- package/src/hooks/gesture/testing/createGestureSimulator.ts +1 -0
- package/src/hooks/gesture/useNativeGestureGuard.spec.ts +10 -2
- package/src/hooks/gesture/utils.ts +15 -4
- package/src/hooks/useAnimatedVisibility.spec.ts +3 -3
- package/src/hooks/useOperationContinuity.spec.ts +17 -10
- package/src/hooks/useOperationContinuity.ts +5 -5
- package/src/hooks/useSharedElementTransition.ts +28 -7
- package/src/modules/dialog/dialogAnimationUtils.spec.ts +0 -1
- package/src/modules/dialog/useDialogContainer.spec.ts +11 -3
- package/src/modules/dialog/useDialogSwipeInput.spec.ts +49 -28
- package/src/modules/dialog/useDialogSwipeInput.ts +37 -6
- package/src/modules/dialog/useDialogTransform.spec.ts +63 -30
- package/src/modules/drawer/drawerStateMachine.ts +500 -0
- package/src/modules/drawer/revealDrawerConstants.ts +38 -0
- package/src/modules/drawer/revealDrawerStateMachine.spec.ts +558 -0
- package/src/modules/drawer/revealDrawerStateMachine.ts +197 -0
- package/src/modules/drawer/strategies/index.ts +9 -0
- package/src/modules/drawer/strategies/overlayStrategy.ts +133 -0
- package/src/modules/drawer/strategies/revealStrategy.ts +111 -0
- package/src/modules/drawer/strategies/types.ts +160 -0
- package/src/modules/drawer/useDrawerSwipeInput.ts +7 -4
- package/src/modules/pivot/SwipePivotContent.spec.tsx +48 -37
- package/src/modules/pivot/usePivotSwipeInput.spec.ts +8 -8
- package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1 -1
- package/src/types.ts +15 -0
- package/dist/FloatingWindow-CUXnEtrb.js +0 -827
- package/dist/FloatingWindow-CUXnEtrb.js.map +0 -1
- package/dist/FloatingWindow-DMwyK0eK.cjs +0 -2
- package/dist/FloatingWindow-DMwyK0eK.cjs.map +0 -1
- package/dist/GridLayout-DKTg_N61.cjs +0 -2
- package/dist/GridLayout-UWNxXw77.js +0 -926
- package/dist/PanelSystem-BqUzNtf2.js.map +0 -1
- package/dist/PanelSystem-D603LKKv.cjs +0 -3
- package/dist/PanelSystem-D603LKKv.cjs.map +0 -1
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs +0 -2
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +0 -1
- package/dist/useNativeGestureGuard-CGYo6O0r.js +0 -347
- package/dist/useNativeGestureGuard-CGYo6O0r.js.map +0 -1
- package/src/components/window/useDrawerSwipeTransform.spec.ts +0 -234
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Drawer - Reveal Mode Fullscreen Demo
|
|
3
|
+
*
|
|
4
|
+
* Standalone page demonstrating reveal mode with body-level transform.
|
|
5
|
+
* No SingleSamplePage wrapper - this is the entire page.
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { Link } from "react-router";
|
|
9
|
+
import { GridLayout } from "../../../../components/grid/GridLayout";
|
|
10
|
+
import type { LayerDefinition, PanelLayoutConfig } from "../../../../types";
|
|
11
|
+
import { FiArrowLeft, FiHome, FiSearch, FiBell, FiMail, FiUser, FiSettings } from "react-icons/fi";
|
|
12
|
+
import styles from "./styles.module.css";
|
|
13
|
+
|
|
14
|
+
const Page: React.FC = () => {
|
|
15
|
+
const [open, setOpen] = React.useState(false);
|
|
16
|
+
|
|
17
|
+
const config = React.useMemo<PanelLayoutConfig>(
|
|
18
|
+
() => ({
|
|
19
|
+
areas: [["content"]],
|
|
20
|
+
rows: [{ size: "1fr" }],
|
|
21
|
+
columns: [{ size: "1fr" }],
|
|
22
|
+
gap: "0",
|
|
23
|
+
}),
|
|
24
|
+
[],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const layers: LayerDefinition[] = [
|
|
28
|
+
{
|
|
29
|
+
id: "content",
|
|
30
|
+
gridArea: "content",
|
|
31
|
+
component: (
|
|
32
|
+
<div className={styles.container}>
|
|
33
|
+
<header className={styles.header}>
|
|
34
|
+
<button type="button" className={styles.menuButton} onClick={() => setOpen(true)}>
|
|
35
|
+
<span className={styles.menuIcon} />
|
|
36
|
+
</button>
|
|
37
|
+
<h1 className={styles.title}>Reveal Mode Demo</h1>
|
|
38
|
+
</header>
|
|
39
|
+
|
|
40
|
+
<main className={styles.main}>
|
|
41
|
+
<div className={styles.card}>
|
|
42
|
+
<h2>Full-screen Reveal Mode</h2>
|
|
43
|
+
<p>
|
|
44
|
+
This page demonstrates the reveal animation mode without any wrapper.
|
|
45
|
+
The entire page slides to reveal the drawer behind it.
|
|
46
|
+
</p>
|
|
47
|
+
<p>
|
|
48
|
+
Try swiping from the left edge, or tap the menu button above.
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div className={styles.card}>
|
|
53
|
+
<h3>How it works</h3>
|
|
54
|
+
<ul className={styles.list}>
|
|
55
|
+
<li>Drawer is positioned behind the main content</li>
|
|
56
|
+
<li>Body element receives transform during animation</li>
|
|
57
|
+
<li>Swipe gestures work from the left edge</li>
|
|
58
|
+
</ul>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<Link to="/components/drawer/reveal" className={styles.backLink}>
|
|
62
|
+
<FiArrowLeft />
|
|
63
|
+
Back to Drawer demos
|
|
64
|
+
</Link>
|
|
65
|
+
</main>
|
|
66
|
+
</div>
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "drawer-reveal-fullscreen",
|
|
71
|
+
component: (
|
|
72
|
+
<div className={styles.drawerContent}>
|
|
73
|
+
<div className={styles.profile}>
|
|
74
|
+
<div className={styles.avatar}>U</div>
|
|
75
|
+
<div className={styles.profileInfo}>
|
|
76
|
+
<div className={styles.profileName}>User Name</div>
|
|
77
|
+
<div className={styles.profileHandle}>@username</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<nav className={styles.nav}>
|
|
82
|
+
<Link to="/" className={styles.navItem}>
|
|
83
|
+
<FiHome className={styles.navIcon} />
|
|
84
|
+
Home
|
|
85
|
+
</Link>
|
|
86
|
+
<a href="#" className={styles.navItem}>
|
|
87
|
+
<FiSearch className={styles.navIcon} />
|
|
88
|
+
Explore
|
|
89
|
+
</a>
|
|
90
|
+
<a href="#" className={styles.navItem}>
|
|
91
|
+
<FiBell className={styles.navIcon} />
|
|
92
|
+
Notifications
|
|
93
|
+
</a>
|
|
94
|
+
<a href="#" className={styles.navItem}>
|
|
95
|
+
<FiMail className={styles.navIcon} />
|
|
96
|
+
Messages
|
|
97
|
+
</a>
|
|
98
|
+
<a href="#" className={styles.navItem}>
|
|
99
|
+
<FiUser className={styles.navIcon} />
|
|
100
|
+
Profile
|
|
101
|
+
</a>
|
|
102
|
+
<a href="#" className={styles.navItem}>
|
|
103
|
+
<FiSettings className={styles.navIcon} />
|
|
104
|
+
Settings
|
|
105
|
+
</a>
|
|
106
|
+
</nav>
|
|
107
|
+
|
|
108
|
+
<button type="button" className={styles.closeButton} onClick={() => setOpen(false)}>
|
|
109
|
+
Close Menu
|
|
110
|
+
</button>
|
|
111
|
+
</div>
|
|
112
|
+
),
|
|
113
|
+
drawer: {
|
|
114
|
+
open,
|
|
115
|
+
onStateChange: setOpen,
|
|
116
|
+
dismissible: true,
|
|
117
|
+
animationMode: "reveal",
|
|
118
|
+
swipeGestures: {
|
|
119
|
+
edgeSwipeOpen: true,
|
|
120
|
+
swipeClose: true,
|
|
121
|
+
edgeWidth: 24,
|
|
122
|
+
dismissThreshold: 0.3,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
position: { left: 0 },
|
|
126
|
+
width: 280,
|
|
127
|
+
height: "100%",
|
|
128
|
+
zIndex: 1200,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
return <GridLayout config={config} layers={layers} root />;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export default Page;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Styles for Reveal Mode Fullscreen Demo
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
.container {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
height: 100vh;
|
|
9
|
+
height: 100dvh;
|
|
10
|
+
overflow-y: auto;
|
|
11
|
+
background: #fff;
|
|
12
|
+
color: #0f1419;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.header {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 16px;
|
|
19
|
+
padding: 12px 16px;
|
|
20
|
+
border-bottom: 1px solid #eff3f4;
|
|
21
|
+
position: sticky;
|
|
22
|
+
top: 0;
|
|
23
|
+
background: #fff;
|
|
24
|
+
z-index: 100;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.menuButton {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
width: 40px;
|
|
32
|
+
height: 40px;
|
|
33
|
+
border: none;
|
|
34
|
+
background: transparent;
|
|
35
|
+
border-radius: 50%;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition: background 0.15s ease;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.menuButton:hover {
|
|
41
|
+
background: rgba(15, 20, 25, 0.1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.menuIcon {
|
|
45
|
+
position: relative;
|
|
46
|
+
width: 18px;
|
|
47
|
+
height: 2px;
|
|
48
|
+
background: #0f1419;
|
|
49
|
+
border-radius: 1px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.menuIcon::before,
|
|
53
|
+
.menuIcon::after {
|
|
54
|
+
content: "";
|
|
55
|
+
position: absolute;
|
|
56
|
+
left: 0;
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: 2px;
|
|
59
|
+
background: #0f1419;
|
|
60
|
+
border-radius: 1px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.menuIcon::before {
|
|
64
|
+
top: -6px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.menuIcon::after {
|
|
68
|
+
top: 6px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.title {
|
|
72
|
+
font-size: 20px;
|
|
73
|
+
font-weight: 700;
|
|
74
|
+
margin: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.main {
|
|
78
|
+
flex: 1;
|
|
79
|
+
padding: 20px 16px;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: 16px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.card {
|
|
86
|
+
padding: 20px;
|
|
87
|
+
background: #f7f9f9;
|
|
88
|
+
border-radius: 16px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.card h2 {
|
|
92
|
+
margin: 0 0 12px;
|
|
93
|
+
font-size: 18px;
|
|
94
|
+
font-weight: 700;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.card h3 {
|
|
98
|
+
margin: 0 0 12px;
|
|
99
|
+
font-size: 16px;
|
|
100
|
+
font-weight: 600;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.card p {
|
|
104
|
+
margin: 0 0 12px;
|
|
105
|
+
color: #536471;
|
|
106
|
+
line-height: 1.5;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.card p:last-child {
|
|
110
|
+
margin-bottom: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.list {
|
|
114
|
+
margin: 0;
|
|
115
|
+
padding-left: 20px;
|
|
116
|
+
color: #536471;
|
|
117
|
+
line-height: 1.8;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.backLink {
|
|
121
|
+
display: inline-flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 8px;
|
|
124
|
+
margin-top: auto;
|
|
125
|
+
padding: 12px 20px;
|
|
126
|
+
background: #1d9bf0;
|
|
127
|
+
color: #fff;
|
|
128
|
+
border-radius: 9999px;
|
|
129
|
+
text-decoration: none;
|
|
130
|
+
font-weight: 600;
|
|
131
|
+
transition: background 0.15s ease;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.backLink:hover {
|
|
135
|
+
background: #1a8cd8;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Drawer content */
|
|
139
|
+
.drawerContent {
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
height: 100%;
|
|
143
|
+
padding: 16px;
|
|
144
|
+
background: #fff;
|
|
145
|
+
border-right: 1px solid #eff3f4;
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
-webkit-overflow-scrolling: touch;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.profile {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: 12px;
|
|
154
|
+
padding: 12px 0;
|
|
155
|
+
border-bottom: 1px solid #eff3f4;
|
|
156
|
+
margin-bottom: 8px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.avatar {
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
justify-content: center;
|
|
163
|
+
width: 40px;
|
|
164
|
+
height: 40px;
|
|
165
|
+
background: #1d9bf0;
|
|
166
|
+
border-radius: 50%;
|
|
167
|
+
color: #fff;
|
|
168
|
+
font-size: 18px;
|
|
169
|
+
font-weight: 700;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.profileInfo {
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-direction: column;
|
|
175
|
+
gap: 2px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.profileName {
|
|
179
|
+
font-size: 15px;
|
|
180
|
+
font-weight: 700;
|
|
181
|
+
color: #0f1419;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.profileHandle {
|
|
185
|
+
font-size: 13px;
|
|
186
|
+
color: #536471;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.nav {
|
|
190
|
+
display: flex;
|
|
191
|
+
flex-direction: column;
|
|
192
|
+
gap: 4px;
|
|
193
|
+
flex: 1;
|
|
194
|
+
padding: 8px 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.navItem {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 16px;
|
|
201
|
+
padding: 12px;
|
|
202
|
+
border-radius: 9999px;
|
|
203
|
+
color: #0f1419;
|
|
204
|
+
font-size: 20px;
|
|
205
|
+
font-weight: 500;
|
|
206
|
+
text-decoration: none;
|
|
207
|
+
transition: background 0.2s ease;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.navItem:hover {
|
|
211
|
+
background: #e7e7e8;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.navIcon {
|
|
215
|
+
width: 26px;
|
|
216
|
+
height: 26px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.closeButton {
|
|
220
|
+
padding: 14px;
|
|
221
|
+
background: #eff3f4;
|
|
222
|
+
border: none;
|
|
223
|
+
border-radius: 9999px;
|
|
224
|
+
font-size: 15px;
|
|
225
|
+
font-weight: 700;
|
|
226
|
+
color: #0f1419;
|
|
227
|
+
cursor: pointer;
|
|
228
|
+
transition: background 0.15s ease;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.closeButton:hover {
|
|
232
|
+
background: #d7dbdc;
|
|
233
|
+
}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* These tests focus on the panel visibility and depth calculation logic,
|
|
5
5
|
* particularly around the exitingPanelId handling during rapid navigation.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// Empty export to make this file a module (prevents global scope pollution)
|
|
9
|
+
export {};
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Pure function version of the isExiting logic for testing.
|
|
@@ -83,69 +85,71 @@ describe("StackBasics panel calculation logic", () => {
|
|
|
83
85
|
|
|
84
86
|
describe("rapid navigation scenarios", () => {
|
|
85
87
|
it("handles push → back → push sequence correctly", () => {
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Now detail should NOT be exiting because it's in the stack
|
|
110
|
-
|
|
111
|
-
|
|
88
|
+
// Helper function to compute stack state for each step
|
|
89
|
+
const computeStackState = (step: number) => {
|
|
90
|
+
if (step === 0) {
|
|
91
|
+
// Initial: list active
|
|
92
|
+
return { stack: ["list"] as readonly string[], depth: 0, exitingPanelId: null as string | null };
|
|
93
|
+
}
|
|
94
|
+
if (step === 1) {
|
|
95
|
+
// Push detail
|
|
96
|
+
return { stack: ["list", "detail"] as readonly string[], depth: 1, exitingPanelId: null as string | null };
|
|
97
|
+
}
|
|
98
|
+
if (step === 2) {
|
|
99
|
+
// Back to list (detail becomes exiting)
|
|
100
|
+
return { stack: ["list"] as readonly string[], depth: 0, exitingPanelId: "detail" };
|
|
101
|
+
}
|
|
102
|
+
// step === 3: Push detail again BEFORE exitingPanelId clears
|
|
103
|
+
return { stack: ["list", "detail"] as readonly string[], depth: 1, exitingPanelId: "detail" };
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Verify detail is exiting at step 2
|
|
107
|
+
const step2 = computeStackState(2);
|
|
108
|
+
const isExitingStep2 = computeIsExiting("detail", step2.exitingPanelId, step2.stack);
|
|
109
|
+
expect(isExitingStep2).toBe(true);
|
|
110
|
+
|
|
111
|
+
// Now detail should NOT be exiting because it's in the stack at step 3
|
|
112
|
+
const step3 = computeStackState(3);
|
|
113
|
+
const isExitingStep3 = computeIsExiting("detail", step3.exitingPanelId, step3.stack);
|
|
114
|
+
expect(isExitingStep3).toBe(false);
|
|
112
115
|
|
|
113
116
|
// Panel depth should be correct
|
|
114
|
-
const panelDepth = computePanelDepth("detail",
|
|
117
|
+
const panelDepth = computePanelDepth("detail", isExitingStep3, step3.stack, step3.depth);
|
|
115
118
|
expect(panelDepth).toBe(1); // Not 2!
|
|
116
119
|
});
|
|
117
120
|
|
|
118
121
|
it("handles deep navigation with rapid back operations", () => {
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
// Helper function to compute stack state for each step
|
|
123
|
+
const computeDeepNavState = (step: number) => {
|
|
124
|
+
if (step === 0) {
|
|
125
|
+
// Initial: list → detail → edit
|
|
126
|
+
return { stack: ["list", "detail", "edit"] as readonly string[], depth: 2, exitingPanelId: null as string | null };
|
|
127
|
+
}
|
|
128
|
+
if (step === 1) {
|
|
129
|
+
// Back to detail (edit becomes exiting)
|
|
130
|
+
return { stack: ["list", "detail"] as readonly string[], depth: 1, exitingPanelId: "edit" };
|
|
131
|
+
}
|
|
132
|
+
if (step === 2) {
|
|
133
|
+
// Back to list immediately (detail becomes exiting)
|
|
134
|
+
return { stack: ["list"] as readonly string[], depth: 0, exitingPanelId: "detail" };
|
|
135
|
+
}
|
|
136
|
+
// step === 3: Push to detail again
|
|
137
|
+
return { stack: ["list", "detail"] as readonly string[], depth: 1, exitingPanelId: "detail" };
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const step1 = computeDeepNavState(1);
|
|
141
|
+
const editIsExiting = computeIsExiting("edit", step1.exitingPanelId, step1.stack);
|
|
130
142
|
expect(editIsExiting).toBe(true);
|
|
131
143
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
depth = 0;
|
|
135
|
-
exitingPanelId = "detail"; // Updated to detail
|
|
136
|
-
|
|
137
|
-
const detailIsExiting = computeIsExiting("detail", exitingPanelId, stack);
|
|
144
|
+
const step2 = computeDeepNavState(2);
|
|
145
|
+
const detailIsExiting = computeIsExiting("detail", step2.exitingPanelId, step2.stack);
|
|
138
146
|
expect(detailIsExiting).toBe(true);
|
|
139
147
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
depth = 1;
|
|
143
|
-
// exitingPanelId still "detail"
|
|
144
|
-
|
|
145
|
-
const detailIsExitingAfterPush = computeIsExiting("detail", exitingPanelId, stack);
|
|
148
|
+
const step3 = computeDeepNavState(3);
|
|
149
|
+
const detailIsExitingAfterPush = computeIsExiting("detail", step3.exitingPanelId, step3.stack);
|
|
146
150
|
expect(detailIsExitingAfterPush).toBe(false);
|
|
147
151
|
|
|
148
|
-
const detailDepth = computePanelDepth("detail", detailIsExitingAfterPush, stack, depth);
|
|
152
|
+
const detailDepth = computePanelDepth("detail", detailIsExitingAfterPush, step3.stack, step3.depth);
|
|
149
153
|
expect(detailDepth).toBe(1);
|
|
150
154
|
});
|
|
151
155
|
});
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* These tests focus on the panel visibility and depth calculation logic,
|
|
5
5
|
* particularly around the exitingPanelId handling during rapid navigation.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// Empty export to make this file a module (prevents global scope pollution)
|
|
9
|
+
export {};
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Pure function version of the isExiting logic for testing.
|
|
@@ -53,67 +55,55 @@ describe("StackTablet panel calculation logic", () => {
|
|
|
53
55
|
|
|
54
56
|
describe("rapid navigation scenarios", () => {
|
|
55
57
|
it("handles push → back → push sequence correctly", () => {
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
stack = ["root", "general"];
|
|
72
|
-
depth = 1;
|
|
73
|
-
// exitingPanelId is still "general" (timeout hasn't fired yet)
|
|
58
|
+
// Helper function to compute stack state for the final step
|
|
59
|
+
const computeFinalState = () => {
|
|
60
|
+
// Steps:
|
|
61
|
+
// 1. Initial: root active (stack: ["root"], depth: 0, exitingPanelId: null)
|
|
62
|
+
// 2. Push general (stack: ["root", "general"], depth: 1)
|
|
63
|
+
// 3. Back to root (stack: ["root"], depth: 0, exitingPanelId: "general")
|
|
64
|
+
// 4. Push general again BEFORE exitingPanelId clears
|
|
65
|
+
return {
|
|
66
|
+
stack: ["root", "general"] as readonly string[],
|
|
67
|
+
depth: 1,
|
|
68
|
+
exitingPanelId: "general",
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const finalState = computeFinalState();
|
|
74
73
|
|
|
75
74
|
// Now general should NOT be exiting because it's in the stack
|
|
76
|
-
const isExiting = computeIsExiting("general", exitingPanelId, stack);
|
|
75
|
+
const isExiting = computeIsExiting("general", finalState.exitingPanelId, finalState.stack);
|
|
77
76
|
expect(isExiting).toBe(false);
|
|
78
77
|
|
|
79
78
|
// Panel depth should be correct
|
|
80
|
-
const panelDepth = computePanelDepth("general", isExiting, stack, depth);
|
|
79
|
+
const panelDepth = computePanelDepth("general", isExiting, finalState.stack, finalState.depth);
|
|
81
80
|
expect(panelDepth).toBe(1); // Not 2!
|
|
82
81
|
});
|
|
83
82
|
|
|
84
83
|
it("handles Settings menu rapid navigation", () => {
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// Immediately back to root
|
|
104
|
-
stack = ["root"];
|
|
105
|
-
depth = 0;
|
|
106
|
-
exitingPanelId = "general";
|
|
107
|
-
|
|
108
|
-
// Immediately push General again
|
|
109
|
-
stack = ["root", "general"];
|
|
110
|
-
depth = 1;
|
|
84
|
+
// Helper function to compute final state after rapid navigation
|
|
85
|
+
const computeSettingsNavState = () => {
|
|
86
|
+
// Steps:
|
|
87
|
+
// 1. Initial: root (stack: ["root"], depth: 0)
|
|
88
|
+
// 2. Click "General" (stack: ["root", "general"], depth: 1)
|
|
89
|
+
// 3. Click "About" (stack: ["root", "general", "about"], depth: 2)
|
|
90
|
+
// 4. Back to General (stack: ["root", "general"], depth: 1, exitingPanelId: "about")
|
|
91
|
+
// 5. Immediately back to root (stack: ["root"], depth: 0, exitingPanelId: "general")
|
|
92
|
+
// 6. Immediately push General again
|
|
93
|
+
return {
|
|
94
|
+
stack: ["root", "general"] as readonly string[],
|
|
95
|
+
depth: 1,
|
|
96
|
+
exitingPanelId: "general",
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const finalState = computeSettingsNavState();
|
|
111
101
|
|
|
112
102
|
// general should NOT be exiting
|
|
113
|
-
const isExiting = computeIsExiting("general", exitingPanelId, stack);
|
|
103
|
+
const isExiting = computeIsExiting("general", finalState.exitingPanelId, finalState.stack);
|
|
114
104
|
expect(isExiting).toBe(false);
|
|
115
105
|
|
|
116
|
-
const panelDepth = computePanelDepth("general", isExiting, stack, depth);
|
|
106
|
+
const panelDepth = computePanelDepth("general", isExiting, finalState.stack, finalState.depth);
|
|
117
107
|
expect(panelDepth).toBe(1);
|
|
118
108
|
});
|
|
119
109
|
});
|
package/src/demo/routes.tsx
CHANGED
|
@@ -28,6 +28,7 @@ import DR_Basics from "./pages/Drawer/basics";
|
|
|
28
28
|
import DR_Menu from "./pages/Drawer/menu";
|
|
29
29
|
import DR_Animations from "./pages/Drawer/animations";
|
|
30
30
|
import DR_Swipe from "./pages/Drawer/swipe";
|
|
31
|
+
import DR_Reveal from "./pages/Drawer/reveal";
|
|
31
32
|
|
|
32
33
|
import DL_Modal from "./pages/Dialog/modal";
|
|
33
34
|
import DL_Alerts from "./pages/Dialog/alerts";
|
|
@@ -93,6 +94,7 @@ export const demoCategories: DemoCategory[] = [
|
|
|
93
94
|
{ id: "menu", label: "Menu", path: "menu", element: <DR_Menu /> },
|
|
94
95
|
{ id: "animations", label: "Animations", path: "animations", element: <DR_Animations /> },
|
|
95
96
|
{ id: "swipe", label: "Swipe Gestures", path: "swipe", element: <DR_Swipe /> },
|
|
97
|
+
{ id: "reveal", label: "Reveal Mode", path: "reveal", element: <DR_Reveal /> },
|
|
96
98
|
],
|
|
97
99
|
},
|
|
98
100
|
{
|
|
@@ -12,13 +12,21 @@ type TestPointerEvent = React.PointerEvent<HTMLElement> & {
|
|
|
12
12
|
wasDefaultPrevented: () => boolean;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Create a mock view for test events.
|
|
17
|
+
*/
|
|
18
|
+
function createMockView(): React.PointerEvent<HTMLElement>["view"] {
|
|
19
|
+
// eslint-disable-next-line custom/no-as-outside-guard -- test helper for view casting
|
|
20
|
+
return window as unknown as React.PointerEvent<HTMLElement>["view"];
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* Creates a mock PointerEvent that satisfies the React.PointerEvent interface.
|
|
17
25
|
*/
|
|
18
26
|
function createMockPointerEvent(props: {
|
|
19
27
|
clientX: number;
|
|
20
28
|
clientY: number;
|
|
21
|
-
pointerType:
|
|
29
|
+
pointerType: "mouse" | "touch" | "pen";
|
|
22
30
|
}): TestPointerEvent {
|
|
23
31
|
const noop = (): void => {};
|
|
24
32
|
const noopBool = (): boolean => false;
|
|
@@ -82,7 +90,7 @@ function createMockPointerEvent(props: {
|
|
|
82
90
|
width: 1,
|
|
83
91
|
// UIEvent properties
|
|
84
92
|
detail: 0,
|
|
85
|
-
view:
|
|
93
|
+
view: createMockView(),
|
|
86
94
|
};
|
|
87
95
|
}
|
|
88
96
|
|