Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feature/screen-management
This commit is contained in:
commit
d1d76bbea8
|
|
@ -216,14 +216,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||||
// 컴포넌트 기본 스타일 - 레이아웃은 항상 맨 아래
|
// 컴포넌트 기본 스타일 - 레이아웃은 항상 맨 아래
|
||||||
// 🔥 모든 컴포넌트를 픽셀 기준으로 통일 (스케일로만 조정)
|
// 🔥 모든 컴포넌트를 픽셀 기준으로 통일 (스케일로만 조정)
|
||||||
const getWidth = () => {
|
const getWidth = () => {
|
||||||
// table-list는 화면 너비 전체 사용
|
// 모든 컴포넌트는 size.width 픽셀 사용 (table-list 포함)
|
||||||
if (component.componentConfig?.type === "table-list") {
|
|
||||||
// 디자인 해상도 기준으로 픽셀 반환
|
|
||||||
const screenWidth = 1920; // 기본 디자인 해상도
|
|
||||||
return `${screenWidth}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모든 컴포넌트는 size.width 픽셀 사용
|
|
||||||
const width = `${size?.width || 100}px`;
|
const width = `${size?.width || 100}px`;
|
||||||
return width;
|
return width;
|
||||||
};
|
};
|
||||||
|
|
@ -269,19 +262,30 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||||
}
|
}
|
||||||
: component;
|
: component;
|
||||||
|
|
||||||
// componentStyle에서 width, height 제거 (size.width, size.height만 사용)
|
|
||||||
const { width: _styleWidth, height: _styleHeight, ...restComponentStyle } = componentStyle || {};
|
|
||||||
|
|
||||||
const baseStyle = {
|
const baseStyle = {
|
||||||
left: `${position.x}px`,
|
left: `${position.x}px`,
|
||||||
top: `${position.y}px`,
|
top: `${position.y}px`,
|
||||||
...restComponentStyle, // width/height 제외한 스타일 먼저 적용
|
...componentStyle, // componentStyle 전체 적용 (DynamicComponentRenderer에서 이미 size가 변환됨)
|
||||||
width: getWidth(), // size.width로 덮어쓰기
|
width: getWidth(), // getWidth() 우선 (table-list 등 특수 케이스)
|
||||||
height: getHeight(), // size.height로 덮어쓰기
|
height: getHeight(), // getHeight() 우선 (flow-widget 등 특수 케이스)
|
||||||
zIndex: component.type === "layout" ? 1 : position.z || 2,
|
zIndex: component.type === "layout" ? 1 : position.z || 2,
|
||||||
right: undefined,
|
right: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 디버깅: 크기 정보 로그
|
||||||
|
if (component.id && isSelected) {
|
||||||
|
console.log("📐 RealtimePreview baseStyle:", {
|
||||||
|
componentId: component.id,
|
||||||
|
componentType: (component as any).componentType || component.type,
|
||||||
|
sizeWidth: size?.width,
|
||||||
|
sizeHeight: size?.height,
|
||||||
|
styleWidth: componentStyle?.width,
|
||||||
|
styleHeight: componentStyle?.height,
|
||||||
|
baseStyleWidth: baseStyle.width,
|
||||||
|
baseStyleHeight: baseStyle.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 🔍 DOM 렌더링 후 실제 크기 측정
|
// 🔍 DOM 렌더링 후 실제 크기 측정
|
||||||
const innerDivRef = React.useRef<HTMLDivElement>(null);
|
const innerDivRef = React.useRef<HTMLDivElement>(null);
|
||||||
const outerDivRef = React.useRef<HTMLDivElement>(null);
|
const outerDivRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
|
||||||
|
|
@ -528,6 +528,34 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
const finalKey = pathParts[pathParts.length - 1];
|
const finalKey = pathParts[pathParts.length - 1];
|
||||||
current[finalKey] = value;
|
current[finalKey] = value;
|
||||||
|
|
||||||
|
// 🆕 size 변경 시 style도 함께 업데이트 (파란 테두리와 실제 크기 동기화)
|
||||||
|
if (path === "size.width" || path === "size.height" || path === "size") {
|
||||||
|
if (!newComp.style) {
|
||||||
|
newComp.style = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path === "size.width") {
|
||||||
|
newComp.style.width = `${value}px`;
|
||||||
|
} else if (path === "size.height") {
|
||||||
|
newComp.style.height = `${value}px`;
|
||||||
|
} else if (path === "size") {
|
||||||
|
// size 객체 전체가 변경된 경우
|
||||||
|
if (value.width !== undefined) {
|
||||||
|
newComp.style.width = `${value.width}px`;
|
||||||
|
}
|
||||||
|
if (value.height !== undefined) {
|
||||||
|
newComp.style.height = `${value.height}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🔄 size 변경 → style 동기화:", {
|
||||||
|
componentId: newComp.id,
|
||||||
|
path,
|
||||||
|
value,
|
||||||
|
updatedStyle: newComp.style,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// gridColumns 변경 시 크기 자동 업데이트 제거 (격자 시스템 제거됨)
|
// gridColumns 변경 시 크기 자동 업데이트 제거 (격자 시스템 제거됨)
|
||||||
// if (path === "gridColumns" && prevLayout.gridSettings) {
|
// if (path === "gridColumns" && prevLayout.gridSettings) {
|
||||||
// const updatedSize = updateSizeFromGridColumns(newComp, prevLayout.gridSettings as GridUtilSettings);
|
// const updatedSize = updateSizeFromGridColumns(newComp, prevLayout.gridSettings as GridUtilSettings);
|
||||||
|
|
@ -2220,7 +2248,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
||||||
labelColor: "#212121",
|
labelColor: "#212121",
|
||||||
labelFontWeight: "500",
|
labelFontWeight: "500",
|
||||||
labelMarginBottom: "4px",
|
labelMarginBottom: "4px",
|
||||||
width: `${widthPercent}%`, // gridColumns에 맞춘 퍼센트 너비
|
width: `${componentSize.width}px`, // size와 동기화 (픽셀 단위)
|
||||||
|
height: `${componentSize.height}px`, // size와 동기화 (픽셀 단위)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,14 @@ export interface ComponentRenderer {
|
||||||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||||
selectedRows?: any[];
|
selectedRows?: any[];
|
||||||
selectedRowsData?: any[];
|
selectedRowsData?: any[];
|
||||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[], tableDisplayData?: any[]) => void;
|
onSelectedRowsChange?: (
|
||||||
|
selectedRows: any[],
|
||||||
|
selectedRowsData: any[],
|
||||||
|
sortBy?: string,
|
||||||
|
sortOrder?: "asc" | "desc",
|
||||||
|
columnOrder?: string[],
|
||||||
|
tableDisplayData?: any[],
|
||||||
|
) => void;
|
||||||
// 테이블 정렬 정보 (엑셀 다운로드용)
|
// 테이블 정렬 정보 (엑셀 다운로드용)
|
||||||
sortBy?: string;
|
sortBy?: string;
|
||||||
sortOrder?: "asc" | "desc";
|
sortOrder?: "asc" | "desc";
|
||||||
|
|
@ -110,7 +117,14 @@ export interface DynamicComponentRendererProps {
|
||||||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||||
selectedRows?: any[];
|
selectedRows?: any[];
|
||||||
selectedRowsData?: any[];
|
selectedRowsData?: any[];
|
||||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[], tableDisplayData?: any[]) => void;
|
onSelectedRowsChange?: (
|
||||||
|
selectedRows: any[],
|
||||||
|
selectedRowsData: any[],
|
||||||
|
sortBy?: string,
|
||||||
|
sortOrder?: "asc" | "desc",
|
||||||
|
columnOrder?: string[],
|
||||||
|
tableDisplayData?: any[],
|
||||||
|
) => void;
|
||||||
// 테이블 정렬 정보 (엑셀 다운로드용)
|
// 테이블 정렬 정보 (엑셀 다운로드용)
|
||||||
sortBy?: string;
|
sortBy?: string;
|
||||||
sortOrder?: "asc" | "desc";
|
sortOrder?: "asc" | "desc";
|
||||||
|
|
@ -290,22 +304,15 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||||
};
|
};
|
||||||
|
|
||||||
// 렌더러 props 구성
|
// 렌더러 props 구성
|
||||||
// component.style에서 height 제거 (RealtimePreviewDynamic에서 size.height로 처리)
|
|
||||||
// 단, layout 타입 컴포넌트(split-panel-layout 등)는 height 유지
|
|
||||||
const isLayoutComponent =
|
|
||||||
component.type === "layout" ||
|
|
||||||
componentType === "split-panel-layout" ||
|
|
||||||
componentType?.includes("layout");
|
|
||||||
|
|
||||||
const { height: _height, ...styleWithoutHeight } = component.style || {};
|
|
||||||
|
|
||||||
// 숨김 값 추출
|
// 숨김 값 추출
|
||||||
const hiddenValue = component.hidden || component.componentConfig?.hidden;
|
const hiddenValue = component.hidden || component.componentConfig?.hidden;
|
||||||
|
|
||||||
// 🆕 조건부 컨테이너용 높이 변화 핸들러
|
// 🆕 조건부 컨테이너용 높이 변화 핸들러
|
||||||
const handleHeightChange = props.onHeightChange ? (newHeight: number) => {
|
const handleHeightChange = props.onHeightChange
|
||||||
props.onHeightChange!(component.id, newHeight);
|
? (newHeight: number) => {
|
||||||
} : undefined;
|
props.onHeightChange!(component.id, newHeight);
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const rendererProps = {
|
const rendererProps = {
|
||||||
component,
|
component,
|
||||||
|
|
@ -315,7 +322,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||||
onDragEnd,
|
onDragEnd,
|
||||||
size: component.size || newComponent.defaultSize,
|
size: component.size || newComponent.defaultSize,
|
||||||
position: component.position,
|
position: component.position,
|
||||||
style: isLayoutComponent ? component.style : styleWithoutHeight, // 레이아웃은 height 유지
|
style: finalStyle, // size를 포함한 최종 style
|
||||||
config: component.componentConfig,
|
config: component.componentConfig,
|
||||||
componentConfig: component.componentConfig,
|
componentConfig: component.componentConfig,
|
||||||
value: currentValue, // formData에서 추출한 현재 값 전달
|
value: currentValue, // formData에서 추출한 현재 값 전달
|
||||||
|
|
@ -407,10 +414,10 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||||
|
|
||||||
// 폴백 렌더링 - 기본 플레이스홀더
|
// 폴백 렌더링 - 기본 플레이스홀더
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center rounded border-2 border-dashed border-border bg-muted p-4">
|
<div className="border-border bg-muted flex h-full w-full items-center justify-center rounded border-2 border-dashed p-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="mb-2 text-sm font-medium text-muted-foreground">{component.label || component.id}</div>
|
<div className="text-muted-foreground mb-2 text-sm font-medium">{component.label || component.id}</div>
|
||||||
<div className="text-xs text-muted-foreground/70">미구현 컴포넌트: {componentType}</div>
|
<div className="text-muted-foreground/70 text-xs">미구현 컴포넌트: {componentType}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -75,13 +75,15 @@ export const CustomerItemMappingComponent: React.FC<CustomerItemMappingComponent
|
||||||
// 스타일 계산
|
// 스타일 계산
|
||||||
const componentStyle: React.CSSProperties = {
|
const componentStyle: React.CSSProperties = {
|
||||||
position: "relative",
|
position: "relative",
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
backgroundColor: "hsl(var(--background))",
|
backgroundColor: "hsl(var(--background))",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: isDesignMode ? "300px" : "100%",
|
||||||
|
...style, // style prop이 위의 기본값들을 덮어씀
|
||||||
};
|
};
|
||||||
|
|
||||||
// 이벤트 핸들러
|
// 이벤트 핸들러
|
||||||
|
|
|
||||||
|
|
@ -61,16 +61,15 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
||||||
className="relative flex h-full flex-col overflow-hidden bg-background shadow-sm"
|
className="relative flex h-full flex-col overflow-hidden bg-background shadow-sm"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
maxWidth: "100%",
|
height: "100%",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="relative flex-1 overflow-x-auto overflow-y-auto">
|
<div className="relative flex-1 overflow-auto">
|
||||||
<Table
|
<Table
|
||||||
className="w-full"
|
className="w-full"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
minWidth: "100%",
|
|
||||||
tableLayout: "auto", // 테이블 크기 자동 조정
|
tableLayout: "auto", // 테이블 크기 자동 조정
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -230,16 +230,16 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentStyle: React.CSSProperties = {
|
const componentStyle: React.CSSProperties = {
|
||||||
width: calculatedWidth,
|
position: "relative",
|
||||||
height: isDesignMode ? "auto" : "100%",
|
|
||||||
minHeight: isDesignMode ? "300px" : "100%",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
backgroundColor: "hsl(var(--background))",
|
backgroundColor: "hsl(var(--background))",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
...style,
|
boxSizing: "border-box",
|
||||||
// width는 항상 100%로 고정 (부모 컨테이너가 gridColumns로 크기 제어)
|
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: isDesignMode ? "300px" : "100%",
|
||||||
|
...style, // style prop이 위의 기본값들을 덮어씀
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
@ -1939,7 +1939,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
onDragStart: isDesignMode ? onDragStart : undefined,
|
onDragStart: isDesignMode ? onDragStart : undefined,
|
||||||
onDragEnd: isDesignMode ? onDragEnd : undefined,
|
onDragEnd: isDesignMode ? onDragEnd : undefined,
|
||||||
draggable: isDesignMode,
|
draggable: isDesignMode,
|
||||||
className: cn(className, isDesignMode && "cursor-move"),
|
className: cn("w-full h-full", className, isDesignMode && "cursor-move"), // customer-item-mapping과 동일
|
||||||
style: componentStyle,
|
style: componentStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2056,17 +2056,27 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
|
|
||||||
{/* 테이블 컨테이너 */}
|
{/* 테이블 컨테이너 */}
|
||||||
<div
|
<div
|
||||||
className="flex w-full max-w-full flex-1 flex-col overflow-hidden"
|
className="flex flex-1 flex-col"
|
||||||
style={{ marginTop: `${tableConfig.filter?.bottomSpacing ?? 8}px` }}
|
style={{
|
||||||
|
marginTop: `${tableConfig.filter?.bottomSpacing ?? 8}px`,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 스크롤 영역 */}
|
{/* 스크롤 영역 */}
|
||||||
<div
|
<div
|
||||||
className="bg-background flex-1 w-full max-w-full overflow-x-auto overflow-y-auto"
|
className="bg-background flex-1"
|
||||||
style={{ position: "relative" }}
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 테이블 */}
|
{/* 테이블 */}
|
||||||
<table
|
<table
|
||||||
className={cn("table-mobile-fixed w-full max-w-full", !showGridLines && "hide-grid")}
|
className={cn("table-mobile-fixed", !showGridLines && "hide-grid")}
|
||||||
style={{
|
style={{
|
||||||
borderCollapse: "collapse",
|
borderCollapse: "collapse",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export const TableListDefinition = createComponentDefinition({
|
||||||
// 데이터 로딩
|
// 데이터 로딩
|
||||||
autoLoad: true,
|
autoLoad: true,
|
||||||
},
|
},
|
||||||
defaultSize: { width: 120, height: 600 }, // 테이블 리스트 기본 높이
|
defaultSize: { width: 1000, height: 600 }, // 테이블 리스트 기본 크기 (너비 1000px, 높이 600px)
|
||||||
configPanel: TableListConfigPanel,
|
configPanel: TableListConfigPanel,
|
||||||
icon: "Table",
|
icon: "Table",
|
||||||
tags: ["테이블", "데이터", "목록", "그리드"],
|
tags: ["테이블", "데이터", "목록", "그리드"],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue