@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
369 lines (367 loc) • 10.6 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 Dialog from '@mui/material/Dialog';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { makeStyles } from 'tss-react/mui';
import Typography from '@mui/material/Typography';
import Checkbox from '@mui/material/Checkbox';
import IconButton from '@mui/material/IconButton';
import MoreVertIcon from '@mui/icons-material/MoreVertRounded';
import ContextMenu from '../ContextMenu';
import { markForTranslation } from '../../services/translation';
import { showErrorDialog } from '../../state/reducers/dialogs/error';
import { useDispatch } from 'react-redux';
import palette from '../../styles/palette';
import DialogBody from '../DialogBody/DialogBody';
import DialogHeader from '../DialogHeader';
import SingleItemSelector from '../SingleItemSelector';
import ActionsBar from '../ActionsBar';
import { useActiveSiteId } from '../../hooks/useActiveSiteId';
import { useUnmount } from '../../hooks/useUnmount';
const translations = defineMessages({
mark: {
id: 'contentLocalization.mark',
defaultMessage: 'Mark for translation'
},
approveTranslation: {
id: 'contentLocalization.approve',
defaultMessage: 'Approve translation'
},
deleteTranslation: {
id: 'contentLocalization.delete',
defaultMessage: 'Delete translation'
},
locales: {
id: 'words.locales',
defaultMessage: 'Locales'
},
status: {
id: 'words.status',
defaultMessage: 'Status'
},
edit: {
id: 'words.edit',
defaultMessage: 'Edit'
},
schedule: {
id: 'words.schedule',
defaultMessage: 'Schedule'
},
delete: {
id: 'words.delete',
defaultMessage: 'Delete'
},
approve: {
id: 'words.approve',
defaultMessage: 'Approve'
},
review: {
id: 'words.review',
defaultMessage: 'Review'
}
});
const useStyles = makeStyles()((theme) => ({
singleItemSelector: {
marginBottom: '10px'
},
contentLocalizationRoot: {
background: palette.white,
border: '1px solid rgba(0, 0, 0, .125)',
minHeight: '30vh',
'& header': {
marginBottom: '5px'
}
},
icon: {
marginLeft: 'auto',
padding: '9px'
},
checkbox: {
color: theme.palette.primary.main
},
flex: {
display: 'flex',
alignItems: 'center'
},
headerTitle: {
fontWeight: 'bold',
paddingRight: '20px'
},
locale: {
paddingRight: '20px'
},
width30: {
width: '30%'
},
menuPaper: {
width: '182px'
},
menuList: {
padding: 0
},
menuItemRoot: {
whiteSpace: 'initial'
}
}));
const localizationMap = {
en: 'English, US (en)',
en_gb: 'English, UK (en_gb)',
es: 'Spanish, Spain (es)',
fr: 'French (fr)',
de: 'German (de)'
};
const menuSections = [
{
id: 'edit',
label: translations.edit
},
{
id: 'review',
label: translations.review
},
{
id: 'mark',
label: translations.mark
},
{
id: 'approve',
label: translations.approve
},
{
id: 'delete',
label: translations.delete
}
];
const menuOptions = [
{
id: 'edit',
label: translations.edit
},
{
id: 'schedule',
label: translations.schedule
},
{
id: 'delete',
label: translations.delete
},
{
id: 'approve',
label: translations.approve
}
];
export function ContentLocalizationDialog(props) {
const { open, onClose } = props;
return React.createElement(
Dialog,
{ open: open, onClose: onClose, fullWidth: true },
React.createElement(ContentLocalizationDialogUI, Object.assign({}, props))
);
}
function ContentLocalizationDialogUI(props) {
const { formatMessage } = useIntl();
const dispatch = useDispatch();
const { classes, cx } = useStyles();
const { onClose, locales, item, rootPath, onItemChange } = props;
const [selected, setSelected] = useState([]);
const [openSelector, setOpenSelector] = useState(false);
const site = useActiveSiteId();
const [menu, setMenu] = useState({
activeItem: null,
anchorEl: null
});
const onOpenCustomMenu = (locale, anchorEl) => {
setMenu({
activeItem: locale,
anchorEl
});
};
const onCloseCustomMenu = () => {
setMenu({
activeItem: null,
anchorEl: null
});
};
const onMenuItemClicked = (option) => {
switch (option) {
case 'mark': {
markForTranslation(site, menu.activeItem.path, menu.activeItem.localeCode).subscribe(
() => {
setMenu({
activeItem: null,
anchorEl: null
});
},
({ response }) => {
dispatch(
showErrorDialog({
error: response
})
);
}
);
break;
}
default:
break;
}
};
const handleSelect = (checked, id) => {
const _selected = [...selected];
if (checked) {
if (!_selected.includes(id)) {
_selected.push(id);
}
} else {
let index = _selected.indexOf(id);
if (index >= 0) {
_selected.splice(index, 1);
}
}
setSelected(_selected);
};
const toggleSelectAll = () => {
if (locales.length === selected.length) {
setSelected([]);
} else {
setSelected(locales.map((locale) => locale.id));
}
};
const onOptionClicked = (option) => {
// TODO: Widget menu option clicked
};
useUnmount(props.onClosed);
return React.createElement(
React.Fragment,
null,
React.createElement(DialogHeader, {
title: React.createElement(FormattedMessage, {
id: 'contentLocalization.title',
defaultMessage: 'Content Localization'
}),
onCloseButtonClick: onClose
}),
React.createElement(
DialogBody,
null,
React.createElement(SingleItemSelector, {
label: React.createElement(FormattedMessage, { id: 'words.item', defaultMessage: 'Item' }),
classes: { root: classes.singleItemSelector },
open: openSelector,
onClose: () => setOpenSelector(false),
onDropdownClick: () => setOpenSelector(!openSelector),
rootPath: rootPath,
selectedItem: item,
onItemClicked: (item) => {
onItemChange(item);
setOpenSelector(false);
}
}),
React.createElement(
'section',
{ className: classes.contentLocalizationRoot },
selected.length > 0
? React.createElement(ActionsBar, {
isIndeterminate: selected.length > 0 && selected.length < locales.length,
onOptionClicked: onOptionClicked,
options: menuOptions,
isChecked: selected.length === locales.length,
onCheckboxChange: toggleSelectAll
})
: React.createElement(
'header',
{ className: classes.flex },
React.createElement(Checkbox, {
color: 'primary',
className: classes.checkbox,
onChange: toggleSelectAll
}),
React.createElement(
React.Fragment,
null,
React.createElement(
Typography,
{ variant: 'subtitle2', className: cx(classes.headerTitle, classes.width30) },
formatMessage(translations.locales)
),
React.createElement(
Typography,
{ variant: 'subtitle2', className: classes.headerTitle },
formatMessage(translations.status)
)
)
),
locales === null || locales === void 0
? void 0
: locales.map((locale) =>
React.createElement(
'div',
{ className: classes.flex, key: locale.id },
React.createElement(Checkbox, {
color: 'primary',
className: classes.checkbox,
checked: selected === null || selected === void 0 ? void 0 : selected.includes(locale.id),
onChange: (event) => handleSelect(event.currentTarget.checked, locale.id)
}),
React.createElement(
Typography,
{ variant: 'subtitle2', className: cx(classes.locale, classes.width30) },
localizationMap[locale.localeCode]
),
React.createElement(Typography, { variant: 'subtitle2', className: classes.locale }, locale.status),
React.createElement(
IconButton,
{
'aria-label': 'options',
className: classes.icon,
onClick: (e) => onOpenCustomMenu(locale, e.currentTarget),
size: 'large'
},
React.createElement(MoreVertIcon, null)
)
)
)
)
),
React.createElement(ContextMenu, {
anchorEl: menu.anchorEl,
open: Boolean(menu.anchorEl),
classes: {
paper: classes.menuPaper
},
onClose: onCloseCustomMenu,
options: [menuSections],
onMenuItemClicked: onMenuItemClicked
})
);
}
export default ContentLocalizationDialog;