'use client'; // ── Box Model Section — Margin / Padding visual diagram ─────────────────────── // Renders a concentric-rectangle diagram (like browser DevTools' box model view). // Outer ring = margin, middle ring = border, inner ring = padding, center = W×H. // Each editable quad shows the current value; clicking focuses an inline input. import { useState } from 'react'; import { useCanvasTheme } from '@/store/canvasTheme'; import { SectionHeader, HSep, parseCssNum, parseCssUnit } from '../DesignInputs'; interface BoxModelSectionProps { styles: Record; onPatch: (prop: string, val: string) => void; } // Parse a shorthand-or-individual CSS value for one side. function getSide(styles: Record, base: string, side: string): string { const individual = styles[`${base}-${side}`]; if (individual) return individual; return styles[base] ?? '0px'; } // Tiny inline editable cell for a box model value. function BoxCell({ prop, value, onPatch, mini = false, style: styleProp, }: { prop: string; value: string; onPatch: (prop: string, val: string) => void; mini?: boolean; style?: React.CSSProperties; }) { const T = useCanvasTheme(); const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(''); const num = parseCssNum(value); const unit = parseCssUnit(value); const display = num || '0'; function commit(raw: string) { const n = parseFloat(raw); if (!isNaN(n)) onPatch(prop, `${n}${unit || 'px'}`); setEditing(false); } if (editing) { return ( setDraft(e.target.value)} onBlur={() => commit(draft)} onKeyDown={e => { if (e.key === 'Enter') commit(draft); if (e.key === 'Escape') setEditing(false); }} style={{ width: mini ? 28 : 36, textAlign: 'center', fontFamily: "'JetBrains Mono', monospace", fontSize: '0.5625rem', background: T.bgDeep, border: `1px solid ${T.accent}`, borderRadius: 3, color: T.fg, outline: 'none', padding: '1px 3px', ...styleProp, }} /> ); } return ( { setEditing(true); setDraft(num || '0'); }} title={`${prop}: ${value}`} style={{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.5625rem', color: T.fg, cursor: 'text', minWidth: mini ? 20 : 28, textAlign: 'center', display: 'inline-block', padding: '1px 2px', borderRadius: 2, transition: 'background 0.1s', ...styleProp, }} onMouseEnter={e => (e.currentTarget.style.background = T.hoverBg ?? 'rgba(255,255,255,0.06)')} onMouseLeave={e => (e.currentTarget.style.background = 'transparent')} > {display} ); } export function BoxModelSection({ styles, onPatch }: BoxModelSectionProps) { const T = useCanvasTheme(); const [open, setOpen] = useState(false); // Margin values const mt = getSide(styles, 'margin', 'top'); const mr = getSide(styles, 'margin', 'right'); const mb = getSide(styles, 'margin', 'bottom'); const ml = getSide(styles, 'margin', 'left'); // Padding values const pt = getSide(styles, 'padding', 'top'); const pr = getSide(styles, 'padding', 'right'); const pb = getSide(styles, 'padding', 'bottom'); const pl = getSide(styles, 'padding', 'left'); // Border values (display only — editable via StrokeSection) const bw = styles['border-width'] ?? '0px'; // Dimensions const w = parseCssNum(styles['width'] ?? '0px') || '—'; const h = parseCssNum(styles['height'] ?? '0px') || '—'; // Shared ring styles const ringBase: React.CSSProperties = { position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', }; const labelStyle: React.CSSProperties = { fontFamily: "'JetBrains Mono', monospace", fontSize: '0.4375rem', color: T.dim, letterSpacing: '0.06em', textTransform: 'uppercase', position: 'absolute', top: 4, left: 6, userSelect: 'none', pointerEvents: 'none', }; return ( <> setOpen(!open)} /> {open && (
{/* Outermost = margin */}
margin {/* Top margin */} {/* Bottom margin */} {/* Left margin */} {/* Right margin */} {/* Border ring */}
border {/* Border width — read-only hint */} {parseCssNum(bw) || '0'} {/* Padding ring */}
padding {/* Content dimensions */} {w} × {h}
)} ); }