| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- import { create } from "zustand";
- import type { ClockBlock, ClockRow, Signal } from "../assets/ClockTimeline/model";
- export const defaultClockSignal: Signal = {
- type: "virtual-signal",
- name: "signal-clock",
- subgroup: "pictographs",
- icon: "virtual-signal/signal-clock.png",
- order: "p[clock]",
- };
- interface ClockState {
- // Global Settings
- duration: number;
- pixelsPerTick: number;
- setDuration: (duration: number) => void;
- setZoom: (val: number) => void;
- clockSignal: Signal;
- setClockSignal: (signal: Signal | null) => void;
- // Normalized Data Models
- rows: Record<string, ClockRow>;
- rowOrder: string[]; // Maintains vertical rendering order
- blocks: Record<string, ClockBlock>;
- // UI State
- selectedBlockIds: Set<string>;
- alignmentTick: number | null;
- setAlignmentTick: (tick: number | null) => void;
- // --- Actions ---
- loadState: (state: Partial<ClockState>) => void;
- // Row Management
- addRow: (row: ClockRow) => void;
- updateRow: (id: string, patch: Partial<ClockRow>) => void;
- removeRow: (id: string) => void;
- // Block Management
- addBlocks: (newBlocks: ClockBlock[]) => void;
- updateBlock: (id: string, patch: Partial<ClockBlock>) => void;
- // High-performance batch update for dragging multiple blocks at once
- moveBlocks: (updates: Record<string, { start: number }>) => void;
- removeBlocks: (ids: string[]) => void;
- // Selection
- selectBlocks: (ids: Set<string> | ((prev: Set<string>) => Set<string>)) => void;
- clearSelection: () => void;
- }
- export const useClockStore = create<ClockState>((set) => ({
- duration: 256,
- pixelsPerTick: 4,
- clockSignal: defaultClockSignal, // Set your default signal here
- rows: {},
- rowOrder: [],
- blocks: {},
- selectedBlockIds: new Set(),
- alignmentTick: null,
- loadState: (state: Partial<ClockState>) => set(() => ({ ...state })),
- setDuration: (duration) => set({ duration }),
- setZoom: (val) => set({ pixelsPerTick: val }),
- setClockSignal: (clockSignal) => {
- if (clockSignal) set({ clockSignal });
- },
- setAlignmentTick: (tick) => set({ alignmentTick: tick }),
- addRow: (row) =>
- set((state) => ({
- rows: { ...state.rows, [row.id]: row },
- rowOrder: [...state.rowOrder, row.id],
- })),
- updateRow: (id, patch) =>
- set((state) => ({
- rows: {
- ...state.rows,
- [id]: { ...state.rows[id], ...patch },
- },
- })),
- removeRow: (id) =>
- set((state) => {
- const newRows = { ...state.rows };
- delete newRows[id];
- // Also clean up orphan blocks
- const newBlocks = Object.fromEntries(Object.entries(state.blocks).filter(([_, block]) => block.rowId !== id));
- return {
- rows: newRows,
- rowOrder: state.rowOrder.filter((rowId) => rowId !== id),
- blocks: newBlocks,
- };
- }),
- addBlocks: (newBlocks) =>
- set((state) => {
- const blockMap = { ...state.blocks };
- newBlocks.forEach((b) => {
- blockMap[b.id] = b;
- });
- return { blocks: blockMap };
- }),
- updateBlock: (id, patch) =>
- set((state) => ({
- blocks: {
- ...state.blocks,
- [id]: { ...state.blocks[id], ...patch },
- },
- })),
- moveBlocks: (updates) =>
- set((state) => {
- const nextBlocks = { ...state.blocks };
- for (const [id, patch] of Object.entries(updates)) {
- if (nextBlocks[id]) {
- nextBlocks[id] = { ...nextBlocks[id], ...patch };
- }
- }
- return { blocks: nextBlocks };
- }),
- removeBlocks: (ids) =>
- set((state) => {
- const newBlocks = { ...state.blocks };
- ids.forEach((id) => delete newBlocks[id]);
- const newSelection = new Set(state.selectedBlockIds);
- ids.forEach((id) => newSelection.delete(id));
- return { blocks: newBlocks, selectedBlockIds: newSelection };
- }),
- selectBlocks: (idsOrUpdater) =>
- set((state) => ({
- selectedBlockIds: typeof idsOrUpdater === "function" ? idsOrUpdater(state.selectedBlockIds) : idsOrUpdater,
- })),
- clearSelection: () => set({ selectedBlockIds: new Set() }),
- }));
|