OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

I'm trying to toggle the opening and closing of a sidebar with a button and a clickoutside hook

  • Thread starter Thread starter Sonny
  • Start date Start date
S

Sonny

Guest
Im using nextjs 13, with an adminContextProvider i created that stores the state of the sidebar being open or closed with a function to toggle the state.

When i have the useOutsideClick hook connected to the sidebar with the if statement, I can no longer close the sidebar on mobile. I only want the click outside to work when in mobile.

When i remove

Code:
if (isMobile && isSidebarOpen) { 
    closeSidebar() 
}

from sidebar.tsx, i can close the sidebar on mobile but then obviously the click outside doesn't close the sidebar. I'm lost as to why that would interfere with trying to close the sidebar. If i press literally ANY other button on the page, the sidebar closes, but when i press the only button that has the closesidebar function, it doesn't close. I don't know what I'm missing or overlooking, I really hope it's something stupid and i apologize in advance for wasting anyone's time.

Sidebar.tsx

Code:
import { HomeIcon } from '@radix-ui/react-icons'
import React, { useEffect, useState } from 'react'
import { Person } from 'react-bootstrap-icons'
import styled from 'styled-components'
import AdminSidebarToggle from '../AdminSidebarToggle'
import useAdminContext from '../../../context/adminContext/useAdminContext'
import Link from 'next/link'
import Image from 'next/image'
import useSettings from '../../../../../hooks/useSettings'
import { Avatar, Flex, Text } from '@radix-ui/themes'
import AdminMenu from '../admin-menu/AdminMenu'
import useMobileDetect from '../../../../../hooks/useMobileDetect'
import useOutsideClick from '../../../../../hooks/useClickOutside'

const { version } = require('../../../../../../package.json')

const SidebarContainer = styled.div<{ isCollapsed: boolean }>`
    // width: ${({ isCollapsed }) => (isCollapsed ? '0rem' : '14rem')};
    height: 100vh;
    background: linear-gradient(180deg, var(--slate-2) 0%, var(--slate-2) 42%, var(--slate-2) 100%);
    transition: all 0.5s cubic-bezier(0.87, 0, 0.13, 1);
    display: flex;
    flex-direction: column;
    align-items: center;
    position: fixed;
    z-index: 1000;
    // margin-top: 60px;
    border-right: 1px solid var(--slate-4);

    // width: ${({ isCollapsed }) => (isCollapsed ? '0' : '14rem')};
    position: fixed;
    top: 0;
    left: ${({ isCollapsed }) => (isCollapsed ? '-14rem' : '0rem')};
    bottom: 0;
    overflow-x: hidden;
`

const Sidebar: React.FC = () => {
    const {
        state: { isSidebarOpen, themeMode },
        closeSidebar,
        openSidebar,
        toggleSidebar,
    } = useAdminContext()
    const { settings }: any = useSettings()


    const { isMobile } = useMobileDetect()

    useEffect(() => {
        if (isMobile) {
            closeSidebar()
        } else {
            openSidebar()
        }
    }, [isMobile])

    const ref = useOutsideClick(() => {
        if (isMobile && isSidebarOpen) { 
            closeSidebar() 
        } 
    })

    return (
        <SidebarContainer ref={ref} isCollapsed={!isSidebarOpen}>
            {/* <ToggleButton onClick={toggleSidebar}>{isCollapsed ? '☰' : '✖'}</ToggleButton> */}
            <Flex direction="column" py="2" mb="4" gap="1" style={{ minHeight: '60px' }}>
                <Link href="/" target="_blank">
                    <div style={{ position: 'relative', width: '200px', height: '52px' }}>
                        <Image
                            alt="Logo"
                            src={
                                themeMode === 'dark'
                                    ? settings['main_logo_alt']?.value
                                    : settings['main_logo']?.value
                            }
                            layout="fill"
                            objectFit="contain"
                            quality={100}
                        />
                    </div>
                </Link>
                <div className={`subHeader text-center`}>
                    <Text className="text-nowrap">{settings['company_name']?.value}</Text>
                </div>
            </Flex>
            <AdminMenu />

           
        </SidebarContainer>
    )
}

export default Sidebar

Toggle

Code:
import { DoubleArrowLeftIcon } from '@radix-ui/react-icons'
import styled from 'styled-components'
import useAdminContext from '../../context/adminContext/useAdminContext'
import { Button, Flex } from '@radix-ui/themes'
import useMobileDetect from '../../../../hooks/useMobileDetect'
import { useEffect } from 'react'

const Icon = styled(DoubleArrowLeftIcon)<{ isSidebarOpen: boolean }>`
    cursor: pointer;
    width: 24px;
    height: 24px;
    transition: transform 0.3s;

    transform: ${(props) => (!props.isSidebarOpen ? 'rotate(180deg)' : 'rotate(0deg)')};
`

const AdminSidebarToggle = () => {
    const {
        state: { isSidebarOpen },
        toggleSidebar,
        closeSidebar,
        openSidebar,
    } = useAdminContext()
    const { isMobile } = useMobileDetect()

    useEffect(() => {
        if (!isMobile && !isSidebarOpen) {
            openSidebar()
        } else if (isMobile && isSidebarOpen) {
            closeSidebar()
        }
    }, [isMobile])

    useEffect(() => {
        console.log(" toggle isSidebarOpen", isSidebarOpen)
    }, [isSidebarOpen])

    return (
        <Button size="3" color="gray" variant="ghost" onClick={() => {
            console.log("isSidebarOpen", isSidebarOpen)
            if(isSidebarOpen){
                console.log("YOURE SUPPOSED TO CLOSE!!!!!!")
                closeSidebar()
            } else {
                openSidebar()
            }

        }}>
            <Icon isSidebarOpen={isSidebarOpen} />
        </Button>
    )
}

export default AdminSidebarToggle

usOutsideClick

Code:
import { useRef, useEffect, RefObject } from 'react'

const useOutsideClick = (callback: () => void): RefObject<any> => {
    const ref = useRef<any>(null)

    useEffect(() => {
        const handleClick = (event: MouseEvent) => {
            if (ref.current && !ref.current.contains(event.target as Node)) {
                callback()
            }
        }

        document.addEventListener('click', handleClick, true)

        return () => {
            document.removeEventListener('click', handleClick, true)
        }
    }, [callback])

    return ref
}

export default useOutsideClick

<p>Im using nextjs 13, with an adminContextProvider i created that stores the state of the sidebar being open or closed with a function to toggle the state.</p>
<p>When i have the useOutsideClick hook connected to the sidebar with the if statement, I can no longer close the sidebar on mobile. I only want the click outside to work when in mobile.</p>
<p>When i remove</p>
<pre><code>if (isMobile && isSidebarOpen) {
closeSidebar()
}
</code></pre>
<p>from sidebar.tsx, i can close the sidebar on mobile but then obviously the click outside doesn't close the sidebar. I'm lost as to why that would interfere with trying to close the sidebar. If i press literally ANY other button on the page, the sidebar closes, but when i press the only button that has the closesidebar function, it doesn't close. I don't know what I'm missing or overlooking, I really hope it's something stupid and i apologize in advance for wasting anyone's time.</p>
<p>Sidebar.tsx</p>
<pre><code>import { HomeIcon } from '@radix-ui/react-icons'
import React, { useEffect, useState } from 'react'
import { Person } from 'react-bootstrap-icons'
import styled from 'styled-components'
import AdminSidebarToggle from '../AdminSidebarToggle'
import useAdminContext from '../../../context/adminContext/useAdminContext'
import Link from 'next/link'
import Image from 'next/image'
import useSettings from '../../../../../hooks/useSettings'
import { Avatar, Flex, Text } from '@radix-ui/themes'
import AdminMenu from '../admin-menu/AdminMenu'
import useMobileDetect from '../../../../../hooks/useMobileDetect'
import useOutsideClick from '../../../../../hooks/useClickOutside'

const { version } = require('../../../../../../package.json')

const SidebarContainer = styled.div<{ isCollapsed: boolean }>`
// width: ${({ isCollapsed }) => (isCollapsed ? '0rem' : '14rem')};
height: 100vh;
background: linear-gradient(180deg, var(--slate-2) 0%, var(--slate-2) 42%, var(--slate-2) 100%);
transition: all 0.5s cubic-bezier(0.87, 0, 0.13, 1);
display: flex;
flex-direction: column;
align-items: center;
position: fixed;
z-index: 1000;
// margin-top: 60px;
border-right: 1px solid var(--slate-4);

// width: ${({ isCollapsed }) => (isCollapsed ? '0' : '14rem')};
position: fixed;
top: 0;
left: ${({ isCollapsed }) => (isCollapsed ? '-14rem' : '0rem')};
bottom: 0;
overflow-x: hidden;
`

const Sidebar: React.FC = () => {
const {
state: { isSidebarOpen, themeMode },
closeSidebar,
openSidebar,
toggleSidebar,
} = useAdminContext()
const { settings }: any = useSettings()


const { isMobile } = useMobileDetect()

useEffect(() => {
if (isMobile) {
closeSidebar()
} else {
openSidebar()
}
}, [isMobile])

const ref = useOutsideClick(() => {
if (isMobile && isSidebarOpen) {
closeSidebar()
}
})

return (
<SidebarContainer ref={ref} isCollapsed={!isSidebarOpen}>
{/* <ToggleButton onClick={toggleSidebar}>{isCollapsed ? '☰' : '✖'}</ToggleButton> */}
<Flex direction="column" py="2" mb="4" gap="1" style={{ minHeight: '60px' }}>
<Link href="/" target="_blank">
<div style={{ position: 'relative', width: '200px', height: '52px' }}>
<Image
alt="Logo"
src={
themeMode === 'dark'
? settings['main_logo_alt']?.value
: settings['main_logo']?.value
}
layout="fill"
objectFit="contain"
quality={100}
/>
</div>
</Link>
<div className={`subHeader text-center`}>
<Text className="text-nowrap">{settings['company_name']?.value}</Text>
</div>
</Flex>
<AdminMenu />


</SidebarContainer>
)
}

export default Sidebar
</code></pre>
<p>Toggle</p>
<pre><code>import { DoubleArrowLeftIcon } from '@radix-ui/react-icons'
import styled from 'styled-components'
import useAdminContext from '../../context/adminContext/useAdminContext'
import { Button, Flex } from '@radix-ui/themes'
import useMobileDetect from '../../../../hooks/useMobileDetect'
import { useEffect } from 'react'

const Icon = styled(DoubleArrowLeftIcon)<{ isSidebarOpen: boolean }>`
cursor: pointer;
width: 24px;
height: 24px;
transition: transform 0.3s;

transform: ${(props) => (!props.isSidebarOpen ? 'rotate(180deg)' : 'rotate(0deg)')};
`

const AdminSidebarToggle = () => {
const {
state: { isSidebarOpen },
toggleSidebar,
closeSidebar,
openSidebar,
} = useAdminContext()
const { isMobile } = useMobileDetect()

useEffect(() => {
if (!isMobile && !isSidebarOpen) {
openSidebar()
} else if (isMobile && isSidebarOpen) {
closeSidebar()
}
}, [isMobile])

useEffect(() => {
console.log(" toggle isSidebarOpen", isSidebarOpen)
}, [isSidebarOpen])

return (
<Button size="3" color="gray" variant="ghost" onClick={() => {
console.log("isSidebarOpen", isSidebarOpen)
if(isSidebarOpen){
console.log("YOURE SUPPOSED TO CLOSE!!!!!!")
closeSidebar()
} else {
openSidebar()
}

}}>
<Icon isSidebarOpen={isSidebarOpen} />
</Button>
)
}

export default AdminSidebarToggle

</code></pre>
<p>usOutsideClick</p>
<pre><code>import { useRef, useEffect, RefObject } from 'react'

const useOutsideClick = (callback: () => void): RefObject<any> => {
const ref = useRef<any>(null)

useEffect(() => {
const handleClick = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback()
}
}

document.addEventListener('click', handleClick, true)

return () => {
document.removeEventListener('click', handleClick, true)
}
}, [callback])

return ref
}

export default useOutsideClick

</code></pre>
 

Latest posts

Online statistics

Members online
1
Guests online
1
Total visitors
2
Top