1
- import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState } from 'react' ;
1
+ import { FC , Fragment , PropsWithChildren , useId , cloneElement , useState , useMemo , useRef , useEffect } 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' ;
@@ -11,6 +11,14 @@ export interface MetaProps extends LabelProps {
11
11
render ?: ( props : Pick < MetaProps , 'start' | 'isArray' | 'className' | 'children' > ) => JSX . Element ;
12
12
}
13
13
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
+
14
22
export function Meta ( props : MetaProps ) {
15
23
const { isArray = false , start = false , className, render, ...reset } = props ;
16
24
const mark = isArray ? '[]' : '{}' ;
@@ -46,6 +54,7 @@ export const Ellipsis: FC<PropsWithChildren<EllipsisProps>> = ({ style, render,
46
54
} ;
47
55
export interface SemicolonProps extends LabelProps {
48
56
show ?: boolean ;
57
+ highlightUpdates ?: boolean ;
49
58
quotes ?: JsonViewProps < object > [ 'quotes' ] ;
50
59
value ?: object ;
51
60
render ?: ( props : Omit < SemicolonProps , 'show' > & { } ) => JSX . Element ;
@@ -57,13 +66,60 @@ export const Semicolon: FC<PropsWithChildren<SemicolonProps>> = ({
57
66
value,
58
67
className = 'w-rjv-object-key' ,
59
68
show,
69
+ highlightUpdates,
60
70
quotes,
61
71
...props
62
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
+
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
+
63
119
const content = show ? `${ quotes } ${ children } ${ quotes } ` : children ;
64
120
if ( render ) return render ( { className, ...props , value, style : { color } , children : content } ) ;
65
121
return (
66
- < Label className = { className } color = { color } { ...props } >
122
+ < Label className = { className } color = { color } { ...props } ref = { highlightContainer } >
67
123
{ content }
68
124
</ Label >
69
125
) ;
@@ -92,6 +148,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
92
148
components = { } ,
93
149
displayObjectSize = true ,
94
150
enableClipboard = true ,
151
+ highlightUpdates = true ,
95
152
indentWidth = 15 ,
96
153
collapsed,
97
154
level = 1 ,
@@ -117,6 +174,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
117
174
displayDataTypes,
118
175
displayObjectSize,
119
176
enableClipboard,
177
+ highlightUpdates,
120
178
onCopied,
121
179
onExpand,
122
180
collapsed,
@@ -178,6 +236,7 @@ export function RooNode<T extends object>(props: RooNodeProps<T>) {
178
236
< Semicolon
179
237
value = { item }
180
238
quotes = { quotes }
239
+ highlightUpdates = { highlightUpdates }
181
240
render = { components . objectKey }
182
241
color = { typeof key === 'number' ? typeMap [ 'number' ] . color : '' }
183
242
show = { typeof key === 'string' }
0 commit comments