feat: add named canvas presets with YouTube Shorts entry

Introduces NAMED_CANVAS_PRESETS with id/label/aspectRatio/projectType metadata so the UI can show meaningful preset names (e.g. "YouTube Shorts (1080×1920)") and so downstream code can resolve a preset by intent rather than dimensions.

- NAMED_CANVAS_PRESETS: landscape 16:9, YouTube Shorts 9:16, square, landscape 4:3
- SHORTS_CANVAS_PRESET convenience export for 9:16 vertical
- DEFAULT_CANVAS_PRESETS preserved (derived from NAMED) so existing editor-store consumers keep working
- Unit tests verify shape, ordering, and back-compat derivation
This commit is contained in:
kseungyong 2026-05-18 17:48:53 +09:00
parent 7131f88bf4
commit 4254dec19f
2 changed files with 118 additions and 7 deletions

View File

@ -0,0 +1,58 @@
import { describe, expect, test } from "bun:test";
import {
DEFAULT_CANVAS_PRESETS,
DEFAULT_CANVAS_SIZE,
NAMED_CANVAS_PRESETS,
SHORTS_CANVAS_PRESET,
} from "@/canvas/sizes";
describe("NAMED_CANVAS_PRESETS", () => {
test("has the four canonical presets in declared order", () => {
const ids = NAMED_CANVAS_PRESETS.map((p) => p.id);
expect(ids).toEqual([
"landscape-1080p",
"shorts-1080x1920",
"square-1080",
"landscape-1440",
]);
});
test("every preset has consistent shape", () => {
for (const preset of NAMED_CANVAS_PRESETS) {
expect(preset.id.length).toBeGreaterThan(0);
expect(preset.label.length).toBeGreaterThan(0);
expect(preset.size.width).toBeGreaterThan(0);
expect(preset.size.height).toBeGreaterThan(0);
expect(preset.aspectRatio).toMatch(/^\d+:\d+$/);
}
});
});
describe("SHORTS_CANVAS_PRESET", () => {
test("is 1080x1920 vertical 9:16", () => {
expect(SHORTS_CANVAS_PRESET.size).toEqual({ width: 1080, height: 1920 });
expect(SHORTS_CANVAS_PRESET.aspectRatio).toBe("9:16");
expect(SHORTS_CANVAS_PRESET.projectType).toBe("shorts");
});
});
describe("DEFAULT_CANVAS_PRESETS (back-compat)", () => {
test("derives from NAMED_CANVAS_PRESETS in order", () => {
expect(DEFAULT_CANVAS_PRESETS).toEqual(
NAMED_CANVAS_PRESETS.map((p) => p.size),
);
});
test("contains the Shorts preset 1080x1920", () => {
expect(DEFAULT_CANVAS_PRESETS).toContainEqual({
width: 1080,
height: 1920,
});
});
});
describe("DEFAULT_CANVAS_SIZE", () => {
test("remains 1920x1080 (landscape default)", () => {
expect(DEFAULT_CANVAS_SIZE).toEqual({ width: 1920, height: 1080 });
});
});

View File

@ -1,10 +1,63 @@
import type { TCanvasSize } from "@/project/types";
import type { TCanvasSize, TProjectType } from "@/project/types";
export const DEFAULT_CANVAS_PRESETS: TCanvasSize[] = [
{ width: 1920, height: 1080 },
{ width: 1080, height: 1920 },
{ width: 1080, height: 1080 },
{ width: 1440, height: 1080 },
/**
* A canvas preset bundles the raw size with display metadata (label,
* aspect ratio) and the project intent it implies. UIs should prefer
* NAMED_CANVAS_PRESETS when rendering preset pickers; consumers that
* only need the size array continue to use DEFAULT_CANVAS_PRESETS.
*/
export interface TCanvasPreset {
id: string;
label: string;
size: TCanvasSize;
aspectRatio: string;
projectType?: TProjectType;
}
export const NAMED_CANVAS_PRESETS: readonly TCanvasPreset[] = [
{
id: "landscape-1080p",
label: "Landscape 1920×1080",
size: { width: 1920, height: 1080 },
aspectRatio: "16:9",
projectType: "standard",
},
{
id: "shorts-1080x1920",
label: "YouTube Shorts (1080×1920)",
size: { width: 1080, height: 1920 },
aspectRatio: "9:16",
projectType: "shorts",
},
{
id: "square-1080",
label: "Square 1080×1080",
size: { width: 1080, height: 1080 },
aspectRatio: "1:1",
projectType: "standard",
},
{
id: "landscape-1440",
label: "Landscape 1440×1080",
size: { width: 1440, height: 1080 },
aspectRatio: "4:3",
projectType: "standard",
},
];
export const DEFAULT_CANVAS_SIZE: TCanvasSize = { width: 1920, height: 1080 };
/**
* Convenience reference to the YouTube Shorts preset.
*/
export const SHORTS_CANVAS_PRESET: TCanvasPreset = NAMED_CANVAS_PRESETS.find(
(p) => p.id === "shorts-1080x1920",
)!;
/**
* Back-compat: existing consumers (editor-store, etc.) read a bare
* TCanvasSize[]. Derived from NAMED_CANVAS_PRESETS to keep them in sync.
*/
export const DEFAULT_CANVAS_PRESETS: TCanvasSize[] = NAMED_CANVAS_PRESETS.map(
(p) => p.size,
);
export const DEFAULT_CANVAS_SIZE: TCanvasSize = NAMED_CANVAS_PRESETS[0].size;