1
- import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState , useMemo , useRef , useEffect } from 'react' ;
1
+ import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState } from 'react' ;
2
2
import { ValueView , ValueViewProps , Colon , Label , LabelProps , Line , typeMap } from './value' ;
3
3
import { TriangleArrow } from './arrow/TriangleArrow' ;
4
4
import { useExpandsStatus , store } from './store' ;
5
5
import { JsonViewProps } from './' ;
6
6
import { Copied } from './copied' ;
7
+ import { Semicolon } from './semicolon' ;
7
8
8
9
export interface MetaProps extends LabelProps {
9
10
isArray ?: boolean ;
10
11
start ?: boolean ;
11
12
render ?: ( props : Pick < MetaProps , 'start' | 'isArray' | 'className' | 'children' > ) => JSX . Element ;
12
13
}
13
14
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
-
22
15
export function Meta ( props : MetaProps ) {
23
16
const { isArray = false , start = false , className, render, ...reset } = props ;
24
17
const mark = isArray ? '[]' : '{}' ;
@@ -52,78 +45,7 @@ export const Ellipsis: FC<PropsWithChildren<EllipsisProps>> = ({ style, render,
52
45
</ span >
53
46
) ;
54
47
} ;
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
- }
100
48
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
- } ;
127
49
export const CountInfo : FC < PropsWithChildren < LabelProps > > = ( { children } ) => (
128
50
< Label
129
51
style = { { paddingLeft : 4 , fontStyle : 'italic' } }
@@ -149,6 +71,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
149
71
displayObjectSize = true ,
150
72
enableClipboard = true ,
151
73
highlightUpdates = true ,
74
+ objectSortKeys = false ,
152
75
indentWidth = 15 ,
153
76
collapsed,
154
77
level = 1 ,
@@ -159,7 +82,6 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
159
82
...reset
160
83
} = props ;
161
84
const isArray = Array . isArray ( value ) ;
162
- const nameKeys = ( isArray ? Object . keys ( value ) . map ( m => Number ( m ) ) : Object . keys ( value ) ) as ( keyof typeof value ) [ ] ;
163
85
const subkeyid = useId ( ) ;
164
86
const expands = useExpandsStatus ( ) ;
165
87
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>) {
203
125
eventProps . onMouseEnter = ( ) => setShowTools ( true ) ;
204
126
eventProps . onMouseLeave = ( ) => setShowTools ( false ) ;
205
127
}
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
+ }
206
137
return (
207
138
< div { ...reset } className = { `${ className } w-rjv-inner` } { ...eventProps } >
208
139
< Line style = { { display : 'inline-flex' , alignItems : 'center' } } onClick = { handle } >
@@ -229,9 +160,9 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
229
160
</ Line >
230
161
{ expand && (
231
162
< 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 ;
235
166
const renderKey = (
236
167
< Semicolon
237
168
value = { item }
@@ -246,7 +177,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
246
177
) ;
247
178
const isEmpty = ( Array . isArray ( item ) && ( item as [ ] ) . length === 0 ) || ( typeof item === 'object' && item && ! ( ( item as any ) instanceof Date ) && Object . keys ( item ) . length === 0 ) ;
248
179
if ( Array . isArray ( item ) && ! isEmpty ) {
249
- const label = isArray ? idx : key ;
180
+ const label = ( isArray ? idx : key ) as string ;
250
181
return (
251
182
< Line key = { label + idx } className = "w-rjv-wrap" >
252
183
< RooNode value = { item } keyid = { keyid + subkeyid + label } keyName = { label } { ...subNodeProps } />
@@ -255,7 +186,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
255
186
}
256
187
if ( typeof item === 'object' && item && ! ( ( item as any ) instanceof Date ) && ! isEmpty ) {
257
188
return (
258
- < Line key = { key + idx } className = "w-rjv-wrap" >
189
+ < Line key = { key + '' + idx } className = "w-rjv-wrap" >
259
190
< RooNode keyid = { keyid + subkeyid + key } value = { item } keyName = { key } { ...subNodeProps } />
260
191
</ Line >
261
192
) ;
0 commit comments