// =============================================================================
// iframe-bridge-client.ts — Script embarqué dans chaque iframe carte
// =============================================================================
// Ce fichier est inclus dans les pages HTML des iframes (map-osm.html, etc.)
// Il gère :
// 1. L'annonce de readiness au parent
// 2. La réception des actions et sync de state
// 3. La remontée d'événements (clics, survols, etc.) vers le parent
// =============================================================================
interface IframeBridgeMessage {
source: 'map-parent' | 'map-iframe';
iframeId: string;
type: 'ACTION' | 'READY' | 'EVENT' | 'STATE_SYNC';
payload: unknown;
}
interface MapBridgeClient {
iframeId: string;
state: any;
onStateSync: (state: any) => void;
onAction: (action: { type: string; payload?: unknown }) => void;
emitEvent: (name: string, data: unknown) => void;
signalReady: () => void;
}
/**
* Crée le client bridge côté iframe.
*
* Usage dans map-osm.html :
*
* const bridge = createMapBridge('map-osm');
*
* bridge.onStateSync = (state) => {
* console.log('Received full state:', state);
* renderMarkers(state.objects);
* };
*
* bridge.onAction = (action) => {
* if (action.type === 'FIT_BOUNDS') fitBounds(action.payload.bounds);
* if (action.type === 'HIGHLIGHT') highlight(action.payload.id);
* };
*
* // Quand la carte est initialisée :
* map.on('load', () => bridge.signalReady());
*
* // Remonter les événements :
* map.on('click', (e) => {
* bridge.emitEvent('object-clicked', { id: e.feature.id });
* });
*/
function createMapBridge(iframeId: string): MapBridgeClient {
const client: MapBridgeClient = {
iframeId,
state: null,
// Callbacks à surcharger par le consommateur
onStateSync: () => {},
onAction: () => {},
/** Envoie un événement au parent */
emitEvent(name: string, data: unknown): void {
const msg: IframeBridgeMessage = {
source: 'map-iframe',
iframeId: this.iframeId,
type: 'EVENT',
payload: { name, data },
};
window.parent.postMessage(msg, '*');
},
/** Signale au parent que l'iframe est prête */
signalReady(): void {
const msg: IframeBridgeMessage = {
source: 'map-iframe',
iframeId: this.iframeId,
type: 'READY',
payload: null,
};
window.parent.postMessage(msg, '*');
console.log(`[MapBridge] iframe "${iframeId}" signaled READY`);
},
};
// Écoute les messages du parent
window.addEventListener('message', (event: MessageEvent<IframeBridgeMessage>) => {
const msg = event.data;
if (!msg || msg.source !== 'map-parent') return;
if (msg.iframeId !== iframeId) return; // Ignore les messages destinés à d'autres iframes
switch (msg.type) {
case 'STATE_SYNC':
client.state = msg.payload;
client.onStateSync(msg.payload);
break;
case 'ACTION':
client.onAction(msg.payload as { type: string; payload?: unknown });
break;
default:
console.log(`[MapBridge] unhandled message type: ${msg.type}`);
}
});
return client;
}
// Exporte pour usage dans un contexte module, ou attache au window pour usage script
if (typeof window !== 'undefined') {
(window as any).createMapBridge = createMapBridge;
}
export { createMapBridge, MapBridgeClient, IframeBridgeMessage };