Skip to content

Latest commit

Β 

History

History
451 lines (375 loc) Β· 11.6 KB

File metadata and controls

451 lines (375 loc) Β· 11.6 KB

Component Architecture Diagram

Application Structure

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         app/layout.tsx                           β”‚
β”‚                     (Root Layout + Metadata)                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         app/page.tsx                             β”‚
β”‚                      (Main Orchestrator)                         β”‚
β”‚                                                                  β”‚
β”‚  State:                                                          β”‚
β”‚  β€’ jsonInput          (string)                                   β”‚
β”‚  β€’ jsonError          (string | null)                            β”‚
β”‚  β€’ rootNode           (TreeNode | null)                          β”‚
β”‚  β€’ selectedNode       (TreeNode | null)                          β”‚
β”‚  β€’ searchTerm         (string)                                   β”‚
β”‚  β€’ expandedPaths      (Set<string>)                              β”‚
β”‚                                                                  β”‚
β”‚  Effects:                                                        β”‚
β”‚  β€’ Load JSON from URL on mount                                   β”‚
β”‚  β€’ Parse JSON when input changes                                 β”‚
β”‚  β€’ Auto-expand search results                                    β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚      β”‚      β”‚      β”‚      β”‚
       β–Ό      β–Ό      β–Ό      β–Ό      β–Ό
    β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ 1 β”‚ β”‚ 2 β”‚ β”‚ 3 β”‚ β”‚ 4 β”‚ β”‚   5    β”‚
    β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Component Tree

app/page.tsx (Main App)
β”‚
β”œβ”€β”€ Header
β”‚   β”œβ”€β”€ Title + Description
β”‚   └── ShareButton (5)
β”‚
β”œβ”€β”€ SearchBar (2) [conditional: if rootNode exists]
β”‚
β”œβ”€β”€ Main Content Area (flex)
β”‚   β”‚
β”‚   β”œβ”€β”€ Left Panel
β”‚   β”‚   └── JsonInput (1)
β”‚   β”‚       β”œβ”€β”€ Textarea
β”‚   β”‚       β”œβ”€β”€ Upload Button
β”‚   β”‚       β”œβ”€β”€ Prettify Button
β”‚   β”‚       β”œβ”€β”€ Clear Button
β”‚   β”‚       └── Error Display
β”‚   β”‚
β”‚   └── Right Panel
β”‚       └── TreeViewer (3)
β”‚           └── TreeNodeComponent (recursive)
β”‚               β”œβ”€β”€ Node Header
β”‚               β”‚   β”œβ”€β”€ Expand Icon
β”‚               β”‚   β”œβ”€β”€ Key
β”‚               β”‚   └── Value
β”‚               └── Children (recursive TreeNodeComponent)
β”‚
β”œβ”€β”€ Bottom Panel [conditional: if selectedNode exists]
β”‚   └── PathPreview (4)
β”‚       β”œβ”€β”€ Path Display + Copy Button
β”‚       β”œβ”€β”€ Value Preview
β”‚       β”œβ”€β”€ Copy Value Button
β”‚       └── Type Indicator
β”‚
└── Footer
    └── Usage Hints

Data Flow

1. JSON Input Flow

User types/pastes JSON
    ↓
JsonInput onChange
    ↓
app/page.tsx setJsonInput
    ↓
useEffect (on jsonInput change)
    ↓
validateJson()
    ↓
JSON.parse()
    ↓
jsonToTree()
    ↓
setRootNode
    ↓
TreeViewer re-renders

2. Node Click Flow (Leaf Value)

User clicks leaf value
    ↓
TreeNodeComponent handleClick
    ↓
copyToClipboard(node.path)
    ↓
onNodeClick(node)
    ↓
app/page.tsx setSelectedNode
    ↓
PathPreview re-renders with new node

3. Node Click Flow (Object/Array)

User clicks object/array
    ↓
TreeNodeComponent handleClick
    ↓
onToggleExpand(node.path)
    ↓
app/page.tsx handleToggleExpand
    ↓
Update expandedPaths Set
    ↓
TreeNodeComponent re-renders
    ↓
Children show/hide

4. Search Flow

User types in SearchBar
    ↓
onSearchChange(term)
    ↓
app/page.tsx setSearchTerm
    ↓
useEffect (on searchTerm change)
    ↓
searchTree(rootNode, term)
    ↓
getParentPaths(each match)
    ↓
Update expandedPaths
    ↓
TreeViewer re-renders
    ↓
Matched nodes visible and highlighted

5. Share Flow

User clicks Share button
    ↓
ShareButton handleShare
    ↓
createShareableUrl(jsonString)
    ↓
encodeJsonToUrl
    β”œβ”€ pako.deflate (compress)
    β”œβ”€ btoa (base64 encode)
    └─ URL-safe replacements
    ↓
copyToClipboard(url)
    ↓
Show success message

Component Responsibilities

1. JsonInput (Left Panel)

Purpose: Accept and validate JSON input

Props:

  • value: string - Current JSON
  • onChange: (json: string) => void - Update callback
  • error: string | null - Error message

Features:

  • Textarea input
  • File upload
  • Drag & drop
  • Prettify button
  • Clear button
  • Error display

No State: Fully controlled by parent


2. SearchBar

Purpose: Search for keys in JSON

Props:

  • searchTerm: string - Current search
  • onSearchChange: (term: string) => void - Update callback
  • matchCount?: number - Number of matches

Features:

  • Text input
  • Clear button
  • Match counter

No State: Fully controlled by parent


3. TreeViewer + TreeNodeComponent

Purpose: Display JSON tree structure

TreeViewer Props:

  • rootNode: TreeNode | null - Tree root
  • onNodeClick: (node: TreeNode) => void - Click handler
  • selectedPath: string | null - Currently selected path
  • searchTerm: string - For highlighting
  • expandedPaths: Set<string> - Expanded nodes
  • onToggleExpand: (path: string) => void - Toggle handler

TreeNodeComponent Props:

  • node: TreeNode - Node to render
  • onToggleExpand: (path: string) => void
  • onNodeClick: (node: TreeNode) => void
  • isExpanded: boolean - Is this node expanded?
  • isSelected: boolean - Is this node selected?
  • searchTerm: string - For highlighting
  • expandedPaths: Set<string> - Pass to children

Features:

  • Recursive rendering
  • Expand/collapse icons
  • Color-coded types
  • Search highlighting
  • Click to copy path (leaf)
  • Click to expand (object/array)
  • Drag to copy value

Local State:

  • showCopyHint: boolean - Temporary copy feedback

4. PathPreview (Bottom Panel)

Purpose: Show path and value of selected node

Props:

  • selectedNode: TreeNode | null - Node to preview

Features:

  • Path display with copy button
  • Value preview (formatted JSON)
  • Copy value button
  • Type indicator

Local State:

  • copiedItem: 'path' | 'value' | null - Temporary copy feedback

5. ShareButton

Purpose: Generate shareable URL

Props:

  • jsonString: string - JSON to encode
  • disabled?: boolean - Disable if invalid

Features:

  • Compress JSON with pako
  • Encode to URL-safe base64
  • Copy to clipboard
  • Error handling for large JSON

Local State:

  • status: 'idle' | 'copied' | 'error' - Button state

Utility Dependencies

Components
    ↓
lib/jsonUtils.ts
    β€’ validateJson
    β€’ prettifyJson
    β€’ jsonToTree
    β€’ searchTree
    β€’ getParentPaths
    β€’ formatValueDisplay
    β€’ buildPath
    β€’ isLeafNode
    β€’ toJsonString
    
lib/urlUtils.ts
    β€’ encodeJsonToUrl
    β€’ decodeJsonFromUrl
    β€’ createShareableUrl
    β€’ getJsonFromUrl
    
lib/clipboardUtils.ts
    β€’ copyToClipboard
    β€’ showNotification
    
lib/types.ts
    β€’ TreeNode
    β€’ JsonValueType
    β€’ Component props interfaces

State Management Strategy

Why Centralized State?

  • Single source of truth: All state in app/page.tsx
  • Predictable: Easy to understand data flow
  • Debuggable: One place to check state
  • No prop drilling: Components receive only what they need

State Variables Explained

  1. jsonInput: Raw JSON string

    • Updated by: JsonInput onChange
    • Used by: JSON parsing effect
  2. jsonError: Validation error

    • Updated by: JSON parsing effect
    • Used by: JsonInput (display), ShareButton (disable)
  3. rootNode: Parsed tree structure

    • Updated by: JSON parsing effect
    • Used by: TreeViewer, search effect
  4. selectedNode: Current selection

    • Updated by: TreeNodeComponent click
    • Used by: PathPreview
  5. searchTerm: Search query

    • Updated by: SearchBar input
    • Used by: Search effect, TreeNodeComponent (highlight)
  6. expandedPaths: Set of expanded paths

    • Updated by: Toggle handler, search effect, auto-expand
    • Used by: TreeNodeComponent (determine expanded state)

Component Communication

Parent β†’ Child (Props)

// app/page.tsx passes data down
<TreeViewer
  rootNode={rootNode}
  selectedPath={selectedNode?.path}
  // ... other props
/>

Child β†’ Parent (Callbacks)

// TreeNodeComponent calls parent function
onNodeClick(node);  // Updates selectedNode in parent
onToggleExpand(path);  // Updates expandedPaths in parent

Sibling Communication

Siblings communicate through shared parent state:

JsonInput β†’ app/page.tsx (jsonInput) β†’ TreeViewer
SearchBar β†’ app/page.tsx (searchTerm) β†’ TreeViewer
TreeViewer β†’ app/page.tsx (selectedNode) β†’ PathPreview

Rendering Optimization

Current Approach

  • React default reconciliation
  • Re-render on state change
  • Sufficient for small to medium JSON

Future Optimizations (if needed)

  1. useMemo for expensive computations:

    const treeData = useMemo(() => jsonToTree(parsed), [parsed]);
  2. useCallback for event handlers:

    const handleClick = useCallback((node) => {...}, [deps]);
  3. React.memo for components:

    export default React.memo(TreeNodeComponent);
  4. Virtual scrolling for large trees:

    • Use react-window or react-virtualized

Key Patterns Used

1. Controlled Components

All components are controlled (no internal state for data):

<JsonInput value={jsonInput} onChange={setJsonInput} />

2. Render Props / Callbacks

Parent passes handlers to control behavior:

<TreeViewer onNodeClick={handleNodeClick} />

3. Conditional Rendering

Show/hide based on state:

{rootNode && <SearchBar />}
{selectedNode && <PathPreview />}

4. Recursive Components

TreeNodeComponent renders itself for children:

{node.children?.map(child => (
  <TreeNodeComponent node={child} />
))}

5. Effect for Side Effects

Load/parse/search in useEffect:

useEffect(() => {
  // Parse JSON when input changes
}, [jsonInput]);

Refer to this diagram when understanding component relationships!