// @flow
import React, { useCallback, useContext } from 'react';
import { TreeDispatcherContext, TreeStateContext } from './TreeContext';
import { BridgeContext, StoreContext } from '../context';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import HooksTree from './HooksTree';
import { ModalDialogContext } from '../ModalDialog';
import InspectedElementTree from './InspectedElementTree';
import { InspectedElementContext } from './InspectedElementContext';
import ViewElementSourceContext from './ViewElementSourceContext';
import Toggle from '../Toggle';
import {
ComponentFilterElementType,
ElementTypeClass,
ElementTypeForwardRef,
ElementTypeFunction,
ElementTypeMemo,
ElementTypeSuspense,
} from 'src/types';
import styles from './SelectedElement.css';
import type { Element, InspectedElement } from './types';
export type Props = {||};
export default function SelectedElement(_: Props) {
const { inspectedElementID } = useContext(TreeStateContext);
const dispatch = useContext(TreeDispatcherContext);
const viewElementSource = useContext(ViewElementSourceContext);
const bridge = useContext(BridgeContext);
const store = useContext(StoreContext);
const { dispatch: modalDialogDispatch } = useContext(ModalDialogContext);
const { read } = useContext(InspectedElementContext);
const element =
inspectedElementID !== null
? store.getElementByID(inspectedElementID)
: null;
const inspectedElement =
inspectedElementID != null ? read(inspectedElementID) : null;
const highlightElement = useCallback(() => {
if (element !== null && inspectedElementID !== null) {
const rendererID = store.getRendererIDForElement(inspectedElementID);
if (rendererID !== null) {
bridge.send('highlightElementInDOM', {
displayName: element.displayName,
hideAfterTimeout: true,
id: inspectedElementID,
openNativeElementsPanel: true,
rendererID,
scrollIntoView: true,
});
}
}
}, [bridge, element, inspectedElementID, store]);
const logElement = useCallback(() => {
if (inspectedElementID !== null) {
const rendererID = store.getRendererIDForElement(inspectedElementID);
if (rendererID !== null) {
bridge.send('logElementToConsole', {
id: inspectedElementID,
rendererID,
});
}
}
}, [bridge, inspectedElementID, store]);
const viewSource = useCallback(() => {
if (viewElementSource != null && inspectedElementID !== null) {
viewElementSource(inspectedElementID);
}
}, [inspectedElementID, viewElementSource]);
const canViewSource =
inspectedElement &&
inspectedElement.canViewSource &&
viewElementSource !== null;
const isSuspended =
element !== null &&
element.type === ElementTypeSuspense &&
inspectedElement != null &&
inspectedElement.state != null;
const canToggleSuspense =
inspectedElement != null && inspectedElement.canToggleSuspense;
// TODO (suspense toggle) Would be nice to eventually use a two setState pattern here as well.
const toggleSuspended = useCallback(() => {
let nearestSuspenseElement = null;
let currentElement = element;
while (currentElement !== null) {
if (currentElement.type === ElementTypeSuspense) {
nearestSuspenseElement = currentElement;
break;
} else if (currentElement.parentID > 0) {
currentElement = store.getElementByID(currentElement.parentID);
} else {
currentElement = null;
}
}
// If we didn't find a Suspense ancestor, we can't suspend.
// Instead we can show a warning to the user.
if (nearestSuspenseElement === null) {
modalDialogDispatch({
type: 'SHOW',
content: