0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-14 04:12:43 +00:00

Merge branch 'master' into addCSSRoute-#1097

This commit is contained in:
Víctor Losada Hernández
2024-08-25 13:21:37 +02:00
committed by GitHub
7 changed files with 265 additions and 77 deletions

View File

@@ -7,6 +7,7 @@ const _ = require('lodash');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js'); const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
const Markdown = require('naturalcrit/markdown.js'); const Markdown = require('naturalcrit/markdown.js');
const ErrorBar = require('./errorBar/errorBar.jsx'); const ErrorBar = require('./errorBar/errorBar.jsx');
const ToolBar = require('./toolBar/toolBar.jsx');
//TODO: move to the brew renderer //TODO: move to the brew renderer
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx'); const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
@@ -60,10 +61,11 @@ const BrewRenderer = (props)=>{
}; };
const [state, setState] = useState({ const [state, setState] = useState({
viewablePageNumber : 0, height : PAGE_HEIGHT,
height : PAGE_HEIGHT, isMounted : false,
isMounted : false, visibility : 'hidden',
visibility : 'hidden', zoom : 100,
currentPageNumber : 1,
}); });
const mainRef = useRef(null); const mainRef = useRef(null);
@@ -85,11 +87,14 @@ const BrewRenderer = (props)=>{
})); }));
}; };
const handleScroll = (e)=>{ const getCurrentPage = (e)=>{
const target = e.target; const { scrollTop, clientHeight, scrollHeight } = e.target;
const totalScrollableHeight = scrollHeight - clientHeight;
const currentPageNumber = Math.ceil((scrollTop / totalScrollableHeight) * rawPages.length);
setState((prevState)=>({ setState((prevState)=>({
...prevState, ...prevState,
viewablePageNumber : Math.floor(target.scrollTop / target.scrollHeight * rawPages.length) currentPageNumber : currentPageNumber || 1
})); }));
}; };
@@ -100,23 +105,12 @@ const BrewRenderer = (props)=>{
if(index == props.currentEditorPage) //Already rendered before this step if(index == props.currentEditorPage) //Already rendered before this step
return false; return false;
if(Math.abs(index - state.viewablePageNumber) <= 3) if(Math.abs(index - state.currentPageNumber) <= 3)
return true; return true;
return false; return false;
}; };
const renderPageInfo = ()=>{
return <div className='pageInfo' ref={mainRef}>
<div>
{props.renderer}
</div>
<div>
{state.viewablePageNumber + 1} / {rawPages.length}
</div>
</div>;
};
const renderDummyPage = (index)=>{ const renderDummyPage = (index)=>{
return <div className='phb page' id={`p${index + 1}`} key={index}> return <div className='phb page' id={`p${index + 1}`} key={index}>
<i className='fas fa-spinner fa-spin' /> <i className='fas fa-spinner fa-spin' />
@@ -186,11 +180,19 @@ const BrewRenderer = (props)=>{
document.dispatchEvent(new MouseEvent('click')); document.dispatchEvent(new MouseEvent('click'));
}; };
//Toolbar settings:
const handleZoom = (newZoom)=>{
setState((prevState)=>({
...prevState,
zoom : newZoom
}));
};
return ( return (
<> <>
{/*render dummy page while iFrame is mounting.*/} {/*render dummy page while iFrame is mounting.*/}
{!state.isMounted {!state.isMounted
? <div className='brewRenderer' onScroll={handleScroll}> ? <div className='brewRenderer' onScroll={getCurrentPage}>
<div className='pages'> <div className='pages'>
{renderDummyPage(1)} {renderDummyPage(1)}
</div> </div>
@@ -198,11 +200,13 @@ const BrewRenderer = (props)=>{
: null} : null}
<ErrorBar errors={props.errors} /> <ErrorBar errors={props.errors} />
<div className='popups'> <div className='popups' ref={mainRef}>
<RenderWarnings /> <RenderWarnings />
<NotificationPopup /> <NotificationPopup />
</div> </div>
<ToolBar onZoomChange={handleZoom} currentPage={state.currentPageNumber} totalPages={rawPages.length}/>
{/*render in iFrame so broken code doesn't crash the site.*/} {/*render in iFrame so broken code doesn't crash the site.*/}
<Frame id='BrewRenderer' initialContent={INITIAL_CONTENT} <Frame id='BrewRenderer' initialContent={INITIAL_CONTENT}
style={{ width: '100%', height: '100%', visibility: state.visibility }} style={{ width: '100%', height: '100%', visibility: state.visibility }}
@@ -210,23 +214,23 @@ const BrewRenderer = (props)=>{
onClick={()=>{emitClick();}} onClick={()=>{emitClick();}}
> >
<div className={'brewRenderer'} <div className={'brewRenderer'}
onScroll={handleScroll} onScroll={getCurrentPage}
onKeyDown={handleControlKeys} onKeyDown={handleControlKeys}
tabIndex={-1} tabIndex={-1}
style={{ height: state.height }}> style={{ height: state.height }}>
{/* Apply CSS from Style tab and render pages from Markdown tab */} {/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted {state.isMounted
&& &&
<> <>
{renderStyle()} {renderStyle()}
<div className='pages' lang={`${props.lang || 'en'}`}> <div className='pages' lang={`${props.lang || 'en'}`} style={{ zoom: `${state.zoom}%` }}>
{renderPages()} {renderPages()}
</div> </div>
</> </>
} }
</div> </div>
</Frame> </Frame>
{renderPageInfo()}
</> </>
); );
}; };

View File

@@ -1,8 +1,9 @@
@import (multiple, less) 'shared/naturalcrit/styles/reset.less'; @import (multiple, less) 'shared/naturalcrit/styles/reset.less';
.brewRenderer { .brewRenderer {
will-change : transform; padding-top : 30px;
overflow-y : scroll; overflow-y : scroll;
will-change : transform;
:where(.pages) { :where(.pages) {
margin : 30px 0px; margin : 30px 0px;
& > :where(.page) { & > :where(.page) {
@@ -14,66 +15,31 @@
box-shadow : 1px 4px 14px #000000; box-shadow : 1px 4px 14px #000000;
} }
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 20px; width : 20px;
&:horizontal{ &:horizontal {
height: 20px; width : auto;
width:auto; height : 20px;
} }
&-thumb { &-thumb {
background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px); background : linear-gradient(90deg, #D3C1AF 15px, #00000000 15px);
&:horizontal{ &:horizontal { background : linear-gradient(0deg, #D3C1AF 15px, #00000000 15px); }
background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px);
}
}
&-corner {
visibility: hidden;
} }
&-corner { visibility : hidden; }
} }
} }
.pane { position : relative; } .pane { position : relative; }
.pageInfo {
position : absolute;
right : 17px;
bottom : 0;
z-index : 1000;
font-size : 10px;
font-weight : 800;
color : white;
background-color : #333333;
div {
display : inline-block;
padding : 8px 10px;
&:not(:last-child) { border-right : 1px solid #666666; }
}
}
.ppr_msg {
position : absolute;
bottom : 0;
left : 0px;
z-index : 1000;
padding : 8px 10px;
font-size : 10px;
font-weight : 800;
color : white;
background-color : #333333;
}
@media print { @media print {
.toolBar { display : none; }
.brewRenderer { .brewRenderer {
height: 100%; height : 100%;
overflow-y: unset; padding-top : unset;
overflow-y : unset;
.pages { .pages {
margin: 0px; margin : 0px;
&>.page { & > .page { box-shadow : unset; }
box-shadow: unset;
}
} }
} }
} }

View File

@@ -1,9 +1,10 @@
.popups { .popups {
position : fixed; position : fixed;
top : @navbarHeight; top : calc(@navbarHeight + @viewerToolsHeight);
right : 24px; right : 24px;
z-index : 10001; z-index : 10001;
width : 450px; width : 450px;
margin-top : 5px;
} }
.notificationPopup { .notificationPopup {

View File

@@ -0,0 +1,119 @@
require('./toolBar.less');
const React = require('react');
const { useState, useEffect } = React;
const _ = require('lodash');
const MAX_ZOOM = 300;
const MIN_ZOOM = 10;
const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
const [zoomLevel, setZoomLevel] = useState(100);
const [pageNum, setPageNum] = useState(currentPage);
useEffect(()=>{
onZoomChange(zoomLevel);
}, [zoomLevel]);
useEffect(()=>{
setPageNum(currentPage);
}, [currentPage]);
const handleZoomButton = (delta)=>{
setZoomLevel(_.clamp(zoomLevel + delta, MIN_ZOOM, MAX_ZOOM));
};
const handlePageInput = (pageInput)=>{
if(/[0-9]/.test(pageInput))
setPageNum(parseInt(pageInput)); // input type is 'text', so `page` comes in as a string, not number.
};
const scrollToPage = (pageNumber)=>{
pageNumber = _.clamp(pageNumber, 1, totalPages);
const iframe = document.getElementById('BrewRenderer');
const brewRenderer = iframe?.contentWindow?.document.querySelector('.brewRenderer');
const page = brewRenderer?.querySelector(`#p${pageNumber}`);
page?.scrollIntoView({ block: 'start' });
setPageNum(pageNumber);
};
return (
<div className='toolBar'>
{/*v=====----------------------< Zoom Controls >---------------------=====v*/}
<div className='group'>
<button
id='zoom-out'
className='tool'
onClick={()=>handleZoomButton(-20)}
disabled={zoomLevel <= MIN_ZOOM}
>
<i className='fas fa-magnifying-glass-minus' />
</button>
<input
id='zoom-slider'
className='range-input tool'
type='range'
name='zoom'
list='zoomLevels'
min={MIN_ZOOM}
max={MAX_ZOOM}
step='1'
value={zoomLevel}
onChange={(e)=>setZoomLevel(parseInt(e.target.value))}
/>
<datalist id='zoomLevels'>
<option value='100' />
</datalist>
<button
id='zoom-in'
className='tool'
onClick={()=>handleZoomButton(20)}
disabled={zoomLevel >= MAX_ZOOM}
>
<i className='fas fa-magnifying-glass-plus' />
</button>
</div>
{/*v=====----------------------< Page Controls >---------------------=====v*/}
<div className='group'>
<button
id='previous-page'
className='previousPage tool'
onClick={()=>scrollToPage(pageNum - 1)}
disabled={pageNum <= 1}
>
<i className='fas fa-arrow-left'></i>
</button>
<div className='tool'>
<input
id='page-input'
className='text-input'
type='text'
name='page'
inputMode='numeric'
pattern='[0-9]'
value={pageNum}
onClick={(e)=>e.target.select()}
onChange={(e)=>handlePageInput(e.target.value)}
onBlur={()=>scrollToPage(pageNum)}
onKeyDown={(e)=>e.key == 'Enter' && scrollToPage(pageNum)}
/>
<span id='page-count'>/ {totalPages}</span>
</div>
<button
id='next-page'
className='tool'
onClick={()=>scrollToPage(pageNum + 1)}
disabled={pageNum >= totalPages}
>
<i className='fas fa-arrow-right'></i>
</button>
</div>
</div>
);
};
module.exports = ToolBar;

View File

@@ -0,0 +1,98 @@
.toolBar {
position : absolute;
z-index : 1;
box-sizing : border-box;
display : flex;
flex-wrap : wrap;
gap : 8px 30px;
align-items : center;
justify-content : center;
width : 100%;
height : auto;
padding : 2px 0;
font-family : 'Open Sans', sans-serif;
color : #CCCCCC;
background-color : #555555;
.group {
box-sizing : border-box;
display : flex;
gap : 0 3px;
align-items : center;
justify-content : center;
height : 28px;
}
.tool {
display : flex;
align-items : center;
}
input {
position : relative;
height : 1.5em;
padding : 2px 5px;
font-family : 'Open Sans', sans-serif;
color : #000000;
background : #EEEEEE;
border : 1px solid gray;
&:focus { outline : 1px solid #D3D3D3; }
// `.range-input` if generic to all range inputs, or `#zoom-slider` if only for zoom slider
&.range-input {
padding : 2px 0;
color : #D3D3D3;
accent-color : #D3D3D3;
&::-webkit-slider-thumb, &::-moz-slider-thumb {
width : 5px;
height : 5px;
cursor : pointer;
outline : none;
}
&:hover::after {
position : absolute;
bottom : -30px;
left : 50%;
z-index : 1;
display : grid;
place-items : center;
width : 4ch;
height : 1.2lh;
pointer-events : none;
content : attr(value);
background-color : #555555;
border : 1px solid #A1A1A1;
transform : translate(-50%, 50%);
}
}
// `.text-input` if generic to all range inputs, or `#page-input` if only for current page input
&#page-input {
width : 4ch;
margin-right : 1ch;
text-align : center;
}
}
button {
box-sizing : content-box;
display : flex;
align-items : center;
justify-content : center;
width : auto;
min-width : 46px;
height : 100%;
padding : 0 0px;
font-weight : unset;
color : inherit;
background-color : unset;
&:hover { background-color : #444444; }
&:focus { outline : 1px solid #D3D3D3; }
&:disabled {
color : #777777;
background-color : unset !important;
}
}
}

View File

@@ -86,7 +86,6 @@ const Editor = createClass({
handleControlKeys : function(e){ handleControlKeys : function(e){
if(!(e.ctrlKey || e.metaKey)) return; if(!(e.ctrlKey || e.metaKey)) return;
console.log(e);
const LEFTARROW_KEY = 37; const LEFTARROW_KEY = 37;
const RIGHTARROW_KEY = 39; const RIGHTARROW_KEY = 39;
if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump(); if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump();

View File

@@ -1,6 +1,7 @@
@import 'naturalcrit/styles/colors.less'; @import 'naturalcrit/styles/colors.less';
@navbarHeight : 28px; @navbarHeight : 28px;
@viewerToolsHeight : 32px;
@keyframes pinkColoring { @keyframes pinkColoring {
0% { color : pink; } 0% { color : pink; }