36 行
1.3 KiB
TypeScript
36 行
1.3 KiB
TypeScript
import { useId, useRef } from 'react'
|
|
import { CircleHelp } from 'lucide-react'
|
|
|
|
import type { ReactNode } from 'react'
|
|
|
|
|
|
const HelpPopover = ({ label, children }: {
|
|
label: { text: string; language: string }; children: ReactNode },
|
|
) => {
|
|
const id = useId ()
|
|
const trigger = useRef<HTMLButtonElement> (null)
|
|
const panel = useRef<HTMLDivElement> (null)
|
|
return <>
|
|
<button ref={trigger} type="button" popoverTarget={id} aria-label={label.text}
|
|
lang={label.language} className="inline-flex align-middle"
|
|
onClick={() => {
|
|
const rect = trigger.current?.getBoundingClientRect ()
|
|
if (panel.current != null && rect != null)
|
|
{
|
|
panel.current.style.top = `${rect.bottom + 4}px`
|
|
panel.current.style.left = `${Math.min (rect.left, window.innerWidth - 288)}px`
|
|
}
|
|
}}><CircleHelp size={14}/></button>
|
|
<div ref={panel} id={id} popover="auto" tabIndex={-1}
|
|
role="dialog" aria-label={label.text} lang={label.language}
|
|
onToggle={event => {
|
|
if (event.newState === 'open') panel.current?.focus ()
|
|
}} className="fixed m-0 w-72 max-w-[90vw] rounded border bg-white p-3
|
|
text-sm text-slate-900 shadow-lg dark:border-slate-600 dark:bg-slate-900
|
|
dark:text-slate-100">{children}</div>
|
|
</>
|
|
}
|
|
|
|
|
|
export default HelpPopover
|