133 lines
3.5 KiB
TypeScript
133 lines
3.5 KiB
TypeScript
import { Icon, Tooltip, cbModal } from "@contentstack/venus-components"
|
|
import React, { PropsWithChildren, useCallback } from "react"
|
|
|
|
import EmbedBtn from "./EmbedBtn"
|
|
import ImageEditModal from "./ImageEditModal"
|
|
|
|
import type {
|
|
IRteElementType,
|
|
IRteParam,
|
|
} from "@contentstack/app-sdk/dist/src/RTE/types"
|
|
import type { ImageVaultAsset, InsertResponse } from "~/types/imagevault"
|
|
import {
|
|
getImageVaultAssetFromData,
|
|
isInsertResponse,
|
|
} from "~/utils/imagevault"
|
|
|
|
type ImageElementProps = PropsWithChildren & {
|
|
element: IRteElementType & { attrs: ImageVaultAsset | InsertResponse }
|
|
rte: IRteParam
|
|
}
|
|
export function ImageElement({ children, element, rte }: ImageElementProps) {
|
|
const assetIsInsertResponse = isInsertResponse(element.attrs)
|
|
const imageVaultAsset = getImageVaultAssetFromData(element.attrs)
|
|
const isSelected = rte.selection.isSelected()
|
|
const isFocused = rte.selection.isFocused()
|
|
const path = rte.getPath(element)
|
|
const isHighlight = isFocused && isSelected
|
|
|
|
const handleMedia = useCallback(
|
|
(asset: ImageVaultAsset) => {
|
|
rte._adv.Transforms.setNodes<IRteElementType>(
|
|
rte._adv.editor,
|
|
{
|
|
attrs: { ...asset },
|
|
},
|
|
{ at: path }
|
|
)
|
|
},
|
|
[rte, path]
|
|
)
|
|
|
|
const handleEdit = useCallback(() => {
|
|
cbModal({
|
|
// @ts-expect-error: Component is badly typed
|
|
component: (compProps) => (
|
|
<ImageEditModal
|
|
element={element}
|
|
setData={handleMedia}
|
|
{...compProps}
|
|
/>
|
|
),
|
|
modalProps: {
|
|
size: "max",
|
|
style: {
|
|
content: {
|
|
maxHeight: "90dvh",
|
|
maxWidth: "90dvw",
|
|
width: "auto",
|
|
},
|
|
overlay: {},
|
|
},
|
|
},
|
|
})
|
|
}, [element, handleMedia])
|
|
|
|
const ToolTipButtons = () => {
|
|
return (
|
|
<div contentEditable={false} className="embed--btn-group">
|
|
<EmbedBtn title="edit" content="Edit" onClick={handleEdit}>
|
|
<Icon icon="Rename" />
|
|
</EmbedBtn>
|
|
|
|
<EmbedBtn
|
|
title="remove"
|
|
content={"Remove"}
|
|
onClick={() => rte.removeNode(element)}
|
|
>
|
|
<Icon icon="Trash" />
|
|
</EmbedBtn>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!imageVaultAsset) {
|
|
return <>{children}</>
|
|
}
|
|
|
|
// The existing data might still be in InsertResponse format if the user has not edited it yet.
|
|
// We'll convert it to ImageVaultAsset when the user edits the RTE.
|
|
if (assetIsInsertResponse) {
|
|
handleMedia(imageVaultAsset)
|
|
}
|
|
|
|
return (
|
|
<Tooltip
|
|
zIndex={909}
|
|
className="p-0"
|
|
style={{ marginBottom: "10px" }}
|
|
position="top"
|
|
variantType="light"
|
|
offset={[0, -15]}
|
|
content={<ToolTipButtons />}
|
|
>
|
|
<span data-type="asset" contentEditable={false}>
|
|
<div
|
|
contentEditable={false}
|
|
style={{
|
|
width: "280px",
|
|
height: "auto",
|
|
...(isHighlight ? { border: "1px solid #6c5ce7" } : {}),
|
|
}}
|
|
>
|
|
<img
|
|
src={imageVaultAsset.url}
|
|
onError={(event) => {
|
|
event.currentTarget.src = "https://placehold.co/600x400"
|
|
}}
|
|
style={{
|
|
width: "100%",
|
|
height: "auto",
|
|
aspectRatio: imageVaultAsset.dimensions.aspectRatio,
|
|
borderRadius: "8px",
|
|
}}
|
|
alt={imageVaultAsset.meta.alt}
|
|
title={`Id: ${imageVaultAsset.imageVaultId} - ${imageVaultAsset.fileName}`}
|
|
/>
|
|
</div>
|
|
{children}
|
|
</span>
|
|
</Tooltip>
|
|
)
|
|
}
|