mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-09 20:12:41 +00:00
Add check for scroll event complete/ lift page state up
This commit is contained in:
@@ -58,6 +58,7 @@ const BrewRenderer = (props)=>{
|
|||||||
errors : [],
|
errors : [],
|
||||||
currentEditorPage : 0,
|
currentEditorPage : 0,
|
||||||
themeBundle : {},
|
themeBundle : {},
|
||||||
|
onPageChange : ()=>{},
|
||||||
...props
|
...props
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,27 +89,12 @@ const BrewRenderer = (props)=>{
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleScroll = (e)=>{
|
|
||||||
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)=>({
|
|
||||||
...prevState,
|
|
||||||
viewablePageNumber : newPage
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCurrentPage = (e)=>{
|
const getCurrentPage = (e)=>{
|
||||||
const { scrollTop, clientHeight, scrollHeight } = e.target;
|
const { scrollTop, clientHeight, scrollHeight } = e.target;
|
||||||
const totalScrollableHeight = scrollHeight - clientHeight;
|
const totalScrollableHeight = scrollHeight - clientHeight;
|
||||||
const currentPageNumber = Math.ceil((scrollTop / totalScrollableHeight) * rawPages.length);
|
const currentPageNumber = Math.ceil((scrollTop / totalScrollableHeight) * rawPages.length);
|
||||||
|
|
||||||
handleScroll(e);
|
props.onPageChange(currentPageNumber);
|
||||||
|
|
||||||
setState((prevState)=>({
|
setState((prevState)=>({
|
||||||
...prevState,
|
...prevState,
|
||||||
@@ -116,7 +102,6 @@ const BrewRenderer = (props)=>{
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const isInView = (index)=>{
|
const isInView = (index)=>{
|
||||||
if(!state.isMounted)
|
if(!state.isMounted)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ const DEFAULT_STYLE_TEXT = dedent`
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
let lastPage = 0;
|
let lastPage = 0;
|
||||||
let lockBrewJump = false;
|
let isJumping = false;
|
||||||
let lockSourceJump = false;
|
|
||||||
let scrollingJump = false;
|
|
||||||
|
|
||||||
const isElementCodeMirror=(element)=>{
|
const isElementCodeMirror=(element)=>{
|
||||||
let el = element;
|
let el = element;
|
||||||
@@ -76,7 +74,6 @@ 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 ) {
|
||||||
@@ -112,6 +109,9 @@ const Editor = createClass({
|
|||||||
if(prevProps.liveScroll != this.props.liveScroll) {
|
if(prevProps.liveScroll != this.props.liveScroll) {
|
||||||
if((prevProps.liveScroll != undefined) && (this.props.liveScroll)) this.brewJump();
|
if((prevProps.liveScroll != undefined) && (this.props.liveScroll)) this.brewJump();
|
||||||
};
|
};
|
||||||
|
if(prevProps.currentBrewRendererPage !== this.props.currentBrewRendererPage) {
|
||||||
|
this.handleBrewScroll();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleControlKeys : function(e){
|
handleControlKeys : function(e){
|
||||||
@@ -149,17 +149,12 @@ const Editor = createClass({
|
|||||||
|
|
||||||
handleBrewScroll : function() {
|
handleBrewScroll : function() {
|
||||||
if(!this.props.liveScroll) return;
|
if(!this.props.liveScroll) return;
|
||||||
scrollingJump = true;
|
this.sourceJump(undefined, false);
|
||||||
this.sourceJump();
|
|
||||||
scrollingJump = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSourceScroll : function() {
|
handleSourceScroll : function() {
|
||||||
if(!this.props.liveScroll) return;
|
if(!this.props.liveScroll) return;
|
||||||
console.log("handleSourceScroll")
|
this.brewJump(this.getCurrentPage(false), false);
|
||||||
scrollingJump = true;
|
|
||||||
this.brewJump(this.getCurrentPage(false));
|
|
||||||
scrollingJump = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateEditorSize : function() {
|
updateEditorSize : function() {
|
||||||
@@ -361,13 +356,10 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
brewJump : function(targetPage=this.getCurrentPage()){
|
brewJump : function(targetPage=this.getCurrentPage(), smooth=true){
|
||||||
console.log(`jumpBrew to page ${targetPage}`)
|
if(isJumping) return;
|
||||||
if(lockBrewJump) return;
|
|
||||||
if(!window) return;
|
if(!window) return;
|
||||||
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;
|
||||||
@@ -376,9 +368,26 @@ const Editor = createClass({
|
|||||||
const bounceDelay = 100;
|
const bounceDelay = 100;
|
||||||
const scrollDelay = 500;
|
const scrollDelay = 500;
|
||||||
|
|
||||||
if(scrollingJump) {
|
if(Math.abs(targetPos) < 1)
|
||||||
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' });
|
return;
|
||||||
} else {
|
|
||||||
|
isJumping = true;
|
||||||
|
|
||||||
|
// Detect end of scroll event to avoid feedback loops
|
||||||
|
let scrollingTimeout;
|
||||||
|
|
||||||
|
const checkIfScrollComplete = () => {
|
||||||
|
clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
|
||||||
|
scrollingTimeout = setTimeout(() => {
|
||||||
|
isJumping = false;
|
||||||
|
brewRenderer.removeEventListener('scroll', checkIfScrollComplete);
|
||||||
|
}, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done
|
||||||
|
};
|
||||||
|
|
||||||
|
brewRenderer.addEventListener('scroll', checkIfScrollComplete);
|
||||||
|
console.log("added event listener")
|
||||||
|
|
||||||
|
if(smooth) {
|
||||||
if(!this.throttleBrewMove) {
|
if(!this.throttleBrewMove) {
|
||||||
this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
|
this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
|
||||||
brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
|
brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
|
||||||
@@ -388,35 +397,19 @@ const Editor = createClass({
|
|||||||
}, scrollDelay, { leading: true, trailing: false });
|
}, scrollDelay, { leading: true, trailing: false });
|
||||||
};
|
};
|
||||||
this.throttleBrewMove(currentPos, interimPos, targetPos);
|
this.throttleBrewMove(currentPos, interimPos, targetPos);
|
||||||
|
} else {
|
||||||
|
brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' });
|
||||||
}
|
}
|
||||||
lockSourceJump = false;
|
|
||||||
lockBrewJump = false;
|
|
||||||
|
|
||||||
// const hashPage = (page != 1) ? `p${page}` : '';
|
|
||||||
// window.location.hash = hashPage;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
sourceJump : function(targetLine=null){
|
sourceJump : function(targetLine=null, smooth=true){
|
||||||
if(lockSourceJump) return;
|
if(isJumping) 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 brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height;
|
|
||||||
|
|
||||||
let currentPage = 1;
|
|
||||||
for (const page of pageCollection) {
|
|
||||||
if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) {
|
|
||||||
currentPage = parseInt(page.id.slice(1)) || 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
|
const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
|
||||||
const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit);
|
const textString = this.props.brew.text.split(textSplit).slice(0, this.props.currentBrewRendererPage-1).join(textSplit);
|
||||||
const textPosition = textString.length;
|
const textPosition = textString.length;
|
||||||
const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0;
|
const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0;
|
||||||
|
|
||||||
@@ -425,7 +418,25 @@ 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);
|
||||||
|
|
||||||
if(!scrollingJump) {
|
if (Math.abs(targetY - currentY) < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isJumping = true;
|
||||||
|
|
||||||
|
// Detect end of scroll event to avoid feedback loops
|
||||||
|
let scrollingTimeout;
|
||||||
|
|
||||||
|
const checkIfScrollComplete = () => {
|
||||||
|
clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
|
||||||
|
scrollingTimeout = setTimeout(() => {
|
||||||
|
isJumping = false;
|
||||||
|
this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete);
|
||||||
|
}, 150); // If 150 ms pass without a scroll event, assume scrolling is done
|
||||||
|
};
|
||||||
|
|
||||||
|
this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete);
|
||||||
|
|
||||||
|
if(smooth) {
|
||||||
//Scroll 1/10 of the way every 10ms until 1px off.
|
//Scroll 1/10 of the way every 10ms until 1px off.
|
||||||
const incrementalScroll = setInterval(()=>{
|
const incrementalScroll = setInterval(()=>{
|
||||||
currentY += (targetY - currentY) / 10;
|
currentY += (targetY - currentY) / 10;
|
||||||
@@ -441,16 +452,12 @@ const Editor = createClass({
|
|||||||
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;
|
|
||||||
lockSourceJump = false;
|
|
||||||
}
|
}
|
||||||
}, 10);
|
}, 10);
|
||||||
} else {
|
} else {
|
||||||
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');
|
||||||
lockBrewJump = false;
|
|
||||||
lockSourceJump = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,22 +41,23 @@ const EditPage = createClass({
|
|||||||
|
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
brew : this.props.brew,
|
brew : this.props.brew,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
isPending : false,
|
isPending : false,
|
||||||
alertTrashedGoogleBrew : this.props.brew.trashed,
|
alertTrashedGoogleBrew : this.props.brew.trashed,
|
||||||
alertLoginToTransfer : false,
|
alertLoginToTransfer : false,
|
||||||
saveGoogle : this.props.brew.googleId ? true : false,
|
saveGoogle : this.props.brew.googleId ? true : false,
|
||||||
confirmGoogleTransfer : false,
|
confirmGoogleTransfer : false,
|
||||||
error : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||||
url : '',
|
url : '',
|
||||||
autoSave : true,
|
autoSave : true,
|
||||||
autoSaveWarning : false,
|
autoSaveWarning : false,
|
||||||
unsavedTime : new Date(),
|
unsavedTime : new Date(),
|
||||||
currentEditorPage : 0,
|
currentEditorPage : 0,
|
||||||
displayLockMessage : this.props.brew.lock || false,
|
currentBrewRendererPage : 0,
|
||||||
themeBundle : {}
|
displayLockMessage : this.props.brew.lock || false,
|
||||||
|
themeBundle : {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -113,6 +114,10 @@ const EditPage = createClass({
|
|||||||
this.editor.current.update();
|
this.editor.current.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleBrewRendererPageChange : function(pageNumber){
|
||||||
|
this.setState(()=>({ currentBrewRendererPage : pageNumber }));
|
||||||
|
},
|
||||||
|
|
||||||
handleTextChange : function(text){
|
handleTextChange : function(text){
|
||||||
//If there are errors, run the validator on every change to give quick feedback
|
//If there are errors, run the validator on every change to give quick feedback
|
||||||
let htmlErrors = this.state.htmlErrors;
|
let htmlErrors = this.state.htmlErrors;
|
||||||
@@ -413,6 +418,7 @@ const EditPage = createClass({
|
|||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
userThemes={this.props.userThemes}
|
userThemes={this.props.userThemes}
|
||||||
snippetBundle={this.state.themeBundle.snippets}
|
snippetBundle={this.state.themeBundle.snippets}
|
||||||
|
currentBrewRendererPage={this.state.currentBrewRendererPage}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer
|
<BrewRenderer
|
||||||
text={this.state.brew.text}
|
text={this.state.brew.text}
|
||||||
@@ -422,6 +428,7 @@ const EditPage = createClass({
|
|||||||
themeBundle={this.state.themeBundle}
|
themeBundle={this.state.themeBundle}
|
||||||
errors={this.state.htmlErrors}
|
errors={this.state.htmlErrors}
|
||||||
lang={this.state.brew.lang}
|
lang={this.state.brew.lang}
|
||||||
|
onPageChange={this.handleBrewRendererPageChange}
|
||||||
currentEditorPage={this.state.currentEditorPage}
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
allowPrint={true}
|
allowPrint={true}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,13 +39,14 @@ const NewPage = createClass({
|
|||||||
const brew = this.props.brew;
|
const brew = this.props.brew;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
brew : brew,
|
brew : brew,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
error : null,
|
error : null,
|
||||||
htmlErrors : Markdown.validate(brew.text),
|
htmlErrors : Markdown.validate(brew.text),
|
||||||
currentEditorPage : 0,
|
currentEditorPage : 0,
|
||||||
themeBundle : {}
|
currentBrewRendererPage : 0,
|
||||||
|
themeBundle : {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -108,6 +109,10 @@ const NewPage = createClass({
|
|||||||
this.editor.current.update();
|
this.editor.current.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleBrewRendererPageChange : function(pageNumber){
|
||||||
|
this.setState(()=>({ currentBrewRendererPage : pageNumber }));
|
||||||
|
},
|
||||||
|
|
||||||
handleTextChange : function(text){
|
handleTextChange : function(text){
|
||||||
//If there are errors, run the validator on every change to give quick feedback
|
//If there are errors, run the validator on every change to give quick feedback
|
||||||
let htmlErrors = this.state.htmlErrors;
|
let htmlErrors = this.state.htmlErrors;
|
||||||
@@ -221,6 +226,7 @@ const NewPage = createClass({
|
|||||||
renderer={this.state.brew.renderer}
|
renderer={this.state.brew.renderer}
|
||||||
userThemes={this.props.userThemes}
|
userThemes={this.props.userThemes}
|
||||||
snippetBundle={this.state.themeBundle.snippets}
|
snippetBundle={this.state.themeBundle.snippets}
|
||||||
|
currentBrewRendererPage={this.state.currentBrewRendererPage}
|
||||||
/>
|
/>
|
||||||
<BrewRenderer
|
<BrewRenderer
|
||||||
text={this.state.brew.text}
|
text={this.state.brew.text}
|
||||||
@@ -230,6 +236,7 @@ const NewPage = createClass({
|
|||||||
themeBundle={this.state.themeBundle}
|
themeBundle={this.state.themeBundle}
|
||||||
errors={this.state.htmlErrors}
|
errors={this.state.htmlErrors}
|
||||||
lang={this.state.brew.lang}
|
lang={this.state.brew.lang}
|
||||||
|
onPageChange={this.handleBrewRendererPageChange}
|
||||||
currentEditorPage={this.state.currentEditorPage}
|
currentEditorPage={this.state.currentEditorPage}
|
||||||
allowPrint={true}
|
allowPrint={true}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user