ERP-node/frontend/lib/meta-components/bindings/ReactiveBindingEngine.ts

204 lines
5.3 KiB
TypeScript
Raw Normal View History

2026-03-01 03:39:00 +09:00
/**
* Reactive Binding Engine
* -
* -
* - Phase A: 기본 ( Phase B)
*/
"use client";
import { ReactiveBinding } from "@/lib/api/metaComponent";
export class ReactiveBindingEngine {
private bindings: ReactiveBinding[] = [];
private componentRegistry: Map<string, any> = new Map();
private eventHandlers: Map<string, Set<(event: any) => void>> = new Map();
constructor(bindings: ReactiveBinding[] = []) {
this.bindings = bindings.sort((a, b) => (a.priority || 100) - (b.priority || 100));
}
/**
*
* -
*/
registerComponent(componentId: string, component: any) {
this.componentRegistry.set(componentId, component);
console.log(`[ReactiveBinding] 컴포넌트 등록: ${componentId}`);
}
/**
*
*/
unregisterComponent(componentId: string) {
this.componentRegistry.delete(componentId);
this.eventHandlers.delete(componentId);
console.log(`[ReactiveBinding] 컴포넌트 등록 해제: ${componentId}`);
}
/**
*
* -
*/
emit(sourceComponentId: string, event: string, data?: any) {
console.log(`[ReactiveBinding] 이벤트 발행: ${sourceComponentId}.${event}`, data);
// 해당 이벤트에 연결된 바인딩 찾기
const matchedBindings = this.bindings.filter(
(binding) =>
binding.sourceComponentId === sourceComponentId &&
binding.sourceEvent === event
);
if (matchedBindings.length === 0) {
console.log(`[ReactiveBinding] 연결된 바인딩 없음`);
return;
}
// 바인딩 실행 (priority 순서대로)
matchedBindings.forEach((binding) => {
this.executeBinding(binding, data);
});
}
/**
*
* -
*/
private executeBinding(binding: ReactiveBinding, data: any) {
console.log(
`[ReactiveBinding] 바인딩 실행: ${binding.sourceComponentId}.${binding.sourceEvent}${binding.targetComponentId}.${binding.targetAction}`
);
// 조건 검사
if (binding.conditionConfig && !this.evaluateCondition(binding.conditionConfig, data)) {
console.log(`[ReactiveBinding] 조건 불만족, 실행 스킵`);
return;
}
// 데이터 변환
let transformedData = data;
if (binding.transformConfig) {
transformedData = this.transformData(binding.transformConfig, data);
}
// 타겟 컴포넌트 찾기
const targetComponent = this.componentRegistry.get(binding.targetComponentId);
if (!targetComponent) {
console.warn(
`[ReactiveBinding] 타겟 컴포넌트를 찾을 수 없음: ${binding.targetComponentId}`
);
return;
}
// 액션 실행
this.executeAction(binding.targetAction, targetComponent, transformedData, binding.targetField);
}
/**
*
*/
private evaluateCondition(conditionConfig: any, data: any): boolean {
// TODO: Phase B에서 구현
// 예: { type: "field_value", fieldId: "status", operator: "eq", value: "active" }
return true;
}
/**
*
*/
private transformData(transformConfig: any, data: any): any {
// TODO: Phase B에서 구현
// 예: { type: "calculate", expression: "quantity * price" }
return data;
}
/**
*
*/
private executeAction(
action: string,
targetComponent: any,
data: any,
targetField?: string | null
) {
console.log(`[ReactiveBinding] 액션 실행: ${action}`, { targetField, data });
switch (action) {
case "filter":
// DataView 필터링
if (targetComponent.setFilter) {
targetComponent.setFilter(data);
}
break;
case "setValue":
// Field 값 설정
if (targetComponent.setValue) {
targetComponent.setValue(data);
}
break;
case "show":
// 컴포넌트 표시
if (targetComponent.show) {
targetComponent.show();
}
break;
case "hide":
// 컴포넌트 숨김
if (targetComponent.hide) {
targetComponent.hide();
}
break;
case "enable":
// 컴포넌트 활성화
if (targetComponent.enable) {
targetComponent.enable();
}
break;
case "disable":
// 컴포넌트 비활성화
if (targetComponent.disable) {
targetComponent.disable();
}
break;
case "refresh":
// 데이터 새로고침
if (targetComponent.refresh) {
targetComponent.refresh();
}
break;
default:
console.warn(`[ReactiveBinding] 지원하지 않는 액션: ${action}`);
}
}
/**
*
*/
addBinding(binding: ReactiveBinding) {
this.bindings.push(binding);
this.bindings.sort((a, b) => (a.priority || 100) - (b.priority || 100));
}
/**
*
*/
removeBinding(bindingId: number) {
this.bindings = this.bindings.filter((b) => b.id !== bindingId);
}
/**
*
*/
getBindings(): ReactiveBinding[] {
return [...this.bindings];
}
}