|
27 | 27 |
|
28 | 28 | let searchQuery = $state(''); |
29 | 29 | let selectedIds = $state.raw<SvelteSet<string>>(new SvelteSet(conversations.map((c) => c.id))); |
| 30 | + let lastClickedId = $state<string | null>(null); |
30 | 31 |
|
31 | 32 | let filteredConversations = $derived( |
32 | 33 | conversations.filter((conv) => { |
|
44 | 45 | filteredConversations.some((conv) => selectedIds.has(conv.id)) && !allSelected |
45 | 46 | ); |
46 | 47 |
|
47 | | - function toggleConversation(id: string) { |
| 48 | + function toggleConversation(id: string, shiftKey: boolean = false) { |
48 | 49 | const newSet = new SvelteSet(selectedIds); |
| 50 | +
|
| 51 | + if (shiftKey && lastClickedId !== null) { |
| 52 | + const lastIndex = filteredConversations.findIndex((c) => c.id === lastClickedId); |
| 53 | + const currentIndex = filteredConversations.findIndex((c) => c.id === id); |
| 54 | +
|
| 55 | + if (lastIndex !== -1 && currentIndex !== -1) { |
| 56 | + const start = Math.min(lastIndex, currentIndex); |
| 57 | + const end = Math.max(lastIndex, currentIndex); |
| 58 | +
|
| 59 | + const shouldSelect = !newSet.has(id); |
| 60 | +
|
| 61 | + for (let i = start; i <= end; i++) { |
| 62 | + if (shouldSelect) { |
| 63 | + newSet.add(filteredConversations[i].id); |
| 64 | + } else { |
| 65 | + newSet.delete(filteredConversations[i].id); |
| 66 | + } |
| 67 | + } |
| 68 | +
|
| 69 | + selectedIds = newSet; |
| 70 | + return; |
| 71 | + } |
| 72 | + } |
| 73 | +
|
49 | 74 | if (newSet.has(id)) { |
50 | 75 | newSet.delete(id); |
51 | 76 | } else { |
52 | 77 | newSet.add(id); |
53 | 78 | } |
| 79 | +
|
54 | 80 | selectedIds = newSet; |
| 81 | + lastClickedId = id; |
55 | 82 | } |
56 | 83 |
|
57 | 84 | function toggleAll() { |
|
76 | 103 | function handleCancel() { |
77 | 104 | selectedIds = new SvelteSet(conversations.map((c) => c.id)); |
78 | 105 | searchQuery = ''; |
| 106 | + lastClickedId = null; |
79 | 107 |
|
80 | 108 | onCancel(); |
81 | 109 | } |
|
86 | 114 | if (open && !previousOpen) { |
87 | 115 | selectedIds = new SvelteSet(conversations.map((c) => c.id)); |
88 | 116 | searchQuery = ''; |
| 117 | + lastClickedId = null; |
89 | 118 | } else if (!open && previousOpen) { |
90 | 119 | onCancel(); |
91 | 120 | } |
|
174 | 203 | {#each filteredConversations as conv (conv.id)} |
175 | 204 | <tr |
176 | 205 | class="cursor-pointer border-b transition-colors hover:bg-muted/50" |
177 | | - onclick={() => toggleConversation(conv.id)} |
| 206 | + onclick={(e) => toggleConversation(conv.id, e.shiftKey)} |
178 | 207 | > |
179 | | - <td class="p-3" onclick={(e) => e.stopPropagation()}> |
| 208 | + <td class="p-3"> |
180 | 209 | <Checkbox |
181 | 210 | checked={selectedIds.has(conv.id)} |
182 | | - onCheckedChange={() => toggleConversation(conv.id)} |
| 211 | + onclick={(e) => { |
| 212 | + e.preventDefault(); |
| 213 | + e.stopPropagation(); |
| 214 | + toggleConversation(conv.id, e.shiftKey); |
| 215 | + }} |
183 | 216 | /> |
184 | 217 | </td> |
185 | 218 |
|
|
0 commit comments