import React, { useState } from 'react'; export interface SpacingValue { top: string; right: string; bottom: string; left: string; } interface SpacingInputProps { label: string; value: SpacingValue; onChange: (value: SpacingValue) => void; } const UNITS = ['px', 'em', '%'] as const; const labelStyle: React.CSSProperties = { fontSize: 11, fontWeight: 600, color: '#a1a1aa', display: 'block', marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.3px', }; const inputStyle: React.CSSProperties = { width: '100%', padding: '4px 6px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 11, textAlign: 'center', }; const sideLabel: React.CSSProperties = { fontSize: 9, color: '#71717a', textAlign: 'center', marginTop: 2, textTransform: 'uppercase', letterSpacing: '0.5px', }; function parseValue(val: string): { num: string; unit: string } { const match = val.match(/^(-?\d*\.?\d+)\s*(px|em|%|rem)?$/); if (match) return { num: match[1], unit: match[2] || 'px' }; return { num: val.replace(/[^0-9.-]/g, '') || '0', unit: 'px' }; } export const SpacingInput: React.FC = ({ label, value, onChange }) => { const [linked, setLinked] = useState(false); const [unit, setUnit] = useState(() => parseValue(value.top).unit || 'px'); const handleSideChange = (side: keyof SpacingValue, raw: string) => { const numericPart = raw.replace(/[^0-9.-]/g, ''); const newVal = numericPart ? `${numericPart}${unit}` : '0'; if (linked) { onChange({ top: newVal, right: newVal, bottom: newVal, left: newVal }); } else { onChange({ ...value, [side]: newVal }); } }; const handleUnitChange = (newUnit: string) => { setUnit(newUnit); // Re-apply current numeric values with new unit const updated: SpacingValue = { top: '', right: '', bottom: '', left: '' }; for (const side of ['top', 'right', 'bottom', 'left'] as const) { const { num } = parseValue(value[side]); updated[side] = num && num !== '0' ? `${num}${newUnit}` : '0'; } onChange(updated); }; const sides: { key: keyof SpacingValue; label: string }[] = [ { key: 'top', label: 'T' }, { key: 'right', label: 'R' }, { key: 'bottom', label: 'B' }, { key: 'left', label: 'L' }, ]; return (
{/* Unit selector */}
{UNITS.map((u) => ( ))}
{/* Link toggle */}
{sides.map((s) => (
handleSideChange(s.key, e.target.value)} style={inputStyle} />
{s.label}
))}
); }; /** Parse a CSS shorthand like "10px 20px 10px 20px" or "10px" into SpacingValue */ export function parseSpacingShorthand(val: string | undefined): SpacingValue { if (!val) return { top: '0', right: '0', bottom: '0', left: '0' }; const parts = val.trim().split(/\s+/); if (parts.length === 1) return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] }; if (parts.length === 2) return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] }; if (parts.length === 3) return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[1] }; return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] }; } /** Convert SpacingValue back to CSS shorthand */ export function spacingToShorthand(val: SpacingValue): string { const { top, right, bottom, left } = val; if (top === right && right === bottom && bottom === left) return top || '0'; if (top === bottom && right === left) return `${top} ${right}`; if (right === left) return `${top} ${right} ${bottom}`; return `${top} ${right} ${bottom} ${left}`; }