mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-10-01 05:09:47 +02:00
fix: support bidirectional shift+click selection in library items (#10034)
* fix: support bidirectional shift+click selection in library items - Enable bottom-up multi-selection (previously only top-down worked) - Use Math.min/max to handle selection range in both directions - Maintains existing behavior for preserving non-contiguous selections - Fixes issue where shift+clicking items above last selected item failed * improve deselection behavior --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
@@ -281,19 +281,29 @@ export const LibraryMenu = memo(() => {
|
||||
if (target.closest(`.${CLASSES.SIDEBAR}`)) {
|
||||
// stop propagation so that we don't prevent it downstream
|
||||
// (default browser behavior is to clear search input on ESC)
|
||||
event.stopPropagation();
|
||||
if (selectedItems.length > 0) {
|
||||
event.stopPropagation();
|
||||
setSelectedItems([]);
|
||||
} else if (
|
||||
isWritableElement(target) &&
|
||||
target instanceof HTMLInputElement &&
|
||||
!target.value
|
||||
) {
|
||||
event.stopPropagation();
|
||||
// if search input empty -> close library
|
||||
// (maybe not a good idea?)
|
||||
setAppState({ openSidebar: null });
|
||||
app.focusContainer();
|
||||
}
|
||||
} else if (selectedItems.length > 0) {
|
||||
const { x, y } = app.lastViewportPosition;
|
||||
const elementUnderCursor = document.elementFromPoint(x, y);
|
||||
// also deselect elements if sidebar doesn't have focus but the
|
||||
// cursor is over it
|
||||
if (elementUnderCursor?.closest(`.${CLASSES.SIDEBAR}`)) {
|
||||
event.stopPropagation();
|
||||
setSelectedItems([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -138,10 +138,13 @@ export default function LibraryMenuItems({
|
||||
}
|
||||
|
||||
const selectedItemsMap = arrayToMap(selectedItems);
|
||||
// Support both top-down and bottom-up selection by using min/max
|
||||
const minRange = Math.min(rangeStart, rangeEnd);
|
||||
const maxRange = Math.max(rangeStart, rangeEnd);
|
||||
const nextSelectedIds = orderedItems.reduce(
|
||||
(acc: LibraryItem["id"][], item, idx) => {
|
||||
if (
|
||||
(idx >= rangeStart && idx <= rangeEnd) ||
|
||||
(idx >= minRange && idx <= maxRange) ||
|
||||
selectedItemsMap.has(item.id)
|
||||
) {
|
||||
acc.push(item.id);
|
||||
@@ -169,6 +172,14 @@ export default function LibraryMenuItems({
|
||||
],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// if selection is removed (e.g. via esc), reset last selected item
|
||||
// so that subsequent shift+clicks don't select a large range
|
||||
if (!selectedItems.length) {
|
||||
setLastSelectedItem(null);
|
||||
}
|
||||
}, [selectedItems]);
|
||||
|
||||
const getInsertedElements = useCallback(
|
||||
(id: string) => {
|
||||
let targetElements;
|
||||
|
Reference in New Issue
Block a user