feat: enhance component configuration and rendering
- Updated the RealtimePreviewDynamic component to display selected component information more clearly. - Added dynamic field type labels in the RealtimePreviewDynamic component for better user understanding. - Introduced a table refresh counter in the ScreenDesigner component to handle table column updates effectively. - Improved the V2PropertiesPanel and V2SelectConfigPanel to support additional properties and enhance usability. - Refactored the DynamicComponentRenderer to better handle field types and improve component configuration merging. Made-with: Cursor
This commit is contained in:
@@ -302,9 +302,16 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
return type;
|
||||
};
|
||||
|
||||
const componentType = mapToV2ComponentType(rawComponentType);
|
||||
const mappedComponentType = mapToV2ComponentType(rawComponentType);
|
||||
|
||||
// 컴포넌트 타입 변환 완료
|
||||
// fieldType 기반 동적 컴포넌트 전환 (통합 필드 설정 패널에서 설정된 값)
|
||||
const componentType = (() => {
|
||||
const ft = (component as any).componentConfig?.fieldType;
|
||||
if (!ft) return mappedComponentType;
|
||||
if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(ft)) return "v2-input";
|
||||
if (["select", "category", "entity"].includes(ft)) return "v2-select";
|
||||
return mappedComponentType;
|
||||
})();
|
||||
|
||||
// 🆕 조건부 렌더링 체크 (conditionalConfig)
|
||||
// componentConfig 또는 overrides에서 conditionalConfig를 가져와서 formData와 비교
|
||||
@@ -738,7 +745,21 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
// 컬럼 메타데이터 기반 componentConfig 병합 (DB 최신 설정 우선)
|
||||
const isEntityJoinColumn = fieldName?.includes(".");
|
||||
const baseColumnName = isEntityJoinColumn ? undefined : fieldName;
|
||||
const mergedComponentConfig = mergeColumnMeta(screenTableName, baseColumnName, component.componentConfig || {});
|
||||
const rawMergedConfig = mergeColumnMeta(screenTableName, baseColumnName, component.componentConfig || {});
|
||||
|
||||
// fieldType이 설정된 경우, source/inputType 보조 속성 자동 보완
|
||||
const mergedComponentConfig = (() => {
|
||||
const ft = rawMergedConfig?.fieldType;
|
||||
if (!ft) return rawMergedConfig;
|
||||
const patch: Record<string, any> = {};
|
||||
if (["select", "category", "entity"].includes(ft) && !rawMergedConfig.source) {
|
||||
patch.source = ft === "category" ? "category" : ft === "entity" ? "entity" : "static";
|
||||
}
|
||||
if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(ft) && !rawMergedConfig.inputType) {
|
||||
patch.inputType = ft;
|
||||
}
|
||||
return Object.keys(patch).length > 0 ? { ...rawMergedConfig, ...patch } : rawMergedConfig;
|
||||
})();
|
||||
|
||||
// NOT NULL 기반 필수 여부를 component.required에 반영
|
||||
const notNullRequired = isColumnRequiredByMeta(screenTableName, baseColumnName);
|
||||
@@ -755,17 +776,16 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
onClick,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
size: needsExternalHorizLabel
|
||||
? { ...(component.size || newComponent.defaultSize), width: undefined, height: undefined }
|
||||
: component.size || newComponent.defaultSize,
|
||||
position: component.position,
|
||||
config: mergedComponentConfig,
|
||||
componentConfig: mergedComponentConfig,
|
||||
// componentConfig의 모든 속성을 props로 spread (tableName, displayField 등)
|
||||
// componentConfig spread를 먼저 → 이후 명시적 속성이 override
|
||||
...(mergedComponentConfig || {}),
|
||||
// 🔧 style은 맨 마지막에! (componentConfig.style이 있어도 mergedStyle이 우선)
|
||||
// size/position/style/label은 componentConfig spread 이후에 설정 (덮어쓰기 방지)
|
||||
size: needsExternalHorizLabel
|
||||
? { ...(component.size || newComponent.defaultSize), width: undefined }
|
||||
: component.size || newComponent.defaultSize,
|
||||
position: component.position,
|
||||
style: mergedStyle,
|
||||
// 수평 라벨 → 외부에서 처리하므로 label 전달 안 함
|
||||
label: needsExternalHorizLabel ? undefined : effectiveLabel,
|
||||
// NOT NULL 메타데이터 포함된 필수 여부 (V2Hierarchy 등 직접 props.required 참조하는 컴포넌트용)
|
||||
required: effectiveRequired,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { ComponentCategory } from "@/types/component";
|
||||
import { createComponentDefinition } from "../../utils/createComponentDefinition";
|
||||
import { V2InputConfigPanel } from "@/components/v2/config-panels/V2InputConfigPanel";
|
||||
import { V2FieldConfigPanel } from "@/components/v2/config-panels/V2FieldConfigPanel";
|
||||
import { V2Input } from "@/components/v2/V2Input";
|
||||
|
||||
export const V2InputDefinition = createComponentDefinition({
|
||||
@@ -72,7 +72,7 @@ export const V2InputDefinition = createComponentDefinition({
|
||||
tags: ["input", "text", "number", "v2"],
|
||||
|
||||
// 설정 패널
|
||||
configPanel: V2InputConfigPanel,
|
||||
configPanel: V2FieldConfigPanel,
|
||||
});
|
||||
|
||||
export default V2InputDefinition;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { ComponentCategory } from "@/types/component";
|
||||
import { createComponentDefinition } from "../../utils/createComponentDefinition";
|
||||
import { V2SelectConfigPanel } from "@/components/v2/config-panels/V2SelectConfigPanel";
|
||||
import { V2FieldConfigPanel } from "@/components/v2/config-panels/V2FieldConfigPanel";
|
||||
import { V2Select } from "@/components/v2/V2Select";
|
||||
|
||||
export const V2SelectDefinition = createComponentDefinition({
|
||||
@@ -82,7 +82,7 @@ export const V2SelectDefinition = createComponentDefinition({
|
||||
tags: ["select", "dropdown", "combobox", "v2"],
|
||||
|
||||
// 설정 패널
|
||||
configPanel: V2SelectConfigPanel,
|
||||
configPanel: V2FieldConfigPanel,
|
||||
});
|
||||
|
||||
export default V2SelectDefinition;
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { ConfigPanelContext } from "@/lib/registry/components/common/Config
|
||||
// 컴포넌트별 ConfigPanel 동적 import 맵
|
||||
const CONFIG_PANEL_MAP: Record<string, () => Promise<any>> = {
|
||||
// ========== V2 컴포넌트 ==========
|
||||
"v2-input": () => import("@/components/v2/config-panels/V2InputConfigPanel"),
|
||||
"v2-select": () => import("@/components/v2/config-panels/V2SelectConfigPanel"),
|
||||
"v2-input": () => import("@/components/v2/config-panels/V2FieldConfigPanel"),
|
||||
"v2-select": () => import("@/components/v2/config-panels/V2FieldConfigPanel"),
|
||||
"v2-date": () => import("@/components/v2/config-panels/V2DateConfigPanel"),
|
||||
"v2-list": () => import("@/components/v2/config-panels/V2ListConfigPanel"),
|
||||
"v2-media": () => import("@/components/v2/config-panels/V2MediaConfigPanel"),
|
||||
@@ -205,6 +205,7 @@ export interface ComponentConfigPanelProps {
|
||||
menuObjid?: number;
|
||||
allComponents?: any[];
|
||||
currentComponent?: any;
|
||||
componentType?: string;
|
||||
}
|
||||
|
||||
export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> = ({
|
||||
@@ -217,6 +218,7 @@ export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> =
|
||||
menuObjid,
|
||||
allComponents,
|
||||
currentComponent,
|
||||
componentType,
|
||||
}) => {
|
||||
const [ConfigPanelComponent, setConfigPanelComponent] = React.useState<React.ComponentType<any> | null>(null);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
@@ -484,6 +486,8 @@ export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> =
|
||||
onConfigChange={onChange}
|
||||
context={context}
|
||||
screenTableName={screenTableName}
|
||||
tableName={screenTableName}
|
||||
columnName={currentComponent?.columnName || config?.columnName || config?.fieldName}
|
||||
tableColumns={selectedTableColumns}
|
||||
tables={tables}
|
||||
allTables={allTablesList.length > 0 ? allTablesList : tables}
|
||||
@@ -493,6 +497,7 @@ export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> =
|
||||
currentComponent={currentComponent}
|
||||
screenComponents={screenComponents}
|
||||
inputType={currentComponent?.inputType || config?.inputType}
|
||||
componentType={componentType || componentId}
|
||||
sourceTableColumns={sourceTableColumns}
|
||||
targetTableColumns={targetTableColumns}
|
||||
onSourceTableChange={handleSourceTableChange}
|
||||
|
||||
Reference in New Issue
Block a user