Files
2026-09-18 16:47:15 -04:00

67 lines
1.7 KiB
TypeScript

import type { Config, Selections } from "./types";
type Listener = () => void;
/**
* Minimal reactive store: holds the immutable config plus mutable user
* selections, and notifies subscribers on any change.
*/
export class Store {
private listeners = new Set<Listener>();
constructor(
public readonly config: Config,
private selections: Selections
) {}
getSelections(): Selections {
return this.selections;
}
onChange(listener: Listener): void {
this.listeners.add(listener);
}
private update(patch: Partial<Selections>): void {
this.selections = { ...this.selections, ...patch };
this.emit();
}
setSelectedPlan(planId: string): void {
this.update({ selectedPlanId: planId });
}
setTargetDate(date: Date): void {
this.update({ targetDate: date });
}
setMissingPeriodActive(periodId: string, isActive: boolean): void {
const next = new Set(this.selections.activeMissingPeriodIds);
if (isActive) next.add(periodId);
else next.delete(periodId);
this.update({ activeMissingPeriodIds: next });
}
setExtraExcludedDays(count: number): void {
this.update({ extraExcludedDays: Math.max(0, Math.floor(count) || 0) });
}
setActualMealBlocks(value: number | null): void {
this.update({ actualMealBlocks: normalizeBalance(value) });
}
setActualFlexDollars(value: number | null): void {
this.update({ actualFlexDollars: normalizeBalance(value) });
}
private emit(): void {
for (const l of this.listeners) l();
}
}
/** Clamp an entered balance to a non-negative number, or null if blank/invalid. */
function normalizeBalance(value: number | null): number | null {
if (value === null || !Number.isFinite(value)) return null;
return Math.max(0, value);
}