import { forwardRef, useEffect, useRef, useState } from 'react'; import { match, P } from 'ts-pattern'; import { Eye } from '../icons/Eye'; import { EyeOff } from '../icons/EyeOff'; import { generateNumbersArray } from '../utils/utils'; import * as Tooltip from './Tooltip'; import { Copy } from '../icons/Copy'; import { CheckCircle } from '../icons/CheckCircle'; import { useTemporaryState } from '../hooks/useTemporaryState'; import { useDelayedCallback } from '../hooks/useDelayedCallback'; import type { ComponentPropsWithoutRef } from 'react'; const ICON_WIDTH = 24; const DOT_WIDTH = 5; const GAP_WIDTH = 4; type DotsProps = Required>; function Dots({ width }: DotsProps) { return (
{generateNumbersArray(Math.floor((width - ICON_WIDTH) / (DOT_WIDTH + GAP_WIDTH))).map((n) => (
))}
); } type HiddenTextTooltipTriggerProps = ComponentPropsWithoutRef & { setRefValue: (value: HTMLButtonElement | null) => void; width: number; show: boolean; }; function HiddenTextTooltipTrigger({ onClick, children, setRefValue, show, ...other }: HiddenTextTooltipTriggerProps) { const ref = useRef(null); useEffect(() => { setRefValue(ref.current); }, [setRefValue]); return ( ); } type TooltipContentProps = ComponentPropsWithoutRef & { tooltipTrigger: HTMLButtonElement | null; }; function HiddenTextTooltipContent({ children, tooltipTrigger }: TooltipContentProps) { return ( { let targetElement = event.target as HTMLElement | null; while (targetElement !== null) { if (targetElement === tooltipTrigger) { // Event occurred on the desired element or one of its children event.preventDefault(); break; } targetElement = targetElement.parentElement; } }} >
{children}
); } type HiddenTextProps = ComponentPropsWithoutRef<'div'> & { children: string; width?: number; revealTooltipText?: string; clickToCopyTooltipText?: string; copiedTooltipText?: string; }; export function HiddenText({ children, width = 200, revealTooltipText = 'Reveal', clickToCopyTooltipText = 'Click to copy', copiedTooltipText = 'Copied', }: HiddenTextProps) { const [show, setShow] = useState(false); const [clicked, setClicked] = useTemporaryState(false, 2000); const [tooltipTriggerRefValue, setTooltipTriggerRefValue] = useState(null); const handleOnTriggerPointerOut = useDelayedCallback(() => setClicked(false), 100); const state = { show, clicked }; return ( { !show ? setShow(true) : setClicked(true); }} > {show ? children : } {match(state) .with({ show: false, clicked: false }, () => revealTooltipText) .with({ show: true, clicked: false }, () => ( <> {clickToCopyTooltipText} )) .with({ show: true, clicked: true }, () => ( <> {copiedTooltipText} )) //that should never happen .with({ show: false, clicked: true }, () => '') .exhaustive()} ); }