import { useState, useCallback, useEffect } from 'react' import { Box, IconButton, Tooltip } from '@mui/material' import { useTheme } from '@mui/material' import { Button } from '@mui/material' import { Close, KeyboardArrowDown, KeyboardArrowUp, ChevronRight, ExpandMore } from '@mui/icons-material' import { useI18nStore } from '../stores/i18n.store' import P3xrDialog from '../components/P3xrDialog' interface JsonNode { key: string value: any type: 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null' children?: JsonNode[] childCount?: number } function jsonToNode(key: string, value: any): JsonNode { if (value === null) return { key, value: null, type: 'null' } if (Array.isArray(value)) { const children = value.map((item, i) => jsonToNode(String(i), item)) return { key, value, type: 'array', children, childCount: children.length } } if (typeof value === 'object') { const children = Object.keys(value).map(k => jsonToNode(k, value[k])) return { key, value, type: 'object', children, childCount: children.length } } return { key, value, type: typeof value as any } } function formatDisplay(node: JsonNode): string { if (node.type === 'null') return 'null' if (node.type === 'string') return `"${node.value}"` return String(node.value) } // Color map from Angular: string=accent, number=primary, boolean=warn, null=muted function useJsonColors() { const muiTheme = useTheme() const isDark = muiTheme.palette.mode === 'dark' return { key: isDark ? 'white' : 'black', string: muiTheme.palette.secondary.main, // --p3xr-btn-accent-bg number: muiTheme.palette.primary.main, // --p3xr-btn-primary-bg boolean: muiTheme.palette.error.main, // --p3xr-btn-warn-bg null: isDark ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.4)', } } function TreeNode({ node, level, expandedKeys, toggleExpand }: { node: JsonNode; level: number; expandedKeys: Set; toggleExpand: (path: string) => void }) { const colors = useJsonColors() const path = `${level}-${node.key}` const isExpandable = node.type === 'object' || node.type === 'array' const isExpanded = expandedKeys.has(path) const valueColor = isExpandable ? undefined : (colors as any)[node.type] ?? 'inherit' return ( <> {isExpandable ? ( toggleExpand(path)} sx={{ width: 24, height: 24, p: 0, flexShrink: 0, opacity: 0.6 }}> {isExpanded ? : } ) : ( )} {node.key} : {isExpandable ? ( !isExpanded ? ( <> {node.type === 'array' ? '[' : '{'} ... {node.type === 'array' ? ']' : '}'} ({node.childCount}) ) : null ) : ( {formatDisplay(node)} )} {isExpandable && isExpanded && node.children?.map((child, i) => ( ))} ) } interface Props { open: boolean value: string onClose: () => void } export default function JsonViewDialog({ open, value, onClose }: Props) { const strings = useI18nStore(s => s.strings) // Start with only root expanded (level 0) — matches Angular expanded=true (first level only) const [expandedKeys, setExpandedKeys] = useState>(new Set()) const rootLabel = strings?.label?.tree ?? 'root' let isJson = false let tree: JsonNode | null = null try { const obj = JSON.parse(value) isJson = true tree = jsonToNode(rootLabel, obj) } catch { /* not parsable */ } // Reset to root-only expanded when value changes useEffect(() => { if (open && isJson) setExpandedKeys(new Set([`0-${rootLabel}`])) }, [open, value]) const toggleExpand = useCallback((path: string) => { setExpandedKeys(prev => { const next = new Set(prev) if (next.has(path)) next.delete(path) else next.add(path) return next }) }, []) const expandAll = useCallback(() => { if (!tree) return const keys = new Set() const collect = (node: JsonNode, level: number) => { const path = `${level}-${node.key}` if (node.type === 'object' || node.type === 'array') { keys.add(path) node.children?.forEach((c, i) => collect(c, level + 1)) } } collect(tree, 0) setExpandedKeys(keys) }, [tree]) const collapseAll = useCallback(() => { // Collapse to level 1: only root expanded const rootPath = `0-${strings?.label?.tree ?? 'root'}` setExpandedKeys(new Set([rootPath])) }, [strings]) if (!open) return null return ( ) : undefined} actions={ }> {isJson && tree ? ( ) : ( {strings?.label?.jsonViewNotParsable} )} ) }