diff --git a/src/app/molecules/mobile-context-menu/MobileContextMenu.jsx b/src/app/molecules/mobile-context-menu/MobileContextMenu.jsx
index fd809fa0..b9beda87 100644
--- a/src/app/molecules/mobile-context-menu/MobileContextMenu.jsx
+++ b/src/app/molecules/mobile-context-menu/MobileContextMenu.jsx
@@ -1,10 +1,21 @@
-import React, { useEffect } from 'react';
-import { useSpring, animated } from '@react-spring/web';
+import React, { useState, useEffect } from 'react';
import { useDrag } from 'react-use-gesture';
import './MobileContextMenu.scss';
export function MobileContextMenu({ isOpen, onClose, children }) {
- const { innerHeight } = window;
+ const getInnerHeight = () => (typeof window !== 'undefined' ? window.innerHeight : 0);
+ const [y, setY] = useState(getInnerHeight());
+ const [isDragging, setIsDragging] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setY(isOpen ? 0 : getInnerHeight());
+ }, 10);
+
+ return () => clearTimeout(timer);
+ }, [isOpen]);
+
+
useEffect(() => {
if (isOpen) {
document.body.style.overscrollBehavior = 'contain';
@@ -14,51 +25,53 @@ export function MobileContextMenu({ isOpen, onClose, children }) {
};
}, [isOpen]);
- const [{ y }, api] = useSpring(() => ({
- y: innerHeight,
- config: { tension: 250, friction: 25 },
- }));
-
- useEffect(() => {
- api.start({ y: isOpen ? 0 : innerHeight });
- }, [api, innerHeight, isOpen]);
-
const bind = useDrag(
- ({ last, movement: [, my], event }) => {
+ ({ last, movement: [, my], down }) => {
+ if (down && !isDragging) {
+ setIsDragging(true);
+ }
+
+ const newY = Math.max(my, 0);
+ setY(newY);
+
if (last) {
- if (my > innerHeight / 4) {
- event.preventDefault();
- event.stopPropagation();
+ setIsDragging(false);
+ if (my > getInnerHeight() / 4) {
onClose();
} else {
- api.start({ y: 0 });
+ setY(0);
}
- } else {
- api.start({ y: Math.max(my, 0), immediate: true });
}
},
{
- from: () => [0, y.get()],
+ from: () => [0, y],
filterTaps: true,
bounds: { top: 0 },
rubberband: true,
}
);
- if (!isOpen) return null;
+
+ if (!isOpen && y >= getInnerHeight()) return null;
+ const containerClasses = [
+ 'bottom-sheet-container',
+ !isDragging ? 'is-transitioning' : '',
+ ].join(' ');
+
+ const backdropOpacity = y > 0 ? 1 - y / getInnerHeight() : 1;
return (
<>
-
-
@@ -66,7 +79,7 @@ export function MobileContextMenu({ isOpen, onClose, children }) {
{children}
-
+
>
);
}
diff --git a/src/app/molecules/mobile-context-menu/MobileContextMenu.scss b/src/app/molecules/mobile-context-menu/MobileContextMenu.scss
index f42e1f75..326385da 100644
--- a/src/app/molecules/mobile-context-menu/MobileContextMenu.scss
+++ b/src/app/molecules/mobile-context-menu/MobileContextMenu.scss
@@ -1,34 +1,49 @@
.bottom-sheet-backdrop {
position: fixed;
- inset: 0;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
background-color: rgba(0, 0, 0, 0.5);
- z-index: 1000;
- touch-action: none;
+ // The backdrop fade will also be smoother with a transition
+ transition: opacity 300ms ease-out;
+ z-index: 999;
}
.bottom-sheet-container {
position: fixed;
- left: 0;
- right: 0;
bottom: 0;
- z-index: 1001;
+ left: 0;
+ width: 100%;
+ // Set transform from the component's style prop
+ // The 'will-change' property is a performance hint for the browser
+ will-change: transform;
+ z-index: 1000;
+
+ // Your existing styles for the sheet itself
+ border-top-left-radius: 16px;
+ border-top-right-radius: 16px;
+ box-shadow: 0 -2px A10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
- max-height: 90vh;
- border-radius: 16px 16px 0 0;
- box-shadow: 0px -4px B16px rgba(0, 0, 0, 0.15);
+
+ // This is the magic: apply a transition only when this class is present
+ &.is-transitioning {
+ transition: transform 300ms ease-out;
+ }
}
+// Your other styles remain the same
.bottom-sheet-grabber {
- flex-shrink: 0;
width: 40px;
height: 5px;
- border-radius: 2.5px;
background-color: #ccc;
+ border-radius: 2.5px;
margin: 8px auto;
+ cursor: grab;
}
.bottom-sheet-content {
+ padding: 16px;
overflow-y: auto;
- padding: 0 1rem 1rem 1rem;
}