126 lines
3.1 KiB
TypeScript
126 lines
3.1 KiB
TypeScript
"use client"
|
||
|
||
import type { components } from "@shared/api/__generated__/openapi.types"
|
||
import type { JSX } from "react"
|
||
|
||
import { FunctionComponent } from "react"
|
||
|
||
import cs from "classnames"
|
||
|
||
import styles from "./StylePreview.module.scss"
|
||
|
||
type CaptionStyleConfig = components["schemas"]["CaptionStyleConfig"]
|
||
|
||
interface IStylePreviewProps {
|
||
config?: CaptionStyleConfig | null
|
||
size?: "small" | "large"
|
||
className?: string
|
||
aspectRatio?: number
|
||
}
|
||
|
||
const SMALL_SCALE = 0.45
|
||
|
||
const buildContainerStyles = (
|
||
config: CaptionStyleConfig,
|
||
scale: number,
|
||
): React.CSSProperties => {
|
||
const bg = config.background
|
||
return {
|
||
backgroundColor: bg?.bg_color ?? "rgba(0,0,0,0.6)",
|
||
borderRadius: (bg?.bg_border_radius_px ?? 8) * scale,
|
||
padding: (bg?.bg_padding_px ?? 12) * scale,
|
||
...(bg?.bg_blur_px
|
||
? { backdropFilter: `blur(${bg.bg_blur_px * scale}px)` }
|
||
: {}),
|
||
...(bg?.bg_glow_color
|
||
? { boxShadow: `0 0 ${20 * scale}px ${bg.bg_glow_color}` }
|
||
: {}),
|
||
}
|
||
}
|
||
|
||
const buildTextStyles = (
|
||
config: CaptionStyleConfig,
|
||
scale: number,
|
||
): React.CSSProperties => {
|
||
const text = config.text
|
||
return {
|
||
fontFamily: text?.font_family ?? "Lobster",
|
||
fontSize: (text?.font_size ?? 40) * scale,
|
||
fontWeight: text?.font_weight ?? 400,
|
||
color: text?.text_color ?? "#FFFFFF",
|
||
textAlign:
|
||
(config.layout?.horizontal_alignment as "left" | "center" | "right") ??
|
||
"center",
|
||
...(text?.text_shadow ? { textShadow: text.text_shadow } : {}),
|
||
...(text?.text_stroke_width
|
||
? {
|
||
WebkitTextStroke: `${(text.text_stroke_width ?? 0) * scale}px ${text.text_stroke_color ?? "#000000"}`,
|
||
}
|
||
: {}),
|
||
}
|
||
}
|
||
|
||
const VERTICAL_MAP: Record<string, string> = {
|
||
top: "flex-start",
|
||
center: "center",
|
||
bottom: "flex-end",
|
||
}
|
||
|
||
const HORIZONTAL_MAP: Record<string, string> = {
|
||
left: "flex-start",
|
||
center: "center",
|
||
right: "flex-end",
|
||
}
|
||
|
||
const buildPositionStyles = (
|
||
config: CaptionStyleConfig,
|
||
scale: number,
|
||
): React.CSSProperties => {
|
||
const layout = config.layout
|
||
const vPos = layout?.vertical_position ?? "bottom"
|
||
const hAlign = layout?.horizontal_alignment ?? "center"
|
||
const padding = (layout?.padding_px ?? 20) * scale
|
||
|
||
return {
|
||
justifyContent: VERTICAL_MAP[vPos] ?? "flex-end",
|
||
alignItems: HORIZONTAL_MAP[hAlign] ?? "center",
|
||
padding,
|
||
}
|
||
}
|
||
|
||
export const StylePreview: FunctionComponent<IStylePreviewProps> = ({
|
||
config,
|
||
size = "small",
|
||
className,
|
||
aspectRatio = 9 / 16,
|
||
}): JSX.Element => {
|
||
const safeConfig = config ?? {}
|
||
const highlightColor = safeConfig.text?.highlight_color ?? "#FFFF00"
|
||
const scale = size === "small" ? SMALL_SCALE : 1
|
||
|
||
return (
|
||
<div
|
||
className={cs(styles.root, styles[size], className)}
|
||
style={{ ...buildPositionStyles(safeConfig, scale), aspectRatio }}
|
||
data-testid="StylePreview"
|
||
>
|
||
<div
|
||
style={{
|
||
...buildContainerStyles(safeConfig, scale),
|
||
maxWidth: `${safeConfig.layout?.max_width_pct ?? 90}%`,
|
||
boxSizing: "border-box",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
...buildTextStyles(safeConfig, scale),
|
||
wordBreak: "break-word",
|
||
}}
|
||
>
|
||
Пример <span style={{ color: highlightColor }}>субтитров</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|