Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/components/DataKeyPair.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,8 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
event.preventDefault()
try {
copy(
JSON.stringify(
typeof value === 'function' ? value.toString() : value,
null,
' '
)
path,
value
)
} catch (e) {
// in some case, this will throw error
Expand Down
55 changes: 45 additions & 10 deletions src/hooks/useCopyToClipboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import copyToClipboard from 'copy-to-clipboard'
import { useCallback, useRef, useState } from 'react'

import { useJsonViewerStore } from '../stores/JsonViewerStore'
import type { JsonViewerOnCopy } from '../type'

/**
* 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
Expand All @@ -19,19 +22,51 @@ export function useClipboard ({ timeout = 2000 } = {}) {
copyTimeout.current = window.setTimeout(() => setCopied(false), timeout)
setCopied(value)
}, [timeout])
const onCopy = useJsonViewerStore(store => store.onCopy)

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))
const copy = useCallback<JsonViewerOnCopy>((path, value: unknown) => {
if (typeof onCopy === 'function') {
try {
const result = onCopy(path, value)
if (result instanceof Promise) {
result.then(() => {
handleCopyResult(true)
}).catch((error) => {
console.error(
`error when copy ${path.length === 0
? 'src'
: `src[${path.join(
'.')}`
}]`, error)
})
} else {
handleCopyResult(true)
}
} catch (error) {
console.error(
`error when copy ${path.length === 0
? 'src'
: `src[${path.join(
'.')}`
}]`, error)
}
} else {
// fallback to copy-to-clipboard when navigator.clipboard is not available
copyToClipboard(valueToCopy)
const valueToCopy = JSON.stringify(
typeof value === 'function' ? value.toString() : value,
null,
' '
)
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])
}, [handleCopyResult, onCopy])

const reset = useCallback(() => {
setCopied(false)
Expand Down
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const JsonViewerInner: React.FC<JsonViewerProps> = (props) => {
useSetIfNotUndefinedEffect('rootName', props.rootName)
useSetIfNotUndefinedEffect('displayDataTypes', props.displayDataTypes)
useSetIfNotUndefinedEffect('displayObjectSize', props.displayObjectSize)
useSetIfNotUndefinedEffect('onCopy', props.onCopy)
useEffect(() => {
if (props.theme === 'light') {
api.setState({
Expand Down
9 changes: 8 additions & 1 deletion src/stores/JsonViewerStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import create from 'zustand'
import createContext from 'zustand/context'
import { combine } from 'zustand/middleware'

import type { JsonViewerOnChange, JsonViewerProps, Path } from '..'
import type {
JsonViewerOnChange,
JsonViewerOnCopy,
JsonViewerProps,
Path
} from '..'
import type { Colorspace } from '../theme/base16'
import { lightColorspace } from '../theme/base16'
import type { JsonViewerKeyRenderer } from '../type'
Expand All @@ -28,6 +33,7 @@ export type JsonViewerState<T = unknown> = {
rootName: false | string
value: T
onChange: JsonViewerOnChange
onCopy: JsonViewerOnCopy | undefined
keyRenderer: JsonViewerKeyRenderer
displayObjectSize: boolean
}
Expand All @@ -54,6 +60,7 @@ export const createJsonViewerStore = <T = unknown> (props: JsonViewerProps<T>) =
maxDisplayLength: props.maxDisplayLength ?? 30,
rootName: props.rootName ?? 'root',
onChange: props.onChange ?? (() => {}),
onCopy: props.onCopy ?? undefined,
keyRenderer: props.keyRenderer ?? DefaultKeyRenderer,
editable: props.editable ?? false,
defaultInspectDepth: props.defaultInspectDepth ?? 5,
Expand Down
27 changes: 18 additions & 9 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,24 @@ import type { Colorspace } from './theme/base16'

export type Path = (string | number)[]

/**
* @param path path to the target value
* @param oldValue
* @param newValue
*/
export type JsonViewerOnChange = <U = unknown>(
path: (string | number)[], oldValue: U,
path: Path, oldValue: U,
newValue: U /*, type: ChangeType */) => void

/**
* @param path path to the target value
* @param value
*/
export type JsonViewerOnCopy = <U = unknown>(
path: Path,
value: U
) => unknown | Promise<unknown>

export interface DataItemProps<ValueType = unknown> {
inspect: boolean
setInspect: Dispatch<SetStateAction<boolean>>
Expand All @@ -33,7 +47,7 @@ export type DataType<ValueType = unknown> = {
}

export interface JsonViewerKeyRenderer extends React.FC<DataItemProps> {
when(props: DataItemProps): boolean
when (props: DataItemProps): boolean
}

export type JsonViewerTheme = 'light' | 'dark' | 'auto' | Colorspace
Expand All @@ -59,13 +73,8 @@ export type JsonViewerProps<T = unknown> = {
*/
keyRenderer?: JsonViewerKeyRenderer
valueTypes?: DataType<any>[]
/**
*
* @param path path to the target value
* @param oldValue
* @param newValue
*/
onChange?: <U>(path: Path, oldValue: U, newValue: U) => void
onChange?: JsonViewerOnChange
onCopy?: JsonViewerOnCopy
/**
* Whether enable clipboard feature.
*
Expand Down