diff options
Diffstat (limited to 'web-timeplot/src/core/store.js')
| -rw-r--r-- | web-timeplot/src/core/store.js | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/web-timeplot/src/core/store.js b/web-timeplot/src/core/store.js new file mode 100644 index 0000000..9989e5f --- /dev/null +++ b/web-timeplot/src/core/store.js @@ -0,0 +1,95 @@ +function clonePanelState(panels) { + return Object.fromEntries(Object.entries(panels).map(([key, value]) => [key, { ...value }])); +} + +export function createInitialState() { + return { + app: { + title: 'TimePlot', + renderer: 'pending', + }, + time: { + realNowMs: Date.now(), + realElapsedMs: 0, + plotTimeMs: 0, + speed: 1, + paused: false, + }, + plot: { + showGrid: true, + showPoints: true, + windowDurationMs: 20000, + maxPoints: 1600, + valueRange: { + min: -1.6, + max: 1.6, + }, + hoveredPoint: null, + tooltip: { + visible: false, + x: 0, + y: 0, + point: null, + }, + }, + source: { + activeId: 'synthetic-wave', + preset: 'telemetry', + sampleRateHz: 60, + amplitude: 1, + noise: 0.08, + }, + panels: { + status: { title: 'Status', visible: true }, + source: { title: 'Data Source', visible: true }, + config: { title: 'Config', visible: true }, + help: { title: 'Help', visible: false }, + }, + }; +} + +export class Store { + constructor(initialState = createInitialState()) { + this.state = initialState; + this.listeners = new Set(); + } + + getState() { + return this.state; + } + + subscribe(listener) { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + setState(updater) { + const nextState = typeof updater === 'function' ? updater(this.state) : updater; + this.state = nextState; + for (const listener of this.listeners) { + listener(this.state); + } + } + + patch(partial) { + this.setState((state) => ({ + ...state, + ...partial, + time: partial.time ? { ...state.time, ...partial.time } : state.time, + plot: partial.plot + ? { + ...state.plot, + ...partial.plot, + valueRange: partial.plot.valueRange + ? { ...state.plot.valueRange, ...partial.plot.valueRange } + : state.plot.valueRange, + tooltip: partial.plot.tooltip + ? { ...state.plot.tooltip, ...partial.plot.tooltip } + : state.plot.tooltip, + } + : state.plot, + source: partial.source ? { ...state.source, ...partial.source } : state.source, + panels: partial.panels ? clonePanelState({ ...state.panels, ...partial.panels }) : state.panels, + })); + } +} |
