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:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 ? (
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user