Merge branch 'gbpark-node' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node
This commit is contained in:
@@ -362,15 +362,15 @@ const RealtimePreviewDynamicComponent: React.FC<RealtimePreviewProps> = ({
|
||||
// 런타임 모드에서 컴포넌트 타입별 높이 처리
|
||||
if (!isDesignMode) {
|
||||
const compType = (component as any).componentType || component.componentConfig?.type || "";
|
||||
// 테이블: 부모 flex 컨테이너가 높이 관리 (flex: 1)
|
||||
const flexGrowTypes = [
|
||||
// 레이아웃 계열: 부모 래퍼를 꽉 채움 (ResponsiveGridRenderer가 % 높이 관리)
|
||||
const fillParentTypes = [
|
||||
"table-list", "v2-table-list",
|
||||
"split-panel-layout", "split-panel-layout2",
|
||||
"v2-split-panel-layout", "screen-split-panel",
|
||||
"v2-tab-container", "tab-container",
|
||||
"tabs-widget", "v2-tabs-widget",
|
||||
];
|
||||
if (flexGrowTypes.some(t => compType === t)) {
|
||||
if (fillParentTypes.some(t => compType === t)) {
|
||||
return "100%";
|
||||
}
|
||||
const autoHeightTypes = [
|
||||
|
||||
@@ -23,8 +23,9 @@ function getComponentTypeId(component: ComponentData): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* 디자이너 절대좌표를 캔버스 대비 비율로 변환하여 렌더링.
|
||||
* 화면이 줄어들면 비율에 맞게 축소, 늘어나면 확대.
|
||||
* 디자이너 절대좌표를 캔버스 대비 비율(%)로 변환하여 렌더링.
|
||||
* 가로: 컨테이너 너비 대비 % → 반응형 스케일
|
||||
* 세로: 컨테이너 높이 대비 % → 뷰포트에 맞게 자동 조절
|
||||
*/
|
||||
function ProportionalRenderer({
|
||||
components,
|
||||
@@ -47,19 +48,12 @@ function ProportionalRenderer({
|
||||
}, []);
|
||||
|
||||
const topLevel = components.filter((c) => !c.parentId);
|
||||
const ratio = containerW > 0 ? containerW / canvasWidth : 1;
|
||||
|
||||
const maxBottom = topLevel.reduce((max, c) => {
|
||||
const bottom = c.position.y + (c.size?.height || 40);
|
||||
return Math.max(max, bottom);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
data-screen-runtime="true"
|
||||
className="bg-background relative w-full overflow-x-hidden"
|
||||
style={{ minHeight: containerW > 0 ? `${maxBottom * ratio}px` : "200px" }}
|
||||
className="bg-background relative h-full w-full overflow-hidden"
|
||||
>
|
||||
{containerW > 0 &&
|
||||
topLevel.map((component) => {
|
||||
@@ -72,9 +66,9 @@ function ProportionalRenderer({
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: `${(component.position.x / canvasWidth) * 100}%`,
|
||||
top: `${component.position.y * ratio}px`,
|
||||
top: `${(component.position.y / canvasHeight) * 100}%`,
|
||||
width: `${((component.size?.width || 100) / canvasWidth) * 100}%`,
|
||||
height: `${(component.size?.height || 40) * ratio}px`,
|
||||
height: `${((component.size?.height || 40) / canvasHeight) * 100}%`,
|
||||
zIndex: component.position.z || 1,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
GroupComponent,
|
||||
DataTableComponent,
|
||||
TableInfo,
|
||||
LayoutComponent,
|
||||
FileComponent,
|
||||
AreaComponent,
|
||||
} from "@/types/screen";
|
||||
@@ -47,7 +46,7 @@ import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent
|
||||
|
||||
// ComponentRegistry import (동적 ConfigPanel 가져오기용)
|
||||
import { ComponentRegistry } from "@/lib/registry/ComponentRegistry";
|
||||
import { columnMetaCache } from "@/lib/registry/DynamicComponentRenderer";
|
||||
import { columnMetaCache, loadColumnMeta } from "@/lib/registry/DynamicComponentRenderer";
|
||||
import { DynamicComponentConfigPanel, hasComponentConfigPanel } from "@/lib/utils/getComponentConfigPanel";
|
||||
import StyleEditor from "../StyleEditor";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
@@ -98,6 +97,24 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
// 🆕 전체 테이블 목록 (selected-items-detail-input 등에서 사용)
|
||||
const [allTables, setAllTables] = useState<Array<{ tableName: string; displayName?: string }>>([]);
|
||||
|
||||
// 🆕 선택된 컴포넌트의 테이블에 대한 columnMeta 캐시가 비어 있으면 로드 후 재렌더
|
||||
const [columnMetaVersion, setColumnMetaVersion] = useState(0);
|
||||
useEffect(() => {
|
||||
if (!selectedComponent) return;
|
||||
const tblName =
|
||||
(selectedComponent as any).tableName ||
|
||||
currentTable?.tableName ||
|
||||
tables?.[0]?.tableName;
|
||||
if (!tblName) return;
|
||||
if (columnMetaCache[tblName]) return;
|
||||
loadColumnMeta(tblName).then(() => setColumnMetaVersion((v) => v + 1));
|
||||
}, [
|
||||
selectedComponent?.id,
|
||||
(selectedComponent as any)?.tableName,
|
||||
currentTable?.tableName,
|
||||
tables?.[0]?.tableName,
|
||||
]);
|
||||
|
||||
// 🆕 전체 테이블 목록 로드
|
||||
useEffect(() => {
|
||||
const loadAllTables = async () => {
|
||||
@@ -211,20 +228,20 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
// 현재 화면의 테이블명 가져오기
|
||||
const currentTableName = tables?.[0]?.tableName;
|
||||
|
||||
// DB input_type 가져오기 (columnMetaCache에서 최신값 조회)
|
||||
const colName = selectedComponent.columnName || currentConfig.fieldKey || currentConfig.columnName;
|
||||
const tblName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
|
||||
// DB input_type만 조회 (saved config와 분리하여 전달)
|
||||
const colName = (selectedComponent as any).columnName || currentConfig.fieldKey || currentConfig.columnName;
|
||||
const tblName = (selectedComponent as any).tableName || currentTable?.tableName || currentTableName;
|
||||
const dbMeta = colName && tblName && !colName.includes(".") ? columnMetaCache[tblName]?.[colName] : undefined;
|
||||
const dbInputType = dbMeta ? (() => { const raw = dbMeta.input_type || dbMeta.inputType; return raw === "direct" || raw === "auto" ? undefined : raw; })() : undefined;
|
||||
const inputType = dbInputType || currentConfig.inputType || currentConfig.webType || (selectedComponent as any).inputType;
|
||||
|
||||
// 컴포넌트별 추가 props
|
||||
const extraProps: Record<string, any> = {};
|
||||
const resolvedTableName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
|
||||
const resolvedColumnName = selectedComponent.columnName || currentConfig.fieldKey || currentConfig.columnName;
|
||||
const resolvedTableName = (selectedComponent as any).tableName || currentTable?.tableName || currentTableName;
|
||||
const resolvedColumnName = (selectedComponent as any).columnName || currentConfig.fieldKey || currentConfig.columnName;
|
||||
|
||||
if (componentId === "v2-input" || componentId === "v2-select") {
|
||||
extraProps.inputType = inputType;
|
||||
extraProps.componentType = componentId;
|
||||
extraProps.inputType = dbInputType;
|
||||
extraProps.tableName = resolvedTableName;
|
||||
extraProps.columnName = resolvedColumnName;
|
||||
extraProps.screenTableName = resolvedTableName;
|
||||
@@ -256,7 +273,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
const currentConfig = selectedComponent.componentConfig || {};
|
||||
|
||||
// 🔧 ConfigPanelWrapper를 인라인 함수 대신 직접 JSX 반환 (리마운트 방지)
|
||||
const config = currentConfig || definition.defaultProps?.componentConfig || {};
|
||||
const config = currentConfig || (definition as any).defaultProps?.componentConfig || {};
|
||||
|
||||
const handlePanelConfigChange = (newConfig: any) => {
|
||||
// 🔧 Partial 업데이트: 기존 componentConfig를 유지하면서 새 설정만 병합
|
||||
@@ -282,14 +299,14 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
onConfigChange={handlePanelConfigChange}
|
||||
tables={tables}
|
||||
allTables={allTables}
|
||||
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
|
||||
tableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
|
||||
screenTableName={(selectedComponent as any).tableName || currentTable?.tableName || currentTableName}
|
||||
tableName={(selectedComponent as any).tableName || currentTable?.tableName || currentTableName}
|
||||
columnName={
|
||||
(selectedComponent as any).columnName || currentConfig?.columnName || currentConfig?.fieldName
|
||||
}
|
||||
inputType={(selectedComponent as any).inputType || currentConfig?.inputType}
|
||||
componentType={componentType}
|
||||
tableColumns={currentTable?.columns || []}
|
||||
tableColumns={(currentTable as any)?.columns || []}
|
||||
allComponents={allComponents}
|
||||
currentComponent={selectedComponent}
|
||||
menuObjid={menuObjid}
|
||||
@@ -323,8 +340,8 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
componentType={componentType}
|
||||
config={selectedComponent.componentConfig || {}}
|
||||
onChange={handleDynamicConfigChange}
|
||||
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={currentTable?.columns || []}
|
||||
screenTableName={(selectedComponent as any).tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={(currentTable as any)?.columns || []}
|
||||
tables={tables}
|
||||
menuObjid={menuObjid}
|
||||
allComponents={allComponents}
|
||||
@@ -491,7 +508,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<span className="text-muted-foreground text-xs">제목</span>
|
||||
<div className="w-[160px]">
|
||||
<Input
|
||||
value={group.title || area.title || ""}
|
||||
value={(group as any).title || (area as any).title || ""}
|
||||
onChange={(e) => handleUpdate("title", e.target.value)}
|
||||
placeholder="제목"
|
||||
className="h-7 text-xs"
|
||||
@@ -503,7 +520,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<span className="text-muted-foreground text-xs">설명</span>
|
||||
<div className="w-[160px]">
|
||||
<Input
|
||||
value={area.description || ""}
|
||||
value={(area as any).description || ""}
|
||||
onChange={(e) => handleUpdate("description", e.target.value)}
|
||||
placeholder="설명"
|
||||
className="h-7 text-xs"
|
||||
@@ -519,9 +536,9 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<h4 className="text-muted-foreground py-2 text-[10px] font-semibold tracking-wider uppercase">OPTIONS</h4>
|
||||
{(isInputField || widget.required !== undefined) &&
|
||||
(() => {
|
||||
const colName = widget.columnName || selectedComponent?.columnName;
|
||||
const colName = widget.columnName || (selectedComponent as any)?.columnName;
|
||||
const colMeta = colName
|
||||
? currentTable?.columns?.find(
|
||||
? (currentTable as any)?.columns?.find(
|
||||
(c: any) => (c.columnName || c.column_name || "").toLowerCase() === colName.toLowerCase(),
|
||||
)
|
||||
: null;
|
||||
@@ -568,7 +585,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-muted-foreground text-xs">숨김</span>
|
||||
<Checkbox
|
||||
checked={selectedComponent.hidden === true || selectedComponent.componentConfig?.hidden === true}
|
||||
checked={(selectedComponent as any).hidden === true || selectedComponent.componentConfig?.hidden === true}
|
||||
onCheckedChange={(checked) => {
|
||||
handleUpdate("hidden", checked);
|
||||
handleUpdate("componentConfig.hidden", checked);
|
||||
@@ -689,7 +706,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<div className="flex items-center justify-between py-1.5">
|
||||
<span className="text-muted-foreground text-xs">표시</span>
|
||||
<Checkbox
|
||||
checked={selectedComponent.style?.labelDisplay === true || selectedComponent.labelDisplay === true}
|
||||
checked={selectedComponent.style?.labelDisplay === true || (selectedComponent as any).labelDisplay === true}
|
||||
onCheckedChange={(checked) => {
|
||||
const boolValue = checked === true;
|
||||
handleUpdate("style.labelDisplay", boolValue);
|
||||
@@ -785,7 +802,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
const webType = selectedComponent.componentConfig?.webType;
|
||||
|
||||
// 테이블 패널에서 드래그한 컴포넌트인지 확인
|
||||
const isFromTablePanel = !!(selectedComponent.tableName && selectedComponent.columnName);
|
||||
const isFromTablePanel = !!((selectedComponent as any).tableName && (selectedComponent as any).columnName);
|
||||
|
||||
if (!componentId) {
|
||||
return (
|
||||
@@ -845,8 +862,8 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<DynamicComponentConfigPanel
|
||||
componentId={componentId}
|
||||
config={selectedComponent.componentConfig || {}}
|
||||
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={currentTable?.columns || []}
|
||||
screenTableName={(selectedComponent as any).tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={(currentTable as any)?.columns || []}
|
||||
tables={tables}
|
||||
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
|
||||
allComponents={allComponents} // 🆕 연쇄 드롭다운 부모 감지용
|
||||
@@ -1006,8 +1023,8 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
<DynamicComponentConfigPanel
|
||||
componentId={widget.widgetType}
|
||||
config={widget.componentConfig || {}}
|
||||
screenTableName={widget.tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={currentTable?.columns || []}
|
||||
screenTableName={(widget as any).tableName || currentTable?.tableName || currentTableName}
|
||||
tableColumns={(currentTable as any)?.columns || []}
|
||||
tables={tables}
|
||||
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
|
||||
allComponents={allComponents} // 🆕 연쇄 드롭다운 부모 감지용
|
||||
@@ -1023,17 +1040,17 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* WebType 선택 (있는 경우만) */}
|
||||
{widget.webType && (
|
||||
{(widget as any).webType && (
|
||||
<div>
|
||||
<Label>입력 타입</Label>
|
||||
<Select value={widget.webType} onValueChange={(value) => handleUpdate("webType", value)}>
|
||||
<Select value={(widget as any).webType} onValueChange={(value) => handleUpdate("webType", value)}>
|
||||
<SelectTrigger className="h-6 w-full px-2 py-0 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{webTypes.map((wt) => (
|
||||
<SelectItem key={wt.web_type} value={wt.web_type}>
|
||||
{wt.web_type_name_kor || wt.web_type}
|
||||
{(wt as any).web_type_name_kor || wt.web_type}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
Reference in New Issue
Block a user