1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop: TinyMCE: Added support for scroll restore

This commit is contained in:
Laurent Cozic 2020-05-04 18:31:55 +01:00
parent c3a1e7c6e9
commit 3f8833eaf1
4 changed files with 101 additions and 4 deletions

View File

@ -68,6 +68,7 @@ ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
ElectronClient/gui/NoteEditor/NoteEditor.js
ElectronClient/gui/NoteEditor/styles/index.js
ElectronClient/gui/NoteEditor/utils/index.js

1
.gitignore vendored
View File

@ -58,6 +58,7 @@ ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js
ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
ElectronClient/gui/NoteEditor/NoteEditor.js
ElectronClient/gui/NoteEditor/styles/index.js
ElectronClient/gui/NoteEditor/utils/index.js

View File

@ -1,7 +1,8 @@
import * as React from 'react';
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from 'react';
import { EditorCommand, NoteBodyEditorProps } from '../../utils/types';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
import { resourcesStatus } from '../../utils/resourceHandling';
import useScroll from './utils/useScroll';
const { MarkupToHtml } = require('lib/joplin-renderer');
const taboverride = require('taboverride');
const { reg } = require('lib/registry.js');
@ -142,6 +143,9 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
const attachResources = useRef(null);
attachResources.current = props.attachResources;
const props_onMessage = useRef(null);
props_onMessage.current = props.onMessage;
const markupToHtml = useRef(null);
markupToHtml.current = props.markupToHtml;
@ -154,6 +158,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
const styles = styles_(props);
const theme = themeStyle(props.theme);
const { scrollToPercent } = useScroll({ editor, onScroll: props.onScroll });
const dispatchDidUpdate = (editor:any) => {
if (dispatchDidUpdateIID_) clearTimeout(dispatchDidUpdateIID_);
dispatchDidUpdateIID_ = setTimeout(() => {
@ -197,10 +203,25 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
console.warn('TinyMCE::setContent - not implemented');
},
resetScroll: () => {
console.warn('TinyMCE::resetScroll - not implemented');
if (editor) editor.getWin().scrollTo(0,0);
},
scrollTo: (/* options:ScrollOptions*/) => {
console.warn('TinyMCE::scrollTo - not implemented');
scrollTo: (options:ScrollOptions) => {
if (!editor) return;
if (options.type === ScrollOptionTypes.Hash) {
const anchor = editor.getDoc().getElementById(options.value);
if (!anchor) {
console.warn('Cannot find hash', options);
return;
}
anchor.scrollIntoView();
} else if (options.type === ScrollOptionTypes.Percent) {
scrollToPercent(options.value);
} else {
throw new Error(`Unsupported scroll options: ${options.type}`);
}
},
clearState: () => {
console.warn('TinyMCE::clearState - not implemented');
@ -575,6 +596,10 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
editor.on('init', () => {
setEditorReady(true);
});
editor.on('SetContent', () => {
props_onMessage.current({ channel: 'noteRenderComplete' });
});
},
});

View File

@ -0,0 +1,70 @@
import { useEffect, useCallback, useRef } from 'react';
interface HookDependencies {
editor:any,
onScroll: Function,
}
export default function useScroll(dependencies:HookDependencies) {
const { editor, onScroll } = dependencies;
const scrollTimeoutId_ = useRef(null);
const maxScrollTop = useCallback(() => {
if (!editor) return 0;
const doc = editor.getDoc();
const win = editor.getWin();
if (!doc || !win) return 0;
const firstChild = doc.firstElementChild;
if (!firstChild) return 0;
const winHeight = win.innerHeight;
const contentHeight = firstChild.scrollHeight;
return contentHeight < winHeight ? 0 : contentHeight - winHeight;
}, [editor]);
const scrollTop = useCallback(() => {
if (!editor) return 0;
const win = editor.getWin();
if (!win) return 0;
return win.scrollY;
}, [editor]);
const scrollPercent = useCallback(() => {
const m = maxScrollTop();
const t = scrollTop();
return m <= 0 ? 0 : t / m;
}, [maxScrollTop, scrollTop]);
const scrollToPercent = useCallback((percent:number) => {
if (!editor) return;
editor.getWin().scrollTo(0, maxScrollTop() * percent);
}, [editor, maxScrollTop]);
const scheduleOnScroll = useCallback((event: any) => {
if (scrollTimeoutId_.current) {
clearTimeout(scrollTimeoutId_.current);
scrollTimeoutId_.current = null;
}
scrollTimeoutId_.current = setTimeout(() => {
scrollTimeoutId_.current = null;
onScroll(event);
}, 10);
}, [onScroll]);
const onEditorScroll = useCallback(() => {
scheduleOnScroll({ percent: scrollPercent() });
}, [scheduleOnScroll, scrollPercent]);
useEffect(() => {
if (!editor) return () => {};
editor.getDoc().addEventListener('scroll', onEditorScroll);
return () => {
editor.getDoc().removeEventListener('scroll', onEditorScroll);
};
}, [editor, onEditorScroll]);
return { scrollToPercent };
}