src/devtools/views/Components/KeyValue.js (144 lines of code) (raw):
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import React, { useState } from 'react';
import type { Element } from 'react';
// import EditableValue from './EditableValue';
import ExpandCollapseToggle from './ExpandCollapseToggle';
import { alphaSortEntries } from '../utils';
import styles from './KeyValue.css';
type KeyValueProps = {|
alphaSort: boolean,
depth: number,
hidden?: boolean,
name: string,
path: Array<any>,
value: any,
|};
export default function KeyValue({
alphaSort,
depth,
hidden,
name,
path,
value,
}: KeyValueProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [wasOpen, setWasOpen] = useState<boolean>(isOpen);
if (isOpen && !wasOpen) {
setWasOpen(true);
}
const toggleIsOpen = () => setIsOpen(prevIsOpen => !prevIsOpen);
const dataType = typeof value;
const isSimpleType =
dataType === 'number' ||
dataType === 'string' ||
dataType === 'boolean' ||
value == null;
const style = {
paddingLeft: `${(depth - 1) * 0.75}rem`,
};
//TODO(damassart): Make this into a function
if (isSimpleType) {
let displayValue = value;
if (dataType === 'string') {
displayValue = `"${value}"`;
} else if (dataType === 'boolean') {
displayValue = value ? 'true' : 'false';
} else if (value === null) {
displayValue = 'null';
} else if (value === undefined) {
displayValue = 'undefined';
}
return (
<div key="root" className={styles.Item} hidden={hidden} style={style}>
<div className={styles.ExpandCollapseToggleSpacer} />
<span className={styles.Name}>{name}</span>
<span className={styles.Value}>{displayValue}</span>
</div>
);
} else {
if (Array.isArray(value)) {
const hasChildren = value.length > 0;
const children = wasOpen
? value.map((innerValue, index) => (
<KeyValue
key={index}
alphaSort={alphaSort}
depth={depth + 1}
hidden={hidden || !isOpen}
name={index}
path={path.concat(index)}
value={value[index]}
/>
))
: [];
children.unshift(
<div
key={`${depth}-root`}
className={styles.Item}
hidden={hidden}
style={style}
>
{hasChildren ? (
<ExpandCollapseToggle isOpen={isOpen} setIsOpen={setIsOpen} />
) : (
<div className={styles.ExpandCollapseToggleSpacer} />
)}
<span
className={styles.Name}
onClick={hasChildren ? toggleIsOpen : undefined}
>
{name}
</span>
<span>
Array
{hasChildren ? '' : <span className={styles.Empty}>(empty)</span>}
</span>
</div>
);
return children;
} else {
//TODO(damassart): Fix this
const entries = Object.entries(value);
if (alphaSort) {
entries.sort(alphaSortEntries);
}
const hasChildren = entries.length > 0;
const displayName = 'Object';
const children = wasOpen
? entries.map<Element<any>>(([entriesName, entriesVal]) => (
<KeyValue
key={entriesName}
alphaSort={alphaSort}
depth={depth + 1}
hidden={hidden || !isOpen}
name={entriesName}
path={path.concat(entriesName)}
value={entriesVal}
/>
))
: [];
children.unshift(
<div
key={`${depth}-root`}
className={styles.Item}
hidden={hidden}
style={style}
>
{hasChildren ? (
<ExpandCollapseToggle isOpen={isOpen} setIsOpen={setIsOpen} />
) : (
<div className={styles.ExpandCollapseToggleSpacer} />
)}
<span
className={styles.Name}
onClick={hasChildren ? toggleIsOpen : undefined}
>
{name}
</span>
<span>
{`${displayName || ''} `}
{hasChildren ? '' : <span className={styles.Empty}>(empty)</span>}
</span>
</div>
);
return children;
}
}
}