1- import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState , useMemo , useRef , useEffect } from 'react' ;
1+ import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState } from 'react' ;
22import { ValueView , ValueViewProps , Colon , Label , LabelProps , Line , typeMap } from './value' ;
33import { TriangleArrow } from './arrow/TriangleArrow' ;
44import { useExpandsStatus , store } from './store' ;
55import { JsonViewProps } from './' ;
66import { Copied } from './copied' ;
7+ import { Semicolon } from './semicolon' ;
78
89export interface MetaProps extends LabelProps {
910 isArray ?: boolean ;
1011 start ?: boolean ;
1112 render ?: ( props : Pick < MetaProps , 'start' | 'isArray' | 'className' | 'children' > ) => JSX . Element ;
1213}
1314
14- export function usePrevious < T > ( value : T ) {
15- const ref = useRef < T > ( ) ;
16- useEffect ( ( ) => {
17- ref . current = value ;
18- } ) ;
19- return ref . current ;
20- }
21-
2215export function Meta ( props : MetaProps ) {
2316 const { isArray = false , start = false , className, render, ...reset } = props ;
2417 const mark = isArray ? '[]' : '{}' ;
@@ -52,78 +45,7 @@ export const Ellipsis: FC<PropsWithChildren<EllipsisProps>> = ({ style, render,
5245 </ span >
5346 ) ;
5447} ;
55- export interface SemicolonProps extends LabelProps {
56- show ?: boolean ;
57- highlightUpdates ?: boolean ;
58- quotes ?: JsonViewProps < object > [ 'quotes' ] ;
59- value ?: object ;
60- render ?: ( props : Omit < SemicolonProps , 'show' > & { } ) => JSX . Element ;
61- }
62- export const Semicolon : FC < PropsWithChildren < SemicolonProps > > = ( {
63- children,
64- render,
65- color,
66- value,
67- className = 'w-rjv-object-key' ,
68- show,
69- highlightUpdates,
70- quotes,
71- ...props
72- } ) => {
73- const prevValue = usePrevious ( value ) ;
74- const highlightContainer = useRef < HTMLSpanElement > ( null )
75- const isHighlight = useMemo ( ( ) => {
76- if ( ! highlightUpdates || prevValue === undefined ) return false
77- // highlight if value type changed
78- if ( typeof value !== typeof prevValue ) {
79- return true
80- }
81- if ( typeof value === 'number' ) {
82- // notice: NaN !== NaN
83- if ( isNaN ( value ) && isNaN ( prevValue as unknown as number ) ) return false
84- return value !== prevValue
85- }
86- // highlight if isArray changed
87- if ( Array . isArray ( value ) !== Array . isArray ( prevValue ) ) {
88- return true
89- }
90- // not highlight object/function
91- // deep compare they will be slow
92- if ( typeof value === 'object' || typeof value === 'function' ) {
93- return false
94- }
95-
96- // highlight if not equal
97- if ( value !== prevValue ) {
98- return true
99- }
10048
101- return false
102- } , [ highlightUpdates , value ] ) ;
103-
104- useEffect ( ( ) => {
105- if ( highlightContainer . current && isHighlight && 'animate' in highlightContainer . current ) {
106- highlightContainer . current . animate (
107- [
108- { backgroundColor : 'var(--w-rjv-update-color, #ebcb8b)' } ,
109- { backgroundColor : '' }
110- ] ,
111- {
112- duration : 1000 ,
113- easing : 'ease-in'
114- }
115- )
116- }
117- } , [ isHighlight , value ] ) ;
118-
119- const content = show ? `${ quotes } ${ children } ${ quotes } ` : children ;
120- if ( render ) return render ( { className, ...props , value, style : { color } , children : content } ) ;
121- return (
122- < Label className = { className } color = { color } { ...props } ref = { highlightContainer } >
123- { content }
124- </ Label >
125- ) ;
126- } ;
12749export const CountInfo : FC < PropsWithChildren < LabelProps > > = ( { children } ) => (
12850 < Label
12951 style = { { paddingLeft : 4 , fontStyle : 'italic' } }
@@ -149,6 +71,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
14971 displayObjectSize = true ,
15072 enableClipboard = true ,
15173 highlightUpdates = true ,
74+ objectSortKeys = false ,
15275 indentWidth = 15 ,
15376 collapsed,
15477 level = 1 ,
@@ -159,7 +82,6 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
15982 ...reset
16083 } = props ;
16184 const isArray = Array . isArray ( value ) ;
162- const nameKeys = ( isArray ? Object . keys ( value ) . map ( m => Number ( m ) ) : Object . keys ( value ) ) as ( keyof typeof value ) [ ] ;
16385 const subkeyid = useId ( ) ;
16486 const expands = useExpandsStatus ( ) ;
16587 const expand = expands [ keyid ] ?? ( typeof collapsed === 'boolean' ? collapsed : ( typeof collapsed === 'number' ? level <= collapsed : true ) ) ;
@@ -203,6 +125,15 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
203125 eventProps . onMouseEnter = ( ) => setShowTools ( true ) ;
204126 eventProps . onMouseLeave = ( ) => setShowTools ( false ) ;
205127 }
128+ const nameKeys = ( isArray ? Object . keys ( value ) . map ( m => Number ( m ) ) : Object . keys ( value ) ) as ( keyof typeof value ) [ ] ;
129+
130+ // object
131+ let entries : [ key : string | number , value : unknown ] [ ] = isArray ? Object . entries ( value ) . map ( m => [ Number ( m [ 0 ] ) , m [ 1 ] ] ) : Object . entries ( value ) ;
132+ if ( objectSortKeys ) {
133+ entries = objectSortKeys === true
134+ ? entries . sort ( ( [ a ] , [ b ] ) => typeof a === 'string' && typeof b === 'string' ? a . localeCompare ( b ) : 0 )
135+ : entries . sort ( ( [ a ] , [ b ] ) => typeof a === 'string' && typeof b === 'string' ? objectSortKeys ( a , b ) : 0 )
136+ }
206137 return (
207138 < div { ...reset } className = { `${ className } w-rjv-inner` } { ...eventProps } >
208139 < Line style = { { display : 'inline-flex' , alignItems : 'center' } } onClick = { handle } >
@@ -229,9 +160,9 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
229160 </ Line >
230161 { expand && (
231162 < Line className = "w-rjv-content" style = { { borderLeft : 'var(--w-rjv-border-left-width, 1px) solid var(--w-rjv-line-color, #ebebeb)' , marginLeft : 6 } } >
232- { nameKeys . length > 0 &&
233- nameKeys . map ( ( key , idx ) => {
234- const item = value [ key ] ;
163+ { entries . length > 0 &&
164+ entries . map ( ( [ key , itemVal ] , idx ) => {
165+ const item = itemVal as T ;
235166 const renderKey = (
236167 < Semicolon
237168 value = { item }
@@ -246,7 +177,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
246177 ) ;
247178 const isEmpty = ( Array . isArray ( item ) && ( item as [ ] ) . length === 0 ) || ( typeof item === 'object' && item && ! ( ( item as any ) instanceof Date ) && Object . keys ( item ) . length === 0 ) ;
248179 if ( Array . isArray ( item ) && ! isEmpty ) {
249- const label = isArray ? idx : key ;
180+ const label = ( isArray ? idx : key ) as string ;
250181 return (
251182 < Line key = { label + idx } className = "w-rjv-wrap" >
252183 < RooNode value = { item } keyid = { keyid + subkeyid + label } keyName = { label } { ...subNodeProps } />
@@ -255,7 +186,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
255186 }
256187 if ( typeof item === 'object' && item && ! ( ( item as any ) instanceof Date ) && ! isEmpty ) {
257188 return (
258- < Line key = { key + idx } className = "w-rjv-wrap" >
189+ < Line key = { key + '' + idx } className = "w-rjv-wrap" >
259190 < RooNode keyid = { keyid + subkeyid + key } value = { item } keyName = { key } { ...subNodeProps } />
260191 </ Line >
261192 ) ;
0 commit comments