From 3e752fb80be602f9d16a46494c2fd07a81add407 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Wed, 26 Jan 2022 12:54:39 +0200 Subject: [PATCH] Added Copy/Paste appextras --- .../theme/AppExtras/CopyPaste/CopyPaste.jsx | 176 ++++++++++++++++++ .../theme/AppExtras/CopyPaste/index.js | 3 + .../theme/AppExtras/CopyPaste/style.less | 18 ++ src/components/theme/AppExtras/index.js | 13 ++ src/index.js | 3 +- 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/components/theme/AppExtras/CopyPaste/CopyPaste.jsx create mode 100644 src/components/theme/AppExtras/CopyPaste/index.js create mode 100644 src/components/theme/AppExtras/CopyPaste/style.less create mode 100644 src/components/theme/AppExtras/index.js diff --git a/src/components/theme/AppExtras/CopyPaste/CopyPaste.jsx b/src/components/theme/AppExtras/CopyPaste/CopyPaste.jsx new file mode 100644 index 0000000..9928fcd --- /dev/null +++ b/src/components/theme/AppExtras/CopyPaste/CopyPaste.jsx @@ -0,0 +1,176 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { Portal } from 'react-portal'; +import { toast } from 'react-toastify'; +import { Button } from 'semantic-ui-react'; +import { getBaseUrl } from '@plone/volto/helpers'; +import { updateContent } from '@plone/volto/actions'; +import { Icon, Toast } from '@plone/volto/components'; +import copySVG from '@plone/volto/icons/copy.svg'; +import pasteSVG from '@plone/volto/icons/paste.svg'; + +import './style.less'; + +const TIMEOUT = 2000; + +const CopyPaste = (props) => { + const [readyToRender, setReadyToRender] = React.useState(false); + const clock = React.useRef(null); + const time = React.useRef(0); + const toolbar = React.useRef(null); + const { content } = props; + + const copyData = () => { + navigator.clipboard.writeText( + JSON.stringify({ + blocks: content.blocks, + blocks_layout: content.blocks_layout, + }), + ); + toast.success( + , + ); + }; + + const pasteData = () => { + const message = [ + '============= BRAKING CHANGE =============', + '\nAre you sure you want to paste from clipboard?', + '\nThis action will replace all the blocks with those from clipboard and will trigger SUBMIT !!!', + ]; + navigator.clipboard.readText().then((text) => { + if ( + // eslint-disable-next-line no-alert + window.confirm(message.join('')) + ) { + try { + const data = JSON.parse(text) || {}; + const { blocks = {}, blocks_layout = {} } = data; + const blocksIds = Object.keys(blocks); + let valid = true; + if ( + blocks_layout && + blocks_layout.items && + blocks_layout.items.length === blocksIds.length + ) { + blocks_layout.items.forEach((block) => { + if (valid && !blocksIds.includes(block)) { + valid = false; + } + }); + } + if (valid) { + props.updateContent(getBaseUrl(props.pathname), data); + toast.success( + , + ); + } else { + toast.error( + , + ); + } + } catch { + toast.error( + , + ); + } + } + }); + }; + + React.useEffect(() => { + clock.current = setInterval(() => { + const element = document.querySelector('#toolbar .toolbar-actions'); + if (element) { + setReadyToRender(true); + clearInterval(clock.current); + time.current = 0; + return; + } + if (time.current >= TIMEOUT) { + clearInterval(clock.current); + time.current = 0; + return; + } + time.current += 100; + }, 100); + return () => { + clearInterval(clock.current); + time.current = 0; + }; + }, []); + + if (!__CLIENT__ || !readyToRender) return ''; + + return ( + +
{ + if ( + e.altKey && + e.ctrlKey && + toolbar.current && + !toolbar.current.classList.contains('__dev_on') + ) { + toolbar.current.classList.add('__dev_on'); + } + }} + onMouseLeave={(e) => { + if ( + toolbar.current && + toolbar.current.classList.contains('__dev_on') + ) { + toolbar.current.classList.remove('__dev_on'); + } + }} + onFocus={() => {}} + > + + +
+
+ ); +}; + +export default compose( + connect( + (state, props) => ({ + content: state.content.data, + updateRequest: state.content.update, + pathname: props.location.pathname, + }), + { + updateContent, + }, + ), +)(CopyPaste); diff --git a/src/components/theme/AppExtras/CopyPaste/index.js b/src/components/theme/AppExtras/CopyPaste/index.js new file mode 100644 index 0000000..4b4d44fb --- /dev/null +++ b/src/components/theme/AppExtras/CopyPaste/index.js @@ -0,0 +1,3 @@ +import CopyPaste from './CopyPaste'; + +export default CopyPaste; diff --git a/src/components/theme/AppExtras/CopyPaste/style.less b/src/components/theme/AppExtras/CopyPaste/style.less new file mode 100644 index 0000000..965e963 --- /dev/null +++ b/src/components/theme/AppExtras/CopyPaste/style.less @@ -0,0 +1,18 @@ +#__developer_tools { + .ui.button { + color: #007eb1; + opacity: 0; + pointer-events: none; + + &.disabled { + opacity: 0 !important; + } + } + + &.__dev_on { + .ui.button { + opacity: 1; + pointer-events: all; + } + } +} diff --git a/src/components/theme/AppExtras/index.js b/src/components/theme/AppExtras/index.js new file mode 100644 index 0000000..c049e45 --- /dev/null +++ b/src/components/theme/AppExtras/index.js @@ -0,0 +1,13 @@ +import CopyPaste from './CopyPaste'; + +export default (config) => { + config.settings.appExtras = [ + ...(config.settings.appExtras || []), + { + match: '/**/edit', + component: CopyPaste, + }, + ]; + + return config; +}; diff --git a/src/index.js b/src/index.js index b5b04c4..6121174 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import Forbidden from '@plone/volto/components/theme/Forbidden/Forbidden'; import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized'; +import installAppExtras from '@eeacms/volto-forests-theme/components/theme/AppExtras'; import { installBlocks } from '@eeacms/volto-plotlycharts'; import { applyConfig as installFiseFrontend } from './localconfig'; @@ -11,7 +12,7 @@ import './slate-styles.css'; export default function applyConfig(config) { // Add here your project's configuration here by modifying `config` accordingly - config = [installBlocks, installFiseFrontend].reduce( + config = [installBlocks, installAppExtras, installFiseFrontend].reduce( (acc, apply) => apply(acc), config, );