chore: improve animation of blurred overlay
This commit is contained in:
Generated
+13
-13
@@ -19,7 +19,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dmenu-plus-frontned": "file:",
|
"dmenu-plus-frontned": "file:",
|
||||||
"framer-motion": "^12.20.1",
|
"framer-motion": "^12.23.3",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"next": "15.3.4",
|
"next": "15.3.4",
|
||||||
@@ -4078,13 +4078,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/framer-motion": {
|
"node_modules/framer-motion": {
|
||||||
"version": "12.20.1",
|
"version": "12.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.3.tgz",
|
||||||
"integrity": "sha512-NW2t2GHQcNvLHq18JyNVY15VKrwru+nkNyhLdqf4MbxbGhxZcSDi68iNcAy6O1nG0yYAQJbLioBIH1Kmg8Xr1g==",
|
"integrity": "sha512-llmLkf44zuIZOPSrE4bl4J0UTg6bav+rlKEfMRKgvDMXqBrUtMg6cspoQeRVK3nqRLxTmAJhfGXk39UDdZD7Kw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"motion-dom": "^12.20.1",
|
"motion-dom": "^12.23.2",
|
||||||
"motion-utils": "^12.19.0",
|
"motion-utils": "^12.23.2",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -5418,18 +5418,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/motion-dom": {
|
"node_modules/motion-dom": {
|
||||||
"version": "12.20.1",
|
"version": "12.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.2.tgz",
|
||||||
"integrity": "sha512-XyveLJ9dmQTmaEsP9RlcuoNFxWlRIGdasdPJBB4aOwPr8bRcJdhltudAbiEjRQBmsGD30sjJdaEjhkHsAHapLQ==",
|
"integrity": "sha512-73j6xDHX/NvVh5L5oS1ouAVnshsvmApOq4F3VZo5MkYSD/YVsVLal4Qp9wvVgJM9uU2bLZyc0Sn8B9c/MMKk4g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"motion-utils": "^12.19.0"
|
"motion-utils": "^12.23.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/motion-utils": {
|
"node_modules/motion-utils": {
|
||||||
"version": "12.19.0",
|
"version": "12.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.2.tgz",
|
||||||
"integrity": "sha512-BuFTHINYmV07pdWs6lj6aI63vr2N4dg0vR+td0rtrdpWOhBzIkEklZyLcvKBoEtwSqx8Jg06vUB5RS0xDiUybw==",
|
"integrity": "sha512-cIEXlBlXAOUyiAtR0S+QPQUM9L3Diz23Bo+zM420NvSd/oPQJwg6U+rT+WRTpp0rizMsBGQOsAwhWIfglUcZfA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dmenu-plus-frontned": "file:",
|
"dmenu-plus-frontned": "file:",
|
||||||
"framer-motion": "^12.20.1",
|
"framer-motion": "^12.23.3",
|
||||||
"iconsax-react": "^0.0.8",
|
"iconsax-react": "^0.0.8",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"next": "15.3.4",
|
"next": "15.3.4",
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ const MenuIndex = () => {
|
|||||||
</VerticalScrollView>
|
</VerticalScrollView>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<AnimatedBottomSheet title="فیلتر ها" visible={filterModal} changeDelay="150" onClick={toggleFilterModal}>
|
<AnimatedBottomSheet title="فیلتر ها" visible={filterModal} outDelay={150} onClick={toggleFilterModal}>
|
||||||
<div className="ps-8.5 pe-[31px] mt-[89px]">
|
<div className="ps-8.5 pe-[31px] mt-[89px]">
|
||||||
<SearchComboBox
|
<SearchComboBox
|
||||||
title="محتویات"
|
title="محتویات"
|
||||||
@@ -277,7 +277,7 @@ const MenuIndex = () => {
|
|||||||
</div>
|
</div>
|
||||||
</AnimatedBottomSheet>
|
</AnimatedBottomSheet>
|
||||||
|
|
||||||
<AnimatedBottomSheet title="مرتب کردن بر اساس" visible={sortingModal} changeDelay="150" onClick={toggleSortingModal}>
|
<AnimatedBottomSheet title="مرتب کردن بر اساس" visible={sortingModal} inDelay={150} onClick={toggleSortingModal}>
|
||||||
<div className="px-8.5 py-10 justify-between">
|
<div className="px-8.5 py-10 justify-between">
|
||||||
{sortings.map((v, i) => {
|
{sortings.map((v, i) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ type Props = {
|
|||||||
|
|
||||||
const AnimatedBottomSheet = (props: Props) => {
|
const AnimatedBottomSheet = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<BlurredOverlayContainer {...props} changeDelay="150">
|
<BlurredOverlayContainer {...props} inDelay={150}>
|
||||||
<div
|
<div
|
||||||
data-visible={props.visible}
|
data-visible={props.visible}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-dvh'>
|
<div className='w-full h-dvh'>
|
||||||
<BlurredOverlayContainer bgOpacity='40' visible={menuStateMemo} onClick={toggleMenuState} changeDelay='300'>
|
<BlurredOverlayContainer bgOpacity={40} visible={menuStateMemo} onClick={toggleMenuState} outDelay={150}>
|
||||||
<div
|
<div
|
||||||
data-isvisible={menuStateMemo}
|
data-isvisible={menuStateMemo}
|
||||||
onClick={(e) => { e.stopPropagation(); }}
|
onClick={(e) => { e.stopPropagation(); }}
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { ReactNode, useEffect, useState } from 'react'
|
||||||
|
import { motion, Variants } from "framer-motion";
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
export type BlurredOverlayContainerProps = {
|
export type BlurredOverlayContainerProps = {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
bgOpacity?: string
|
bgOpacity?: number
|
||||||
changeDelay?: string
|
inDelay?: number
|
||||||
effectdelay?: string
|
outDelay?: number
|
||||||
} & React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
|
transitionDelay?: number
|
||||||
|
children: ReactNode
|
||||||
|
} & React.ComponentProps<typeof motion.div>
|
||||||
|
|
||||||
const BlurredOverlayContainer = ({
|
const BlurredOverlayContainer = ({
|
||||||
visible = true,
|
visible = true,
|
||||||
bgOpacity = '30',
|
bgOpacity = 30,
|
||||||
changeDelay = '0',
|
inDelay = 0,
|
||||||
effectdelay = '0',
|
outDelay = 0,
|
||||||
|
transitionDelay: transitionDelay = 0,
|
||||||
children,
|
children,
|
||||||
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
...props
|
...props
|
||||||
}: BlurredOverlayContainerProps) => {
|
}: BlurredOverlayContainerProps) => {
|
||||||
@@ -23,34 +29,55 @@ const BlurredOverlayContainer = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
// Show immediately or after delay
|
// Show immediately or after delay
|
||||||
const timeout = setTimeout(() => setLoaded(true), +changeDelay)
|
const timeout = setTimeout(() => setLoaded(true), inDelay)
|
||||||
return () => clearTimeout(timeout)
|
return () => clearTimeout(timeout)
|
||||||
} else {
|
} else {
|
||||||
// Hide with delay (matches opacity transition duration)
|
// Hide with delay (matches opacity transition duration)
|
||||||
const timeout = setTimeout(() => setLoaded(false), +changeDelay) // 300ms matches Tailwind transition
|
const timeout = setTimeout(() => setLoaded(false), outDelay) // 300ms matches Tailwind transition
|
||||||
return () => clearTimeout(timeout)
|
return () => clearTimeout(timeout)
|
||||||
}
|
}
|
||||||
}, [visible, changeDelay])
|
}, [visible, inDelay, outDelay])
|
||||||
|
|
||||||
const zClass =
|
const variants: Variants = {
|
||||||
visible || loaded ? 'z-50' : !visible && !loaded ? '-z-50' : 'z-0'
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
delay: transitionDelay / 1000,
|
||||||
|
duration: 0.3,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
delay: transitionDelay / 1000,
|
||||||
|
duration: 0.3,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<motion.div
|
||||||
data-visible={visible}
|
data-visible={visible}
|
||||||
data-loaded={loaded}
|
data-loaded={loaded}
|
||||||
className={props.className + `
|
className={clsx(
|
||||||
fixed inset-0 flex items-center justify-center h-dvh w-full
|
className,
|
||||||
backdrop-blur-sm
|
visible || loaded ? 'z-50' : !visible && !loaded ? '-z-50' : 'z-0',
|
||||||
opacity-0 transition-opacity duration-300 ease-in-out delay-${effectdelay}
|
'fixed inset-0 flex items-center justify-center h-dvh w-full backdrop-blur-sm',
|
||||||
${zClass}
|
)}
|
||||||
${visible ? 'opacity-100' : 'opacity-0'}`}
|
style={{
|
||||||
style={{ backgroundColor: `rgba(0, 0, 0, ${+bgOpacity / 100})` }}
|
backgroundColor: `rgba(0, 0, 0, ${bgOpacity / 100})`,
|
||||||
|
}}
|
||||||
|
|
||||||
|
initial={visible ? 'visible' : 'hidden'}
|
||||||
|
animate={visible ? 'visible' : 'hidden'}
|
||||||
|
variants={variants}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className='w-full h-dvh overflow-hidden' onClick={onClick}></div>
|
<div className='w-full h-dvh overflow-hidden' onClick={onClick}></div>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</motion.div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user