Files
flatrender/src/lib/studio-layer-props.ts
T

121 lines
3.3 KiB
TypeScript
Raw Normal View History

import type { LayerProps } from "@/lib/studio-types";
export type ShapeKind = "rect" | "circle" | "line" | "arrow";
export type TextAlign = "left" | "center" | "right";
export type TextAnimation =
| "none"
| "fadeIn"
| "slideUp"
| "zoomIn"
| "typewriter";
export const FONT_FAMILY_OPTIONS = [
{ label: "Inter", value: "Inter, sans-serif" },
{ label: "Roboto", value: "Roboto, sans-serif" },
{ label: "Playfair", value: "Playfair Display, serif" },
{ label: "Montserrat", value: "Montserrat, sans-serif" },
{ label: "Oswald", value: "Oswald, sans-serif" },
] as const;
export const TEXT_ANIMATION_OPTIONS: { label: string; value: TextAnimation }[] =
[
{ label: "None", value: "none" },
{ label: "Fade In", value: "fadeIn" },
{ label: "Slide Up", value: "slideUp" },
{ label: "Zoom In", value: "zoomIn" },
{ label: "Typewriter", value: "typewriter" },
];
function asNumber(value: unknown, fallback: number): number {
return typeof value === "number" && !Number.isNaN(value) ? value : fallback;
}
function asString(value: unknown, fallback: string): string {
return typeof value === "string" ? value : fallback;
}
function asBoolean(value: unknown): boolean {
return value === true;
}
export function buildKonvaFontStyle(bold: boolean, italic: boolean): string {
if (bold && italic) return "bold italic";
if (bold) return "bold";
if (italic) return "italic";
return "normal";
}
export function getTextProps(props: LayerProps) {
const bold = asBoolean(props.bold);
const italic = asBoolean(props.italic);
const alignRaw = props.align;
const align: TextAlign =
alignRaw === "center" || alignRaw === "right" ? alignRaw : "left";
const animationRaw = props.animation;
const animation: TextAnimation =
animationRaw === "fadeIn" ||
animationRaw === "slideUp" ||
animationRaw === "zoomIn" ||
animationRaw === "typewriter"
? animationRaw
: "none";
return {
text: asString(props.text, "Text"),
fontSize: asNumber(props.fontSize, 48),
fill: asString(props.fill, "#111827"),
fontFamily: asString(props.fontFamily, "Inter, sans-serif"),
bold,
italic,
underline: asBoolean(props.underline),
align,
letterSpacing: asNumber(props.letterSpacing, 0),
lineHeight: asNumber(props.lineHeight, 1.2),
animation,
fontStyle: buildKonvaFontStyle(bold, italic),
};
}
export function getImageProps(props: LayerProps) {
return {
src:
typeof props.src === "string" && props.src.length > 0
? props.src
: undefined,
flipHorizontal: asBoolean(props.flipHorizontal),
flipVertical: asBoolean(props.flipVertical),
cornerRadius: asNumber(props.cornerRadius, 0),
};
}
export function getImageSrc(props: LayerProps): string | undefined {
return getImageProps(props).src;
}
export function getShapeProps(props: LayerProps) {
const shapeRaw = props.shape;
const shape: ShapeKind =
shapeRaw === "circle" ||
shapeRaw === "line" ||
shapeRaw === "arrow"
? shapeRaw
: "rect";
return {
shape,
fill: asString(props.fill, "#2563EB"),
stroke: asString(props.stroke, "#1E3A8A"),
strokeWidth: asNumber(props.strokeWidth, 0),
cornerRadius: asNumber(props.cornerRadius, 0),
};
}
export function mergeLayerProps(
current: LayerProps,
updates: LayerProps
): LayerProps {
return { ...current, ...updates };
}