UNPKG

@toolpad/utils

Version:

Shared utilities used by Toolpad packages.

1 lines 8.65 kB
{"version":3,"sources":["../../src/hooks/useStorageState.ts","../../src/events.ts"],"sourcesContent":["import * as React from 'react';\nimport { Emitter } from '../events';\n\n// storage events only work across windows, we'll use an event emitter to announce within the window\nconst emitter = new Emitter<Record<string, null>>();\n\nfunction subscribe(area: Storage, key: string, cb: () => void): () => void {\n const storageHandler = (event: StorageEvent) => {\n if (event.storageArea === area && event.key === key) {\n cb();\n }\n };\n window.addEventListener('storage', storageHandler);\n emitter.on(key, cb);\n return () => {\n window.removeEventListener('storage', storageHandler);\n emitter.off(key, cb);\n };\n}\n\nfunction getSnapshot(area: Storage, key: string): string | null {\n return area.getItem(key);\n}\n\nfunction setValue(area: Storage, key: string, value: string | null) {\n if (typeof window !== 'undefined') {\n if (value === null) {\n area.removeItem(key);\n } else {\n area.setItem(key, String(value));\n }\n emitter.emit(key, null);\n }\n}\n\ntype Initializer<T> = () => T;\n\ntype UseStorageStateHookResult<T> = [T, React.Dispatch<React.SetStateAction<T>>];\n\nfunction useStorageStateServer(\n kind: 'session' | 'local',\n key: string,\n initializer: string | Initializer<string>,\n): UseStorageStateHookResult<string>;\nfunction useStorageStateServer(\n kind: 'session' | 'local',\n key: string,\n initializer?: string | null | Initializer<string | null>,\n): UseStorageStateHookResult<string | null>;\nfunction useStorageStateServer(\n kind: 'session' | 'local',\n key: string,\n initializer: string | null | Initializer<string | null> = null,\n): UseStorageStateHookResult<string | null> | UseStorageStateHookResult<string> {\n const [initialValue] = React.useState(initializer);\n return [initialValue, () => {}];\n}\n\n/**\n * Sync state to local/session storage so that it persists through a page refresh. Usage is\n * similar to useState except we pass in a storage key so that we can default\n * to that value on page load instead of the specified initial value.\n *\n * Since the storage API isn't available in server-rendering environments, we\n * return initialValue during SSR and hydration.\n *\n * Things this hook does different from existing solutions:\n * - SSR-capable: it shows initial value during SSR and hydration, but immediately\n * initializes when clientside mounted.\n * - Sync state across tabs: When another tab changes the value in the storage area, the\n * current tab follows suit.\n */\nfunction useStorageStateBrowser(\n kind: 'session' | 'local',\n key: string,\n initializer: string | Initializer<string>,\n): UseStorageStateHookResult<string>;\nfunction useStorageStateBrowser(\n kind: 'session' | 'local',\n key: string,\n initializer?: string | null | Initializer<string | null>,\n): UseStorageStateHookResult<string | null>;\nfunction useStorageStateBrowser(\n kind: 'session' | 'local',\n key: string,\n initializer: string | null | Initializer<string | null> = null,\n): UseStorageStateHookResult<string | null> | UseStorageStateHookResult<string> {\n const [initialValue] = React.useState(initializer);\n const area = kind === 'session' ? window.sessionStorage : window.localStorage;\n const subscribeKey = React.useCallback((cb: () => void) => subscribe(area, key, cb), [area, key]);\n const getKeySnapshot = React.useCallback(\n () => getSnapshot(area, key) ?? initialValue,\n [area, initialValue, key],\n );\n const getKeyServerSnapshot = React.useCallback(() => initialValue, [initialValue]);\n\n const storedValue = React.useSyncExternalStore(\n subscribeKey,\n getKeySnapshot,\n getKeyServerSnapshot,\n );\n\n const setStoredValue = React.useCallback(\n (value: React.SetStateAction<string | null>) => {\n const valueToStore = value instanceof Function ? value(storedValue) : value;\n setValue(area, key, valueToStore);\n },\n [area, key, storedValue],\n );\n\n return [storedValue, setStoredValue];\n}\n\nexport default typeof window === 'undefined' ? useStorageStateServer : useStorageStateBrowser;\n","export type EventName = string | symbol;\n\nexport type EventHandlers = Record<EventName, unknown>;\n\nexport type EventHandler<T extends EventHandlers, K extends keyof T = keyof T> = (\n event: T[K],\n) => void;\n\nexport type AllEventsHandler<T extends EventHandlers, K extends keyof T = keyof T> = (\n type: K,\n event: T[K],\n) => void;\n\n/**\n * Lightweight event emitter\n */\nexport class Emitter<T extends EventHandlers = {}> {\n private handlers = new Map<keyof T, Set<EventHandler<T> | AllEventsHandler<T>>>();\n\n /**\n * Add a listener for an event\n */\n on(name: '*', handler: AllEventsHandler<T>): void;\n on<K extends keyof T>(name: K, handler: EventHandler<T, K>): void;\n on<K extends keyof T>(name: K | '*', handler: EventHandler<T, K> | AllEventsHandler<T>): void {\n let eventHandlers = this.handlers.get(name);\n if (!eventHandlers) {\n eventHandlers = new Set();\n this.handlers.set(name, eventHandlers);\n }\n eventHandlers.add(handler as EventHandler<T> | AllEventsHandler<T>);\n }\n\n /**\n * Remove a listener from an event\n */\n off<K extends keyof T>(name: K, handler: EventHandler<T, K>) {\n const eventHandlers = this.handlers.get(name);\n if (eventHandlers) {\n eventHandlers.delete(handler as EventHandler<T> | AllEventsHandler<T>);\n if (eventHandlers.size <= 0) {\n this.handlers.delete(name);\n }\n }\n }\n\n /**\n * Subscribe to an event and return an unsubscribe function.\n */\n subscribe<K extends keyof T>(name: K, handler: EventHandler<T, K>) {\n this.on(name, handler);\n return () => {\n this.off(name, handler);\n };\n }\n\n /**\n * Emit an event.\n */\n emit<K extends keyof T>(name: K, event: T[K]) {\n const eventHandlers = this.handlers.get(name);\n if (eventHandlers) {\n for (const eventHandler of eventHandlers) {\n (eventHandler as EventHandler<T, K>)(event);\n }\n }\n const allHandlers = this.handlers.get('*');\n if (allHandlers) {\n for (const eventHandler of allHandlers) {\n (eventHandler as AllEventsHandler<T>)(name, event);\n }\n }\n }\n}\n"],"mappings":"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","names":[]}