+
{
className={cx('block simple-text-block', {
'public-ui': inEditMode,
})}
+ {...(inEditMode ? { tabIndex: '-1' } : {})}
>
{children}
diff --git a/src/components/ItaliaTheme/Blocks/TextCard/SimpleCard/Edit.jsx b/src/components/ItaliaTheme/Blocks/TextCard/SimpleCard/Edit.jsx
index 6eabc1a98..7caf1d679 100644
--- a/src/components/ItaliaTheme/Blocks/TextCard/SimpleCard/Edit.jsx
+++ b/src/components/ItaliaTheme/Blocks/TextCard/SimpleCard/Edit.jsx
@@ -3,12 +3,13 @@
* @module components/Blocks/TitleVM/Edit
*/
-import React, { useState, useEffect } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, useIntl } from 'react-intl';
import { Card, CardBody, CardTitle, CardText } from 'design-react-kit';
import { TextEditorWidget } from 'design-comuni-plone-theme/components/ItaliaTheme';
import BodyWrapper from './BodyWrapper';
+import { useHandleDetachedBlockFocus } from 'design-comuni-plone-theme/helpers/blocks';
const messages = defineMessages({
simple_card_title: {
@@ -29,30 +30,21 @@ const messages = defineMessages({
* @class Edit
* @extends Component
*/
-const Edit = ({
- data,
- onChangeBlock,
- block,
- onSelectBlock,
- selected,
- ...otherProps
-}) => {
+const Edit = (props) => {
+ const {
+ data,
+ onChangeBlock,
+ block,
+ onSelectBlock,
+ selected,
+ ...otherProps
+ } = props;
const intl = useIntl();
- const [selectedField, setSelectedField] = useState('title');
- useEffect(() => {
- if (selected && !selectedField) {
- setSelectedField('title');
- } else if (!selected) {
- setSelectedField(null);
- }
- }, [selected]);
-
- useEffect(() => {
- if (!selected && selectedField) {
- onSelectBlock(block);
- }
- }, [selectedField]);
+ const { selectedField, setSelectedField } = useHandleDetachedBlockFocus(
+ props,
+ 'simple_card_title',
+ );
return __SERVER__ ? (
@@ -75,15 +67,13 @@ const Edit = ({
data={data}
block={block}
fieldName="simple_card_title"
- selected={selectedField === 'title'}
+ selected={selectedField === 'simple_card_title'}
onChangeBlock={onChangeBlock}
onSelectBlock={onSelectBlock}
placeholder={intl.formatMessage(messages.simple_card_title)}
- setSelected={() => {
- setSelectedField('title');
- }}
+ setSelected={setSelectedField}
focusNextField={() => {
- setSelectedField('content');
+ setSelectedField('simple_card_content');
}}
/>
@@ -94,10 +84,8 @@ const Edit = ({
showToolbar={true}
data={data}
fieldName="simple_card_content"
- selected={selectedField === 'content'}
- setSelected={() => {
- setSelectedField('content');
- }}
+ selected={selectedField === 'simple_card_content'}
+ setSelected={setSelectedField}
block={block}
onChangeBlock={onChangeBlock}
onSelectBlock={onSelectBlock}
@@ -105,7 +93,7 @@ const Edit = ({
messages.simple_card_content,
)}
focusPrevField={() => {
- setSelectedField('title');
+ setSelectedField('simple_card_title');
}}
/>
diff --git a/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx b/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx
index 21675f70d..da8cbdf07 100644
--- a/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx
+++ b/src/components/ItaliaTheme/manage/Widgets/SimpleTextEditorWidget.jsx
@@ -52,6 +52,7 @@ const SimpleTextEditorWidget = (props) => {
getBlockProps: () => {
return { ...props };
},
+ getSavedSelection: () => null,
};
return handlers.find((handler) =>
handler({ editor: handlerProps, event }),
@@ -73,6 +74,14 @@ const SimpleTextEditorWidget = (props) => {
}
}, []);
+ const selectThis = () => {
+ if (setSelected) {
+ setSelected(fieldName ?? true);
+ } else if (onSelectBlock) {
+ onSelectBlock(block);
+ }
+ };
+
return (
{
}}
onFocus={(e) => {
if (!selected) {
- if (onSelectBlock) {
- onSelectBlock(block);
- } else {
- setSelected();
- }
+ selectThis();
}
}}
+ onBlur={(e) => {
+ setSelected(fieldName ? null : false);
+ }}
onKeyDown={handleKey}
ref={fieldRef}
/>
diff --git a/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx b/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
index 603017863..838c13764 100644
--- a/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
+++ b/src/components/ItaliaTheme/manage/Widgets/TextEditorWidget.jsx
@@ -74,12 +74,12 @@ const TextEditorWidget = (props) => {
const selectThis = () => {
if (setSelected) {
- setSelected();
+ setSelected(fieldName ?? true);
} else if (onSelectBlock) {
onSelectBlock(block);
}
};
- console.log(textblockExtensions.map((f) => f.name));
+
const extensions = [...textblockExtensions].map((f) => {
if (f.name === 'breakList') {
return customBreakList;
@@ -131,7 +131,12 @@ const TextEditorWidget = (props) => {
selected={selected}
placeholder={placeholder}
onKeyDown={handleKeyDetached}
- editableProps={{ 'aria-multiline': 'true' }}
+ editableProps={{
+ 'aria-multiline': 'true',
+ }}
+ onBlur={(e) => {
+ setSelected(fieldName ? null : false);
+ }}
/>
) : (
diff --git a/src/config/Slate/handlers.js b/src/config/Slate/handlers.js
index e6441d097..baec5afd7 100644
--- a/src/config/Slate/handlers.js
+++ b/src/config/Slate/handlers.js
@@ -1,7 +1,8 @@
+import { Node } from 'slate';
import {
goDown,
goUp,
- softBreak,
+ //softBreak,
unwrapEmptyString,
} from '@plone/volto-slate/blocks/Text/keyboard';
@@ -78,6 +79,7 @@ const goToNextVoltoBlock = (props) => {
index,
saveSlateBlockSelection,
} = props.editor.getBlockProps();
+
const next = getNextVoltoBlock(index, properties);
if (!next || next[0]?.['@type'] !== 'slate')
return onFocusNextBlock(block, blockNode.current);
@@ -101,33 +103,40 @@ const focusNext = (props) => {
showToolbar,
onFocusNextBlock,
} = props.editor.getBlockProps();
-
+ props.event.preventDefault();
props.event.stopPropagation();
let isAtEnd = false;
if (showToolbar) {
isAtEnd = isCursorAtBlockEnd(props.editor);
} else {
- let _range = document.getSelection().getRangeAt(0);
- let range = _range.cloneRange();
- range.selectNodeContents(props.event.target);
- range.setEnd(_range.endContainer, _range.endOffset);
- isAtEnd = range.toString().length === props.event.target.innerText?.length;
- // isAtEnd =
- // props.event.target.selectionEnd === props.event.target.value?.length;
+ let selection = document.getSelection();
+
+ if (selection && selection.rangeCount > 0) {
+ const _range = selection.getRangeAt(0);
+ let range = _range.cloneRange();
+ range.selectNodeContents(props.event.target);
+ range.setEnd(_range.endContainer, _range.endOffset);
+ isAtEnd =
+ range.toString().length === props.event.target.innerText?.length;
+ // isAtEnd =
+ // props.event.target.selectionEnd === props.event.target.value?.length;
+ }
}
- if (isAtEnd) {
- //move to next field
- if (focusNextField) {
- return focusNextField();
- }
+ //move to next field
+ if (focusNextField) {
+ focusNextField();
+ return true;
+ }
+ if (isAtEnd) {
//handle SimpleTextEditorWidget -> move to next block
if (!showToolbar && onFocusNextBlock) {
- goToNextVoltoBlock(props);
+ return goToNextVoltoBlock(props);
}
}
+
//handle SlateEditor arrow-down key
return goDown(props);
};
@@ -146,10 +155,14 @@ const breakInSimpleTextEditor = (props) => {
//disable break in SimpleTextEditorWidget
const getBlockProps = props.editor.getBlockProps;
- const { showToolbar } = getBlockProps
+ const { showToolbar, focusNextField } = getBlockProps
? getBlockProps()
: { showToolbar: true };
if (props.event.key === 'Enter' && !props.event.shiftKey && !showToolbar) {
+ if (focusNextField) {
+ focusNextField();
+ return false;
+ }
props.event.preventDefault();
goToNextVoltoBlock(props);
return false;
@@ -166,6 +179,7 @@ export default function install(config) {
breakInSimpleTextEditor,
customSoftBreak,
//focusNext,
+ focusNext,
],
ArrowUp: [focusPrev],
ArrowDown: [focusNext],
diff --git a/src/config/italiaConfig.js b/src/config/italiaConfig.js
index 09a05c9a0..bd9d34fe6 100644
--- a/src/config/italiaConfig.js
+++ b/src/config/italiaConfig.js
@@ -390,6 +390,7 @@ export default function applyConfig(voltoConfig) {
},
hero: {
...config.blocks.blocksConfig.hero,
+ hasOwnFocusManagement: true,
sidebarTab: 1,
},
html: {
diff --git a/src/customizations/volto/components/manage/Blocks/HeroImageLeft/Edit.jsx b/src/customizations/volto/components/manage/Blocks/HeroImageLeft/Edit.jsx
index 8cee7eeea..5e523ad8a 100644
--- a/src/customizations/volto/components/manage/Blocks/HeroImageLeft/Edit.jsx
+++ b/src/customizations/volto/components/manage/Blocks/HeroImageLeft/Edit.jsx
@@ -18,7 +18,7 @@ import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
import { isEqual } from 'lodash';
import { defineMessages, injectIntl } from 'react-intl';
import cx from 'classnames';
-
+import { handleKeyDownOwnFocusManagement } from 'design-comuni-plone-theme/helpers/blocks';
import {
flattenToAppURL,
getBaseUrl,
@@ -123,6 +123,12 @@ class EditComponent extends Component {
}
}
+ handleEnter = (e) => {
+ if (this.props.selected) {
+ handleKeyDownOwnFocusManagement(e, this.props);
+ }
+ };
+
/**
* Component did mount
* @method componentDidMount
@@ -130,7 +136,17 @@ class EditComponent extends Component {
*/
componentDidMount() {
if (this.props.selected) {
- this.titleEditor.focus();
+ this.setState(() => ({ currentFocused: 'title' }));
+ }
+
+ const blockNode = this.props.blockNode;
+
+ if (this.props.selected && this.node) {
+ this.node.focus();
+ }
+
+ if (blockNode && blockNode.current) {
+ blockNode.current.addEventListener('keydown', this.handleEnter, false);
}
}
@@ -154,8 +170,15 @@ class EditComponent extends Component {
url: nextProps.content['@id'],
});
}
- }
+ if (nextProps.selected) {
+ if (!this.props.selected) {
+ this.setState({ currentFocused: 'title' });
+ }
+ } else {
+ this.setState({ currentFocused: null });
+ }
+ }
/**
* @param {*} nextProps
* @param {*} nextState
@@ -210,7 +233,7 @@ class EditComponent extends Component {
this.props.intl.formatMessage(messages.placeholder);
return (
-
+
{
- this.setState(() => ({ currentFocused: 'title' }));
+ setSelected={(f) => {
+ this.setState(() => ({ currentFocused: f }));
}}
focusNextField={() => {
this.setState(() => ({ currentFocused: 'description' }));
@@ -307,8 +330,8 @@ class EditComponent extends Component {
placeholder={this.props.intl.formatMessage(
messages.description,
)}
- setSelected={() => {
- this.setState(() => ({ currentFocused: 'description' }));
+ setSelected={(f) => {
+ this.setState(() => ({ currentFocused: f }));
}}
focusPrevField={() => {
this.setState(() => ({ currentFocused: 'title' }));
diff --git a/src/helpers/blocks.js b/src/helpers/blocks.js
index a02bd31e7..db3ff97e6 100644
--- a/src/helpers/blocks.js
+++ b/src/helpers/blocks.js
@@ -1,7 +1,86 @@
import { v4 as uuid } from 'uuid';
-
+import { useState, useEffect } from 'react';
+import config from '@plone/volto/registry';
export const cloneBlock = (blockData) => {
const blockID = uuid();
const clonedData = { ...blockData, block: blockID };
return [blockID, clonedData];
};
+
+export const handleKeyDownOwnFocusManagement = (
+ e,
+ props,
+ {
+ disableEnter = false,
+ disableArrowUp = false,
+ disableArrowDown = false,
+ } = {},
+) => {
+ const {
+ index,
+ block,
+ node,
+ onFocusPreviousBlock = () => {},
+ onFocusNextBlock = () => {},
+ onSelectBlock = () => {},
+ onAddBlock = () => {},
+ } = props;
+ const isMultipleSelection = e.shiftKey;
+
+ if (e.key === 'ArrowUp' && !disableArrowUp) {
+ onFocusPreviousBlock(block, node, isMultipleSelection);
+ e.preventDefault();
+ }
+ if (e.key === 'ArrowDown' && !disableArrowDown) {
+ onFocusNextBlock(block, node, isMultipleSelection);
+ e.preventDefault();
+ }
+ if (e.key === 'Enter' && !disableEnter) {
+ onSelectBlock(onAddBlock(config.settings.defaultBlockType, index + 1));
+
+ e.preventDefault();
+ }
+};
+
+/*HOOK to handle detached blocks with own focus management*/
+export const useHandleDetachedBlockFocus = (
+ blockProps,
+ defaultSelectedField,
+) => {
+ const [selectedField, setSelectedField] = useState(defaultSelectedField);
+ const { selected, onSelectBlock, block } = blockProps;
+
+ useEffect(() => {
+ if (selected && !selectedField) {
+ setSelectedField(defaultSelectedField);
+ } else if (!selected) {
+ setSelectedField(null);
+ }
+ }, [selected]);
+
+ useEffect(() => {
+ if (!selected && selectedField && onSelectBlock) {
+ onSelectBlock(block);
+ }
+ }, [selectedField]);
+
+ useEffect(() => {
+ const handleEnter = (e) => {
+ if (selected && !selectedField) {
+ handleKeyDownOwnFocusManagement(e, blockProps);
+ }
+ };
+ const blockNode = blockProps.blockNode;
+
+ if (blockNode && blockNode.current) {
+ blockNode.current.addEventListener('keydown', handleEnter, false);
+ return function cleanup() {
+ if (blockNode?.current) {
+ blockNode.current.removeEventListener('keydown', handleEnter, false);
+ }
+ };
+ }
+ }, [selected, selectedField]);
+
+ return { selectedField, setSelectedField };
+};
diff --git a/src/theme/ItaliaTheme/_main.scss b/src/theme/ItaliaTheme/_main.scss
index 262bdf377..7df067fb1 100644
--- a/src/theme/ItaliaTheme/_main.scss
+++ b/src/theme/ItaliaTheme/_main.scss
@@ -86,7 +86,7 @@ iframe {
.cms-ui {
blockquote,
.blockquote {
- display: flex; //serve per quando i blockquote sono affiancati alle immagini allineate a dx o sx
+ display: flow-root; //serve per quando i blockquote sono affiancati alle immagini allineate a dx o sx
border-color: $primary !important;
ul:first-child,
@@ -125,7 +125,7 @@ iframe {
font-weight: 600;
text-align: center;
text-decoration: none;
- margin: 0 0.5rem;
+ margin: 0;
&:hover,
&:active {
font-weight: 600;
diff --git a/src/theme/_cms-ui.scss b/src/theme/_cms-ui.scss
index 6de6ff31e..1ddfe20d8 100644
--- a/src/theme/_cms-ui.scss
+++ b/src/theme/_cms-ui.scss
@@ -1,7 +1,7 @@
@import 'cms-ui_container';
body.cms-ui {
.block {
- font-family: $font-family-serif;
+ font-family: $font-family-sans-serif;
%heading {
margin-top: 0; /* 1*/