diff --git a/editors/demo/src/Editoria/Editoria.js b/editors/demo/src/Editoria/Editoria.js index 45d5b114ee90370a54a3c881cb138b55390385c8..a1e859cac18e18ec0d088873a6e1982f450a612e 100644 --- a/editors/demo/src/Editoria/Editoria.js +++ b/editors/demo/src/Editoria/Editoria.js @@ -5,6 +5,7 @@ import { Wax } from 'wax-prosemirror-core'; import { EditoriaLayout, EditoriaMobileLayout } from './layout'; import { config, configMobile } from './config'; import { demo } from './demo'; +import { debounce } from 'lodash'; const renderImage = file => { const reader = new FileReader(); @@ -47,10 +48,13 @@ const Editoria = () => { autoFocus placeholder="Type Something..." fileUpload={file => renderImage(file)} - value={`<p> some text</p><h2>h2</h2><h3>a head</h3><h4>fff</h4><h1>ttt</h1>`} + value={`<p class="paragraph"><span class="small-caps">some</span> text</p><h2>h2</h2><h3>a head</h3><h4>fff</h4><h1>ttt</h1>`} // readonly layout={layout} - // onChange={source => console.log(source)} + onChange={debounce(source => { + console.log(source); + }, 3000)} + onBlur={source => console.log(source)} user={user} /> </> diff --git a/wax-prosemirror-components/src/components/specialCharacters/SpecialCharactersComponent.js b/wax-prosemirror-components/src/components/specialCharacters/SpecialCharactersComponent.js index 5d1b1465f4d91f09c52a0388372df66f2b0acb7c..6796de0d9ca1eaf3bbad414b039eae0ffe8bf4c5 100644 --- a/wax-prosemirror-components/src/components/specialCharacters/SpecialCharactersComponent.js +++ b/wax-prosemirror-components/src/components/specialCharacters/SpecialCharactersComponent.js @@ -7,7 +7,7 @@ import React, { useEffect, } from 'react'; import styled from 'styled-components'; -import { grid, th } from '@pubsweet/ui-toolkit'; +import { grid, th, override } from '@pubsweet/ui-toolkit'; import { v4 as uuidv4 } from 'uuid'; import { WaxContext } from 'wax-prosemirror-core'; import { filter, groupBy, debounce } from 'lodash'; @@ -53,24 +53,32 @@ const CharactersListComponent = styled.div` overflow-y: scroll; overflow-x: hidden; padding-top: ${grid(2)}; + + ${override('Wax.CharactersListComponent')} `; const SpecialCharactersGroup = styled.div` display: flex; flex-direction: column; padding-top: ${grid(2)}; + + ${override('Wax.SpecialCharactersGroup')} `; const GroupTitle = styled.div` font-size: 17px; color: ${th('colorPrimary')}; padding: 0 ${grid(2)} ${grid(2)} ${grid(2)}; + + ${override('Wax.GroupTitle')} `; const GroupCharacters = styled.div` display: flex; flex-direction: row; flex-wrap: wrap; + + ${override('Wax.GroupCharacters')} `; const SpecialCharacter = styled.div` @@ -95,6 +103,8 @@ const SpecialCharacter = styled.div` color: ${th('colorPrimary')}; } } + ${override('Wax.SpecialCharacterButton')} + `; // const LastUsedComponent = styled.div` diff --git a/wax-prosemirror-components/src/ui/buttons/Dropdown.js b/wax-prosemirror-components/src/ui/buttons/Dropdown.js index 5b1aa5ce82949ceb116d8cd69543d4192ffddf57..4dd6147fa36016dd9f4671666dda268f72406fd6 100644 --- a/wax-prosemirror-components/src/ui/buttons/Dropdown.js +++ b/wax-prosemirror-components/src/ui/buttons/Dropdown.js @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { grid } from '@pubsweet/ui-toolkit'; +import { grid, override } from '@pubsweet/ui-toolkit'; import MenuButton from './MenuButton'; // font size 0 reason: https://stackoverflow.com/a/19212391 @@ -16,6 +16,8 @@ const DropWrapper = styled.div` background: white; margin-top: ${grid(1)}; position: absolute; + + ${override('Wax.MoreDropWrapper')} `; const Dropdown = props => { diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js index 09f5faf635f4d3239ebcd446b34e2fd95ea5848e..e76225864f8965cff8ba4b86da2894059cffa94b 100644 --- a/wax-prosemirror-core/src/Wax.js +++ b/wax-prosemirror-core/src/Wax.js @@ -51,49 +51,45 @@ const Wax = props => { if (!application) return null; const WaxOnchange = onChange || (v => true); - const finalOnChange = schema => - debounce( - content => { - /* HACK alter toDOM of footnote, because of how PM treats inline nodes + const finalOnChange = schema => content => { + /* HACK alter toDOM of footnote, because of how PM treats inline nodes with content */ - const notes = []; - each(schema.nodes, node => { - if (node.groups.includes('notes')) notes.push(node); - }); - - if (notes.length > 0) { - notes.forEach(note => { - const old = schema.nodes[note.name].spec.toDOM; - schema.nodes[note.name].spec.toDOM = node => { - // eslint-disable-next-line prefer-rest-params - old.apply(this); - if (node) return [note.name, node.attrs, 0]; - }; - }); - } - - if (targetFormat === 'JSON') { - WaxOnchange(content); - } else { - const serialize = serializer(schema); - WaxOnchange(serialize(content)); - } - - if (notes.length > 0) { - notes.forEach(note => { - const old = schema.nodes[note.name].spec.toDOM; - schema.nodes[note.name].spec.toDOM = node => { - // eslint-disable-next-line prefer-rest-params - old.apply(this); - if (node) return [note.name, node.attrs]; - }; - }); - } - }, - 1000, - { maxWait: 5000 }, - ); + const notes = []; + each(schema.nodes, node => { + if (node.groups.includes('notes')) notes.push(node); + }); + + if (notes.length > 0) { + notes.forEach(note => { + const old = schema.nodes[note.name].spec.toDOM; + schema.nodes[note.name].spec.toDOM = node => { + // eslint-disable-next-line prefer-rest-params + old.apply(this); + if (node) return [note.name, node.attrs, 0]; + }; + }); + } + + if (targetFormat === 'JSON') { + WaxOnchange(content); + } else { + const serialize = serializer(schema); + WaxOnchange(serialize(content)); + } + + if (notes.length > 0) { + notes.forEach(note => { + const old = schema.nodes[note.name].spec.toDOM; + schema.nodes[note.name].spec.toDOM = node => { + // eslint-disable-next-line prefer-rest-params + old.apply(this); + if (node) return [note.name, node.attrs]; + }; + }); + } + }; + const TrackChange = application.config.get('config.EnableTrackChangeService'); const Layout = application.container.get('Layout'); @@ -114,6 +110,7 @@ const Wax = props => { TrackChange={TrackChange} user={user} value={value} + serializer={serializer} > {({ editor }) => <WaxRender className={className} editor={editor} />} </WaxView> diff --git a/wax-prosemirror-core/src/WaxView.js b/wax-prosemirror-core/src/WaxView.js index e6faf536c2680442c72c5d78084ac9279b6a2786..f4749b4b34c97576b3a6e39bc52a7856677803a7 100644 --- a/wax-prosemirror-core/src/WaxView.js +++ b/wax-prosemirror-core/src/WaxView.js @@ -4,6 +4,7 @@ import React, { useCallback, useMemo, useEffect, + useState, } from 'react'; import applyDevTools from 'prosemirror-dev-tools'; @@ -21,11 +22,12 @@ import WaxOptions from './WaxOptions'; const WaxPortals = ComponentPlugin('waxPortals'); let previousDoc; -let view; + export default props => { const { readonly, onBlur, debug, autoFocus, user, targetFormat } = props; const editorRef = useRef(); - + let view; + const [mounted, setMounted] = useState(false); const context = useContext(WaxContext); const { createPortal } = useContext(PortalContext); @@ -33,7 +35,7 @@ export default props => { const schema = context.app.getSchema(); - if (!view) { + if (!mounted) { context.app.bootServices(); } @@ -63,9 +65,9 @@ export default props => { scrollThreshold: 200, handleDOMEvents: { blur: onBlur - ? // eslint-disable-next-line no-shadow - view => { - onBlur(view.state.doc.content); + ? editorView => { + const serialize = props.serializer(schema); + onBlur(serialize(editorView.state.doc.content)); } : null, }, @@ -78,6 +80,8 @@ export default props => { }, ); + setMounted(true); + context.updateView( { main: view, diff --git a/wax-prosemirror-schema/src/marks/smallcapsMark.js b/wax-prosemirror-schema/src/marks/smallcapsMark.js index 5b8e7e32360fa5fcdff1928ff8286ee762e436b7..bdf73f6f7f08c7ca817cd6cd20e1ec16d08bf90a 100644 --- a/wax-prosemirror-schema/src/marks/smallcapsMark.js +++ b/wax-prosemirror-schema/src/marks/smallcapsMark.js @@ -6,8 +6,11 @@ const smallcaps = { parseDOM: [ { tag: 'span.small-caps', - getAttrs(dom) { - return { class: dom.getAttribute('class') }; + getAttrs(hook, next) { + Object.assign(hook, { + class: hook.dom.getAttribute('class'), + }); + next(); }, }, ], diff --git a/wax-prosemirror-services/src/ShortCutsService/ShortCutsService.js b/wax-prosemirror-services/src/ShortCutsService/ShortCutsService.js index cbccb3caaca70074516938e8f1f6b557e3a21a53..1fd9a92c1ab5a4d4118ff7616357bbccfd8a721d 100644 --- a/wax-prosemirror-services/src/ShortCutsService/ShortCutsService.js +++ b/wax-prosemirror-services/src/ShortCutsService/ShortCutsService.js @@ -9,16 +9,23 @@ export default class ShortCutsService extends Service { shortCuts.createShortCuts(); } + // TODO start ShortCuts as Schema is initiated register() { const { PmPlugins } = this.app; this.container .bind('ShortCuts') .toDynamicValue(() => { - const { - schema: { schema }, - } = this.app; + if (this.app.schema) { + return new ShortCuts( + PmPlugins, + this.container.get('Schema').getSchema(), + ); + } - return new ShortCuts(PmPlugins, schema); + return new ShortCuts( + PmPlugins, + this.container.get('Schema').getSchema(), + ); }) .inSingletonScope(); }