UNPKG

extml

Version:

Converts html tagged templates to ExtJS component object.

141 lines (120 loc) 4.69 kB
import { getActiveTracker } from './dependencyTracker.js'; export function createState(initialValue, context = null, treatAsSingleEntity = false, sync = false) { const stateCache = createState.stateCache || (createState.stateCache = new WeakMap()); const getPersistentState = (context, valueInitializer) => { if (!stateCache.has(context)) { stateCache.set(context, valueInitializer()); } return stateCache.get(context); }; if (context) { return getPersistentState(context, () => { return createState(initialValue, null, treatAsSingleEntity, sync); }); } const isObject = typeof initialValue === 'object' && initialValue !== null && !Array.isArray(initialValue) && !(initialValue instanceof Date) && !treatAsSingleEntity; let state = isObject ? { ...initialValue } : initialValue; const globalListeners = new Set(); const propertyListeners = isObject ? Object.fromEntries(Object.keys(state).map(key => [key, new Set()])) : null; let batchUpdates = []; let pendingUpdate = false; const getState = () => { const tracker = getActiveTracker(); if (tracker) { tracker.add(getState); } return state; }; getState.$$isState = true; getState.$$subscribe = listener => { globalListeners.add(listener); return () => globalListeners.delete(listener); }; const setState = (newValue) => { if (typeof newValue === "function") { newValue = newValue(state); } if (sync) { applyState(newValue); return Promise.resolve(); } else { batchUpdates.push(newValue); if (!pendingUpdate) { pendingUpdate = true; return new Promise(resolve => { queueMicrotask(() => { pendingUpdate = false; processBatchUpdates(); resolve(); }); }); } else { return Promise.resolve(); } } }; getState.$$setState = setState; const applyState = (newValue) => { let hasChanges = false; if (isObject) { const newState = { ...state }; Object.keys(newValue).forEach(key => { if (newValue[key] !== state[key]) { newState[key] = newValue[key]; hasChanges = true; propertyListeners[key].forEach(listener => listener(newState[key])); } }); if (hasChanges) { state = newState; } } else { if (newValue !== state) { state = newValue; hasChanges = true; } } if (hasChanges) { globalListeners.forEach(listener => listener(state)); } }; const processBatchUpdates = () => { batchUpdates.forEach(newValue => applyState(newValue)); batchUpdates = []; }; if (isObject) { Object.keys(state).forEach(key => { setState[key] = (newValue) => { const updatedValue = typeof newValue === "function" ? newValue(state[key]) : newValue; if (state[key] !== updatedValue) { state = { ...state, [key]: updatedValue }; propertyListeners[key].forEach(listener => listener(state[key])); globalListeners.forEach(listener => listener(state)); } }; }); } const stateGetters = isObject ? Object.fromEntries( Object.keys(state).map(key => { const propertyGetter = () => { const tracker = getActiveTracker(); if (tracker) { tracker.add(propertyGetter); } return state[key]; }; propertyGetter.$$isState = true; propertyGetter.$$setState = setState[key]; propertyGetter.$$subscribe = listener => { propertyListeners[key].add(listener); return () => propertyListeners[key].delete(listener); }; return [key, propertyGetter]; }) ) : getState; return [isObject ? stateGetters : getState, setState, getState.$$subscribe]; }