Files
fofos-admin/web/sections/BlockMedia.tsx
T

55 lines
1.7 KiB
TypeScript

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,
}: {
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;
}) => {
const [draft, setDraft] = useState<string | null>(crop);
useEffect(() => setDraft(crop), [url, aspect]);
useAutosave(draft !== crop ? (draft ?? '') : null, false, () => onCrop(draft));
return (
<MediaField
value={url}
onChange={onUrl}
crop={draft}
onCropChange={setDraft}
aspect={aspect}
label="Choose a photograph"
/>
);
};