1차 싱크 완료
CI / test (push) Failing after 41m13s
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-11-24 21:32:37 +09:00
parent 7a8ad7c057
commit 672cca5271
83 changed files with 6681 additions and 325 deletions
+186 -18
View File
@@ -72,6 +72,9 @@ export interface TextBlockProps {
// 커스텀 색상 값 (예: "#ff0000", "rgb(…)" 등)
colorCustom?: string;
// 블록 배경색 커스텀 값 (예: "#123456")
backgroundColorCustom?: string;
// 최대 너비 모드: 프리셋 또는 커스텀 값
maxWidthMode?: "scale" | "custom";
// 최대 너비 스케일
@@ -127,6 +130,8 @@ export interface ImageBlockProps {
borderRadius?: "none" | "sm" | "md" | "lg" | "full";
// 모서리 둥글기 px 값 (이미지에 한해서는 연속적인 px 단위 조정을 지원한다)
borderRadiusPx?: number;
// 이미지 카드를 감싸는 배경색 커스텀 값 (예: "#123456")
backgroundColorCustom?: string;
// 이미지 소스 타입: 업로드된 에셋 또는 외부 URL
sourceType?: "asset" | "externalUrl";
// 업로드된 에셋인 경우 Asset.id 를 저장해둔다.
@@ -450,6 +455,8 @@ export interface ListBlockProps {
fontSizeCustom?: string;
lineHeightCustom?: string;
textColorCustom?: string;
// 리스트 전체를 감싸는 배경색 커스텀 값
backgroundColorCustom?: string;
// 불릿/간격
bulletStyle?:
| "disc"
@@ -619,6 +626,8 @@ export interface FormBlockProps {
formWidthMode?: "auto" | "full" | "fixed";
formWidthPx?: number;
marginYPx?: number;
// 폼 전체를 감싸는 배경색 커스텀 값
backgroundColorCustom?: string;
}
export type CanvasPreset = "mobile" | "tablet" | "desktop" | "full" | "custom";
@@ -804,14 +813,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Hero 템플릿 섹션 추가: 섹션 1개와 기본 텍스트/버튼 블록들을 첫 컬럼에 배치한다
addHeroTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createHeroTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -827,14 +854,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Features 템플릿 섹션 추가: 3컬럼(4/4/4) 섹션과 각 컬럼의 제목/설명 텍스트를 생성한다
addFeaturesTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createFeaturesTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -845,14 +890,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Blog 템플릿 섹션 추가: 3컬럼 포스트 카드(제목/요약)를 생성한다
addBlogTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createBlogTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -863,14 +926,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Team 템플릿 섹션 추가: 3컬럼 팀 카드(이름/역할/소개)를 생성한다
addTeamTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createTeamTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -881,14 +962,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Footer 템플릿 섹션 추가: 링크/카피라이트 텍스트가 포함된 1컬럼 섹션을 생성한다
addFooterTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createFooterTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -899,14 +998,32 @@ const createEditorState = (set: any, get: any): EditorState => ({
// CTA 템플릿 섹션 추가: 텍스트와 버튼이 포함된 1컬럼 섹션을 생성한다
addCtaTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createCtaTemplateBlocks({
sectionId,
createId,
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -917,7 +1034,17 @@ const createEditorState = (set: any, get: any): EditorState => ({
// FAQ 템플릿 섹션 추가: 질문/답변 쌍이 수직으로 나열된 섹션을 생성한다
addFaqTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createFaqTemplateBlocks({
sectionId,
@@ -925,7 +1052,14 @@ const createEditorState = (set: any, get: any): EditorState => ({
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -936,7 +1070,17 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Pricing 템플릿 섹션 추가: 3컬럼 요금제 카드(플랜 이름/가격/설명)를 생성한다
addPricingTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createPricingTemplateBlocks({
sectionId,
@@ -944,7 +1088,14 @@ const createEditorState = (set: any, get: any): EditorState => ({
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,
@@ -955,7 +1106,17 @@ const createEditorState = (set: any, get: any): EditorState => ({
// Testimonials 템플릿 섹션 추가: 3컬럼 후기 카드(본문/작성자)를 생성한다
addTestimonialsTemplateSection: () => {
const sectionId = createId();
const { selectedBlockId, blocks } = get();
let sectionId = createId();
let replacingSectionId: string | null = null;
if (selectedBlockId) {
const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
if (target) {
sectionId = target.id;
replacingSectionId = target.id;
}
}
const { blocks: templateBlocks, lastSelectedId } = createTestimonialsTemplateBlocks({
sectionId,
@@ -963,7 +1124,14 @@ const createEditorState = (set: any, get: any): EditorState => ({
});
set((state: EditorState) => {
const newBlocks = [...state.blocks, ...templateBlocks];
let baseBlocks = state.blocks as Block[];
if (replacingSectionId) {
baseBlocks = baseBlocks.filter(
(b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
);
}
const newBlocks = [...baseBlocks, ...templateBlocks];
return {
blocks: newBlocks,