From af0011097324b59fae323b60bc6b09536e74d848 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 14 Nov 2025 13:00:21 -0800 Subject: [PATCH] enforce data order in list selection --- .../helpers/item-list-reducer-utils.ts | 12 ++-- .../item-list/helpers/item-list-state.ts | 60 ++++++++++++++----- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/renderer/components/item-list/helpers/item-list-reducer-utils.ts b/src/renderer/components/item-list/helpers/item-list-reducer-utils.ts index 4011845e0..58d5405e6 100644 --- a/src/renderer/components/item-list/helpers/item-list-reducer-utils.ts +++ b/src/renderer/components/item-list/helpers/item-list-reducer-utils.ts @@ -8,7 +8,7 @@ import { * Action creators for item grid state management * These can be reused across different components and contexts */ -export const itemGridActions = { +export const itemListActions = { clearAll: (): ItemListAction => ({ type: 'CLEAR_ALL', }), @@ -71,7 +71,7 @@ export const itemGridActions = { * Selector functions for item grid state * These can be reused to extract specific data from state */ -export const itemGridSelectors = { +export const itemListSelectors = { getDragging: (state: ItemListState): unknown[] => { return Array.from(state.draggingItems.values()); }, @@ -179,8 +179,8 @@ export const itemListUtils = { return rowId ? currentState.expanded.has(rowId) : false; }); return allExpanded - ? itemGridActions.clearExpanded() - : itemGridActions.setExpanded(items, extractRowId); + ? itemListActions.clearExpanded() + : itemListActions.setExpanded(items, extractRowId); }, /** @@ -196,7 +196,7 @@ export const itemListUtils = { return rowId ? currentState.selected.has(rowId) : false; }); return allSelected - ? itemGridActions.clearSelected() - : itemGridActions.setSelected(items, extractRowId); + ? itemListActions.clearSelected() + : itemListActions.setSelected(items, extractRowId); }, }; diff --git a/src/renderer/components/item-list/helpers/item-list-state.ts b/src/renderer/components/item-list/helpers/item-list-state.ts index abdcbcbeb..1d3607bac 100644 --- a/src/renderer/components/item-list/helpers/item-list-state.ts +++ b/src/renderer/components/item-list/helpers/item-list-state.ts @@ -1,8 +1,36 @@ import { useCallback, useMemo, useReducer } from 'react'; -import { itemGridSelectors } from '/@/renderer/components/item-list/helpers/item-list-reducer-utils'; +import { itemListSelectors } from '/@/renderer/components/item-list/helpers/item-list-reducer-utils'; import { LibraryItem } from '/@/shared/types/domain-types'; +const sortByDataOrder = ( + items: T[], + data: unknown[], + extractRowId: (item: unknown) => string | undefined, + isIdArray: boolean, +): T[] => { + const rowIdToIndex = new Map(); + + // Create a map of rowId to index in the data array + data.forEach((item, index) => { + if (item && typeof item === 'object') { + const itemRowId = extractRowId(item); + if (itemRowId) { + rowIdToIndex.set(itemRowId, index); + } + } + }); + + // Sort items by their index in the data array (create new array to avoid mutation) + return [...items].sort((a, b) => { + const rowIdA = isIdArray ? (a as string) : extractRowId(a as unknown); + const rowIdB = isIdArray ? (b as string) : extractRowId(b as unknown); + const indexA = rowIdA ? (rowIdToIndex.get(rowIdA) ?? Infinity) : Infinity; + const indexB = rowIdB ? (rowIdToIndex.get(rowIdB) ?? Infinity) : Infinity; + return indexA - indexB; + }); +}; + export type ItemListAction = | { extractRowId: (item: unknown) => string | undefined; @@ -330,29 +358,31 @@ export const useItemListState = ( const isExpanded = useCallback( (rowId: string) => { - return itemGridSelectors.isExpanded(state, rowId); + return itemListSelectors.isExpanded(state, rowId); }, [state], ); const isSelected = useCallback( (rowId: string) => { - return itemGridSelectors.isSelected(state, rowId); + return itemListSelectors.isSelected(state, rowId); }, [state], ); const getExpanded = useCallback(() => { - return itemGridSelectors.getExpanded(state); + return itemListSelectors.getExpanded(state); }, [state]); const getDragging = useCallback(() => { - return itemGridSelectors.getDragging(state); + return itemListSelectors.getDragging(state); }, [state]); const getSelected = useCallback(() => { - return itemGridSelectors.getSelected(state); - }, [state]); + const selectedItems = itemListSelectors.getSelected(state); + const data = getDataFn ? getDataFn() : []; + return sortByDataOrder(selectedItems, data, extractRowIdFn, false); + }, [state, getDataFn, extractRowIdFn]); const getDraggingIds = useCallback(() => { return Array.from(state.dragging); @@ -363,8 +393,10 @@ export const useItemListState = ( }, [state.expanded]); const getSelectedIds = useCallback(() => { - return Array.from(state.selected); - }, [state.selected]); + const selectedIds = Array.from(state.selected); + const data = getDataFn ? getDataFn() : []; + return sortByDataOrder(selectedIds, data, extractRowIdFn, true); + }, [state.selected, getDataFn, extractRowIdFn]); const clearExpanded = useCallback(() => { dispatch({ type: 'CLEAR_EXPANDED' }); @@ -383,24 +415,24 @@ export const useItemListState = ( }, []); const getVersion = useCallback(() => { - return itemGridSelectors.getVersion(state); + return itemListSelectors.getVersion(state); }, [state]); const hasExpanded = useCallback(() => { - return itemGridSelectors.hasAnyExpanded(state); + return itemListSelectors.hasAnyExpanded(state); }, [state]); const hasDragging = useCallback(() => { - return itemGridSelectors.hasAnyDragging(state); + return itemListSelectors.hasAnyDragging(state); }, [state]); const hasSelected = useCallback(() => { - return itemGridSelectors.hasAnySelected(state); + return itemListSelectors.hasAnySelected(state); }, [state]); const isDragging = useCallback( (rowId: string) => { - return itemGridSelectors.isDragging(state, rowId); + return itemListSelectors.isDragging(state, rowId); }, [state], );