"use client"; import { useEntity, useReplicache } from "src/replicache"; import { BlockProps } from "./Block"; import { useUIState } from "src/useUIState"; import Image from "next/image"; import { v7 } from "uuid"; import { useEntitySetContext } from "components/EntitySetProvider"; import { generateKeyBetween } from "fractional-indexing"; import { addImage, localImages } from "src/utils/addImage"; import { elementId } from "src/utils/elementId"; import { createContext, useContext, useEffect, useState } from "react"; import { BlockImageSmall } from "components/Icons/BlockImageSmall"; import { Popover } from "components/Popover"; import { theme } from "tailwind.config"; import { EditTiny } from "components/Icons/EditTiny"; import { AsyncValueAutosizeTextarea } from "components/utils/AutosizeTextarea"; import { set } from "colorjs.io/fn"; import { ImageAltSmall } from "components/Icons/ImageAlt"; export function ImageBlock(props: BlockProps & { preview?: boolean }) { let { rep } = useReplicache(); let image = useEntity(props.value, "block/image"); let entity_set = useEntitySetContext(); let isSelected = useUIState((s) => s.selectedBlocks.find((b) => b.value === props.value), ); let isLocked = useEntity(props.value, "block/is-locked")?.data.value; let isFullBleed = useEntity(props.value, "image/full-bleed")?.data.value; let isFirst = props.previousBlock === null; let isLast = props.nextBlock === null; let altText = useEntity(props.value, "image/alt")?.data.value; let nextIsFullBleed = useEntity( props.nextBlock && props.nextBlock.value, "image/full-bleed", )?.data.value; let prevIsFullBleed = useEntity( props.previousBlock && props.previousBlock.value, "image/full-bleed", )?.data.value; useEffect(() => { if (props.preview) return; let input = document.getElementById(elementId.block(props.entityID).input); if (isSelected) { input?.focus(); } else { input?.blur(); } }, [isSelected, props.preview, props.entityID]); const handleImageUpload = async (file: File) => { if (!rep) return; let entity = props.entityID; if (!entity) { entity = v7(); await rep?.mutate.addBlock({ parent: props.parent, factID: v7(), permission_set: entity_set.set, type: "text", position: generateKeyBetween( props.position, props.nextPosition, ), newEntityID: entity, }); } await rep.mutate.assertFact({ entity, attribute: "block/type", data: { type: "block-type-union", value: "image" }, }); await addImage(file, rep, { entityID: entity, attribute: "block/image", }); }; if (!image) { if (!entity_set.permissions.write) return null; return (
); } let className = isFullBleed ? "" : isSelected ? "block-border-selected border-transparent! " : "block-border border-transparent!"; let isLocalUpload = localImages.get(image.data.src); return (
{isFullBleed && isSelected ? : null} {isLocalUpload || image.data.local ? ( {altText} ) : ( {altText )} {altText !== undefined && !props.preview ? ( ) : null}
); } export const FullBleedSelectionIndicator = () => { return (
); }; export const ImageBlockContext = createContext({ altEditorOpen: false, setAltEditorOpen: (s: boolean) => {}, }); const ImageAlt = (props: { entityID: string }) => { let { rep } = useReplicache(); let altText = useEntity(props.entityID, "image/alt")?.data.value; let entity_set = useEntitySetContext(); let setAltEditorOpen = useUIState((s) => s.setOpenPopover); let altEditorOpen = useUIState((s) => s.openPopover === props.entityID); if (!entity_set.permissions.write && altText === "") return null; return (
setAltEditorOpen(altEditorOpen ? null : props.entityID) } > } > {entity_set.permissions.write ? ( { e.currentTarget.setSelectionRange( e.currentTarget.value.length, e.currentTarget.value.length, ); }} onChange={async (e) => { await rep?.mutate.assertFact({ entity: props.entityID, attribute: "image/alt", data: { type: "string", value: e.currentTarget.value }, }); }} placeholder="add alt text..." /> ) : (
{altText}
)}
); };