ERP-node/frontend/hooks/pop/useConnectionResolver.ts

127 lines
4.0 KiB
TypeScript
Raw Normal View History

/**
* useConnectionResolver -
*
* PopViewerWithModals에서 .
* layout.dataFlow.connections를 , __comp_output__
* __comp_input__ /.
*
* :
* 소스: __comp_output__${sourceComponentId}__${outputKey}
* 타겟: __comp_input__${targetComponentId}__${inputKey}
*
* _auto :
* sourceOutput="_auto" / connectionMeta를
* key가 category="event" .
*/
import { useEffect, useRef } from "react";
import { usePopEvent } from "./usePopEvent";
import type { PopDataConnection } from "@/components/pop/designer/types/pop-layout";
import {
PopComponentRegistry,
type ConnectionMetaItem,
} from "@/lib/registry/PopComponentRegistry";
interface UseConnectionResolverOptions {
screenId: string;
connections: PopDataConnection[];
componentTypes?: Map<string, string>;
}
/**
* / connectionMeta에서 .
* 규칙: category="event" key가
*/
function getAutoMatchPairs(
sourceType: string,
targetType: string
): { sourceKey: string; targetKey: string }[] {
const sourceDef = PopComponentRegistry.getComponent(sourceType);
const targetDef = PopComponentRegistry.getComponent(targetType);
if (!sourceDef?.connectionMeta?.sendable || !targetDef?.connectionMeta?.receivable) {
return [];
}
const pairs: { sourceKey: string; targetKey: string }[] = [];
for (const s of sourceDef.connectionMeta.sendable) {
if (s.category !== "event") continue;
for (const r of targetDef.connectionMeta.receivable) {
if (r.category !== "event") continue;
if (s.key === r.key) {
pairs.push({ sourceKey: s.key, targetKey: r.key });
}
}
}
return pairs;
}
export function useConnectionResolver({
screenId,
connections,
componentTypes,
}: UseConnectionResolverOptions): void {
const { publish, subscribe } = usePopEvent(screenId);
const connectionsRef = useRef(connections);
connectionsRef.current = connections;
const componentTypesRef = useRef(componentTypes);
componentTypesRef.current = componentTypes;
useEffect(() => {
if (!connections || connections.length === 0) return;
const unsubscribers: (() => void)[] = [];
for (const conn of connections) {
const isAutoMode = conn.sourceOutput === "_auto" || !conn.sourceOutput;
if (isAutoMode && componentTypesRef.current) {
const sourceType = componentTypesRef.current.get(conn.sourceComponent);
const targetType = componentTypesRef.current.get(conn.targetComponent);
if (!sourceType || !targetType) continue;
const pairs = getAutoMatchPairs(sourceType, targetType);
for (const pair of pairs) {
const sourceEvent = `__comp_output__${conn.sourceComponent}__${pair.sourceKey}`;
const targetEvent = `__comp_input__${conn.targetComponent}__${pair.targetKey}`;
const unsub = subscribe(sourceEvent, (payload: unknown) => {
publish(targetEvent, {
value: payload,
_connectionId: conn.id,
});
});
unsubscribers.push(unsub);
}
} else {
const sourceEvent = `__comp_output__${conn.sourceComponent}__${conn.sourceOutput || conn.sourceField}`;
const unsub = subscribe(sourceEvent, (payload: unknown) => {
const targetEvent = `__comp_input__${conn.targetComponent}__${conn.targetInput || conn.targetField}`;
const enrichedPayload = {
value: payload,
filterConfig: conn.filterConfig,
_connectionId: conn.id,
};
publish(targetEvent, enrichedPayload);
});
unsubscribers.push(unsub);
}
}
return () => {
for (const unsub of unsubscribers) {
unsub();
}
};
}, [screenId, connections, subscribe, publish]);
}