-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: use native copy method #1852
base: next
Are you sure you want to change the base?
Changes from all commits
5fe782b
e900b24
01daf89
45dce74
6f1539a
55b6ec8
4755272
38a0177
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,23 +2,27 @@ | |
|
||
import React from 'react'; | ||
|
||
import ReactCopyToClipboard from 'react-copy-to-clipboard'; | ||
import {copyText} from '../../utils/copyText'; | ||
|
||
import type {CopyToClipboardProps, CopyToClipboardStatus} from './types'; | ||
|
||
const INITIAL_STATUS: CopyToClipboardStatus = 'pending'; | ||
|
||
export function CopyToClipboard(props: CopyToClipboardProps) { | ||
const {children, text, options, timeout, onCopy} = props; | ||
const {children, text, timeout, onCopy} = props; | ||
|
||
const textRef = React.useRef(text); | ||
const [status, setStatus] = React.useState<CopyToClipboardStatus>(INITIAL_STATUS); | ||
|
||
const timerIdRef = React.useRef<number>(); | ||
|
||
const content = React.useMemo(() => children(status), [children, status]); | ||
const content = React.useMemo<React.ReactElement<React.HTMLAttributes<HTMLElement>>>( | ||
() => children(status), | ||
[children, status], | ||
); | ||
|
||
const handleCopy = React.useCallback<Required<ReactCopyToClipboard.Props>['onCopy']>( | ||
(copyText, result) => { | ||
const handleCopy = React.useCallback( | ||
(copyText: string, result: boolean) => { | ||
setStatus(result ? 'success' : 'error'); | ||
window.clearTimeout(timerIdRef.current); | ||
timerIdRef.current = window.setTimeout(() => setStatus(INITIAL_STATUS), timeout); | ||
|
@@ -27,15 +31,37 @@ | |
[onCopy, timeout], | ||
); | ||
|
||
const onClickWithCopy: React.MouseEventHandler<HTMLElement> = React.useCallback( | ||
(event) => { | ||
textRef.current = text; | ||
|
||
function copy(result: boolean) { | ||
if (text === textRef.current) { | ||
handleCopy(text, result); | ||
|
||
content.props?.onClick?.(event); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we call this |
||
} | ||
} | ||
|
||
copyText(text).then( | ||
() => { | ||
copy(true); | ||
}, | ||
() => { | ||
copy(false); | ||
}, | ||
); | ||
}, | ||
[content.props, handleCopy, text], | ||
); | ||
|
||
React.useEffect(() => () => window.clearTimeout(timerIdRef.current), []); | ||
|
||
if (!React.isValidElement(content)) { | ||
throw new Error('Content must be a valid react element'); | ||
} | ||
|
||
return ( | ||
<ReactCopyToClipboard text={text} onCopy={handleCopy} options={options}> | ||
{content} | ||
</ReactCopyToClipboard> | ||
); | ||
return React.cloneElement(content, { | ||
onClick: onClickWithCopy, | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,8 @@ | ||
import React from 'react'; | ||
|
||
import {Bulb} from '@gravity-ui/icons'; | ||
import ReactCopyToClipboard from 'react-copy-to-clipboard'; | ||
|
||
import {ActionTooltip, Button, Icon} from '../../components'; | ||
import {ActionTooltip, Button, CopyToClipboard, Icon} from '../../components'; | ||
import {useUniqId} from '../../hooks'; | ||
|
||
import './ColorPanel.scss'; | ||
|
@@ -39,18 +38,20 @@ export function ColorPanel(props: ColorPanelProps) { | |
const copyText = `var(${varName})`; | ||
return ( | ||
<div className="color-panel__card" key={color.name}> | ||
<ReactCopyToClipboard text={copyText}> | ||
<div | ||
className={`color-panel__card-box ${boxBorders}`} | ||
style={{background: `var(${varName})`}} | ||
/> | ||
</ReactCopyToClipboard> | ||
<CopyToClipboard text={copyText}> | ||
{() => ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's support normal children type as well as it was in |
||
<div | ||
className={`color-panel__card-box ${boxBorders}`} | ||
style={{background: `var(${varName})`}} | ||
/> | ||
)} | ||
</CopyToClipboard> | ||
<div className="color-panel__card-texts"> | ||
<div className="color-panel__card-headline"> | ||
<div className="color-panel__card-title">{color.title}</div> | ||
<ReactCopyToClipboard text={copyText}> | ||
<div className="color-panel__card-var">{varName}</div> | ||
</ReactCopyToClipboard> | ||
<CopyToClipboard text={copyText}> | ||
{() => <div className="color-panel__card-var">{varName}</div>} | ||
</CopyToClipboard> | ||
</div> | ||
<div className="color-panel__card-description">{color.description}</div> | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function copyText(text: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file is not needed to be at the root utils, place it near the component please |
||
if (navigator?.clipboard?.writeText) { | ||
return navigator.clipboard.writeText(text); | ||
} | ||
|
||
return Promise.reject(new Error('Native copy is not available')); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's no benefit of useMemo at all, children as function typically inline in jsx, so every render is new instance