diff --git a/examples/App.tsx b/examples/App.tsx index fb14584..4194fd8 100644 --- a/examples/App.tsx +++ b/examples/App.tsx @@ -28,14 +28,26 @@ const App = () => { {' '} is a react component for displaying serializable data.

- You can open the repo{' '} + You can open the Github repo + + + { darkMode: mode === 'dark', originSource, renderSource, - rjvProps: item.props, + rjvProps: item.props }} /> diff --git a/examples/components/SourceRender/index.tsx b/examples/components/SourceRender/index.tsx index c0bb1fc..13dfd6c 100644 --- a/examples/components/SourceRender/index.tsx +++ b/examples/components/SourceRender/index.tsx @@ -10,14 +10,8 @@ const ExchangeIcon = (props: React.SVGAttributes) => { viewBox="0 0 24 24" {...props} > - + + ); }; @@ -50,7 +44,7 @@ export const SourceRender = ({ darkMode, originSource, renderSource, - rjvProps, + rjvProps }: Props) => { return (

diff --git a/src/ReactJsonView/components/CopyContent.tsx b/src/ReactJsonView/components/CopyContent.tsx index 9efeeb8..d2f69a6 100644 --- a/src/ReactJsonView/components/CopyContent.tsx +++ b/src/ReactJsonView/components/CopyContent.tsx @@ -1,29 +1,29 @@ -import { Fragment, useCallback, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import copy from 'copy-to-clipboard'; -import React from 'react'; -import { cutOffStringLiteral, useSwitchWithDelay } from '../../utils'; +import React, { MouseEvent } from 'react'; +import { useSwitchWithDelay } from '../../utils'; import { CheckMark, CrossMark, TextCopy } from './SvgIcon'; interface Props { - content: string; + data: string; } -const CopyContent: React.FC = ({ content }) => { +const Copyable: React.FC = ({ data }) => { const [state, switchState] = useSwitchWithDelay(0); - const computedContent = useMemo(() => { - if (typeof content !== 'string') return content; - return cutOffStringLiteral(content); - }, [content]); - const onCopy = useCallback(() => { - if (state !== 0) return; - const copyResult = copy(`${content}`); - if (copyResult) { - switchState(1); - } else { - switchState(-1); - } - }, [content, state, switchState]); + const onCopy = useCallback( + (e: MouseEvent) => { + e.stopPropagation(); + if (state !== 0) return; + const copyResult = copy(`${data}`); + if (copyResult) { + switchState(1); + } else { + switchState(-1); + } + }, + [data, state, switchState] + ); const icon = useMemo(() => { let Icon = TextCopy; @@ -43,17 +43,13 @@ const CopyContent: React.FC = ({ content }) => { return ; }, [state]); - if (computedContent === content) - return React.createElement(Fragment, null, content); - return ( - - {computedContent} - + + {icon} ); }; -export default CopyContent; +export default Copyable; diff --git a/src/ReactJsonView/components/JsonNode.tsx b/src/ReactJsonView/components/JsonNode.tsx index a6e367a..fed8dc5 100644 --- a/src/ReactJsonView/components/JsonNode.tsx +++ b/src/ReactJsonView/components/JsonNode.tsx @@ -1,21 +1,32 @@ -import { useState, useMemo, ReactNode } from 'react'; +import { useState, useMemo, ReactNode, Fragment } from 'react'; import { clsx, + cutOffStringLiteral, isBoolean, isListOrMap, isNumber, - shortTitle, + shortTitle } from '../../utils'; import { ArrowRight } from './SvgIcon'; import { useConfigInfo } from './ConfigContext'; -import CopyContent from './CopyContent'; import LazyLoadMore from './LazyLoadMore'; import { Options } from '../../../types'; +import React from 'react'; +import Copyable from './CopyContent'; + +const PrimitiveContent = ({ content }: { content: React.ReactNode }) => { + const computedContent = useMemo(() => { + if (typeof content !== 'string') return content; + return cutOffStringLiteral(content); + }, [content]); + + return React.createElement(Fragment, null, computedContent); +}; const JsonNode = ({ source = null, depth = 1, - label = '', + label = '' }: { source: Options['source']; depth?: number; @@ -49,7 +60,7 @@ const JsonNode = ({ let className = ''; const type = typeof data; - const primitiveType = ['string', 'number', 'symbol', 'boolean', 'undefined']; + const primitiveType = ['string', 'number', 'boolean']; if (primitiveType.indexOf(type) > -1) { className = type; } else if (data === null) { @@ -57,7 +68,7 @@ const JsonNode = ({ } if (className) { return ( - + {labelContent && ( <> @@ -66,9 +77,10 @@ const JsonNode = ({ {labelContent} )} - - + + + ); } @@ -77,20 +89,27 @@ const JsonNode = ({ const title = shortTitle(data, maxTitleSize); return ( -
-
setExpanded(!expanded)}> +
+
setExpanded(!expanded)} + data-depth={depth - 1} + > {labelContent} - {(!label || !expanded) && {title}} + {(!label || !expanded) && ( + {title} + )} +
{expanded && ( -
+
( diff --git a/src/ReactJsonView/index.less b/src/ReactJsonView/index.less index f70bcc5..9e5f8ef 100644 --- a/src/ReactJsonView/index.less +++ b/src/ReactJsonView/index.less @@ -2,16 +2,18 @@ @fontFamily: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace; -.rjv-node { - font-size: @fontSize; - line-height: 1.4; +.rjv-ref { code { font-family: @fontFamily; } - &__title { + &-node { + font-size: @fontSize; + line-height: 1.4; + } + &-title { cursor: default; } - &__spread-controller { + &-arrow { transform: rotateZ(0); transform-origin: 35% center; transition: transform linear 0.1s; @@ -19,26 +21,11 @@ transform: rotateZ(90deg); } } - &__property { + &-property { margin-left: 12px; - &-key { - font-size: @fontSize; - font-family: @fontFamily; - color: #7d237c; - } - } - .rjv-load-more { - color: #7d237c; - font-size: 13px; - > span { - cursor: pointer; - &:hover { - text-decoration: underline; - } - } } } -.rjv-type-node { +.rjv-primitive-type { font-size: @fontSize; font-family: @fontFamily; &.number, @@ -54,15 +41,6 @@ &::after { content: '"'; } - .copyable { - &-icon { - position: absolute; - right: 0; - bottom: 0; - transform: translate(17px, 4px); - cursor: pointer; - } - } } &.symbol { color: #be3c31; @@ -76,12 +54,47 @@ } } +.rjv-node__property-key { + font-size: @fontSize; + font-family: @fontFamily; + color: #7d237c; +} + +.rjv-load-more { + color: #7d237c; + font-size: 13px; + > span { + cursor: pointer; + &:hover { + text-decoration: underline; + } + } +} + +.rjv-copyable { + display: inline-block; + margin-left: 4px; + cursor: pointer; + visibility: hidden; + transform: translateY(2px); +} + + +.rjv-primitive, +.rjv-ref-title { + &:hover { + .rjv-copyable { + visibility: visible; + } + } +} + [data-dark-mode='true'] { .rjv-node__property-key, .rjv-load-more { color: #70afd3; } - .rjv-type-node { + .rjv-primitive-type { &.number, &.boolean { color: #9384f7; diff --git a/src/utils/index.ts b/src/utils/index.ts index d9d0b58..2921bbf 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -58,7 +58,7 @@ export function shortTitle(data: any, max = 100) { if (typeof curVal === 'string') { curVal = cutOffStringLiteral(curVal); } - const curStr = `${key}: ${JSON.stringify(curVal)}`; + const curStr = `"${key}": ${JSON.stringify(curVal)}`; const result = `${content}${curStr}, `; if (result.length > max) { hasEllipsis = true;