react-panel-layout 0.6.0 → 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/{FloatingPanelFrame-SgYLc6Ud.js → FloatingPanelFrame-3eU9AwPo.js} +2 -2
- package/dist/{FloatingPanelFrame-SgYLc6Ud.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
- 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-BltqeCPK.js.map → GridLayout-B4aCqSyd.js.map} +1 -1
- package/dist/GridLayout-DNOClFzz.cjs +2 -0
- package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DNOClFzz.cjs.map} +1 -1
- package/dist/{HorizontalDivider-WF1k_qND.js → HorizontalDivider-DdxzfV0l.js} +3 -3
- package/dist/{HorizontalDivider-WF1k_qND.js.map → HorizontalDivider-DdxzfV0l.js.map} +1 -1
- package/dist/{HorizontalDivider-B5Z-KZLk.cjs → HorizontalDivider-_pgV4Mcv.cjs} +2 -2
- package/dist/{HorizontalDivider-B5Z-KZLk.cjs.map → HorizontalDivider-_pgV4Mcv.cjs.map} +1 -1
- package/dist/PanelSystem-B8Igvnb2.cjs +3 -0
- package/dist/PanelSystem-B8Igvnb2.cjs.map +1 -0
- package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-DDUSFjXD.js} +209 -248
- package/dist/PanelSystem-DDUSFjXD.js.map +1 -0
- package/dist/ResizeHandle-CBcAS918.cjs +2 -0
- package/dist/{ResizeHandle-CScipO5l.cjs.map → ResizeHandle-CBcAS918.cjs.map} +1 -1
- package/dist/{ResizeHandle-CdA_JYfN.js → ResizeHandle-CXjc1meV.js} +28 -29
- package/dist/{ResizeHandle-CdA_JYfN.js.map → ResizeHandle-CXjc1meV.js.map} +1 -1
- package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
- package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
- package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
- package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
- package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
- package/dist/components/window/Drawer.d.ts +4 -1
- package/dist/components/window/DrawerLayers.d.ts +1 -1
- 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 +74 -0
- package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
- package/dist/components/window/useDrawerSwipeTransform.d.ts +29 -0
- 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 +9 -8
- package/dist/config.js.map +1 -1
- package/dist/constants/styles.d.ts +17 -0
- package/dist/dialog/index.d.ts +69 -0
- package/dist/floating.js +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.js +2 -2
- package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +7 -0
- package/dist/hooks/gesture/types.d.ts +48 -5
- package/dist/hooks/gesture/utils.d.ts +19 -0
- package/dist/hooks/useAnimationFrame.d.ts +2 -0
- package/dist/hooks/useOperationContinuity.d.ts +64 -0
- package/dist/hooks/useResizeObserver.d.ts +33 -1
- package/dist/hooks/useSharedElementTransition.d.ts +112 -0
- package/dist/hooks/useSwipeContentTransform.d.ts +9 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +7 -7
- package/dist/modules/dialog/AlertDialog.d.ts +9 -0
- package/dist/modules/dialog/DialogContainer.d.ts +37 -0
- package/dist/modules/dialog/Modal.d.ts +26 -0
- package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
- package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
- package/dist/modules/dialog/types.d.ts +183 -0
- package/dist/modules/dialog/useDialog.d.ts +39 -0
- package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
- package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
- package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
- 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/modules/drawer/types.d.ts +74 -0
- package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
- package/dist/modules/pivot/SwipePivotTabBar.d.ts +3 -0
- package/dist/modules/stack/SwipeStackContent.d.ts +6 -3
- package/dist/modules/stack/SwipeStackOutlet.d.ts +4 -4
- package/dist/modules/stack/computeSwipeStackTransform.d.ts +1 -1
- package/dist/panels.cjs +1 -1
- package/dist/panels.js +1 -1
- package/dist/pivot.cjs +1 -1
- package/dist/pivot.js +1 -1
- package/dist/resizer.cjs +1 -1
- package/dist/resizer.js +2 -2
- package/dist/stack.cjs +1 -1
- package/dist/stack.cjs.map +1 -1
- package/dist/stack.js +480 -780
- package/dist/stack.js.map +1 -1
- package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
- package/dist/sticky-header.cjs +1 -1
- package/dist/sticky-header.cjs.map +1 -1
- package/dist/sticky-header.js +59 -51
- package/dist/sticky-header.js.map +1 -1
- package/dist/{styles-DPPuJ0sf.js → styles-NkjuMOVS.js} +13 -13
- package/dist/{styles-DPPuJ0sf.js.map → styles-NkjuMOVS.js.map} +1 -1
- package/dist/styles-qf6ptVLD.cjs.map +1 -1
- package/dist/types.d.ts +30 -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/useDocumentPointerEvents-DXxw3qWj.js +54 -0
- package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
- package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
- package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
- package/dist/window/index.d.ts +2 -0
- package/dist/window.cjs +1 -1
- package/dist/window.cjs.map +1 -1
- package/dist/window.js +114 -103
- package/dist/window.js.map +1 -1
- package/package.json +6 -1
- package/src/components/gesture/SwipeSafeZone.tsx +70 -0
- package/src/components/grid/GridLayout.tsx +110 -38
- package/src/components/window/Drawer.tsx +353 -162
- package/src/components/window/DrawerLayers.tsx +54 -11
- 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 +302 -0
- package/src/components/window/drawerStyles.ts +252 -0
- package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
- package/src/components/window/drawerSwipeConfig.ts +112 -0
- package/src/components/window/useDrawerSwipeTransform.ts +67 -0
- 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/constants/styles.ts +19 -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/alerts/index.tsx +22 -0
- package/src/demo/pages/Dialog/card/index.tsx +22 -0
- package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +219 -0
- package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
- package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
- package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
- package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
- package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
- package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
- package/src/demo/pages/Dialog/modal/index.tsx +17 -0
- package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
- 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/components/DrawerSwipe.module.css +316 -0
- package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -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/Drawer/swipe/index.tsx +17 -0
- package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +54 -23
- package/src/demo/pages/Pivot/swipe-debug/index.tsx +1 -1
- package/src/demo/pages/Stack/components/StackBasics.spec.tsx +156 -0
- package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
- package/src/demo/pages/Stack/components/StackTablet.spec.tsx +110 -0
- package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
- package/src/demo/routes.tsx +24 -1
- package/src/dialog/index.ts +85 -0
- package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +68 -64
- package/src/hooks/gesture/testing/createGestureSimulator.ts +113 -37
- package/src/hooks/gesture/types.ts +83 -6
- package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +22 -14
- package/src/hooks/gesture/useNativeGestureGuard.spec.ts +99 -31
- package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
- package/src/hooks/gesture/utils.ts +102 -0
- package/src/hooks/useAnimatedVisibility.spec.ts +44 -24
- package/src/hooks/useAnimatedVisibility.ts +28 -2
- package/src/hooks/useAnimationFrame.ts +8 -0
- package/src/hooks/useOperationContinuity.spec.ts +394 -0
- package/src/hooks/useOperationContinuity.ts +135 -0
- package/src/hooks/useResizeObserver.spec.tsx +277 -0
- package/src/hooks/useResizeObserver.tsx +108 -39
- package/src/hooks/useScrollContainer.ts +4 -10
- package/src/hooks/useSharedElementTransition.ts +354 -0
- package/src/hooks/useSwipeContentTransform.spec.ts +18 -18
- package/src/hooks/useSwipeContentTransform.ts +166 -28
- package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
- package/src/modules/dialog/AlertDialog.tsx +221 -0
- package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
- package/src/modules/dialog/DialogContainer.tsx +188 -0
- package/src/modules/dialog/Modal.spec.tsx +220 -0
- package/src/modules/dialog/Modal.tsx +182 -0
- package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
- package/src/modules/dialog/dialogAnimationUtils.spec.ts +252 -0
- package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
- package/src/modules/dialog/types.ts +186 -0
- package/src/modules/dialog/useDialog.spec.tsx +447 -0
- package/src/modules/dialog/useDialog.ts +214 -0
- package/src/modules/dialog/useDialogContainer.spec.ts +339 -0
- package/src/modules/dialog/useDialogContainer.ts +150 -0
- package/src/modules/dialog/useDialogSwipeInput.spec.ts +178 -0
- package/src/modules/dialog/useDialogSwipeInput.ts +350 -0
- package/src/modules/dialog/useDialogTransform.spec.ts +403 -0
- package/src/modules/dialog/useDialogTransform.ts +407 -0
- 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/types.ts +102 -0
- package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
- package/src/modules/drawer/useDrawerSwipeInput.ts +402 -0
- package/src/modules/panels/rendering/ContentRegistry.spec.tsx +21 -14
- package/src/modules/pivot/SwipePivotContent.position.spec.tsx +12 -8
- package/src/modules/pivot/SwipePivotContent.spec.tsx +66 -25
- package/src/modules/pivot/SwipePivotContent.tsx +2 -2
- package/src/modules/pivot/SwipePivotTabBar.spec.tsx +85 -68
- package/src/modules/pivot/SwipePivotTabBar.tsx +75 -15
- package/src/modules/pivot/scaleInputState.spec.ts +11 -2
- package/src/modules/pivot/usePivot.spec.ts +17 -3
- package/src/modules/pivot/usePivotSwipeInput.spec.ts +182 -123
- package/src/modules/stack/SwipeStackContent.spec.tsx +387 -100
- package/src/modules/stack/SwipeStackContent.tsx +43 -33
- package/src/modules/stack/SwipeStackOutlet.spec.tsx +14 -16
- package/src/modules/stack/SwipeStackOutlet.tsx +6 -6
- package/src/modules/stack/computeSwipeStackTransform.spec.ts +5 -5
- package/src/modules/stack/computeSwipeStackTransform.ts +3 -3
- package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
- package/src/modules/stack/useStackAnimationState.spec.ts +3 -1
- package/src/modules/stack/useStackAnimationState.ts +18 -13
- package/src/modules/stack/useStackNavigation.spec.ts +198 -3
- package/src/modules/stack/useStackNavigation.tsx +113 -56
- package/src/modules/stack/useStackSwipeInput.spec.ts +65 -32
- package/src/modules/stack/useStackSwipeInput.ts +1 -1
- package/src/sticky-header/StickyArea.tsx +29 -57
- package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
- package/src/sticky-header/calculateStickyMetrics.ts +50 -0
- package/src/types.ts +33 -0
- package/src/window/index.ts +2 -0
- package/dist/FloatingWindow-BpdOpg_L.js +0 -400
- package/dist/FloatingWindow-BpdOpg_L.js.map +0 -1
- package/dist/FloatingWindow-TCDNY5gE.cjs +0 -2
- package/dist/FloatingWindow-TCDNY5gE.cjs.map +0 -1
- package/dist/GridLayout-B4VRsC0r.cjs +0 -2
- package/dist/GridLayout-BltqeCPK.js +0 -927
- package/dist/PanelSystem-Bs8bQwQF.cjs +0 -3
- package/dist/PanelSystem-Bs8bQwQF.cjs.map +0 -1
- package/dist/PanelSystem-Dr1TBhxM.js.map +0 -1
- package/dist/ResizeHandle-CScipO5l.cjs +0 -2
- package/dist/SwipePivotTabBar-BGO9X94m.js +0 -407
- package/dist/SwipePivotTabBar-BGO9X94m.js.map +0 -1
- package/dist/SwipePivotTabBar-BrQismcZ.cjs +0 -2
- package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +0 -1
- package/dist/useDocumentPointerEvents-CKdhGXd0.js +0 -46
- package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +0 -1
- package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +0 -2
- package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +0 -1
- package/dist/useEffectEvent-Dp7HLCf0.js +0 -13
- package/dist/useEffectEvent-Dp7HLCf0.js.map +0 -1
- package/dist/useEffectEvent-huSsGUnl.cjs +0 -2
- package/dist/useEffectEvent-huSsGUnl.cjs.map +0 -1
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Stack Navigation demo - iOS-style hierarchical navigation
|
|
3
|
+
* Uses SwipeStackContent for direct DOM manipulation during swipe gestures.
|
|
3
4
|
*/
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import { useStackNavigation } from "../../../../modules/stack/useStackNavigation.js";
|
|
6
7
|
import { useStackSwipeInput } from "../../../../modules/stack/useStackSwipeInput.js";
|
|
7
|
-
import {
|
|
8
|
+
import { SwipeStackContent } from "../../../../modules/stack/SwipeStackContent.js";
|
|
9
|
+
import { useResizeObserver } from "../../../../hooks/useResizeObserver.js";
|
|
10
|
+
import { toContinuousOperationState } from "../../../../hooks/gesture/types.js";
|
|
8
11
|
import type { StackPanel } from "../../../../modules/stack/types.js";
|
|
9
12
|
import styles from "./Stack.module.css";
|
|
10
13
|
|
|
14
|
+
const ANIMATION_DURATION = 300;
|
|
15
|
+
|
|
11
16
|
const panels: StackPanel[] = [
|
|
12
17
|
{
|
|
13
18
|
id: "list",
|
|
@@ -61,16 +66,78 @@ export const StackBasics: React.FC = () => {
|
|
|
61
66
|
const navigation = useStackNavigation({
|
|
62
67
|
panels,
|
|
63
68
|
displayMode: "overlay",
|
|
64
|
-
transitionMode: "
|
|
69
|
+
transitionMode: "none", // Using direct DOM manipulation
|
|
65
70
|
});
|
|
66
71
|
|
|
67
|
-
const { isEdgeSwiping, progress, containerProps } = useStackSwipeInput({
|
|
72
|
+
const { isEdgeSwiping, progress, inputState, containerProps } = useStackSwipeInput({
|
|
68
73
|
containerRef,
|
|
69
74
|
navigation,
|
|
70
75
|
edge: "left",
|
|
71
76
|
edgeWidth: 30,
|
|
72
77
|
});
|
|
73
78
|
|
|
79
|
+
// Track container size for SwipeStackContent
|
|
80
|
+
const { rect } = useResizeObserver(containerRef, { box: "border-box" });
|
|
81
|
+
const containerSize = rect?.width ?? 0;
|
|
82
|
+
|
|
83
|
+
const { stack, depth } = navigation.state;
|
|
84
|
+
|
|
85
|
+
// Track exiting panel when navigating back.
|
|
86
|
+
// CRITICAL: exitingPanelId must be computed synchronously during render
|
|
87
|
+
// to prevent the exiting panel from being unmounted for a frame.
|
|
88
|
+
const prevDepthRef = React.useRef(depth);
|
|
89
|
+
const prevStackRef = React.useRef<ReadonlyArray<string>>(stack);
|
|
90
|
+
const exitingPanelClearTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
91
|
+
const [exitingPanelState, setExitingPanelState] = React.useState<string | null>(null);
|
|
92
|
+
|
|
93
|
+
// Compute exiting panel ID synchronously during render
|
|
94
|
+
const prevDepth = prevDepthRef.current;
|
|
95
|
+
const prevStack = prevStackRef.current;
|
|
96
|
+
const isNavigatingBack = depth < prevDepth;
|
|
97
|
+
const computedExitingId = isNavigatingBack ? (prevStack[prevDepth] ?? null) : null;
|
|
98
|
+
const exitingPanelId = computedExitingId ?? exitingPanelState;
|
|
99
|
+
|
|
100
|
+
// Update refs and state in effect
|
|
101
|
+
React.useLayoutEffect(() => {
|
|
102
|
+
prevDepthRef.current = depth;
|
|
103
|
+
prevStackRef.current = stack;
|
|
104
|
+
|
|
105
|
+
if (computedExitingId != null) {
|
|
106
|
+
setExitingPanelState(computedExitingId);
|
|
107
|
+
|
|
108
|
+
if (exitingPanelClearTimeoutRef.current != null) {
|
|
109
|
+
clearTimeout(exitingPanelClearTimeoutRef.current);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
exitingPanelClearTimeoutRef.current = setTimeout(() => {
|
|
113
|
+
setExitingPanelState(null);
|
|
114
|
+
exitingPanelClearTimeoutRef.current = null;
|
|
115
|
+
}, ANIMATION_DURATION);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
if (exitingPanelClearTimeoutRef.current != null) {
|
|
120
|
+
clearTimeout(exitingPanelClearTimeoutRef.current);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}, [depth, stack, computedExitingId]);
|
|
124
|
+
|
|
125
|
+
// Get visible panel IDs: active + behind (for swipe reveal) + exiting (for back animation)
|
|
126
|
+
const visiblePanelIds = React.useMemo(() => {
|
|
127
|
+
const ids: string[] = [];
|
|
128
|
+
// Behind panel (if exists)
|
|
129
|
+
if (depth > 0) {
|
|
130
|
+
ids.push(stack[depth - 1]);
|
|
131
|
+
}
|
|
132
|
+
// Active panel
|
|
133
|
+
ids.push(stack[depth]);
|
|
134
|
+
// Include exiting panel if not already in the list
|
|
135
|
+
if (exitingPanelId != null && !ids.includes(exitingPanelId)) {
|
|
136
|
+
ids.push(exitingPanelId);
|
|
137
|
+
}
|
|
138
|
+
return ids;
|
|
139
|
+
}, [stack, depth, exitingPanelId]);
|
|
140
|
+
|
|
74
141
|
const handleItemClick = (item: typeof listItems[0]) => {
|
|
75
142
|
setSelectedItem(item);
|
|
76
143
|
navigation.push("detail");
|
|
@@ -98,11 +165,93 @@ export const StackBasics: React.FC = () => {
|
|
|
98
165
|
const showBackButton = navigation.state.depth > 0;
|
|
99
166
|
const showEditButton = navigation.state.depth === 1;
|
|
100
167
|
|
|
168
|
+
const renderBackButton = (): React.ReactNode => {
|
|
169
|
+
if (!showBackButton) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return <BackButton onClick={backButtonProps.onClick} disabled={backButtonProps.disabled} />;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const renderPanelContent = (panelId: string): React.ReactNode => {
|
|
176
|
+
if (panelId === "list") {
|
|
177
|
+
return (
|
|
178
|
+
<div className={styles.panel}>
|
|
179
|
+
<ul className={styles.list}>
|
|
180
|
+
{listItems.map((item) => (
|
|
181
|
+
<li key={item.id} className={styles.listItem}>
|
|
182
|
+
<button
|
|
183
|
+
className={styles.listItemButton}
|
|
184
|
+
onClick={() => handleItemClick(item)}
|
|
185
|
+
>
|
|
186
|
+
<span className={styles.listItemName}>{item.name}</span>
|
|
187
|
+
<span className={styles.listItemDesc}>{item.description}</span>
|
|
188
|
+
<span className={styles.chevron}>→</span>
|
|
189
|
+
</button>
|
|
190
|
+
</li>
|
|
191
|
+
))}
|
|
192
|
+
</ul>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (panelId === "detail") {
|
|
198
|
+
return (
|
|
199
|
+
<div className={styles.panel}>
|
|
200
|
+
<div className={styles.detailContent}>
|
|
201
|
+
<h2>{selectedItem?.name}</h2>
|
|
202
|
+
<p>{selectedItem?.description}</p>
|
|
203
|
+
<div className={styles.detailMeta}>
|
|
204
|
+
<span>ID: {selectedItem?.id}</span>
|
|
205
|
+
</div>
|
|
206
|
+
<p className={styles.hint}>
|
|
207
|
+
Swipe from the left edge to go back, or tap the Back button.
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (panelId === "edit") {
|
|
215
|
+
return (
|
|
216
|
+
<div className={styles.panel}>
|
|
217
|
+
<div className={styles.editContent}>
|
|
218
|
+
<h2>Edit {selectedItem?.name}</h2>
|
|
219
|
+
<div className={styles.form}>
|
|
220
|
+
<label className={styles.label}>
|
|
221
|
+
Name
|
|
222
|
+
<input
|
|
223
|
+
type="text"
|
|
224
|
+
className={styles.input}
|
|
225
|
+
defaultValue={selectedItem?.name}
|
|
226
|
+
/>
|
|
227
|
+
</label>
|
|
228
|
+
<label className={styles.label}>
|
|
229
|
+
Description
|
|
230
|
+
<textarea
|
|
231
|
+
className={styles.textarea}
|
|
232
|
+
defaultValue={selectedItem?.description}
|
|
233
|
+
/>
|
|
234
|
+
</label>
|
|
235
|
+
</div>
|
|
236
|
+
<button
|
|
237
|
+
className={styles.saveButton}
|
|
238
|
+
onClick={() => navigation.go(-1)}
|
|
239
|
+
>
|
|
240
|
+
Save Changes
|
|
241
|
+
</button>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return null;
|
|
248
|
+
};
|
|
249
|
+
|
|
101
250
|
return (
|
|
102
251
|
<div className={styles.container}>
|
|
103
252
|
{/* Header */}
|
|
104
253
|
<header className={styles.header}>
|
|
105
|
-
{
|
|
254
|
+
{renderBackButton()}
|
|
106
255
|
<h1 className={styles.title}>{getHeaderTitle()}</h1>
|
|
107
256
|
{showEditButton ? <EditButton onClick={handleEdit} /> : null}
|
|
108
257
|
</header>
|
|
@@ -113,97 +262,32 @@ export const StackBasics: React.FC = () => {
|
|
|
113
262
|
className={styles.stackContainer}
|
|
114
263
|
{...containerProps}
|
|
115
264
|
>
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
</StackContent>
|
|
143
|
-
|
|
144
|
-
{/* Detail Panel */}
|
|
145
|
-
<StackContent
|
|
146
|
-
id="detail"
|
|
147
|
-
depth={1}
|
|
148
|
-
isActive={navigation.currentPanelId === "detail"}
|
|
149
|
-
displayMode="overlay"
|
|
150
|
-
transitionMode="css"
|
|
151
|
-
navigationState={navigation.state}
|
|
152
|
-
swipeProgress={navigation.currentPanelId === "detail" ? progress : undefined}
|
|
153
|
-
>
|
|
154
|
-
<div className={styles.panel}>
|
|
155
|
-
<div className={styles.detailContent}>
|
|
156
|
-
<h2>{selectedItem?.name}</h2>
|
|
157
|
-
<p>{selectedItem?.description}</p>
|
|
158
|
-
<div className={styles.detailMeta}>
|
|
159
|
-
<span>ID: {selectedItem?.id}</span>
|
|
160
|
-
</div>
|
|
161
|
-
<p className={styles.hint}>
|
|
162
|
-
Swipe from the left edge to go back, or tap the Back button.
|
|
163
|
-
</p>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
166
|
-
</StackContent>
|
|
167
|
-
|
|
168
|
-
{/* Edit Panel */}
|
|
169
|
-
<StackContent
|
|
170
|
-
id="edit"
|
|
171
|
-
depth={2}
|
|
172
|
-
isActive={navigation.currentPanelId === "edit"}
|
|
173
|
-
displayMode="overlay"
|
|
174
|
-
transitionMode="css"
|
|
175
|
-
navigationState={navigation.state}
|
|
176
|
-
swipeProgress={navigation.currentPanelId === "edit" ? progress : undefined}
|
|
177
|
-
>
|
|
178
|
-
<div className={styles.panel}>
|
|
179
|
-
<div className={styles.editContent}>
|
|
180
|
-
<h2>Edit {selectedItem?.name}</h2>
|
|
181
|
-
<div className={styles.form}>
|
|
182
|
-
<label className={styles.label}>
|
|
183
|
-
Name
|
|
184
|
-
<input
|
|
185
|
-
type="text"
|
|
186
|
-
className={styles.input}
|
|
187
|
-
defaultValue={selectedItem?.name}
|
|
188
|
-
/>
|
|
189
|
-
</label>
|
|
190
|
-
<label className={styles.label}>
|
|
191
|
-
Description
|
|
192
|
-
<textarea
|
|
193
|
-
className={styles.textarea}
|
|
194
|
-
defaultValue={selectedItem?.description}
|
|
195
|
-
/>
|
|
196
|
-
</label>
|
|
197
|
-
</div>
|
|
198
|
-
<button
|
|
199
|
-
className={styles.saveButton}
|
|
200
|
-
onClick={() => navigation.go(-1)}
|
|
201
|
-
>
|
|
202
|
-
Save Changes
|
|
203
|
-
</button>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
</StackContent>
|
|
265
|
+
{visiblePanelIds.map((panelId) => {
|
|
266
|
+
// Panel is only "exiting" if it matches exitingPanelId AND is not in current stack
|
|
267
|
+
// This prevents the bug where a re-pushed panel is incorrectly treated as exiting
|
|
268
|
+
const isInCurrentStack = stack.includes(panelId);
|
|
269
|
+
const isExiting = panelId === exitingPanelId && !isInCurrentStack;
|
|
270
|
+
// For exiting panels, use depth + 1 since they were previously at the active position
|
|
271
|
+
const panelDepth = isExiting ? depth + 1 : stack.indexOf(panelId);
|
|
272
|
+
const isActive = panelDepth === depth && !isExiting;
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<SwipeStackContent
|
|
276
|
+
key={panelId}
|
|
277
|
+
id={panelId}
|
|
278
|
+
depth={panelDepth}
|
|
279
|
+
navigationDepth={depth}
|
|
280
|
+
isActive={isActive}
|
|
281
|
+
operationState={toContinuousOperationState(inputState)}
|
|
282
|
+
containerSize={containerSize}
|
|
283
|
+
animateOnMount={true}
|
|
284
|
+
animationDuration={ANIMATION_DURATION}
|
|
285
|
+
displayMode="overlay"
|
|
286
|
+
>
|
|
287
|
+
{renderPanelContent(panelId)}
|
|
288
|
+
</SwipeStackContent>
|
|
289
|
+
);
|
|
290
|
+
})}
|
|
207
291
|
</div>
|
|
208
292
|
|
|
209
293
|
{/* Debug info */}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests for StackTablet component logic.
|
|
3
|
+
*
|
|
4
|
+
* These tests focus on the panel visibility and depth calculation logic,
|
|
5
|
+
* particularly around the exitingPanelId handling during rapid navigation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Empty export to make this file a module (prevents global scope pollution)
|
|
9
|
+
export {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Pure function version of the isExiting logic for testing.
|
|
13
|
+
* This matches the logic in StackTablet.tsx.
|
|
14
|
+
*/
|
|
15
|
+
function computeIsExiting(
|
|
16
|
+
panelId: string,
|
|
17
|
+
exitingPanelId: string | null,
|
|
18
|
+
stack: readonly string[],
|
|
19
|
+
): boolean {
|
|
20
|
+
// FIX: Check if panel is in current stack - if so, it's not exiting
|
|
21
|
+
const isInCurrentStack = stack.includes(panelId);
|
|
22
|
+
return panelId === exitingPanelId && !isInCurrentStack;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pure function version of panelDepth calculation.
|
|
27
|
+
*/
|
|
28
|
+
function computePanelDepth(
|
|
29
|
+
panelId: string,
|
|
30
|
+
isExiting: boolean,
|
|
31
|
+
stack: readonly string[],
|
|
32
|
+
depth: number,
|
|
33
|
+
): number {
|
|
34
|
+
return isExiting ? depth + 1 : stack.indexOf(panelId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("StackTablet panel calculation logic", () => {
|
|
38
|
+
describe("isExiting calculation", () => {
|
|
39
|
+
it("returns false when panel is in current stack even if it matches exitingPanelId", () => {
|
|
40
|
+
// Bug scenario: user pushed panel again before exitingPanelId timeout cleared
|
|
41
|
+
const result = computeIsExiting("general", "general", ["root", "general"]);
|
|
42
|
+
expect(result).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("panelDepth calculation", () => {
|
|
47
|
+
it("returns correct depth for re-pushed panel (not exiting)", () => {
|
|
48
|
+
// Bug scenario: general was exiting but got re-pushed
|
|
49
|
+
const stack = ["root", "general"];
|
|
50
|
+
const isExiting = computeIsExiting("general", "general", stack);
|
|
51
|
+
const depth = computePanelDepth("general", isExiting, stack, 1);
|
|
52
|
+
expect(depth).toBe(1); // stack.indexOf("general") = 1, NOT depth + 1 = 2
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("rapid navigation scenarios", () => {
|
|
57
|
+
it("handles push → back → push sequence correctly", () => {
|
|
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();
|
|
73
|
+
|
|
74
|
+
// Now general should NOT be exiting because it's in the stack
|
|
75
|
+
const isExiting = computeIsExiting("general", finalState.exitingPanelId, finalState.stack);
|
|
76
|
+
expect(isExiting).toBe(false);
|
|
77
|
+
|
|
78
|
+
// Panel depth should be correct
|
|
79
|
+
const panelDepth = computePanelDepth("general", isExiting, finalState.stack, finalState.depth);
|
|
80
|
+
expect(panelDepth).toBe(1); // Not 2!
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("handles Settings menu rapid navigation", () => {
|
|
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();
|
|
101
|
+
|
|
102
|
+
// general should NOT be exiting
|
|
103
|
+
const isExiting = computeIsExiting("general", finalState.exitingPanelId, finalState.stack);
|
|
104
|
+
expect(isExiting).toBe(false);
|
|
105
|
+
|
|
106
|
+
const panelDepth = computePanelDepth("general", isExiting, finalState.stack, finalState.depth);
|
|
107
|
+
expect(panelDepth).toBe(1);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -8,6 +8,7 @@ import { useStackNavigation } from "../../../../modules/stack/useStackNavigation
|
|
|
8
8
|
import { useStackSwipeInput } from "../../../../modules/stack/useStackSwipeInput.js";
|
|
9
9
|
import { SwipeStackContent } from "../../../../modules/stack/SwipeStackContent.js";
|
|
10
10
|
import { useResizeObserver } from "../../../../hooks/useResizeObserver.js";
|
|
11
|
+
import { toContinuousOperationState } from "../../../../hooks/gesture/types.js";
|
|
11
12
|
import type { StackPanel } from "../../../../modules/stack/types.js";
|
|
12
13
|
import styles from "./StackTablet.module.css";
|
|
13
14
|
import "../../../styles/stack-themes.css";
|
|
@@ -271,36 +272,53 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
|
|
|
271
272
|
// Get visible panels: active + behind (for swipe reveal) + exiting (for back animation)
|
|
272
273
|
const { stack, depth } = navigation.state;
|
|
273
274
|
|
|
274
|
-
// Track exiting panel when navigating back
|
|
275
|
-
|
|
275
|
+
// Track exiting panel when navigating back.
|
|
276
|
+
// CRITICAL: exitingPanelId must be computed synchronously during render
|
|
277
|
+
// to prevent the exiting panel from being unmounted for a frame.
|
|
278
|
+
// If the panel unmounts, it loses its position state and causes a visual jump.
|
|
276
279
|
const prevDepthRef = React.useRef(depth);
|
|
277
280
|
const prevStackRef = React.useRef<ReadonlyArray<string>>(stack);
|
|
281
|
+
const exitingPanelClearTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
282
|
+
const [exitingPanelState, setExitingPanelState] = React.useState<string | null>(null);
|
|
278
283
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
// Compute exiting panel ID synchronously during render
|
|
285
|
+
// This ensures the panel stays in the render tree even on the navigation change render
|
|
286
|
+
const prevDepth = prevDepthRef.current;
|
|
287
|
+
const prevStack = prevStackRef.current;
|
|
288
|
+
const isNavigatingBack = depth < prevDepth;
|
|
289
|
+
const computedExitingId = isNavigatingBack ? (prevStack[prevDepth] ?? null) : null;
|
|
290
|
+
|
|
291
|
+
// Use computed value if navigating back, otherwise use state (for timeout-based cleanup)
|
|
292
|
+
const exitingPanelId = computedExitingId ?? exitingPanelState;
|
|
283
293
|
|
|
294
|
+
// Update refs and state in effect
|
|
295
|
+
React.useLayoutEffect(() => {
|
|
284
296
|
// Update refs
|
|
285
297
|
prevDepthRef.current = depth;
|
|
286
298
|
prevStackRef.current = stack;
|
|
287
299
|
|
|
288
|
-
//
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
const exitingId = prevStack[prevDepth];
|
|
292
|
-
if (exitingId != null) {
|
|
293
|
-
setExitingPanelId(exitingId);
|
|
294
|
-
|
|
295
|
-
// Clear exiting panel after animation completes
|
|
296
|
-
const timeoutId = setTimeout(() => {
|
|
297
|
-
setExitingPanelId(null);
|
|
298
|
-
}, THEME_DURATIONS[selectedTheme]);
|
|
300
|
+
// If we just started navigating back, update state and schedule cleanup
|
|
301
|
+
if (computedExitingId != null) {
|
|
302
|
+
setExitingPanelState(computedExitingId);
|
|
299
303
|
|
|
300
|
-
|
|
304
|
+
// Clear any existing timeout
|
|
305
|
+
if (exitingPanelClearTimeoutRef.current != null) {
|
|
306
|
+
clearTimeout(exitingPanelClearTimeoutRef.current);
|
|
301
307
|
}
|
|
308
|
+
|
|
309
|
+
// Clear exiting panel after animation completes
|
|
310
|
+
exitingPanelClearTimeoutRef.current = setTimeout(() => {
|
|
311
|
+
setExitingPanelState(null);
|
|
312
|
+
exitingPanelClearTimeoutRef.current = null;
|
|
313
|
+
}, THEME_DURATIONS[selectedTheme]);
|
|
302
314
|
}
|
|
303
|
-
|
|
315
|
+
|
|
316
|
+
return () => {
|
|
317
|
+
if (exitingPanelClearTimeoutRef.current != null) {
|
|
318
|
+
clearTimeout(exitingPanelClearTimeoutRef.current);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}, [depth, stack, selectedTheme, computedExitingId]);
|
|
304
322
|
|
|
305
323
|
const visiblePanelIds = React.useMemo(() => {
|
|
306
324
|
const ids = [stack[depth]]; // Active panel
|
|
@@ -327,7 +345,10 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
|
|
|
327
345
|
return null;
|
|
328
346
|
}
|
|
329
347
|
|
|
330
|
-
|
|
348
|
+
// Panel is only "exiting" if it matches exitingPanelId AND is not in current stack
|
|
349
|
+
// This prevents the bug where a re-pushed panel is incorrectly treated as exiting
|
|
350
|
+
const isInCurrentStack = stack.includes(panelId);
|
|
351
|
+
const isExiting = panelId === exitingPanelId && !isInCurrentStack;
|
|
331
352
|
// For exiting panels, use depth + 1 since they were previously at the active position
|
|
332
353
|
const panelDepth = isExiting ? depth + 1 : stack.indexOf(panelId);
|
|
333
354
|
const isActive = panelDepth === depth && !isExiting;
|
|
@@ -339,7 +360,7 @@ export const StackTablet: React.FC<StackTabletProps> = ({ theme = "ios" }) => {
|
|
|
339
360
|
depth={panelDepth}
|
|
340
361
|
navigationDepth={depth}
|
|
341
362
|
isActive={isActive}
|
|
342
|
-
|
|
363
|
+
operationState={toContinuousOperationState(inputState)}
|
|
343
364
|
containerSize={containerSize}
|
|
344
365
|
animateOnMount={true}
|
|
345
366
|
animationDuration={THEME_DURATIONS[selectedTheme]}
|
package/src/demo/routes.tsx
CHANGED
|
@@ -27,6 +27,14 @@ import PL_Overlays from "./pages/PanelLayout/draggable-overlays";
|
|
|
27
27
|
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
|
+
import DR_Swipe from "./pages/Drawer/swipe";
|
|
31
|
+
import DR_Reveal from "./pages/Drawer/reveal";
|
|
32
|
+
|
|
33
|
+
import DL_Modal from "./pages/Dialog/modal";
|
|
34
|
+
import DL_Alerts from "./pages/Dialog/alerts";
|
|
35
|
+
import DL_CustomAlert from "./pages/Dialog/custom-alert";
|
|
36
|
+
import DL_Swipe from "./pages/Dialog/swipe";
|
|
37
|
+
import DL_Card from "./pages/Dialog/card";
|
|
30
38
|
|
|
31
39
|
import PS_Preview from "./pages/PanelSystem/preview";
|
|
32
40
|
import PS_Tabbar from "./pages/PanelSystem/tabbar";
|
|
@@ -46,7 +54,7 @@ import ST_Tablet from "./pages/Stack/tablet";
|
|
|
46
54
|
|
|
47
55
|
import SH_Basics from "./pages/StickyHeader/basics";
|
|
48
56
|
|
|
49
|
-
import { FiGrid, FiLayers, FiColumns, FiMaximize2, FiBox, FiCpu, FiSmartphone, FiSidebar, FiNavigation, FiImage } from "react-icons/fi";
|
|
57
|
+
import { FiGrid, FiLayers, FiColumns, FiMaximize2, FiBox, FiCpu, FiSmartphone, FiSidebar, FiNavigation, FiImage, FiMessageSquare } from "react-icons/fi";
|
|
50
58
|
|
|
51
59
|
export type DemoPage = {
|
|
52
60
|
id: string;
|
|
@@ -85,6 +93,21 @@ export const demoCategories: DemoCategory[] = [
|
|
|
85
93
|
{ id: "basics", label: "Basics", path: "basics", element: <DR_Basics /> },
|
|
86
94
|
{ id: "menu", label: "Menu", path: "menu", element: <DR_Menu /> },
|
|
87
95
|
{ id: "animations", label: "Animations", path: "animations", element: <DR_Animations /> },
|
|
96
|
+
{ id: "swipe", label: "Swipe Gestures", path: "swipe", element: <DR_Swipe /> },
|
|
97
|
+
{ id: "reveal", label: "Reveal Mode", path: "reveal", element: <DR_Reveal /> },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "dialog",
|
|
102
|
+
label: "Dialog",
|
|
103
|
+
icon: <FiMessageSquare />,
|
|
104
|
+
base: "/components/dialog",
|
|
105
|
+
pages: [
|
|
106
|
+
{ id: "modal", label: "Modal", path: "modal", element: <DL_Modal /> },
|
|
107
|
+
{ id: "alerts", label: "Alert / Confirm / Prompt", path: "alerts", element: <DL_Alerts /> },
|
|
108
|
+
{ id: "custom-alert", label: "Custom Alert Component", path: "custom-alert", element: <DL_CustomAlert /> },
|
|
109
|
+
{ id: "swipe", label: "Swipe Dismiss", path: "swipe", element: <DL_Swipe /> },
|
|
110
|
+
{ id: "card", label: "Card Expansion", path: "card", element: <DL_Card /> },
|
|
88
111
|
],
|
|
89
112
|
},
|
|
90
113
|
{
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Dialog entry point - Modal and alert/confirm/prompt dialogs
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*
|
|
5
|
+
* This is a subpath export entry point for `react-panel-layout/dialog`.
|
|
6
|
+
*
|
|
7
|
+
* ## Overview
|
|
8
|
+
* Dialog provides modal dialogs and imperative alert/confirm/prompt APIs
|
|
9
|
+
* using native HTML dialog element for proper accessibility and top-layer rendering.
|
|
10
|
+
*
|
|
11
|
+
* ## Installation
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { Modal, useDialog, DialogContainer } from "react-panel-layout/dialog";
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* ## Modal Usage
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
19
|
+
*
|
|
20
|
+
* <Modal
|
|
21
|
+
* visible={isOpen}
|
|
22
|
+
* onClose={() => setIsOpen(false)}
|
|
23
|
+
* header={{ title: "Settings" }}
|
|
24
|
+
* >
|
|
25
|
+
* <form>
|
|
26
|
+
* <input type="text" placeholder="Name" />
|
|
27
|
+
* <button type="submit">Save</button>
|
|
28
|
+
* </form>
|
|
29
|
+
* </Modal>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## useDialog Hook Usage
|
|
33
|
+
* ```tsx
|
|
34
|
+
* function MyComponent() {
|
|
35
|
+
* const { alert, confirm, prompt, Outlet } = useDialog();
|
|
36
|
+
*
|
|
37
|
+
* const handleClick = async () => {
|
|
38
|
+
* await alert("Hello!");
|
|
39
|
+
*
|
|
40
|
+
* const confirmed = await confirm({
|
|
41
|
+
* message: "Are you sure?",
|
|
42
|
+
* confirmLabel: "Yes",
|
|
43
|
+
* cancelLabel: "No",
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* if (confirmed) {
|
|
47
|
+
* const name = await prompt({
|
|
48
|
+
* message: "Enter your name:",
|
|
49
|
+
* defaultValue: "Anonymous",
|
|
50
|
+
* });
|
|
51
|
+
* console.log("Name:", name);
|
|
52
|
+
* }
|
|
53
|
+
* };
|
|
54
|
+
*
|
|
55
|
+
* return (
|
|
56
|
+
* <>
|
|
57
|
+
* <button onClick={handleClick}>Show dialogs</button>
|
|
58
|
+
* <Outlet />
|
|
59
|
+
* </>
|
|
60
|
+
* );
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
// Components
|
|
66
|
+
export { Modal } from "../modules/dialog/Modal.js";
|
|
67
|
+
export { DialogContainer } from "../modules/dialog/DialogContainer.js";
|
|
68
|
+
export { AlertDialog } from "../modules/dialog/AlertDialog.js";
|
|
69
|
+
|
|
70
|
+
// Hooks
|
|
71
|
+
export { useDialog } from "../modules/dialog/useDialog.js";
|
|
72
|
+
export { useDialogContainer } from "../modules/dialog/useDialogContainer.js";
|
|
73
|
+
|
|
74
|
+
// Types
|
|
75
|
+
export type {
|
|
76
|
+
ModalProps,
|
|
77
|
+
ModalHeader,
|
|
78
|
+
DialogContainerProps,
|
|
79
|
+
DialogTransitionMode,
|
|
80
|
+
AlertOptions,
|
|
81
|
+
ConfirmOptions,
|
|
82
|
+
PromptOptions,
|
|
83
|
+
AlertDialogProps,
|
|
84
|
+
UseDialogReturn,
|
|
85
|
+
} from "../modules/dialog/types.js";
|