import { Button } from '@/components/ui/button'; import { DEVICES, type DeviceName } from '../ViewMode'; /** * Padding, per section, per breakpoint. * * ── The device toggle IS the breakpoint selector ── * * Editing all three widths at once would be six controls per section, and five of them would be for a * width you are not looking at. Instead the bar's Desktop / Tablet / Mobile decides which breakpoint * these two controls write to — so you switch to Mobile, see the phone, and change the padding you can * see changing. * * The mapping is Tailwind's, and it lines up with the three preview widths exactly: * * Mobile 390 base every width * Tablet 834 md 768 and up * Desktop 1440 lg 1024 and up * * ── Inherit is a real value, and not the same as zero ── * * They cascade, so an unset breakpoint takes the smaller one's value. That is what makes the common * case — one padding everywhere — a single choice rather than three. Zero is the opposite instruction: * no padding HERE even though the smaller width has some. Both have to be expressible, so "Inherit" * and "0" are separate options rather than one meaning both. */ export type SectionPadding = Partial>; export type LayoutBand = { id: number; name: string; padding: SectionPadding }; /** Which breakpoint a device is looking at. See the table above. */ export const BREAKPOINT: Record = { mobile: 'base', tablet: 'md', desktop: 'lg', }; /** What a breakpoint inherits when an axis is unset: the nearest smaller one that has a value. */ const inherited = (padding: SectionPadding, breakpoint: 'base' | 'md' | 'lg', axis: 'x' | 'y'): number | null => { const below = { base: [], md: ['base'], lg: ['md', 'base'] } as const; for (const smaller of below[breakpoint]) { const step = padding[smaller]?.[axis]; if (typeof step === 'number') return step; } return null; }; export const PaddingTab = ({ bands, device, steps, selected, onSelect, onChange, }: { bands: LayoutBand[]; device: DeviceName; /** The scale, sent by the API so this file holds no copy of it. */ steps: number[]; selected: number | null; onSelect: (id: number) => void; onChange: (id: number, padding: SectionPadding) => void; }) => { const breakpoint = BREAKPOINT[device]; const band = bands.find((one) => one.id === selected) ?? bands[0]; if (!band) { // Before the layout has been seeded. Said plainly rather than shown as an empty form somebody // would try to use. return

The layout has no sections yet.

; } const set = (axis: 'x' | 'y', value: string) => { const at = { ...(band.padding[breakpoint] ?? {}) }; if (value === '') delete at[axis]; else at[axis] = Number(value); const next = { ...band.padding }; // An empty breakpoint is REMOVED rather than left as `{}` — otherwise "inherit everything" would // be stored as a thing rather than as an absence, and the two would have to be told apart later. if (Object.keys(at).length) next[breakpoint] = at; else delete next[breakpoint]; onChange(band.id, next); }; const axis = (name: 'x' | 'y', label: string) => { const own = band.padding[breakpoint]?.[name]; const from = inherited(band.padding, breakpoint, name); return (
); }; return (
{bands.map((one) => ( ))} {/* Which width is being edited, said out loud. The device buttons are at the top of the panel and the effect of pressing one is not obvious from down here. */} editing {DEVICES[device].label.toLowerCase()} — {breakpoint}
{axis('x', 'Horizontal')} {axis('y', 'Vertical')}
); };