Convert reader image styling to vw/vh

Preparation for infinite scroll, since there will be a wrapper which fits its content in width and height, thus, percentage based dimension won't work anymore
This commit is contained in:
schroda
2025-01-19 20:19:45 +01:00
parent da600bcdec
commit 7fad96dab7
4 changed files with 69 additions and 76 deletions

View File

@@ -24,6 +24,8 @@ import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx'; import { useReaderScrollbarContext } from '@/modules/reader/contexts/ReaderScrollbarContext.tsx';
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx'; import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx'; import { withPropsFrom } from '@/modules/core/hoc/withPropsFrom.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { NavbarContextType } from '@/modules/navigation-bar/NavigationBar.types.ts';
const getCustomFilterString = (customFilter: ReaderCustomFilter): string => const getCustomFilterString = (customFilter: ReaderCustomFilter): string =>
Object.keys(customFilter) Object.keys(customFilter)
@@ -73,10 +75,12 @@ const BaseReaderPage = ({
onLoad, onLoad,
onError, onError,
setRef, setRef,
readerNavBarWidth,
...props ...props
}: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle' | 'onLoad' | 'onError'> & }: Omit<ComponentProps<typeof SpinnerImage>, 'ref' | 'spinnerStyle' | 'imgStyle' | 'onLoad' | 'onError'> &
Pick<IReaderSettings, 'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth'> & Pick<IReaderSettings, 'readingMode' | 'customFilter' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth'> &
Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> & { Pick<TReaderScrollbarContext, 'scrollbarXSize' | 'scrollbarYSize'> &
Pick<NavbarContextType, 'readerNavBarWidth'> & {
pageIndex: number; pageIndex: number;
pagesIndex: number; pagesIndex: number;
isPrimaryPage: boolean; isPrimaryPage: boolean;
@@ -129,7 +133,15 @@ const BaseReaderPage = ({
...getImageMarginStyling(doublePage, position), ...getImageMarginStyling(doublePage, position),
}} }}
imgStyle={{ imgStyle={{
...getReaderImageStyling(readingMode, shouldStretchPage, pageScaleMode, doublePage, readerWidth), ...getReaderImageStyling(
readingMode,
shouldStretchPage,
pageScaleMode,
doublePage,
readerWidth,
readerNavBarWidth + scrollbarYSize,
scrollbarXSize,
),
display: 'block', display: 'block',
...applyStyles(!display, { ...applyStyles(!display, {
display: 'none', display: 'none',
@@ -156,7 +168,7 @@ const BaseReaderPage = ({
export const ReaderPage = withPropsFrom( export const ReaderPage = withPropsFrom(
memo(BaseReaderPage), memo(BaseReaderPage),
[ReaderService.useSettingsWithoutDefaultFlag, useReaderScrollbarContext], [ReaderService.useSettingsWithoutDefaultFlag, useReaderScrollbarContext, useNavBarContext],
[ [
'readingMode', 'readingMode',
'customFilter', 'customFilter',
@@ -165,5 +177,6 @@ export const ReaderPage = withPropsFrom(
'readerWidth', 'readerWidth',
'scrollbarXSize', 'scrollbarXSize',
'scrollbarYSize', 'scrollbarYSize',
'readerNavBarWidth',
], ],
); );

View File

@@ -9,7 +9,7 @@
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'; import { memo, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
import Box, { BoxProps } from '@mui/material/Box'; import Box, { BoxProps } from '@mui/material/Box';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { getPageIndexesToLoad, getReaderImageWrapperStyling } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { getPageIndexesToLoad } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts'; import { ReaderStatePages } from '@/modules/reader/types/ReaderProgressBar.types.ts';
import { IReaderSettings, ReaderPagerProps, ReaderTransitionPageMode } from '@/modules/reader/types/Reader.types.ts'; import { IReaderSettings, ReaderPagerProps, ReaderTransitionPageMode } from '@/modules/reader/types/Reader.types.ts';
import { ReaderTransitionPage } from '@/modules/reader/components/viewer/ReaderTransitionPage.tsx'; import { ReaderTransitionPage } from '@/modules/reader/components/viewer/ReaderTransitionPage.tsx';
@@ -23,15 +23,9 @@ const BaseBasePager = ({
createPage, createPage,
slots, slots,
readingMode, readingMode,
pageScaleMode,
shouldStretchPage,
readerWidth,
imagePreLoadAmount, imagePreLoadAmount,
}: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> & }: Omit<ReaderPagerProps, 'pageLoadStates' | 'retryFailedPagesKeyPrefix'> &
Pick< Pick<IReaderSettings, 'readingMode' | 'imagePreLoadAmount'> & {
IReaderSettings,
'readingMode' | 'pageScaleMode' | 'shouldStretchPage' | 'readerWidth' | 'imagePreLoadAmount'
> & {
createPage: ( createPage: (
page: ReaderStatePages['pages'][number], page: ReaderStatePages['pages'][number],
pagesIndex: number, pagesIndex: number,
@@ -68,10 +62,7 @@ const BaseBasePager = ({
return ( return (
<Box <Box
{...slots?.boxProps} {...slots?.boxProps}
sx={[ sx={[...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx])]}
...(Array.isArray(slots?.boxProps?.sx) ? (slots?.boxProps?.sx ?? []) : [slots?.boxProps?.sx]),
getReaderImageWrapperStyling(readingMode, shouldStretchPage, pageScaleMode, readerWidth),
]}
> >
<ReaderTransitionPage type={ReaderTransitionPageMode.PREVIOUS} /> <ReaderTransitionPage type={ReaderTransitionPageMode.PREVIOUS} />
{pages.map((page, pagesIndex) => {pages.map((page, pagesIndex) =>
@@ -91,5 +82,5 @@ const BaseBasePager = ({
export const BasePager = withPropsFrom( export const BasePager = withPropsFrom(
memo(BaseBasePager), memo(BaseBasePager),
[ReaderService.useSettingsWithoutDefaultFlag], [ReaderService.useSettingsWithoutDefaultFlag],
['readingMode', 'pageScaleMode', 'shouldStretchPage', 'readerWidth', 'imagePreLoadAmount'], ['readingMode', 'imagePreLoadAmount'],
); );

View File

@@ -10,7 +10,12 @@ import { Direction, useTheme } from '@mui/material/styles';
import { Fragment, memo, useMemo } from 'react'; import { Fragment, memo, useMemo } from 'react';
import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx'; import { BasePager } from '@/modules/reader/components/viewer/pager/BasePager.tsx';
import { ReaderService } from '@/modules/reader/services/ReaderService.ts'; import { ReaderService } from '@/modules/reader/services/ReaderService.ts';
import { IReaderSettings, ReaderPagerProps, ReadingDirection } from '@/modules/reader/types/Reader.types.ts'; import {
IReaderSettings,
ReaderPagerProps,
ReaderPageScaleMode,
ReadingDirection,
} from '@/modules/reader/types/Reader.types.ts';
import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts'; import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx'; import { createReaderPage } from '@/modules/reader/utils/ReaderPager.utils.tsx';
import { getNextIndexFromPage, getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx'; import { getNextIndexFromPage, getPage } from '@/modules/reader/utils/ReaderProgressBar.utils.tsx';
@@ -44,8 +49,9 @@ const BaseReaderDoublePagedPager = ({
pageLoadStates, pageLoadStates,
retryFailedPagesKeyPrefix, retryFailedPagesKeyPrefix,
readingDirection, readingDirection,
pageScaleMode,
...props ...props
}: ReaderPagerProps & Pick<IReaderSettings, 'readingDirection'>) => { }: ReaderPagerProps & Pick<IReaderSettings, 'readingDirection' | 'pageScaleMode'>) => {
const { currentPageIndex, pages, totalPages } = props; const { currentPageIndex, pages, totalPages } = props;
const { direction: themeDirection } = useTheme(); const { direction: themeDirection } = useTheme();
@@ -102,7 +108,13 @@ const BaseReaderDoublePagedPager = ({
slots={{ slots={{
boxProps: { boxProps: {
sx: { sx: {
margin: 'auto', ...applyStyles(pageScaleMode === ReaderPageScaleMode.ORIGINAL, {
margin: 'auto',
}),
width: '100%',
minWidth: 'fit-content',
height: '100%',
minHeight: 'fit-content',
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'nowrap', flexWrap: 'nowrap',
@@ -122,5 +134,5 @@ const BaseReaderDoublePagedPager = ({
export const ReaderDoublePagedPager = withPropsFrom( export const ReaderDoublePagedPager = withPropsFrom(
memo(BaseReaderDoublePagedPager), memo(BaseReaderDoublePagedPager),
[ReaderService.useSettingsWithoutDefaultFlag], [ReaderService.useSettingsWithoutDefaultFlag],
['readingDirection'], ['readingDirection', 'pageScaleMode'],
); );

View File

@@ -38,20 +38,22 @@ const getPageWidth = (
pageScaleMode: IReaderSettings['pageScaleMode'], pageScaleMode: IReaderSettings['pageScaleMode'],
isDoublePage: boolean, isDoublePage: boolean,
readerWidth: IReaderSettings['readerWidth'], readerWidth: IReaderSettings['readerWidth'],
shouldStretchPage: IReaderSettings['shouldStretchPage'],
isImage: boolean, isImage: boolean,
): string => { ): string => {
// only return 50% in case pages should get stretched, otherwise, pages might unintentionally shrink in size (e.g. 2 pages with different dimensions, the bigger page will shrink due to taking up more than 50%) if (shouldApplyReaderWidth(readerWidth, pageScaleMode)) {
if (isImage && isDoublePage && shouldStretchPage) { const width = readerWidth.value;
return '50%'; if (isImage && isDoublePage) {
return `${width / 2}vw`;
}
return `${width}vw`;
} }
// the image wrapper gets applied the reader width limit and therefore the images can take up 100% if (isImage && isDoublePage) {
if (!isImage && shouldApplyReaderWidth(readerWidth, pageScaleMode)) { return `50vw`;
return `${readerWidth?.value}%`;
} }
return '100%'; return `100vw`;
}; };
export const getImagePlaceholderStyling = ( export const getImagePlaceholderStyling = (
@@ -142,7 +144,13 @@ const getReaderDimensionStyling = (
readingMode: IReaderSettings['readingMode'], readingMode: IReaderSettings['readingMode'],
shouldStretchPage: IReaderSettings['shouldStretchPage'], shouldStretchPage: IReaderSettings['shouldStretchPage'],
pageScaleMode: IReaderSettings['pageScaleMode'], pageScaleMode: IReaderSettings['pageScaleMode'],
widthOffset: number,
heightOffset: number,
): CSSObject => { ): CSSObject => {
const finalWidthOffset = readingMode === ReadingMode.DOUBLE_PAGE ? widthOffset / 2 : widthOffset;
const fullWidth = `calc(${width} - ${finalWidthOffset}px)`;
const fullHeight = `calc(100vh - ${heightOffset}px)`;
switch (pageScaleMode) { switch (pageScaleMode) {
case ReaderPageScaleMode.WIDTH: case ReaderPageScaleMode.WIDTH:
return { return {
@@ -151,9 +159,9 @@ const getReaderDimensionStyling = (
minWidth: 'unset', minWidth: 'unset',
}), }),
...applyStyles(shouldStretchPage, { ...applyStyles(shouldStretchPage, {
minWidth: width, minWidth: fullWidth,
}), }),
maxWidth: width, maxWidth: fullWidth,
}; };
case ReaderPageScaleMode.HEIGHT: case ReaderPageScaleMode.HEIGHT:
return { return {
@@ -162,9 +170,9 @@ const getReaderDimensionStyling = (
minHeight: 'unset', minHeight: 'unset',
}), }),
...applyStyles(shouldStretchPage, { ...applyStyles(shouldStretchPage, {
minHeight: '100%', minHeight: fullHeight,
}), }),
maxHeight: `100%`, maxHeight: fullHeight,
}; };
case ReaderPageScaleMode.SCREEN: case ReaderPageScaleMode.SCREEN:
return { return {
@@ -175,19 +183,19 @@ const getReaderDimensionStyling = (
minHeight: 'unset', minHeight: 'unset',
}), }),
...applyStyles(shouldStretchPage, { ...applyStyles(shouldStretchPage, {
minWidth: width, minWidth: fullWidth,
minHeight: '100%', minHeight: fullHeight,
...applyStyles(readingMode === ReadingMode.CONTINUOUS_HORIZONTAL, { ...applyStyles(readingMode === ReadingMode.CONTINUOUS_HORIZONTAL, {
minWidth: 'unset', minWidth: 'unset',
minHeight: '100%', minHeight: fullHeight,
}), }),
...applyStyles(isContinuousVerticalReadingMode(readingMode), { ...applyStyles(isContinuousVerticalReadingMode(readingMode), {
minWidth: width, minWidth: fullWidth,
minHeight: 'unset', minHeight: 'unset',
}), }),
}), }),
maxWidth: width, maxWidth: fullWidth,
maxHeight: `100%`, maxHeight: fullHeight,
}; };
case ReaderPageScaleMode.ORIGINAL: case ReaderPageScaleMode.ORIGINAL:
return {}; return {};
@@ -196,56 +204,25 @@ const getReaderDimensionStyling = (
} }
}; };
export const getReaderImageWrapperStyling = (
readingMode: IReaderSettings['readingMode'],
shouldStretchPage: IReaderSettings['shouldStretchPage'],
pageScaleMode: IReaderSettings['pageScaleMode'],
readerWidth: IReaderSettings['readerWidth'],
): CSSObject => {
const width = getPageWidth(pageScaleMode, false, readerWidth, shouldStretchPage, false);
const readerImageStyling = getReaderDimensionStyling(width, readingMode, shouldStretchPage, pageScaleMode);
switch (pageScaleMode) {
case ReaderPageScaleMode.WIDTH:
case ReaderPageScaleMode.SCREEN:
return {
...readerImageStyling,
...applyStyles(shouldStretchPage, {
width: '100%',
}),
// setting the "width" of the wrapper is required for being able to properly size the image placeholders
...applyStyles(shouldApplyReaderWidth(readerWidth, pageScaleMode), {
width,
}),
};
case ReaderPageScaleMode.HEIGHT:
case ReaderPageScaleMode.ORIGINAL:
return {
...readerImageStyling,
};
default:
throw new Error(`Unexpected "PageScaleMode" (${pageScaleMode})`);
}
};
export const getReaderImageStyling = ( export const getReaderImageStyling = (
readingMode: IReaderSettings['readingMode'], readingMode: IReaderSettings['readingMode'],
shouldStretchPage: IReaderSettings['shouldStretchPage'], shouldStretchPage: IReaderSettings['shouldStretchPage'],
pageScaleMode: IReaderSettings['pageScaleMode'], pageScaleMode: IReaderSettings['pageScaleMode'],
isDoublePage: boolean, isDoublePage: boolean,
readerWidth: IReaderSettings['readerWidth'], readerWidth: IReaderSettings['readerWidth'],
widthOffset: number,
heightOffset: number,
): CSSObject => { ): CSSObject => {
const width = getPageWidth(pageScaleMode, isDoublePage, readerWidth, shouldStretchPage, true); const width = getPageWidth(pageScaleMode, isDoublePage, readerWidth, true);
return getReaderDimensionStyling(width, readingMode, shouldStretchPage, pageScaleMode); return getReaderDimensionStyling(width, readingMode, shouldStretchPage, pageScaleMode, widthOffset, heightOffset);
}; };
export const getImageMarginStyling = (doublePage: boolean, objectFitPosition?: 'left' | 'right'): CSSObject => ({ export const getImageMarginStyling = (doublePage: boolean, objectFitPosition?: 'left' | 'right'): CSSObject => ({
m: 'auto', m: 'auto',
...applyStyles(doublePage, { ...applyStyles(doublePage, {
m: 'unset', // the margin on the object fit position needs to be removed so that there is no space between both images
// the applied margin is the opposite of the objectFitPosition ...applyStyles(objectFitPosition === 'right', { mr: 'unset ' }),
...applyStyles(objectFitPosition === 'right', { ml: 'auto ' }), ...applyStyles(objectFitPosition === 'left', { ml: 'unset ' }),
...applyStyles(objectFitPosition === 'left', { mr: 'auto ' }),
}), }),
}); });