import { computed, type ComputedRef, type Ref, ref, unref } from "vue"; export function useSidebarSelection(items: Ref | ComputedRef, getId: (item: T) => string) { const selectionMode = ref(false); const selectedIds = ref(new Set()); const lastClickedIndex = ref(null); const allSelected = computed(() => unref(items).length > 0 && selectedIds.value.size === unref(items).length); function toggleSelectionMode() { selectionMode.value = !selectionMode.value; if (!selectionMode.value) { selectedIds.value = new Set(); lastClickedIndex.value = null; } } function toggleSelection(id: string) { const next = new Set(selectedIds.value); if (next.has(id)) { next.delete(id); } else { next.add(id); } selectedIds.value = next; } function toggleSelectAll() { if (allSelected.value) { selectedIds.value = new Set(); } else { selectedIds.value = new Set(unref(items).map(getId)); } } /** Handle a click in the context of selection mode. * Returns true if the click was consumed (caller should not navigate). */ function handleSelectionClick(item: T, index: number, event: MouseEvent): boolean { if (!selectionMode.value) { return false; } if (event.shiftKey && lastClickedIndex.value !== null) { const start = Math.min(lastClickedIndex.value, index); const end = Math.max(lastClickedIndex.value, index); const next = new Set(selectedIds.value); for (let i = start; i <= end; i++) { const el = unref(items)[i]; if (el) { const id = getId(el); if (id) { next.add(id); } } } selectedIds.value = next; } else { toggleSelection(getId(item)); } lastClickedIndex.value = index; return true; } /** Call after items are removed (e.g. batch delete). Prunes stale * selections and exits selection mode if the list is now empty. */ function pruneAfterDelete() { const currentIds = new Set(unref(items).map(getId)); selectedIds.value = new Set([...selectedIds.value].filter((id) => currentIds.has(id))); if (unref(items).length === 0) { selectionMode.value = false; } } return { selectionMode, selectedIds, allSelected, toggleSelectionMode, toggleSelection, toggleSelectAll, handleSelectionClick, pruneAfterDelete, }; }