From 3073b3e35d05b97b3704c8c74e1c59099d466581 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 30 Nov 2023 23:52:42 -0500 Subject: [PATCH 1/3] Convert BrewRenderer to function, PPR always on --- client/homebrew/brewRenderer/brewRenderer.jsx | 336 ++++++++---------- themes/Legacy/5ePHB/style.less | 5 +- 2 files changed, 152 insertions(+), 189 deletions(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 1cd2a301e..796c35127 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -1,9 +1,8 @@ /*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./brewRenderer.less'); const React = require('react'); -const createClass = require('create-react-class'); +const { useState, useRef, useEffect } = React; const _ = require('lodash'); -const cx = require('classnames'); const MarkdownLegacy = require('naturalcrit/markdownLegacy.js'); const Markdown = require('naturalcrit/markdown.js'); @@ -13,254 +12,215 @@ const ErrorBar = require('./errorBar/errorBar.jsx'); const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx'); const NotificationPopup = require('./notificationPopup/notificationPopup.jsx'); const Frame = require('react-frame-component').default; +const dedent = require('dedent-tabs').default; const Themes = require('themes/themes.json'); const PAGE_HEIGHT = 1056; -const PPR_THRESHOLD = 50; +const INITIAL_CONTENT = dedent` + + + + + +
`; + +//v=====----------------------< Brew Page Component >---------------------=====v// const BrewPage = (props)=>{ props = { contents : '', index : 0, ...props }; - return
+ return
; }; -const BrewRenderer = createClass({ - displayName : 'BrewRenderer', - getDefaultProps : function() { - return { - text : '', - style : '', - renderer : 'legacy', - theme : '5ePHB', - lang : '', - errors : [] - }; - }, - getInitialState : function() { - let pages; - if(this.props.renderer == 'legacy') { - pages = this.props.text.split('\\page'); - } else { - pages = this.props.text.split(/^\\page$/gm); - } - return { - viewablePageNumber : 0, - height : 0, - isMounted : false, +//v=====--------------------< Brew Renderer Component >-------------------=====v// +const renderedPages = []; +let rawPages = []; - pages : pages, - usePPR : pages.length >= PPR_THRESHOLD, - visibility : 'hidden', - initialContent : ` - - - - -
` - }; - }, - height : 0, - lastRender :
, - renderedPages : [], +const BrewRenderer = (props)=>{ + props = { + text : '', + style : '', + renderer : 'legacy', + theme : '5ePHB', + lang : '', + errors : [], + ...props + }; - componentWillUnmount : function() { - window.removeEventListener('resize', this.updateSize); - }, + const [state, setState] = useState({ + viewablePageNumber : 0, + height : PAGE_HEIGHT, + isMounted : false, + visibility : 'hidden', + }); - componentDidUpdate : function(prevProps) { - if(prevProps.text !== this.props.text) { - let pages; - if(this.props.renderer == 'legacy') { - pages = this.props.text.split('\\page'); - } else { - pages = this.props.text.split(/^\\page$/gm); - } - this.setState({ - pages : pages, - usePPR : pages.length >= PPR_THRESHOLD - }); - } - }, + const mainRef = useRef(null); - updateSize : function() { - this.setState({ - height : this.refs.main.parentNode.clientHeight, - }); - }, + if(props.renderer == 'legacy') { + rawPages = props.text.split('\\page'); + } else { + rawPages = props.text.split(/^\\page$/gm); + } - handleScroll : function(e){ - const target = e.target; - this.setState((prevState)=>({ - viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * prevState.pages.length) + useEffect(()=>{ // Unmounting steps + return ()=>{window.removeEventListener('resize', updateSize);}; + }, []); + + const updateSize = ()=>{ + setState((prevState)=>({ + ...prevState, + height : mainRef.current.parentNode.clientHeight, })); - }, + }; - shouldRender : function(pageText, index){ - if(!this.state.isMounted) return false; + const handleScroll = (e)=>{ + const target = e.target; + setState((prevState)=>({ + ...prevState, + viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * rawPages.length) + })); + }; - const viewIndex = this.state.viewablePageNumber; - if(index == viewIndex - 3) return true; - if(index == viewIndex - 2) return true; - if(index == viewIndex - 1) return true; - if(index == viewIndex) return true; - if(index == viewIndex + 1) return true; - if(index == viewIndex + 2) return true; - if(index == viewIndex + 3) return true; + const shouldRender = (pageText, index)=>{ + if(!state.isMounted) return false; - //Check for style tages - if(pageText.indexOf('` }} />; + const renderStyle = ()=>{ + if(!props.style) return; + const cleanStyle = sanitizeScriptTags(props.style); + //return
@layer styleTab {\n${sanitizeScriptTags(props.style)}\n} ` }} />; return
${cleanStyle} ` }} />; - }, + }; - renderPage : function(pageText, index){ - let cleanPageText = this.sanitizeScriptTags(pageText); - if(this.props.renderer == 'legacy') - return
; - else { + const renderPage = (pageText, index)=>{ + console.log(`renderPage ${index}`); + let cleanPageText = sanitizeScriptTags(pageText); + if(props.renderer == 'legacy') { + const html = MarkdownLegacy.render(cleanPageText); + return ; + } else { cleanPageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear) const html = Markdown.render(cleanPageText); - return ( - - ); + return ; } - }, + }; - renderPages : function(){ - if(this.state.usePPR){ - _.forEach(this.state.pages, (page, index)=>{ - if((this.shouldRender(page, index) || !this.renderedPages[index]) && typeof window !== 'undefined'){ - this.renderedPages[index] = this.renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range - } - }); - return this.renderedPages; - } - if(this.props.errors && this.props.errors.length) return this.lastRender; - this.lastRender = _.map(this.state.pages, (page, index)=>{ - if(typeof window !== 'undefined') { - return this.renderPage(page, index); - } else { - return this.renderDummyPage(index); + const renderPages = ()=>{ + if(props.errors && props.errors.length) + return renderedPages; + + _.forEach(rawPages, (page, index)=>{ + if((shouldRender(page, index) || !renderedPages[index]) && typeof window !== 'undefined'){ + renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range } }); - return this.lastRender; - }, + return renderedPages; + }; - frameDidMount : function(){ //This triggers when iFrame finishes internal "componentDidMount" + const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount" setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame - this.updateSize(); - window.addEventListener('resize', this.updateSize); - this.renderPages(); //Make sure page is renderable before showing - this.setState({ + updateSize(); + window.addEventListener('resize', updateSize); + renderPages(); //Make sure page is renderable before showing + setState((prevState)=>({ + ...prevState, isMounted : true, visibility : 'visible' - }); + })); }, 100); - }, + }; - emitClick : function(){ - // console.log('iFrame clicked'); + const emitClick = ()=>{ // Allow clicks inside iFrame to interact with dropdowns, etc. from outside if(!window || !document) return; document.dispatchEvent(new MouseEvent('click')); - }, + }; - render : function(){ - //render in iFrame so broken code doesn't crash the site. - //Also render dummy page while iframe is mounting. - const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; - const themePath = this.props.theme ?? '5ePHB'; - const baseThemePath = Themes[rendererPath][themePath].baseTheme; - return ( - - {!this.state.isMounted - ?
-
- {this.renderDummyPage(1)} -
+ const rendererPath = props.renderer == 'V3' ? 'V3' : 'Legacy'; + const themePath = props.theme ?? '5ePHB'; + const baseThemePath = Themes[rendererPath][themePath].baseTheme; + + return ( + <> + {/*render dummy page while iFrame is mounting.*/} + {!state.isMounted + ?
+
+ {renderDummyPage(1)}
- : null} +
+ : null} - {this.emitClick();}} - > -
+ {/*render in iFrame so broken code doesn't crash the site.*/} + {emitClick();}} + > +
- -
- - -
- - {baseThemePath && - - } - - {/* Apply CSS from Style tab and render pages from Markdown tab */} - {this.state.isMounted - && - <> - {this.renderStyle()} -
- {this.renderPages()} -
- - } + +
+ +
- - {this.renderPageInfo()} - {this.renderPPRmsg()} - - ); - } -}); + + {baseThemePath && + + } + + + {/* Apply CSS from Style tab and render pages from Markdown tab */} + {state.isMounted + && + <> + {renderStyle()} +
+ {renderPages()} +
+ + } +
+ + {renderPageInfo()} + + ); +}; module.exports = BrewRenderer; diff --git a/themes/Legacy/5ePHB/style.less b/themes/Legacy/5ePHB/style.less index fa7539f16..51b5ddfbb 100644 --- a/themes/Legacy/5ePHB/style.less +++ b/themes/Legacy/5ePHB/style.less @@ -40,7 +40,7 @@ body { -webkit-column-gap : 1cm; -moz-column-gap : 1cm; } -.phb{ +.phb,.page{ .useColumns(); counter-increment : phb-page-numbers; position : relative; @@ -59,6 +59,9 @@ body { page-break-before : always; page-break-after : always; contain : size; +} + +.phb{ //***************************** // * BASE // *****************************/ From 39e33da2d1ebde20d8e72258bc2c64e4fd3732d8 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 4 Dec 2023 14:46:51 -0500 Subject: [PATCH 2/3] Cleanup --- client/homebrew/brewRenderer/brewRenderer.jsx | 5 ++--- themes/Legacy/5ePHB/style.less | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 796c35127..fde91fc68 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -88,7 +88,7 @@ const BrewRenderer = (props)=>{ })); }; - const shouldRender = (pageText, index)=>{ + const shouldRender = (index)=>{ if(!state.isMounted) return false; if(Math.abs(index - state.viewablePageNumber) <= 3) @@ -128,7 +128,6 @@ const BrewRenderer = (props)=>{ }; const renderPage = (pageText, index)=>{ - console.log(`renderPage ${index}`); let cleanPageText = sanitizeScriptTags(pageText); if(props.renderer == 'legacy') { const html = MarkdownLegacy.render(cleanPageText); @@ -145,7 +144,7 @@ const BrewRenderer = (props)=>{ return renderedPages; _.forEach(rawPages, (page, index)=>{ - if((shouldRender(page, index) || !renderedPages[index]) && typeof window !== 'undefined'){ + if((shouldRender(index) || !renderedPages[index]) && typeof window !== 'undefined'){ renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range } }); diff --git a/themes/Legacy/5ePHB/style.less b/themes/Legacy/5ePHB/style.less index 51b5ddfbb..89b5dd1e9 100644 --- a/themes/Legacy/5ePHB/style.less +++ b/themes/Legacy/5ePHB/style.less @@ -40,7 +40,7 @@ body { -webkit-column-gap : 1cm; -moz-column-gap : 1cm; } -.phb,.page{ +.phb{ .useColumns(); counter-increment : phb-page-numbers; position : relative; From 202b27596648289f8ca80fc57f9c827dea33529e Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Mon, 4 Dec 2023 17:07:20 -0500 Subject: [PATCH 3/3] Tweak legacy style so V3 pages offscreen keep size --- themes/Legacy/5ePHB/style.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/Legacy/5ePHB/style.less b/themes/Legacy/5ePHB/style.less index 89b5dd1e9..09eb2eec7 100644 --- a/themes/Legacy/5ePHB/style.less +++ b/themes/Legacy/5ePHB/style.less @@ -40,7 +40,7 @@ body { -webkit-column-gap : 1cm; -moz-column-gap : 1cm; } -.phb{ +.phb, .page{ .useColumns(); counter-increment : phb-page-numbers; position : relative;