UNPKG

@sanity/ui-workshop

Version:

An environment for designing, reviewing, and quality-testing React components.

85 lines (71 loc) 2.01 kB
import {isArray} from './lib/isArray' import {isRecord} from './lib/isRecord' import {Pubsub} from './lib/pubsub' import {WorkshopMsg} from './types' /** @internal */ export interface WorkshopFrameController { message: Pubsub<WorkshopMsg> setElement: (el: HTMLIFrameElement | null) => void } /** @internal */ export function createWorkshopFrameController(): WorkshopFrameController { const _subscribers = new Set<(msg: WorkshopMsg) => void>() let _frameElement: HTMLIFrameElement | null = null let _msgQueue: WorkshopMsg[] = [] let _flushTimeout: NodeJS.Timeout | null = null function _flush() { if (_flushTimeout) { clearInterval(_flushTimeout) } _flushTimeout = setTimeout(() => { _frameElement?.contentWindow?.postMessage(_msgQueue, window.location.origin) _msgQueue = [] _flushTimeout = null }, 0) } function _handleMessage(event: MessageEvent<unknown>) { const msgs = event.data if (isArray(msgs)) { for (const msg of msgs) { if (isRecord(msg) && typeof msg.type === 'string' && msg.type.startsWith('workshop/')) { for (const subscriber of _subscribers) { subscriber(msg as unknown as WorkshopMsg) } } } } } function _mount() { if (_frameElement?.contentWindow) { window.addEventListener('message', _handleMessage) } } function _unmount(el: HTMLIFrameElement) { if (el?.contentWindow) { window.removeEventListener('message', _handleMessage) } } return { message: { publish(msg) { _msgQueue.push(msg) _flush() }, subscribe(subscriber) { _subscribers.add(subscriber) return () => { _subscribers.delete(subscriber) } }, }, setElement(el) { const prevFrameElement = _frameElement _frameElement = el if (el) { _mount() } else if (prevFrameElement) { _unmount(prevFrameElement) } }, } }