Generated by Spark: Expand All, Collapse All buttons are too big, Maybe just use icon buttons?

This commit is contained in:
2026-01-17 20:09:04 +00:00
committed by GitHub
parent c14aeeec87
commit 9b13431bd3
2 changed files with 117 additions and 30 deletions

View File

@@ -17,6 +17,9 @@ interface ComponentTreeNodeProps {
onDragLeave: (e: React.DragEvent) => void
onDrop: (e: React.DragEvent) => void
depth?: number
hasChildren?: boolean
isExpanded?: boolean
onToggleExpand?: () => void
}
export function ComponentTreeNode({
@@ -33,10 +36,12 @@ export function ComponentTreeNode({
onDragLeave,
onDrop,
depth = 0,
hasChildren = false,
isExpanded = false,
onToggleExpand,
}: ComponentTreeNodeProps) {
const def = getComponentDef(component.type)
const IconComponent = def ? (Icons as any)[def.icon] || Icons.Cube : Icons.Cube
const hasChildren = Array.isArray(component.children) && component.children.length > 0
return (
<div className="relative">
@@ -67,7 +72,19 @@ export function ComponentTreeNode({
)}
>
{hasChildren ? (
<Icons.CaretDown className="w-3 h-3 text-muted-foreground" />
<button
onClick={(e) => {
e.stopPropagation()
onToggleExpand?.()
}}
className="hover:text-accent"
>
{isExpanded ? (
<Icons.CaretDown className="w-3 h-3 text-muted-foreground" />
) : (
<Icons.CaretRight className="w-3 h-3 text-muted-foreground" />
)}
</button>
) : (
<div className="w-3" />
)}

View File

@@ -1,9 +1,10 @@
import { useState, useCallback } from 'react'
import { UIComponent } from '@/types/json-ui'
import { ComponentTreeNode } from '@/components/atoms/ComponentTreeNode'
import { PanelHeader } from '@/components/atoms'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Button } from '@/components/ui/button'
import { Tree, Plus } from '@phosphor-icons/react'
import { Tree, CaretDown, CaretRight } from '@phosphor-icons/react'
interface ComponentTreeProps {
components: UIComponent[]
@@ -34,39 +35,108 @@ export function ComponentTree({
onDragLeave,
onDrop,
}: ComponentTreeProps) {
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
const getAllComponentIds = useCallback((comps: UIComponent[]): string[] => {
const ids: string[] = []
const traverse = (components: UIComponent[]) => {
components.forEach((comp) => {
if (Array.isArray(comp.children) && comp.children.length > 0) {
ids.push(comp.id)
traverse(comp.children)
}
})
}
traverse(comps)
return ids
}, [])
const handleExpandAll = useCallback(() => {
const allIds = getAllComponentIds(components)
setExpandedIds(new Set(allIds))
}, [components, getAllComponentIds])
const handleCollapseAll = useCallback(() => {
setExpandedIds(new Set())
}, [])
const toggleExpand = useCallback((id: string) => {
setExpandedIds((prev) => {
const next = new Set(prev)
if (next.has(id)) {
next.delete(id)
} else {
next.add(id)
}
return next
})
}, [])
const renderTree = (comps: UIComponent[], depth = 0) => {
return comps.map((comp) => (
<div key={comp.id}>
<ComponentTreeNode
component={comp}
isSelected={selectedId === comp.id}
isHovered={hoveredId === comp.id}
isDraggedOver={draggedOverId === comp.id}
dropPosition={draggedOverId === comp.id ? dropPosition : null}
onSelect={() => onSelect(comp.id)}
onHover={() => onHover(comp.id)}
onHoverEnd={onHoverEnd}
onDragStart={(e) => onDragStart(comp.id, e)}
onDragOver={(e) => onDragOver(comp.id, e)}
onDragLeave={onDragLeave}
onDrop={(e) => onDrop(comp.id, e)}
depth={depth}
/>
{Array.isArray(comp.children) && comp.children.length > 0 && (
<div>{renderTree(comp.children, depth + 1)}</div>
)}
</div>
))
return comps.map((comp) => {
const hasChildren = Array.isArray(comp.children) && comp.children.length > 0
const isExpanded = expandedIds.has(comp.id)
return (
<div key={comp.id}>
<ComponentTreeNode
component={comp}
isSelected={selectedId === comp.id}
isHovered={hoveredId === comp.id}
isDraggedOver={draggedOverId === comp.id}
dropPosition={draggedOverId === comp.id ? dropPosition : null}
onSelect={() => onSelect(comp.id)}
onHover={() => onHover(comp.id)}
onHoverEnd={onHoverEnd}
onDragStart={(e) => onDragStart(comp.id, e)}
onDragOver={(e) => onDragOver(comp.id, e)}
onDragLeave={onDragLeave}
onDrop={(e) => onDrop(comp.id, e)}
depth={depth}
hasChildren={hasChildren}
isExpanded={isExpanded}
onToggleExpand={() => toggleExpand(comp.id)}
/>
{hasChildren && isExpanded && comp.children && (
<div>{renderTree(comp.children, depth + 1)}</div>
)}
</div>
)
})
}
return (
<div className="h-full flex flex-col">
<div className="p-4">
<PanelHeader
title="Component Tree"
subtitle={`${components.length} component${components.length !== 1 ? 's' : ''}`}
icon={<Tree size={20} weight="duotone" />}
/>
<div className="flex items-center justify-between mb-2">
<PanelHeader
title="Component Tree"
subtitle={`${components.length} component${components.length !== 1 ? 's' : ''}`}
icon={<Tree size={20} weight="duotone" />}
/>
{components.length > 0 && (
<div className="flex gap-1">
<Button
variant="ghost"
size="sm"
onClick={handleExpandAll}
className="h-7 w-7 p-0"
title="Expand All"
>
<CaretDown size={16} />
</Button>
<Button
variant="ghost"
size="sm"
onClick={handleCollapseAll}
className="h-7 w-7 p-0"
title="Collapse All"
>
<CaretRight size={16} />
</Button>
</div>
)}
</div>
</div>
<ScrollArea className="flex-1">