174 lines
4.7 KiB
TypeScript
174 lines
4.7 KiB
TypeScript
import React, { useRef, useCallback, PropsWithChildren } from "react"
|
|
import { Tooltip, Icon, cbModal } from "@contentstack/venus-components"
|
|
|
|
import { Resizable } from "re-resizable"
|
|
import EmbedBtn from "./EmbedBtn"
|
|
import ImageEditModal from "./ImageEditModal"
|
|
|
|
import type {
|
|
IRteParam,
|
|
IRteElementType,
|
|
} from "@contentstack/app-sdk/dist/src/RTE/types"
|
|
import type { InsertResponse } from "~/types/imagevault"
|
|
|
|
type ImageElementProps = PropsWithChildren & {
|
|
element: IRteElementType & { attrs: InsertResponse }
|
|
rte: IRteParam
|
|
}
|
|
export function ImageElement({ children, element, rte }: ImageElementProps) {
|
|
const assetUrl = element.attrs.MediaConversions[0].Url
|
|
const isSelected = rte?.selection?.isSelected()
|
|
const isFocused = rte?.selection?.isFocused()
|
|
const isHighlight = isFocused && isSelected
|
|
|
|
const imgRef = useRef<HTMLDivElement | null>(null)
|
|
|
|
const handleEdit = useCallback(() => {
|
|
cbModal({
|
|
// @ts-expect-error: Component is badly typed
|
|
component: (compProps) =>
|
|
ImageEditModal({
|
|
element,
|
|
rte,
|
|
path: rte.getPath(element),
|
|
...compProps,
|
|
}),
|
|
modalProps: {
|
|
size: "max",
|
|
},
|
|
})
|
|
}, [element, rte])
|
|
|
|
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>
|
|
)
|
|
}
|
|
|
|
const onResizeStop = () => {
|
|
const { attrs: elementAttrs } = element
|
|
const { offsetWidth: width, offsetHeight: height } = imgRef?.current ?? {}
|
|
|
|
const newAttrs: { [key: string]: unknown } = {
|
|
...elementAttrs,
|
|
style: {
|
|
...(elementAttrs?.style ?? {}),
|
|
"max-width": `${width}px`,
|
|
},
|
|
...(width && height
|
|
? { width: `${width.toString()}px`, height: `${height.toString()}px` }
|
|
: {}),
|
|
}
|
|
|
|
rte?._adv?.Transforms?.setNodes<IRteElementType>(
|
|
rte._adv.editor,
|
|
{ attrs: newAttrs },
|
|
{ at: rte.getPath(element) }
|
|
)
|
|
}
|
|
|
|
let alignmentStyles = {}
|
|
const marginAlignment: Record<string, { [key: string]: string }> = {
|
|
center: { margin: "auto" },
|
|
left: { marginRight: "auto" },
|
|
right: { marginLeft: "auto" },
|
|
}
|
|
|
|
if (typeof element.attrs.position === "string") {
|
|
alignmentStyles = marginAlignment[element.attrs.position]
|
|
}
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
...alignmentStyles,
|
|
...element.attrs.style,
|
|
}}
|
|
>
|
|
<Tooltip
|
|
zIndex={909}
|
|
className="p-0"
|
|
style={{ marginBottom: "10px" }}
|
|
position="top"
|
|
variantType="light"
|
|
offset={[0, -15]}
|
|
content={<ToolTipButtons />}
|
|
>
|
|
<span
|
|
data-type="asset"
|
|
contentEditable={false}
|
|
style={element.attrs?.style}
|
|
>
|
|
<Resizable
|
|
lockAspectRatio
|
|
size={{
|
|
width: element.attrs.width ?? "180px",
|
|
height: element.attrs.heigth ?? "auto",
|
|
}}
|
|
onResizeStop={onResizeStop}
|
|
handleStyles={{
|
|
right: { right: 0, width: "15px" },
|
|
left: { left: 0, width: "15px" },
|
|
bottom: { bottom: "0" },
|
|
bottomRight: { width: "15px" },
|
|
}}
|
|
>
|
|
<div
|
|
ref={imgRef}
|
|
contentEditable={false}
|
|
style={{
|
|
width: "100%",
|
|
height: "100%",
|
|
...(isHighlight ? { border: "1px solid #6c5ce7" } : {}),
|
|
}}
|
|
>
|
|
<img
|
|
src={assetUrl}
|
|
onError={(event) => {
|
|
event.currentTarget.src = "https://placehold.co/600x400"
|
|
}}
|
|
style={{
|
|
width: "100%",
|
|
height: "auto",
|
|
aspectRatio: element.attrs.MediaConversions[0].AspectRatio,
|
|
borderRadius: "8px",
|
|
}}
|
|
alt={element.attrs.altText}
|
|
title={element.attrs?.Name}
|
|
/>
|
|
<div
|
|
style={{
|
|
position: "absolute",
|
|
bottom: 0,
|
|
right: 0,
|
|
background: "#fff",
|
|
color: "#000",
|
|
height: "25px",
|
|
padding: "3px",
|
|
borderRadius: "4px",
|
|
}}
|
|
>
|
|
<Icon icon="Embed" />
|
|
</div>
|
|
</div>
|
|
</Resizable>
|
|
|
|
{children}
|
|
</span>
|
|
</Tooltip>
|
|
</div>
|
|
)
|
|
}
|