@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
324 lines (322 loc) • 12.1 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';
const useStyles = makeStyles()(
(
theme,
{
root,
mainItem,
actionsContainer,
actionsColumn,
emptyRoot,
itemsList,
itemInfo,
itemInfoContentType,
itemEdited,
itemEditedText,
itemState,
infoItem,
menuItem,
itemDisplayRoot,
itemTypeIcon,
itemTypography,
icon
} = {}
) => ({
root: Object.assign({ maxWidth: 400, borderRadius: '12px' }, root),
mainItem: Object.assign({ padding: '10px 20px' }, mainItem),
actionsContainer: Object.assign({ display: 'flex', flexDirection: 'row', padding: '10px' }, actionsContainer),
actionsColumn: Object.assign(
{
display: 'flex',
flexDirection: 'column',
flexBasis: '100%',
flex: '1',
'&:first-child': {
marginRight: '60px'
}
},
actionsColumn
),
emptyRoot: Object.assign({ display: 'block', padding: '10px', textAlign: 'center' }, emptyRoot),
itemsList: Object.assign({ padding: 0 }, itemsList),
itemInfo: Object.assign({ display: 'block', borderBottom: `1px solid ${palette.gray.light4}` }, itemInfo),
itemInfoContentType: Object.assign(
{ color: theme.palette.text.secondary, marginBottom: '4px' },
itemInfoContentType
),
itemEdited: Object.assign({ paddingTop: '12px', borderTop: `1px solid ${palette.gray.light4}` }, itemEdited),
itemEditedText: Object.assign({ color: theme.palette.text.secondary, fontWeight: 600 }, itemEditedText),
itemState: Object.assign(
{
'&> *': {
marginRight: '5px'
}
},
itemState
),
infoItem: Object.assign(
{
cursor: 'default',
backgroundColor: 'inherit !important',
'&:hover': {
backgroundColor: 'inherit'
}
},
infoItem
),
menuItem: Object.assign({ minWidth: '100px' }, menuItem),
itemDisplayRoot: Object.assign({ marginBottom: 5 }, itemDisplayRoot),
itemTypeIcon: Object.assign({ color: palette.teal.main, marginRight: '2px' }, itemTypeIcon),
itemTypography: Object.assign({ color: theme.palette.text.primary }, itemTypography),
icon: Object.assign({ 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,
onMenuItemClicked,
classes: propClasses
} = props;
const { classes, cx } = useStyles(styles);
const isFolder = (item === null || item === void 0 ? void 0 : item.systemType) === 'folder';
const inWorkflow = isInWorkflow(item === null || item === void 0 ? void 0 : item.stateMap);
return React.createElement(
Popover,
{
open: open,
onClose: onClose,
anchorEl: anchorEl,
anchorOrigin: anchorOrigin,
anchorReference: anchorReference,
anchorPosition: anchorPosition,
classes: Object.assign({ 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 === null || item === void 0 ? void 0 : item.stateMap, {
user: item === null || item === void 0 ? void 0 : item.lockOwner
})
)
)
: React.createElement(
React.Fragment,
null,
React.createElement(ItemPublishingTargetIcon, { item: item, className: classes.icon }),
React.createElement(
Typography,
{ variant: 'body2', component: 'span' },
getItemPublishingTargetText(item === null || item === void 0 ? void 0 : 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 === null || propClasses === void 0 ? void 0 : 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 === null || propClasses === void 0 ? void 0 : 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 === null || propClasses === void 0 ? void 0 : 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 === null || item === void 0 ? void 0 : item.sandbox.dateModified)
),
by: item === null || item === void 0 ? void 0 : item.sandbox.modifier,
edited: React.createElement(
'span',
{ className: classes.itemEditedText },
React.createElement(FormattedMessage, { id: 'words.edited', defaultMessage: 'Edited' })
),
byLabel: (item === null || item === void 0 ? void 0 : item.sandbox.modifier)
? React.createElement(
'span',
{ className: classes.itemEditedText },
React.createElement(FormattedMessage, { id: 'words.by', defaultMessage: 'By' })
)
: ''
}
})
)
)
)
);
}
export default ItemMegaMenuUI;