Skip to content

Commit

Permalink
Add rename, duplicate and delete for Page Hierarchy (#3336)
Browse files Browse the repository at this point in the history
Co-authored-by: MUI bot <2109932+Janpot@users.noreply.github.com>
  • Loading branch information
asif-choudhari and Janpot authored Mar 28, 2024
1 parent e20af50 commit aec9918
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,41 @@
import * as React from 'react';
import clsx from 'clsx';
import { NodeId } from '@toolpad/studio-runtime';
import { Box, Typography, styled } from '@mui/material';
import { SimpleTreeView, TreeItem, TreeItemProps } from '@mui/x-tree-view';
import { Box, Typography, styled, IconButton } from '@mui/material';
import { SimpleTreeView, TreeItem, TreeItemProps, treeItemClasses } from '@mui/x-tree-view';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import useBoolean from '@toolpad/utils/hooks/useBoolean';
import * as appDom from '@toolpad/studio-runtime/appDom';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import invariant from 'invariant';
import { useAppState, useDomApi, useAppStateApi } from '../../AppState';
import { ComponentIcon } from '../PageEditor/ComponentCatalog/ComponentCatalogItem';
import { DomView } from '../../../utils/domView';
import { removePageLayoutNode } from '../pageLayout';
import EditableTreeItem from '../../../components/EditableTreeItem';
import EditableTreeItem, { EditableTreeItemProps } from '../../../components/EditableTreeItem';
import ExplorerHeader from '../ExplorerHeader';
import NodeMenu from '../NodeMenu';

const CollapseIcon = styled(ExpandMoreIcon)({ fontSize: '0.9rem', opacity: 0.5 });
const ExpandIcon = styled(ChevronRightIcon)({ fontSize: '0.9rem', opacity: 0.5 });

export interface CustomTreeItemProps extends TreeItemProps {
const classes = {
treeItemMenuButton: 'Toolpad__HierarchyListItem',
treeItemMenuOpen: 'Toolpad__HierarchyListItemMenuOpen',
};

const StyledTreeItem = styled(EditableTreeItem)({
[`& .${classes.treeItemMenuButton}`]: {
visibility: 'hidden',
},
[`& .${treeItemClasses.content}:hover .${classes.treeItemMenuButton}, & .${classes.treeItemMenuOpen}`]:
{
visibility: 'visible',
},
});

interface CustomTreeItemProps extends TreeItemProps, EditableTreeItemProps {
ref?: React.RefObject<HTMLLIElement>;
node: appDom.ElementNode;
}
Expand All @@ -28,7 +47,11 @@ function CustomTreeItem(props: CustomTreeItemProps) {

const { label, node, ...other } = props;

const { value: domNodeEditing, setFalse: stopDomNodeEditing } = useBoolean(false);
const {
value: domNodeEditing,
setTrue: startDomNodeEditing,
setFalse: stopDomNodeEditing,
} = useBoolean(false);

const existingNames = React.useMemo(() => appDom.getExistingNamesForNode(dom, node), [dom, node]);

Expand All @@ -50,8 +73,41 @@ function CustomTreeItem(props: CustomTreeItemProps) {
const handleNameSave = React.useCallback(
(newName: string) => {
domApi.setNodeName(node.id, newName);
stopDomNodeEditing();
},
[domApi, node.id, stopDomNodeEditing],
);

const handleNodeDelete = React.useCallback(
(nodeId: NodeId) => {
domApi.update((draft) => {
const toRemove = appDom.getNode(draft, nodeId);
if (appDom.isElement(toRemove)) {
draft = removePageLayoutNode(draft, toRemove);
}

return draft;
});
},
[domApi],
);

const handleNodeDuplicate = React.useCallback(
(nodeId: NodeId) => {
const currentNode = appDom.getNode(dom, nodeId);

invariant(
node.parentId && node.parentProp,
'Duplication should never be called on nodes that are not placed in the dom',
);

domApi.update((draft) => {
draft = appDom.duplicateNode(draft, currentNode);

return draft;
});
},
[domApi, node.id],
[dom, domApi, node.parentId, node.parentProp],
);

const handleNodeHover = React.useCallback(
Expand All @@ -66,7 +122,7 @@ function CustomTreeItem(props: CustomTreeItemProps) {
}, [appStateApi]);

return (
<EditableTreeItem
<StyledTreeItem
key={node.id}
labelText={node.name}
renderLabel={(children) => (
Expand All @@ -83,6 +139,26 @@ function CustomTreeItem(props: CustomTreeItemProps) {
sx={{ marginRight: 1, fontSize: 18, opacity: 0.5 }}
/>
{children}
{node.id ? (
<NodeMenu
renderButton={({ buttonProps, menuProps }) => (
<IconButton
className={clsx(classes.treeItemMenuButton, {
[classes.treeItemMenuOpen]: menuProps.open,
})}
aria-label="Open hierarchy menu"
size="small"
{...buttonProps}
>
<MoreVertIcon fontSize="inherit" />
</IconButton>
)}
nodeId={node.id}
onRenameNode={startDomNodeEditing}
onDuplicateNode={handleNodeDuplicate}
onDeleteNode={handleNodeDelete}
/>
) : null}
</Box>
)}
isEditing={domNodeEditing}
Expand Down
6 changes: 3 additions & 3 deletions packages/toolpad-studio/src/toolpad/AppEditor/NodeMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export interface NodeMenuProps {
export default function NodeMenu({
nodeId,
renderButton,
renameLabelText,
deleteLabelText,
duplicateLabelText,
renameLabelText = 'Rename',
deleteLabelText = 'Delete',
duplicateLabelText = 'Duplicate',
onRenameNode,
onDeleteNode,
onDuplicateNode,
Expand Down

0 comments on commit aec9918

Please sign in to comment.