add: logout dialog and auth based side menu items
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useMemo, useRef } from 'react'
|
import React, { useMemo, useRef, useState } from 'react'
|
||||||
import { motion, PanInfo, Variants } from 'framer-motion';
|
import { motion, PanInfo, Variants } from 'framer-motion';
|
||||||
import MenuItemType from './SideMenuItem'
|
import MenuItemType from './SideMenuItem'
|
||||||
import SideMenuItem from './SideMenuItem';
|
import SideMenuItem from './SideMenuItem';
|
||||||
@@ -17,11 +17,15 @@ import BlurredOverlayContainer from '../overlays/BlurredOverlayContainer';
|
|||||||
import SettingIcon from '../icons/SettingsIcon';
|
import SettingIcon from '../icons/SettingsIcon';
|
||||||
import ExitIcon from '../icons/ExitIcon';
|
import ExitIcon from '../icons/ExitIcon';
|
||||||
import NightModeSwitch from '../button/NightModeSwitch';
|
import NightModeSwitch from '../button/NightModeSwitch';
|
||||||
|
import Button from '../button/PrimaryButton';
|
||||||
|
import { useAuthStore } from '@/zustand/authStore';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
type MenuItemType = {
|
type MenuItemType = {
|
||||||
href: string | undefined;
|
href: string | undefined;
|
||||||
title: string;
|
title: string;
|
||||||
icon: React.ReactElement
|
icon: React.ReactElement;
|
||||||
|
auth?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuItems: Array<Array<MenuItemType>> = [
|
const menuItems: Array<Array<MenuItemType>> = [
|
||||||
@@ -57,10 +61,10 @@ const menuItems: Array<Array<MenuItemType>> = [
|
|||||||
[],
|
[],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
href: undefined, title: 'تنظیمات', icon: <SettingIcon width={20} height={20} className='text-icon-deactive' />
|
auth: true, href: undefined, title: 'تنظیمات', icon: <SettingIcon width={20} height={20} className='text-icon-deactive' />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: undefined, title: 'خروج', icon: <ExitIcon width={20} height={20} className='text-icon-deactive' />
|
auth: true, href: '?logout', title: 'خروج', icon: <ExitIcon width={20} height={20} className='text-icon-deactive' />
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -74,15 +78,24 @@ type Props = {
|
|||||||
|
|
||||||
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
|
function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) {
|
||||||
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
const menuStateMemo = useMemo(() => menuState, [menuState]);
|
||||||
|
const [logoutModalVisible, setLogoutModalVisible] = useState(false);
|
||||||
const closeRef: React.Ref<HTMLDivElement> | undefined = useRef(null);
|
const closeRef: React.Ref<HTMLDivElement> | undefined = useRef(null);
|
||||||
|
const userIsAuthenticated = true; //useAuthStore((state) => state.isAuthenticated);
|
||||||
|
const authLogout = useAuthStore((state) => state.logout);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const variants: Variants = {
|
const variants: Variants = {
|
||||||
hidden: {
|
hidden: {
|
||||||
x: '100%',
|
x: '100%',
|
||||||
|
transition: {
|
||||||
|
duration: 0.1,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
visible: {
|
visible: {
|
||||||
x: 0,
|
x: 0,
|
||||||
transition: {
|
transition: {
|
||||||
|
delay: 0.075,
|
||||||
duration: 0.1,
|
duration: 0.1,
|
||||||
ease: 'easeInOut',
|
ease: 'easeInOut',
|
||||||
},
|
},
|
||||||
@@ -90,19 +103,37 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onDrag = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
|
const onDrag = (_event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
|
||||||
if (info.offset.x > 100) {
|
if (info.offset.x > 20) {
|
||||||
if (closeRef.current) {
|
if (closeRef.current) {
|
||||||
closeRef.current.click();
|
closeRef.current.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleLogoutModalVisiblity = (e: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement> | React.MouseEvent<HTMLDivElement> | undefined) => {
|
||||||
|
e?.stopPropagation();
|
||||||
|
setLogoutModalVisible((state) => !state);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLogout = (e: React.MouseEvent<HTMLButtonElement> | null) => {
|
||||||
|
if (!e) return;
|
||||||
|
if (userIsAuthenticated) {
|
||||||
|
authLogout();
|
||||||
|
router.replace('/');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hrefOnClicks: Array<{ href: string, handler: React.MouseEventHandler<HTMLAnchorElement> | undefined }> = [
|
||||||
|
{ href: '?logout', handler: toggleLogoutModalVisiblity }
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className='w-full' ref={closeRef} onClick={toggleMenuState} >
|
<div className='w-full' ref={closeRef} onClick={toggleMenuState} >
|
||||||
<BlurredOverlayContainer bgOpacity={40} visible={menuStateMemo} outDelay={150}>
|
<BlurredOverlayContainer bgOpacity={40} visible={menuStateMemo} outDelay={150} inDuration={200}>
|
||||||
<motion.div
|
<motion.div
|
||||||
drag='x'
|
drag='x'
|
||||||
dragConstraints={{ left: 0, right: 0 }}
|
dragConstraints={{ left: 0, right: 20 }}
|
||||||
transition={{ type: "spring", stiffness: 200, damping: 20 }}
|
transition={{ type: "spring", stiffness: 200, damping: 20 }}
|
||||||
dragTransition={{ bounceDamping: 10, bounceStiffness: 600 }}
|
dragTransition={{ bounceDamping: 10, bounceStiffness: 600 }}
|
||||||
dragElastic={0.05}
|
dragElastic={0.05}
|
||||||
@@ -113,28 +144,64 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS
|
|||||||
data-visible={menuState}
|
data-visible={menuState}
|
||||||
data-isvisible={menuStateMemo}
|
data-isvisible={menuStateMemo}
|
||||||
onClick={(e) => { e.stopPropagation(); }}
|
onClick={(e) => { e.stopPropagation(); }}
|
||||||
className={`fixed top-0 bottom-0 -right-full data-[isvisible=true]:right-0 transition-all duration-100 ease-in-out not-xl:!rounded-s-none overflow-clip bg-white w-[200px] h-dvh flex flex-col z-40`}>
|
className={`fixed top-0 bottom-0 right-0 ease-in-out not-xl:!rounded-s-none overflow-clip bg-white w-[200px] h-dvh flex flex-col z-40`}>
|
||||||
<ul className="overflow-y-scroll noscrollbar pt-20 pb-10 flex flex-col justify-between h-full ">
|
<ul className="overflow-y-scroll noscrollbar pt-20 pb-10 flex flex-col justify-between h-full ">
|
||||||
<li>
|
<li>
|
||||||
<div className='flex flex-col gap-[60px]'>
|
<div className='flex flex-col gap-[60px]'>
|
||||||
<div className='flex flex-col gap-[23px]'>
|
<div className='flex flex-col gap-[23px]'>
|
||||||
<div className='pt-2 text-start px-12 text-menu-header'>منو</div>
|
<div className='pt-2 text-start px-12 text-menu-header'>منو</div>
|
||||||
{menuItems[0].map((v, i) => {
|
{menuItems[0].filter((x) => x.auth ? userIsAuthenticated && x : x).map((v, i) => {
|
||||||
return <SideMenuItem key={i} href={v.href || ''} title={v.title} icon={v.icon} />
|
return <SideMenuItem key={i} href={v.href || ''} title={v.title} icon={v.icon} />
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li className='flex flex-col gap-6 pt-16'>
|
<li className='flex flex-col gap-6 pt-16'>
|
||||||
{menuItems[2].map((v, i) => {
|
{menuItems[2].filter((x) => x.auth ? userIsAuthenticated && x : x).map((v, i) => {
|
||||||
return <SideMenuItem key={i} href={v.href || ''} title={v.title} icon={v.icon} />
|
return <SideMenuItem
|
||||||
|
key={i}
|
||||||
|
href={v.href || ''}
|
||||||
|
onClick={v.href && hrefOnClicks.find((i) => i.href === v.href)?.handler || undefined}
|
||||||
|
title={v.title}
|
||||||
|
icon={v.icon}
|
||||||
|
/>
|
||||||
})}
|
})}
|
||||||
<SideMenuItem href={''} title={''} icon={<></>}><NightModeSwitch checked={nightModeState} onClick={togglenightModeState} /></SideMenuItem>
|
<SideMenuItem href={''} title={''} icon={<></>}><NightModeSwitch checked={nightModeState} onClick={togglenightModeState} /></SideMenuItem>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
</BlurredOverlayContainer>
|
</BlurredOverlayContainer>
|
||||||
</div>
|
</div>
|
||||||
|
<BlurredOverlayContainer visible={logoutModalVisible} onClick={toggleLogoutModalVisiblity} inDuration={100} outDuration={100} outDelay={75}>
|
||||||
|
<div
|
||||||
|
className='absolute top-1/2 left-0 w-full px-6' onClick={toggleLogoutModalVisiblity}>
|
||||||
|
<motion.div
|
||||||
|
animate={{ y: logoutModalVisible ? [10, 0] : [0, 10], }}
|
||||||
|
transition={{ duration: 0.1, ease: 'easeInOut' }}
|
||||||
|
className='top-1/2 min-w-xs w-full max-w-sm justify-self-center text-center -translate-y-1/2 px-6 pt-8 pb-9 drop-shadow-container bg-white/63 rounded-4xl'
|
||||||
|
>
|
||||||
|
<div className='text-base font-medium'>خروج از سیستم</div>
|
||||||
|
<div className='text-xs font-medium mt-6'>آیا میخواهید از سیستم خارج شوید؟</div>
|
||||||
|
<div className='grid grid-cols-2 gap-5.5 mt-8'>
|
||||||
|
<Button
|
||||||
|
onClick={onLogout}
|
||||||
|
className='text-sm font-normal'
|
||||||
|
>
|
||||||
|
بله خارج میشوم
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className='bg-disabled! text-disabled2! text-sm font-normal'
|
||||||
|
onClick={toggleLogoutModalVisiblity}
|
||||||
|
>
|
||||||
|
منصرف شدم
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</BlurredOverlayContainer>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export type BlurredOverlayContainerProps = {
|
|||||||
inDelay?: number
|
inDelay?: number
|
||||||
outDelay?: number
|
outDelay?: number
|
||||||
transitionDelay?: number
|
transitionDelay?: number
|
||||||
|
inDuration?: number
|
||||||
|
outDuration?: number
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
} & React.ComponentProps<typeof motion.div>
|
} & React.ComponentProps<typeof motion.div>
|
||||||
|
|
||||||
@@ -17,7 +19,9 @@ const BlurredOverlayContainer = ({
|
|||||||
visible = true,
|
visible = true,
|
||||||
bgOpacity = 30,
|
bgOpacity = 30,
|
||||||
inDelay = 0,
|
inDelay = 0,
|
||||||
outDelay = 0,
|
outDelay = 200,
|
||||||
|
inDuration = 300,
|
||||||
|
outDuration = 300,
|
||||||
transitionDelay: transitionDelay = 0,
|
transitionDelay: transitionDelay = 0,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
@@ -45,7 +49,7 @@ const BlurredOverlayContainer = ({
|
|||||||
display: 'none',
|
display: 'none',
|
||||||
transition: {
|
transition: {
|
||||||
delay: transitionDelay / 1000,
|
delay: transitionDelay / 1000,
|
||||||
duration: 0.3,
|
duration: outDuration / 1000,
|
||||||
ease: 'easeInOut',
|
ease: 'easeInOut',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -55,7 +59,7 @@ const BlurredOverlayContainer = ({
|
|||||||
display: 'block',
|
display: 'block',
|
||||||
transition: {
|
transition: {
|
||||||
delay: transitionDelay / 1000,
|
delay: transitionDelay / 1000,
|
||||||
duration: 0.3,
|
duration: inDuration / 1000,
|
||||||
ease: 'easeInOut',
|
ease: 'easeInOut',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user