sdlc-hub/web/src/board/useBoardPrefs.ts
Andrey Epifancev 66bcb7e293 feat(web): страница доски — панель, фильтры, легенда, проблемы, таблица, экспорт (#21)
Заглушка модели в useBoardSource стабильна между рендерами, readTheme не возвращает null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:14:13 +04:00

68 lines
3.9 KiB
TypeScript

import { useCallback, useMemo, useState } from "react";
import { initialSprints, initialWho } from "./analysis/facets.ts";
import type { LayoutStore } from "./components/BoardCanvas.tsx";
import * as st from "./storage.ts";
import type { BoardModel, Filters, JsonImport } from "./types.ts";
export interface BoardPrefsApi {
filters: Filters; snap: boolean; table: boolean; problems: boolean; store: LayoutStore;
toggle(name: st.FlagName | "snap" | "table" | "problems"): void;
setSprints(s: Set<string>): void;
setWho(s: Set<string>): void;
toggleHidden(what: "kind" | "edge", value: string): void;
toggleLane(name: string): void;
moveLane(current: string[], name: string, to: number): void;
setColWidth(date: string, w: number | null): void;
resetLayout(): void;
applyJson(r: JsonImport): void;
}
const readStore = (version: number): LayoutStore =>
({ laneOrder: st.laneOrder(), folded: st.foldedSet(), colWidths: st.colWidths(), version });
const pick = (model: BoardModel, f: Filters) =>
({ model, sprints: initialSprints(model, f, st.rememberedSprints()), who: initialWho(model, f, st.rememberedWho()) });
// настройки доски из хранилища links.html: флаги, скрытые виды и типы, выбор спринтов и исполнителей, раскладка
export function useBoardPrefs(model: BoardModel): BoardPrefsApi {
const [prefs, setPrefs] = useState<st.BoardPrefs>(() => { st.migrateMode(); st.migrateStore(); return st.readPrefs(); });
const [store, setStore] = useState<LayoutStore>(() => readStore(0));
const [sel, setSel] = useState(() => pick(model, prefs.filters));
if (sel.model !== model) setSel(pick(model, prefs.filters)); // новая модель — выбор заново, как fillSprints/fillWho
const bump = useCallback(() => setStore((s) => readStore(s.version + 1)), []);
const filters = useMemo<Filters>(() => ({ ...prefs.filters, sprints: sel.sprints, who: sel.who }), [prefs.filters, sel]);
const toggle = useCallback((name: st.FlagName | "snap" | "table" | "problems") => {
setPrefs((p) => {
if (name === "snap" || name === "table" || name === "problems") { st.writeFlag(name, !p[name]); return { ...p, [name]: !p[name] }; }
st.writeFlag(name, !p.filters[name]);
return { ...p, filters: { ...p.filters, [name]: !p.filters[name] } };
});
}, []);
const setSprints = useCallback((s: Set<string>) => { st.writeSprints(s); setSel((x) => ({ ...x, sprints: new Set(s) })); }, []);
const setWho = useCallback((s: Set<string>) => { st.writeWho(s); setSel((x) => ({ ...x, who: new Set(s) })); }, []);
const toggleHidden = useCallback((what: "kind" | "edge", value: string) => {
setPrefs((p) => {
const key = what === "kind" ? "hideKind" : "hideEdge";
const next = new Set(p.filters[key]);
if (next.has(value)) next.delete(value); else next.add(value);
const filters = { ...p.filters, [key]: next };
st.writeHidden(filters.hideKind, filters.hideEdge);
return { ...p, filters };
});
}, []);
const toggleLane = useCallback((name: string) => { st.toggleLane(name); bump(); }, [bump]);
const moveLane = useCallback((current: string[], name: string, to: number) => { if (st.moveLane(current, name, to)) bump(); }, [bump]);
const setColWidth = useCallback((date: string, w: number | null) => { st.setColWidth(date, w); bump(); }, [bump]);
const resetLayout = useCallback(() => { st.resetLayout(prefs.filters.byDate); bump(); }, [prefs.filters.byDate, bump]);
const applyJson = useCallback((r: JsonImport) => {
const patch = st.applyJsonSettings(r);
setPrefs((p) => ({ ...p, filters: { ...p.filters, ...patch } }));
setSel(pick(model, { ...prefs.filters, ...patch }));
bump();
}, [model, prefs.filters, bump]);
return { filters, snap: prefs.snap, table: prefs.table, problems: prefs.problems, store,
toggle, setSprints, setWho, toggleHidden, toggleLane, moveLane, setColWidth, resetLayout, applyJson };
}