From 08e3ddc83dbff47f236db139dada5fa4d501916b Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Wed, 5 Oct 2022 23:23:05 +0200 Subject: [PATCH] context-state: `combineStates` --- packages/context-state/src/combineStates.ts | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 packages/context-state/src/combineStates.ts diff --git a/packages/context-state/src/combineStates.ts b/packages/context-state/src/combineStates.ts new file mode 100644 index 0000000..4a8e402 --- /dev/null +++ b/packages/context-state/src/combineStates.ts @@ -0,0 +1,47 @@ +import { of } from "rxjs" +import { + mapRecord, + detachedNode, + EMPTY_VALUE, + recordEntries, + children, +} from "./internal" +import { StateNode, StringRecord } from "./types" + +type StringRecordNodeToNodeStringRecord< + States extends StringRecord>, +> = StateNode<{ + [K in keyof States]: States[K] extends StateNode ? V : never +}> + +export const combineStates = >>( + states: States, +): StringRecordNodeToNodeStringRecord => { + let inactiveStates = Object.keys(states).length + let emptyStates = 0 + const activeStates = mapRecord(states, () => false) + const latestStates = mapRecord(states, () => null) + + const [result, run] = detachedNode(() => of({ ...latestStates })) + + let latestValue: boolean | EMPTY_VALUE = false + recordEntries(states).forEach(([key, node]) => { + children.get(node)!.add((isActive, value) => { + if (isActive !== activeStates[key]) { + inactiveStates += isActive ? -1 : +1 + activeStates[key] = isActive + } + + if (value !== latestStates[key]) { + emptyStates += + latestStates[key] === EMPTY_VALUE ? -1 : value === EMPTY_VALUE ? 1 : 0 + latestStates[key] = value + } + + latestValue = emptyStates === 0 ? !latestValue : EMPTY_VALUE + run(inactiveStates === 0, latestValue) + }) + }) + + return result as StringRecordNodeToNodeStringRecord +}