@@ -2,17 +2,185 @@ import React from 'react';
22import PropTypes from 'prop-types' ;
33import { useSelector } from 'react-redux' ;
44import url from 'url' ;
5- import Inspector from 'react-inspector' ;
6- import { withStyles , createMuiTheme , useTheme } from '@material-ui/core/styles' ;
5+ import ExpandIcon from '@material-ui/icons/ExpandMore' ;
6+ import CollapseIcon from '@material-ui/icons/ChevronRight' ;
7+ import TreeView from '@material-ui/lab/TreeView' ;
8+ import TreeItem from '@material-ui/lab/TreeItem' ;
9+ import clsx from 'clsx' ;
10+ import { makeStyles , withStyles , createMuiTheme , lighten } from '@material-ui/core/styles' ;
711import FormControlLabel from '@material-ui/core/FormControlLabel' ;
812import Switch from '@material-ui/core/Switch' ;
913
14+ /**
15+ * @param {unknown } value
16+ */
17+ function useType ( value ) {
18+ if ( Array . isArray ( value ) ) {
19+ return 'array' ;
20+ }
21+ if ( value === null ) {
22+ return 'null' ;
23+ }
24+
25+ return typeof value ;
26+ }
27+
28+ /**
29+ *
30+ * @param {unknown } value
31+ * @param {ReturnType<typeof useType> } type
32+ */
33+ function useLabel ( value , type ) {
34+ switch ( type ) {
35+ case 'array' :
36+ return `Array(${ value . length } )` ;
37+ case 'null' :
38+ return 'null' ;
39+ case 'undefined' :
40+ return 'undefined' ;
41+ case 'function' :
42+ return `f ${ value . name } ()` ;
43+ case 'object' :
44+ return 'Object' ;
45+ case 'string' :
46+ return `"${ value } "` ;
47+ case 'symbol' :
48+ return `Symbol(${ String ( value ) } )` ;
49+ case 'bigint' :
50+ case 'boolean' :
51+ case 'number' :
52+ default :
53+ return String ( value ) ;
54+ }
55+ }
56+
57+ function useTokenType ( type ) {
58+ switch ( type ) {
59+ case 'object' :
60+ case 'array' :
61+ return 'comment' ;
62+ default :
63+ return type ;
64+ }
65+ }
66+
67+ function ObjectEntryLabel ( { objectKey, objectValue } ) {
68+ const type = useType ( objectValue ) ;
69+ const label = useLabel ( objectValue , type ) ;
70+ const tokenType = useTokenType ( type ) ;
71+
72+ return (
73+ < React . Fragment >
74+ { objectKey } : < span className = { clsx ( 'token' , tokenType ) } > { label } </ span >
75+ </ React . Fragment >
76+ ) ;
77+ }
78+ ObjectEntryLabel . propTypes = { objectKey : PropTypes . any , objectValue : PropTypes . any } ;
79+
80+ const useObjectEntryStyles = makeStyles ( {
81+ treeItem : {
82+ '&:focus > $treeItemContent' : {
83+ backgroundColor : lighten ( '#333' , 0.08 ) ,
84+ outline : `2px dashed ${ lighten ( '#333' , 0.3 ) } ` ,
85+ } ,
86+ } ,
87+ treeItemContent : {
88+ '&:hover' : {
89+ backgroundColor : lighten ( '#333' , 0.08 ) ,
90+ } ,
91+ } ,
92+ } ) ;
93+
94+ function ObjectEntry ( props ) {
95+ const { nodeId, objectKey, objectValue } = props ;
96+
97+ const keyPrefix = nodeId ;
98+
99+ let children = null ;
100+ if (
101+ ( objectValue !== null && typeof objectValue === 'object' ) ||
102+ typeof objectValue === 'function'
103+ ) {
104+ children =
105+ Object . keys ( objectValue ) . length === 0
106+ ? undefined
107+ : Object . keys ( objectValue ) . map ( key => {
108+ return (
109+ < ObjectEntry
110+ key = { key }
111+ nodeId = { `${ keyPrefix } .${ key } ` }
112+ objectKey = { key }
113+ objectValue = { objectValue [ key ] }
114+ />
115+ ) ;
116+ } ) ;
117+ }
118+
119+ const classes = useObjectEntryStyles ( ) ;
120+
121+ return (
122+ < TreeItem
123+ classes = { { root : classes . treeItem , content : classes . treeItemContent } }
124+ nodeId = { nodeId }
125+ label = { < ObjectEntryLabel objectKey = { objectKey } objectValue = { objectValue } /> }
126+ >
127+ { children }
128+ </ TreeItem >
129+ ) ;
130+ }
131+ ObjectEntry . propTypes = {
132+ nodeId : PropTypes . string . isRequired ,
133+ objectKey : PropTypes . any . isRequired ,
134+ objectValue : PropTypes . any ,
135+ } ;
136+
137+ function Inspector ( props ) {
138+ const { data, expandPaths } = props ;
139+
140+ const keyPrefix = '$ROOT' ;
141+ const defaultExpanded = React . useMemo ( ( ) => {
142+ return Array . isArray ( expandPaths )
143+ ? expandPaths . map ( expandPath => `${ keyPrefix } .${ expandPath } ` )
144+ : [ ] ;
145+ } , [ keyPrefix , expandPaths ] ) ;
146+ // for default* to take effect we need to remount
147+ const key = React . useMemo ( ( ) => defaultExpanded . join ( '' ) , [ defaultExpanded ] ) ;
148+
149+ return (
150+ < TreeView
151+ key = { key }
152+ defaultCollapseIcon = { < ExpandIcon /> }
153+ defaultEndIcon = { < div style = { { width : 24 } } /> }
154+ defaultExpanded = { defaultExpanded }
155+ defaultExpandIcon = { < CollapseIcon /> }
156+ >
157+ { Object . keys ( data ) . map ( objectKey => {
158+ return (
159+ < ObjectEntry
160+ key = { objectKey }
161+ nodeId = { `${ keyPrefix } .${ objectKey } ` }
162+ objectKey = { objectKey }
163+ objectValue = { data [ objectKey ] }
164+ />
165+ ) ;
166+ } ) }
167+ </ TreeView >
168+ ) ;
169+ }
170+
171+ Inspector . propTypes = {
172+ data : PropTypes . any ,
173+ expandPaths : PropTypes . arrayOf ( PropTypes . string ) ,
174+ } ;
175+
10176const styles = theme => ( {
11177 root : {
178+ backgroundColor : '#333' ,
179+ borderRadius : 4 ,
180+ color : '#fff' ,
181+ display : 'block' ,
12182 padding : theme . spacing ( 2 ) ,
13183 paddingTop : 0 ,
14- // Match <Inspector /> default theme.
15- backgroundColor : theme . palette . type === 'light' ? theme . palette . common . white : '#242424' ,
16184 minHeight : theme . spacing ( 40 ) ,
17185 width : '100%' ,
18186 } ,
@@ -21,9 +189,32 @@ const styles = theme => ({
21189 } ,
22190} ) ;
23191
192+ function computeNodeIds ( object , prefix ) {
193+ if ( ( object !== null && typeof object === 'object' ) || typeof object === 'function' ) {
194+ const ids = [ ] ;
195+ Object . keys ( object ) . forEach ( key => {
196+ ids . push ( `${ prefix } ${ key } ` , ...computeNodeIds ( object [ key ] , `${ prefix } ${ key } .` ) ) ;
197+ } ) ;
198+
199+ return ids ;
200+ }
201+ return [ ] ;
202+ }
203+
204+ function useNodeIdsLazy ( object ) {
205+ const [ allNodeIds , setAllNodeIds ] = React . useState ( [ ] ) ;
206+ // technically we want to compute them lazily until we need them (expand all)
207+ // yielding is good enough. technically we want to schedule the computation
208+ // with low pri and upgrade the priority later
209+ React . useEffect ( ( ) => {
210+ setAllNodeIds ( computeNodeIds ( object , '' ) ) ;
211+ } , [ object ] ) ;
212+
213+ return allNodeIds ;
214+ }
215+
24216function DefaultTheme ( props ) {
25217 const { classes } = props ;
26- const docsTheme = useTheme ( ) ;
27218 const [ checked , setChecked ] = React . useState ( false ) ;
28219 const [ expandPaths , setExpandPaths ] = React . useState ( null ) ;
29220 const t = useSelector ( state => state . options . t ) ;
@@ -45,12 +236,16 @@ function DefaultTheme(props) {
45236 ) ;
46237 } , [ ] ) ;
47238
48- const theme = createMuiTheme ( {
49- palette : {
50- type : docsTheme . palette . type ,
51- } ,
52- direction : docsTheme . direction ,
53- } ) ;
239+ const data = React . useMemo ( createMuiTheme , [ ] ) ;
240+
241+ const allNodeIds = useNodeIdsLazy ( data ) ;
242+ React . useDebugValue ( allNodeIds ) ;
243+ React . useEffect ( ( ) => {
244+ if ( checked ) {
245+ // in case during the event handler allNodeIds wasn't computed yet
246+ setExpandPaths ( allNodeIds ) ;
247+ }
248+ } , [ checked , allNodeIds ] ) ;
54249
55250 return (
56251 < div className = { classes . root } >
@@ -59,20 +254,19 @@ function DefaultTheme(props) {
59254 control = {
60255 < Switch
61256 checked = { checked }
62- onChange = { ( event , value ) => {
63- setChecked ( value ) ;
257+ onChange = { ( event , newChecked ) => {
258+ setChecked ( newChecked ) ;
259+ if ( newChecked ) {
260+ setExpandPaths ( allNodeIds ) ;
261+ } else {
262+ setExpandPaths ( [ ] ) ;
263+ }
64264 } }
65265 />
66266 }
67267 label = { t ( 'expandAll' ) }
68268 />
69- < Inspector
70- theme = { theme . palette . type === 'light' ? 'chromeLight' : 'chromeDark' }
71- data = { theme }
72- expandPaths = { expandPaths }
73- expandLevel = { checked ? 100 : 1 }
74- key = { `${ checked } -${ theme . palette . type } ` } // Remount
75- />
269+ < Inspector data = { data } expandPaths = { expandPaths } expandLevel = { checked ? 100 : 1 } />
76270 </ div >
77271 ) ;
78272}
0 commit comments