UNPKG

@craftercms/studio-ui

Version:

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

221 lines (219 loc) 8 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 from 'react'; import ToolPanel from '../ToolPanel/ToolPanel'; import CloseRounded from '@mui/icons-material/CloseRounded'; import Typography from '@mui/material/Typography'; import { useDispatch } from 'react-redux'; import { defineMessages, useIntl } from 'react-intl'; import { nnou } from '../../utils/object'; import * as ModelHelper from '../../utils/model'; import { findParentModelId } from '../../utils/model'; import { popPiece } from '../../utils/string'; import { showCodeEditorDialog, showEditDialog } from '../../state/actions/dialogs'; import { getField } from '../../utils/contentType'; import { Menu, MenuItem } from '@mui/material'; import { useSelection } from '../../hooks/useSelection'; import { useActiveSiteId } from '../../hooks/useActiveSiteId'; import { usePreviewState } from '../../hooks/usePreviewState'; const getEditDialogProps = (props) => { const { authoringBase, hierarchyMap, model, models, path, selectedId, site, selectedFields } = props; if (path) { return { authoringBase, site, path, ...(selectedFields ? { selectedFields } : {}) }; } else { let parentPath; if (model === models[selectedId]) { let parentId = findParentModelId(model.craftercms.id, hierarchyMap, models); parentPath = models[parentId].craftercms.path; } else { parentPath = models[model.craftercms.id].craftercms.path; } return { authoringBase, site, path: parentPath, isHidden: true, modelId: selectedId, ...(selectedFields ? { selectedFields } : {}) }; } }; const translations = defineMessages({ openComponentForm: { id: 'previewEditFormTool.openComponentForm', defaultMessage: 'Edit' }, editTemplate: { id: 'previewEditFormTool.editTemplate', defaultMessage: 'Edit Template' }, editController: { id: 'previewEditFormTool.editController', defaultMessage: 'Edit Controller' } }); export function EditFormPanel(props) { const { showToolsPanel, guest } = usePreviewState(); const { selected, models, hierarchyMap, modelIdByPath } = guest ?? {}; const getAnchorPosition = () => { let iframe = document.getElementById('crafterCMSPreviewIframe'); let bounding = iframe.getBoundingClientRect(); let selection = selected[0]; return { top: bounding.top + selection.coordinates.y, left: (showToolsPanel ? bounding.left : 0) + selection.coordinates.x }; }; return React.createElement( Menu, { open: props.open && Boolean(selected), onClose: props.onDismiss, anchorReference: 'anchorPosition', anchorPosition: props.open ? getAnchorPosition() : null }, React.createElement( 'div', null, selected ? React.createElement(EditFormPanelBody, { models: models, selected: selected, hierarchyMap: hierarchyMap, modelIdByPath: modelIdByPath, onDismiss: props.onDismiss }) : '' ) ); } function EditFormPanelBody(props) { const { onDismiss, selected, models, modelIdByPath, hierarchyMap } = props; const dispatch = useDispatch(); const contentTypesBranch = useSelection((state) => state.contentTypes); const site = useActiveSiteId(); const { formatMessage } = useIntl(); const authoringBase = useSelection((state) => state.env.authoringBase); const item = selected[0]; const fieldId = item.fieldId[0]; let model = models[item.modelId]; let contentType = contentTypesBranch.byId[model.craftercms.contentTypeId]; const levelDescriptorPath = model.craftercms.sourceMap?.[fieldId]; if (levelDescriptorPath) { // Is inherited field const modelId = modelIdByPath[levelDescriptorPath]; model = models[modelId]; contentType = contentTypesBranch.byId[model.craftercms.contentTypeId]; } const field = fieldId ? getField(contentType, fieldId) : null; let title; let selectedId; if (nnou(field)) { if (field.type === 'node-selector' && nnou(item.index)) { let component; if (nnou(fieldId) && fieldId.includes('.')) { const aux = ModelHelper.extractCollectionItem(model, fieldId, item.index); component = models[aux]; } else { const id = ModelHelper.value(model, fieldId)[item.index]; component = models[id]; } if (component) { selectedId = component.craftercms.id; title = `${component.craftercms.label} (${contentType.name})`; } else { selectedId = item.modelId; title = `${field.name} (${contentType.name})`; } } else { selectedId = item.modelId; title = `${field.name} (${contentType.name})`; } } else { selectedId = item.modelId; title = `${model.craftercms.label} (${contentType.name})`; } const path = ModelHelper.prop(models[selectedId], 'path'); const selectedContentTypeId = contentType.id; function openDialog(type) { onDismiss(); if (type === 'form') { const selectedFields = selected[0]?.fieldId.length ? selected[0].fieldId : null; dispatch( showEditDialog( getEditDialogProps({ authoringBase, hierarchyMap, model, models, path, selectedFields, selectedId, site }) ) ); } else { dispatch( showCodeEditorDialog({ path: type === 'template' ? contentType.displayTemplate : `/scripts/pages/${popPiece(selectedContentTypeId, '/')}.groovy`, contentType: selectedContentTypeId, mode: type === 'template' ? 'ftl' : 'groovy' }) ); } } if (selected.length > 1) { // TODO: Implement Multi-mode... return React.createElement( React.Fragment, null, React.createElement( ToolPanel, { BackIcon: CloseRounded, title: 'Not Implemented.' }, React.createElement(Typography, null, 'This condition is not implemented yet.') ) ); } return React.createElement( React.Fragment, null, React.createElement(Typography, { variant: 'caption', style: { padding: '0 16px 5px' }, component: 'p' }, title), React.createElement(MenuItem, { onClick: () => openDialog('form') }, formatMessage(translations.openComponentForm)), React.createElement(MenuItem, { onClick: () => openDialog('template') }, formatMessage(translations.editTemplate)), selectedContentTypeId.includes('/page') && React.createElement( MenuItem, { onClick: () => openDialog('controller') }, formatMessage(translations.editController) ) ); } export default EditFormPanel;