import React from "react"
import { cn } from "@/lib/utils"
import { CellConfiguration, MediaTemplateConfig } from "../lib/template-config"
interface TemplateRendererProps {
config: MediaTemplateConfig
renderCell: (index: number, cellConfig: CellConfiguration) => React.ReactNode
className?: string
}
/**
* Компонент для рендеринга шаблонов на основе конфигурации
* Поддерживает различные типы разделения: vertical, horizontal, diagonal, grid, custom
*/
export function TemplateRenderer({ config, renderCell, className }: TemplateRendererProps) {
const { split, screens, cells = [], dividers, layout, gridConfig } = config
// Генерируем конфигурации ячеек, если они не заданы
const cellConfigs: CellConfiguration[] = cells.length > 0 ? cells : Array.from({ length: screens }, (_, _i) => ({}))
// Стили контейнера
const containerStyle: React.CSSProperties = {
backgroundColor: layout?.backgroundColor,
borderRadius: layout?.borderRadius,
padding: layout?.padding,
...layout?.containerStyle,
}
// Рендеринг разделителя
const renderDivider = (orientation: "horizontal" | "vertical", key?: string) => {
if (!dividers?.show) return null
const dividerStyle: React.CSSProperties = {
backgroundColor: dividers.color || "#4b5563",
opacity: dividers.opacity,
...(orientation === "horizontal"
? {
height: dividers.width || "1px",
width: "100%",
}
: {
width: dividers.width || "1px",
height: "100%",
}),
...(dividers.style === "dashed" && {
backgroundImage: `repeating-linear-gradient(
${orientation === "horizontal" ? "to right" : "to bottom"},
${dividers.color || "#4b5563"} 0,
${dividers.color || "#4b5563"} ${dividers.dashArray?.split(",")[0] || "5"}px,
transparent ${dividers.dashArray?.split(",")[0] || "5"}px,
transparent ${dividers.dashArray?.split(",")[1] || "5"}px
)`,
backgroundColor: "transparent",
}),
...(dividers.shadow && {
boxShadow: `0 0 ${dividers.shadowBlur || "4px"} ${dividers.shadowColor || "rgba(0,0,0,0.2)"}`,
}),
}
return
}
// Рендеринг заголовка ячейки
const renderCellTitle = (cellConfig: CellConfiguration, index: number) => {
if (!cellConfig.title?.show) return null
const { text = String(index + 1), position = "center", style = {} } = cellConfig.title
const positionClasses = {
center: "top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2",
"top-left": "top-4 left-4",
"top-right": "top-4 right-4",
"bottom-left": "bottom-4 left-4",
"bottom-right": "bottom-4 right-4",
}
return (
{text}
)
}
// Рендеринг ячейки с настройками
const renderCellWithConfig = (index: number, cellConfig: CellConfiguration, style?: React.CSSProperties) => {
const cellStyle: React.CSSProperties = {
...style,
backgroundColor: cellConfig.background?.color,
backgroundImage: cellConfig.background?.gradient || cellConfig.background?.image,
opacity: cellConfig.background?.opacity,
borderWidth: cellConfig.border?.width,
borderColor: cellConfig.border?.color,
borderStyle: cellConfig.border?.style,
borderRadius: cellConfig.border?.radius,
padding: cellConfig.padding,
margin: cellConfig.margin,
}
return (
{renderCell(index, cellConfig)}
{renderCellTitle(cellConfig, index)}
)
}
// Рендеринг в зависимости от типа разделения
switch (split) {
case "vertical":
return (
{cellConfigs.map((cellConfig, index) => (
{index > 0 && renderDivider("vertical", `divider-${index}`)}
{renderCellWithConfig(index, cellConfig, { flex: 1 })}
))}
)
case "horizontal":
return (
{cellConfigs.map((cellConfig, index) => (
{index > 0 && renderDivider("horizontal", `divider-${index}`)}
{renderCellWithConfig(index, cellConfig, { flex: 1 })}
))}
)
case "grid":
if (!gridConfig) {
console.warn("Grid template requires gridConfig")
return null
}
const gridStyle: React.CSSProperties = {
display: "grid",
gridTemplateColumns: `repeat(${gridConfig.columns}, 1fr)`,
gridTemplateRows: `repeat(${gridConfig.rows}, 1fr)`,
columnGap: gridConfig.columnGap || layout?.gap,
rowGap: gridConfig.rowGap || layout?.gap,
...containerStyle,
}
return (
{cellConfigs.map((cellConfig, index) => renderCellWithConfig(index, cellConfig))}
)
case "diagonal":
if (!config.splitPoints || config.splitPoints.length < 2) {
console.warn("Diagonal template requires at least 2 split points")
return null
}
const [startPoint, endPoint] = config.splitPoints
return (
{/* Первая ячейка */}
{renderCellWithConfig(0, cellConfigs[0] || {})}
{/* Вторая ячейка */}
{screens > 1 && (
{renderCellWithConfig(1, cellConfigs[1] || {})}
)}
{/* Диагональная линия */}
{dividers?.show && (
)}
)
case "custom":
// Для кастомных шаблонов используем cellLayouts если они определены
if (config.cellLayouts && config.cellLayouts.length > 0) {
// Используем абсолютное позиционирование с cellLayouts
const customContainerStyle: React.CSSProperties = {
...containerStyle,
position: "relative",
width: "100%",
height: "100%",
}
return (
{cellConfigs.map((cellConfig, index) => {
const cellLayout = config.cellLayouts![index] || {}
const cellStyle: React.CSSProperties = {
position: cellLayout.position || "absolute",
top: cellLayout.top,
left: cellLayout.left,
right: cellLayout.right,
bottom: cellLayout.bottom,
width: cellLayout.width,
height: cellLayout.height,
flex: cellLayout.flex,
gridColumn: cellLayout.gridColumn,
gridRow: cellLayout.gridRow,
zIndex: cellLayout.zIndex,
}
return renderCellWithConfig(index, cellConfig, cellStyle)
})}
)
}
// Fallback для старой логики (можно будет удалить после полной миграции)
const customStyle: React.CSSProperties = {
...containerStyle,
display: "flex",
flexWrap: "wrap",
gap: layout?.gap,
}
// Определяем размеры ячеек для разных кастомных шаблонов
const getCellStyle = (index: number): React.CSSProperties => {
// Базовый стиль
let cellStyle: React.CSSProperties = { flex: 1 }
// Специальная логика для разных типов кастомных шаблонов
if (config.id?.includes("1-3")) {
// 1:3 layout - первая ячейка меньше
cellStyle = index === 0 ? { flex: "0 0 25%" } : { flex: "0 0 25%" }
} else if (config.id?.includes("3-1")) {
// 3:1 layout - последняя ячейка больше
cellStyle = index === screens - 1 ? { flex: "0 0 75%" } : { flex: "0 0 25%" }
} else if (config.id?.includes("mixed-1")) {
// Mixed layout 1 - первая ячейка больше
cellStyle = index === 0 ? { flex: "0 0 50%", height: "100%" } : { flex: "0 0 50%", height: "50%" }
} else if (config.id?.includes("mixed-2")) {
// Mixed layout 2 - последняя ячейка больше
cellStyle = index === screens - 1 ? { flex: "0 0 50%", height: "100%" } : { flex: "0 0 50%", height: "50%" }
} else if (config.id?.includes("custom-5")) {
// 5-screen layouts с различными конфигурациями
if (config.id?.includes("5-1")) {
// 2 больших сверху, 3 маленьких снизу
cellStyle = index < 2 ? { flex: "0 0 50%", height: "60%" } : { flex: "0 0 33.33%", height: "40%" }
} else if (config.id?.includes("5-2")) {
// 1 большой слева, 4 маленьких справа
cellStyle = index === 0 ? { flex: "0 0 50%", height: "100%" } : { flex: "0 0 25%", height: "50%" }
} else if (config.id?.includes("5-3")) {
// Центральный большой, остальные по углам
cellStyle = index === 2 ? { flex: "0 0 50%", height: "50%" } : { flex: "0 0 25%", height: "50%" }
}
} else if (config.id?.includes("custom-7")) {
// 7-screen layouts
// Можно добавить специфичную логику для каждого варианта
cellStyle = { flex: "0 0 33.33%", height: "33.33%" }
}
return cellStyle
}
return (
{cellConfigs.map((cellConfig, index) => renderCellWithConfig(index, cellConfig, getCellStyle(index)))}
)
default:
console.warn(`Unknown split type: ${split}`)
return null
}
}