@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
364 lines (362 loc) • 11.7 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import EmptyState from '../EmptyState/EmptyState';
import { FormattedMessage } from 'react-intl';
import Typography from '@mui/material/Typography';
import ItemDisplay from '../ItemDisplay';
import ItemStateIcon from '../ItemStateIcon/ItemStateIcon';
import { getItemPublishingTargetText, getItemStateText, isInWorkflow } from '../ItemDisplay/utils';
import React from 'react';
import { makeStyles } from 'tss-react/mui';
import Popover from '@mui/material/Popover';
import palette from '../../styles/palette';
import Skeleton from '@mui/material/Skeleton';
import ItemPublishingTargetIcon from '../ItemPublishingTargetIcon/ItemPublishingTargetIcon';
import useUnmount from '../../hooks/useUnmount';
const useStyles = makeStyles()(
(
theme,
{
root,
mainItem,
actionsContainer,
actionsColumn,
emptyRoot,
itemsList,
itemInfo,
itemInfoContentType,
itemEdited,
itemEditedText,
itemState,
infoItem,
menuItem,
itemDisplayRoot,
itemTypeIcon,
itemTypography,
icon
} = {}
) => ({
root: {
maxWidth: 400,
borderRadius: '12px',
...root
},
mainItem: {
padding: '10px 20px',
...mainItem
},
actionsContainer: {
display: 'flex',
flexDirection: 'row',
padding: '10px',
...actionsContainer
},
actionsColumn: {
display: 'flex',
flexDirection: 'column',
flexBasis: '100%',
flex: '1',
'&:first-child': {
marginRight: '60px'
},
...actionsColumn
},
emptyRoot: {
display: 'block',
padding: '10px',
textAlign: 'center',
...emptyRoot
},
itemsList: {
padding: 0,
...itemsList
},
itemInfo: {
display: 'block',
borderBottom: `1px solid ${palette.gray.light4}`,
...itemInfo
},
itemInfoContentType: {
color: theme.palette.text.secondary,
marginBottom: '4px',
...itemInfoContentType
},
itemEdited: {
paddingTop: '12px',
borderTop: `1px solid ${palette.gray.light4}`,
...itemEdited
},
itemEditedText: {
color: theme.palette.text.secondary,
fontWeight: 600,
...itemEditedText
},
itemState: {
'&> *': {
marginRight: '5px'
},
...itemState
},
infoItem: {
cursor: 'default',
backgroundColor: 'inherit !important',
'&:hover': {
backgroundColor: 'inherit'
},
...infoItem
},
menuItem: {
minWidth: '100px',
...menuItem
},
itemDisplayRoot: {
marginBottom: 5,
...itemDisplayRoot
},
itemTypeIcon: {
color: palette.teal.main,
marginRight: '2px',
...itemTypeIcon
},
itemTypography: {
color: theme.palette.text.primary,
...itemTypography
},
icon: {
fontSize: '0.8rem',
verticalAlign: 'middle',
...icon
}
})
);
export function ItemMegaMenuUI(props) {
const {
open,
styles,
item,
isLoading = false,
numOfLoaderItems = 5,
options,
editorialOptions,
nonEditorialOptions,
anchorEl,
anchorOrigin,
anchorReference,
anchorPosition,
contentType,
locale,
onClose,
onClosed,
onMenuItemClicked,
classes: propClasses
} = props;
const { classes, cx } = useStyles(styles);
const isFolder = item?.systemType === 'folder';
const inWorkflow = isInWorkflow(item?.stateMap);
return React.createElement(
Popover,
{
open: open,
onClose: onClose,
anchorEl: anchorEl,
anchorOrigin: anchorOrigin,
anchorReference: anchorReference,
anchorPosition: anchorPosition,
classes: {
paper: classes.root,
...propClasses
}
},
React.createElement(
'section',
{ className: cx(classes.itemInfo, classes.infoItem, classes.mainItem) },
React.createElement(
Typography,
{ variant: 'body2', className: classes.itemInfoContentType },
isLoading ? React.createElement(Skeleton, { animation: 'wave' }) : contentType
),
isLoading
? React.createElement(Skeleton, { animation: 'wave' })
: React.createElement(ItemDisplay, {
item: item,
labelComponent: 'h2',
showPublishingTarget: false,
showWorkflowState: false,
classes: { root: classes.itemDisplayRoot, icon: classes.itemTypeIcon },
labelTypographyProps: {
className: classes.itemTypography
}
}),
isLoading
? React.createElement(Skeleton, { animation: 'wave' })
: React.createElement(
'div',
{ className: classes.itemState },
!isFolder &&
(inWorkflow
? React.createElement(
React.Fragment,
null,
React.createElement(ItemStateIcon, { item: item, className: classes.icon }),
React.createElement(
Typography,
{ variant: 'body2', component: 'span' },
getItemStateText(item?.stateMap, { user: item?.lockOwner?.username })
)
)
: React.createElement(
React.Fragment,
null,
React.createElement(ItemPublishingTargetIcon, { item: item, className: classes.icon }),
React.createElement(
Typography,
{ variant: 'body2', component: 'span' },
getItemPublishingTargetText(item?.stateMap)
)
))
)
),
isLoading
? React.createElement(
'div',
{ className: cx(classes.actionsContainer) },
new Array(2).fill(null).map((value, i) =>
React.createElement(
MenuList,
{ key: i, className: cx(classes.actionsColumn, classes.itemsList) },
new Array(Math.ceil(numOfLoaderItems / 2))
.fill(null)
.map((value, j) =>
React.createElement(
MenuItem,
{ key: j, className: cx(classes.menuItem, propClasses?.menuItem) },
React.createElement(Skeleton, { animation: 'wave', width: '100%' })
)
)
)
)
)
: options.flatMap((i) => i).length === 0
? React.createElement(EmptyState, {
title: React.createElement(FormattedMessage, {
id: 'contextMenu.emptyOptionsMessage',
defaultMessage: 'No options available to display.'
})
})
: React.createElement(
'div',
{ className: cx(classes.actionsContainer) },
React.createElement(
MenuList,
{ className: cx(classes.actionsColumn, classes.itemsList) },
editorialOptions.map((option, y) =>
React.createElement(MenuItem, {
dense: true,
autoFocus: y === 0,
key: option.id,
onClick: (e) => onMenuItemClicked(option.id, e),
className: cx(classes.menuItem, propClasses?.menuItem),
children: option.label
})
)
),
React.createElement(
'div',
{ className: classes.actionsColumn },
nonEditorialOptions.map((section, i) =>
React.createElement(
MenuList,
{ key: i, className: classes.itemsList },
section.map((option, y) =>
React.createElement(MenuItem, {
dense: true,
key: option.id,
divider: i !== nonEditorialOptions.length - 1 && y === section.length - 1,
onClick: (e) => onMenuItemClicked(option.id, e),
className: cx(classes.menuItem, propClasses?.menuItem),
children: option.label
})
)
)
)
)
),
React.createElement(
'section',
{ className: cx(classes.itemEdited, classes.infoItem, classes.mainItem) },
isLoading
? React.createElement(Skeleton, { animation: 'wave', width: '100%' })
: React.createElement(
React.Fragment,
null,
React.createElement(
Typography,
{ variant: 'body2', color: 'text.secondary', title: item.path, noWrap: true },
item.path
),
React.createElement(
Typography,
{ variant: 'body2' },
React.createElement(FormattedMessage, {
id: 'itemMegaMenu.editedBy',
defaultMessage: '{edited} {date} {byLabel} {by}',
values: {
date: new Intl.DateTimeFormat(locale.localeCode, locale.dateTimeFormatOptions).format(
new Date(item?.sandbox.dateModified)
),
by: item?.sandbox.modifier?.username ?? '',
edited: React.createElement(
'span',
{ className: classes.itemEditedText },
React.createElement(FormattedMessage, { id: 'words.edited', defaultMessage: 'Edited' })
),
byLabel: item?.sandbox.modifier?.username
? React.createElement(
'span',
{ className: classes.itemEditedText },
React.createElement(FormattedMessage, { id: 'words.by', defaultMessage: 'By' })
)
: ''
}
})
)
)
),
React.createElement(Unmount, { onClosed: onClosed })
);
}
function Unmount(props) {
useUnmount(props.onClosed);
return null;
}
export default ItemMegaMenuUI;