react18-json-view
Version:
JSON viewer for react18
155 lines (135 loc) • 5.25 kB
text/typescript
import type { Collapsed, CustomizeOptions, DisplaySize, Editable } from './types'
import copy from 'copy-to-clipboard'
export function isObject(node: any): node is Record<string, any> {
return Object.prototype.toString.call(node) === '[object Object]'
}
export function objectSize(node: Record<string, any> | Array<any>) {
return Array.isArray(node) ? node.length : isObject(node) ? Object.keys(node).length : 0
}
export function stringifyForCopying(node: any, space?: string | number | undefined) {
// return single string nodes without quotes
if (typeof node === 'string') {
return node
}
try {
return JSON.stringify(
node,
(key, value) => {
switch (typeof value) {
case 'bigint':
return String(value) + 'n'
case 'number':
case 'boolean':
case 'object':
case 'string':
return value
default:
return String(value)
}
},
space
)
} catch (error: any) {
return `${error.name}: ${error.message}` || 'JSON.stringify failed'
}
}
export async function writeClipboard(value: string) {
try {
await navigator.clipboard.writeText(value)
} catch (err) {
copy(value)
}
}
export function isCollapsed(
node: Record<string, any> | Array<any>,
depth: number,
indexOrName: number | string | undefined,
collapsed: Collapsed,
collapseObjectsAfterLength: number,
customOptions?: CustomizeOptions
): boolean {
if (customOptions && customOptions.collapsed !== undefined) return !!customOptions.collapsed
if (typeof collapsed === 'boolean') return collapsed
if (typeof collapsed === 'number' && depth > collapsed) return true
const size = objectSize(node)
if (typeof collapsed === 'function') {
const result = safeCall(collapsed, [{ node, depth, indexOrName, size }])
if (typeof result === 'boolean') return result
}
if (Array.isArray(node) && size > collapseObjectsAfterLength) return true
if (isObject(node) && size > collapseObjectsAfterLength) return true
return false
}
export function isCollapsed_largeArray(
node: Record<string, any> | Array<any>,
depth: number,
indexOrName: number | string | undefined,
collapsed: Collapsed,
collapseObjectsAfterLength: number,
customOptions?: CustomizeOptions
): boolean {
if (customOptions && customOptions.collapsed !== undefined) return !!customOptions.collapsed
if (typeof collapsed === 'boolean') return collapsed
if (typeof collapsed === 'number' && depth > collapsed) return true
const size = Math.ceil(node.length / 100)
if (typeof collapsed === 'function') {
const result = safeCall(collapsed, [{ node, depth, indexOrName, size }])
if (typeof result === 'boolean') return result
}
if (Array.isArray(node) && size > collapseObjectsAfterLength) return true
if (isObject(node) && size > collapseObjectsAfterLength) return true
return false
}
export function ifDisplay(displaySize: DisplaySize, depth: number, fold: boolean) {
if (typeof displaySize === 'boolean') return displaySize
if (typeof displaySize === 'number' && depth > displaySize) return true
if (displaySize === 'collapsed' && fold) return true
if (displaySize === 'expanded' && !fold) return true
return false
}
export function safeCall<T extends (...args: any[]) => any>(func: T, params: Parameters<T>) {
try {
return func(...params)
} catch (event) {
reportError(event)
}
}
export function editableAdd(editable: Editable) {
if (editable === true) return true
if (isObject(editable) && (editable as { add: boolean }).add === true) return true
}
export function editableEdit(editable: Editable) {
if (editable === true) return true
if (isObject(editable) && (editable as { edit: boolean }).edit === true) return true
}
export function editableDelete(editable: Editable) {
if (editable === true) return true
if (isObject(editable) && (editable as { delete: boolean }).delete === true) return true
}
function isClassComponent(component: any) {
return typeof component === 'function' && !!component.prototype?.isReactComponent
}
export function isReactComponent(component: any): component is (new () => React.Component<any, any>) | React.FC<any> {
return typeof component === 'function'
}
export function customAdd(customOptions?: CustomizeOptions) {
return !customOptions || customOptions.add === undefined || !!customOptions.add
}
export function customEdit(customOptions?: CustomizeOptions) {
return !customOptions || customOptions.edit === undefined || !!customOptions.edit
}
export function customDelete(customOptions?: CustomizeOptions) {
return !customOptions || customOptions.delete === undefined || !!customOptions.delete
}
export function customCopy(customOptions?: CustomizeOptions) {
return !customOptions || customOptions.enableClipboard === undefined || !!customOptions.enableClipboard
}
export function customMatchesURL(customOptions?: CustomizeOptions) {
return !customOptions || customOptions.matchesURL === undefined || !!customOptions.matchesURL
}
export function resolveEvalFailedNewValue(type: string, value: string) {
if (type === 'string') {
return value.trim().replace(/^\"([\s\S]+?)\"$/, '$1')
}
return value
}