diff --git a/cypress/e2e/item/copy/gridCopyItem.cy.ts b/cypress/e2e/item/copy/gridCopyItem.cy.ts
index 967f6f478..59b4fc29d 100644
--- a/cypress/e2e/item/copy/gridCopyItem.cy.ts
+++ b/cypress/e2e/item/copy/gridCopyItem.cy.ts
@@ -1,7 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
- HOME_MODAL_ITEM_ID,
ITEM_MENU_COPY_BUTTON_CLASS,
+ MY_GRAASP_ITEM_PATH,
buildItemCard,
buildItemMenu,
buildItemMenuButtonId,
@@ -9,11 +9,19 @@ import {
import { ITEM_LAYOUT_MODES } from '../../../../src/enums';
import { SAMPLE_ITEMS } from '../../../fixtures/items';
-const copyItem = ({ id, toItemPath }: { id: string; toItemPath: string }) => {
+const copyItem = ({
+ id,
+ toItemPath,
+ rootId,
+}: {
+ id: string;
+ toItemPath: string;
+ rootId?: string;
+}) => {
const menuSelector = `#${buildItemMenuButtonId(id)}`;
cy.get(menuSelector).click();
cy.get(`#${buildItemMenu(id)} .${ITEM_MENU_COPY_BUTTON_CLASS}`).click();
- cy.fillTreeModal(toItemPath);
+ cy.handleTreeMenu(toItemPath, rootId);
};
describe('Copy Item in Grid', () => {
@@ -63,7 +71,7 @@ describe('Copy Item in Grid', () => {
// copy
const { id: copyItemId } = SAMPLE_ITEMS.items[2];
- const toItemPath = HOME_MODAL_ITEM_ID;
+ const toItemPath = MY_GRAASP_ITEM_PATH;
copyItem({ id: copyItemId, toItemPath });
cy.wait('@copyItems').then(({ request: { url } }) => {
diff --git a/cypress/e2e/item/copy/listCopyItem.cy.ts b/cypress/e2e/item/copy/listCopyItem.cy.ts
index 023e4998f..aaefe8e10 100644
--- a/cypress/e2e/item/copy/listCopyItem.cy.ts
+++ b/cypress/e2e/item/copy/listCopyItem.cy.ts
@@ -1,19 +1,13 @@
+import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
- HOME_PATH,
- SHARED_ITEMS_PATH,
- buildItemPath,
-} from '../../../../src/config/paths';
-import {
- HOME_MODAL_ITEM_ID,
ITEM_MENU_COPY_BUTTON_CLASS,
- TREE_MODAL_SHARED_ITEMS_ID,
+ MY_GRAASP_ITEM_PATH,
buildItemMenu,
buildItemMenuButtonId,
buildItemsTableRowIdAttribute,
} from '../../../../src/config/selectors';
import ITEM_LAYOUT_MODES from '../../../../src/enums/itemLayoutModes';
import { SAMPLE_ITEMS } from '../../../fixtures/items';
-import { SHARED_ITEMS } from '../../../fixtures/sharedItems';
const copyItem = ({
id,
@@ -26,7 +20,7 @@ const copyItem = ({
}) => {
cy.get(`#${buildItemMenuButtonId(id)}`).click();
cy.get(`#${buildItemMenu(id)} .${ITEM_MENU_COPY_BUTTON_CLASS}`).click();
- cy.fillTreeModal(toItemPath, rootId);
+ cy.handleTreeMenu(toItemPath, rootId);
};
describe('Copy Item in List', () => {
@@ -76,7 +70,7 @@ describe('Copy Item in List', () => {
// copy
const { id: copyItemId } = SAMPLE_ITEMS.items[2];
- copyItem({ id: copyItemId, toItemPath: HOME_MODAL_ITEM_ID });
+ copyItem({ id: copyItemId, toItemPath: MY_GRAASP_ITEM_PATH });
cy.wait('@copyItems').then(({ request: { url } }) => {
expect(url).to.contain(copyItemId);
@@ -84,25 +78,4 @@ describe('Copy Item in List', () => {
cy.get(buildItemsTableRowIdAttribute(copyItemId)).should('exist');
});
});
-
- it('copy item in a shared item', () => {
- cy.setUpApi(SHARED_ITEMS);
- const { path } = SHARED_ITEMS.items[0];
-
- // go to children item
- cy.visit(SHARED_ITEMS_PATH);
- cy.switchMode(ITEM_LAYOUT_MODES.LIST);
-
- // copy
- const { id: copyItemId } = SHARED_ITEMS.items[1];
- copyItem({
- id: copyItemId,
- toItemPath: path,
- rootId: TREE_MODAL_SHARED_ITEMS_ID,
- });
-
- cy.wait('@copyItems').then(() => {
- cy.get(buildItemsTableRowIdAttribute(copyItemId)).should('exist');
- });
- });
});
diff --git a/cypress/e2e/item/copy/listCopyMultiple.cy.ts b/cypress/e2e/item/copy/listCopyMultiple.cy.ts
index ee6e14f8d..a8d7f742c 100644
--- a/cypress/e2e/item/copy/listCopyMultiple.cy.ts
+++ b/cypress/e2e/item/copy/listCopyMultiple.cy.ts
@@ -1,7 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
- HOME_MODAL_ITEM_ID,
ITEMS_TABLE_COPY_SELECTED_ITEMS_ID,
+ MY_GRAASP_ITEM_PATH,
buildItemsTableRowIdAttribute,
} from '../../../../src/config/selectors';
import ITEM_LAYOUT_MODES from '../../../../src/enums/itemLayoutModes';
@@ -10,9 +10,11 @@ import { SAMPLE_ITEMS } from '../../../fixtures/items';
const copyItems = ({
itemIds,
toItemPath,
+ rootId,
}: {
itemIds: string[];
toItemPath: string;
+ rootId?: string;
}) => {
// check selected ids
itemIds.forEach((id) => {
@@ -20,7 +22,7 @@ const copyItems = ({
});
cy.get(`#${ITEMS_TABLE_COPY_SELECTED_ITEMS_ID}`).click();
- cy.fillTreeModal(toItemPath);
+ cy.handleTreeMenu(toItemPath, rootId);
};
describe('Copy items in List', () => {
@@ -74,7 +76,7 @@ describe('Copy items in List', () => {
// copy
const itemIds = [SAMPLE_ITEMS.items[2].id, SAMPLE_ITEMS.items[4].id];
- copyItems({ itemIds, toItemPath: HOME_MODAL_ITEM_ID });
+ copyItems({ itemIds, toItemPath: MY_GRAASP_ITEM_PATH });
cy.wait('@copyItems').then(({ request: { url } }) => {
itemIds.forEach((id) => {
diff --git a/cypress/e2e/item/create/createShortcut.cy.ts b/cypress/e2e/item/create/createShortcut.cy.ts
index e2212f1f0..82bc59152 100644
--- a/cypress/e2e/item/create/createShortcut.cy.ts
+++ b/cypress/e2e/item/create/createShortcut.cy.ts
@@ -5,6 +5,7 @@ import * as qs from 'qs';
import { HOME_PATH } from '../../../../src/config/paths';
import {
ITEM_MENU_SHORTCUT_BUTTON_CLASS,
+ MY_GRAASP_ITEM_PATH,
buildItemMenu,
buildItemMenuButtonId,
} from '../../../../src/config/selectors';
@@ -16,12 +17,14 @@ import { SAMPLE_ITEMS } from '../../../fixtures/items';
const createShortcut = ({
id,
toItemPath,
+ rootId,
}: {
id: string;
toItemPath: string;
+ rootId?: string;
}) => {
cy.get(`#${buildItemMenu(id)} .${ITEM_MENU_SHORTCUT_BUTTON_CLASS}`).click();
- cy.fillTreeModal(toItemPath);
+ cy.handleTreeMenu(toItemPath, rootId);
};
const createShortcutInGrid = ({
@@ -39,13 +42,15 @@ const createShortcutInGrid = ({
const createShortcutInList = ({
id,
toItemPath,
+ rootId,
}: {
id: string;
toItemPath?: string;
+ rootId?: string;
}) => {
const menuSelector = `#${buildItemMenuButtonId(id)}`;
cy.get(menuSelector).click();
- createShortcut({ id, toItemPath });
+ createShortcut({ id, toItemPath, rootId });
};
const checkCreateShortcutRequest = ({
@@ -77,8 +82,7 @@ describe('Create Shortcut', () => {
cy.visit(HOME_PATH);
const { id } = SAMPLE_ITEMS.items[0];
- // toItemId: TREE_MODAL_MY_ITEMS_ID
- createShortcutInList({ id });
+ createShortcutInList({ id, toItemPath: MY_GRAASP_ITEM_PATH });
checkCreateShortcutRequest({ id });
});
@@ -123,8 +127,7 @@ describe('Create Shortcut', () => {
cy.switchMode(ITEM_LAYOUT_MODES.GRID);
const { id } = SAMPLE_ITEMS.items[0];
- // toItemId: TREE_MODAL_MY_ITEMS_ID
- createShortcutInGrid({ id });
+ createShortcutInGrid({ id, toItemPath: MY_GRAASP_ITEM_PATH });
checkCreateShortcutRequest({ id });
});
diff --git a/cypress/e2e/item/move/gridMoveItem.cy.ts b/cypress/e2e/item/move/gridMoveItem.cy.ts
index d8216818a..def6037d2 100644
--- a/cypress/e2e/item/move/gridMoveItem.cy.ts
+++ b/cypress/e2e/item/move/gridMoveItem.cy.ts
@@ -1,6 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
ITEM_MENU_MOVE_BUTTON_CLASS,
+ MY_GRAASP_ITEM_PATH,
buildItemMenu,
buildItemMenuButtonId,
} from '../../../../src/config/selectors';
@@ -69,7 +70,7 @@ describe('Move Item in Grid', () => {
// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
- moveItem({ id: movedItem, toItemPath: 'selectionModalMyGraasp' });
+ moveItem({ id: movedItem, toItemPath: MY_GRAASP_ITEM_PATH });
cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
diff --git a/cypress/e2e/item/move/listMoveItem.cy.ts b/cypress/e2e/item/move/listMoveItem.cy.ts
index 7c9198613..3e793c059 100644
--- a/cypress/e2e/item/move/listMoveItem.cy.ts
+++ b/cypress/e2e/item/move/listMoveItem.cy.ts
@@ -1,6 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
ITEM_MENU_MOVE_BUTTON_CLASS,
+ MY_GRAASP_ITEM_PATH,
buildItemMenu,
buildItemMenuButtonId,
buildItemRowArrowId,
@@ -112,7 +113,7 @@ describe('Move Item in List', () => {
// move
const { id: movedItem } = SAMPLE_ITEMS.items[2];
- moveItem({ id: movedItem, toItemPath: 'selectionModalMyGraasp' });
+ moveItem({ id: movedItem, toItemPath: MY_GRAASP_ITEM_PATH });
cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
diff --git a/cypress/e2e/item/move/listMoveMultiple.cy.ts b/cypress/e2e/item/move/listMoveMultiple.cy.ts
index 1d96e6f21..f9bfa4654 100644
--- a/cypress/e2e/item/move/listMoveMultiple.cy.ts
+++ b/cypress/e2e/item/move/listMoveMultiple.cy.ts
@@ -1,6 +1,7 @@
import { HOME_PATH, buildItemPath } from '../../../../src/config/paths';
import {
ITEMS_TABLE_MOVE_SELECTED_ITEMS_ID,
+ MY_GRAASP_ITEM_PATH,
buildItemsTableRowIdAttribute,
} from '../../../../src/config/selectors';
import { ITEM_LAYOUT_MODES } from '../../../../src/enums';
@@ -71,7 +72,7 @@ describe('Move Items in List', () => {
// move
const itemIds = [SAMPLE_ITEMS.items[2].id, SAMPLE_ITEMS.items[4].id];
- moveItems({ itemIds, toItemPath: 'selectionModalMyGraasp' });
+ moveItems({ itemIds, toItemPath: MY_GRAASP_ITEM_PATH });
cy.wait('@moveItems').then(({ request: { body, url } }) => {
expect(body.parentId).to.equal(undefined);
diff --git a/cypress/support/commands/item.ts b/cypress/support/commands/item.ts
index 5ef6219a9..61c76010f 100644
--- a/cypress/support/commands/item.ts
+++ b/cypress/support/commands/item.ts
@@ -81,45 +81,6 @@ Cypress.Commands.add(
},
);
-Cypress.Commands.add(
- 'fillTreeModal',
- (toItemPath, treeRootId = HOME_MODAL_ITEM_ID) => {
- const ids = getParentsIdsFromPath(toItemPath);
-
- cy.wait(TREE_VIEW_PAUSE);
-
- [treeRootId, ...ids].forEach((value, idx, array) => {
- cy.get(`#${treeRootId}`).then(($tree) => {
- // click on the element
- if (idx === array.length - 1) {
- cy.wrap($tree)
- .get(
- `#${buildTreeItemId(value, treeRootId)} .MuiTreeItem-label input`,
- )
- .first()
- .click();
- }
- // if can't find children click on parent (current value)
- if (
- idx !== array.length - 1 &&
- !$tree.find(
- `#${buildTreeItemId(
- array[idx + 1],
- treeRootId,
- )} .MuiTreeItem-label`,
- ).length
- ) {
- cy.wrap($tree)
- .get(`#${buildTreeItemId(value, treeRootId)} .MuiTreeItem-label`)
- .first()
- .click();
- }
- });
- });
-
- cy.get(`#${TREE_MODAL_CONFIRM_BUTTON_ID}`).click();
- },
-);
Cypress.Commands.add(
'fillBaseItemModal',
({ name = '' }, { confirm = true } = {}) => {
diff --git a/cypress/support/index.ts b/cypress/support/index.ts
index 078589a39..b5223cdd9 100644
--- a/cypress/support/index.ts
+++ b/cypress/support/index.ts
@@ -44,7 +44,6 @@ declare global {
wsClientStub: any,
): void;
- fillTreeModal(path: string, rootId?: string): void;
handleTreeMenu(path: string, rootId?: string): void;
switchMode(mode: string): void;
goToItemInGrid(path: string): void;
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index 04e9fd61f..fc6b7e00a 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -22,6 +22,7 @@ import {
import App from './App';
import FallbackComponent from './Fallback';
import { CurrentUserContextProvider } from './context/CurrentUserContext';
+import { FilterItemsContextProvider } from './context/FilterItemsContext';
import ModalProviders from './context/ModalProviders';
const Root = (): JSX.Element => (
@@ -34,7 +35,9 @@ const Root = (): JSX.Element => (
-
+
+
+
diff --git a/src/components/common/MoveButton.tsx b/src/components/common/MoveButton.tsx
index aed3493e2..e5ff53247 100644
--- a/src/components/common/MoveButton.tsx
+++ b/src/components/common/MoveButton.tsx
@@ -1,15 +1,17 @@
-import { useEffect, useState } from 'react';
+import { useState } from 'react';
import { IconButtonProps } from '@mui/material/IconButton';
+import { DiscriminatedItem } from '@graasp/sdk';
import {
ActionButton,
ActionButtonVariant,
MoveButton as GraaspMoveButton,
} from '@graasp/ui';
-import { hooks, mutations } from '@/config/queryClient';
-import { applyEllipsisOnLength, getDirectParentId } from '@/utils/item';
+import { mutations } from '@/config/queryClient';
+import { getDirectParentId } from '@/utils/item';
+import { computeButtonText } from '@/utils/itemSelection';
import { useBuilderTranslation } from '../../config/i18n';
import {
@@ -22,8 +24,6 @@ import ItemSelectionModal, {
ItemSelectionModalProps,
} from '../main/itemSelectionModal/ItemSelectionModal';
-const TITLE_MAX_NAME_LENGTH = 15;
-
type MoveButtonProps = {
itemIds: string[];
color?: IconButtonProps['color'];
@@ -33,7 +33,7 @@ type MoveButtonProps = {
};
const MoveButton = ({
- itemIds: defaultItemsIds,
+ itemIds,
color = 'default',
id,
type = ActionButton.ICON_BUTTON,
@@ -43,78 +43,59 @@ const MoveButton = ({
const { mutate: moveItems } = mutations.useMoveItems();
const [open, setOpen] = useState(false);
- const [itemIds, setItemIds] = useState(defaultItemsIds || []);
-
- const { data: items } = hooks.useItems(itemIds);
- const openMoveModal = (newItemIds: string[]) => {
+ const openMoveModal = () => {
setOpen(true);
- setItemIds(newItemIds);
};
const onClose = () => {
setOpen(false);
};
- const onConfirm: ItemSelectionModalProps['onConfirm'] = (payload) => {
- // change item's root id to null
- const newPayload = {
+ const onConfirm: ItemSelectionModalProps['onConfirm'] = (destination) => {
+ moveItems({
ids: itemIds,
- to: payload,
- };
- moveItems(newPayload);
+ to: destination,
+ });
onClose();
};
- useEffect(() => {
- // necessary to sync prop with a state because move-many-items' targets are updated dynamically with the table
- setItemIds(defaultItemsIds);
- }, [defaultItemsIds]);
const handleMove = () => {
- openMoveModal(itemIds);
+ openMoveModal();
onClick?.();
};
- const isDisabled = (item: NavigationElement, homeId: string) => {
- if (items?.data) {
+ const isDisabled = (
+ items: DiscriminatedItem[],
+ item: NavigationElement,
+ homeId: string,
+ ) => {
+ if (items) {
// cannot move inside self and below
- const moveInSelf = Object.values(items.data).some((i) =>
- item.path.includes(i.path),
- );
+ const moveInSelf = items.some((i) => item.path.includes(i.path));
// cannot move in same direct parent
// todo: not opti because we only have the ids from the table
- const directParentIds = Object.values(items.data).map((i) =>
- getDirectParentId(i.path),
- );
+ const directParentIds = items.map((i) => getDirectParentId(i.path));
const moveInDirectParent = directParentIds.includes(item.id);
// cannot move to home if was already on home
let moveToHome = false;
- if (items?.data) {
- moveToHome =
- item.id === homeId &&
- !getDirectParentId(Object.values(items.data)[0].path);
+ if (items) {
+ moveToHome = item.id === homeId && !getDirectParentId(items[0].path);
}
return moveInSelf || moveInDirectParent || moveToHome;
}
return false;
};
- const title = items
- ? translateBuilder(BUILDER.MOVE_ITEM_MODAL_TITLE, {
- name: applyEllipsisOnLength(
- Object.values(items.data)[0].name,
- TITLE_MAX_NAME_LENGTH,
- ),
- // -1 because we show one name
- count: itemIds.length - 1,
- })
- : translateBuilder(BUILDER.MOVE_ITEM_MODAL_TITLE);
-
const buttonText = (name?: string) =>
- translateBuilder(BUILDER.MOVE_BUTTON, { name, count: name ? 1 : 0 });
+ computeButtonText({
+ translateBuilder,
+ translateKey: BUILDER.MOVE_BUTTON,
+ name,
+ });
return (
<>
@@ -127,16 +108,15 @@ const MoveButton = ({
menuItemClassName={ITEM_MENU_MOVE_BUTTON_CLASS}
iconClassName={ITEM_MOVE_BUTTON_CLASS}
/>
-
- {items?.data && open && (
+ {itemIds && open && (
)}
>
diff --git a/src/components/common/SelectTypes.tsx b/src/components/common/SelectTypes.tsx
new file mode 100644
index 000000000..19c2ee9c3
--- /dev/null
+++ b/src/components/common/SelectTypes.tsx
@@ -0,0 +1,89 @@
+import {
+ Checkbox,
+ FormControl,
+ InputLabel,
+ ListItemText,
+ MenuItem,
+ OutlinedInput,
+ Select,
+ SelectChangeEvent,
+ Stack,
+} from '@mui/material';
+
+import { ItemType } from '@graasp/sdk';
+import { ItemIcon } from '@graasp/ui';
+
+import { useBuilderTranslation, useEnumsTranslation } from '@/config/i18n';
+import { BUILDER } from '@/langs/constants';
+
+import { useFilterItemsContext } from '../context/FilterItemsContext';
+
+const ITEM_HEIGHT = 48;
+const ITEM_PADDING_TOP = 8;
+const MenuProps = {
+ PaperProps: {
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
+ },
+ },
+};
+
+// Exclude LOCAL_FILE because it is also file like for S3_FILE but stored in another location.
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+const { LOCAL_FILE, ...BUILDER_ITEM_TYPES } = ItemType;
+const LABEL_ID = 'select-types-filter-label';
+
+export const SelectTypes = (): JSX.Element => {
+ const { itemTypes, setItemTypes } = useFilterItemsContext();
+ const { t: translateEnums } = useEnumsTranslation();
+ const { t: translateBuilder } = useBuilderTranslation();
+
+ const types = Object.values(BUILDER_ITEM_TYPES).sort((t1, t2) =>
+ translateEnums(t1).localeCompare(translateEnums(t2)),
+ );
+
+ const handleChange = (event: SelectChangeEvent) => {
+ const {
+ target: { value },
+ } = event;
+ setItemTypes(value as typeof itemTypes);
+ };
+
+ const label = translateBuilder(BUILDER.FILTER_BY_TYPES_LABEL);
+
+ const renderValues = (value: typeof itemTypes) => (
+
+ {value.map((type) => (
+
+ ))}
+
+ );
+
+ return (
+
+ {label}
+ }
+ renderValue={renderValues}
+ MenuProps={MenuProps}
+ >
+ {types.map((type) => (
+
+ ))}
+
+
+ );
+};
+
+export default SelectTypes;
diff --git a/src/components/context/CopyItemModalContext.tsx b/src/components/context/CopyItemModalContext.tsx
deleted file mode 100644
index 29cce7e84..000000000
--- a/src/components/context/CopyItemModalContext.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { createContext, useContext, useMemo, useState } from 'react';
-
-import { validate } from 'uuid';
-
-import { useBuilderTranslation } from '../../config/i18n';
-import { mutations } from '../../config/queryClient';
-import { BUILDER } from '../../langs/constants';
-import TreeModal, { TreeModalProps } from '../main/TreeModal';
-
-type CopyItemModalContextType = {
- openModal: (newItemIds: string[]) => void;
-};
-
-const CopyItemModalContext = createContext({
- openModal: () => null,
-});
-
-type Props = {
- children: JSX.Element;
-};
-
-export const CopyItemModalProvider = ({ children }: Props): JSX.Element => {
- const { t: translateBuilder } = useBuilderTranslation();
- const { mutate: copyItems } = mutations.useCopyItems();
- const [open, setOpen] = useState(false);
- const [itemIds, setItemIds] = useState([]);
-
- const openModal = (newItemIds: string[]) => {
- setOpen(true);
- setItemIds(newItemIds);
- };
-
- const onClose = () => {
- setOpen(false);
- setItemIds([]);
- };
-
- const onConfirm: TreeModalProps['onConfirm'] = (payload) => {
- // change item's root id to null
- const newPayload = {
- ...payload,
- to: payload.to && validate(payload.to) ? payload.to : undefined,
- };
- copyItems(newPayload);
- onClose();
- };
-
- const renderModal = () => {
- if (!itemIds.length) {
- return null;
- }
-
- return (
-
- );
- };
-
- const value = useMemo(() => ({ openModal }), []);
-
- return (
-
- {renderModal()}
- {children}
-
- );
-};
-
-export const useCopyItemModalContext = (): CopyItemModalContextType =>
- useContext(CopyItemModalContext);
diff --git a/src/components/context/CreateShortcutModalContext.tsx b/src/components/context/CreateShortcutModalContext.tsx
deleted file mode 100644
index 97e29a090..000000000
--- a/src/components/context/CreateShortcutModalContext.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { createContext, useMemo, useState } from 'react';
-
-import {
- DiscriminatedItem,
- Item,
- ItemType,
- ShortcutItemType,
-} from '@graasp/sdk';
-
-import { useBuilderTranslation } from '../../config/i18n';
-import { mutations } from '../../config/queryClient';
-import { HOME_MODAL_ITEM_ID } from '../../config/selectors';
-import { BUILDER } from '../../langs/constants';
-import { buildShortcutExtra } from '../../utils/itemExtra';
-import TreeModal, { TreeModalProps } from '../main/TreeModal';
-
-const CreateShortcutModalContext = createContext({
- openModal: (_newItem: Item) => {
- // do nothing
- },
-});
-
-type Props = {
- children: JSX.Element | JSX.Element[];
-};
-
-const CreateShortcutModalProvider = ({ children }: Props): JSX.Element => {
- const { t: translateBuilder } = useBuilderTranslation();
- const { mutate: createShortcut } = mutations.usePostItem();
- const [open, setOpen] = useState(false);
- const [item, setItem] = useState- (null);
-
- const openModal = (newItem: Item) => {
- setOpen(true);
- setItem(newItem);
- };
-
- const onClose = () => {
- setOpen(false);
- setItem(null);
- };
-
- const onConfirm: TreeModalProps['onConfirm'] = ({ ids: [target], to }) => {
- const shortcut: Partial &
- Pick & {
- parentId?: string;
- } = {
- name: translateBuilder(BUILDER.CREATE_SHORTCUT_DEFAULT_NAME, {
- name: item?.name,
- }),
- extra: buildShortcutExtra(target),
- type: ItemType.SHORTCUT,
- // set parent id if not root
- parentId: to !== HOME_MODAL_ITEM_ID ? to : undefined,
- };
- createShortcut(shortcut);
-
- onClose();
- };
-
- const renderModal = () => {
- if (!item) {
- return null;
- }
-
- return (
-
- );
- };
-
- const value = useMemo(() => ({ openModal }), []);
-
- return (
-
- {renderModal()}
- {children}
-
- );
-};
-
-export { CreateShortcutModalProvider, CreateShortcutModalContext };
diff --git a/src/components/context/FilterItemsContext.tsx b/src/components/context/FilterItemsContext.tsx
new file mode 100644
index 000000000..06b13c971
--- /dev/null
+++ b/src/components/context/FilterItemsContext.tsx
@@ -0,0 +1,62 @@
+import { createContext, useContext, useEffect, useMemo, useState } from 'react';
+import { useLocation } from 'react-router';
+
+import { DiscriminatedItem } from '@graasp/sdk';
+
+type ItemTypeConst = DiscriminatedItem['type'];
+type ItemTypes = ItemTypeConst[];
+
+type FilterItemsContextType = {
+ itemTypes: ItemTypes;
+ setItemTypes: (
+ newTypes: ItemTypes | ((types: ItemTypes) => ItemTypes),
+ ) => void;
+ shouldDisplayItem: (itemType: ItemTypeConst) => boolean;
+};
+
+const FilterItemsContext = createContext({
+ itemTypes: [],
+ setItemTypes: () => {
+ console.error(
+ 'No Provider found for "FilterItemsContext". Check that this Provider is accessible.',
+ );
+ },
+ shouldDisplayItem: (_itemType): boolean => {
+ console.error(
+ 'No Provider found for "FilterItemsContext". Check that this Provider is accessible.',
+ );
+ return true;
+ },
+});
+
+type Props = {
+ children: JSX.Element | JSX.Element[];
+};
+
+export const FilterItemsContextProvider = ({
+ children,
+}: Props): JSX.Element => {
+ const location = useLocation();
+ const [itemTypes, setItemTypes] = useState([]);
+
+ useEffect(() => setItemTypes([]), [location]);
+
+ const value = useMemo(
+ () => ({
+ itemTypes,
+ setItemTypes,
+ shouldDisplayItem: (itemType: ItemTypeConst) =>
+ itemTypes.length === 0 || itemTypes.includes(itemType),
+ }),
+ [itemTypes],
+ );
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useFilterItemsContext = (): FilterItemsContextType =>
+ useContext(FilterItemsContext);
diff --git a/src/components/context/ModalProviders.tsx b/src/components/context/ModalProviders.tsx
index 04960e852..b9590b900 100644
--- a/src/components/context/ModalProviders.tsx
+++ b/src/components/context/ModalProviders.tsx
@@ -1,5 +1,3 @@
-import { CopyItemModalProvider } from './CopyItemModalContext';
-import { CreateShortcutModalProvider } from './CreateShortcutModalContext';
import { FlagItemModalProvider } from './FlagItemModalContext';
import { LayoutContextProvider } from './LayoutContext';
@@ -7,11 +5,7 @@ type Props = { children: JSX.Element };
const ModalProviders = ({ children }: Props): JSX.Element => (
-
-
- {children}
-
-
+ {children}
);
diff --git a/src/components/item/ItemContent.tsx b/src/components/item/ItemContent.tsx
index 4ea7ff5b8..f1c5784e9 100644
--- a/src/components/item/ItemContent.tsx
+++ b/src/components/item/ItemContent.tsx
@@ -48,6 +48,7 @@ import {
} from '../../config/selectors';
import ErrorAlert from '../common/ErrorAlert';
import { useCurrentUserContext } from '../context/CurrentUserContext';
+import { useFilterItemsContext } from '../context/FilterItemsContext';
import ItemActions from '../main/ItemActions';
import Items from '../main/Items';
import NewItemButton from '../main/NewItemButton';
@@ -177,6 +178,8 @@ const FolderContent = ({
item: FolderItemType;
enableEditing: boolean;
}): JSX.Element => {
+ const { shouldDisplayItem } = useFilterItemsContext();
+
const {
data: children,
isLoading,
@@ -185,6 +188,9 @@ const FolderContent = ({
ordered: true,
});
+ // TODO: use hook's filter when available
+ const folderChildren = children?.filter((f) => shouldDisplayItem(f.type));
+
if (isLoading) {
return ;
}
@@ -198,14 +204,14 @@ const FolderContent = ({
parentId={item.id}
id={buildItemsTableId(item.id)}
title={item.name}
- items={children ?? []}
+ items={folderChildren ?? []}
headerElements={
enableEditing ? [] : undefined
}
// todo: not exactly correct, since you could have write rights on some child,
// but it's more tedious to check permissions over all selected items
ToolbarActions={enableEditing ? ItemActions : undefined}
- totalCount={children?.length}
+ totalCount={folderChildren?.length}
/>
);
};
diff --git a/src/components/main/CopyButton.tsx b/src/components/main/CopyButton.tsx
index 77dc169f2..8484dc260 100644
--- a/src/components/main/CopyButton.tsx
+++ b/src/components/main/CopyButton.tsx
@@ -1,4 +1,4 @@
-import { MouseEventHandler } from 'react';
+import { useState } from 'react';
import { IconButtonProps } from '@mui/material';
@@ -7,18 +7,23 @@ import {
CopyButton as GraaspCopyButton,
} from '@graasp/ui';
+import { mutations } from '@/config/queryClient';
+import { computeButtonText } from '@/utils/itemSelection';
+
import { useBuilderTranslation } from '../../config/i18n';
import {
ITEM_COPY_BUTTON_CLASS,
ITEM_MENU_COPY_BUTTON_CLASS,
} from '../../config/selectors';
import { BUILDER } from '../../langs/constants';
-import { useCopyItemModalContext } from '../context/CopyItemModalContext';
+import ItemSelectionModal, {
+ ItemSelectionModalProps,
+} from './itemSelectionModal/ItemSelectionModal';
export type Props = {
color?: IconButtonProps['color'];
id?: string;
- onClick?: MouseEventHandler;
+ onClick?: () => void;
type?: ActionButtonVariant;
itemIds: string[];
};
@@ -31,26 +36,60 @@ const CopyButton = ({
onClick,
}: Props): JSX.Element => {
const { t: translateBuilder } = useBuilderTranslation();
+ const { mutate: copyItems } = mutations.useCopyItems();
+ const [open, setOpen] = useState(false);
+
+ const openCopyModal = () => {
+ setOpen(true);
+ };
+
+ const onClose = () => {
+ setOpen(false);
+ };
- const { openModal: openCopyModal } = useCopyItemModalContext();
+ const onConfirm: ItemSelectionModalProps['onConfirm'] = (destination) => {
+ copyItems({
+ ids: itemIds,
+ to: destination,
+ });
+ onClose();
+ };
- const handleCopy: MouseEventHandler = (
- e,
- ) => {
- openCopyModal(itemIds);
- onClick?.(e);
+ const handleCopy = () => {
+ openCopyModal();
+ onClick?.();
};
+ const buttonText = (name?: string) =>
+ computeButtonText({
+ translateBuilder,
+ translateKey: BUILDER.COPY_BUTTON,
+ name,
+ });
+
return (
-
+ <>
+
+
+ {itemIds && open && (
+
+ )}
+ >
);
};
diff --git a/src/components/main/CreateShortcutButton.tsx b/src/components/main/CreateShortcutButton.tsx
new file mode 100644
index 000000000..106366f05
--- /dev/null
+++ b/src/components/main/CreateShortcutButton.tsx
@@ -0,0 +1,103 @@
+import { useState } from 'react';
+
+import LabelImportantIcon from '@mui/icons-material/LabelImportant';
+import { ListItemIcon, MenuItem } from '@mui/material';
+
+import {
+ DiscriminatedItem,
+ ItemType,
+ ShortcutItemType,
+ buildShortcutExtra,
+} from '@graasp/sdk';
+
+import { mutations } from '@/config/queryClient';
+import { computeButtonText } from '@/utils/itemSelection';
+
+import { useBuilderTranslation } from '../../config/i18n';
+import { ITEM_MENU_SHORTCUT_BUTTON_CLASS } from '../../config/selectors';
+import { BUILDER } from '../../langs/constants';
+import ItemSelectionModal, {
+ ItemSelectionModalProps,
+} from './itemSelectionModal/ItemSelectionModal';
+
+export type Props = {
+ item: DiscriminatedItem;
+ onClick?: () => void;
+};
+
+const CreateShortcutButton = ({
+ item: defaultItem,
+ onClick,
+}: Props): JSX.Element => {
+ const { t: translateBuilder } = useBuilderTranslation();
+ const { mutate: createShortcut } = mutations.usePostItem();
+ const [open, setOpen] = useState(false);
+ const [item, setItem] = useState(defaultItem);
+
+ const openShortcutModal = (newItem: DiscriminatedItem) => {
+ setOpen(true);
+ setItem(newItem);
+ };
+
+ const onClose = () => {
+ setOpen(false);
+ };
+
+ const onConfirm: ItemSelectionModalProps['onConfirm'] = (destination) => {
+ const target = item.id; // id of the item where the shortcut is pointing
+
+ const shortcut: Partial &
+ Pick & {
+ parentId?: string;
+ } = {
+ name: translateBuilder(BUILDER.CREATE_SHORTCUT_DEFAULT_NAME, {
+ name: item?.name,
+ }),
+ extra: buildShortcutExtra(target),
+ type: ItemType.SHORTCUT,
+ parentId: destination,
+ };
+
+ createShortcut(shortcut);
+ onClose();
+ };
+
+ const handleShortcut = () => {
+ openShortcutModal(item);
+ onClick?.();
+ };
+
+ const buttonText = (name?: string) =>
+ computeButtonText({
+ translateBuilder,
+ translateKey: BUILDER.CREATE_SHORTCUT_BUTTON,
+ name,
+ });
+
+ return (
+ <>
+
+
+ {item && open && (
+
+ )}
+ >
+ );
+};
+
+export default CreateShortcutButton;
diff --git a/src/components/main/ItemMenu.tsx b/src/components/main/ItemMenu.tsx
index 7fac8a4ec..25d3ef2da 100644
--- a/src/components/main/ItemMenu.tsx
+++ b/src/components/main/ItemMenu.tsx
@@ -2,7 +2,6 @@ import { useContext, useState } from 'react';
import FileCopyIcon from '@mui/icons-material/FileCopy';
import FlagIcon from '@mui/icons-material/Flag';
-import LabelImportantIcon from '@mui/icons-material/LabelImportant';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import IconButton, { IconButtonProps } from '@mui/material/IconButton';
import ListItemIcon from '@mui/material/ListItemIcon';
@@ -20,7 +19,6 @@ import {
ITEM_MENU_BUTTON_CLASS,
ITEM_MENU_DUPLICATE_BUTTON_CLASS,
ITEM_MENU_FLAG_BUTTON_CLASS,
- ITEM_MENU_SHORTCUT_BUTTON_CLASS,
buildItemMenu,
buildItemMenuButtonId,
} from '../../config/selectors';
@@ -31,10 +29,10 @@ import HideButton from '../common/HideButton';
import MoveButton from '../common/MoveButton';
import PinButton from '../common/PinButton';
import RecycleButton from '../common/RecycleButton';
-import { CreateShortcutModalContext } from '../context/CreateShortcutModalContext';
import { useCurrentUserContext } from '../context/CurrentUserContext';
import { FlagItemModalContext } from '../context/FlagItemModalContext';
import CopyButton from './CopyButton';
+import CreateShortcutButton from './CreateShortcutButton';
type Props = {
item: DiscriminatedItem;
@@ -52,9 +50,6 @@ const ItemMenu = ({
const { data: member } = useCurrentUserContext();
const [anchorEl, setAnchorEl] = useState(null);
const { t: translateBuilder } = useBuilderTranslation();
- const { openModal: openCreateShortcutModal } = useContext(
- CreateShortcutModalContext,
- );
const { openModal: openFlagModal } = useContext(FlagItemModalContext);
const { mutate: copyItems } = mutations.useCopyItems();
const { data: memberships } = hooks.useItemMemberships(item.id);
@@ -67,11 +62,6 @@ const ItemMenu = ({
setAnchorEl(null);
};
- const handleCreateShortcut = () => {
- openCreateShortcutModal(item);
- handleClose();
- };
-
const handleFlag = () => {
openFlagModal?.(item.id);
handleClose();
@@ -83,11 +73,10 @@ const ItemMenu = ({
const to =
parentsIds.length > 1 ? parentsIds[parentsIds.length - 2] : undefined;
- const newPayload = {
+ copyItems({
ids: [item.id],
to,
- };
- copyItems(newPayload);
+ });
};
const renderEditorActions = () => {
if (canEdit) {
@@ -154,15 +143,11 @@ const ItemMenu = ({
{translateBuilder(BUILDER.ITEM_MENU_DUPLICATE_MENU_ITEM)}
-
+
void;
diff --git a/src/components/main/ItemsToolbar.tsx b/src/components/main/ItemsToolbar.tsx
index 7e05871c5..596483be7 100644
--- a/src/components/main/ItemsToolbar.tsx
+++ b/src/components/main/ItemsToolbar.tsx
@@ -1,14 +1,16 @@
-import { Stack, Typography } from '@mui/material';
-import Checkbox, { CheckboxProps } from '@mui/material/Checkbox';
+import { Stack, Switch, Typography } from '@mui/material';
import FormControlLabel from '@mui/material/FormControlLabel';
import { useBuilderTranslation } from '@/config/i18n';
import { ACCESSIBLE_ITEMS_ONLY_ME_ID } from '@/config/selectors';
+import { ShowOnlyMeChangeType } from '@/config/types';
+
+import SelectTypes from '../common/SelectTypes';
type Props = {
title: string;
headerElements?: JSX.Element[];
- onShowOnlyMeChange?: CheckboxProps['onChange'];
+ onShowOnlyMeChange?: ShowOnlyMeChangeType;
showOnlyMe?: boolean;
};
@@ -29,18 +31,21 @@ const ItemsToolbar = ({
{headerElements}
- {onShowOnlyMeChange && (
-
- }
- label={t('Show only created by me')}
- />
- )}
+
+ {onShowOnlyMeChange && (
+ onShowOnlyMeChange(checked)}
+ />
+ }
+ label={t('Show only created by me')}
+ />
+ )}
+
+
>
);
};
diff --git a/src/components/main/TreeModal.tsx b/src/components/main/TreeModal.tsx
deleted file mode 100644
index 7873097bc..000000000
--- a/src/components/main/TreeModal.tsx
+++ /dev/null
@@ -1,208 +0,0 @@
-import { useState } from 'react';
-
-import Dialog from '@mui/material/Dialog';
-import DialogActions from '@mui/material/DialogActions';
-import DialogContent from '@mui/material/DialogContent';
-import DialogTitle from '@mui/material/DialogTitle';
-
-import { DiscriminatedItem, ItemType } from '@graasp/sdk';
-import { Button, DynamicTreeView, Loader } from '@graasp/ui';
-
-import { TREE_VIEW_MAX_WIDTH } from '../../config/constants';
-import { useBuilderTranslation } from '../../config/i18n';
-import { hooks } from '../../config/queryClient';
-import {
- HOME_MODAL_ITEM_ID,
- TREE_MODAL_CONFIRM_BUTTON_ID,
- TREE_MODAL_SHARED_ITEMS_ID,
- buildTreeItemId,
-} from '../../config/selectors';
-import { TreePreventSelection } from '../../enums';
-import { BUILDER } from '../../langs/constants';
-import { getParentsIdsFromPath } from '../../utils/item';
-import CancelButton from '../common/CancelButton';
-
-const dialogId = 'simple-dialog-title';
-const { useItem, useItems, useOwnItems, useChildren, useSharedItems } = hooks;
-
-export type TreeModalProps = {
- onConfirm: (args: { ids: string[]; to?: string }) => void;
- onClose: (args: { id: string | null; open: boolean }) => void;
- title: string;
- itemIds?: string[];
- open?: boolean;
- prevent?: TreePreventSelection;
-};
-
-const TreeModal = ({
- title,
- onClose,
- onConfirm,
- open = false,
- itemIds = [],
- prevent = TreePreventSelection.NONE,
-}: TreeModalProps): JSX.Element => {
- const { t: translateBuilder } = useBuilderTranslation();
- const { data: ownItems, isLoading: isOwnItemsLoading } = useOwnItems();
- // todo: get only shared items with write/admin rights
- // otherwise choosing an item without the write rights will result in an error
- const { data: sharedItems, isLoading: isSharedItemsLoading } =
- useSharedItems();
- const [selectedId, setSelectedId] = useState();
- const { data: items, isLoading: isItemLoading } = useItems(itemIds);
-
- // build the expanded item ids list for a given tree (with treeRootId as id)
- // by default, we expand all parents of items
- // all other tree roots should be closed
- const buildExpandedItems = (treeRootId: string) => {
- if (!items || !items.data) {
- return [];
- }
-
- // suppose all items are in the same parent
- const parentIds =
- getParentsIdsFromPath(Object.values(items.data)[0].path) || [];
- if (!parentIds.length) {
- return [];
- }
-
- // return expanded list depending current root id
- // define root id depending on whether is the root parent is in the owned items
- const rootItemId = parentIds[0];
- const isRootItemOwned = Boolean(
- ownItems?.find(({ id }) => id === rootItemId),
- );
- const itemRootId = isRootItemOwned
- ? HOME_MODAL_ITEM_ID
- : TREE_MODAL_SHARED_ITEMS_ID;
-
- // trees root not being treeRootId should be closed
- if (treeRootId !== itemRootId) {
- return [];
- }
-
- // return expanded ids
- const newExpandedItems = [treeRootId, ...parentIds];
- return newExpandedItems;
- };
-
- if (isOwnItemsLoading || isSharedItemsLoading || isItemLoading) {
- return ;
- }
-
- // compute whether the given id tree item is disabled
- // it depends on the prevent mode and the previous items
- const isTreeItemDisabled = ({
- itemId: iId,
- parentIsDisabled,
- }: {
- itemId: string;
- parentIsDisabled: boolean;
- }) => {
- switch (prevent) {
- case TreePreventSelection.SELF_AND_CHILDREN:
- // if the previous item is disabled, its children will be disabled
- // and prevent selection on self
- return Boolean(parentIsDisabled || itemIds.find((x) => x === iId));
- case TreePreventSelection.NONE:
- default:
- return false;
- }
- };
-
- const handleClose = () => {
- onClose({ id: null, open: false });
- };
-
- const onClickConfirm = () => {
- onConfirm({ ids: itemIds, to: selectedId });
- handleClose();
- };
-
- const onTreeItemSelect = (nodeId: string) => {
- if (selectedId === nodeId) {
- setSelectedId(undefined);
- } else {
- setSelectedId(nodeId);
- }
- };
-
- const isFolder = (i: Pick) =>
- i.type === ItemType.FOLDER;
-
- // compute tree only when the modal is open
- const tree = !open ? null : (
- <>
- {ownItems && (
-
- )}
- {sharedItems && (
-
- )}
- >
- );
-
- return (
-
- );
-};
-
-export default TreeModal;
diff --git a/src/components/main/itemSelectionModal/ChildrenNavigationTree.tsx b/src/components/main/itemSelectionModal/ChildrenNavigationTree.tsx
index 7a5806047..aa4e52a4c 100644
--- a/src/components/main/itemSelectionModal/ChildrenNavigationTree.tsx
+++ b/src/components/main/itemSelectionModal/ChildrenNavigationTree.tsx
@@ -25,11 +25,9 @@ const ChildrenNavigationTree = ({
isDisabled,
}: ChildrenNavigationTreeProps): JSX.Element => {
const { t: translateBuilder } = useBuilderTranslation();
- // TODO: use filter in useChildren directly in another PR
const { data: children } = hooks.useChildren(selectedNavigationItem.id);
-
- const folders = children?.filter((item) => item.type === ItemType.FOLDER);
-
+ // TODO: use hook's filter when available
+ const folders = children?.filter((f) => f.type === ItemType.FOLDER);
return (
<>
{folders?.map((ele) => (
diff --git a/src/components/main/itemSelectionModal/ItemSelectionModal.tsx b/src/components/main/itemSelectionModal/ItemSelectionModal.tsx
index cc6864bf2..1af289b3d 100644
--- a/src/components/main/itemSelectionModal/ItemSelectionModal.tsx
+++ b/src/components/main/itemSelectionModal/ItemSelectionModal.tsx
@@ -9,10 +9,13 @@ import DialogTitle from '@mui/material/DialogTitle';
import { DiscriminatedItem } from '@graasp/sdk';
+import { computeTitle } from '@/utils/itemSelection';
+
import { useBuilderTranslation } from '../../../config/i18n';
import { hooks } from '../../../config/queryClient';
import {
HOME_MODAL_ITEM_ID,
+ MY_GRAASP_ITEM_PATH,
TREE_MODAL_CONFIRM_BUTTON_ID,
} from '../../../config/selectors';
import { BUILDER } from '../../../langs/constants';
@@ -23,31 +26,42 @@ import ChildrenNavigationTree from './ChildrenNavigationTree';
import RootNavigationTree from './RootNavigationTree';
const dialogId = 'items-tree-modal';
-const MY_GRAASP_BREADCRUMB_ID = 'selectionModalMyGraasp';
export type ItemSelectionModalProps = {
buttonText: (itemName?: string) => string;
/** disabled rows
* */
- isDisabled: (item: NavigationElement, homeId: string) => boolean;
+ isDisabled?: (
+ items: DiscriminatedItem[],
+ item: NavigationElement,
+ homeId: string,
+ ) => boolean;
// items can be undefined because "many" operations start empty
- items?: DiscriminatedItem[];
+ itemIds?: string[];
onClose: (args: { id: string | null; open: boolean }) => void;
- onConfirm: (args: string | undefined) => void;
+ onConfirm: (destination: string | undefined) => void;
open?: boolean;
- title: string;
+ titleKey: string;
};
const ItemSelectionModal = ({
buttonText = () => 'Submit',
- isDisabled,
- items = [],
+ isDisabled = () => false,
+ itemIds = [],
onClose,
onConfirm,
open = false,
- title,
+ titleKey,
}: ItemSelectionModalProps): JSX.Element => {
const { t: translateBuilder } = useBuilderTranslation();
+ const { data: items } = hooks.useItems(itemIds);
+
+ const title = computeTitle({
+ items,
+ count: itemIds.length - 1,
+ translateBuilder,
+ translateKey: titleKey,
+ });
// special elements for breadcrumbs
// root displays specific paths
@@ -60,8 +74,8 @@ const ItemSelectionModal = ({
// my graasp displays accessible items
const MY_GRAASP_BREADCRUMB: NavigationElement = {
name: translateBuilder(BUILDER.MY_ITEMS_TITLE),
- id: MY_GRAASP_BREADCRUMB_ID,
- path: MY_GRAASP_BREADCRUMB_ID,
+ id: MY_GRAASP_ITEM_PATH,
+ path: MY_GRAASP_ITEM_PATH,
};
const SPECIAL_BREADCRUMB_IDS = [ROOT_BREADCRUMB.id, MY_GRAASP_BREADCRUMB.id];
@@ -124,6 +138,10 @@ const ItemSelectionModal = ({
return ;
};
+ const isDisabledLocal = (item: NavigationElement) =>
+ !items?.data ||
+ isDisabled(Object.values(items.data), item, MY_GRAASP_BREADCRUMB.id);
+
return (