/* eslint-disable */ import { createContext } from "react"; import { Appearance, Dimensions, type ColorSchemeName, type LayoutRectangle, } from "react-native"; import type { StyleDescriptor } from "react-native-css/compiler"; export type Effect = { observers: Set; run(): void; }; export type Observable = { observers: Set; get: (effect?: Effect) => Value; set: (arg: Arg) => void; run: () => void; }; type Read = (get: Getter, arg?: Arg) => Value; export type Getter = (observable: Observable) => Value; export const observableBatch: { current?: Set; } = {}; export function observable( init: Value | Read, equality: (value1: Value, value2: Value) => boolean = Object.is, ) { let value: Value; let isStatic = typeof init !== "function"; let didInit: boolean | undefined; let lastArg: Arg | undefined; if (typeof init !== "function") { value = init; didInit = true; } const observers = new Set(); const effect: Effect = { observers, run: () => { if (!isStatic) { const nextValue = (init as Read)(getter, lastArg); if (equality(value, nextValue)) { return; } value = nextValue; } notify(); }, }; const getter: Getter = (observable) => observable.get(effect); function get(effect?: Effect) { if (effect) { observers.add(effect); } if (!didInit) { value = (init as Read)(getter, undefined); } return value; } function set(arg: Arg) { if (isStatic) { if (equality(value, arg as unknown as Value)) { return; } value = arg as unknown as Value; } else { const nextValue = (init as Read)(getter, arg); didInit = true; lastArg = arg; if (equality(value, nextValue)) { return; } value = nextValue; } notify(); return obs; } function notify() { Array.from(observers).forEach((observer) => { if (observableBatch.current) { observableBatch.current.add(observer); } else { observer.run(); } }); } const obs: Observable = { observers, get, set, run: effect.run, }; return obs; } export function cleanupEffect(effect: Effect) { if (!effect) return; for (const dep of effect.observers) { dep.observers.delete(effect); } effect.observers.clear(); } /** Family Helpers ************************************************************/ export function family( fn: (key: Key, args: Args) => Result, ) { const map = new Map(); return Object.assign( (key: Key, args: Args) => { let value = map.get(key); if (!value) { value = fn(key, args); map.set(key, value); } return value; }, { delete(key: Key) { return map.delete(key); }, clear() { return map.clear(); }, }, ); } type WeakFamilyFn = (( key: Key, args: Args, ) => Result) & { has(key: Key): boolean; }; export function weakFamily( fn: (key: Key) => Result, ): WeakFamilyFn; export function weakFamily( fn: (key: Key, args: Args) => Result, ): WeakFamilyFn; export function weakFamily( fn: (key: Key, args: Args) => Result, ): WeakFamilyFn { const map = new WeakMap(); return Object.assign( (key: Key, args: Args) => { let value = map.get(key); if (!value) { value = fn(key, args); map.set(key, value); } return value; }, { has: (key: Key) => map.has(key), }, ); } /********************************* Variables **********************************/ export const VAR_SYMBOL = Symbol.for("react-native-css.var"); export type VariableContextValue = Record & { [VAR_SYMBOL]: true; }; /** Pseudo Classes ************************************************************/ export const hoverFamily = weakFamily(() => observable(false)); export const activeFamily = weakFamily(() => observable(false)); export const focusFamily = weakFamily(() => observable(false)); /** Dimensions ****************************************************************/ export const dimensions = observable(Dimensions.get("window")); export const vw = observable( (read, value) => value ?? read(dimensions)?.width, ); export const vh = observable( (read, value) => value ?? read(dimensions)?.height, ); Dimensions.addEventListener("change", ({ window }) => { observableBatch.current = new Set(); vw.set(window.width); vh.set(window.height); for (const effect of observableBatch.current) { effect.run(); } observableBatch.current = undefined; }); /** Color Scheme **************************************************************/ export const colorScheme = observable( Appearance.getColorScheme(), ); Appearance.addChangeListener((event) => colorScheme.set(event.colorScheme)); /** Containers ****************************************************************/ export type ContainerContextValue = Record; export const ContainerContext = createContext({}); export const containerLayoutFamily = weakFamily(() => { return observable({ x: 0, y: 0, width: 0, height: 0, }); }); export const containerWidthFamily = weakFamily((key) => { return observable((read) => { return read(containerLayoutFamily(key))?.width || 0; }); }); export const containerHeightFamily = weakFamily((key) => { return observable((read) => { return read(containerLayoutFamily(key))?.width || 0; }); });