@plone/volto
Version:
Volto
279 lines (250 loc) • 7.36 kB
JSX
/**
* Field component.
* @module components/manage/Form/Field
*/
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl } from 'react-intl';
import config from '@plone/volto/registry';
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
const MODE_HIDDEN = 'hidden'; //hidden mode. If mode is hidden, field is not rendered
/**
* Get default widget
* @method getViewDefault
* @param {object} widgets Widgets config
* @returns {string} Widget component.
*/
const getWidgetDefault = (widgets) => widgets.default;
/**
* Get widget by field's `id` attribute
* @method getWidgetById
* @param {object} widgets Widgets config
* @param {string} id Id
* @returns {string} Widget component.
*/
const getWidgetByFieldId = (widgets, id) => widgets.id[id] || null;
/**
* Get widget by factory attribute
* @method getWidgetByFactory
* @param {object} widgets Widgets config
* @param {string} id Id
* @returns {string} Widget component.
*/
const getWidgetByFactory = (widgets, factory) =>
widgets.factory?.[factory] || null;
/**
* Get widget by field's `widget` attribute
* @method getWidgetByName
* @param {object} widgets Widgets config
* @param {string} widget Widget
* @returns {string} Widget component.
*/
const getWidgetByName = (widgets, widget) =>
typeof widget === 'string'
? widgets.widget[widget] || getWidgetDefault(widgets)
: null;
/**
* Get widget by tagged values
* @param {object} widgetOptions
* @param {object} widgets Widgets config
* @returns {string} Widget component.
*
directives.widget(
'fieldname',
frontendOptions={
"widget": 'specialwidget',
"version": 'extra'
})
*/
const getWidgetFromTaggedValues = (widgets, widgetOptions) =>
typeof widgetOptions?.frontendOptions?.widget === 'string'
? widgets.widget[widgetOptions.frontendOptions.widget]
: null;
/**
* Get widget props from tagged values
* @param {object} widgets Widgets config
* @param {object} widgetOptions
* @returns {string} Widget component.
*
directives.widget(
"fieldname",
frontendOptions={
"widget": "specialwidget",
"widgetProps": {"prop1": "specialprop"}
})
*/
const getWidgetPropsFromTaggedValues = (widgets, widgetOptions) =>
typeof widgetOptions?.frontendOptions?.widgetProps === 'object'
? widgetOptions.frontendOptions.widgetProps
: null;
/**
* Get widget by field's `vocabulary` attribute
* @method getWidgetByVocabulary
* @param {object} widgets Widgets config
* @param {string} vocabulary Widget
* @returns {string} Widget component.
*/
const getWidgetByVocabulary = (widgets, vocabulary) =>
vocabulary && vocabulary['@id']
? widgets.vocabulary[vocabulary['@id'].replace(/^.*\/@vocabularies\//, '')]
: null;
/**
* Get widget by field's hints `vocabulary` attribute in widgetOptions
* @method getWidgetByVocabularyFromHint
* @param {object} widgets Widgets config
* @param {string} props Widget props
* @returns {string} Widget component.
*/
const getWidgetByVocabularyFromHint = (widgets, props) =>
props.widgetOptions && props.widgetOptions.vocabulary
? widgets.vocabulary[
props.widgetOptions.vocabulary['@id'].replace(
/^.*\/@vocabularies\//,
'',
)
]
: null;
/**
* Get widget by field's `choices` attribute
* @method getWidgetByChoices
* @param {object} widgets Widgets config
* @param {string} choices Widget
* @returns {string} Widget component.
*/
const getWidgetByChoices = (widgets, props) => {
if (props.choices) {
return widgets.choices;
}
if (props.vocabulary) {
// If vocabulary exists, then it means it's a choice field in disguise with
// no widget specified that probably contains a string then we force it
// to be a select widget instead
return widgets.choices;
}
return null;
};
/**
* Get widget by field's `type` attribute
* @method getWidgetByType
* @param {object} widgets Widgets config
* @param {string} type Type
* @returns {string} Widget component.
*/
const getWidgetByType = (widgets, type) => widgets.type[type] || null;
/**
* Field component class.
* @function Field
* @param {Object} props Properties.
* @returns {string} Markup of the component.
*/
const UnconnectedField = (props, { intl }) => {
const widgets = props.widgets || config.widgets;
const Widget =
getWidgetByFieldId(widgets, props.id) ||
getWidgetFromTaggedValues(widgets, props.widgetOptions) ||
getWidgetByName(widgets, props.widget) ||
getWidgetByChoices(widgets, props) ||
getWidgetByVocabulary(widgets, props.vocabulary) ||
getWidgetByVocabularyFromHint(widgets, props) ||
getWidgetByFactory(widgets, props.factory) ||
getWidgetByType(widgets, props.type) ||
getWidgetDefault(widgets);
if (props.mode === MODE_HIDDEN) {
return null;
}
// Adding the widget props from tagged values (if any)
const widgetProps = {
...props,
...getWidgetPropsFromTaggedValues(widgets, props.widgetOptions),
};
if (props.onOrder) {
const { DropTarget, DragSource } = props.reactDnd;
const WrappedWidget = DropTarget(
'field',
{
hover(properties, monitor) {
const dragOrder = monitor.getItem().order;
const hoverOrder = properties.order;
if (dragOrder === hoverOrder) {
return;
}
properties.onOrder(dragOrder, hoverOrder - dragOrder);
monitor.getItem().order = hoverOrder;
},
},
(connect) => ({
connectDropTarget: connect.dropTarget(),
}),
)(
DragSource(
'field',
{
beginDrag(properties) {
return {
id: properties.label,
order: properties.order,
};
},
},
(connect, monitor) => ({
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging(),
}),
)(
({
connectDropTarget,
connectDragSource,
connectDragPreview,
...rest
}) =>
connectDropTarget(
connectDragSource(
connectDragPreview(
<div>
<Widget {...rest} />
</div>,
),
),
),
),
);
return <WrappedWidget {...widgetProps} />;
}
return <Widget {...widgetProps} />;
};
const DndConnectedField = injectLazyLibs(['reactDnd'])(UnconnectedField);
const Field = (props) =>
props.onOrder ? (
<DndConnectedField {...props} />
) : (
<UnconnectedField {...props} />
);
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
Field.propTypes = {
widget: PropTypes.string,
vocabulary: PropTypes.shape({ '@id': PropTypes.string }),
choices: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
type: PropTypes.string,
id: PropTypes.string.isRequired,
focus: PropTypes.bool,
onOrder: PropTypes.func,
};
/**
* Default properties.
* @property {Object} defaultProps Default properties.
* @static
*/
Field.defaultProps = {
widget: null,
vocabulary: null,
choices: null,
type: 'string',
focus: false,
onOrder: null,
};
export default injectIntl(Field);