Skip to content

Commit 2df006e

Browse files
authored
[docs] Replace react-inspector with custom TreeView implementa… (#17662)
1 parent 0f5a304 commit 2df006e

File tree

3 files changed

+215
-36
lines changed

3 files changed

+215
-36
lines changed

docs/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
"react-draggable": "^4.0.3",
9999
"react-final-form": "^6.3.0",
100100
"react-frame-component": "^4.1.1",
101-
"react-inspector": "^3.0.2",
102101
"react-number-format": "^4.0.8",
103102
"react-redux": "^7.1.1",
104103
"react-router": "^5.0.0",

docs/src/pages/customization/default-theme/DefaultTheme.js

Lines changed: 214 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,185 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import { useSelector } from 'react-redux';
44
import 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';
711
import FormControlLabel from '@material-ui/core/FormControlLabel';
812
import 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+
10176
const 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+
24216
function 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
}

yarn.lock

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8186,11 +8186,6 @@ is-directory@^0.3.1:
81868186
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
81878187
integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
81888188

8189-
is-dom@^1.0.9:
8190-
version "1.0.9"
8191-
resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.0.9.tgz#483832d52972073de12b9fe3f60320870da8370d"
8192-
integrity sha1-SDgy1SlyBz3hK5/j9gMghw2oNw0=
8193-
81948189
is-extendable@^0.1.0, is-extendable@^0.1.1:
81958190
version "0.1.1"
81968191
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
@@ -11798,7 +11793,7 @@ [email protected]:
1179811793
loose-envify "^1.3.1"
1179911794
object-assign "^4.1.1"
1180011795

11801-
prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
11796+
prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
1180211797
version "15.7.2"
1180311798
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
1180411799
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -12159,15 +12154,6 @@ react-input-autosize@^2.2.2:
1215912154
dependencies:
1216012155
prop-types "^15.5.8"
1216112156

12162-
react-inspector@^3.0.2:
12163-
version "3.0.2"
12164-
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-3.0.2.tgz#c530a06101f562475537e47df428e1d7aff16ed8"
12165-
integrity sha512-PSR8xDoGFN8R3LKmq1NT+hBBwhxjd9Qwz8yKY+5NXY/CHpxXHm01CVabxzI7zFwFav/M3JoC/Z0Ro2kSX6Ef2Q==
12166-
dependencies:
12167-
babel-runtime "^6.26.0"
12168-
is-dom "^1.0.9"
12169-
prop-types "^15.6.1"
12170-
1217112157
1217212158
version "16.8.6"
1217312159
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"

0 commit comments

Comments
 (0)