mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-11 15:32:47 +00:00
Bind livescrolling when done via scrollbars.
This commit is contained in:
@@ -19,6 +19,7 @@ const DOMPurify = require('dompurify');
|
|||||||
const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false };
|
const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false };
|
||||||
|
|
||||||
const PAGE_HEIGHT = 1056;
|
const PAGE_HEIGHT = 1056;
|
||||||
|
let isScrolling;
|
||||||
|
|
||||||
const INITIAL_CONTENT = dedent`
|
const INITIAL_CONTENT = dedent`
|
||||||
<!DOCTYPE html><html><head>
|
<!DOCTYPE html><html><head>
|
||||||
@@ -87,9 +88,16 @@ const BrewRenderer = (props)=>{
|
|||||||
|
|
||||||
const handleScroll = (e)=>{
|
const handleScroll = (e)=>{
|
||||||
const target = e.target;
|
const target = e.target;
|
||||||
|
const newPage = Math.floor(target.scrollTop / target.scrollHeight * rawPages.length);
|
||||||
|
if(newPage != state.viewablePageNumber) {
|
||||||
|
window.clearTimeout(isScrolling);
|
||||||
|
isScrolling = setTimeout(function() {
|
||||||
|
window.parent.document.dispatchEvent(new CustomEvent('renderScrolled', {}));
|
||||||
|
}, 66);
|
||||||
|
}
|
||||||
setState((prevState)=>({
|
setState((prevState)=>({
|
||||||
...prevState,
|
...prevState,
|
||||||
viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * rawPages.length)
|
viewablePageNumber : newPage
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ const DEFAULT_STYLE_TEXT = dedent`
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
let lastPage = 0;
|
let lastPage = 0;
|
||||||
|
let lockBrewJump = false;
|
||||||
|
let lockSourceJump = false;
|
||||||
|
let scrollingJump = false;
|
||||||
|
|
||||||
const isElementCodeMirror=(element)=>{
|
const isElementCodeMirror=(element)=>{
|
||||||
let el = element;
|
let el = element;
|
||||||
@@ -73,6 +76,7 @@ const Editor = createClass({
|
|||||||
this.highlightCustomMarkdown();
|
this.highlightCustomMarkdown();
|
||||||
window.addEventListener('resize', this.updateEditorSize);
|
window.addEventListener('resize', this.updateEditorSize);
|
||||||
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
|
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
|
||||||
|
document.addEventListener('renderScrolled', this.handleBrewScroll);
|
||||||
document.addEventListener('keydown', this.handleControlKeys);
|
document.addEventListener('keydown', this.handleControlKeys);
|
||||||
document.addEventListener('click', (e)=>{
|
document.addEventListener('click', (e)=>{
|
||||||
if(isElementCodeMirror(e.target) && this.props.liveScroll ) {
|
if(isElementCodeMirror(e.target) && this.props.liveScroll ) {
|
||||||
@@ -128,7 +132,6 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!(e.ctrlKey || e.metaKey)) return;
|
if(!(e.ctrlKey || e.metaKey)) return;
|
||||||
console.log(e);
|
|
||||||
// Handle CTRL-HOME and CTRL-END
|
// Handle CTRL-HOME and CTRL-END
|
||||||
if(((e.keyCode == END_KEY) || (e.keyCode == HOME_KEY)) && this.props.liveScroll) this.brewJump();
|
if(((e.keyCode == END_KEY) || (e.keyCode == HOME_KEY)) && this.props.liveScroll) this.brewJump();
|
||||||
|
|
||||||
@@ -140,6 +143,19 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleBrewScroll : function() {
|
||||||
|
if(!this.props.liveScroll) return;
|
||||||
|
scrollingJump = true;
|
||||||
|
this.sourceJump();
|
||||||
|
scrollingJump = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSourceScroll : function(e) {
|
||||||
|
if(!this.props.liveScroll) return;
|
||||||
|
scrollingJump = true;
|
||||||
|
this.brewJump();
|
||||||
|
scrollingJump = false;
|
||||||
|
},
|
||||||
|
|
||||||
updateEditorSize : function() {
|
updateEditorSize : function() {
|
||||||
if(this.codeEditor.current) {
|
if(this.codeEditor.current) {
|
||||||
@@ -318,8 +334,11 @@ const Editor = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
brewJump : function(targetPage=this.getCurrentPage()){
|
brewJump : function(targetPage=this.getCurrentPage()){
|
||||||
|
if(lockBrewJump) return;
|
||||||
if(!window) return;
|
if(!window) return;
|
||||||
// console.log(`Scroll to: p${targetPage}`);
|
lockSourceJump = true;
|
||||||
|
lockBrewJump = true;
|
||||||
|
//console.log(`Scroll to: p${targetPage}`);
|
||||||
const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0];
|
const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0];
|
||||||
const currentPos = brewRenderer.scrollTop;
|
const currentPos = brewRenderer.scrollTop;
|
||||||
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
|
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
|
||||||
@@ -328,24 +347,33 @@ const Editor = createClass({
|
|||||||
const bounceDelay = 100;
|
const bounceDelay = 100;
|
||||||
const scrollDelay = 500;
|
const scrollDelay = 500;
|
||||||
|
|
||||||
if(!this.throttleBrewMove) {
|
if(scrollingJump) {
|
||||||
this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
|
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' });
|
||||||
brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
|
} else {
|
||||||
setTimeout(()=>{
|
if(!this.throttleBrewMove) {
|
||||||
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' });
|
this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
|
||||||
}, bounceDelay);
|
brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
|
||||||
}, scrollDelay, { leading: true, trailing: false });
|
setTimeout(()=>{
|
||||||
};
|
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' });
|
||||||
this.throttleBrewMove(currentPos, interimPos, targetPos);
|
}, bounceDelay);
|
||||||
|
}, scrollDelay, { leading: true, trailing: false });
|
||||||
|
};
|
||||||
|
this.throttleBrewMove(currentPos, interimPos, targetPos);
|
||||||
|
}
|
||||||
|
lockSourceJump = false;
|
||||||
|
lockBrewJump = false;
|
||||||
|
|
||||||
// const hashPage = (page != 1) ? `p${page}` : '';
|
// const hashPage = (page != 1) ? `p${page}` : '';
|
||||||
// window.location.hash = hashPage;
|
// window.location.hash = hashPage;
|
||||||
},
|
},
|
||||||
|
|
||||||
sourceJump : function(targetLine=null){
|
sourceJump : function(targetLine=null){
|
||||||
|
if(lockSourceJump) return;
|
||||||
if(this.isText()) {
|
if(this.isText()) {
|
||||||
if(targetLine == null) {
|
if(targetLine == null) {
|
||||||
targetLine = 0;
|
targetLine = 0;
|
||||||
|
lockBrewJump = true;
|
||||||
|
lockSourceJump = true;
|
||||||
|
|
||||||
const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page');
|
const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page');
|
||||||
const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height;
|
const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height;
|
||||||
@@ -368,23 +396,33 @@ const Editor = createClass({
|
|||||||
let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
|
let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
|
||||||
let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
||||||
|
|
||||||
//Scroll 1/10 of the way every 10ms until 1px off.
|
if(!scrollingJump) {
|
||||||
const incrementalScroll = setInterval(()=>{
|
//Scroll 1/10 of the way every 10ms until 1px off.
|
||||||
currentY += (targetY - currentY) / 10;
|
const incrementalScroll = setInterval(()=>{
|
||||||
this.codeEditor.current.codeMirror.scrollTo(null, currentY);
|
currentY += (targetY - currentY) / 10;
|
||||||
|
this.codeEditor.current.codeMirror.scrollTo(null, currentY);
|
||||||
|
|
||||||
// Update target: target height is not accurate until within +-10 lines of the visible window
|
// Update target: target height is not accurate until within +-10 lines of the visible window
|
||||||
if(Math.abs(targetY - currentY > 100))
|
if(Math.abs(targetY - currentY > 100))
|
||||||
targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
||||||
|
|
||||||
// End when close enough
|
// End when close enough
|
||||||
if(Math.abs(targetY - currentY) < 1) {
|
if(Math.abs(targetY - currentY) < 1) {
|
||||||
this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
|
this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
|
||||||
this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
|
this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
|
||||||
this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
||||||
clearInterval(incrementalScroll);
|
clearInterval(incrementalScroll);
|
||||||
}
|
lockBrewJump = false;
|
||||||
}, 10);
|
lockSourceJump = false;
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
} else {
|
||||||
|
this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
|
||||||
|
this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
|
||||||
|
this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
||||||
|
lockBrewJump = false;
|
||||||
|
lockSourceJump = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -415,6 +453,7 @@ const Editor = createClass({
|
|||||||
view={this.state.view}
|
view={this.state.view}
|
||||||
value={this.props.brew.text}
|
value={this.props.brew.text}
|
||||||
onChange={this.props.onTextChange}
|
onChange={this.props.onTextChange}
|
||||||
|
onScroll={this.handleSourceScroll}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
rerenderParent={this.rerenderParent} />
|
rerenderParent={this.rerenderParent} />
|
||||||
</>;
|
</>;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const closeTag = require('./close-tag');
|
|||||||
const autoCompleteEmoji = require('./autocompleteEmoji');
|
const autoCompleteEmoji = require('./autocompleteEmoji');
|
||||||
|
|
||||||
let CodeMirror;
|
let CodeMirror;
|
||||||
|
let isScrolling;
|
||||||
if(typeof window !== 'undefined'){
|
if(typeof window !== 'undefined'){
|
||||||
CodeMirror = require('codemirror');
|
CodeMirror = require('codemirror');
|
||||||
|
|
||||||
@@ -190,6 +191,14 @@ const CodeEditor = createClass({
|
|||||||
|
|
||||||
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
||||||
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
||||||
|
this.codeMirror.on('scroll', (cm)=>{
|
||||||
|
window.clearTimeout(isScrolling);
|
||||||
|
const props = this.props;
|
||||||
|
isScrolling = setTimeout(function() {
|
||||||
|
cm.setCursor({ line: cm.lineAtHeight(cm.getWrapperElement().getBoundingClientRect().top) + 1, ch: 0 });
|
||||||
|
props.onScroll(cm.lineAtHeight(cm.getWrapperElement().getBoundingClientRect().top));
|
||||||
|
}, 66);
|
||||||
|
});
|
||||||
this.updateSize();
|
this.updateSize();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user