diff --git a/src/components/DataKeyPair.tsx b/src/components/DataKeyPair.tsx index 76bf006e..bad294d0 100644 --- a/src/components/DataKeyPair.tsx +++ b/src/components/DataKeyPair.tsx @@ -5,11 +5,11 @@ import { Edit as EditIcon } from '@mui/icons-material' import { Box, styled } from '@mui/material' -import copy from 'copy-to-clipboard' import type React from 'react' import { useCallback, useMemo, useState } from 'react' import { useTextColor } from '../hooks/useColor' +import { useClipboard } from '../hooks/useCopyToClipboard' import { useInspect } from '../hooks/useInspect' import { useJsonViewerStore } from '../stores/JsonViewerStore' import { useTypeComponents } from '../stores/typeRegistry' @@ -22,7 +22,7 @@ export type DataKeyPairProps = { path: (string | number)[] } -const IconBox = styled(props => )` +const IconBox = styled(props => )` cursor: pointer; padding-left: 0.7rem; ` as typeof Box @@ -56,6 +56,8 @@ export const DataKeyPair: React.FC = (props) => { setInspect, value }), [inspect, path, setInspect, value]) + const { copy, copied } = useClipboard() + const actionIcons = useMemo(() => { if (editing) { return ( @@ -101,11 +103,23 @@ export const DataKeyPair: React.FC = (props) => { event.preventDefault() }} > - + { + copied + ? ( + + ) + : ( + + ) + } {/* todo: support edit object */} {Editor && @@ -126,15 +140,15 @@ export const DataKeyPair: React.FC = (props) => { } ) - }, [Editor, editing, onChange, path, tempValue, value]) + }, [Editor, copied, copy, editing, onChange, path, tempValue, value]) const expandable = PreComponent && PostComponent const KeyRenderer = useJsonViewerStore(store => store.keyRenderer) return ( setHover(path), [setHover, path]) - } + onMouseEnter={ + useCallback(() => setHover(path), [setHover, path]) + } > = (props) => { > { KeyRenderer.when(downstreamProps) - ? + ? : !props.nested && ( isNumberKey ? {displayKey} + style={{ color: numberKeyColor }}>{displayKey} : <>"{displayKey}" ) } @@ -169,18 +183,18 @@ export const DataKeyPair: React.FC = (props) => { : ) } - {PreComponent && } + {PreComponent && } {(isHover && expandable && inspect) && actionIcons} { editing - ? (Editor && ) + ? (Editor && ) : (Component) - ? + ? : {`fallback: ${value}`} + className='data-value-fallback'>{`fallback: ${value}`} } - {PostComponent && } + {PostComponent && } {(isHover && expandable && !inspect) && actionIcons} {(isHover && !expandable) && actionIcons} diff --git a/src/hooks/useCopyToClipboard.ts b/src/hooks/useCopyToClipboard.ts new file mode 100644 index 00000000..5c72f66b --- /dev/null +++ b/src/hooks/useCopyToClipboard.ts @@ -0,0 +1,43 @@ +import copyToClipboard from 'copy-to-clipboard' +import { useCallback, useRef, useState } from 'react' + +/** + * useClipboard hook accepts one argument options in which copied status timeout duration is defined (defaults to 2000). Hook returns object with properties: + * - copy – function to copy value to clipboard + * - copied – value that indicates that copy handler was called less than options.timeout ms ago + * - reset – function to clear timeout and reset copied to false + */ +export function useClipboard ({ timeout = 2000 } = {}) { + const [copied, setCopied] = useState(false) + const copyTimeout = useRef(null) + + const handleCopyResult = useCallback((value: boolean) => { + if (copyTimeout.current) { + clearTimeout(copyTimeout.current) + } + copyTimeout.current = setTimeout(() => setCopied(false), timeout) + setCopied(value) + }, [timeout]) + + const copy = useCallback((valueToCopy: string) => { + if ('clipboard' in navigator) { + navigator.clipboard + .writeText(valueToCopy) + .then(() => handleCopyResult(true)) + // When navigator.clipboard throws an error, fallback to copy-to-clipboard package + .catch(() => copyToClipboard(valueToCopy)) + } else { + // fallback to copy-to-clipboard when navigator.clipboard is not available + copyToClipboard(valueToCopy) + } + }, [handleCopyResult]) + + const reset = useCallback(() => { + setCopied(false) + if (copyTimeout.current) { + clearTimeout(copyTimeout.current) + } + }, []) + + return { copy, reset, copied } +}