PluginProbe
Code Snippets / 3.10.2
Code Snippets v3.10.2
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / common / CopyToClipboardButton.tsx

CopyToClipboardButton.tsx in Code Snippets 3.10.2, at js/components/common/CopyToClipboardButton.tsx

89 lines 2.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Spinner } from '@wordpress/components'
2 import { __ } from '@wordpress/i18n'
3 import React, { useState } from 'react'
4 import { Button } from './Button'
5 import { CopyIcon } from './icons/CopyIcon'
6 import type { ButtonProps } from './Button'
7
8 const TIMEOUT = 1500
9
10 enum Status {
11 INITIAL,
12 PROGRESSING,
13 SUCCESS,
14 ERROR
15 }
16
17 interface StatusIconProps {
18 status: Status
19 }
20
21 const StatusIcon: React.FC<StatusIconProps> = ({ status }) => {
22 switch (status) {
23 case Status.INITIAL:
24 return <CopyIcon aria-hidden="true" />
25 case Status.PROGRESSING:
26 return <span className="spinner-wrapper" aria-hidden="true"><Spinner /></span>
27 case Status.SUCCESS:
28 return <span className="dashicons dashicons-yes" aria-hidden="true"></span>
29 case Status.ERROR:
30 return <span className="dashicons dashicons-warning" aria-hidden="true"></span>
31 }
32 }
33
34 export interface CopyToClipboardButtonProps extends ButtonProps {
35 text: string
36 timeout?: number
37 }
38
39 const STATUS_MESSAGES: Record<Status, string> = {
40 [Status.INITIAL]: '',
41 [Status.PROGRESSING]: __('Copying to clipboard…', 'code-snippets'),
42 [Status.SUCCESS]: __('Copied to clipboard.', 'code-snippets'),
43 [Status.ERROR]: __('Failed to copy to clipboard. Please try again.', 'code-snippets')
44 }
45
46 export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
47 text,
48 timeout = TIMEOUT,
49 ...props
50 }) => {
51 const [status, setStatus] = useState(Status.INITIAL)
52 const clipboard = window.navigator.clipboard as Clipboard | undefined
53
54 const handleClick = () => {
55 setStatus(Status.PROGRESSING)
56
57 clipboard?.writeText(text)
58 .then(() => {
59 setStatus(Status.SUCCESS)
60 setTimeout(() => setStatus(Status.INITIAL), timeout)
61 })
62 .catch((error: unknown) => {
63 console.error('Failed to copy text to clipboard.', error)
64 setStatus(Status.ERROR)
65 setTimeout(() => setStatus(Status.INITIAL), timeout)
66 })
67 }
68
69 return clipboard && window.isSecureContext
70 ? <>
71 <Button
72 className="code-snippets-copy-text"
73 onClick={handleClick}
74 {...props}
75 >
76 <StatusIcon status={status} />
77 {__('Copy', 'code-snippets')}
78 </Button>
79 <span
80 className="screen-reader-text"
81 role={Status.ERROR === status ? 'alert' : 'status'}
82 aria-live={Status.ERROR === status ? 'assertive' : 'polite'}
83 >
84 {STATUS_MESSAGES[status]}
85 </span>
86 </>
87 : null
88 }
89