a post-component library for building user-interfaces on the web.
at main 56 lines 1.7 kB view raw
1import type { Renderable } from '../index.ts' 2import { assert, is_renderable } from '../shared.ts' 3import { type Cleanup } from './util.ts' 4 5export interface Controller { 6 _mount_callbacks: (() => Cleanup)[] 7 _unmount_callbacks: Cleanup[] 8 _invalidate: Map<object, () => void> 9} 10 11export const controllers: WeakMap<Renderable, Controller> = new WeakMap() 12const invalidated_controllers: Set<Controller> = new Set() 13let invalidate_queued: null | Promise<void> = null 14 15export function get_controller(renderable: Renderable): Controller { 16 let controller = controllers.get(renderable) 17 if (!controller) 18 controllers.set( 19 renderable, 20 (controller = { 21 _mount_callbacks: [], 22 _unmount_callbacks: [], 23 _invalidate: new Map(), 24 }), 25 ) 26 return controller 27} 28 29export function invalidate(...renderables: Renderable[]): Promise<void> { 30 for (const renderable of renderables) invalidated_controllers.add(get_controller(renderable)) 31 32 return (invalidate_queued ??= Promise.resolve() 33 .then(() => { 34 for (const controller of invalidated_controllers) { 35 invalidated_controllers.delete(controller) 36 controller._invalidate.forEach(invalidate => invalidate()) 37 } 38 }) 39 .finally(() => { 40 invalidate_queued = null 41 })) 42} 43 44export function onMount(renderable: Renderable, callback: () => Cleanup): void { 45 assert(is_renderable(renderable), 'expected a renderable') 46 const controller = get_controller(renderable) 47 if (controller._invalidate.size) { 48 controller._unmount_callbacks.push(callback()) 49 } else { 50 controller._mount_callbacks.push(callback) 51 } 52} 53 54export function onUnmount(renderable: Renderable, callback: () => void): void { 55 onMount(renderable, () => callback) 56}