Skip to content

Commit d6e3c39

Browse files
authored
#160 Handle clipboard errors (#162)
1 parent 9de6b37 commit d6e3c39

File tree

4 files changed

+59
-14
lines changed

4 files changed

+59
-14
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,11 @@ A similar callback is executed whenever an item is copied to the clipboard (if p
248248
type // Either "path" or "value" depending on whether "Cmd/Ctrl" was pressed
249249
stringValue // A nicely stringified version of `value`
250250
// (i.e. what the clipboard actually receives)
251+
success // true/false -- whether clipboard copy action actually succeeded
252+
errorMessage// Error detail if success === false
251253
```
252254

253-
Since there is very little user feedback when clicking "Copy", a good idea would be to present some kind of notification in this callback.
255+
Since there is very little user feedback when clicking "Copy", a good idea would be to present some kind of notification in this callback. There are situations (such as an insecure environment) where the browser won't actually permit any clipboard actions. In this case, the `success` property will be `false`, so you can handle it appropriately.
254256

255257
### Custom Buttons
256258

demo/src/App.tsx

+17-8
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,23 @@ function App() {
363363
}
364364
enableClipboard={
365365
allowCopy
366-
? ({ stringValue, type }) =>
367-
toast({
368-
title: `${type === 'value' ? 'Value' : 'Path'} copied to clipboard:`,
369-
description: truncate(String(stringValue)),
370-
status: 'success',
371-
duration: 5000,
372-
isClosable: true,
373-
})
366+
? ({ stringValue, type, success, errorMessage }) => {
367+
success
368+
? toast({
369+
title: `${type === 'value' ? 'Value' : 'Path'} copied to clipboard:`,
370+
description: truncate(String(stringValue)),
371+
status: 'success',
372+
duration: 5000,
373+
isClosable: true,
374+
})
375+
: toast({
376+
title: 'Problem copying to clipboard',
377+
description: errorMessage,
378+
status: 'error',
379+
duration: 5000,
380+
isClosable: true,
381+
})
382+
}
374383
: false
375384
}
376385
restrictEdit={restrictEdit}

src/ButtonPanels.tsx

+36-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type NodeData,
1111
type CustomButtonDefinition,
1212
type KeyboardControlsFull,
13+
type JsonData,
1314
} from './types'
1415
import { getModifier } from './helpers'
1516

@@ -67,8 +68,10 @@ export const EditButtons: React.FC<EditButtonProps> = ({
6768
const handleCopy = (e: React.MouseEvent<HTMLElement>) => {
6869
e.stopPropagation()
6970
let copyType: CopyType = 'value'
70-
let value
71+
let value: JsonData
7172
let stringValue = ''
73+
let success: boolean
74+
let errorMessage: string | null = null
7275
if (enableClipboard) {
7376
const modifier = getModifier(e)
7477
if (modifier && keyboardControls.clipboardModifier.includes(modifier)) {
@@ -79,10 +82,39 @@ export const EditButtons: React.FC<EditButtonProps> = ({
7982
value = data
8083
stringValue = type ? JSON.stringify(data, null, 2) : String(value)
8184
}
82-
void navigator.clipboard?.writeText(stringValue)
83-
if (typeof enableClipboard === 'function') {
84-
enableClipboard({ value, stringValue, path, key, type: copyType })
85+
if (!navigator.clipboard) {
86+
if (typeof enableClipboard === 'function')
87+
enableClipboard({
88+
success: false,
89+
value,
90+
stringValue,
91+
path,
92+
key,
93+
type: copyType,
94+
errorMessage: "Can't access clipboard API",
95+
})
96+
return
8597
}
98+
navigator.clipboard
99+
?.writeText(stringValue)
100+
.then(() => (success = true))
101+
.catch((err) => {
102+
success = false
103+
errorMessage = err.message
104+
})
105+
.finally(() => {
106+
if (typeof enableClipboard === 'function') {
107+
enableClipboard({
108+
success,
109+
errorMessage,
110+
value,
111+
stringValue,
112+
path,
113+
key,
114+
type: copyType,
115+
})
116+
}
117+
})
86118
}
87119
}
88120

src/types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ export type SearchFilterInputFunction = (
139139

140140
export type CopyType = 'path' | 'value'
141141
export type CopyFunction = (input: {
142+
success: boolean
143+
errorMessage: string | null
142144
key: CollectionKey
143145
path: CollectionKey[]
144146
value: unknown
@@ -208,7 +210,7 @@ export interface NodeData {
208210
path: CollectionKey[]
209211
level: number
210212
index: number
211-
value: unknown
213+
value: JsonData
212214
size: number | null
213215
parentData: object | null
214216
fullData: JsonData

0 commit comments

Comments
 (0)