components.Table.provider.ToolbarOverlay.tsx Maven / Gradle / Ivy
The newest version!
/* eslint-disable @typescript-eslint/no-explicit-any */
import { createContext, useContext } from 'use-context-selector'
import React, {
FC,
MouseEvent,
useCallback,
useMemo,
useRef,
useState,
RefObject,
} from 'react'
import { createPortal } from 'react-dom'
import useClickOutside from '../../../core/hooks/useClickOutside'
type OverlayContext = {
onShowOverlay?(event: MouseEvent, model: any): void
onHideOverlay?(): void
}
type OverlayProps = {
overlay?: {
className?: string
toolbar: Array<{
id: string
buttons: Array<{
component: FC
id: string
[x: string]: any
}>
}>
}
refContainerElem: RefObject
onClickActionButton(data: any): void
}
const overlayContext = createContext({})
const WithOverlay: FC> = ({
children,
overlay,
refContainerElem,
onClickActionButton,
}) => {
const { toolbar, className } = overlay
const isDisabledToggleFunc = useRef(false)
const rowElem = useRef()
const model = useRef({})
const [isVisibleOverlay, setIsVisibleOverlay] = useState(false)
const overlayContainerRef = useRef(null)
const methods = useMemo(() => ({
onShowOverlay(event: MouseEvent, data: any) {
const overlay = overlayContainerRef.current
const container = refContainerElem.current
if (!overlay || !container || isDisabledToggleFunc.current) {
return
}
const row = event.currentTarget
const rowParam = row.getBoundingClientRect()
const tableContainerParam = container.getBoundingClientRect()
rowElem.current = row
setIsVisibleOverlay(true)
model.current = data
overlay.style.display = 'flex'
overlay.style.top = `${rowParam.top + rowParam.height}px`
overlay.style.left = `${tableContainerParam.left}px`
overlay.style.width = `${tableContainerParam.width}px`
},
onHideOverlay() {
const overlay = overlayContainerRef.current
if (!overlay || isDisabledToggleFunc.current) {
return
}
if (rowElem.current) {
rowElem.current.dataset.overlayHovered = 'false'
}
setIsVisibleOverlay(false)
},
}), [refContainerElem])
const onPinOverlay = () => {
isDisabledToggleFunc.current = true
}
const continueShowOverlay = () => {
const overlay = overlayContainerRef.current
if (!overlay) {
return
}
if (rowElem.current) {
rowElem.current.dataset.overlayHovered = 'true'
}
overlay.style.display = 'flex'
setIsVisibleOverlay(true)
}
const unPinOverlay = useCallback(() => {
isDisabledToggleFunc.current = false
}, [])
const onOutsideClick = useCallback(() => {
unPinOverlay()
methods.onHideOverlay()
}, [methods, unPinOverlay])
const onClickActionButtonCallback = useCallback(() => {
unPinOverlay()
onClickActionButton(model.current)
}, [onClickActionButton, unPinOverlay])
useClickOutside(onOutsideClick, [overlayContainerRef])
return (
{children}
{createPortal(
(
{isVisibleOverlay ? (
{toolbar.map(({ id, buttons }) => (
{buttons.map(({ component: ButtonComponent, id, ...props }) => (
))}
))}
) : null}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
), document.querySelector('#n2o')!,
)}
)
}
export const ToolbarOverlay: FC = ({
children,
overlay,
...props
}) => (
// eslint-disable-next-line react/jsx-no-useless-fragment
<>
{overlay ? (
{children}
) : children}
>
)
ToolbarOverlay.displayName = 'ToolbarOverlay'
export const useToolbarOverlay = () => useContext(overlayContext)