kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { create } from "zustand";
2
3interface BacklogBulkSelectionState {
4 selectedTaskIds: Set<string>;
5 isSelectMode: boolean;
6 availableTaskIds: string[];
7 focusedTaskId: string | null;
8
9 selectTask: (taskId: string) => void;
10 deselectTask: (taskId: string) => void;
11 toggleSelection: (taskId: string) => void;
12 clearSelection: () => void;
13 selectAll: () => void;
14 setAvailableTasks: (taskIds: string[]) => void;
15 getSelectedCount: () => number;
16 isSelected: (taskId: string) => boolean;
17 setFocusedTask: (taskId: string | null) => void;
18 clearFocus: () => void;
19 isFocused: (taskId: string) => boolean;
20 focusNext: () => void;
21 focusPrevious: () => void;
22}
23
24const useBacklogBulkSelectionStore = create<BacklogBulkSelectionState>(
25 (set, get) => ({
26 selectedTaskIds: new Set(),
27 isSelectMode: false,
28 availableTaskIds: [],
29 focusedTaskId: null,
30
31 selectTask: (taskId: string) =>
32 set((state) => ({
33 selectedTaskIds: new Set([...state.selectedTaskIds, taskId]),
34 isSelectMode: true,
35 })),
36
37 deselectTask: (taskId: string) =>
38 set((state) => {
39 const newSet = new Set(state.selectedTaskIds);
40 newSet.delete(taskId);
41 return {
42 selectedTaskIds: newSet,
43 isSelectMode: newSet.size > 0,
44 };
45 }),
46
47 toggleSelection: (taskId: string) => {
48 const { selectedTaskIds } = get();
49 if (selectedTaskIds.has(taskId)) {
50 get().deselectTask(taskId);
51 } else {
52 get().selectTask(taskId);
53 }
54 },
55
56 clearSelection: () =>
57 set({
58 selectedTaskIds: new Set(),
59 isSelectMode: false,
60 }),
61
62 selectAll: () =>
63 set((state) => ({
64 selectedTaskIds: new Set(state.availableTaskIds),
65 isSelectMode: true,
66 })),
67
68 setAvailableTasks: (taskIds: string[]) =>
69 set(() => ({
70 availableTaskIds: taskIds,
71 })),
72
73 getSelectedCount: () => {
74 const { selectedTaskIds } = get();
75 return selectedTaskIds.size;
76 },
77
78 isSelected: (taskId: string) => {
79 const { selectedTaskIds } = get();
80 return selectedTaskIds.has(taskId);
81 },
82
83 setFocusedTask: (taskId: string | null) =>
84 set(() => ({
85 focusedTaskId: taskId,
86 })),
87
88 clearFocus: () =>
89 set(() => ({
90 focusedTaskId: null,
91 })),
92
93 isFocused: (taskId: string) => {
94 const { focusedTaskId } = get();
95 return focusedTaskId === taskId;
96 },
97
98 focusNext: () => {
99 const { availableTaskIds, focusedTaskId } = get();
100
101 if (availableTaskIds.length === 0) return;
102
103 if (!focusedTaskId) {
104 get().setFocusedTask(availableTaskIds[0]);
105 return;
106 }
107
108 const currentIndex = availableTaskIds.indexOf(focusedTaskId);
109 const nextIndex = (currentIndex + 1) % availableTaskIds.length;
110 get().setFocusedTask(availableTaskIds[nextIndex]);
111 },
112
113 focusPrevious: () => {
114 const { availableTaskIds, focusedTaskId } = get();
115
116 if (availableTaskIds.length === 0) return;
117
118 if (!focusedTaskId) {
119 get().setFocusedTask(availableTaskIds[availableTaskIds.length - 1]);
120 return;
121 }
122
123 const currentIndex = availableTaskIds.indexOf(focusedTaskId);
124 const previousIndex =
125 currentIndex === 0 ? availableTaskIds.length - 1 : currentIndex - 1;
126 get().setFocusedTask(availableTaskIds[previousIndex]);
127 },
128 }),
129);
130
131export default useBacklogBulkSelectionStore;