diff --git a/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx b/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx
new file mode 100644
index 000000000..10b8e98b1
--- /dev/null
+++ b/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx
@@ -0,0 +1,102 @@
+/**
+ * Edit simple text block.
+ * @module components/Widgets/SimpleTextEditorWidget/SimpleTextEditorWidget
+ *
+ * E' un editor di testo da mettere nei blocchi, senza formattazione.
+ */
+
+import React from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import { defineMessages, useIntl } from 'react-intl';
+import { useInView } from 'react-intersection-observer';
+
+import { handleKeyDetached } from '@plone/volto-slate/blocks/Text/keyboard';
+import {
+ uploadContent,
+ saveSlateBlockSelection,
+} from '@plone/volto-slate/actions';
+import { commonSearchBlockMessages } from '../../../../helpers';
+import config from '@plone/volto/registry';
+
+const messages = defineMessages({
+ text: {
+ id: 'Type text…',
+ defaultMessage: 'Type text…',
+ },
+});
+
+const SimpleTextEditorWidget = (props) => {
+ const intl = useIntl();
+ const {
+ data,
+ setSelected,
+ onSelectBlock,
+ onChangeBlock,
+ block,
+ value,
+ selected,
+ placeholder,
+ } = props;
+ const { ref, inView } = useInView({
+ threshold: 0,
+ rootMargin: '0px 0px 200px 0px',
+ });
+
+ const handleKey = (event) => {
+ const { slate } = config.settings;
+
+ const handlers = slate.textblockDetachedKeyboardHandlers[event.key];
+
+ if (handlers) {
+ // a handler can return `true` to signify it has handled the event in this
+ // case, the execution flow is stopped
+ const handlerProps = { getBlockProps: () => props };
+ return handlers.find((handler) =>
+ handler({ editor: handlerProps, event }),
+ );
+ }
+ };
+
+ return (
+
+
+ );
+};
+
+SimpleTextEditorWidget.propTypes = {
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
+ setSelected: PropTypes.func.isRequired,
+ onSelectBlock: PropTypes.func.isRequired,
+ onChangeBlock: PropTypes.func.isRequired,
+ block: PropTypes.string.isRequired,
+ value: PropTypes.string.isRequired,
+ selected: PropTypes.bool.isRequired,
+ placeholder: PropTypes.string.isRequired,
+ focusPrevField: PropTypes.func.isRequired,
+ focusNextField: PropTypes.func.isRequired,
+ //from block props:
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
+ onFocusPreviousBlock: PropTypes.objectOf(PropTypes.any).isRequired,
+ onFocusNextBlock: PropTypes.objectOf(PropTypes.any).isRequired,
+};
+
+export default SimpleTextEditorWidget;
diff --git a/src/components/ItaliaTheme/manage/Widgets/TextEditorDraftJSWidget.jsx b/src/components/ItaliaTheme/manage/Widgets/TextEditorDraftJSWidget.jsx
deleted file mode 100644
index 3baf8e730..000000000
--- a/src/components/ItaliaTheme/manage/Widgets/TextEditorDraftJSWidget.jsx
+++ /dev/null
@@ -1,299 +0,0 @@
-/**
- * Edit text block.
- * @module components/Widgets/TextEditorDraftJSWidget/TextEditorDraftJSWidget
- */
-
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { compose } from 'redux';
-import { defineMessages, injectIntl } from 'react-intl';
-import { includes, isEqual } from 'lodash';
-import loadable from '@loadable/component';
-import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
-
-import config from '@plone/volto/registry';
-
-const messages = defineMessages({
- text: {
- id: 'Type text…',
- defaultMessage: 'Type text…',
- },
-});
-
-const Editor = loadable(() => import('draft-js-plugins-editor'));
-
-/**
- * TextEditorDraftJSWidget class.
- * @class Edit
- * @extends Component
- */
-class TextEditorDraftJSWidgetComponent extends Component {
- /**
- * Property types.
- * @property {Object} propTypes Property types.
- * @static
- */
- static propTypes = {
- data: PropTypes.objectOf(PropTypes.any).isRequired,
- fieldName: PropTypes.string.isRequired,
- selected: PropTypes.bool.isRequired,
- block: PropTypes.string.isRequired,
- onChangeBlock: PropTypes.func.isRequired,
- placeholder: PropTypes.string,
- focusOn: PropTypes.func,
- nextFocus: PropTypes.any,
- prevFocus: PropTypes.any,
- onFocusNextBlock: PropTypes.any,
- onFocusPreviousBlock: PropTypes.any,
- showToolbar: PropTypes.bool,
- onSelectBlock: PropTypes.func,
- onAddBlock: PropTypes.func,
- disableMoveToNearest: PropTypes.bool,
- };
-
- /**
- * Default properties
- * @property {Object} defaultProps Default properties.
- * @static
- */
- static defaultProps = {
- showToolbar: true,
- };
-
- /**
- * Constructor
- * @method constructor
- * @param {Object} props Component properties
- * @constructs WysiwygEditor
- */
- constructor(props) {
- super(props);
- const { settings } = config;
-
- this.draftConfig = settings.richtextEditorSettings(props);
-
- const { EditorState, convertFromRaw } = props.draftJs;
- const createInlineToolbarPlugin = props.draftJsInlineToolbarPlugin.default;
-
- if (!__SERVER__) {
- let editorState;
- if (props.data && props.data[props.fieldName]) {
- editorState = EditorState.createWithContent(
- convertFromRaw(props.data[props.fieldName]),
- );
- } else {
- editorState = EditorState.createEmpty();
- }
-
- const inlineToolbarPlugin = createInlineToolbarPlugin({
- structure: this.draftConfig.richTextEditorInlineToolbarButtons,
- });
-
- this.state = {
- editorState,
- inlineToolbarPlugin,
- addNewBlockOpened: false,
- };
- }
- }
-
- /**
- * Component will receive props
- * @method componentDidMount
- * @returns {undefined}
- */
- componentDidMount() {
- if (this.props.selected && this.node) {
- setTimeout(this.node.focus, 0);
- }
- }
-
- /**
- * Component will receive props
- * @method componentWillReceiveProps
- * @param {Object} nextProps Next properties
- * @returns {undefined}
- */
- UNSAFE_componentWillReceiveProps(nextProps) {
- if (!this.props.selected && nextProps.selected) {
- // See https://github.com/draft-js-plugins/draft-js-plugins/issues/800
- setTimeout(this.node.focus, 0);
- const { EditorState } = this.props.draftJs;
-
- this.setState({
- editorState: EditorState.moveFocusToEnd(this.state.editorState),
- });
- }
- }
-
- /**
- * Change handler
- * @method onChange
- * @param {object} editorState Editor state.
- * @returns {undefined}
- */
- onChange = (editorState) => {
- const { convertToRaw } = this.props.draftJs;
- if (
- !isEqual(
- convertToRaw(editorState.getCurrentContent()),
- convertToRaw(this.state.editorState.getCurrentContent()),
- )
- ) {
- this.props.onChangeBlock({
- ...this.props.data,
- [this.props.fieldName]: convertToRaw(editorState.getCurrentContent()),
- });
- }
- this.setState({ editorState });
- };
-
- /**
- * Render method.
- * @method render
- * @returns {string} Markup for the component.
- */
- render() {
- if (__SERVER__) {
- return ;
- }
-
- const { InlineToolbar } = this.state.inlineToolbarPlugin;
- let placeholder = this.props.placeholder
- ? this.props.placeholder
- : this.props.intl.formatMessage(messages.text);
- let disableMoveToNearest = this.props.disableMoveToNearest;
- const isSoftNewlineEvent = this.props.draftJsLibIsSoftNewlineEvent.default;
- const { RichUtils } = this.props.draftJs;
-
- return (
- <>
-
- {
- // if (disableMoveToNearest) {
- // e.stopPropagation();
- // }
- if (isSoftNewlineEvent(e)) {
- this.onChange(
- RichUtils.insertSoftNewline(this.state.editorState),
- );
- return 'handled';
- }
-
- if (
- !disableMoveToNearest &&
- this.props.onSelectBlock &&
- this.props.onAddBlock
- ) {
- const selectionState = this.state.editorState.getSelection();
- const anchorKey = selectionState.getAnchorKey();
- const currentContent = this.state.editorState.getCurrentContent();
- const currentContentBlock = currentContent.getBlockForKey(
- anchorKey,
- );
- const blockType = currentContentBlock.getType();
- if (!includes(this.draftConfig.listBlockTypes, blockType)) {
- this.props.onSelectBlock(
- this.props.onAddBlock('text', this.props.index + 1),
- );
- return 'handled';
- }
- return 'un-handled';
- }
-
- return {};
- }}
- onUpArrow={(e) => {
- if (this.props.prevFocus) {
- this.props.setFocus(this.props.prevFocus);
- e.stopPropagation();
- } else {
- if (this.props.disableMoveToNearest) {
- e.stopPropagation();
- } else {
- if (this.props.onFocusPreviousBlock) {
- const selectionState = this.state.editorState.getSelection();
- const currentCursorPosition = selectionState.getStartOffset();
-
- if (currentCursorPosition === 0) {
- this.props.onFocusPreviousBlock(
- this.props.block,
- this.node,
- );
- }
- }
- }
- }
- }}
- onDownArrow={(e) => {
- if (this.props.nextFocus) {
- this.props.setFocus(this.props.nextFocus);
- e.stopPropagation();
- } else {
- if (this.props.disableMoveToNearest) {
- e.stopPropagation();
- } else {
- if (this.props.onFocusNextBlock) {
- const selectionState = this.state.editorState.getSelection();
- const { editorState } = this.state;
- const currentCursorPosition = selectionState.getStartOffset();
- const blockLength = editorState
- .getCurrentContent()
- .getFirstBlock()
- .getLength();
-
- if (currentCursorPosition === blockLength) {
- this.props.onFocusNextBlock(this.props.block, this.node);
- }
- }
- }
- }
- }}
- ref={(node) => {
- this.node = node;
- }}
- />
- {this.props.showToolbar && this.node && }
-
- >
- );
- }
-}
-
-export const TextEditorDraftJSWidget = React.memo(
- compose(
- injectIntl,
- injectLazyLibs([
- 'draftJs',
- 'draftJsLibIsSoftNewlineEvent',
- 'draftJsFilters',
- 'draftJsInlineToolbarPlugin',
- 'draftJsBlockBreakoutPlugin',
- 'draftJsCreateInlineStyleButton',
- 'draftJsCreateBlockStyleButton',
- 'immutableLib',
- // TODO: add all plugin dependencies, also in Wysiwyg and Cell
- ]),
- )(TextEditorDraftJSWidgetComponent),
-);
-
-const Preloader = (props) => {
- const [loaded, setLoaded] = React.useState(false);
- React.useEffect(() => {
- Editor.load().then(() => setLoaded(true));
- }, []);
- return loaded ? : null;
-};
-
-export default Preloader;
diff --git a/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx b/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
index 27687d353..107b48ad6 100644
--- a/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
+++ b/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
@@ -1,15 +1,25 @@
/**
* Edit text block.
* @module components/Widgets/TextEditorWidget/TextEditorWidget
+ *
+ * E' come il componente DetatchedTextBlockEditor di @plone/volto-slate,
+ * ma in più ha il withBlockProperties,
+ * che serve per getire gli handler (le function di focusPrev e focusNext)
*/
import React from 'react';
-import { defineMessages } from 'react-intl';
+import { connect } from 'react-redux';
import PropTypes from 'prop-types';
-
-import config from '@plone/volto/registry';
-import { TextEditorDraftJSWidget } from './TextEditorDraftJSWidget';
-import { TextBlockEdit } from '@plone/volto-slate/Blocks/Text';
+import { defineMessages, useIntl } from 'react-intl';
+import { useInView } from 'react-intersection-observer';
+import { SlateEditor } from '@plone/volto-slate/editor';
+import { serializeNodesToText } from '@plone/volto-slate/editor/render';
+import { handleKeyDetached } from '@plone/volto-slate/blocks/Text/keyboard';
+import {
+ uploadContent,
+ saveSlateBlockSelection,
+} from '@plone/volto-slate/actions';
+import SimpleTextEditorWidget from './SimpleTextEditorWidget';
const messages = defineMessages({
text: {
@@ -18,16 +28,40 @@ const messages = defineMessages({
},
});
-//[ToDo]: se togliamo completamente draftjs, togliere la prop editor_type, la condizione e il widget di TextEditorDraftJSWidget
+const TextEditorWidget = (props) => {
+ const {
+ showToolbar = true,
+ setSelected,
+ wrapClass,
+ index,
+ properties,
+ value,
+ block,
+ selected,
+ onSelectBlock,
+ onChangeBlock,
+ data,
+ ...otherProps
+ } = props;
+
+ const withBlockProperties = React.useCallback(
+ (editor) => {
+ editor.getBlockProps = () => props;
+ return editor;
+ },
+ [props],
+ );
+
+ const intl = useIntl();
+ const placeholder =
+ otherProps.placeholder || intl.formatMessage(messages.text);
-const TextEditorWidget = ({
- editor_type = 'slate',
- showToolbar = true,
- setSelected,
- wrapClass,
- ...props
-}) => {
- return editor_type === 'slate' ? (
+ const { ref, inView } = useInView({
+ threshold: 0,
+ rootMargin: '0px 0px 200px 0px',
+ });
+
+ return (
setSelected()}
@@ -36,16 +70,54 @@ const TextEditorWidget = ({
role="textbox"
tabIndex="-1"
>
-
+ {showToolbar ? (
+
+ {
+ if (!selected) {
+ if (onSelectBlock) {
+ onSelectBlock(block);
+ } else {
+ setSelected();
+ }
+ }
+ }}
+ onChange={(value, selection, editor) => {
+ onChangeBlock(block, {
+ ...data,
+ value,
+ plaintext: serializeNodesToText(value || []),
+ // TODO: also add html serialized value
+ });
+ }}
+ selected={selected}
+ placeholder={placeholder}
+ onKeyDown={handleKeyDetached}
+ editableProps={{ 'aria-multiline': 'true' }}
+ showToolbar={showToolbar}
+ />
+
+ ) : (
+
+
+
+ )}
- ) : (
-
);
};
TextEditorWidget.propTypes = {
- editor_type: PropTypes.string.isRequired,
- data: PropTypes.object.isRequired,
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
setSelected: PropTypes.func.isRequired,
onSelectBlock: PropTypes.func.isRequired,
onChangeBlock: PropTypes.func.isRequired,
@@ -53,6 +125,27 @@ TextEditorWidget.propTypes = {
selected: PropTypes.bool.isRequired,
showToolbar: PropTypes.bool,
wrapClass: PropTypes.string,
+ focusPrevField: PropTypes.func.isRequired,
+ focusNextField: PropTypes.func.isRequired,
+ //from block props:
+ properties: PropTypes.objectOf(PropTypes.any).isRequired,
+ onFocusPreviousBlock: PropTypes.objectOf(PropTypes.any).isRequired,
+ onFocusNextBlock: PropTypes.objectOf(PropTypes.any).isRequired,
};
-export default TextEditorWidget;
+export default connect(
+ (state, props) => {
+ const blockId = props.block;
+ return {
+ defaultSelection: blockId
+ ? state.slate_block_selections?.[blockId]
+ : null,
+ uploadRequest: state.upload_content?.[props.block]?.upload || {},
+ uploadedContent: state.upload_content?.[props.block]?.data || {},
+ };
+ },
+ {
+ uploadContent,
+ saveSlateBlockSelection, // needed as editor blockProps
+ },
+)(TextEditorWidget);
diff --git a/src/config/Blocks/blocks.js b/src/config/Blocks/blocks.js
index 76ca118e5..84c25385c 100644
--- a/src/config/Blocks/blocks.js
+++ b/src/config/Blocks/blocks.js
@@ -379,6 +379,7 @@ const italiaBlocks = {
view: [],
},
sidebarTab: 1,
+ blockHasOwnFocusManagement: true,
},
};
diff --git a/src/config/Slate/config.js b/src/config/Slate/config.js
index d2de6fbc2..5abb71f72 100644
--- a/src/config/Slate/config.js
+++ b/src/config/Slate/config.js
@@ -7,6 +7,7 @@ import installLinkButton from 'design-comuni-plone-theme/config/Slate/LinkButton
import installTextLarger from 'design-comuni-plone-theme/config/Slate/TextLarger';
import installLink from 'design-comuni-plone-theme/config/Slate/Link';
+import installHandlers from 'design-comuni-plone-theme/config/Slate/handlers';
export default function applyItaliaSlateConfig(config) {
installAlignment(config);
@@ -14,10 +15,11 @@ export default function applyItaliaSlateConfig(config) {
installUnderline(config);
installTextLarger(config);
installLink(config);
-
installBlockquote(config);
installLinkButton(config);
+ installHandlers(config);
+
//remove callout because there's a Volto's block for it
delete config.settings.slate.elements.callout;
diff --git a/src/config/Slate/handlers.js b/src/config/Slate/handlers.js
new file mode 100644
index 000000000..3c9f5f00c
--- /dev/null
+++ b/src/config/Slate/handlers.js
@@ -0,0 +1,58 @@
+import {
+ goDown,
+ goUp,
+ softBreak,
+} from '@plone/volto-slate/blocks/Text/keyboard';
+
+import {
+ isCursorAtBlockStart,
+ isCursorAtBlockEnd,
+} from '@plone/volto-slate/utils';
+
+const focusPrev = (props) => {
+ const { focusPrevField, showToolbar } = props.editor.getBlockProps();
+
+ let isAtStart = false;
+
+ if (showToolbar) {
+ isAtStart = isCursorAtBlockStart(props.editor);
+ } else {
+ isAtStart = props.event.target.selectionStart === 0;
+ }
+
+ if (focusPrevField && isAtStart) {
+ props.event.stopPropagation();
+ return focusPrevField();
+ }
+ return goUp(props); // Select prev block
+};
+
+const focusNext = (props) => {
+ const { focusNextField, showToolbar } = props.editor.getBlockProps();
+
+ let isAtEnd = false;
+ if (showToolbar) {
+ isAtEnd = isCursorAtBlockEnd(props.editor);
+ } else {
+ isAtEnd =
+ props.event.target.selectionEnd === props.event.target.value.length;
+ }
+ if (focusNextField && isAtEnd) {
+ props.event.stopPropagation();
+ return focusNextField();
+ }
+ return goDown(props); // Select next block
+};
+export default function install(config) {
+ config.settings.slate.textblockDetachedKeyboardHandlers = {
+ ...config.settings.slate.textblockDetachedKeyboardHandlers,
+ Enter: [
+ ...config.settings.slate.textblockDetachedKeyboardHandlers.Enter,
+ focusNext,
+ ],
+ ArrowUp: [focusPrev],
+ ArrowDown: [focusNext],
+ };
+
+ return config;
+}