import React, { useState } from "react";
import { useEntity, useReplicache } from "src/replicache";
import { localImages } from "src/utils/addImage";
import { Modal } from "components/Modal";
import { MobileSheet } from "components/MobileSheet";
import { ButtonPrimary, ButtonTertiary } from "components/Buttons";
import { useIsMobile } from "src/hooks/isMobile";
// Alt-text editor shown as a modal (desktop) or mobile sheet. Does not
// autosave — the value is committed on Save or Enter. Shared by ImageBlock,
// the gallery's per-image button, and the gallery lightbox.
export function ImageAltModal(props: {
entityID: string;
trigger: React.ReactNode;
title: string;
}) {
let [altEditorOpen, setAltEditorOpen] = useState(false);
let isMobile = useIsMobile();
// Mount fresh on open so the textarea seeds from the current alt text.
if (isMobile)
return (
setAltEditorOpen(false)}
/>
);
return (
setAltEditorOpen(false)}
/>
);
}
function ImageAltModalContent(props: {
entityID: string;
onClose: () => void;
}) {
let { rep } = useReplicache();
let image = useEntity(props.entityID, "block/image");
let altText = useEntity(props.entityID, "image/alt")?.data.value ?? "";
// `draft` stays null until the user edits, so the textarea reflects the live
// alt text once Replicache's subscription resolves — the first render returns
// a stale page-load snapshot that would otherwise get frozen into state.
let [draft, setDraft] = useState(null);
let value = draft ?? altText;
let src = image ? localImages.get(image.data.src) ?? image.data.src : null;
let save = async (next: string = value) => {
await rep?.mutate.assertFact({
entity: props.entityID,
attribute: "image/alt",
data: { type: "string", value: next },
});
props.onClose();
};
return (