Fix reader width (#567)

* Fix reader width

The margin: auto on the Box container for the image was preventing 100% width
to actually mean 100%.

Now that 100% is actually possible, I think fitPageToWindow makes more
sense as a default. Additionally, since the image can fill 100% of the
page, it can cover the ReaderNavBar, so set the z-index of the
ReaderNavBar so it's rendered on top of the image and clickable.

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

* Support configuring reader width for DoublePage readers

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>

* Fix single page of DoublePageReader not being able to take up full width

In case the parent container is flex row, the container does not automatically take up 100% of the available width, thus, the page also was not able to take up 100% of the width

* Fix applying reader width to double pages

The set reader width can't be applied to each page of the double pages because otherwise it will already take up 100% of the available width with the setting only being at 50%.

Instead, the set width has to be divided by 2, so that both pages take up the set reader width

* Prevent double page spinner from being larger than 100% of the available width

* Update width styling of the page spinner

* Always center pages in the middle of the screen

* Take up full height fitting page to window height

---------

Signed-off-by: Chance Zibolski <chance.zibolski@gmail.com>
Co-authored-by: schroda <50052685+schroda@users.noreply.github.com>
This commit is contained in:
Chance Zibolski
2024-01-26 11:51:08 -08:00
committed by GitHub
parent e0c5e0521d
commit 4d75474b39
10 changed files with 63 additions and 57 deletions

View File

@@ -26,7 +26,11 @@ import { useTranslation } from 'react-i18next';
import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, TChapter, TManga } from '@/typings'; import { AllowedMetadataValueTypes, ChapterOffset, IReaderSettings, TChapter, TManga } from '@/typings';
import { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions'; import { ReaderSettingsOptions } from '@/components/reader/ReaderSettingsOptions';
const Root = styled('div')(({ theme }) => ({ const Root = styled('div')({
zIndex: 10,
});
const NavContainer = styled('div')(({ theme }) => ({
top: 0, top: 0,
left: 0, left: 0,
width: '300px', width: '300px',
@@ -198,9 +202,9 @@ export function ReaderNavBar(props: IProps) {
}; };
return ( return (
<> <Root>
<Slide direction="right" in={drawerOpen} timeout={200} appear={false} mountOnEnter unmountOnExit> <Slide direction="right" in={drawerOpen} timeout={200} appear={false} mountOnEnter unmountOnExit>
<Root <NavContainer
sx={{ sx={{
position: 'fixed', position: 'fixed',
}} }}
@@ -374,7 +378,7 @@ export function ReaderNavBar(props: IProps) {
</Tooltip> </Tooltip>
</ChapterNavigation> </ChapterNavigation>
</Navigation> </Navigation>
</Root> </NavContainer>
</Slide> </Slide>
<Zoom in={!drawerOpen}> <Zoom in={!drawerOpen}>
<Fade in={!hideOpenButton}> <Fade in={!hideOpenButton}>
@@ -392,6 +396,6 @@ export function ReaderNavBar(props: IProps) {
</Tooltip> </Tooltip>
</Fade> </Fade>
</Zoom> </Zoom>
</> </Root>
); );
} }

View File

@@ -6,26 +6,11 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
import { CSSProperties, forwardRef, useRef } from 'react'; import { forwardRef, useRef } from 'react';
import { Box, SxProps, Theme } from '@mui/material'; import { Box, SxProps, Theme } from '@mui/material';
import { IReaderSettings } from '@/typings'; import { IReaderSettings } from '@/typings';
import { SpinnerImage } from '@/components/util/SpinnerImage'; import { SpinnerImage } from '@/components/util/SpinnerImage';
import { imageStyle } from '@/components/reader/Page';
const imgStyles: CSSProperties = {
display: 'block',
marginBottom: 0,
width: 'auto',
minHeight: '99vh',
height: 'auto',
maxHeight: '99vh',
objectFit: 'contain',
};
const spinnerStyle: SxProps<Theme> = {
...imgStyles,
width: 'calc((100vw - 300px) * 0.5)',
backgroundColor: '#525252',
};
interface IProps { interface IProps {
index: number; index: number;
@@ -39,18 +24,26 @@ export const DoublePage = forwardRef((props: IProps, ref: any) => {
const { image1src, image2src, index, onImageLoad, settings } = props; const { image1src, image2src, index, onImageLoad, settings } = props;
const imgRef = useRef<HTMLImageElement>(null); const imgRef = useRef<HTMLImageElement>(null);
const baseImgStyle = imageStyle(settings);
const imgStyle = {
...baseImgStyle,
width: settings.fitPageToWindow ? baseImgStyle.width : `calc(${baseImgStyle.width} * 0.5)`,
};
const spinnerStyle: SxProps<Theme> = {
...imgStyle,
height: '100vh',
width: '50%',
backgroundColor: '#525252',
};
return ( return (
<Box <Box
ref={ref} ref={ref}
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: settings.readerType === 'DoubleLTR' ? 'row' : 'row-reverse',
justifyContent: 'center', justifyContent: 'center',
margin: '0 auto', width: '100%',
width: 'auto',
height: 'auto',
overflowX: 'scroll',
}} }}
> >
<SpinnerImage <SpinnerImage
@@ -59,7 +52,7 @@ export const DoublePage = forwardRef((props: IProps, ref: any) => {
alt={`Page #${index}`} alt={`Page #${index}`}
imgRef={imgRef} imgRef={imgRef}
spinnerStyle={spinnerStyle} spinnerStyle={spinnerStyle}
imgStyle={imgStyles} imgStyle={imgStyle}
/> />
<SpinnerImage <SpinnerImage
src={image2src} src={image2src}
@@ -68,10 +61,10 @@ export const DoublePage = forwardRef((props: IProps, ref: any) => {
imgRef={imgRef} imgRef={imgRef}
spinnerStyle={{ spinnerStyle={{
...spinnerStyle, ...spinnerStyle,
width: 'calc((100vw - 300px - 5px) * 0.5)', width: 'calc(50% - 5px)',
marginLeft: '5px', marginLeft: '5px',
}} }}
imgStyle={imgStyles} imgStyle={imgStyle}
/> />
</Box> </Box>
); );

View File

@@ -8,13 +8,15 @@
import { useState, useEffect, forwardRef, useRef } from 'react'; import { useState, useEffect, forwardRef, useRef } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { useTheme } from '@mui/material/styles';
import { useMediaQuery } from '@mui/material';
import { IReaderSettings, ReaderType } from '@/typings'; import { IReaderSettings, ReaderType } from '@/typings';
import { SpinnerImage } from '@/components/util/SpinnerImage'; import { SpinnerImage } from '@/components/util/SpinnerImage';
export const isFillsPageReaderType = (readerType: ReaderType): boolean => export const isHorizontalReaderType = (readerType: ReaderType): boolean =>
['DoubleRTL', 'DoubleLTR', 'ContinuesHorizontalLTR', 'ContinuesHorizontalRTL'].includes(readerType); ['ContinuesHorizontalLTR', 'ContinuesHorizontalRTL'].includes(readerType);
function imageStyle(settings: IReaderSettings): any { export function imageStyle(settings: IReaderSettings): any {
const [dimensions, setDimensions] = useState({ const [dimensions, setDimensions] = useState({
height: window.innerHeight, height: window.innerHeight,
width: window.innerWidth, width: window.innerWidth,
@@ -32,27 +34,26 @@ function imageStyle(settings: IReaderSettings): any {
window.removeEventListener('resize', handleResize); window.removeEventListener('resize', handleResize);
}; };
}, []); }, []);
if (settings.fitPageToWindow || isFillsPageReaderType(settings.readerType)) {
const isHorizontal = isHorizontalReaderType(settings.readerType);
if (settings.fitPageToWindow || isHorizontal) {
return { return {
display: 'block', marginLeft: isHorizontal ? '7px' : 0,
marginLeft: '7px', marginRight: isHorizontal ? '7px' : 0,
marginRight: '7px',
width: 'auto', width: 'auto',
minHeight: '99vh', minHeight: '100vh',
height: 'auto', height: 'auto',
maxHeight: '99vh', maxHeight: '100vh',
objectFit: 'contain', objectFit: 'contain',
}; };
} }
return { return {
display: 'block',
marginBottom: settings.readerType === 'ContinuesVertical' ? '15px' : 0, marginBottom: settings.readerType === 'ContinuesVertical' ? '15px' : 0,
minWidth: '10vw', minWidth: '10vw',
width: dimensions.width < dimensions.height ? '100vw' : `${settings.readerWidth}%`, width: dimensions.width < dimensions.height ? '100vw' : `${settings.readerWidth}%`,
maxWidth: '100%', maxWidth: '100%',
marginLeft: 'auto', objectFit: 'contain',
marginRight: 'auto',
}; };
} }
@@ -66,12 +67,19 @@ interface IProps {
export const Page = forwardRef((props: IProps, ref: any) => { export const Page = forwardRef((props: IProps, ref: any) => {
const { src, index, onImageLoad, settings } = props; const { src, index, onImageLoad, settings } = props;
const theme = useTheme();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('md'));
const imgRef = useRef<HTMLImageElement>(null); const imgRef = useRef<HTMLImageElement>(null);
const imgStyle = imageStyle(settings); const imgStyle = imageStyle(settings);
const isDoublePageReader = ['DoubleRTL', 'DoubleLTR'].includes(settings.readerType);
return ( return (
<Box ref={ref} sx={{ margin: 'auto' }}> <Box
ref={ref}
sx={{ display: 'flex', justifyContent: 'center', minWidth: isDoublePageReader ? '100%' : undefined }}
>
<SpinnerImage <SpinnerImage
src={src} src={src}
onImageLoad={onImageLoad} onImageLoad={onImageLoad}
@@ -80,7 +88,7 @@ export const Page = forwardRef((props: IProps, ref: any) => {
spinnerStyle={{ spinnerStyle={{
...imgStyle, ...imgStyle,
height: '100vh', height: '100vh',
width: '70vw', width: isMobileWidth ? '100vw' : 'calc(100% * 0.5)',
backgroundColor: '#525252', backgroundColor: '#525252',
}} }}
imgStyle={imgStyle} imgStyle={imgStyle}

View File

@@ -12,7 +12,7 @@ import MenuItem from '@mui/material/MenuItem';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AllowedMetadataValueTypes, IReaderSettings } from '@/typings'; import { AllowedMetadataValueTypes, IReaderSettings } from '@/typings';
import { NumberSetting } from '@/components/settings/NumberSetting.tsx'; import { NumberSetting } from '@/components/settings/NumberSetting.tsx';
import { isFillsPageReaderType } from '@/components/reader/Page.tsx'; import { isHorizontalReaderType } from '@/components/reader/Page.tsx';
interface IProps extends IReaderSettings { interface IProps extends IReaderSettings {
setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => void; setSettingValue: (key: keyof IReaderSettings, value: AllowedMetadataValueTypes) => void;
@@ -30,7 +30,7 @@ export function ReaderSettingsOptions({
readerWidth, readerWidth,
}: IProps) { }: IProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const fitPageToWindowEligible = !isFillsPageReaderType(readerType); const fitPageToWindowEligible = !isHorizontalReaderType(readerType);
return ( return (
<List> <List>
<ListItem> <ListItem>

View File

@@ -175,10 +175,8 @@ export function DoublePagedPager(props: IReaderProps) {
display: 'flex', display: 'flex',
flexDirection: settings.readerType === 'DoubleLTR' ? 'row' : 'row-reverse', flexDirection: settings.readerType === 'DoubleLTR' ? 'row' : 'row-reverse',
justifyContent: 'center', justifyContent: 'center',
margin: '0 auto',
width: 'auto', width: 'auto',
height: 'auto', height: 'auto',
overflowX: 'scroll',
}} }}
> >
{getPagesToDisplay() === 2 ? ( {getPagesToDisplay() === 2 ? (

View File

@@ -166,7 +166,6 @@ export function HorizontalPager(props: IReaderProps) {
display: 'flex', display: 'flex',
flexDirection: settings.readerType === 'ContinuesHorizontalLTR' ? 'row' : 'row-reverse', flexDirection: settings.readerType === 'ContinuesHorizontalLTR' ? 'row' : 'row-reverse',
justifyContent: settings.readerType === 'ContinuesHorizontalLTR' ? 'flex-start' : 'flex-end', justifyContent: settings.readerType === 'ContinuesHorizontalLTR' ? 'flex-start' : 'flex-end',
margin: '0 auto',
width: 'auto', width: 'auto',
height: 'auto', height: 'auto',
overflowX: 'visible', overflowX: 'visible',

View File

@@ -99,11 +99,10 @@ export function PagedPager(props: IReaderProps) {
ref={selfRef} ref={selfRef}
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
margin: '0 auto', width: 'auto',
width: '100%', height: 'auto',
height: '100vh',
}} }}
onClick={clickControl} onClick={clickControl}
> >

View File

@@ -173,8 +173,8 @@ export function VerticalPager(props: IReaderProps) {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
margin: '0 auto', width: 'auto',
width: '100%', height: 'auto',
userSelect: 'none', userSelect: 'none',
}} }}
onClick={(e) => { onClick={(e) => {

View File

@@ -400,7 +400,12 @@ export function Reader() {
return ( return (
<Box <Box
sx={{ sx={{
width: settings.staticNav ? 'calc(100vw - 300px)' : '100vw', display: 'flex',
flexDirection: 'column',
alignContent: 'center',
justifyContent: 'center',
minWidth: settings.staticNav ? 'calc((100vw - (100vw - 100%)) - 300px)' : '100vw - (100vw - 100%)', // 100vw = width excluding scrollbar; 100% = width including scrollbar
minHeight: '100vh',
marginLeft: settings.staticNav ? '300px' : 'unset', marginLeft: settings.staticNav ? '300px' : 'unset',
}} }}
> >

View File

@@ -25,7 +25,7 @@ export const getDefaultSettings = (): IReaderSettings => ({
showPageNumber: true, showPageNumber: true,
loadNextOnEnding: false, loadNextOnEnding: false,
skipDupChapters: true, skipDupChapters: true,
fitPageToWindow: false, fitPageToWindow: true,
readerType: 'ContinuesVertical', readerType: 'ContinuesVertical',
offsetFirstPage: false, offsetFirstPage: false,
readerWidth: 100, readerWidth: 100,