2026-01-30 10:51:33 +09:00
|
|
|
"use client";
|
|
|
|
|
|
2026-03-12 01:18:09 +09:00
|
|
|
import React, { useEffect, useRef } from "react";
|
2026-01-30 10:51:33 +09:00
|
|
|
import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer";
|
|
|
|
|
import { V2InputDefinition } from "./index";
|
|
|
|
|
import { V2Input } from "@/components/v2/V2Input";
|
2026-03-10 14:16:02 +09:00
|
|
|
import { isColumnRequiredByMeta } from "../../DynamicComponentRenderer";
|
2026-03-12 01:18:09 +09:00
|
|
|
import { v2EventBus, V2_EVENTS } from "@/lib/v2-core";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* dataBinding이 설정된 v2-input을 위한 wrapper
|
|
|
|
|
* v2-table-list의 TABLE_DATA_CHANGE 이벤트를 구독하여
|
|
|
|
|
* 선택된 행의 특정 컬럼 값을 자동으로 formData에 반영
|
|
|
|
|
*/
|
|
|
|
|
function DataBindingWrapper({
|
|
|
|
|
dataBinding,
|
|
|
|
|
columnName,
|
|
|
|
|
onFormDataChange,
|
|
|
|
|
isInteractive,
|
|
|
|
|
children,
|
|
|
|
|
}: {
|
|
|
|
|
dataBinding: { sourceComponentId: string; sourceColumn: string };
|
|
|
|
|
columnName: string;
|
|
|
|
|
onFormDataChange?: (field: string, value: any) => void;
|
|
|
|
|
isInteractive?: boolean;
|
|
|
|
|
children: React.ReactNode;
|
|
|
|
|
}) {
|
|
|
|
|
const lastBoundValueRef = useRef<any>(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!dataBinding?.sourceComponentId || !dataBinding?.sourceColumn) return;
|
|
|
|
|
|
|
|
|
|
console.log("[DataBinding] 구독 시작:", {
|
|
|
|
|
sourceComponentId: dataBinding.sourceComponentId,
|
|
|
|
|
sourceColumn: dataBinding.sourceColumn,
|
|
|
|
|
targetColumn: columnName,
|
|
|
|
|
isInteractive,
|
|
|
|
|
hasOnFormDataChange: !!onFormDataChange,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const unsubscribe = v2EventBus.subscribe(V2_EVENTS.TABLE_DATA_CHANGE, (payload: any) => {
|
|
|
|
|
console.log("[DataBinding] TABLE_DATA_CHANGE 수신:", {
|
|
|
|
|
payloadSource: payload.source,
|
|
|
|
|
expectedSource: dataBinding.sourceComponentId,
|
|
|
|
|
dataLength: payload.data?.length,
|
|
|
|
|
match: payload.source === dataBinding.sourceComponentId,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (payload.source !== dataBinding.sourceComponentId) return;
|
|
|
|
|
|
|
|
|
|
const selectedData = payload.data;
|
|
|
|
|
if (selectedData && selectedData.length > 0) {
|
|
|
|
|
const value = selectedData[0][dataBinding.sourceColumn];
|
|
|
|
|
console.log("[DataBinding] 바인딩 값:", { column: dataBinding.sourceColumn, value, columnName });
|
|
|
|
|
if (value !== lastBoundValueRef.current) {
|
|
|
|
|
lastBoundValueRef.current = value;
|
|
|
|
|
if (onFormDataChange && columnName) {
|
|
|
|
|
onFormDataChange(columnName, value ?? "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (lastBoundValueRef.current !== null) {
|
|
|
|
|
lastBoundValueRef.current = null;
|
|
|
|
|
if (onFormDataChange && columnName) {
|
|
|
|
|
onFormDataChange(columnName, "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return () => unsubscribe();
|
|
|
|
|
}, [dataBinding?.sourceComponentId, dataBinding?.sourceColumn, columnName, onFormDataChange, isInteractive]);
|
|
|
|
|
|
|
|
|
|
return <>{children}</>;
|
|
|
|
|
}
|
2026-01-30 10:51:33 +09:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* V2Input 렌더러
|
|
|
|
|
* 자동 등록 시스템을 사용하여 컴포넌트를 레지스트리에 등록
|
|
|
|
|
*/
|
|
|
|
|
export class V2InputRenderer extends AutoRegisteringComponentRenderer {
|
|
|
|
|
static componentDefinition = V2InputDefinition;
|
|
|
|
|
|
|
|
|
|
render(): React.ReactElement {
|
|
|
|
|
const { component, formData, onFormDataChange, isDesignMode, isSelected, isInteractive, ...restProps } = this.props;
|
|
|
|
|
|
|
|
|
|
const config = component.componentConfig || component.config || {};
|
|
|
|
|
const columnName = component.columnName;
|
|
|
|
|
const tableName = component.tableName || this.props.tableName;
|
|
|
|
|
|
|
|
|
|
const currentValue = formData?.[columnName] ?? component.value ?? "";
|
|
|
|
|
|
|
|
|
|
const handleChange = (value: any) => {
|
|
|
|
|
if (isInteractive && onFormDataChange && columnName) {
|
|
|
|
|
onFormDataChange(columnName, value);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-03 11:02:13 +09:00
|
|
|
const style = component.style || {};
|
2026-02-04 18:01:20 +09:00
|
|
|
const labelDisplay = style.labelDisplay ?? (component as any).labelDisplay;
|
2026-02-05 17:38:06 +09:00
|
|
|
const effectiveLabel = labelDisplay === true ? style.labelText || component.label : undefined;
|
2026-02-03 11:02:13 +09:00
|
|
|
|
2026-03-12 01:18:09 +09:00
|
|
|
const dataBinding = config.dataBinding || (component as any).dataBinding || config.componentConfig?.dataBinding;
|
|
|
|
|
|
|
|
|
|
if (dataBinding || (config as any).dataBinding || (component as any).dataBinding) {
|
|
|
|
|
console.log("[V2InputRenderer] dataBinding 탐색:", {
|
|
|
|
|
componentId: component.id,
|
|
|
|
|
columnName,
|
|
|
|
|
configKeys: Object.keys(config),
|
|
|
|
|
configDataBinding: config.dataBinding,
|
|
|
|
|
componentDataBinding: (component as any).dataBinding,
|
|
|
|
|
nestedDataBinding: config.componentConfig?.dataBinding,
|
|
|
|
|
finalDataBinding: dataBinding,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const inputElement = (
|
2026-01-30 10:51:33 +09:00
|
|
|
<V2Input
|
|
|
|
|
id={component.id}
|
|
|
|
|
value={currentValue}
|
|
|
|
|
onChange={handleChange}
|
2026-02-03 11:02:13 +09:00
|
|
|
onFormDataChange={onFormDataChange}
|
2026-01-30 10:51:33 +09:00
|
|
|
config={{
|
|
|
|
|
type: config.inputType || config.webType || "text",
|
|
|
|
|
inputType: config.inputType || config.webType || "text",
|
|
|
|
|
placeholder: config.placeholder,
|
|
|
|
|
format: config.format,
|
|
|
|
|
min: config.min,
|
|
|
|
|
max: config.max,
|
|
|
|
|
step: config.step,
|
|
|
|
|
rows: config.rows,
|
|
|
|
|
autoGeneration: config.autoGeneration || component.autoGeneration,
|
|
|
|
|
}}
|
|
|
|
|
style={component.style}
|
|
|
|
|
size={component.size}
|
|
|
|
|
formData={formData}
|
|
|
|
|
columnName={columnName}
|
|
|
|
|
tableName={tableName}
|
|
|
|
|
autoGeneration={config.autoGeneration || component.autoGeneration}
|
|
|
|
|
originalData={(this.props as any).originalData}
|
|
|
|
|
{...restProps}
|
2026-03-10 14:16:02 +09:00
|
|
|
label={effectiveLabel}
|
|
|
|
|
required={component.required || isColumnRequiredByMeta(tableName, columnName)}
|
2026-03-12 01:18:09 +09:00
|
|
|
readonly={config.readonly || component.readonly || !!dataBinding?.sourceComponentId}
|
2026-03-10 14:16:02 +09:00
|
|
|
disabled={config.disabled || component.disabled}
|
2026-01-30 10:51:33 +09:00
|
|
|
/>
|
|
|
|
|
);
|
2026-03-12 01:18:09 +09:00
|
|
|
|
|
|
|
|
// dataBinding이 있으면 wrapper로 감싸서 이벤트 구독
|
|
|
|
|
if (dataBinding?.sourceComponentId && dataBinding?.sourceColumn) {
|
|
|
|
|
return (
|
|
|
|
|
<DataBindingWrapper
|
|
|
|
|
dataBinding={dataBinding}
|
|
|
|
|
columnName={columnName}
|
|
|
|
|
onFormDataChange={onFormDataChange}
|
|
|
|
|
isInteractive={isInteractive}
|
|
|
|
|
>
|
|
|
|
|
{inputElement}
|
|
|
|
|
</DataBindingWrapper>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return inputElement;
|
2026-01-30 10:51:33 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 자동 등록 실행
|
|
|
|
|
V2InputRenderer.registerSelf();
|
|
|
|
|
|
|
|
|
|
// Hot Reload 지원 (개발 모드)
|
|
|
|
|
if (process.env.NODE_ENV === "development") {
|
|
|
|
|
V2InputRenderer.enableHotReload();
|
|
|
|
|
}
|