diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 1cd2a301e..fde91fc68 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,214 @@ 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 = (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)=>{ + 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(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..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; @@ -59,6 +59,9 @@ body { page-break-before : always; page-break-after : always; contain : size; +} + +.phb{ //***************************** // * BASE // *****************************/