// @flow
import { copy } from 'clipboard-js';
import React, { useCallback, useContext, useState } from 'react';
import { BridgeContext, StoreContext } from '../context';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import EditableValue from './EditableValue';
import ExpandCollapseToggle from './ExpandCollapseToggle';
import KeyValue from './KeyValue';
import { serializeHooksForCopy } from '../utils';
import styles from './HooksTree.css';
import { meta } from '../../../hydration';
import type { HooksNode, HooksTree } from 'src/backend/types';
type HooksTreeViewProps = {|
canEditHooks: boolean,
hooks: HooksTree | null,
id: number,
|};
export function HooksTreeView({ canEditHooks, hooks, id }: HooksTreeViewProps) {
const handleCopy = useCallback(() => copy(serializeHooksForCopy(hooks)), [
hooks,
]);
if (hooks === null) {
return null;
} else {
return (
);
}
}
type InnerHooksTreeViewProps = {|
canEditHooks: boolean,
hooks: HooksTree,
id: number,
|};
export function InnerHooksTreeView({
canEditHooks,
hooks,
id,
}: InnerHooksTreeViewProps) {
// $FlowFixMe "Missing type annotation for U" whatever that means
return hooks.map((hook, index) => (
));
}
type HookViewProps = {|
canEditHooks: boolean,
hook: HooksNode,
id: number,
path?: Array,
|};
function HookView({ canEditHooks, hook, id, path = [] }: HookViewProps) {
const { name, id: hookID, isStateEditable, subHooks, value } = hook;
const bridge = useContext(BridgeContext);
const store = useContext(StoreContext);
const [isOpen, setIsOpen] = useState(false);
const toggleIsOpen = useCallback(
() => setIsOpen(prevIsOpen => !prevIsOpen),
[]
);
if (hook.hasOwnProperty(meta.inspected)) {
// This Hook is too deep and hasn't been hydrated.
// TODO: show UI to load its data.
return (
);
}
// TODO Add click and key handlers for toggling element open/close state.
const isCustomHook = subHooks.length > 0;
const type = typeof value;
let displayValue;
let isComplexDisplayValue = false;
// Format data for display to mimic the props/state/context for now.
if (type === 'string') {
displayValue = `"${((value: any): string)}"`;
} else if (type === 'boolean') {
displayValue = value ? 'true' : 'false';
} else if (type === 'number') {
displayValue = value;
} else if (value === null) {
displayValue = 'null';
} else if (value === undefined) {
displayValue = null;
} else if (Array.isArray(value)) {
isComplexDisplayValue = true;
displayValue = 'Array';
} else if (type === 'object') {
isComplexDisplayValue = true;
displayValue = 'Object';
}
if (isCustomHook) {
if (isComplexDisplayValue) {
return (
);
} else {
return (
{name}
{' '}
{/* $FlowFixMe */}
{displayValue}
{isOpen && (
)}
);
}
} else {
let overrideValueFn = null;
// TODO Maybe read editable value from debug hook?
if (canEditHooks && isStateEditable) {
overrideValueFn = (path: Array, value: any) => {
const rendererID = store.getRendererIDForElement(id);
bridge.send('overrideHookState', {
id,
hookID,
path,
rendererID,
value,
});
};
}
if (isComplexDisplayValue) {
return (
);
} else {
return (
{name}
{typeof overrideValueFn === 'function' ? (
) : (
// $FlowFixMe Cannot create span element because in property children
{displayValue}
)}
);
}
}
}
// $FlowFixMe
export default React.memo(HooksTreeView);