@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
192 lines (190 loc) • 6.78 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 from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { getHostToGuestBus } from '../../utils/subjects';
import { makeStyles } from 'tss-react/mui';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import List from '@mui/material/List';
import Avatar from '@mui/material/Avatar';
import MoveToInboxRounded from '@mui/icons-material/MoveToInboxRounded';
import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select';
import { useDispatch } from 'react-redux';
import {
clearHighlightedDropTargets,
clearDropTargets,
contentTypeDropTargetsRequest,
scrollToDropTarget,
setPreviewEditMode
} from '../../state/actions/preview';
import { SuspenseWithEmptyState } from '../Suspencified/Suspencified';
import { useSelection } from '../../hooks/useSelection';
import { useLogicResource } from '../../hooks/useLogicResource';
import { useMount } from '../../hooks/useMount';
const translations = defineMessages({
dropTargetsPanel: {
id: 'previewDropTargetsPanel.title',
defaultMessage: 'Component Drop Targets'
},
selectContentType: {
id: 'previewDropTargetsPanel.selectContentType',
defaultMessage: 'Select content type'
},
noResults: {
id: 'previewDropTargetsPanel.noResults',
defaultMessage: 'No results found.'
},
chooseContentType: {
id: 'previewDropTargetsPanel.chooseContentType',
defaultMessage: 'Please choose a content type.'
}
});
const useStyles = makeStyles()(() => ({
select: {
width: '100%',
padding: '15px',
'& > div': {
width: '100%'
}
}
}));
export function PreviewDropTargetsPanel() {
const { classes } = useStyles();
const hostToGuest$ = getHostToGuestBus();
const dropTargetsBranch = useSelection((state) => state.preview.dropTargets);
const contentTypesBranch = useSelection((state) => state.contentTypes);
const editMode = useSelection((state) => state.preview.editMode);
const contentTypes = contentTypesBranch.byId
? Object.values(contentTypesBranch.byId).filter((contentType) => contentType.type === 'component')
: null;
const { formatMessage } = useIntl();
const dispatch = useDispatch();
useMount(() => {
return () => {
dispatch(clearDropTargets());
hostToGuest$.next({
type: clearHighlightedDropTargets.type
});
};
});
const onSelectedDropZone = (dropTarget) => {
if (!editMode) {
dispatch(setPreviewEditMode({ editMode: true }));
}
hostToGuest$.next({
type: scrollToDropTarget.type,
payload: dropTarget
});
};
function handleSelectChange(contentTypeId) {
hostToGuest$.next({
type: contentTypeDropTargetsRequest.type,
payload: contentTypeId
});
}
const dropTargetsResource = useLogicResource(dropTargetsBranch, {
shouldResolve: (source) => source.selectedContentType === null || Boolean(source.byId),
shouldReject: (source) => false,
shouldRenew: (source, resource) => resource.complete,
resultSelector: (source) =>
source.byId
? Object.values(source.byId).filter(
(dropTarget) => dropTarget.contentTypeId === dropTargetsBranch.selectedContentType
)
: [],
errorSelector: (source) => null
});
return React.createElement(
React.Fragment,
null,
React.createElement(
'div',
{ className: classes.select },
React.createElement(
Select,
{
value: dropTargetsBranch.selectedContentType || '',
displayEmpty: true,
onChange: (event) => handleSelectChange(event.target.value)
},
React.createElement(MenuItem, { value: '', disabled: true }, formatMessage(translations.selectContentType)),
contentTypes === null || contentTypes === void 0
? void 0
: contentTypes.map((contentType, i) => {
return React.createElement(MenuItem, { value: contentType.id, key: i }, contentType.name);
})
)
),
React.createElement(
List,
null,
React.createElement(
SuspenseWithEmptyState,
{
resource: dropTargetsResource,
withEmptyStateProps: {
emptyStateProps: {
title: dropTargetsBranch.selectedContentType
? formatMessage(translations.noResults)
: formatMessage(translations.chooseContentType)
}
}
},
React.createElement(DropTargetsList, { resource: dropTargetsResource, onSelectedDropZone: onSelectedDropZone })
)
)
);
}
function DropTargetsList(props) {
const dropTargets = props.resource.read();
return React.createElement(
React.Fragment,
null,
dropTargets === null || dropTargets === void 0
? void 0
: dropTargets.map((dropTarget) =>
React.createElement(
ListItem,
{ key: dropTarget.id, button: true, onClick: () => props.onSelectedDropZone(dropTarget) },
React.createElement(
ListItemAvatar,
null,
React.createElement(Avatar, null, React.createElement(MoveToInboxRounded, null))
),
React.createElement(ListItemText, { primary: dropTarget.label })
)
)
);
}
export default PreviewDropTargetsPanel;