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`
+
+
+ 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
// *****************************/