import { getCode, keyboardKey } from '@fluentui/keyboard-key'; import * as PropTypes from 'prop-types'; import * as React from 'react'; import { EventListener } from '@fluentui/react-component-event-listener'; import { isBrowser } from '../../utils'; import { isDebugEnabled } from '@fluentui/styles'; import { DebugPanel } from './DebugPanel'; import { FiberNavigator } from './FiberNavigator'; import { DebugRect } from './DebugRect'; /* eslint-disable no-console */ export type DebugProps = { /** Existing document the popup should add listeners. */ mountDocument?: Document; }; export type DebugState = { debugPanelPosition?: 'left' | 'right'; fiberNav: FiberNavigator; selectedFiberNav: FiberNavigator; isSelecting: boolean; }; const INITIAL_STATE: DebugState = { fiberNav: null, selectedFiberNav: null, isSelecting: false, }; export class Debug extends React.Component { state = INITIAL_STATE; static defaultProps = { // eslint-disable-next-line no-undef mountDocument: isBrowser() ? window.document : null, }; static propTypes = { mountDocument: PropTypes.object.isRequired, }; constructor(p, s) { super(p, s); if (process.env.NODE_ENV !== 'production' && isDebugEnabled && isBrowser()) { // eslint-disable-next-line no-undef (window as any).openDebugPanel = () => { // eslint-disable-next-line no-undef this.debugReactComponent((window as any).$r); }; } } debugReactComponent = r => { if (!r) { console.error( "No React component selected. Please select a Fluent UI component from the React's Component panel.", ); return; } if (!r._reactInternalFiber) { console.error( 'React does not provide data for debugging for this component. Try selecting some Fluent UI component.', ); return; } if (!r.fluentUIDebug) { console.error('Not a debuggable component. Try selecting some Fluent UI component.'); return; } const fiberNav = FiberNavigator.fromFiber(r._reactInternalFiber); this.setState({ fiberNav, isSelecting: false, selectedFiberNav: null }); }; debugDOMNode = domNode => { let fiberNav = FiberNavigator.fromDOMNode(domNode); if (!fiberNav) { console.error('No fiber for dom node', domNode); return; } fiberNav = fiberNav.findOwner(fiber => fiber.fluentUIDebug); if (fiberNav !== this.state.fiberNav) { this.setState({ fiberNav }); } }; handleKeyDown = e => { const code = getCode(e); switch (code) { case keyboardKey.Escape: this.stopSelecting(); break; case keyboardKey.d: if (e.altKey && e.shiftKey) { this.startSelecting(); } break; } }; handleMouseMove = e => { this.debugDOMNode(e.target); }; handleDOMNodeClick = e => { e.preventDefault(); e.stopPropagation(); this.setState({ isSelecting: false }); }; startSelecting = () => { const isSelecting = !this.state.isSelecting; this.setState({ ...(!isSelecting && INITIAL_STATE), isSelecting, }); }; stopSelecting = () => { this.setState(INITIAL_STATE); }; selectFiber = selectedFiberNav => this.setState({ selectedFiberNav }); changeFiber = fiberNav => this.setState({ fiberNav }); positionRight = () => this.setState({ debugPanelPosition: 'right' }); positionLeft = () => this.setState({ debugPanelPosition: 'left' }); close = () => this.setState(INITIAL_STATE); render() { const { mountDocument } = this.props; const { fiberNav, selectedFiberNav, isSelecting, debugPanelPosition } = this.state; if (process.env.NODE_ENV !== 'production' && isDebugEnabled) { return ( <> {isSelecting && ( )} {isSelecting && fiberNav && fiberNav.domNode && ( )} {isSelecting && fiberNav && } {selectedFiberNav && } {!isSelecting && fiberNav && fiberNav.instance && ( )} ); } return null; } }