mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-09-15 15:22:25 +03:00
Completely revise the animations for the context menu to be compatible with Firefox for Android (worked everywhere else amusingly)
This commit is contained in:
parent
4963142ed4
commit
a4c5d1a6f9
2 changed files with 67 additions and 39 deletions
|
@ -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 (
|
||||
<>
|
||||
<animated.div
|
||||
<div
|
||||
className="bottom-sheet-backdrop"
|
||||
onClick={onClose}
|
||||
style={{ opacity: y.to([0, innerHeight], [1, 0], 'clamp') }}
|
||||
style={{ opacity: Math.max(0, backdropOpacity) }}
|
||||
/>
|
||||
|
||||
<animated.div
|
||||
className="bottom-sheet-container"
|
||||
<div
|
||||
className={containerClasses}
|
||||
{...bind()}
|
||||
style={{
|
||||
y,
|
||||
transform: `translate3d(0, ${y}px, 0)`,
|
||||
touchAction: 'none',
|
||||
}}
|
||||
>
|
||||
|
@ -66,7 +79,7 @@ export function MobileContextMenu({ isOpen, onClose, children }) {
|
|||
<div className="bottom-sheet-content" style={{ overflow: 'visible' }}>
|
||||
{children}
|
||||
</div>
|
||||
</animated.div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue