import { Segment, Path as UiPath } from "@/lib/path"; import { defineStore } from "pinia"; import { ref, watchEffect } from "vue"; export const useUiStore = defineStore("ui", () => { const anchorId = ref(); const focusPath = ref(new UiPath()); const openPaths = ref>(new Set()); const pinned = ref<{ segment: Segment; parentId?: string }>(); // Ensure all nodes on the focusPath are unfolded. watchEffect(() => { // The node pointed to by the path itself doesn't need to be unfolded. for (const ancestor of focusPath.value.ancestors().slice(1)) { setOpen(ancestor, true); } }); function isOpen(path: UiPath): boolean { return openPaths.value.has(path.fmt()); } function setOpen(path: UiPath, value: boolean) { // Don't update openPaths unnecessarily. // Just in case vue itself doesn't debounce Set operations. if (value && !isOpen(path)) { openPaths.value.add(path.fmt()); } else if (!value && isOpen(path)) { // Move the focusPath if necessary if (path.isPrefixOf(focusPath.value)) focusPath.value = path; openPaths.value.delete(path.fmt()); } } function toggleOpen(path: UiPath) { setOpen(path, !isOpen(path)); } function isPinned(segment: Segment, parentId?: string): boolean { if (!pinned.value) return false; return pinned.value.segment.eq(segment) && pinned.value.parentId === parentId; } function setPinned(segment: Segment, parentId?: string) { pinned.value = { segment, parentId }; } function unsetPinned() { pinned.value = undefined; } return { anchorId, focusPath, isOpen, setOpen, toggleOpen, isPinned, setPinned, unsetPinned, }; });