{item.children.map((childItem) => {
return (
@@ -245,6 +171,7 @@ export const TreeItem = ({
textToHighlight={textToHighlight}
textToFilter={textToFilter}
markedItemIds={markedItemIds}
+ treeItemActionRender={treeItemActionRender}
/>
);
@@ -409,8 +336,6 @@ export const TreeItem = ({
}
}
- const shouldDisplayMoreButton = item.deletable || item.editable || treeItemMenuContributionComponents.length > 0;
-
let currentTreeItem: JSX.Element | null;
if (textToFilter && isFilterCandidate(item, textToFilter)) {
currentTreeItem = null;
@@ -447,32 +372,37 @@ export const TreeItem = ({
{image}
{text}
- {shouldDisplayMoreButton ? (
-
-
-
- ) : null}
+
+ {treeItemActionRender ? (
+ treeItemActionRender({
+ editingContextId: editingContextId,
+ treeId: treeId,
+ item: item,
+ depth: depth,
+ onExpand: onExpand,
+ onExpandAll: onExpandAll,
+ readOnly: readOnly,
+ onEnterEditingMode: enterEditingMode,
+ isHovered: state.isHovered,
+ })
+ ) : (
+
+ )}
+
- {!shouldDisplayMoreButton && state.isHovered && item.hasChildren && (
- {
- onExpandAll(item);
- }}>
-
-
- )}
- {children}
- {contextMenu}
+ {content}
>
);
}
diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.types.ts b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.types.ts
index 772f1f7a20..40346ec187 100644
--- a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.types.ts
+++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItem.types.ts
@@ -11,6 +11,7 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { GQLTreeItem } from '../views/TreeView.types';
+import { TreeItemActionProps } from './TreeItemAction.types';
export interface TreeItemProps {
editingContextId: string;
@@ -24,14 +25,11 @@ export interface TreeItemProps {
textToFilter: string | null;
enableMultiSelection: boolean;
markedItemIds: string[];
+ treeItemActionRender?: (props: TreeItemActionProps) => React.ReactNode;
}
export interface TreeItemState {
- showContextMenu: boolean;
- menuAnchor: Element | null;
editingMode: boolean;
- label: string;
- prevSelectionId: string | null;
editingKey: string | null;
isHovered: boolean;
}
diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.tsx b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.tsx
new file mode 100644
index 0000000000..9775076ec5
--- /dev/null
+++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.tsx
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Obeo.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+import IconButton from '@material-ui/core/IconButton';
+import { makeStyles } from '@material-ui/core/styles';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import { useState } from 'react';
+import { TreeItemActionProps, TreeItemActionState } from './TreeItemAction.types';
+import { TreeItemContextMenu } from './TreeItemContextMenu';
+
+const useTreeItemActionStyle = makeStyles((theme) => ({
+ more: {
+ hover: {
+ backgroundColor: theme.palette.action.hover,
+ },
+ focus: {
+ backgroundColor: theme.palette.action.selected,
+ },
+ },
+}));
+
+export const TreeItemAction = ({
+ editingContextId,
+ treeId,
+ item,
+ readOnly,
+ depth,
+ onExpand,
+ onExpandAll,
+ onEnterEditingMode,
+}: TreeItemActionProps) => {
+ const classes = useTreeItemActionStyle();
+ const [state, setState] = useState({
+ showContextMenu: false,
+ menuAnchor: null,
+ });
+
+ const openContextMenu = (event) => {
+ if (!state.showContextMenu) {
+ const { currentTarget } = event;
+ setState((prevState) => ({
+ ...prevState,
+ showContextMenu: true,
+ menuAnchor: currentTarget,
+ }));
+ }
+ };
+
+ let contextMenu = null;
+ if (state.showContextMenu) {
+ const closeContextMenu = () => {
+ setState((prevState) => ({
+ ...prevState,
+ showContextMenu: false,
+ menuAnchor: null,
+ }));
+ };
+ const enterEditingMode = () => {
+ setState((prevState) => ({
+ ...prevState,
+ showContextMenu: false,
+ menuAnchor: null,
+ }));
+ onEnterEditingMode();
+ };
+
+ contextMenu = (
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+ {contextMenu}
+ >
+ );
+};
diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.types.ts b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.types.ts
new file mode 100644
index 0000000000..5d6f79a8a4
--- /dev/null
+++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemAction.types.ts
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Obeo.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+import { GQLTreeItem } from '../views/TreeView.types';
+
+export interface TreeItemActionProps {
+ editingContextId: string;
+ treeId: string;
+ item: GQLTreeItem;
+ depth: number;
+ onExpand: (id: string, depth: number) => void;
+ onExpandAll: (treeItem: GQLTreeItem) => void;
+ onEnterEditingMode: () => void;
+ readOnly: boolean;
+ isHovered: boolean;
+}
+
+export interface TreeItemActionState {
+ showContextMenu: boolean;
+ menuAnchor: Element | null;
+}
diff --git a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemContextMenu.tsx b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemContextMenu.tsx
index db68279330..f6e72b1239 100644
--- a/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemContextMenu.tsx
+++ b/packages/trees/frontend/sirius-components-trees/src/treeitems/TreeItemContextMenu.tsx
@@ -11,7 +11,12 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { gql, useMutation } from '@apollo/client';
-import { Toast, useDeletionConfirmationDialog } from '@eclipse-sirius/sirius-components-core';
+import {
+ Toast,
+ useDeletionConfirmationDialog,
+ useComponents,
+ ComponentExtension,
+} from '@eclipse-sirius/sirius-components-core';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Menu from '@material-ui/core/Menu';
@@ -19,18 +24,18 @@ import MenuItem from '@material-ui/core/MenuItem';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';
import UnfoldMore from '@material-ui/icons/UnfoldMore';
-import React, { useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
import {
GQLDeleteTreeItemData,
GQLDeleteTreeItemInput,
GQLDeleteTreeItemPayload,
GQLDeleteTreeItemVariables,
GQLErrorPayload,
- TreeItemContextMenuContextValue,
TreeItemContextMenuProps,
TreeItemContextMenuState,
} from './TreeItemContextMenu.types';
-import { TreeItemContextMenuComponentProps } from './TreeItemContextMenuContribution.types';
+import { treeItemContextMenuEntryExtensionPoint } from './TreeItemContextMenuEntryExtensionPoints';
+import { TreeItemContextMenuComponentProps } from './TreeItemContextMenuEntry.types';
const deleteTreeItemMutation = gql`
mutation deleteTreeItem($input: DeleteTreeItemInput!) {
@@ -46,15 +51,12 @@ const deleteTreeItemMutation = gql`
const isErrorPayload = (payload: GQLDeleteTreeItemPayload): payload is GQLErrorPayload =>
payload.__typename === 'ErrorPayload';
-export const TreeItemContextMenuContext = React.createContext([]);
-
export const TreeItemContextMenu = ({
menuAnchor,
editingContextId,
treeId,
item,
readOnly,
- treeItemMenuContributionComponents,
depth,
onExpand,
onExpandAll,
@@ -65,6 +67,10 @@ export const TreeItemContextMenu = ({
const { showDeletionConfirmation } = useDeletionConfirmationDialog();
+ const treeItemMenuContextComponents: ComponentExtension[] = useComponents(
+ treeItemContextMenuEntryExtensionPoint
+ );
+
const expandItem = () => {
if (!item.expanded && item.hasChildren) {
onExpand(item.id, depth);
@@ -119,19 +125,17 @@ export const TreeItemContextMenu = ({
vertical: 'bottom',
horizontal: 'right',
}}>
- {treeItemMenuContributionComponents.map((component, index) => {
- const props: TreeItemContextMenuComponentProps = {
- editingContextId,
- item,
- readOnly,
- onClose,
- expandItem,
- key: index.toString(),
- treeId: treeId,
- };
- const element = React.createElement(component, props);
- return element;
- })}
+ {treeItemMenuContextComponents.map(({ Component: TreeItemMenuContextComponent }, index) => (
+
+ ))}
{item.hasChildren ? (