diff --git a/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestion.js b/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestion.js index a363e1c99abbd0af746afe75961aaef11d024b37..2fe6f664e94d82023b0d25fe193f0bf77e4da690 100644 --- a/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestion.js +++ b/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestion.js @@ -1,6 +1,6 @@ import { injectable } from 'inversify'; import { Tools } from 'wax-prosemirror-services'; -import { setBlockType } from 'prosemirror-commands'; +import {Commands} from 'wax-prosemirror-utilities' @injectable() class MultipleChoiceQuestion extends Tools { @@ -9,8 +9,9 @@ class MultipleChoiceQuestion extends Tools { name = 'Multiple Choice'; get run() { + return (state, dispatch) => { - setBlockType(state.config.schema.nodes.multiple_choice)(state, dispatch); + Commands.setBlockType(state.config.schema.nodes.multiple_choice)(state, dispatch); }; } diff --git a/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestionService.js b/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestionService.js index 7d926286d751ead6767ca44291297d1e971b810b..6a259507f8051b8849fe824393f16d1c9951323a 100644 --- a/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestionService.js +++ b/editors/demo/src/HHMI/MultipleChoiceQuestionService/MultipleChoiceQuestionService.js @@ -1,9 +1,16 @@ import { Service } from 'wax-prosemirror-services'; +// import { MultipleChoicePlugin } from 'wax-prosemirror-plugins'; import MultipleChoiceQuestion from './MultipleChoiceQuestion'; import multipleChoiceNode from './schema/multipleChoiceNode'; +import TestComponentPortal from './components/TestComponentPortal'; class MultipleChoiceQuestionService extends Service { - boot() {} + boot() { + // this.app.PmPlugins.add( + // 'multipleChoicePlugin', + // MultipleChoicePlugin('multipleChoicePlugin'), + // ); + } register() { this.container.bind('MultipleChoiceQuestion').to(MultipleChoiceQuestion); @@ -11,7 +18,7 @@ class MultipleChoiceQuestionService extends Service { createNode({ multiple_choice: multipleChoiceNode, }); - console.log(this.schema); + // console.log(this.schema); } } diff --git a/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponent.js b/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponent.js index 7b11dfff9fcbee2cc7624bb8aec9b2fb8df5b1ea..e8d820a895fcc2cec7cc3c505cd91e8cd68fd587 100644 --- a/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponent.js +++ b/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponent.js @@ -1,9 +1,5 @@ -import React, { useEffect, useRef, useCallback, useMemo } from 'react'; -import { EditorState } from 'prosemirror-state'; -import { EditorView } from 'prosemirror-view'; -import { schema } from 'prosemirror-schema-basic'; -import { WaxContext } from 'wax-prosemirror-core'; -import { useReactNodeView } from 'wax-prosemirror-core/src/ReactNodeView'; +import React from 'react'; + const styles = { backgroundColor: 'red', @@ -11,50 +7,8 @@ const styles = { height: '200px', }; -const onChange = () => {}; export default () => { - const context = useReactNodeView(); - console.log(context); - const editorViewRef = useRef(null); - const handleChange = useCallback(onChange, []); - const state = useMemo(() => { - const doc = schema.nodeFromJSON({ - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: ' ', - }, - ], - }, - ], - }); - return EditorState.create({ - doc, - plugins: [ - // history(), - // keymap({ 'Mod-z': undo, 'Mod-y': redo }), - // keymap(baseKeymap), - ], - }); - }, []); - const createEditorView = useCallback( - editorViewDOM => { - const view = new EditorView(editorViewDOM, { - state, - dispatchTransaction(transaction) { - const newState = view.state.apply(transaction); - handleChange(newState.doc.toJSON()); - view.updateState(newState); - }, - }); - }, - [state, handleChange], - ); // useEffect(() => { // const editorViewDOM = editorViewRef.current; diff --git a/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponentPortal.js b/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponentPortal.js new file mode 100644 index 0000000000000000000000000000000000000000..f40a9b2e01c994d7e1bb4a48a3100ee7323acd84 --- /dev/null +++ b/editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponentPortal.js @@ -0,0 +1,25 @@ +import React, { useContext } from 'react'; +import { WaxContext } from 'wax-prosemirror-core'; +import {isEmpty} from 'lodash'; +import ReactDOM from 'react-dom'; +import { v4 as uuidv4 } from 'uuid'; +import TestComponent from './TestComponent'; + +export default () => { + const { activeView } = useContext(WaxContext); + + // useEffect(() => { + // const editorViewDOM = editorViewRef.current; + // if (editorViewDOM) { + // createEditorView(editorViewDOM); + // } + // }, [createEditorView]); + + // + + if (isEmpty(activeView)) return null + + if (!activeView.state.multipleChoicePlugin$.dom) return null + + return ReactDOM.createPortal(<TestComponent />, document.getElementById('portalId'), uuidv4()) + }; \ No newline at end of file diff --git a/editors/demo/src/HHMI/layout/HhmiLayout.js b/editors/demo/src/HHMI/layout/HhmiLayout.js index 4ad5f65d176cab6b0cc9710dde33c8c6136d0cfa..59f37955ba943e8e9e18bf394f3b6a71648db435 100644 --- a/editors/demo/src/HHMI/layout/HhmiLayout.js +++ b/editors/demo/src/HHMI/layout/HhmiLayout.js @@ -119,6 +119,7 @@ const HhmiLayout = ({ editor }) => { </WaxSurfaceScroll> </EditorArea> </Main> + WaxOverlays <WaxOverlays /> </Wrapper> </ThemeProvider> diff --git a/wax-prosemirror-core/index.js b/wax-prosemirror-core/index.js index b1455b18358b781f6ce8a2a606bccf48ca480751..38fa2e8ac6d9499a576a66dd73769516dcbec616 100644 --- a/wax-prosemirror-core/index.js +++ b/wax-prosemirror-core/index.js @@ -1,4 +1,5 @@ export { WaxContext, useInjection } from './src/WaxContext'; +export { PortalContext } from './src/PortalContext'; export { default as ComponentPlugin } from './src/ComponentPlugin'; export { default as Wax } from './src/Wax'; export { default as useReactNodeView } from './src/ReactNodeView'; diff --git a/wax-prosemirror-core/src/PortalContext.js b/wax-prosemirror-core/src/PortalContext.js new file mode 100644 index 0000000000000000000000000000000000000000..0de864cce333ca6ff98e9e95e73b0bef4a585524 --- /dev/null +++ b/wax-prosemirror-core/src/PortalContext.js @@ -0,0 +1,31 @@ +/* eslint react/prop-types: 0 */ +/* eslint react/destructuring-assignment: 0 */ +import React, { useState } from 'react'; + +export const PortalContext = React.createContext({ + createPortal: () => {}, + portals: {}, +}); + +export default props => { + const [portal, setPortal] = useState({ + portals: [], + createPortal: (element, Component) => { + portal.portals.push({ + element, + component: Component, + }); + setPortal({ ...portal, portals: [...portal.portals] }); + }, + }); + + return ( + <PortalContext.Provider + value={{ + ...portal, + }} + > + {props.children} + </PortalContext.Provider> + ); +}; diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js index 982fd82cbb92033877fcfdefafba289d0ff741b8..d54aa7fd118f6ed62eadd46fe659a7c2ab0c5ccc 100644 --- a/wax-prosemirror-core/src/Wax.js +++ b/wax-prosemirror-core/src/Wax.js @@ -2,25 +2,13 @@ import React, { useEffect, useState } from 'react'; import debounce from 'lodash/debounce'; -import { DOMSerializer, DOMParser } from 'prosemirror-model'; +import { DOMSerializer } from 'prosemirror-model'; import WaxProvider from './WaxContext'; +import { PortalContext } from './PortalContext'; import Application from './Application'; import WaxView from './WaxView'; -import defaultPlugins from './plugins/defaultPlugins'; -import Placeholder from './plugins/placeholder'; - -const parser = schema => { - const WaxParser = DOMParser.fromSchema(schema); - - return content => { - const container = document.createElement('article'); - - container.innerHTML = content; - return WaxParser.parse(container); - }; -}; const serializer = schema => { const WaxSerializer = DOMSerializer.fromSchema(schema); @@ -39,12 +27,7 @@ const createApplication = props => { return application; }; -const createPlaceholder = placeholder => { - return Placeholder({ content: placeholder }); -}; - const Wax = props => { - let finalPlugins = []; const [application, setApplication] = useState(); useEffect(() => { @@ -66,53 +49,21 @@ const Wax = props => { user, onChange, targetFormat, - nodeViews, } = props; if (!application) return null; - // const { schema } = application.schema; const WaxOnchange = onChange || (v => true); - finalPlugins = defaultPlugins.concat([ - createPlaceholder(placeholder), - ...application.getPlugins(), - ]); - - const WaxOptions = { - schema, - plugins: finalPlugins, - }; - - if (targetFormat === 'JSON') { - const editorContent = value || { - type: 'doc', - content: [ - { - type: 'paragraph', - content: [ - { - type: 'text', - text: ' ', - }, - ], - }, - ], - }; - WaxOptions.doc = schema.nodeFromJSON(editorContent); - } else { - const editorContent = value || ''; - const parse = parser(schema); - WaxOptions.doc = parse(editorContent); - } - const finalOnChange = debounce( + // eslint-disable-next-line no-shadow value => { /* HACK alter toDOM of footnote, because of how PM treats inline nodes with content */ if (schema.nodes.footnote) { const old = schema.nodes.footnote.spec.toDOM; - schema.nodes.footnote.spec.toDOM = function (node) { - old.apply(this, arguments); + schema.nodes.footnote.spec.toDOM = node => { + // eslint-disable-next-line prefer-rest-params + old.apply(this); return ['footnote', node.attrs, 0]; }; } @@ -126,8 +77,9 @@ const Wax = props => { if (schema.nodes.footnote) { const old = schema.nodes.footnote.spec.toDOM; - schema.nodes.footnote.spec.toDOM = function (node) { - old.apply(this, arguments); + schema.nodes.footnote.spec.toDOM = node => { + // eslint-disable-next-line prefer-rest-params + old.apply(this); return ['footnote', node.attrs]; }; } @@ -140,25 +92,25 @@ const Wax = props => { const Layout = application.container.get('Layout'); if (layout) Layout.setLayout(layout); const WaxRender = Layout.layoutComponent; - return ( <WaxProvider app={application}> - <WaxView - autoFocus={autoFocus} - debug={debug} - fileUpload={fileUpload} - onBlur={onBlur || (v => true)} - onChange={finalOnChange || (v => true)} - options={WaxOptions} - placeholder={placeholder} - readonly={readonly} - targetFormat={targetFormat} - TrackChange={TrackChange} - user={user} - nodeViews={nodeViews} - > - {({ editor }) => <WaxRender className={className} editor={editor} />} - </WaxView> + <PortalContext> + <WaxView + autoFocus={autoFocus} + debug={debug} + fileUpload={fileUpload} + onBlur={onBlur || (v => true)} + onChange={finalOnChange || (v => true)} + placeholder={placeholder} + readonly={readonly} + targetFormat={targetFormat} + TrackChange={TrackChange} + user={user} + value={value} + > + {({ editor }) => <WaxRender className={className} editor={editor} />} + </WaxView> + </PortalContext> </WaxProvider> ); }; diff --git a/wax-prosemirror-core/src/WaxContext.js b/wax-prosemirror-core/src/WaxContext.js index 098df4a4127ff0867fbb57e9d0e3ef230173a08b..bbfe41a5389e982713f8d1b58c507641882059dc 100644 --- a/wax-prosemirror-core/src/WaxContext.js +++ b/wax-prosemirror-core/src/WaxContext.js @@ -2,36 +2,6 @@ /* eslint react/destructuring-assignment: 0 */ import React, { useContext, useState } from 'react'; -const initialState = {}; - -const ReactNodeViewPortalsContext = React.createContext({ - createPortal: () => {}, - state: {}, -}); - -const ReactNodeViewPortalsProvider = ({ children }) => { - const [data, addPortal] = useState(initialState); - - const { portal: Portal } = data; - - return ( - <ReactNodeViewPortalsContext.Provider - value={{ - createPortal: portal => { - addPortal({ key: portal.key, portal }); - }, - state: data, - }} - > - {children} - {Portal} - </ReactNodeViewPortalsContext.Provider> - ); -}; - -export const useReactNodeViewPortals = () => - useContext(ReactNodeViewPortalsContext); - export const WaxContext = React.createContext({ view: {}, activeView: {}, @@ -68,15 +38,13 @@ export default props => { }); return ( - <ReactNodeViewPortalsProvider> - <WaxContext.Provider - value={{ - ...context, - }} - > - {props.children} - </WaxContext.Provider> - </ReactNodeViewPortalsProvider> + <WaxContext.Provider + value={{ + ...context, + }} + > + {props.children} + </WaxContext.Provider> ); }; diff --git a/wax-prosemirror-core/src/WaxView.js b/wax-prosemirror-core/src/WaxView.js index c99d363d0b25e8185f63a7917969bc8387facaf1..1a3fbdb32c79c5a4464b5c137dc6353bb3793a4c 100644 --- a/wax-prosemirror-core/src/WaxView.js +++ b/wax-prosemirror-core/src/WaxView.js @@ -3,14 +3,13 @@ import React, { useRef, useContext, useCallback, useMemo } from 'react'; import applyDevTools from 'prosemirror-dev-tools'; import { EditorState } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; - import 'prosemirror-view/style/prosemirror.css'; - import { trackedTransaction } from 'wax-prosemirror-services'; -import { WaxContext, useReactNodeViewPortals } from './WaxContext'; + +import ComponentPlugin from './ComponentPlugin'; +import { WaxContext } from './WaxContext'; import transformPasted from './helpers/TransformPasted'; -import { createReactNodeView } from './ReactNodeView'; -import TestComponent from '../../editors/demo/src/HHMI/MultipleChoiceQuestionService/components/TestComponent'; +import useWaxOptions from './useWaxOptions'; let previousDoc; @@ -26,13 +25,15 @@ export default props => { nodeViews, } = props; +export default props => { + const { readonly, onBlur, debug, autoFocus, user, targetFormat } = props; const editorRef = useRef(); let view; const context = useContext(WaxContext); - const { createPortal } = useReactNodeViewPortals(); - const handleCreatePortal = useCallback(createPortal, []); + const options = useWaxOptions(props); const setEditorRef = useCallback( + // eslint-disable-next-line consistent-return node => { if (editorRef.current) { // this is where you do cleanup if you have to. the editorRef.current will @@ -49,22 +50,10 @@ export default props => { user, scrollMargin: 200, scrollThreshold: 200, - nodeViews: { - multiple_choice(theNode, view, getPos, decorations) { - console.log('rerenders for ever', theNode); - return createReactNodeView({ - node: theNode, - view, - getPos, - decorations, - component: TestComponent, - onCreatePortal: handleCreatePortal, - }); - }, - }, handleDOMEvents: { blur: onBlur - ? view => { + ? // eslint-disable-next-line no-shadow + view => { onBlur(view.state.doc.content); } : null, @@ -97,26 +86,6 @@ export default props => { [readonly], ); - const createNodeVies = () => { - const test = nodeViews.map((nodeView, key, index) => { - return { - [nodeView.multiple_choice.node](node, view, getPos, decorations) { - console.log('rerenders for ever', node); - return createReactNodeView({ - node, - view, - getPos, - decorations, - component: nodeView.multiple_choice.component, - onCreatePortal: handleCreatePortal, - }); - }, - }; - }); - console.log(test); - return test[0]; - }; - const dispatchTransaction = transaction => { const { TrackChange } = props; const tr = @@ -150,7 +119,12 @@ export default props => { } }; - const editor = <div ref={setEditorRef} />; + const editor = ( + <> + <div ref={setEditorRef} /> + <WaxPortals /> + </> + ); return useMemo( () => diff --git a/wax-prosemirror-core/src/config/defaultConfig.js b/wax-prosemirror-core/src/config/defaultConfig.js index 662a9b2de013a8f1929998cd799b27be3d98147f..0be93d5b25dc6ef484511997cf2c26078784467e 100644 --- a/wax-prosemirror-core/src/config/defaultConfig.js +++ b/wax-prosemirror-core/src/config/defaultConfig.js @@ -5,6 +5,7 @@ import { ShortCutsService, LayoutService, OverlayService, + PortalService, } from 'wax-prosemirror-services'; export default () => ({ @@ -13,6 +14,7 @@ export default () => ({ new RulesService(), new ShortCutsService(), new LayoutService(), + new PortalService(), new MenuService(), new OverlayService(), ], diff --git a/wax-prosemirror-core/src/plugins/portalPlugin.js b/wax-prosemirror-core/src/plugins/portalPlugin.js new file mode 100644 index 0000000000000000000000000000000000000000..83b46c6ac5d309721a53b6d92ddff18c3e1f17b2 --- /dev/null +++ b/wax-prosemirror-core/src/plugins/portalPlugin.js @@ -0,0 +1,38 @@ +import { Plugin, PluginKey } from 'prosemirror-state'; + +const portalPlugin = new PluginKey('portalPlugin'); + +class ReactNodeView { + constructor(node, view, getPos, decorations, createPortal) { + this.dom = document.createElement('div'); + this.dom.id = 'portalId'; + this.dom.classList.add('portal'); + + createPortal(this.dom, Component); + } + + update(node) { + return true; + } + + destroy() { + this.dom = undefined; + this.contentDOM = undefined; + } +} + +const multipleChoice = ({ createPortal }) => { + return (theNode, view, getPos, decorations) => + new ReactNodeView(theNode, view, getPos, decorations, createPortal); +}; + +export default props => { + return new Plugin({ + key: portalPlugin, + state: { + init: (_, state) => { + return props; + }, + }, + }); +}; diff --git a/wax-prosemirror-core/src/useWaxOptions.js b/wax-prosemirror-core/src/useWaxOptions.js new file mode 100644 index 0000000000000000000000000000000000000000..4f41a14cd6f63323c0a5bad0907e4f316d907cb5 --- /dev/null +++ b/wax-prosemirror-core/src/useWaxOptions.js @@ -0,0 +1,68 @@ +import { useContext } from 'react'; +import { DOMParser } from 'prosemirror-model'; +import { WaxContext } from './WaxContext'; +import { PortalContext } from './PortalContext'; +import Placeholder from './plugins/placeholder'; +import PortalPlugin from './plugins/portalPlugin'; +import defaultPlugins from './plugins/defaultPlugins'; + +const parser = schema => { + const WaxParser = DOMParser.fromSchema(schema); + + return content => { + const container = document.createElement('article'); + + container.innerHTML = content; + return WaxParser.parse(container); + }; +}; + +export default ({ placeholder, targetFormat, value }) => { + const context = useContext(WaxContext); + const { createPortal } = useContext(PortalContext); + + let finalPlugins = []; + + // eslint-disable-next-line no-shadow + const createPlaceholder = placeholder => { + return Placeholder({ content: placeholder }); + }; + + context.app.PmPlugins.add('portalPlugin', PortalPlugin({ createPortal })); + + finalPlugins = defaultPlugins.concat([ + createPlaceholder(placeholder), + ...context.app.getPlugins(), + ]); + + const schema = context.app.getSchema(); + + const WaxOptions = { + schema, + plugins: finalPlugins, + }; + + if (targetFormat === 'JSON') { + const editorContent = value || { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: ' ', + }, + ], + }, + ], + }; + WaxOptions.doc = schema.nodeFromJSON(editorContent); + } else { + const editorContent = value || ''; + const parse = parser(schema); + WaxOptions.doc = parse(editorContent); + } + + return WaxOptions; +}; diff --git a/wax-prosemirror-services/index.js b/wax-prosemirror-services/index.js index 26bf0eba3c178e0f9edbb1896cb8a9be7aca044f..b49e402422a3caf7a3787047a26b8065e5044f68 100644 --- a/wax-prosemirror-services/index.js +++ b/wax-prosemirror-services/index.js @@ -1,6 +1,7 @@ export { default as Service } from './src/Service'; export { default as LayoutService } from './src/LayoutService/LayoutService'; +export { default as PortalService } from './src/PortalService/PortalService'; export { default as MenuService } from './src/MenuService/MenuService'; export { default as OverlayService } from './src/OverlayService/OverlayService'; export { default as ImageService } from './src/ImageService/ImageService'; diff --git a/wax-prosemirror-services/src/PortalService/PortalService.js b/wax-prosemirror-services/src/PortalService/PortalService.js new file mode 100644 index 0000000000000000000000000000000000000000..5dc67774430acbb7a9794d57ee265c6b2511c582 --- /dev/null +++ b/wax-prosemirror-services/src/PortalService/PortalService.js @@ -0,0 +1,19 @@ +import Service from '../Service'; +import PortalComponent from './components/PortalComponent'; + +class PortalService extends Service { + boot() {} + + register() { + const layout = this.container.get('Layout'); + layout.addComponent('waxPortals', PortalComponent); + // this.container.bind('MultipleChoiceQuestion').to(MultipleChoiceQuestion); + // const createNode = this.container.get('CreateNode'); + // createNode({ + // multiple_choice: multipleChoiceNode, + // }); + // console.log(this.schema); + } +} + +export default PortalService; diff --git a/wax-prosemirror-services/src/PortalService/components/PortalComponent.js b/wax-prosemirror-services/src/PortalService/components/PortalComponent.js new file mode 100644 index 0000000000000000000000000000000000000000..3bb081801701828189fdeb26699fa6ad6b7d80de --- /dev/null +++ b/wax-prosemirror-services/src/PortalService/components/PortalComponent.js @@ -0,0 +1,22 @@ +import React, { useContext } from 'react'; +import { PortalContext } from 'wax-prosemirror-core'; +import ReactDOM from 'react-dom'; +import { v4 as uuidv4 } from 'uuid'; +import TestComponent from './TestComponent'; + +export default () => { + const { portals } = useContext(PortalContext); + + console.log(portals); + return ( + <> + {portals.length > 0 + ? ReactDOM.createPortal( + <TestComponent />, + document.getElementById('portalId'), + uuidv4(), + ) + : null} + </> + ); +}; diff --git a/wax-prosemirror-services/src/PortalService/components/TestComponent.js b/wax-prosemirror-services/src/PortalService/components/TestComponent.js new file mode 100644 index 0000000000000000000000000000000000000000..e8d820a895fcc2cec7cc3c505cd91e8cd68fd587 --- /dev/null +++ b/wax-prosemirror-services/src/PortalService/components/TestComponent.js @@ -0,0 +1,21 @@ +import React from 'react'; + + +const styles = { + backgroundColor: 'red', + width: '200px', + height: '200px', +}; + +export default () => { + + + // useEffect(() => { + // const editorViewDOM = editorViewRef.current; + // if (editorViewDOM) { + // createEditorView(editorViewDOM); + // } + // }, [createEditorView]); + + return <div style={styles}></div>; +}; diff --git a/wax-prosemirror-utilities/src/commands/Commands.js b/wax-prosemirror-utilities/src/commands/Commands.js index 89d5644729c475b45a051f535b7ae980204c03f0..bb3d9411926144d8403d02f2d5e923a2d4091ef6 100644 --- a/wax-prosemirror-utilities/src/commands/Commands.js +++ b/wax-prosemirror-utilities/src/commands/Commands.js @@ -17,16 +17,11 @@ const setBlockType = (nodeType, attrs = {}) => { applicable = $pos.parent.canReplaceWith(index, index + 1, nodeType); } if (applicable) { - tr.setBlockType( - from, - to, - nodeType, - Object.assign({}, node.attrs, attrs), - ); + tr.setBlockType(from, to, nodeType, { ...node.attrs, ...attrs }); } }); if (!tr.steps.length) return false; - if (dispatch) dispatch(tr.scrollIntoView()); + if (dispatch) dispatch(tr.setMeta('click', { pressed: true })); return true; }; };