import React from 'react'; import _ from 'lodash'; import cx from 'classnames'; import { ToolTip } from '@/_components/ToolTip'; import CopyToClipboardComponent from '@/_components/CopyToClipboard'; import { Popover } from 'react-bootstrap'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import JSONNodeObject from './JSONNodeObject'; import JSONNodeArray from './JSONNodeArray'; import JSONNodeValue from './JSONNodeValue'; import JSONNodeIndicator from './JSONNodeIndicator'; export const JSONNode = ({ data, ...restProps }) => { const { path, shouldExpandNode, currentNode, selectedNode, hoveredNode, getCurrentPath, getCurrentNodeType, getLength, toUseNodeIcons, renderNodeIcons, useIndentedBlock, updateSelectedNode, updateHoveredNode, useActions, enableCopyToClipboard, getNodeShowHideComponents, getOnSelectLabelDispatchActions, expandWithLabels, getAbsoluteNodePath, actionsList, updateParentState = () => null, } = restProps; const [expandable, set] = React.useState(() => typeof shouldExpandNode === 'function' ? shouldExpandNode(path, data) : shouldExpandNode ); const [showHiddenOptionsForNode, setShowHiddenOptionsForNode] = React.useState(false); const [showHiddenOptionButtons, setShowHiddenOptionButtons] = React.useState([]); const [onSelectDispatchActions, setOnSelectDispatchActions] = React.useState([]); React.useEffect(() => { if (showHiddenOptionButtons) { setShowHiddenOptionButtons(() => getNodeShowHideComponents(currentNode, path)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React.useEffect(() => { if (useActions && currentNode) { const onSelectDispatchActions = getOnSelectLabelDispatchActions(currentNode, path).filter( (action) => action.onSelect ); if (onSelectDispatchActions.length > 0) { setOnSelectDispatchActions(onSelectDispatchActions); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedNode]); const toggleExpandNode = (node) => { if (expandable) { updateSelectedNode(null); } else { updateSelectedNode(node, path); } set((prev) => !prev); }; const onSelect = (data, currentNode, path) => { const actions = onSelectDispatchActions; actions.forEach((action) => action.dispatchAction(data, currentNode)); if (!expandWithLabels) { updateSelectedNode(currentNode, path); set(true); } }; const handleOnClickLabels = (data, currentNode, path) => { if (expandWithLabels) { toggleExpandNode(currentNode); } if (useActions) { onSelect(data, currentNode, path); } }; const typeofCurrentNode = getCurrentNodeType(data); const currentNodePath = getCurrentPath(path, currentNode); const toExpandNode = (data instanceof Array || data instanceof Object) && !_.isEmpty(data); const toShowNodeIndicator = (data instanceof Array || data instanceof Object) && typeofCurrentNode !== 'Function'; const numberOfEntries = getLength(typeofCurrentNode, data); const toRenderSelector = (typeofCurrentNode === 'Object' || typeofCurrentNode === 'Array') && numberOfEntries > 0; let $VALUE = null; let $NODEType = null; let $NODEIcon = null; const checkSelectedNode = (_selectedNode, _currentNode, parent, toExpand) => { if (selectedNode?.parent && parent) { return _selectedNode.parent === parent && _selectedNode?.node === _currentNode && toExpand; } return toExpand && _selectedNode?.node === _currentNode; }; const parent = path && typeof path?.length === 'number' ? path[path.length - 2] : null; const applySelectedNodeStyles = toExpandNode ? checkSelectedNode(selectedNode, currentNode, parent, expandable) : false; React.useEffect(() => { if (!expandable) { updateSelectedNode(null); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [expandable]); React.useEffect(() => { if (selectedNode?.node === currentNode) { set(true); } }, [selectedNode, currentNode]); React.useEffect(() => { if (hoveredNode?.node === currentNode && hoveredNode?.parent === parent) { setShowHiddenOptionsForNode(true); } return () => { setShowHiddenOptionsForNode(false); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [hoveredNode]); if (toUseNodeIcons && currentNode) { $NODEIcon = renderNodeIcons(currentNode); } switch (typeofCurrentNode) { case 'String': case 'Boolean': case 'Number': case 'Null': case 'Undefined': case 'Function': $VALUE = ; $NODEType = ; break; case 'Object': $VALUE = ; $NODEType = ( {`${numberOfEntries} ${numberOfEntries > 1 ? 'entries' : 'entry'}`}{' '} ); break; case 'Array': $VALUE = ; $NODEType = ( {`${numberOfEntries} ${numberOfEntries > 1 ? 'items' : 'item'}`}{' '} ); break; default: $VALUE = {String(data)}; $NODEType = typeofCurrentNode; } let $key = ( toExpandNode && handleOnClickLabels(data, currentNode, path)} style={{ marginTop: '1px', cursor: 'pointer', textTransform: 'none' }} className={cx('node-key fs-12 mx-0 badge badge-outline', { 'color-primary': applySelectedNodeStyles && !showHiddenOptionsForNode, 'hovered-node': showHiddenOptionsForNode, 'node-key-outline': !applySelectedNodeStyles && !showHiddenOptionsForNode, })} > {String(currentNode)} ); if (!currentNode) { return $VALUE; } const shouldDisplayIntendedBlock = useIndentedBlock && expandable && (typeofCurrentNode === 'Object' || typeofCurrentNode === 'Array'); function moreActionsPopover(actions) { //Todo: For adding more actions to the menu popover! const darkMode = localStorage.getItem('darkMode') === 'true'; return (
{actions?.map((action, index) => ( { action.dispatchAction(data, currentNode); updateParentState(); }} > {action.name} ))}
); } const renderHiddenOptionsForNode = () => { const moreActions = actionsList.filter((action) => action.for === 'all')[0]; const renderOptions = () => { if (!useActions || showHiddenOptionButtons?.length === 0) return null; return showHiddenOptionButtons?.map((actionOption, index) => { const { name, icon, src, iconName, dispatchAction, width = 12, height = 12 } = actionOption; if (icon) { return ( dispatchAction(data, currentNode)} > ); } }); }; return (
{enableCopyToClipboard && ( )} {renderOptions()} {moreActions.actions?.length > 0 && ( )}
); }; return (
updateHoveredNode(currentNode, currentNodePath)} onMouseLeave={() => updateHoveredNode(null)} className={cx('d-flex', { 'group-object-container': shouldDisplayIntendedBlock, 'mx-2': typeofCurrentNode !== 'Object' && typeofCurrentNode !== 'Array', })} > {$NODEIcon &&
{$NODEIcon}
} {$key} {$NODEType} {!toExpandNode && !expandable && !toRenderSelector ? $VALUE : null}
{showHiddenOptionsForNode && renderHiddenOptionsForNode()}
{toRenderSelector && (toExpandNode && !expandable ? null : $VALUE)}
); }; const DisplayNodeLabel = ({ type = '', children }) => { if (type === 'Null' || type === 'Undefined') { return null; } return ( <> {type} {children} ); }; JSONNode.DisplayNodeLabel = DisplayNodeLabel;