Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"papaparse": "5.5.3",
"prism-react-renderer": "2.4.1",
"react": "19.1.1",
"react-accessible-treeview": "2.11.0",
"react-date-range": "2.0.1",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
Expand Down
18 changes: 0 additions & 18 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions src/components/tree/ExpandButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { JSX, MouseEventHandler } from 'react';

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

import ItemIcon from '@/ui/icons/ItemIcon';

import { PartialItemWithChildren } from './utils';

type Props = {
element: PartialItemWithChildren;
level: number;
isExpanded: boolean;
toggleExpand: (id: string) => void;
};

export function ExpandButton({
element,
level,
isExpanded,
toggleExpand,
}: Readonly<Props>): JSX.Element {
const handleExpandClick: MouseEventHandler = (e) => {
e.stopPropagation();
toggleExpand(element.id);
};

return (
<>
{/* icon type for root level items */}
{level === 0 && element.metadata?.type && (
<ItemIcon
size="17px"
alt="icon"
type={element.metadata.type}
mimetype={element.metadata.mimetype}
/>
)}
{level > 0 && (
<IconButton
onClick={handleExpandClick}
sx={{
p: 0,
'&:hover': {
opacity: 0.6,
},
}}
>
{/* lucid icons */}
{isExpanded ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#5050d2"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" />
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#5050d2"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
</svg>
)}
</IconButton>
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import { hooks } from '@/config/queryClient';
import { MAIN_MENU_ID, TREE_VIEW_ID } from '@/config/selectors';
import MainMenu from '@/ui/MainMenu/MainMenu';

import { LoadingTree } from './tree/LoadingTree';
import { TreeView } from './tree/TreeView';
import { combineUuids, shuffleAllButLastItemInArray } from './utils/shuffle';
import { LoadingTree } from './LoadingTree';
import { TreeView } from './TreeView';
import { combineUuids, shuffleAllButLastItemInArray } from './shuffle';

const { useItem, useDescendants } = hooks;

const DrawerNavigation = ({
export const ItemNavigation = ({
rootId,
itemId,
shuffle = false,
Expand Down Expand Up @@ -66,7 +66,6 @@ const DrawerNavigation = ({
id={TREE_VIEW_ID}
rootItems={[rootItem]}
items={[rootItem, ...shuffledDescendants]}
firstLevelStyle={{ fontWeight: 'bold' }}
onTreeItemSelect={handleNavigationOnClick}
itemId={itemId}
allowedTypes={types}
Expand All @@ -93,5 +92,3 @@ const DrawerNavigation = ({

return null;
};

export default DrawerNavigation;
143 changes: 143 additions & 0 deletions src/components/tree/TreeView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { type JSX, useState } from 'react';

import { Collapse, List, ListItemButton, Typography } from '@mui/material';

import { DiscriminatedItem, ItemType, getIdsFromPath } from '@graasp/sdk';

import { ErrorBoundary } from '@sentry/react';

import { buildTreeItemClass } from '@/config/selectors';

import { ExpandButton } from './ExpandButton';
import { TreeErrorBoundary } from './TreeErrorBoundary';
import { PartialItemWithChildren, buildItemsTree } from './utils';

export const GRAASP_MENU_ITEMS = [ItemType.FOLDER, ItemType.SHORTCUT];

function ListItem({
expandedIds,
selectedIds,
item,
// bug: to allow animation of collapse, the sidebar should avoid to re-render the whole tree on navigation
toggleExpand,
level = 0,
onClick,
}: Readonly<{
expandedIds: string[];
selectedIds: string[];
item: PartialItemWithChildren;
toggleExpand: (id: string) => void;
level?: number;
onClick?: (id: string) => void;
}>): JSX.Element {
const isExpanded = expandedIds.includes(item.id);
const isSelected = selectedIds.includes(item.id);

return (
<>
<ListItemButton
className={buildTreeItemClass(item.id)}
selected={isSelected}
sx={{
ml: 3 * level,
gap: 1,
py: 0,
fontWeight: isSelected || level === 0 ? 'bold' : 'normal',
}}
onClick={() => {
onClick?.(item.id);
if (!isExpanded) {
toggleExpand(item.id);
}
}}
>
{Boolean(item.children?.length) && (
<ExpandButton
element={item}
level={level}
isExpanded={isExpanded}
toggleExpand={toggleExpand}
/>
)}
{item.name}
</ListItemButton>
<Collapse in={isExpanded} timeout="auto">
<List component="div" disablePadding>
{item.children?.map((child) => (
<ListItem
key={child.id}
toggleExpand={toggleExpand}
selectedIds={selectedIds}
expandedIds={expandedIds}
item={child}
level={level + 1}
onClick={onClick}
/>
))}
</List>
</Collapse>
</>
);
}

type TreeViewProps = {
id: string;
header?: string;
rootItems: (DiscriminatedItem & { children?: DiscriminatedItem[] })[];
items?: (DiscriminatedItem & { children?: DiscriminatedItem[] })[];
onTreeItemSelect?: (value: string) => void;
itemId: string;
/**
* Item whose type is not in the list is filtered out. If the array is empty, no item is filtered.
*/
allowedTypes?: DiscriminatedItem['type'][];
};

export function TreeView({
id,
header,
items,
rootItems,
onTreeItemSelect,
allowedTypes = [],
itemId,
}: Readonly<TreeViewProps>): JSX.Element {
const itemsToShow = items?.filter((item) =>
allowedTypes.length ? allowedTypes.includes(item.type) : true,
);
const [expandedIds, setExpandedIds] = useState<string[]>(() => {
const focusedItem = itemsToShow?.find((i) => i.id === itemId);
const defaultExpandedIds = rootItems[0]?.id ? [rootItems[0].id] : [];
return focusedItem ? getIdsFromPath(focusedItem.path) : defaultExpandedIds;
});

const itemTree = buildItemsTree(itemsToShow ?? [], rootItems);
const tree = Object.values(itemTree);

const toggleExpand = (targetId: string) => {
setExpandedIds((prev) =>
prev.includes(targetId)
? prev.filter((expandedId) => expandedId !== targetId)
: [...prev, targetId],
);
};

return (
<ErrorBoundary fallback={<TreeErrorBoundary />}>
{header && (
<Typography sx={{ ml: 2, fontWeight: 'bold' }} variant="body1">
{header}
</Typography>
)}
<List id={id} dense component="nav">
<ListItem
item={tree[0]}
selectedIds={itemId ? [itemId] : []}
expandedIds={expandedIds}
toggleExpand={toggleExpand}
onClick={onTreeItemSelect}
/>
</List>
</ErrorBoundary>
);
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type ItemMetaData = {
mimetype?: string;
};

type PartialItemWithChildren = {
export type PartialItemWithChildren = {
id: string;
name: string;
metadata: ItemMetaData;
Expand Down
9 changes: 4 additions & 5 deletions src/modules/player/navigationIsland/PreviousNextButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ import { useParams, useSearch } from '@tanstack/react-router';
import { ChevronLeft, ChevronRight } from 'lucide-react';

import { useAuth } from '@/AuthContext';
import {
combineUuids,
shuffleAllButLastItemInArray,
} from '@/components/tree/shuffle';
import { hooks } from '@/config/queryClient';
import {
NEXT_ITEM_NAV_BUTTON_ID,
PREVIOUS_ITEM_NAV_BUTTON_ID,
} from '@/config/selectors';

import {
combineUuids,
shuffleAllButLastItemInArray,
} from '~player/utils/shuffle';

import { LoadingButton, NavigationButton } from './customButtons';

function getPrevious(
Expand Down
Loading