import type { CSSProperties } from "react"; import type { SectionBlockProps } from "@/features/editor/state/editorStore"; export interface SectionExportTokens { sectionClasses: string; sectionStyleParts: string[]; backgroundVideoHtml: string; } export function computeSectionExportTokens(props: SectionBlockProps): SectionExportTokens { const bgToken = props.background ?? "default"; const bgClassMap: Record = { default: "pb-section-bg-default", muted: "pb-section-bg-muted", primary: "pb-section-bg-primary", }; const bgClass = bgClassMap[bgToken] ?? "pb-section-bg-default"; const pyToken = props.paddingY ?? "md"; const pyClassMap: Record = { sm: "pb-section-py-sm", md: "pb-section-py-md", lg: "pb-section-py-lg", }; const pyClass = pyClassMap[pyToken] ?? "pb-section-py-md"; const sectionClasses = ["pb-section", bgClass, pyClass].filter(Boolean).join(" "); const sectionStyleParts: string[] = []; if (typeof props.backgroundColorCustom === "string" && props.backgroundColorCustom.trim() !== "") { sectionStyleParts.push(`background-color:${props.backgroundColorCustom.trim()}`); } const bgImgSrc = props.backgroundImageSrc; if (typeof bgImgSrc === "string" && bgImgSrc.trim() !== "") { const url = bgImgSrc.trim(); sectionStyleParts.push(`background-image:url(${url})`); const size = props.backgroundImageSize ?? "cover"; sectionStyleParts.push(`background-size:${size}`); const posMode = props.backgroundImagePositionMode ?? "preset"; if (posMode === "custom") { const x = props.backgroundImagePositionXPercent; const y = props.backgroundImagePositionYPercent; const xSafe = typeof x === "number" ? x : 50; const ySafe = typeof y === "number" ? y : 50; sectionStyleParts.push(`background-position:${xSafe}% ${ySafe}%`); } else { const posToken = props.backgroundImagePosition ?? "center"; const position = posToken === "top" ? "center top" : posToken === "bottom" ? "center bottom" : posToken === "left" ? "left center" : posToken === "right" ? "right center" : "center center"; sectionStyleParts.push(`background-position:${position}`); } const repeat = props.backgroundImageRepeat ?? "no-repeat"; sectionStyleParts.push(`background-repeat:${repeat}`); } const bgVideoSrc = props.backgroundVideoSrc; let backgroundVideoHtml = ""; if (typeof bgVideoSrc === "string" && bgVideoSrc.trim() !== "") { const url = bgVideoSrc.trim(); backgroundVideoHtml = ``; sectionStyleParts.push("position:relative"); sectionStyleParts.push("overflow:hidden"); } return { sectionClasses, sectionStyleParts, backgroundVideoHtml, }; } export interface SectionPublicTokens { sectionStyle: CSSProperties; innerWrapperStyle: CSSProperties; columnsContainerStyle: CSSProperties; hasBackgroundVideo: boolean; backgroundVideoSrc: string | null; } const pxToEm = (px: number, base = 16) => `${px / base}em`; export function computeSectionPublicTokens(props: SectionBlockProps): SectionPublicTokens { const sectionStyle: CSSProperties = {}; if (props.backgroundColorCustom && props.backgroundColorCustom.trim() !== "") { sectionStyle.backgroundColor = props.backgroundColorCustom.trim(); } if (typeof props.paddingYPx === "number" && props.paddingYPx > 0) { const paddingEm = pxToEm(props.paddingYPx); sectionStyle.paddingTop = paddingEm; sectionStyle.paddingBottom = paddingEm; } const rawBgVideoSrc = (props as any).backgroundVideoSrc; let hasBackgroundVideo = false; let backgroundVideoSrc: string | null = null; if (typeof rawBgVideoSrc === "string" && rawBgVideoSrc.trim() !== "") { hasBackgroundVideo = true; backgroundVideoSrc = rawBgVideoSrc.trim(); sectionStyle.position = "relative"; sectionStyle.overflow = "hidden"; } if (props.backgroundImageSrc && props.backgroundImageSrc.trim() !== "") { const url = props.backgroundImageSrc.trim(); sectionStyle.backgroundImage = `url(${url})`; const size = props.backgroundImageSize ?? "cover"; sectionStyle.backgroundSize = size; const posMode = props.backgroundImagePositionMode ?? "preset"; if ( posMode === "custom" && typeof props.backgroundImagePositionXPercent === "number" && typeof props.backgroundImagePositionYPercent === "number" ) { const x = props.backgroundImagePositionXPercent; const y = props.backgroundImagePositionYPercent; sectionStyle.backgroundPosition = `${x}% ${y}%`; } else { const posToken = props.backgroundImagePosition ?? "center"; const position = posToken === "top" ? "center top" : posToken === "bottom" ? "center bottom" : posToken === "left" ? "left center" : posToken === "right" ? "right center" : "center center"; sectionStyle.backgroundPosition = position; } const repeat = props.backgroundImageRepeat ?? "no-repeat"; sectionStyle.backgroundRepeat = repeat; } const innerWrapperStyle: CSSProperties = {}; if (typeof props.maxWidthPx === "number" && props.maxWidthPx > 0) { innerWrapperStyle.maxWidth = pxToEm(props.maxWidthPx); } const columnsContainerStyle: CSSProperties = {}; if (typeof props.gapXPx === "number" && props.gapXPx > 0) { columnsContainerStyle.columnGap = pxToEm(props.gapXPx); } return { sectionStyle, innerWrapperStyle, columnsContainerStyle, hasBackgroundVideo, backgroundVideoSrc, }; } export interface SectionEditorTokens { bgClass: string; pyClass: string; alignItemsClass: string; wrapperStyle: CSSProperties; innerWrapperStyle: CSSProperties; columnsContainerStyle: CSSProperties; hasBackgroundVideo: boolean; backgroundVideoSrc: string | null; } export function computeSectionEditorTokens(props: SectionBlockProps): SectionEditorTokens { const bgClass = props.background === "muted" ? "bg-slate-950/40" : props.background === "primary" ? "bg-sky-950/40 border-sky-900/60" : "bg-slate-900/60"; const pyClass = props.paddingY === "sm" ? "py-4" : props.paddingY === "lg" ? "py-10" : "py-6"; const alignItemsClass = props.alignItems === "center" ? "items-center" : props.alignItems === "bottom" ? "items-end" : "items-start"; const wrapperStyle: CSSProperties = {}; if (props.backgroundColorCustom && props.backgroundColorCustom.trim() !== "") { wrapperStyle.backgroundColor = props.backgroundColorCustom; } if (typeof props.paddingYPx === "number" && props.paddingYPx > 0) { wrapperStyle.paddingTop = `${props.paddingYPx}px`; wrapperStyle.paddingBottom = `${props.paddingYPx}px`; } let hasBackgroundVideo = false; let backgroundVideoSrc: string | null = null; if (typeof props.backgroundVideoSrc === "string" && props.backgroundVideoSrc.trim() !== "") { hasBackgroundVideo = true; backgroundVideoSrc = props.backgroundVideoSrc.trim(); wrapperStyle.position = "relative"; wrapperStyle.overflow = "hidden"; } if (props.backgroundImageSrc && props.backgroundImageSrc.trim() !== "") { const url = props.backgroundImageSrc.trim(); wrapperStyle.backgroundImage = `url(${url})`; const size = props.backgroundImageSize ?? "cover"; wrapperStyle.backgroundSize = size; const posMode = props.backgroundImagePositionMode ?? "preset"; if ( posMode === "custom" && typeof props.backgroundImagePositionXPercent === "number" && typeof props.backgroundImagePositionYPercent === "number" ) { const x = props.backgroundImagePositionXPercent; const y = props.backgroundImagePositionYPercent; wrapperStyle.backgroundPosition = `${x}% ${y}%`; } else { const posToken = props.backgroundImagePosition ?? "center"; const position = posToken === "top" ? "center top" : posToken === "bottom" ? "center bottom" : posToken === "left" ? "left center" : posToken === "right" ? "right center" : "center center"; wrapperStyle.backgroundPosition = position; } const repeat = props.backgroundImageRepeat ?? "no-repeat"; wrapperStyle.backgroundRepeat = repeat; } const innerWrapperStyle: CSSProperties = {}; if (typeof props.maxWidthPx === "number" && props.maxWidthPx > 0) { innerWrapperStyle.maxWidth = `${props.maxWidthPx}px`; } const columnsContainerStyle: CSSProperties = {}; if (typeof props.gapXPx === "number" && props.gapXPx > 0) { columnsContainerStyle.columnGap = `${props.gapXPx}px`; } return { bgClass, pyClass, alignItemsClass, wrapperStyle, innerWrapperStyle, columnsContainerStyle, hasBackgroundVideo, backgroundVideoSrc, }; }