import { Tab } from '@headlessui/react' import { ChevronDownIcon, ChevronRightIcon, XIcon } from '@heroicons/react/solid' import animatedScrollTo from 'animated-scroll-to' import { Fragment, ReactNode, Ref, useEffect, useRef, useState } from 'react' import packageInfo from '../package.json' import { Link } from './link' import { Fiber, fiberGetSiblings, nodeGetReactRoot } from './react-source' export type NavTreeNode = Fiber | Node export type NavTreePanelProps = { height: number rootRef: Ref selectedNode: Node onNodeClick: (element: NavTreeNode) => void onCloseClick: () => void sidePanel?: ReactNode } export const NavTreePanel = (props: NavTreePanelProps) => { return (
) } export function NavTreePanelView(props: NavTreePanelProps) { const root = nodeGetReactRoot(props.selectedNode) const selectedNodeContainerRef = useRef(null) const selectedNodeElementRef = useRef(null) const scrollToSelectedElement = () => { if (!selectedNodeElementRef.current || !selectedNodeContainerRef.current) { return } animatedScrollTo(selectedNodeElementRef.current, { elementToScroll: selectedNodeContainerRef.current, verticalOffset: -selectedNodeContainerRef.current.offsetHeight * 0.25, }) } useEffect(scrollToSelectedElement, [selectedNodeElementRef.current, selectedNodeContainerRef]) const [hoveredNodes, setHoveredNodes] = useState([]) const hoveredNode = hoveredNodes[hoveredNodes.length - 1] const tabs = ['Elements', 'About'] return ( { setTimeout(scrollToSelectedElement, 1) }} >
{tabs.map((tabName) => ( {({ selected }) => ( )} ))}
{ setHoveredNodes((nodes) => { return [...nodes, node] }) }} onNodeMouseLeave={(node) => { setHoveredNodes((nodes) => { return nodes.filter((n) => n !== node) }) }} />
{props.sidePanel}

Version:{' '} {(() => { return packageInfo.version })()}

Github:{' '} impulse-oss/impulse

Discord:{' '} Join

Made by{' '} @krogovoy {' '} and{' '} @IVolchenskov

) } export function NavTreeFromNode({ node, level, selectedNode, hoveredNode, onNodeClick, onNodeMouseEnter, onNodeMouseLeave, selectedNodeElementRef, }: { node: NavTreeNode level: number selectedNode: Node hoveredNode: NavTreeNode | null onNodeClick: (node: NavTreeNode) => void onNodeMouseEnter: (node: NavTreeNode) => void onNodeMouseLeave: (node: NavTreeNode) => void selectedNodeElementRef: Ref }) { type State = { type: 'collapsed' | 'expanded' } const state: State = (() => { if (node instanceof Node) { return { type: 'expanded' } } if (typeof node.elementType === 'function') { return { type: 'expanded' } } if (typeof node.elementType === 'object') { return { type: 'expanded' } } const domNode = node.stateNode! if (!(domNode instanceof Element)) { return { type: 'expanded' } } if (domNode === selectedNode || domNode.contains(selectedNode)) { return { type: 'expanded' } } return { type: 'collapsed' } })() const isSelected = node instanceof Node ? node === selectedNode : node.stateNode === selectedNode const isHovered = node === hoveredNode const chevron = (() => { if (node instanceof Node) { return
} const isJsxElement = typeof node.elementType === 'function' || node.tag === 11 const isHtmlElement = typeof node.elementType === 'string' && node.stateNode instanceof Element if (isJsxElement || isHtmlElement) { return (
{state.type === 'collapsed' ? ( ) : ( )}
) } return null })() return (
{ e.stopPropagation() onNodeClick(node) }} onMouseEnter={() => { onNodeMouseEnter(node) }} onMouseLeave={() => { onNodeMouseLeave(node) }} > {chevron}
{(() => { // node is a text Node if (node instanceof Node) { return ( <> {'"'} {node.nodeValue} {'"'} ) } const fiberChild = node.child const children = fiberChild ? fiberGetSiblings(fiberChild) : [...(node.stateNode?.childNodes ?? [])] // node is a React component if (typeof node.elementType === 'function') { const componentName = node.elementType.name ?? 'UnknownComponent' return ( <>
{'<'} {componentName} {'>'}
{state.type === 'expanded' ? children.map((child, idx) => ( )) : '...'}
{''}
) } // node is an HTML element if (typeof node.elementType === 'string') { return ( <>
{state.type === 'expanded' ? children.map((child, idx) => ( )) : '...'}
{node.stateNode instanceof Element && (
{'{node.stateNode.tagName.toLowerCase()} {'>'}
)} ) } // node is a text Node with Fiber if (node.elementType === null && node.stateNode instanceof Node) { return ( <> {'"'} {node.stateNode.nodeValue!} {'"'} ) } // node is a forward ref if (node.tag === 11 && typeof node.elementType === 'object' && node.elementType.render) { const componentName = node.elementType.render.displayName || node.elementType.render.name || 'UnknownComponent' return ( <>
{'<'} {componentName} {'>'}
{state.type === 'expanded' ? children.map((child, idx) => ( )) : '...'}
{''}
) } // node is a context provider or fragment or something else if (typeof node.elementType === 'object') { return ( <> {state.type === 'expanded' ? children.map((child, idx) => ( )) : '...'} ) } return null })()}
) } function ElementDetails(props: { node: Node }) { const { node } = props const truncate = (str: string, maxLength: number) => { if (str.length <= maxLength) { return str } return str.substring(0, maxLength) + '...' } if (!(node instanceof Element)) { const text = node.textContent || '' return <>"{truncate(text, 50)}" } return (
{'<'} {node.tagName.toLocaleLowerCase()} {Array.from(node.attributes) .filter((attribute) => { if (attribute.name !== 'class') { return false } if (attribute.value.trim() === '') { return false } const classes = attribute.value .split(' ') .map((x) => x.trim()) .filter((className) => !className.startsWith('__impulse__')) if (classes.length === 0) { return false } return true }) .map((attribute, idx) => { const attributeValue = (() => { if (attribute.name === 'class') { return [...node.classList].filter((className) => !className.startsWith('__impulse__')).join(' ') } return attribute.value })() return ( {' '} {attribute.name}= "{attributeValue}" ) })} {'>'}
) }