feat : remove legacy feature
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { SerializedBlock } from "@/lib/blocks";
|
||||
|
||||
import {
|
||||
createEditorBlock,
|
||||
duplicateBlock,
|
||||
moveBlock,
|
||||
moveBlockTo,
|
||||
} from "./draft";
|
||||
|
||||
const blocks: SerializedBlock[] = ["a", "b", "c"].map((id, sortOrder) => ({
|
||||
id,
|
||||
type: "separator",
|
||||
schemaVersion: 1,
|
||||
config: {},
|
||||
sortOrder,
|
||||
}));
|
||||
|
||||
describe("editor block operations", () => {
|
||||
it("moves blocks with keyboard-friendly relative operations", () => {
|
||||
expect(moveBlock(blocks, "b", -1).map((block) => block.id)).toEqual(["b", "a", "c"]);
|
||||
expect(moveBlock(blocks, "a", -1)).toEqual(blocks);
|
||||
expect(moveBlock(blocks, "b", 1).map((block) => block.sortOrder)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it("supports pointer drop targets and duplication with stable order", () => {
|
||||
expect(moveBlockTo(blocks, "a", "c").map((block) => block.id)).toEqual(["b", "c", "a"]);
|
||||
expect(duplicateBlock(blocks, "b", "copy").map((block) => block.id)).toEqual(["a", "b", "copy", "c"]);
|
||||
});
|
||||
|
||||
it("requires a configured source for data-backed blocks", () => {
|
||||
expect(createEditorBlock("comparison-chart")).toBeNull();
|
||||
expect(createEditorBlock("heading", undefined, "heading-id")).toMatchObject({
|
||||
id: "heading-id",
|
||||
type: "heading",
|
||||
config: { text: "หัวข้อใหม่" },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,179 +0,0 @@
|
||||
import type { SerializedBlock } from "@/lib/blocks";
|
||||
import type { DataSourceSnapshot, PageSnapshot } from "@/lib/content/types";
|
||||
|
||||
export interface EditorDraft {
|
||||
pageId: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
visible: boolean;
|
||||
publicNote: string | null;
|
||||
blocks: SerializedBlock[];
|
||||
}
|
||||
|
||||
export function draftFromSnapshot(snapshot: PageSnapshot): EditorDraft {
|
||||
return {
|
||||
pageId: snapshot.page.id,
|
||||
title: snapshot.page.title,
|
||||
slug: snapshot.page.slug,
|
||||
visible: snapshot.page.visible,
|
||||
publicNote: snapshot.page.publicNote,
|
||||
blocks: structuredClone(snapshot.blocks),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeBlockOrder(
|
||||
blocks: readonly SerializedBlock[],
|
||||
): SerializedBlock[] {
|
||||
return blocks.map((block, sortOrder) => ({ ...block, sortOrder }));
|
||||
}
|
||||
|
||||
export function moveBlock(
|
||||
blocks: readonly SerializedBlock[],
|
||||
blockId: string,
|
||||
direction: -1 | 1,
|
||||
): SerializedBlock[] {
|
||||
const current = blocks.findIndex((block) => block.id === blockId);
|
||||
const target = current + direction;
|
||||
if (current < 0 || target < 0 || target >= blocks.length) return [...blocks];
|
||||
const next = [...blocks];
|
||||
[next[current], next[target]] = [next[target], next[current]];
|
||||
return normalizeBlockOrder(next);
|
||||
}
|
||||
|
||||
export function moveBlockTo(
|
||||
blocks: readonly SerializedBlock[],
|
||||
blockId: string,
|
||||
targetId: string,
|
||||
): SerializedBlock[] {
|
||||
const from = blocks.findIndex((block) => block.id === blockId);
|
||||
const target = blocks.findIndex((block) => block.id === targetId);
|
||||
if (from < 0 || target < 0 || from === target) return [...blocks];
|
||||
const next = [...blocks];
|
||||
const [moved] = next.splice(from, 1);
|
||||
next.splice(target, 0, moved);
|
||||
return normalizeBlockOrder(next);
|
||||
}
|
||||
|
||||
export function duplicateBlock(
|
||||
blocks: readonly SerializedBlock[],
|
||||
blockId: string,
|
||||
id = crypto.randomUUID(),
|
||||
): SerializedBlock[] {
|
||||
const index = blocks.findIndex((block) => block.id === blockId);
|
||||
if (index < 0) return [...blocks];
|
||||
const clone = structuredClone(blocks[index]);
|
||||
clone.id = id;
|
||||
const next = [...blocks];
|
||||
next.splice(index + 1, 0, clone);
|
||||
return normalizeBlockOrder(next);
|
||||
}
|
||||
|
||||
function sourceFields(source: DataSourceSnapshot | undefined) {
|
||||
const text = source?.fields.find((field) => field.type !== "number")?.key ?? "name";
|
||||
const numbers = source?.fields.filter((field) => field.type === "number" || field.formula) ?? [];
|
||||
return { text, numbers };
|
||||
}
|
||||
|
||||
export function createEditorBlock(
|
||||
type: string,
|
||||
source?: DataSourceSnapshot,
|
||||
id = crypto.randomUUID(),
|
||||
): SerializedBlock | null {
|
||||
const nestedId = () => crypto.randomUUID();
|
||||
const { text, numbers } = sourceFields(source);
|
||||
const numeric = numbers[0]?.key ?? "value";
|
||||
const configs: Record<string, Record<string, unknown> | null> = {
|
||||
profile: { title: "หัวข้อโปรไฟล์", summary: "เพิ่มคำแนะนำสั้น ๆ", badges: [] },
|
||||
"rich-text": { content: [{ type: "paragraph", text: "เริ่มเขียนเนื้อหาที่นี่" }] },
|
||||
heading: { text: "หัวข้อใหม่", level: 2 },
|
||||
callout: { title: "ข้อควรรู้", body: "เพิ่มรายละเอียด", tone: "info" },
|
||||
"stat-cards": {
|
||||
title: "ค่าสำคัญ",
|
||||
items: [{ id: nestedId(), label: "ค่า", value: "-" }],
|
||||
},
|
||||
"key-value": {
|
||||
items: [{ id: nestedId(), label: "หัวข้อ", value: "รายละเอียด" }],
|
||||
columns: 2,
|
||||
},
|
||||
ranking: source
|
||||
? {
|
||||
title: "อันดับ",
|
||||
dataSourceId: source.id,
|
||||
labelField: text,
|
||||
valueField: numeric,
|
||||
higherIsBetter: true,
|
||||
}
|
||||
: null,
|
||||
"team-composition": source
|
||||
? {
|
||||
title: "ทีมแนะนำ",
|
||||
dataSourceId: source.id,
|
||||
teamField: text,
|
||||
memberFields: [text],
|
||||
}
|
||||
: null,
|
||||
rotation: {
|
||||
title: "ลำดับการเล่น",
|
||||
steps: [{ id: nestedId(), action: "เพิ่มขั้นตอน" }],
|
||||
},
|
||||
table: source
|
||||
? {
|
||||
dataSourceId: source.id,
|
||||
columns: source.fields.slice(0, 4).map((field) => ({
|
||||
field: field.key,
|
||||
label: field.label,
|
||||
align: field.type === "number" ? "end" : "start",
|
||||
})),
|
||||
}
|
||||
: null,
|
||||
"comparison-chart": source && numbers.length > 0
|
||||
? {
|
||||
title: "กราฟเปรียบเทียบ",
|
||||
dataSourceId: source.id,
|
||||
categoryField: text,
|
||||
series: [{ field: numeric, label: numbers[0].label, color: "chart-1" }],
|
||||
presentation: "horizontal-bar",
|
||||
normalization: "raw",
|
||||
sort: "authored",
|
||||
showValues: true,
|
||||
showLegend: false,
|
||||
tableFallback: true,
|
||||
}
|
||||
: null,
|
||||
image: null,
|
||||
gallery: null,
|
||||
tabs: {
|
||||
tabs: [
|
||||
{ id: nestedId(), label: "ตัวเลือก 1", content: "เพิ่มรายละเอียด" },
|
||||
{ id: nestedId(), label: "ตัวเลือก 2", content: "เพิ่มรายละเอียด" },
|
||||
],
|
||||
},
|
||||
accordion: {
|
||||
items: [{ id: nestedId(), title: "หัวข้อ", content: "เพิ่มรายละเอียด" }],
|
||||
},
|
||||
section: { title: "ส่วนใหม่" },
|
||||
columns: { columns: 2, gap: "normal" },
|
||||
links: {
|
||||
links: [{ id: nestedId(), label: "ลิงก์", href: "/", description: "เพิ่มคำอธิบาย" }],
|
||||
},
|
||||
separator: {},
|
||||
};
|
||||
const config = configs[type];
|
||||
if (!config) return null;
|
||||
return {
|
||||
id,
|
||||
type,
|
||||
schemaVersion: 1,
|
||||
config,
|
||||
responsiveLayout: {
|
||||
width: ["ranking", "table", "comparison-chart", "team-composition", "gallery"].includes(type)
|
||||
? "wide"
|
||||
: "content",
|
||||
mobileSpan: 4,
|
||||
tabletSpan: 8,
|
||||
desktopSpan: 12,
|
||||
hideOnMobile: false,
|
||||
},
|
||||
sortOrder: 0,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user