import { useEffect, useState } from 'react'; import { MediaField } from '../MediaField'; import { useAutosave } from '../useAutosave'; /** * A photograph block's picture and its framing. * * ── Why the crop is held here ── * * `MediaField` is controlled: it reports every pointer move and draws whatever it is handed back. Wired * straight to the block, that meant a PATCH and a full reload of the preview PER MOVE — the page went * white while somebody dragged, which is what this exists to stop. * * So the drag runs against local state, which is instant, and the write is debounced by `useAutosave` * — one request when the hand stops, not sixty on the way there. The same trade every other control in * this admin makes, and the same hook. * * ── Reset by IDENTITY, not by value ── * * The effect keys on the url and the shape rather than on the crop. Keyed on the crop it would fight * the drag: the parent's value is the last SAVED one, so every move would be pulled back to it until * the save landed. */ export const BlockMedia = ({ url, crop, aspect, onUrl, onCrop, fixed = false, }: { url: string | null; crop: string | null; /** Tailwind's ratio, as the page spells it — `21/9`, `1/1`. */ aspect: string; onUrl: (url: string | null) => void; onCrop: (crop: string | null) => void; /** The picture is fixed and only its framing is editable — see MediaField. */ fixed?: boolean; }) => { const [draft, setDraft] = useState(crop); useEffect(() => setDraft(crop), [url, aspect]); useAutosave(draft !== crop ? (draft ?? '') : null, false, () => onCrop(draft)); return ( ); };