feat: icon size picker (S/M/L) on device nodes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,10 +7,14 @@ import { NodeTooltip, NodeTooltipTrigger, NodeTooltipContent } from '../ui/node-
|
|||||||
import { getDeviceRenderConfig } from './deviceRegistry'
|
import { getDeviceRenderConfig } from './deviceRegistry'
|
||||||
import type { DeviceProperties } from '@/types'
|
import type { DeviceProperties } from '@/types'
|
||||||
|
|
||||||
|
export type IconSize = 'sm' | 'md' | 'lg'
|
||||||
|
export const ICON_SIZE_PX: Record<IconSize, number> = { sm: 18, md: 28, lg: 42 }
|
||||||
|
|
||||||
export interface DeviceNodeData {
|
export interface DeviceNodeData {
|
||||||
label: string
|
label: string
|
||||||
deviceType: string
|
deviceType: string
|
||||||
category?: string
|
category?: string
|
||||||
|
iconSize?: IconSize
|
||||||
properties: DeviceProperties
|
properties: DeviceProperties
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
@@ -31,6 +35,7 @@ function DeviceNodeComponent({ data }: NodeProps) {
|
|||||||
const status = (nodeData.properties?.status || 'unknown') as NodeStatus
|
const status = (nodeData.properties?.status || 'unknown') as NodeStatus
|
||||||
const ip = nodeData.properties?.ip
|
const ip = nodeData.properties?.ip
|
||||||
const props = nodeData.properties || {}
|
const props = nodeData.properties || {}
|
||||||
|
const iconPx = ICON_SIZE_PX[nodeData.iconSize ?? 'md']
|
||||||
|
|
||||||
const hasTooltipContent = props.hostname || props.ip || props.vendor || props.model || props.role || props.notes
|
const hasTooltipContent = props.hostname || props.ip || props.vendor || props.model || props.role || props.notes
|
||||||
|
|
||||||
@@ -40,7 +45,7 @@ function DeviceNodeComponent({ data }: NodeProps) {
|
|||||||
<NodeTooltipTrigger>
|
<NodeTooltipTrigger>
|
||||||
<BaseNode className="min-w-[120px] group">
|
<BaseNode className="min-w-[120px] group">
|
||||||
<BaseNodeHeader className="flex-col gap-1 items-center py-3 px-4">
|
<BaseNodeHeader className="flex-col gap-1 items-center py-3 px-4">
|
||||||
<Icon size={28} style={{ color }} />
|
<Icon size={iconPx} style={{ color }} />
|
||||||
<BaseNodeHeaderTitle className="text-center text-xs">
|
<BaseNodeHeaderTitle className="text-center text-xs">
|
||||||
{nodeData.label}
|
{nodeData.label}
|
||||||
</BaseNodeHeaderTitle>
|
</BaseNodeHeaderTitle>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Trash2, Minus, Spline, GitBranch, BringToFront, SendToBack } from 'luci
|
|||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import type { DeviceProperties, DiagramEdge } from '@/types'
|
import type { DeviceProperties, DiagramEdge } from '@/types'
|
||||||
import type { Node, Edge } from '@xyflow/react'
|
import type { Node, Edge } from '@xyflow/react'
|
||||||
import type { DeviceNodeData } from '../nodes/DeviceNode'
|
import type { DeviceNodeData, IconSize } from '../nodes/DeviceNode'
|
||||||
|
|
||||||
interface PropertiesPanelProps {
|
interface PropertiesPanelProps {
|
||||||
selectedNode: Node | null
|
selectedNode: Node | null
|
||||||
@@ -272,6 +272,30 @@ export function PropertiesPanel({
|
|||||||
<FieldInput value={nodeData.label} onChange={handleLabelChange} placeholder="Device name" />
|
<FieldInput value={nodeData.label} onChange={handleLabelChange} placeholder="Device name" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Icon size */}
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<FieldLabel>Icon Size</FieldLabel>
|
||||||
|
<div className="flex gap-1">
|
||||||
|
{(['sm', 'md', 'lg'] as IconSize[]).map(size => {
|
||||||
|
const active = (nodeData.iconSize ?? 'md') === size
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={size}
|
||||||
|
onClick={() => onNodeUpdate(selectedNode!.id, { iconSize: size } as Partial<DeviceNodeData>)}
|
||||||
|
className={cn(
|
||||||
|
'flex flex-1 items-center justify-center rounded border py-1.5 text-[10px] font-medium uppercase transition-colors',
|
||||||
|
active
|
||||||
|
? 'border-accent bg-accent/10 text-accent'
|
||||||
|
: 'border-default text-muted-foreground hover:border-hover hover:text-primary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{size}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Layering */}
|
{/* Layering */}
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<FieldLabel>Layer</FieldLabel>
|
<FieldLabel>Layer</FieldLabel>
|
||||||
|
|||||||
Reference in New Issue
Block a user