import { useCallback, useEffect, useState } from "react"; import { flushSync } from "react-dom"; import { AssetCardVertical, Button, FieldLabel, cbModal, } from "@contentstack/venus-components"; import ImageEditModal from "./ImageEditModal"; import FullSizeImage from "./FullSizeImage"; import { isInsertResponse, openImageVault } from "~/utils/imagevault"; import type { CbModalProps } from "@contentstack/venus-components/build/components/Modal/Modal"; import type UiLocation from "@contentstack/app-sdk/dist/src/uiLocation"; import type { InsertResponse } from "~/types/imagevault"; import type { EntryDataPublishDetails, ImageVaultDAMConfig, } from "~/utils/imagevault"; import type { Lang } from "~/types/lang"; export type ImageVaultDAMProps = { sdk: UiLocation; config: ImageVaultDAMConfig; initialData: InsertResponse | null; }; type DAMButtonProps = { onClick: () => void; }; function DAMButton({ onClick }: DAMButtonProps) { return (
); } type MediaProps = { media: InsertResponse; onDelete: () => void; onEdit: () => void; }; function Media({ media, onDelete, onEdit }: MediaProps) { // MediaConversions is an array but will only contain one object const { Url, Height, FormatHeight, Width, FormatWidth, Name, AspectRatio } = media.MediaConversions[0]; const assetUrl = Url; const title = media.Name; const width = FormatWidth || Width; const height = FormatHeight || Height; const alt = media.Metadata?.find((meta) => meta.Name.includes("AltText_"))?.Value || Name; return (
{ const cbModalProps: CbModalProps = { // @ts-expect-error: Component is badly typed component: (props) => { return ( ); }, modalProps: { size: "max", style: { content: { display: "grid", width: "90dvw", height: "90dvh", maxHeight: "100%", gridTemplateRows: "auto 1fr", }, overlay: {}, }, }, }; cbModal(cbModalProps); }} />
); } export default function ImageVaultDAM({ sdk, config, initialData, }: ImageVaultDAMProps) { const [media, setMedia] = useState(initialData); const field = sdk.location.CustomField?.field; const frame = sdk.location.CustomField?.frame; const entry = sdk.location.CustomField?.entry; const stack = sdk.location.CustomField?.stack; const updateFrameHeight = useCallback(() => { if (frame?.updateHeight) { // We need to recalculate the height of the iframe when an image is added. // Cannot use flushSync inside useEffect. setTimeout(() => frame.updateHeight(document.body.scrollHeight), 0); } }, [frame]); const handleMedia = useCallback( (result?: InsertResponse) => { if (field && result) { flushSync(() => { setMedia(result); field.setData(result); document.body.style.overflow = "hidden"; }); } updateFrameHeight(); }, [field, updateFrameHeight] ); useEffect(() => { updateFrameHeight(); }, [updateFrameHeight]); const handleEdit = useCallback(() => { const fieldData = field?.getData() as InsertResponse; if (isInsertResponse(fieldData)) { cbModal({ // @ts-expect-error: Component is badly typed component: (compProps) => ( ), modalProps: { size: "max", }, }); } }, [field, handleMedia]); if (!field || !frame || !entry || !stack) { return

Initializing custom field...

; } const entryData: EntryDataPublishDetails = { //TODO: Add support for branches branch: "main", contentTypeUid: entry.content_type.uid, locale: entry.locale as Lang, stackApiKey: stack._data.api_key, title: entry.getField("title").getData().toString() || `Untitled (${entry.content_type.uid})`, uid: entry._data.uid, }; return (
ImageVault Asset
{media ? ( { setMedia(null); handleMedia(); }} onEdit={handleEdit} /> ) : ( { openImageVault({ config, entryData, onSuccess: handleMedia, }); }} /> )}
); }