'use client'; import type { CSSProperties, ReactNode } from 'react'; import type { PPTImageElement } from '@openmaic/dsl'; import { useElementShadow } from '../shared/useElementShadow'; import { useElementFlip } from '../shared/useElementFlip'; import { useClipImage } from './useClipImage'; import { useFilter } from './useFilter'; import { ImageOutline } from './ImageOutline'; export interface BaseImageElementProps { elementInfo: PPTImageElement; /** * Optional render slot: replace the default with custom content. * The slot receives `(element, resolvedSrc, defaultContent)` and is responsible * for selecting placeholders, retry UI, or the renderer's prepared image. * Its return value is authoritative, including `null`. */ renderImage?: ( element: PPTImageElement, resolvedSrc: string, defaultContent: ReactNode, ) => ReactNode; } export function BaseImageElement({ elementInfo, renderImage }: BaseImageElementProps) { const { shadowStyle } = useElementShadow(elementInfo.shadow); const { flipStyle } = useElementFlip(elementInfo.flipH, elementInfo.flipV); const { clipShape, imgPosition } = useClipImage(elementInfo); const { filter } = useFilter(elementInfo.filters); const src = elementInfo.src; // Soft-edge feather (a:softEdge): fade the image alpha to transparent over // `softEdge` px at every edge. Two intersecting linear-gradient masks feather // all four sides (corners get both). html2canvas-pro ignores CSS masks, so // slideToPng bakes the same feather into pixels via the data-soft-edge hook. const softEdge = elementInfo.softEdge && elementInfo.softEdge > 0 ? elementInfo.softEdge : undefined; const softEdgeMaskStyle: CSSProperties = softEdge ? (() => { const grad = (dir: string) => `linear-gradient(${dir}, transparent 0, #000 ${softEdge}px, #000 calc(100% - ${softEdge}px), transparent 100%)`; const mask = `${grad('to right')}, ${grad('to bottom')}`; return { maskImage: mask, WebkitMaskImage: mask, maskComposite: 'intersect', WebkitMaskComposite: 'source-in', maskRepeat: 'no-repeat', WebkitMaskRepeat: 'no-repeat', } as CSSProperties; })() : {}; const defaultContent = src ? ( <> e.preventDefault()} /> {elementInfo.colorMask && (
)} ) : null; return (
{renderImage ? renderImage(elementInfo, src, defaultContent) : defaultContent}
); }