UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

292 lines (290 loc) 10.4 kB
/* * 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, { forwardRef, 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'; import { processPathMacros } from '../../utils/path'; 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?.availableActionsMap.createContent; const onFormDisplay = (item) => { const { contentTypeId, path } = item; const formatPath = processPathMacros({ path, // Since we can't support these at this stage of creation, at least this will avoid the form opening with an error objectId: '(objectId)', objectGroupId: '(objectGroupId)', fullParentPath: '', useUUID: false }); 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 = forwardRef((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 = forwardRef((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({ ...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;