@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
221 lines (219 loc) • 8 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 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;