140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { BaseLayoutRenderer, LayoutRendererProps } from "./BaseLayoutRenderer";
|
|
|
|
export default class FlexboxLayoutRenderer extends BaseLayoutRenderer {
|
|
render(): React.ReactElement {
|
|
const { layout, isDesignMode, isSelected, onClick, className } = this.props;
|
|
|
|
if (!layout.layoutConfig.flexbox) {
|
|
return <div className="error-layout">플렉스박스 설정이 없습니다.</div>;
|
|
}
|
|
|
|
const flexConfig = layout.layoutConfig.flexbox;
|
|
const containerStyle = this.getLayoutContainerStyle();
|
|
|
|
// 플렉스박스 스타일 설정
|
|
const flexStyle: React.CSSProperties = {
|
|
...containerStyle,
|
|
display: "flex",
|
|
flexDirection: flexConfig.direction,
|
|
justifyContent: flexConfig.justify,
|
|
alignItems: flexConfig.align,
|
|
flexWrap: flexConfig.wrap,
|
|
gap: `${flexConfig.gap}px`,
|
|
};
|
|
|
|
// 디자인 모드 스타일
|
|
if (isDesignMode) {
|
|
flexStyle.border = isSelected ? "2px solid #3b82f6" : "1px solid #e2e8f0";
|
|
flexStyle.borderRadius = "8px";
|
|
flexStyle.padding = "8px";
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={`flexbox-layout ${isDesignMode ? "design-mode" : ""} ${className || ""}`}
|
|
style={flexStyle}
|
|
onClick={onClick}
|
|
draggable={isDesignMode}
|
|
onDragStart={this.props.onDragStart}
|
|
onDragEnd={this.props.onDragEnd}
|
|
>
|
|
{layout.zones.map((zone, index) => {
|
|
const zoneChildren = this.getZoneChildren(zone.id);
|
|
|
|
// 플렉스 아이템 스타일 설정
|
|
const zoneStyle: React.CSSProperties = {
|
|
flex: this.calculateFlexValue(zone, flexConfig.direction),
|
|
};
|
|
|
|
return this.renderZone(zone, zoneChildren, {
|
|
style: zoneStyle,
|
|
className: "flex-zone",
|
|
});
|
|
})}
|
|
|
|
{/* 존이 없을 때 안내 메시지 */}
|
|
{layout.zones.length === 0 && (
|
|
<div
|
|
className="empty-flex-container"
|
|
style={{
|
|
flex: 1,
|
|
border: isDesignMode ? "2px dashed #cbd5e1" : "1px solid #e2e8f0",
|
|
borderRadius: "8px",
|
|
backgroundColor: isDesignMode ? "rgba(148, 163, 184, 0.05)" : "rgba(248, 250, 252, 0.5)",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
fontSize: isDesignMode ? "14px" : "12px",
|
|
color: "#64748b",
|
|
minHeight: "100px",
|
|
padding: "20px",
|
|
textAlign: "center",
|
|
transition: "all 0.2s ease",
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.borderColor = "#3b82f6";
|
|
e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.05)";
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.borderColor = isDesignMode ? "#cbd5e1" : "#e2e8f0";
|
|
e.currentTarget.style.backgroundColor = isDesignMode
|
|
? "rgba(148, 163, 184, 0.05)"
|
|
: "rgba(248, 250, 252, 0.5)";
|
|
}}
|
|
>
|
|
{isDesignMode ? "플렉스박스 레이아웃에 존을 추가하세요" : "빈 레이아웃"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 플렉스 아이템의 flex 값을 계산합니다.
|
|
*/
|
|
private calculateFlexValue(zone: any, direction: string): string {
|
|
// 존의 크기에 따라 flex 값 결정
|
|
if (direction === "row" || direction === "row-reverse") {
|
|
// 가로 방향: width를 기준으로 flex 값 계산
|
|
if (typeof zone.size.width === "string") {
|
|
if (zone.size.width.includes("fr")) {
|
|
return zone.size.width.replace("fr", "");
|
|
} else if (zone.size.width.includes("%")) {
|
|
const percent = parseInt(zone.size.width.replace("%", ""));
|
|
return `0 0 ${percent}%`;
|
|
} else if (zone.size.width.includes("px")) {
|
|
return `0 0 ${zone.size.width}`;
|
|
}
|
|
} else if (typeof zone.size.width === "number") {
|
|
return `0 0 ${zone.size.width}px`;
|
|
}
|
|
} else {
|
|
// 세로 방향: height를 기준으로 flex 값 계산
|
|
if (typeof zone.size.height === "string") {
|
|
if (zone.size.height.includes("fr")) {
|
|
return zone.size.height.replace("fr", "");
|
|
} else if (zone.size.height.includes("%")) {
|
|
const percent = parseInt(zone.size.height.replace("%", ""));
|
|
return `0 0 ${percent}%`;
|
|
} else if (zone.size.height.includes("px")) {
|
|
return `0 0 ${zone.size.height}`;
|
|
}
|
|
} else if (typeof zone.size.height === "number") {
|
|
return `0 0 ${zone.size.height}px`;
|
|
}
|
|
}
|
|
|
|
// 기본값: 균등 분할
|
|
return "1";
|
|
}
|
|
}
|
|
|
|
// React 컴포넌트로 래핑
|
|
export const FlexboxLayout: React.FC<LayoutRendererProps> = (props) => {
|
|
const renderer = new FlexboxLayoutRenderer(props);
|
|
return renderer.render();
|
|
};
|