mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-29 04:42:41 +00:00
New icons for Zoom to Fit, and Fit Width buttons. Used SVGR online tool to create react components and then combined them. If doing in future, be sure to set currentColor on `fill` property in the SVG itself. Make the SVGs as closed curves only (don't rely on stroke). set only a width property, not height.
184 lines
4.7 KiB
JavaScript
184 lines
4.7 KiB
JavaScript
require('./toolBar.less');
|
|
const React = require('react');
|
|
const { useState, useEffect } = React;
|
|
const _ = require('lodash')
|
|
|
|
import * as ZoomIcons from '../../../icons/icon-components/zoomIcons.jsx';
|
|
|
|
const MAX_ZOOM = 300;
|
|
const MIN_ZOOM = 10;
|
|
|
|
const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
|
|
|
|
const [zoomLevel, setZoomLevel] = useState(100);
|
|
const [pageInput, setPageInput] = useState(currentPage);
|
|
|
|
useEffect(()=>{
|
|
onZoomChange(zoomLevel);
|
|
}, [zoomLevel]);
|
|
|
|
useEffect(()=>{
|
|
setPageInput(currentPage);
|
|
}, [currentPage])
|
|
|
|
const handleZoomChange = (delta)=>{
|
|
const zoomChange = _.clamp(zoomLevel + delta, MIN_ZOOM, MAX_ZOOM);
|
|
|
|
setZoomLevel(zoomChange);
|
|
};
|
|
|
|
const handlePageChange = (page)=>{
|
|
setPageInput(page);
|
|
};
|
|
|
|
const scrollToPage = (pageNumber) => {
|
|
pageNumber = _.clamp(pageNumber - 1, 0, totalPages - 1);
|
|
const iframe = document.getElementById('BrewRenderer');
|
|
if(iframe && iframe.contentWindow) {
|
|
const brewRenderer = iframe.contentWindow.document.querySelector('.brewRenderer');
|
|
if(brewRenderer) {
|
|
const pages = brewRenderer.querySelectorAll('.page');
|
|
pages[pageNumber]?.scrollIntoView({ block: 'start' });
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
const calculateZoom = (mode)=>{
|
|
const iframe = document.getElementById('BrewRenderer');
|
|
const iframeWidth = iframe.getBoundingClientRect().width;
|
|
const iframeHeight = iframe.getBoundingClientRect().height;
|
|
const pages = iframe.contentWindow.document.getElementsByClassName('page');
|
|
|
|
let desiredZoom = 0;
|
|
|
|
if(mode == 'fill'){
|
|
// find widest page, in case pages are different widths, so that the zoom is adapted to not cut the widest page off screen.
|
|
let widestPage = 0;
|
|
[...pages].forEach((page)=>{
|
|
const width = page.offsetWidth;
|
|
if(width > widestPage)
|
|
widestPage = width;
|
|
});
|
|
|
|
desiredZoom = (iframeWidth / widestPage) * 100;
|
|
|
|
} else if(mode == 'fit'){
|
|
// find the page with the largest single dim (height or width) so that zoom can be adapted to fit it.
|
|
let maxDimRatio = 0;
|
|
|
|
[...pages].forEach((page)=>{
|
|
const widthRatio = iframeWidth / page.offsetWidth;
|
|
const heightRatio = iframeHeight / page.offsetHeight;
|
|
const dimensionRatio = Math.min(widthRatio, heightRatio);
|
|
if(dimensionRatio < maxDimRatio || maxDimRatio == 0){
|
|
maxDimRatio = dimensionRatio;
|
|
}
|
|
});
|
|
|
|
desiredZoom = maxDimRatio * 100;
|
|
|
|
}
|
|
|
|
const margin = 5; // extra space so page isn't edge to edge (not truly "to fill")
|
|
|
|
const deltaZoom = (desiredZoom - zoomLevel) - margin;
|
|
return deltaZoom;
|
|
};
|
|
|
|
return (
|
|
<div className='toolBar'>
|
|
<div className='group'>
|
|
<button
|
|
id='zoom-to-fill'
|
|
className='tool'
|
|
onClick={()=>handleZoomChange(calculateZoom('fill'))}
|
|
>
|
|
<ZoomIcons.FitWidth title='Fit to Width' style={{ width: '1.5em' }} />
|
|
</button>
|
|
<button
|
|
id='zoom-to-fit'
|
|
className='tool'
|
|
onClick={()=>handleZoomChange(calculateZoom('fit'))}
|
|
>
|
|
<ZoomIcons.FitAll title='Zoom to Fit' style={{ width: '1.5em' }} />
|
|
</button>
|
|
<button
|
|
id='zoom-out'
|
|
className='tool'
|
|
onClick={()=>handleZoomChange(-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={()=>handleZoomChange(20)}
|
|
disabled={zoomLevel >= MAX_ZOOM}
|
|
>
|
|
<i className='fas fa-magnifying-glass-plus' />
|
|
</button>
|
|
</div>
|
|
|
|
<div className='group'>
|
|
<button
|
|
id='previous-page'
|
|
className='previousPage tool'
|
|
onClick={()=>scrollToPage(pageInput - 1)}
|
|
disabled={pageInput <= 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={pageInput}
|
|
onChange={(e)=>{
|
|
handlePageChange(e.target.value == false ? e.target.value : parseInt(e.target.value));}}
|
|
onBlur={()=>scrollToPage(pageInput)}
|
|
onKeyDown={(e)=>{e.key == 'Enter' ? scrollToPage(pageInput) : null;}}
|
|
/>
|
|
|
|
<span id='page-count'>/ {totalPages}</span>
|
|
</div>
|
|
|
|
|
|
<button
|
|
id='next-page'
|
|
className='tool'
|
|
onClick={()=>scrollToPage(pageInput + 1)}
|
|
disabled={pageInput >= totalPages}
|
|
>
|
|
<i className='fas fa-arrow-right'></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
);
|
|
};
|
|
|
|
module.exports = ToolBar;
|