diff --git a/editors/demo/src/Editoria/Editoria.js b/editors/demo/src/Editoria/Editoria.js index de7af773d97d8f31ea0e9b3e520a311a8e940195..1e09a6445604dc02ab50b6b4ea25484588e6972f 100644 --- a/editors/demo/src/Editoria/Editoria.js +++ b/editors/demo/src/Editoria/Editoria.js @@ -6,7 +6,6 @@ import { EditoriaLayout, EditoriaMobileLayout } from './layout'; import { config, configMobile } from './config'; import { demo } from './demo'; import { debounce } from 'lodash'; -import { TablesService } from 'wax-table-service'; const renderImage = file => { const reader = new FileReader(); @@ -27,13 +26,7 @@ const user = { username: 'admin', }; -// const users = [{ -// userId: 'b3cfc28e-0f2e-45b5-b505-e66783d4f946', -// username: 'admin', -// }]; - const Editoria = () => { - const [myConfig, setMyConfig] = useState(config); const [width] = useWindowSize(); let layout = EditoriaLayout; @@ -46,46 +39,32 @@ const Editoria = () => { key = 'editoriaMobile'; } const editorRef = useRef(); - return ( - <> - <button - onClick={() => { - console.log(myConfig); - myConfig.PmPlugins = []; - myConfig.services = [...myConfig.services, new TablesService()]; - setMyConfig({ ...myConfig }); - }} - > - {' '} - change config - </button> - <Wax - ref={editorRef} - key={key} - config={myConfig} - autoFocus - placeholder="Type Something..." - fileUpload={file => renderImage(file)} - // value={demo} - // readonly - layout={layout} - // onChange={debounce(source => { - // console.log(JSON.stringify(source)); - // }, 200)} - user={user} - scrollMargin={200} - scrollThreshold={200} - /> - </> + const EditoriaComponent = useMemo( + () => ( + <> + <Wax + ref={editorRef} + key={key} + config={finalConfig} + autoFocus + placeholder="Type Something..." + fileUpload={file => renderImage(file)} + // value={demo} + // readonly + layout={layout} + // onChange={debounce(source => { + // console.log(JSON.stringify(source)); + // }, 200)} + user={user} + scrollMargin={200} + scrollThreshold={200} + /> + </> + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [layout, finalConfig], ); - // const EditoriaComponent = useMemo( - // () => ( - - // ), - // // eslint-disable-next-line react-hooks/exhaustive-deps - // [layout, myConfig], - // ); return <>{EditoriaComponent}</>; }; diff --git a/editors/demo/src/Editoria/config/config.js b/editors/demo/src/Editoria/config/config.js index f69db099ffa413d933121d4370f775b8efdbc6be..c8d58f3702dc2d5650d9a2c70ac407d2eb03af71 100644 --- a/editors/demo/src/Editoria/config/config.js +++ b/editors/demo/src/Editoria/config/config.js @@ -233,7 +233,7 @@ export default { 'SpecialCharacters', 'CodeBlock', 'ToggleAi', - // 'Tables', + 'Tables', 'TrackingAndEditing', 'FullScreen', ], @@ -305,13 +305,13 @@ export default { ], updateTags: saveTags, }, - // YjsService: { - // // eslint-disable-next-line no-restricted-globals - // connectionUrl: 'ws://localhost:5010', - // // connectionUrl: 'ws://0.tcp.ap.ngrok.io:17607', - // docIdentifier: 'prosemirror-r5dw4q2fe2eedreeeeeweewwewerc', - // YjsType: 'prosemirror', - // }, + YjsService: { + // eslint-disable-next-line no-restricted-globals + connectionUrl: 'ws://localhost:5010', + // connectionUrl: 'ws://0.tcp.ap.ngrok.io:17607', + docIdentifier: 'prosemirror-r5dw4q2fe2eedreeeeeweewwewerc', + YjsType: 'prosemirror', + }, AskAiContentService: { AskAiContentTransformation: DummyPromise, @@ -324,7 +324,7 @@ export default { }, services: [ - // new YjsService(), + new YjsService(), new BlockDropDownToolGroupService(), new AskAiContentService(), new CustomTagService(), @@ -336,9 +336,9 @@ export default { new TrackChangeService(), new CommentsService(), new ImageService(), - // new TablesService(), + new TablesService(), new BaseService(), - new NoteService(), + // new NoteService(), new CodeBlockService(), new EditingSuggestingService(), new DisplayTextToolGroupService(), diff --git a/editors/demo/src/Editoria/layout/EditoriaLayout.js b/editors/demo/src/Editoria/layout/EditoriaLayout.js index 87eeef71f895ce77d65f63f896b6045e2e7d1335..b93ce476c635fa6e8ae555dab143fde265cf6211 100644 --- a/editors/demo/src/Editoria/layout/EditoriaLayout.js +++ b/editors/demo/src/Editoria/layout/EditoriaLayout.js @@ -1,10 +1,4 @@ -import React, { - useContext, - useState, - useCallback, - useEffect, - useMemo, -} from 'react'; +import React, { useContext, useState, useCallback, useEffect } from 'react'; import styled, { css, ThemeProvider } from 'styled-components'; import PanelGroup from 'react-panelgroup'; import { @@ -216,22 +210,11 @@ const RightArea = ComponentPlugin('rightArea'); const CommentTrackToolBar = ComponentPlugin('commentTrackToolBar'); const BottomRightInfo = ComponentPlugin('BottomRightInfo'); -const WaxPortals = ComponentPlugin('waxPortals'); -const WaxOverlays = ComponentPlugin('waxOverlays'); - -const DummyCompo = () => { - console.log('dummy'); - return <div>MY DIV</div>; -}; - const EditoriaLayout = props => { const { pmViews: { main }, options, } = useContext(WaxContext); - const Dummy = useMemo(() => { - return <DummyCompo />; - }, []); let fullScreenStyles = {}; @@ -263,23 +246,14 @@ const EditoriaLayout = props => { }; const delayedShowedNotes = useCallback( - // setTimeout(() => showNotes(), 100), + setTimeout(() => showNotes(), 100), [], ); useEffect(() => {}, [delayedShowedNotes]); - const users = [ - { - userId: '1', - displayName: 'test test', - currentUser: true, - }, - ]; - return ( <ThemeProvider theme={cokoTheme}> - {Dummy} <Wrapper style={fullScreenStyles} id="wax-container"> <TopMenu> <MainMenuToolBar /> diff --git a/editors/demo/src/Editors.js b/editors/demo/src/Editors.js index 02c1e2bd0d8523adee16c870bfcf727ade1e0919..9227cb24d058f05235db4ac16201367105dbd3d8 100644 --- a/editors/demo/src/Editors.js +++ b/editors/demo/src/Editors.js @@ -91,7 +91,7 @@ const Editors = () => { case 'oen': return <OEN />; default: - return <Editoria />; + return <HHMI />; } }; diff --git a/editors/demo/src/HHMI/layout/HhmiLayout.js b/editors/demo/src/HHMI/layout/HhmiLayout.js index 44a3e589f73e74b938dedb90177ffd7b38e7bc9a..02ebe1a6da0a302f58731a2ca86ad23407562222 100644 --- a/editors/demo/src/HHMI/layout/HhmiLayout.js +++ b/editors/demo/src/HHMI/layout/HhmiLayout.js @@ -1,6 +1,6 @@ import React, { useContext } from 'react'; import styled, { ThemeProvider } from 'styled-components'; -import { WaxContext, ComponentPlugin } from 'wax-prosemirror-core'; +import { WaxContext, ComponentPlugin, WaxView } from 'wax-prosemirror-core'; import { grid, th } from '@pubsweet/ui-toolkit'; import { cokoTheme } from '../theme'; import EditorElements from './EditorElements'; @@ -86,7 +86,7 @@ const EditorContainer = styled.div` const MainMenuToolBar = ComponentPlugin('mainMenuToolBar'); -const HhmiLayout = ({ editor }) => { +const HhmiLayout = props => { const { options } = useContext(WaxContext); let fullScreenStyles = {}; @@ -115,7 +115,9 @@ const HhmiLayout = ({ editor }) => { <Main> <EditorArea> <WaxSurfaceScroll> - <EditorContainer>{editor}</EditorContainer> + <EditorContainer> + <WaxView {...props} /> + </EditorContainer> </WaxSurfaceScroll> </EditorArea> </Main> diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js index 46ec248371bcf4773ad2d8218289282ccb066c83..716d71005ac292f231ffaf5059f8f159ac244dba 100644 --- a/wax-prosemirror-core/src/Wax.js +++ b/wax-prosemirror-core/src/Wax.js @@ -63,17 +63,12 @@ const Wax = forwardRef((props, innerViewRef) => { const [application, setApplication] = useState(); const configHash = createConfigWithHash(config); - // useEffect(() => { - // // const newApplication = createApplication(props); - // // setApplication(newApplication); - // return () => application.resetApp(); - // }, []); + useEffect(() => { + return () => application.resetApp(); + }, []); useEffect(() => { - console.log('create application from config. hash:', configHash); - // if (application) application.resetApp(); const newApplication = createApplication(props); - WaxLayout = setupLayout(newApplication, layout); setApplication(newApplication); }, [configHash]); diff --git a/wax-prosemirror-core/src/WaxView.js b/wax-prosemirror-core/src/WaxView.js index 49783fec46487affa51182605e091ef0c250d5dd..cba278c69f74dc5610531ea93bfcb7d840c9ceeb 100644 --- a/wax-prosemirror-core/src/WaxView.js +++ b/wax-prosemirror-core/src/WaxView.js @@ -1,30 +1,19 @@ /* eslint-disable consistent-return */ /* eslint-disable react/prop-types */ -import React, { - useContext, - useCallback, - useMemo, - useEffect, - forwardRef, - useState, - useImperativeHandle, -} from 'react'; +import React, { useContext, useCallback, useEffect } from 'react'; import styled from 'styled-components'; -import { EditorState } from 'prosemirror-state'; -import { EditorView } from 'prosemirror-view'; -import trackedTransaction from './utilities/track-changes/trackedTransaction'; import { WaxContext } from './WaxContext'; -import { PortalContext } from './PortalContext'; import ComponentPlugin from './ComponentPlugin'; -import WaxOptions from './WaxOptions'; - -import helpers from './helpers/helpers'; import './styles/styles.css'; import useWaxView from './useWaxView'; const EditorContainer = styled.div` height: 100%; position: relative; + + > div:first-child { + height: 100%; + } `; const WaxPortals = ComponentPlugin('waxPortals'); @@ -35,17 +24,12 @@ const WaxView = props => { useWaxView(props); const { pmViews: { main }, - app, } = useContext(WaxContext); - // useEffect(() => { - // return () => app.resetApp(); - // }, []); - const editorRef = useCallback( element => { if (element && main) { - element.replaceWith(main?.dom); + element.replaceChildren(main?.dom); } }, [main], @@ -55,7 +39,7 @@ const WaxView = props => { if (autoFocus && main) { main.focus(); } - }, [autoFocus, main]); + }, [autoFocus]); return ( <EditorContainer> diff --git a/wax-prosemirror-core/src/useWaxView.js b/wax-prosemirror-core/src/useWaxView.js new file mode 100644 index 0000000000000000000000000000000000000000..0a0b8549487ffa40934f0161b1daee7f33f844c7 --- /dev/null +++ b/wax-prosemirror-core/src/useWaxView.js @@ -0,0 +1,122 @@ +/* eslint-disable consistent-return */ +/* eslint-disable react/prop-types */ +import { useContext, useEffect, useImperativeHandle } from 'react'; +import { EditorState } from 'prosemirror-state'; +import { EditorView } from 'prosemirror-view'; +import trackedTransaction from './utilities/track-changes/trackedTransaction'; +import { WaxContext } from './WaxContext'; +import { PortalContext } from './PortalContext'; +import WaxOptions from './WaxOptions'; +import helpers from './helpers/helpers'; +import './styles/styles.css'; + +let previousDoc; + +const useWaxView = props => { + const { + browserSpellCheck, + customValues, + readonly, + autoFocus, + user, + innerViewRef, + targetFormat, + serializer, + scrollMargin, + scrollThreshold, + } = props; + + let view; + + const context = useContext(WaxContext); + const { createPortal } = useContext(PortalContext); + + context.app.setContext({ ...context, createPortal }); + const schema = context.app.getSchema(); + + useEffect(() => { + context.app.bootServices(); + context.app.getShortCuts(); + context.app.getRules(); + + const options = WaxOptions({ + ...props, + schema, + plugins: context.app.getPlugins(), + }); + + view = new EditorView(null, { + editable: () => !readonly, + customValues, + state: EditorState.create(options), + dispatchTransaction, + disallowedTools: [], + user, + scrollMargin: scrollMargin || 200, + scrollThreshold: scrollThreshold || 200, + attributes: { + spellcheck: browserSpellCheck ? 'true' : 'false', + }, + }); + + context.updateView( + { + main: view, + }, + 'main', + ); + setTimeout(() => { + if (autoFocus && view) { + view.state.tr.insertText('', 0); + view.dispatch(view.state.tr.scrollIntoView()); + view.focus(); + } + }, 500); + }, [readonly, customValues, context.app.id]); + + useEffect(() => { + return () => (view = null); + }, []); + + useImperativeHandle(innerViewRef, () => ({ + getContent() { + return helpers.getDocContent(schema, serializer, targetFormat, context); + }, + })); + + const dispatchTransaction = transaction => { + const { TrackChange } = props; + const tr = + TrackChange && TrackChange.enabled + ? trackedTransaction(transaction, view.state, user, context) + : transaction; + + if (!view) return; + + previousDoc = view.state.doc; + const state = view.state.apply(tr); + view.updateState(state); + + /* when a transaction comes from a view other than + main don't keep updating the view ,as this is + the central point of each transaction + */ + context.setTransaction(transaction); + + if (!transaction.getMeta('outsideView')) { + context.updateView( + { + main: view, + }, + 'main', + ); + } + + const docContent = + targetFormat === 'JSON' ? state.doc.toJSON() : state.doc.content; + if (!previousDoc.eq(view.state.doc) || tr.getMeta('forceUpdate')) + props.onChange(docContent); + }; +}; + +export default useWaxView;