Fix/manga screen prevent fab from overlaying last chapter in list (#298)
* Introduce "StyledFAb" MUI "Fab" with the "default" styling applied * Prevent FAB from overlaying last chapter in list of Manga details page
This commit is contained in:
@@ -21,6 +21,7 @@ import ChaptersToolbarMenu from 'components/manga/ChaptersToolbarMenu';
|
|||||||
import SelectionFAB from 'components/manga/SelectionFAB';
|
import SelectionFAB from 'components/manga/SelectionFAB';
|
||||||
import { BatchChaptersChange, IChapter, IDownloadChapter, IQueue, TranslationKey } from 'typings';
|
import { BatchChaptersChange, IChapter, IDownloadChapter, IQueue, TranslationKey } from 'typings';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { DEFAULT_FULL_FAB_HEIGHT } from 'components/util/StyledFab';
|
||||||
|
|
||||||
const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
|
const StyledVirtuoso = styled(Virtuoso)(({ theme }) => ({
|
||||||
listStyle: 'none',
|
listStyle: 'none',
|
||||||
@@ -252,15 +253,29 @@ const ChapterList: React.FC<IProps> = ({ mangaId }) => {
|
|||||||
// 900 is the md breakpoint in MUI
|
// 900 is the md breakpoint in MUI
|
||||||
overflowY: window.innerWidth < 900 ? 'visible' : 'auto',
|
overflowY: window.innerWidth < 900 ? 'visible' : 'auto',
|
||||||
}}
|
}}
|
||||||
totalCount={visibleChapters.length}
|
totalCount={visibleChapters.length + 1}
|
||||||
itemContent={(index: number) => (
|
itemContent={(index: number) => {
|
||||||
<ChapterCard
|
// hacky way of adding padding to the bottom of the list, so the FAB doesn't overlay the last chapter
|
||||||
{...chaptersWithMeta[index]}
|
// since I was unable to find another solution on how to achieve this
|
||||||
showChapterNumber={options.showChapterNumber}
|
if (index === visibleChapters.length) {
|
||||||
triggerChaptersUpdate={() => mutate()}
|
return (
|
||||||
onSelect={() => handleSelection(index)}
|
<div
|
||||||
/>
|
style={{
|
||||||
)}
|
paddingBottom: DEFAULT_FULL_FAB_HEIGHT,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChapterCard
|
||||||
|
{...chaptersWithMeta[index]}
|
||||||
|
showChapterNumber={options.showChapterNumber}
|
||||||
|
triggerChaptersUpdate={() => mutate()}
|
||||||
|
onSelect={() => handleSelection(index)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
useWindowScroll={window.innerWidth < 900}
|
useWindowScroll={window.innerWidth < 900}
|
||||||
overscan={window.innerHeight * 0.5}
|
overscan={window.innerHeight * 0.5}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
* 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 React from 'react';
|
import React from 'react';
|
||||||
import { Fab } from '@mui/material';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { PlayArrow } from '@mui/icons-material';
|
import { PlayArrow } from '@mui/icons-material';
|
||||||
import { BACK } from 'util/useBackTo';
|
import { BACK } from 'util/useBackTo';
|
||||||
import { IChapter } from 'typings';
|
import { IChapter } from 'typings';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import StyledFab from 'components/util/StyledFab';
|
||||||
|
|
||||||
interface ResumeFABProps {
|
interface ResumeFABProps {
|
||||||
chapter: IChapter;
|
chapter: IChapter;
|
||||||
@@ -26,8 +26,7 @@ export default function ResumeFab(props: ResumeFABProps) {
|
|||||||
mangaId,
|
mangaId,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<Fab
|
<StyledFab
|
||||||
sx={{ position: 'fixed', bottom: '2em', right: '3em' }}
|
|
||||||
component={Link}
|
component={Link}
|
||||||
variant="extended"
|
variant="extended"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -38,6 +37,6 @@ export default function ResumeFab(props: ResumeFABProps) {
|
|||||||
>
|
>
|
||||||
<PlayArrow />
|
<PlayArrow />
|
||||||
{index === 1 ? t('global.button.start') : t('global.button.resume')}
|
{index === 1 ? t('global.button.start') : t('global.button.resume')}
|
||||||
</Fab>
|
</StyledFab>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import React, { useRef, useState } from 'react';
|
|||||||
import type { IChapterWithMeta } from 'components/manga/ChapterList';
|
import type { IChapterWithMeta } from 'components/manga/ChapterList';
|
||||||
import SelectionFABActionItem from 'components/manga/SelectionFABActionItem';
|
import SelectionFABActionItem from 'components/manga/SelectionFABActionItem';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { DEFAULT_FAB_STYLE } from 'components/util/StyledFab';
|
||||||
|
|
||||||
export type SelectionAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
export type SelectionAction = 'download' | 'delete' | 'bookmark' | 'unbookmark' | 'mark_as_read' | 'mark_as_unread';
|
||||||
|
|
||||||
@@ -38,9 +39,8 @@ const SelectionFAB: React.FC<SelectionFABProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'fixed',
|
...DEFAULT_FAB_STYLE,
|
||||||
bottom: '2em',
|
height: `calc(${DEFAULT_FAB_STYLE.height} + 1)`,
|
||||||
right: '3em',
|
|
||||||
pt: 1,
|
pt: 1,
|
||||||
}}
|
}}
|
||||||
ref={anchorEl}
|
ref={anchorEl}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||||
import { Button, Fab, Stack } from '@mui/material';
|
import { Button, Stack } from '@mui/material';
|
||||||
import { Box } from '@mui/system';
|
import { Box } from '@mui/system';
|
||||||
import OptionsPanel from 'components/molecules/OptionsPanel';
|
import OptionsPanel from 'components/molecules/OptionsPanel';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -23,6 +23,7 @@ import GroupFilter from 'components/source/filters/GroupFilter';
|
|||||||
import SeperatorFilter from 'components/source/filters/SeparatorFilter';
|
import SeperatorFilter from 'components/source/filters/SeparatorFilter';
|
||||||
import { ISourceFilters, IState } from 'typings';
|
import { ISourceFilters, IState } from 'typings';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import StyledFab from 'components/util/StyledFab';
|
||||||
|
|
||||||
interface IFilters {
|
interface IFilters {
|
||||||
sourceFilter: ISourceFilters[];
|
sourceFilter: ISourceFilters[];
|
||||||
@@ -160,15 +161,10 @@ export default function SourceOptions({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Fab
|
<StyledFab onClick={() => setFilterOptions(!FilterOptions)} variant="extended" color="primary">
|
||||||
sx={{ position: 'fixed', bottom: '2em', right: '3em' }}
|
|
||||||
onClick={() => setFilterOptions(!FilterOptions)}
|
|
||||||
variant="extended"
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
<FilterListIcon />
|
<FilterListIcon />
|
||||||
{t('global.button.filter')}
|
{t('global.button.filter')}
|
||||||
</Fab>
|
</StyledFab>
|
||||||
|
|
||||||
<OptionsPanel open={FilterOptions} onClose={() => setFilterOptions(false)}>
|
<OptionsPanel open={FilterOptions} onClose={() => setFilterOptions(false)}>
|
||||||
<Box sx={{ display: 'flex', p: 2, pb: 0 }}>
|
<Box sx={{ display: 'flex', p: 2, pb: 0 }}>
|
||||||
|
|||||||
17
src/components/util/StyledFab.tsx
Normal file
17
src/components/util/StyledFab.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Fab } from '@mui/material';
|
||||||
|
import { styled } from '@mui/system';
|
||||||
|
|
||||||
|
export const DEFAULT_FAB_STYLE = {
|
||||||
|
position: 'fixed',
|
||||||
|
height: '48px',
|
||||||
|
right: '3em',
|
||||||
|
bottom: '2em',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const DEFAULT_FULL_FAB_HEIGHT = `calc(${DEFAULT_FAB_STYLE.bottom} + ${DEFAULT_FAB_STYLE.height})`;
|
||||||
|
|
||||||
|
const StyledFab = styled(Fab)({
|
||||||
|
...DEFAULT_FAB_STYLE,
|
||||||
|
}) as typeof Fab;
|
||||||
|
|
||||||
|
export default StyledFab;
|
||||||
Reference in New Issue
Block a user