From 3f00a904a4e5bb5ee268025683f7ea0d2d10ddc0 Mon Sep 17 00:00:00 2001 From: Marek Mihok Date: Fri, 18 Aug 2023 16:15:42 +0200 Subject: [PATCH] chore: implement portal inside button component #2025 --- ui/src/copyable_text.tsx | 90 +++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/ui/src/copyable_text.tsx b/ui/src/copyable_text.tsx index 8da6ae233c..5b22eff80f 100644 --- a/ui/src/copyable_text.tsx +++ b/ui/src/copyable_text.tsx @@ -64,13 +64,22 @@ export interface CopyableText { height?: S } -type CopyButton = { value: S, anchorElement: HTMLElement | undefined, showOnHoverOnly?: B } +type CopyButton = { + /** Text to be copied to clipboard. */ + value: S, + /** The element to which the copy button is attached. */ + anchorElement: HTMLElement | undefined, + /** Show copy button only on hover. */ + showOnHoverOnly?: B, + /** Use portal if ClipboardCopyButton is not direct child of the anchor element. */ + portal?: B +} -export const ClipboardCopyButton = ({ value, anchorElement, showOnHoverOnly = false }: CopyButton) => { +export const ClipboardCopyButton = ({ value, anchorElement, showOnHoverOnly = false, portal = false }: CopyButton) => { const timeoutRef = React.useRef(), [copied, setCopied] = React.useState(false), - onClick = async () => { + onClick = React.useCallback(async () => { if (!anchorElement) return try { if (document.queryCommandSupported('copy')) { @@ -84,21 +93,29 @@ export const ClipboardCopyButton = ({ value, anchorElement, showOnHoverOnly = fa setCopied(true) timeoutRef.current = window.setTimeout(() => setCopied(false), 2000) - } + }, [anchorElement, value]), + CopyButton = React.useMemo(() => , [copied, onClick, showOnHoverOnly]) React.useEffect(() => { - if (showOnHoverOnly && anchorElement) anchorElement.classList.add(css.hover) - }, [anchorElement, showOnHoverOnly]) + if (!anchorElement) return + if (portal) { + ReactDOM.render( + ReactDOM.createPortal(CopyButton, anchorElement), + document.createElement('div') + ) + } + if (showOnHoverOnly) anchorElement.classList.add(css.hover) + }, [CopyButton, anchorElement, portal, showOnHoverOnly]) React.useEffect(() => () => window.clearTimeout(timeoutRef.current), []) - return () + return portal ? null : CopyButton } export const XCopyableText = ({ model }: { model: CopyableText }) => { @@ -111,32 +128,27 @@ export const XCopyableText = ({ model }: { model: CopyableText }) => { if (inputEl) setInputEl(inputEl) }, []) - React.useEffect(() => { - if (!inputEl) return - ReactDOM.render( - ReactDOM.createPortal(, inputEl), - document.createElement('div') - ) - }, [inputEl, multiline, value]) - return ( - + <> + + + ) } \ No newline at end of file