@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
286 lines (284 loc) • 10.4 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 React, { useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import IconButton from '@mui/material/IconButton';
import AddCircleIcon from '@mui/icons-material/AddRounded';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Typography from '@mui/material/Typography';
import { makeStyles } from 'tss-react/mui';
import { useDispatch } from 'react-redux';
import { newContentCreationComplete, showEditDialog, showNewContentDialog } from '../../state/actions/dialogs';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import Button from '@mui/material/Button';
import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined';
import Suspencified from '../Suspencified/Suspencified';
import { getSimplifiedVersion } from '../../utils/string';
import palette from '../../styles/palette';
import Tooltip from '@mui/material/Tooltip';
import { useSelection } from '../../hooks/useSelection';
import { usePreviewState } from '../../hooks/usePreviewState';
import { useQuickCreateListResource } from '../../hooks/useQuickCreateListResource';
import { useSystemVersionResource } from '../../hooks/useSystemVersionResource';
import { useItemsByPath } from '../../hooks/useItemsByPath';
import { lookupItemByPath } from '../../utils/content';
const translations = defineMessages({
quickCreateBtnLabel: {
id: 'quickCreateBtnLabel.label',
defaultMessage: 'Open quick create menu'
},
quickCreateMenuTooltip: {
id: 'previewToolbar.quickCreateMenuTooltip',
defaultMessage: 'Quick create menu'
}
});
const useStyles = makeStyles()((theme) => ({
menu: {
paddingTop: 0,
minWidth: '140px'
},
menuItem: {
fontSize: 14
},
menuTitle: {
fontSize: 14
},
menuSectionTitle: {
fontSize: 12,
backgroundColor: theme.palette.background.default,
color: theme.palette.text.secondary,
padding: '5px 16px'
},
quickCreateEmptyRoot: {
width: '149px',
justifyContent: 'center',
display: 'flex',
flexDirection: 'column',
textAlign: 'center',
alignItems: 'center',
boxShadow: 'none'
},
quickCreateEmptyCardContent: {
padding: '5px 10px'
},
quickCreateEmptyDescription: {
fontSize: '12px'
},
quickCreateEmptyCardActions: {
padding: 0,
'& .MuiButton-root': {
fontSize: '14px',
textDecoration: 'underline',
color: palette.blue.main
}
},
quickCreateLoadingState: {
width: 80
}
}));
export function QuickCreateMenu(props) {
const { open, onClose, anchorEl, resource, onNewContentSelected, onQuickCreateItemSelected, item } = props;
const { classes } = useStyles();
const authoringBase = useSelection((state) => state.env.authoringBase);
const itemNewContentButton = item === null || item === void 0 ? void 0 : item.availableActionsMap.createContent;
const onFormDisplay = ({ contentTypeId, path }) => {
const today = new Date();
const formatPath = path
.replace('{year}', `${today.getFullYear()}`)
.replace('{month}', ('0' + (today.getMonth() + 1)).slice(-2));
onQuickCreateItemSelected === null || onQuickCreateItemSelected === void 0
? void 0
: onQuickCreateItemSelected({
path: formatPath,
contentTypeId,
isNewContent: true,
authoringBase
});
};
return React.createElement(
React.Fragment,
null,
React.createElement(
Menu,
{ classes: { paper: classes.menu }, anchorEl: anchorEl, open: open, onClose: onClose },
itemNewContentButton &&
React.createElement(
MenuItem,
{
className: classes.menuTitle,
onClick: onNewContentSelected,
sx: { borderBottom: 1, borderBottomColor: 'divider' }
},
React.createElement(FormattedMessage, { id: 'quickCreateMenu.title', defaultMessage: 'New Content' })
),
React.createElement(
Typography,
{ component: 'h4', className: classes.menuSectionTitle },
React.createElement(FormattedMessage, { id: 'quickCreateMenu.sectionTitle', defaultMessage: 'Quick Create' })
),
React.createElement(
Suspencified,
{ loadingStateProps: { classes: { graphic: classes.quickCreateLoadingState } } },
React.createElement(QuickCreateSection, { classes: classes, resource: resource, onItemSelected: onFormDisplay })
)
)
);
}
function QuickCreateSection(props) {
const { resource, classes, onItemSelected } = props;
const quickCreateItems = resource.quickCreate.read();
let version = getSimplifiedVersion(resource.version.read());
return React.createElement(
React.Fragment,
null,
quickCreateItems.map((item) =>
React.createElement(
MenuItem,
{ key: item.path, onClick: () => onItemSelected(item), className: classes.menuItem },
item.label
)
),
quickCreateItems.length === 0 &&
React.createElement(
Card,
{ className: classes.quickCreateEmptyRoot },
React.createElement(
CardContent,
{ className: classes.quickCreateEmptyCardContent },
React.createElement(
Typography,
{ color: 'textSecondary', gutterBottom: true },
React.createElement(ErrorOutlineOutlinedIcon, { fontSize: 'small' })
),
React.createElement(
Typography,
{ className: classes.quickCreateEmptyDescription },
React.createElement(FormattedMessage, {
id: 'quickCreateMenu.learnMoreError',
defaultMessage: 'Quick create has not been configured. Please contact your system administrator.'
})
)
),
React.createElement(
CardActions,
{ className: classes.quickCreateEmptyCardActions },
version &&
React.createElement(
Button,
{
size: 'small',
href: `https://docs.craftercms.org/en/${version}/developers/content-modeling.html#setting-up-quick-create`,
target: '_blank',
rel: 'nofollow noreferrer'
},
React.createElement(FormattedMessage, { id: 'quickCreateMenu.learnMore', defaultMessage: 'Learn More' })
)
)
)
);
}
const QuickCreateMenuButton = React.forwardRef(function (props, ref) {
const { onMenuBtnClick, disabled = false } = props;
const { formatMessage } = useIntl();
return React.createElement(
Tooltip,
{ title: disabled ? '' : formatMessage(translations.quickCreateMenuTooltip) },
React.createElement(
IconButton,
{
disabled: disabled,
ref: ref,
size: 'small',
color: 'primary',
onClick: onMenuBtnClick,
'aria-label': formatMessage(translations.quickCreateBtnLabel)
},
React.createElement(AddCircleIcon, { fontSize: 'large' })
)
);
});
const QuickCreate = React.forwardRef(function (props, ref) {
const { item } = props;
const [anchorEl, setAnchorEl] = useState(null);
const [currentPreviewItemPath, setCurrentPreviewItemPath] = useState(null);
const { guest } = usePreviewState();
const dispatch = useDispatch();
const items = useItemsByPath();
const onMenuBtnClick = (e) => {
setAnchorEl(e.currentTarget);
if (guest) {
const { modelId, models } = guest;
const {
craftercms: { path }
} = models[modelId];
setCurrentPreviewItemPath(path);
}
};
const onMenuClose = () => setAnchorEl(null);
const onNewContentSelected = () => {
onMenuClose();
dispatch(
showNewContentDialog({
item: lookupItemByPath(currentPreviewItemPath, items),
// @ts-ignore - required attributes of `showEditDialog` are submitted by new content dialog `onContentTypeSelected` callback and injected into the showEditDialog action by the GlobalDialogManger
onContentTypeSelected: showEditDialog({})
})
);
};
const onQuickCreateItemSelected = (props) => {
onMenuClose();
dispatch(
showEditDialog(
Object.assign(Object.assign({}, props), { inProgress: false, onSaveSuccess: newContentCreationComplete() })
)
);
};
const quickCreateResource = useQuickCreateListResource();
const versionResource = useSystemVersionResource();
return React.createElement(
React.Fragment,
null,
React.createElement(QuickCreateMenuButton, { ref: ref, onMenuBtnClick: onMenuBtnClick }),
React.createElement(QuickCreateMenu, {
item: item,
open: Boolean(anchorEl),
anchorEl: anchorEl,
onClose: onMenuClose,
resource: { quickCreate: quickCreateResource, version: versionResource },
onNewContentSelected: onNewContentSelected,
onQuickCreateItemSelected: onQuickCreateItemSelected
})
);
});
export default QuickCreate;