ERP-node/frontend/lib/registry/components/v2-input/V2InputRenderer.tsx

135 lines
4.6 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useEffect, useRef } from "react";
import { AutoRegisteringComponentRenderer } from "../../AutoRegisteringComponentRenderer";
import { V2InputDefinition } from "./index";
import { V2Input } from "@/components/v2/V2Input";
import { isColumnRequiredByMeta } from "../../DynamicComponentRenderer";
/**
* dataBinding이 v2-input을 wrapper
* v2-table-list의 window CustomEvent로
* formData에
*/
function DataBindingWrapper({
dataBinding,
columnName,
onFormDataChange,
children,
}: {
dataBinding: { sourceComponentId: string; sourceColumn: string };
columnName: string;
onFormDataChange?: (field: string, value: any) => void;
children: React.ReactNode;
}) {
const lastBoundValueRef = useRef<any>(null);
const onFormDataChangeRef = useRef(onFormDataChange);
onFormDataChangeRef.current = onFormDataChange;
useEffect(() => {
if (!dataBinding?.sourceComponentId || !dataBinding?.sourceColumn) return;
const handler = (e: Event) => {
const detail = (e as CustomEvent).detail;
if (!detail || detail.source !== dataBinding.sourceComponentId) return;
const selectedRow = detail.data?.[0];
const value = selectedRow?.[dataBinding.sourceColumn] ?? "";
if (value !== lastBoundValueRef.current) {
lastBoundValueRef.current = value;
onFormDataChangeRef.current?.(columnName, value);
}
};
window.addEventListener("v2-table-selection", handler);
return () => window.removeEventListener("v2-table-selection", handler);
}, [dataBinding?.sourceComponentId, dataBinding?.sourceColumn, columnName]);
return <>{children}</>;
}
/**
* 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);
}
};
const style = component.style || {};
const labelDisplay = style.labelDisplay ?? (component as any).labelDisplay;
const effectiveLabel = labelDisplay === true ? style.labelText || component.label : undefined;
const dataBinding = config.dataBinding || (component as any).dataBinding || config.componentConfig?.dataBinding;
const inputElement = (
<V2Input
id={component.id}
value={currentValue}
onChange={handleChange}
onFormDataChange={onFormDataChange}
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}
label={effectiveLabel}
required={component.required || isColumnRequiredByMeta(tableName, columnName)}
readonly={config.readonly || component.readonly || !!dataBinding?.sourceComponentId}
disabled={config.disabled || component.disabled}
/>
);
// dataBinding이 있으면 wrapper로 감싸서 이벤트 구독
if (dataBinding?.sourceComponentId && dataBinding?.sourceColumn) {
return (
<DataBindingWrapper
dataBinding={dataBinding}
columnName={columnName}
onFormDataChange={onFormDataChange}
>
{inputElement}
</DataBindingWrapper>
);
}
return inputElement;
}
}
// 자동 등록 실행
V2InputRenderer.registerSelf();
// Hot Reload 지원 (개발 모드)
if (process.env.NODE_ENV === "development") {
V2InputRenderer.enableHotReload();
}