diff --git a/src/components/item/FolderContent.tsx b/src/components/item/FolderContent.tsx
index 5d01d9590..3d2a03563 100644
--- a/src/components/item/FolderContent.tsx
+++ b/src/components/item/FolderContent.tsx
@@ -1,4 +1,4 @@
-import { Alert, Box, Stack, Typography } from '@mui/material';
+import { Alert, Box, Stack, Typography, useTheme } from '@mui/material';
import {
PackedItem,
@@ -27,6 +27,7 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '../main/list/SelectionContext';
+import { useDragSelection } from '../main/list/useDragSelection';
import { DesktopMap } from '../map/DesktopMap';
import NoItemFilters from '../pages/NoItemFilters';
import SortingSelect from '../table/SortingSelect';
@@ -49,6 +50,10 @@ const Content = ({ item, searchText, items, sortBy }: Props) => {
const { itemTypes } = useFilterItemsContext();
const { selectedIds, clearSelection, toggleSelection } =
useSelectionContext();
+ const theme = useTheme();
+ const DragSelection = useDragSelection({
+ adjustments: { marginTop: 70, marginLeft: theme.spacing(3) },
+ });
const enableEditing = item.permission
? PermissionLevelCompare.lte(PermissionLevel.Write, item.permission)
@@ -86,6 +91,7 @@ const Content = ({ item, searchText, items, sortBy }: Props) => {
/>
)}
+
>
);
}
diff --git a/src/components/main/list/SelectionContext.tsx b/src/components/main/list/SelectionContext.tsx
index 61051cf61..4101847a5 100644
--- a/src/components/main/list/SelectionContext.tsx
+++ b/src/components/main/list/SelectionContext.tsx
@@ -7,34 +7,24 @@ import {
useState,
} from 'react';
-import { PRIMARY_COLOR } from '@graasp/ui';
-
-import {
- Box,
- boxesIntersect,
- useSelectionContainer,
-} from '@air/react-drag-to-select';
-
-import { ITEM_CARD_CLASS } from '@/config/selectors';
-
type SelectionContextValue = {
selectedIds: string[];
toggleSelection: (id: string) => void;
+ addToSelection: (id: string) => void;
clearSelection: () => void;
};
export const SelectionContext = createContext({
selectedIds: [],
toggleSelection: () => {},
+ addToSelection: () => {},
clearSelection: () => {},
});
export const SelectionContextProvider = ({
children,
- elementClass = ITEM_CARD_CLASS,
}: {
children: JSX.Element;
- elementClass?: string;
}): JSX.Element => {
const [selection, setSelection] = useState(new Set());
const elementsContainerRef = useRef(null);
@@ -55,58 +45,15 @@ export const SelectionContextProvider = ({
[selection],
);
- const { DragSelection } = useSelectionContainer({
- eventsElement: document.getElementById('root'),
- onSelectionChange: (box) => {
- /**
- * Here we make sure to adjust the box's left and top with the scroll position of the window
- * @see https://github.com/AirLabsTeam/react-drag-to-select/#scrolling
- */
- const scrollAwareBox: Box = {
- ...box,
- top: box.top + window.scrollY,
- left: box.left + window.scrollX,
- };
-
- Array.from(document.getElementsByClassName(elementClass)).forEach(
- (item) => {
- const bb = item.getBoundingClientRect();
- if (
- boxesIntersect(scrollAwareBox, bb) &&
- item.parentNode instanceof HTMLElement
- ) {
- const itemId = item.parentNode.dataset.id;
- if (itemId) {
- selection.add(itemId);
- }
- }
- },
- );
-
- setSelection(new Set(selection));
- },
- shouldStartSelecting: (e) => {
- // does not trigger drag selection if mousedown on card
- if (e instanceof HTMLElement) {
- return !e?.closest(`.${ITEM_CARD_CLASS}`);
+ const addToSelection = useCallback(
+ (id: string) => {
+ if (!selection.has(id)) {
+ selection.add(id);
+ setSelection(new Set(selection));
}
- return true;
- },
- onSelectionStart: () => {
- // clear selection on new dragging action
- clearSelection();
},
- onSelectionEnd: () => {},
- selectionProps: {
- style: {
- border: `2px dashed ${PRIMARY_COLOR}`,
- borderRadius: 4,
- backgroundColor: 'lightblue',
- opacity: 0.5,
- },
- },
- isEnabled: true,
- });
+ [selection],
+ );
const value: SelectionContextValue = useMemo(
() => ({
@@ -114,13 +61,19 @@ export const SelectionContextProvider = ({
toggleSelection,
clearSelection,
elementsContainerRef,
+ addToSelection,
}),
- [selection, toggleSelection, clearSelection, elementsContainerRef],
+ [
+ selection,
+ toggleSelection,
+ clearSelection,
+ addToSelection,
+ elementsContainerRef,
+ ],
);
return (
-
{children}
);
diff --git a/src/components/main/list/useDragSelection.tsx b/src/components/main/list/useDragSelection.tsx
new file mode 100644
index 000000000..a0e7612d3
--- /dev/null
+++ b/src/components/main/list/useDragSelection.tsx
@@ -0,0 +1,76 @@
+import { ReactElement } from 'react';
+
+import { PRIMARY_COLOR } from '@graasp/ui';
+
+import {
+ Box,
+ boxesIntersect,
+ useSelectionContainer,
+} from '@air/react-drag-to-select';
+
+import { ITEM_CARD_CLASS } from '@/config/selectors';
+
+import { useSelectionContext } from './SelectionContext';
+
+export const useDragSelection = ({
+ elementClass = ITEM_CARD_CLASS,
+ adjustments = {},
+} = {}): (() => ReactElement) => {
+ const { addToSelection, clearSelection } = useSelectionContext();
+
+ const { DragSelection } = useSelectionContainer({
+ eventsElement: document.getElementById('root'),
+ onSelectionChange: (box) => {
+ /**
+ * Here we make sure to adjust the box's left and top with the scroll position of the window
+ * @see https://github.com/AirLabsTeam/react-drag-to-select/#scrolling
+ */
+ const scrollAwareBox: Box = {
+ ...box,
+ top: box.top + window.scrollY,
+ left: box.left + window.scrollX,
+ };
+
+ Array.from(document.getElementsByClassName(elementClass)).forEach(
+ (item) => {
+ const bb = item.getBoundingClientRect();
+ if (
+ boxesIntersect(scrollAwareBox, bb) &&
+ item.parentNode instanceof HTMLElement
+ ) {
+ const itemId = item.parentNode.dataset.id;
+ if (itemId) {
+ addToSelection(itemId);
+ }
+ }
+ },
+ );
+ },
+ shouldStartSelecting: (e) => {
+ // does not trigger drag selection if mousedown on card
+ if (e instanceof HTMLElement) {
+ return !e?.closest(`.${elementClass}`);
+ }
+ return true;
+ },
+ onSelectionStart: () => {
+ // clear selection on new dragging action
+ clearSelection();
+ },
+ onSelectionEnd: () => {},
+ selectionProps: {
+ style: {
+ // adjustement
+ // https://github.com/AirLabsTeam/react-drag-to-select/issues/30
+ ...adjustments,
+ border: `2px dashed ${PRIMARY_COLOR}`,
+ borderRadius: 4,
+ backgroundColor: 'lightblue',
+ opacity: 0.5,
+ },
+ },
+ isEnabled: true,
+ });
+
+ return DragSelection;
+};
diff --git a/src/components/pages/RecycledItemsScreen.tsx b/src/components/pages/RecycledItemsScreen.tsx
index d32b42b77..40f567522 100644
--- a/src/components/pages/RecycledItemsScreen.tsx
+++ b/src/components/pages/RecycledItemsScreen.tsx
@@ -1,4 +1,4 @@
-import { Alert, Stack } from '@mui/material';
+import { Alert, Stack, useTheme } from '@mui/material';
import { Ordering } from '@/enums';
@@ -21,6 +21,7 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '../main/list/SelectionContext';
+import { useDragSelection } from '../main/list/useDragSelection';
import ItemCard from '../table/ItemCard';
import SortingSelect from '../table/SortingSelect';
import { SortingOptions } from '../table/types';
@@ -37,6 +38,7 @@ const RecycledItemsScreenContent = ({
const { data: recycledItems, isLoading, isError } = hooks.useRecycledItems();
const options = useTranslatedSortingOptions();
const { shouldDisplayItem } = useFilterItemsContext();
+ const theme = useTheme();
const { sortBy, setSortBy, ordering, setOrdering, sortFn } =
useSorting({
sortBy: SortingOptions.ItemUpdatedAt,
@@ -51,70 +53,77 @@ const RecycledItemsScreenContent = ({
?.sort(sortFn);
const { selectedIds, toggleSelection } = useSelectionContext();
+ const DragSelection = useDragSelection({
+ adjustments: { marginTop: 70, marginLeft: theme.spacing(3) },
+ });
+
// render this when there is data from the query
if (recycledItems?.length) {
const hasSelection = selectedIds.length && filteredData?.length;
return (
-
-
- {hasSelection ? (
-
- ) : (
-
-
-
- {sortBy && setSortBy && (
-
- )}
-
+ <>
+
+
+ {hasSelection ? (
+
+ ) : (
+
+
+
+ {sortBy && setSortBy && (
+
+ )}
+
+
-
- )}
+ )}
+
+ {
+ // render the filtered data and when it is empty display that nothing matches the search
+ filteredData?.length ? (
+ filteredData.map((item) => (
+ toggleSelection(item.id)}
+ isSelected={selectedIds.includes(item.id)}
+ showThumbnail={false}
+ allowNavigation={false}
+ footer={
+
+
+
+
+ }
+ />
+ ))
+ ) : (
+
+ {translateBuilder(BUILDER.TRASH_NO_ITEM_SEARCH, {
+ search: searchText,
+ })}
+
+ )
+ }
- {
- // render the filtered data and when it is empty display that nothing matches the search
- filteredData?.length ? (
- filteredData.map((item) => (
- toggleSelection(item.id)}
- isSelected={selectedIds.includes(item.id)}
- showThumbnail={false}
- allowNavigation={false}
- footer={
-
-
-
-
- }
- />
- ))
- ) : (
-
- {translateBuilder(BUILDER.TRASH_NO_ITEM_SEARCH, {
- search: searchText,
- })}
-
- )
- }
-
+
+ >
);
}
diff --git a/src/components/pages/home/HomeScreen.tsx b/src/components/pages/home/HomeScreen.tsx
index 0ad0f113b..f175f489b 100644
--- a/src/components/pages/home/HomeScreen.tsx
+++ b/src/components/pages/home/HomeScreen.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { Alert, Box, LinearProgress, Stack } from '@mui/material';
+import { Alert, Box, LinearProgress, Stack, useTheme } from '@mui/material';
import { Button } from '@graasp/ui';
@@ -9,6 +9,7 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '@/components/main/list/SelectionContext';
+import { useDragSelection } from '@/components/main/list/useDragSelection';
import { ITEM_PAGE_SIZE } from '@/config/constants';
import { ShowOnlyMeChangeType } from '@/config/types';
import { ItemLayoutMode, Ordering } from '@/enums';
@@ -43,6 +44,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
const { data: currentMember } = hooks.useCurrentMember();
const { itemTypes } = useFilterItemsContext();
const [showOnlyMe, setShowOnlyMe] = useState(false);
+ const theme = useTheme();
const { selectedIds, toggleSelection, clearSelection } =
useSelectionContext();
@@ -66,6 +68,10 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
{ pageSize: ITEM_PAGE_SIZE },
);
+ const DragSelection = useDragSelection({
+ adjustments: { marginTop: 100, marginLeft: theme.spacing(3) },
+ });
+
const onShowOnlyMeChange: ShowOnlyMeChangeType = (checked) => {
setShowOnlyMe(checked);
};
@@ -129,6 +135,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
return (
<>
+