@@ -32,7 +32,10 @@ import {
3232 getPublishedHistories ,
3333 getSharedHistories ,
3434} from " @/api/histories" ;
35+ import type HistoryCard from " @/components/History/HistoryCard.vue" ;
36+ import { useHistoryCardActions } from " @/components/History/useHistoryCardActions" ;
3537import { useConfirmDialog } from " @/composables/confirmDialog" ;
38+ import { useSelectedItems } from " @/composables/selectedItems/selectedItems" ;
3639import { Toast } from " @/composables/toast" ;
3740import { useHistoryStore } from " @/stores/historyStore" ;
3841import { updateHistoryFields } from " @/stores/services/history.services" ;
@@ -98,7 +101,6 @@ const bulkTagsLoading = ref(false);
98101const bulkDeleteOrRestoreLoading = ref (false );
99102const bulkPurgeLoading = ref (false );
100103const historiesLoaded = ref <AnyHistoryEntry []>([]);
101- const selectedHistories = ref <SelectedHistory []>([]);
102104
103105/** Computed property that determines if the current view is "My Histories" */
104106const myView = computed (() => props .activeList === " my" );
@@ -150,6 +152,49 @@ const invalidFilters = computed(() => historyListFilters.value.getValidFilters(r
150152const isSurroundedByQuotes = computed (() => / ^ ["'] . * ["'] $ / .test (filterText .value ));
151153const hasInvalidFilters = computed (() => ! isSurroundedByQuotes .value && Object .keys (invalidFilters .value ).length > 0 );
152154
155+ const selectedHistories = computed <SelectedHistory []>(() => {
156+ const ids = Array .from (selectedItems .value .keys ());
157+ const matchingHistories = historiesLoaded .value .filter ((h ) => ids .includes (h .id ));
158+ return matchingHistories .map ((h ) => ({
159+ id: h .id ,
160+ name: h .name ,
161+ published: h .published ,
162+ purged: h .purged ,
163+ }));
164+ });
165+
166+ const {
167+ selectedItems,
168+ selectAllInCurrentQuery,
169+ isSelected,
170+ setSelected,
171+ resetSelection,
172+ itemRefs,
173+ initSelectedItem,
174+ onClick,
175+ onKeyDown,
176+ } = useSelectedItems <AnyHistoryEntry , typeof HistoryCard >({
177+ scopeKey: computed (() => ` ${props .activeList }-histories-${filterText .value } ` ),
178+ getItemKey : (item ) => item .id ,
179+ filterText: filterText ,
180+ totalItemsInQuery: computed (() => totalHistories .value ?? 0 ),
181+ allItems: historiesLoaded ,
182+ filterClass: historyListFilters .value ,
183+ selectable: computed (() => myView .value ),
184+ onDelete : async (item ) => {
185+ const { onDeleteHistory } = useHistoryCardActions (
186+ computed (() => item ),
187+ false ,
188+ () => load (true ),
189+ );
190+ await onDeleteHistory ();
191+ },
192+ expectedKeyDownClass: " history-card" ,
193+ getAttributeForRangeSelection(item ) {
194+ return ` g-card-${item .id } ` ;
195+ },
196+ });
197+
153198/**
154199 * Updates a specific filter value in the current filter text
155200 * @param {string} filterKey - The key of the filter to update
@@ -252,16 +297,10 @@ function validatedFilterText(): string {
252297
253298/**
254299 * Toggles selection of a specific history item
255- * @param {SelectedHistory } h - The history object to toggle selection for
300+ * @param {AnyHistoryEntry } h - The history object to toggle selection for
256301 */
257- function onSelectHistory(h : SelectedHistory ) {
258- const index = selectedHistories .value .findIndex ((selected ) => selected .id === h .id );
259-
260- if (index === - 1 ) {
261- selectedHistories .value .push (h );
262- } else {
263- selectedHistories .value .splice (index , 1 );
264- }
302+ function onSelectHistory(h : AnyHistoryEntry ) {
303+ setSelected (h , ! isSelected (h ));
265304}
266305
267306/**
@@ -270,14 +309,9 @@ function onSelectHistory(h: SelectedHistory) {
270309 */
271310function onSelectAllHistories() {
272311 if (selectedHistories .value .length === historiesLoaded .value .length ) {
273- selectedHistories . value = [] ;
312+ resetSelection () ;
274313 } else {
275- selectedHistories .value = historiesLoaded .value .map ((h ) => ({
276- id: h .id ,
277- name: h .name ,
278- published: h .published ,
279- purged: h .purged ,
280- }));
314+ selectAllInCurrentQuery ();
281315 }
282316}
283317
@@ -326,15 +360,13 @@ async function onBulkDeleteOrPurge(purge: boolean = false) {
326360
327361 Toast .success (` ${purge ? " Purged" : " Deleted" } ${totalSelected } histories. ` );
328362
329- selectedHistories . value = [] ;
363+ resetSelection () ;
330364 } catch (e ) {
331365 Toast .error (` Failed to ${purge ? " purge" : " delete" } some histories. ` );
332366 } finally {
333367 bulkPurgeLoading .value = false ;
334368 bulkDeleteOrRestoreLoading .value = false ;
335369
336- selectedHistories .value = tmpSelected ;
337-
338370 await load (true );
339371 }
340372 }
@@ -374,14 +406,12 @@ async function onBulkRestore() {
374406
375407 Toast .success (` Restored ${totalSelected } histories. ` );
376408
377- selectedHistories . value = [] ;
409+ resetSelection () ;
378410 } catch (e ) {
379411 Toast .error (` Failed to restore some histories. ` );
380412 } finally {
381413 bulkDeleteOrRestoreLoading .value = false ;
382414
383- selectedHistories .value = tmpSelected ;
384-
385415 await load (true );
386416 }
387417 }
@@ -423,8 +453,6 @@ async function onBulkTagsAdd(tags: string[]) {
423453 } finally {
424454 bulkTagsLoading .value = false ;
425455
426- selectedHistories .value = tmpSelected ;
427-
428456 await load (true );
429457 }
430458}
@@ -436,7 +464,7 @@ async function onBulkTagsAdd(tags: string[]) {
436464watch ([filterText , sortBy , sortDesc ], async () => {
437465 offset .value = 0 ;
438466
439- selectedHistories . value = [] ;
467+ resetSelection () ;
440468
441469 await load (true );
442470});
@@ -597,8 +625,13 @@ onMounted(async () => {
597625 :grid-view =" currentListViewMode === 'grid'"
598626 :selectable =" myView"
599627 :selected-history-ids =" selectedHistories"
628+ :item-refs =" itemRefs"
629+ :range-select-anchor =" initSelectedItem"
630+ :clickable =" true"
600631 @refreshList =" load"
601632 @select =" onSelectHistory"
633+ @on-key-down =" onKeyDown"
634+ @on-history-card-click =" onClick"
602635 @updateFilter =" updateFilterValue"
603636 @tagClick =" (tag) => updateFilterValue('tag', `'${tag}'`)" />
604637 </BOverlay >
0 commit comments