4
0
forked from crowetic/commerce

Adding Click Outside

This commit is contained in:
Belen Curcio 2020-12-02 13:16:30 -03:00
parent c37ed3d418
commit 6020d5cfd2
5 changed files with 107 additions and 45 deletions

View File

@ -1,9 +1,10 @@
import cn from 'classnames' import cn from 'classnames'
import Link from 'next/link' import Link from 'next/link'
import { FC, useState } from 'react' import { useRef, FC, useState } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import s from './I18nWidget.module.css' import s from './I18nWidget.module.css'
import { Cross, ChevronUp } from '@components/icons' import { Cross, ChevronUp } from '@components/icons'
import ClickOutside from '@lib/click-outside'
interface LOCALE_DATA { interface LOCALE_DATA {
name: string name: string
img: { img: {
@ -37,55 +38,62 @@ const I18nWidget: FC = () => {
defaultLocale = 'en-US', defaultLocale = 'en-US',
asPath: currentPath, asPath: currentPath,
} = useRouter() } = useRouter()
const options = locales?.filter((val) => val !== locale) const options = locales?.filter((val) => val !== locale)
const currentLocale = locale || defaultLocale const currentLocale = locale || defaultLocale
const ref = useRef<HTMLDivElement | null>(null)
return ( return (
<nav className={s.root}> <ClickOutside active={display} onClick={() => setDisplay(false)} ref={ref}>
<div className="flex items-center relative"> <nav className={s.root}>
<button className={s.button} aria-label="Language selector"> <div
<img className="flex items-center relative"
className="block mr-2 w-5" onClick={() => setDisplay(!display)}
src={`/${LOCALES_MAP[currentLocale].img.filename}`} >
alt={LOCALES_MAP[currentLocale].img.alt} <button className={s.button} aria-label="Language selector">
/> <img
{options && ( className="block mr-2 w-5"
<span src={`/${LOCALES_MAP[currentLocale].img.filename}`}
className="cursor-pointer" alt={LOCALES_MAP[currentLocale].img.alt}
onClick={() => setDisplay(!display)} />
> {options && (
<ChevronUp className={cn({ [s.icon]: display })} /> <span className="cursor-pointer">
</span> <ChevronUp className={cn({ [s.icon]: display })} />
)} </span>
</button> )}
</div> </button>
<div className="absolute top-0 right-0"> </div>
{options?.length && display ? ( <div className="absolute top-0 right-0">
<div className={s.dropdownMenu}> {options?.length && display ? (
<div className="flex flex-row justify-end px-6"> <div className={s.dropdownMenu}>
<button <div className="flex flex-row justify-end px-6">
onClick={() => setDisplay(false)} <button
aria-label="Close panel" onClick={() => setDisplay(false)}
className={s.closeButton} aria-label="Close panel"
> className={s.closeButton}
<Cross className="h-6 w-6" /> >
</button> <Cross className="h-6 w-6" />
</button>
</div>
<ul>
{options.map((locale) => (
<li key={locale}>
<Link href={currentPath} locale={locale}>
<a
className={cn(s.item)}
onClick={() => setDisplay(false)}
>
{LOCALES_MAP[locale].name}
</a>
</Link>
</li>
))}
</ul>
</div> </div>
<ul> ) : null}
{options.map((locale) => ( </div>
<li key={locale}> </nav>
<Link href={currentPath} locale={locale}> </ClickOutside>
<a className={cn(s.item)} onClick={() => setDisplay(false)}>
{LOCALES_MAP[locale].name}
</a>
</Link>
</li>
))}
</ul>
</div>
) : null}
</div>
</nav>
) )
} }

View File

@ -0,0 +1,45 @@
import React, { forwardRef, useEffect, Ref, MouseEvent } from 'react'
import hasParent from './has-parent'
interface ClickOutsideProps {
active: boolean
onClick: (e?: MouseEvent) => void
children: any
}
const ClickOutside = (
{ active = true, onClick, children }: ClickOutsideProps,
ref: Ref<HTMLDivElement> | null | any = {}
) => {
console.log('--------', active, '-----------')
const innerRef = ref?.current
const handleClick = (event: any) => {
console.log(innerRef, event.target)
if (!hasParent(event.target, innerRef)) {
if (typeof onClick === 'function') {
event.preventDefault()
event.stopImmediatePropagation()
onClick(event)
}
}
}
useEffect(() => {
if (active) {
document.addEventListener('mousedown', handleClick)
document.addEventListener('touchstart', handleClick)
}
return () => {
if (active) {
document.removeEventListener('mousedown', handleClick)
document.removeEventListener('touchstart', handleClick)
}
}
})
return React.cloneElement(children, { ref })
}
export default forwardRef(ClickOutside)

View File

@ -0,0 +1,5 @@
import isInDOM from './is-in-dom'
export default function hasParent(element, root) {
return root && root.contains(element) && isInDOM(element)
}

View File

@ -0,0 +1 @@
export { default } from './click-outside'

View File

@ -0,0 +1,3 @@
export default function isInDom(obj) {
return Boolean(obj.closest('body'))
}