feat: full studio build -- light theme, canvas thumbnails, i18n (fa/en)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user