webapp/src/components/Nav/Popup.tsx

62 lines
1.7 KiB
TypeScript
Raw Normal View History

2022-10-25 15:36:32 +00:00
import { createEffect, createSignal, JSX, onCleanup, onMount, Show } from 'solid-js'
import styles from './Popup.module.scss'
import { clsx } from 'clsx'
type HorizontalAnchor = 'center' | 'right'
2022-10-25 15:36:32 +00:00
export type PopupProps = {
containerCssClass?: string
trigger: JSX.Element
children: JSX.Element
onVisibilityChange?: (isVisible) => void
horizontalAnchor?: HorizontalAnchor
2022-10-17 20:53:04 +00:00
}
export const Popup = (props: PopupProps) => {
2022-10-25 15:36:32 +00:00
const [isVisible, setIsVisible] = createSignal(false)
const horizontalAnchor: HorizontalAnchor = props.horizontalAnchor || 'center'
2022-10-17 20:53:04 +00:00
2022-10-25 15:36:32 +00:00
createEffect(() => {
if (props.onVisibilityChange) {
props.onVisibilityChange(isVisible())
}
2022-10-17 20:53:04 +00:00
})
2022-10-25 15:36:32 +00:00
let container: HTMLDivElement | undefined
const handleClickOutside = (event: MouseEvent & { target: Element }) => {
if (!isVisible()) {
return
}
if (event.target === container || container?.contains(event.target)) {
return
}
setIsVisible(false)
}
onMount(() => {
document.addEventListener('click', handleClickOutside, { capture: true })
onCleanup(() => document.removeEventListener('click', handleClickOutside, { capture: true }))
2022-10-17 20:53:04 +00:00
})
2022-10-25 15:36:32 +00:00
const toggle = () => setIsVisible((oldVisible) => !oldVisible)
2022-10-17 20:53:04 +00:00
return (
2022-10-25 15:36:32 +00:00
<span class={clsx(styles.container, props.containerCssClass)} ref={container}>
<span onClick={toggle}>{props.trigger}</span>
<Show when={isVisible()}>
<div
class={clsx(styles.popup, {
[styles.horizontalAnchorCenter]: horizontalAnchor === 'center',
[styles.horizontalAnchorRight]: horizontalAnchor === 'right'
})}
>
{props.children}
</div>
2022-10-25 15:36:32 +00:00
</Show>
</span>
2022-10-17 20:53:04 +00:00
)
}