Skip to content

Commit

Permalink
fix: define container for drag selection (#1481)
Browse files Browse the repository at this point in the history
* fix: define container for drag selection

* refactor: apply PR requested changes
  • Loading branch information
pyphilia authored Sep 27, 2024
1 parent a353659 commit 62081e2
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 68 deletions.
47 changes: 27 additions & 20 deletions src/components/item/FolderContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '../main/list/SelectionContext';
import { useDragSelection } from '../main/list/useDragSelection';
import {
DragContainerStack,
useDragSelection,
} from '../main/list/useDragSelection';
import { DesktopMap } from '../map/DesktopMap';
import NoItemFilters from '../pages/NoItemFilters';
import { OutletType } from '../pages/item/type';
Expand All @@ -49,6 +52,8 @@ type Props = {
canWrite?: boolean;
};

const CONTAINER_ID = 'items-container-id';

const Content = ({
item,
searchText,
Expand All @@ -60,7 +65,7 @@ const Content = ({
const { itemTypes } = useFilterItemsContext();
const { selectedIds, clearSelection, toggleSelection } =
useSelectionContext();
const DragSelection = useDragSelection();
const DragSelection = useDragSelection({ containerId: CONTAINER_ID });

if (mode === ItemLayoutMode.Map) {
return (
Expand All @@ -73,24 +78,26 @@ const Content = ({
if (items?.length) {
return (
<>
<ItemsTable
selectedIds={selectedIds}
enableMoveInBetween={sortBy === SortingOptionsForFolder.Order}
id={buildItemsTableId(item.id)}
items={items ?? []}
onCardClick={toggleSelection}
onMove={clearSelection}
/>
{Boolean(canWrite && !searchText && !itemTypes?.length) && (
<Stack alignItems="center" mb={2}>
<NewItemButton
type="icon"
key="newButton"
// add new items at the end of the list
previousItemId={items ? items[items.length - 1]?.id : undefined}
/>
</Stack>
)}
<DragContainerStack id={CONTAINER_ID}>
<ItemsTable
selectedIds={selectedIds}
enableMoveInBetween={sortBy === SortingOptionsForFolder.Order}
id={buildItemsTableId(item.id)}
items={items ?? []}
onCardClick={toggleSelection}
onMove={clearSelection}
/>
{Boolean(canWrite && !searchText && !itemTypes?.length) && (
<Stack alignItems="center" mb={2}>
<NewItemButton
type="icon"
key="newButton"
// add new items at the end of the list
previousItemId={items ? items[items.length - 1]?.id : undefined}
/>
</Stack>
)}
</DragContainerStack>
{DragSelection}
</>
);
Expand Down
11 changes: 8 additions & 3 deletions src/components/main/list/ItemsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,16 @@ const ItemsTable = ({
return;
}

// silent error, happens when you want to cancel the operation
if (movedItem.id === targetItem.id) {
console.error('cannot move target into itself');
return;
}

// cannot move item into itself, or target cannot be part of selection if moving selection
if (
movedItem.id === targetItem.id ||
(selectedIds.includes(movedItem?.id) &&
selectedIds.includes(targetItem.id))
selectedIds.includes(movedItem?.id) &&
selectedIds.includes(targetItem.id)
) {
toast.error(translateMessage(FAILURE_MESSAGES.INVALID_MOVE_TARGET));
return;
Expand Down
6 changes: 5 additions & 1 deletion src/components/main/list/SelectionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
useState,
} from 'react';

import { Stack } from '@mui/material';

type SelectionContextValue = {
selectedIds: string[];
toggleSelection: (id: string) => void;
Expand Down Expand Up @@ -74,7 +76,9 @@ export const SelectionContextProvider = ({

return (
<SelectionContext.Provider value={value}>
<div ref={elementsContainerRef}>{children}</div>
<Stack height="100%" ref={elementsContainerRef}>
{children}
</Stack>
</SelectionContext.Provider>
);
};
Expand Down
39 changes: 36 additions & 3 deletions src/components/main/list/useDragSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { useState } from 'react';
import { ReactNode, useState } from 'react';

import { Stack } from '@mui/material';

import { PRIMARY_COLOR } from '@graasp/ui';

Expand All @@ -12,9 +14,35 @@ import { ITEM_CARD_CLASS } from '@/config/selectors';

import { useSelectionContext } from './SelectionContext';

export const DragContainerStack = ({
gap,
id,
children,
}: {
gap?: number;
id: string;
children: ReactNode;
}): JSX.Element => (
<Stack
// this is a hack to allow selection dragging from margin
// 100 is artbitrary big
mx={-100}
px={100}
height="100%"
id={id}
gap={gap}
>
{children}
</Stack>
);

export const useDragSelection = ({
elementClass = ITEM_CARD_CLASS,
} = {}): JSX.Element => {
containerId,
}: {
containerId: string;
elementClass?: string;
}): JSX.Element => {
const { addToSelection, clearSelection } = useSelectionContext();
const [boundingBox, setBoundingBox] = useState<null | {
top: number;
Expand Down Expand Up @@ -52,8 +80,13 @@ export const useDragSelection = ({
);
},
shouldStartSelecting: (e) => {
// does not trigger drag selection if mousedown on card
if (e instanceof HTMLElement || e instanceof SVGElement) {
// does not trigger if click is outside of container
if (!e.closest(`#${containerId}`)) {
return false;
}

// does not trigger drag selection if mousedown on card
return !e?.closest(`.${elementClass}`);
}
return true;
Expand Down
25 changes: 18 additions & 7 deletions src/components/pages/PageWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ const PageWrapper = ({
<Helmet>
<title>{title}</title>
</Helmet>
<Container id={id} sx={{ my: 2 }}>
<Stack mb={2} direction="row" justifyContent="space-between" spacing={1}>
<Typography variant="h2" component="h1" sx={{ wordWrap: 'break-word' }}>
{title}
</Typography>
{options}
<Container id={id} sx={{ pt: 1, height: '100%' }}>
<Stack height="100%">
<Stack
mb={2}
direction="row"
justifyContent="space-between"
spacing={1}
>
<Typography
variant="h2"
component="h1"
sx={{ wordWrap: 'break-word' }}
>
{title}
</Typography>
{options}
</Stack>
{children}
</Stack>
{children}
</Container>
</>
);
Expand Down
65 changes: 36 additions & 29 deletions src/components/pages/RecycledItemsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '../main/list/SelectionContext';
import { useDragSelection } from '../main/list/useDragSelection';
import {
DragContainerStack,
useDragSelection,
} from '../main/list/useDragSelection';
import ItemCard from '../table/ItemCard';
import SortingSelect from '../table/SortingSelect';
import { SortingOptions } from '../table/types';
import { useSorting, useTranslatedSortingOptions } from '../table/useSorting';
import PageWrapper from './PageWrapper';
import RecycleBinToolbar from './recycleBin/RecycleBinSelectionToolbar';

const CONTAINER_ID = 'recycle-items-container';

const RecycledItemsScreenContent = ({
searchText,
}: {
Expand All @@ -52,14 +57,14 @@ const RecycledItemsScreenContent = ({
?.sort(sortFn);
const { selectedIds, toggleSelection } = useSelectionContext();

const DragSelection = useDragSelection();
const DragSelection = useDragSelection({ containerId: CONTAINER_ID });

// render this when there is data from the query
if (recycledItems?.length) {
const hasSelection = selectedIds.length && filteredData?.length;
return (
<>
<Stack gap={1}>
<Stack gap={1} height="100%">
<Stack
alignItems="space-between"
direction="column"
Expand Down Expand Up @@ -91,32 +96,34 @@ const RecycledItemsScreenContent = ({
</Stack>
)}
</Stack>
{
// render the filtered data and when it is empty display that nothing matches the search
filteredData?.length ? (
filteredData.map((item) => (
<ItemCard
item={item}
onThumbnailClick={() => toggleSelection(item.id)}
isSelected={selectedIds.includes(item.id)}
showThumbnail={false}
allowNavigation={false}
footer={
<Stack justifyContent="right" direction="row">
<RestoreButton itemIds={[item.id]} />
<DeleteButton items={[item]} />
</Stack>
}
/>
))
) : (
<Alert severity="info">
{translateBuilder(BUILDER.TRASH_NO_ITEM_SEARCH, {
search: searchText,
})}
</Alert>
)
}
<DragContainerStack id={CONTAINER_ID}>
{
// render the filtered data and when it is empty display that nothing matches the search
filteredData?.length ? (
filteredData.map((item) => (
<ItemCard
item={item}
onThumbnailClick={() => toggleSelection(item.id)}
isSelected={selectedIds.includes(item.id)}
showThumbnail={false}
allowNavigation={false}
footer={
<Stack justifyContent="right" direction="row">
<RestoreButton itemIds={[item.id]} />
<DeleteButton items={[item]} />
</Stack>
}
/>
))
) : (
<Alert severity="info">
{translateBuilder(BUILDER.TRASH_NO_ITEM_SEARCH, {
search: searchText,
})}
</Alert>
)
}
</DragContainerStack>
</Stack>
{DragSelection}
</>
Expand Down
15 changes: 10 additions & 5 deletions src/components/pages/home/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import {
SelectionContextProvider,
useSelectionContext,
} from '@/components/main/list/SelectionContext';
import { useDragSelection } from '@/components/main/list/useDragSelection';
import {
DragContainerStack,
useDragSelection,
} from '@/components/main/list/useDragSelection';
import { ITEM_PAGE_SIZE } from '@/config/constants';
import { ShowOnlyMeChangeType } from '@/config/types';
import { ItemLayoutMode, Ordering } from '@/enums';
Expand Down Expand Up @@ -46,6 +49,8 @@ import NoItemFilters from '../NoItemFilters';
import PageWrapper from '../PageWrapper';
import HomeSelectionToolbar from './HomeSelectionToolbar';

const CONTAINER_ID = 'home-items-container';

const HomeScreenContent = ({ searchText }: { searchText: string }) => {
const { t: translateBuilder } = useBuilderTranslation();
const { t: translateEnums } = useEnumsTranslation();
Expand Down Expand Up @@ -75,7 +80,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
{ pageSize: ITEM_PAGE_SIZE },
);

const DragSelection = useDragSelection();
const DragSelection = useDragSelection({ containerId: CONTAINER_ID });

const onShowOnlyMeChange: ShowOnlyMeChangeType = (checked) => {
setShowOnlyMe(checked);
Expand Down Expand Up @@ -105,7 +110,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
? data.pages.map(({ data: d }) => d.length).reduce((a, b) => a + b, 0)
: 0;
content = (
<>
<DragContainerStack id={CONTAINER_ID}>
<ItemsTable
canMove={!searchText}
id={ACCESSIBLE_ITEMS_TABLE_ID}
Expand All @@ -128,7 +133,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
<NewItemButton type="icon" />
</Stack>
)}
</>
</DragContainerStack>
);
} else if (itemTypes.length || searchText) {
content = <NoItemFilters searchText={searchText} />;
Expand Down Expand Up @@ -182,7 +187,7 @@ const HomeScreenContent = ({ searchText }: { searchText: string }) => {
</Stack>
)}
</Stack>
<Stack>
<Stack height="100%">
{content}
{data && isFetching && (
<Box sx={{ width: '100%' }}>
Expand Down

0 comments on commit 62081e2

Please sign in to comment.