From fb8f6001683cb51e3ea1c4828d23d838534d9655 Mon Sep 17 00:00:00 2001 From: razvanMiu Date: Mon, 22 Mar 2021 17:22:35 +0200 Subject: [PATCH] Added BlockToolbar for copy/paste inside a column --- src/ColumnsBlock/ColumnsBlockEdit.jsx | 120 +++++++++++++++++++++++--- src/ColumnsBlock/EditBlockWrapper.jsx | 9 +- src/ColumnsBlock/styles.less | 7 ++ 3 files changed, 124 insertions(+), 12 deletions(-) diff --git a/src/ColumnsBlock/ColumnsBlockEdit.jsx b/src/ColumnsBlock/ColumnsBlockEdit.jsx index 24b15a1..e9eea9c 100644 --- a/src/ColumnsBlock/ColumnsBlockEdit.jsx +++ b/src/ColumnsBlock/ColumnsBlockEdit.jsx @@ -1,9 +1,12 @@ import React from 'react'; import { Grid, Segment } from 'semantic-ui-react'; -import { isEmpty } from 'lodash'; -import { SidebarPortal, Icon } from '@plone/volto/components'; // BlocksForm, Icon, +import { isEmpty, without } from 'lodash'; +import { SidebarPortal, BlocksToolbar, Icon } from '@plone/volto/components'; // BlocksForm, Icon, import InlineForm from '@plone/volto/components/manage/Form/InlineForm'; -import { emptyBlocksForm } from '@plone/volto/helpers'; +import { + emptyBlocksForm, + getBlocksLayoutFieldname, +} from '@plone/volto/helpers'; import { setSidebarTab } from '@plone/volto/actions'; import { connect } from 'react-redux'; import { BlocksForm } from '@plone/volto/components'; @@ -44,6 +47,7 @@ class ColumnsBlockEdit extends React.Component { constructor(props) { super(props); this.state = { + multiSelected: [], colSelections: {}, // selected block for each column showSidebar: false, activeColumn: null, @@ -154,6 +158,58 @@ class ColumnsBlockEdit extends React.Component { } }; + onSelectBlock = ( + id, + colId, + colData, + activeBlock, + isMultipleSelection, + event, + ) => { + let newMultiSelected = []; + let selected = id; + + if (isMultipleSelection) { + selected = null; + const blocksLayoutFieldname = getBlocksLayoutFieldname(colData); + + const blocks_layout = colData[blocksLayoutFieldname].items; + + if (event.shiftKey) { + const anchor = + this.state.multiSelected.length > 0 + ? blocks_layout.indexOf(this.state.multiSelected[0]) + : blocks_layout.indexOf(activeBlock); + const focus = blocks_layout.indexOf(id); + + if (anchor === focus) { + newMultiSelected = [id]; + } else if (focus > anchor) { + newMultiSelected = [...blocks_layout.slice(anchor, focus + 1)]; + } else { + newMultiSelected = [...blocks_layout.slice(focus, anchor + 1)]; + } + } + + if ((event.ctrlKey || event.metaKey) && !event.shiftKey) { + if (this.state.multiSelected.includes(id)) { + selected = null; + newMultiSelected = without(this.state.multiSelected, id); + } else { + newMultiSelected = [...(this.state.multiSelected || []), id]; + } + } + } + + this.setState({ + multiSelected: newMultiSelected, + colSelections: { + // this invalidates selection in all other columns + [colId]: selected, + }, + }); + }; + getColumnsBlockSchema = () => { const schema = ColumnsBlockSchema(); const { data } = this.props; @@ -226,6 +282,12 @@ class ColumnsBlockEdit extends React.Component { const { gridCols, gridSize } = data; const coldata = data.data; const columnList = getColumns(coldata); + const selectedCol = + Object.keys(this.state.colSelections).length > 0 + ? Object.keys(this.state.colSelections)[0] + : null; + const selectedColData = coldata.blocks[selectedCol] || null; + const selectedBlock = this.state.colSelections[selectedCol]; const { gridSizes, @@ -284,14 +346,21 @@ class ColumnsBlockEdit extends React.Component { selectedBlock={ selected ? this.state.colSelections[colId] : null } - onSelectBlock={(id) => - this.setState({ - colSelections: { - // this invalidates selection in all other columns - [colId]: id, - }, - }) - } + onSelectBlock={(id, selected, e) => { + const isMultipleSelection = e + ? e.shiftKey || e.ctrlKey || e.metaKey + : false; + this.onSelectBlock( + id, + colId, + selectedColData, + selectedBlock, + selectedCol !== colId || selectedBlock === id + ? false + : isMultipleSelection, + e, + ); + }} onChangeFormData={(newFormData) => { onChangeBlock(block, { ...data, @@ -334,6 +403,9 @@ class ColumnsBlockEdit extends React.Component { )} } + multiSelected={this.state.multiSelected.includes( + blockProps.block, + )} > {editBlock} @@ -344,6 +416,32 @@ class ColumnsBlockEdit extends React.Component { )} + {selected && selectedColData ? ( + { + onChangeBlock(block, { + ...data, + data: { + ...coldata, + blocks: { + ...coldata.blocks, + [selectedCol]: { ...selectedColData, ...newBlockData }, + }, + }, + }); + }} + onSetSelectedBlocks={(blockIds) => { + this.setState({ multiSelected: blockIds }); + }} + onSelectBlock={this.onSelectBlock} + /> + ) : ( + '' + )} + {Object.keys(this.state.colSelections).length === 0 && !data?.readOnlySettings ? ( diff --git a/src/ColumnsBlock/EditBlockWrapper.jsx b/src/ColumnsBlock/EditBlockWrapper.jsx index 0596042..8e9ae2e 100644 --- a/src/ColumnsBlock/EditBlockWrapper.jsx +++ b/src/ColumnsBlock/EditBlockWrapper.jsx @@ -7,6 +7,7 @@ import includes from 'lodash/includes'; import isBoolean from 'lodash/isBoolean'; import { defineMessages, injectIntl } from 'react-intl'; import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib'; +import cx from 'classnames'; import dragSVG from '@plone/volto/icons/drag.svg'; import addSVG from '@plone/volto/icons/circle-plus.svg'; @@ -148,7 +149,13 @@ class EditBlockWrapper extends React.Component { )} -
{children}
+
+ {children} +
); diff --git a/src/ColumnsBlock/styles.less b/src/ColumnsBlock/styles.less index 8cc571f..57b7c82 100644 --- a/src/ColumnsBlock/styles.less +++ b/src/ColumnsBlock/styles.less @@ -88,6 +88,13 @@ [data-rbd-draggable-context-id] { margin-bottom: 0.3em; } + + .block.wrapper.multiSelected { + .block .block::before { + z-index: 1; + background-color: rgba(120, 192, 215, 0.375); + } + } } .columns-view {