Migrate to mui grid v2
This commit is contained in:
@@ -16,8 +16,8 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import Grid, { GridTypeMap } from '@mui/material/Grid';
|
import Grid, { Grid2TypeMap as GridTypeMap } from '@mui/material/Grid2';
|
||||||
import Box from '@mui/material/Box';
|
import Box, { BoxProps } from '@mui/material/Box';
|
||||||
import { GridItemProps, GridStateSnapshot, VirtuosoGrid } from 'react-virtuoso';
|
import { GridItemProps, GridStateSnapshot, VirtuosoGrid } from 'react-virtuoso';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -35,7 +35,7 @@ import { useResizeObserver } from '@/util/useResizeObserver.tsx';
|
|||||||
import { useNavBarContext } from '@/components/context/NavbarContext.tsx';
|
import { useNavBarContext } from '@/components/context/NavbarContext.tsx';
|
||||||
|
|
||||||
const GridContainer = React.forwardRef<HTMLDivElement, GridTypeMap['props']>(({ children, ...props }, ref) => (
|
const GridContainer = React.forwardRef<HTMLDivElement, GridTypeMap['props']>(({ children, ...props }, ref) => (
|
||||||
<Grid {...props} ref={ref} container sx={{ paddingLeft: '5px', paddingRight: '13px' }}>
|
<Grid {...props} ref={ref} container spacing={1}>
|
||||||
{children}
|
{children}
|
||||||
</Grid>
|
</Grid>
|
||||||
));
|
));
|
||||||
@@ -51,7 +51,7 @@ const GridItemContainerWithDimension = (
|
|||||||
|
|
||||||
// MUI GridProps and Virtuoso GridItemProps use different types for the "ref" prop which conflict with each other
|
// MUI GridProps and Virtuoso GridItemProps use different types for the "ref" prop which conflict with each other
|
||||||
return ({ children, ...itemProps }: GridTypeMap['props'] & Omit<Partial<GridItemProps>, 'ref'>) => (
|
return ({ children, ...itemProps }: GridTypeMap['props'] & Omit<Partial<GridItemProps>, 'ref'>) => (
|
||||||
<Grid {...itemProps} item xs={columnsPerItem} sx={{ width: '100%', paddingTop: '8px', paddingLeft: '8px' }}>
|
<Grid {...itemProps} size={columnsPerItem}>
|
||||||
{children}
|
{children}
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
@@ -109,10 +109,8 @@ const HorizontalGrid = forwardRef(
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
container
|
container
|
||||||
spacing={1}
|
spacing={1}
|
||||||
style={{
|
sx={{
|
||||||
margin: 0,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '5px',
|
|
||||||
overflowX: 'auto',
|
overflowX: 'auto',
|
||||||
display: '-webkit-inline-box',
|
display: '-webkit-inline-box',
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
@@ -232,6 +230,7 @@ export interface IMangaGridProps
|
|||||||
loadMore: () => void;
|
loadMore: () => void;
|
||||||
horizontal?: boolean | undefined;
|
horizontal?: boolean | undefined;
|
||||||
noFaces?: boolean | undefined;
|
noFaces?: boolean | undefined;
|
||||||
|
gridWrapperProps?: Omit<BoxProps, 'ref'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MangaGrid: React.FC<IMangaGridProps> = ({
|
export const MangaGrid: React.FC<IMangaGridProps> = ({
|
||||||
@@ -250,6 +249,7 @@ export const MangaGrid: React.FC<IMangaGridProps> = ({
|
|||||||
handleSelection,
|
handleSelection,
|
||||||
mode,
|
mode,
|
||||||
retry,
|
retry,
|
||||||
|
gridWrapperProps,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -353,13 +353,7 @@ export const MangaGrid: React.FC<IMangaGridProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Box {...gridWrapperProps} ref={gridWrapperRef} sx={{ ...gridWrapperProps?.sx, overflow: 'hidden' }}>
|
||||||
ref={gridWrapperRef}
|
|
||||||
style={{
|
|
||||||
overflow: 'hidden',
|
|
||||||
paddingBottom: '13px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{horizontal ? (
|
{horizontal ? (
|
||||||
<HorizontalGrid
|
<HorizontalGrid
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
@@ -389,6 +383,6 @@ export const MangaGrid: React.FC<IMangaGridProps> = ({
|
|||||||
mode={mode}
|
mode={mode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export const LibraryMangaGrid: React.FC<LibraryMangaGridProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MangaGrid
|
<MangaGrid
|
||||||
|
gridWrapperProps={{ sx: { p: 1 } }}
|
||||||
{...gridProps}
|
{...gridProps}
|
||||||
hasNextPage={false}
|
hasNextPage={false}
|
||||||
loadMore={() => undefined}
|
loadMore={() => undefined}
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ type TMangaBaseGrid = Omit<IMangaGridProps['mangas'][number], 'downloadCount' |
|
|||||||
export function BaseMangaGrid(props: Omit<IMangaGridProps, 'mangas'> & { mangas: TMangaBaseGrid[] }) {
|
export function BaseMangaGrid(props: Omit<IMangaGridProps, 'mangas'> & { mangas: TMangaBaseGrid[] }) {
|
||||||
const { mangas } = props;
|
const { mangas } = props;
|
||||||
|
|
||||||
return <MangaGrid {...props} mangas={mangas as IMangaGridProps['mangas']} />;
|
return <MangaGrid gridWrapperProps={{ sx: { p: 1 } }} {...props} mangas={mangas as IMangaGridProps['mangas']} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
|
|||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { StringParam, useQueryParam } from 'use-query-params';
|
import { StringParam, useQueryParam } from 'use-query-params';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||||
import { useLocalStorage } from '@/util/useStorage.tsx';
|
import { useLocalStorage } from '@/util/useStorage.tsx';
|
||||||
import { langSortCmp, sourceDefualtLangs, sourceForcedDefaultLangs } from '@/util/language';
|
import { langSortCmp, sourceDefualtLangs, sourceForcedDefaultLangs } from '@/util/language';
|
||||||
@@ -144,7 +145,7 @@ const SourceSearchPreview = React.memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card sx={{ margin: '10px' }}>
|
<Card sx={{ mb: 1 }}>
|
||||||
<CardActionArea component={Link} to={`/sources/${id}?query=${searchString}`} sx={{ p: 3 }}>
|
<CardActionArea component={Link} to={`/sources/${id}?query=${searchString}`} sx={{ p: 3 }}>
|
||||||
<Typography variant="h5">{displayName}</Typography>
|
<Typography variant="h5">{displayName}</Typography>
|
||||||
<Typography variant="caption">{translateExtensionLanguage(lang)}</Typography>
|
<Typography variant="caption">{translateExtensionLanguage(lang)}</Typography>
|
||||||
@@ -167,6 +168,7 @@ const SourceSearchPreview = React.memo(
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BaseMangaGrid
|
<BaseMangaGrid
|
||||||
|
gridWrapperProps={{ sx: { px: 0 } }}
|
||||||
mangas={mangas}
|
mangas={mangas}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
hasNextPage={false}
|
hasNextPage={false}
|
||||||
@@ -275,17 +277,18 @@ export const SearchAll: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ p: 1 }}>
|
||||||
{sourcesSortedByResult.map((source) => (
|
{sourcesSortedByResult.map((source, index) => (
|
||||||
<SourceSearchPreview
|
<Box key={source.id} sx={{ pb: index + 1 !== sourcesSortedByResult.length ? 2 : 0 }}>
|
||||||
key={source.id}
|
<SourceSearchPreview
|
||||||
source={source}
|
source={source}
|
||||||
onSearchRequestFinished={updateSourceLoadingState}
|
onSearchRequestFinished={updateSourceLoadingState}
|
||||||
searchString={searchString}
|
searchString={searchString}
|
||||||
emptyQuery={!query}
|
emptyQuery={!query}
|
||||||
mode={isMigrateMode ? 'migrate.select' : 'source'}
|
mode={isMigrateMode ? 'migrate.select' : 'source'}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
))}
|
))}
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,37 +49,20 @@ import { MangaIdInfo } from '@/lib/data/Mangas.ts';
|
|||||||
|
|
||||||
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
const ContentTypeMenu = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
position: 'fixed',
|
position: 'sticky',
|
||||||
top: '64px',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
gap: theme.spacing(1),
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
[theme.breakpoints.down('sm')]: {
|
|
||||||
top: '56px', // header height
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ContentTypeButton = styled(Button)(() => ({
|
const ContentTypeButton = styled(Button)(() => ({}));
|
||||||
marginTop: '13px',
|
|
||||||
marginBottom: '13px',
|
|
||||||
marginLeft: '13px',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledGridWrapper = styled(Box, { shouldForwardProp: (prop) => prop !== 'hasContent' })<{ hasContent: boolean }>(
|
const StyledGridWrapper = styled(Box)(() => ({
|
||||||
({ theme, hasContent }) => ({
|
minHeight: '100%',
|
||||||
// 62.5px ContentTypeMenu height (- padding of grid + grid item)
|
position: 'relative',
|
||||||
marginTop: `calc(62.5px ${hasContent ? '- 8px' : ''})`,
|
}));
|
||||||
// header height - ContentTypeMenu height
|
|
||||||
minHeight: 'calc(100vh - 64px - 62.5px)',
|
|
||||||
position: 'relative',
|
|
||||||
[theme.breakpoints.down('sm')]: {
|
|
||||||
// 62.5px ContentTypeMenu - 8px margin diff header height (56px) (- padding of grid item)
|
|
||||||
marginTop: `calc(62.5px - 8px ${hasContent ? '- 8px' : ''})`,
|
|
||||||
// header height (+ 8px margin) - footer height - ContentTypeMenu height
|
|
||||||
minHeight: 'calc(100vh - 64px - 64px - 62.5px)',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
export enum SourceContentType {
|
export enum SourceContentType {
|
||||||
POPULAR,
|
POPULAR,
|
||||||
@@ -212,7 +195,7 @@ const useSourceManga = (
|
|||||||
|
|
||||||
export function SourceMangas() {
|
export function SourceMangas() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setTitle, setAction } = useContext(NavBarContext);
|
const { setTitle, setAction, appBarHeight } = useContext(NavBarContext);
|
||||||
|
|
||||||
const { sourceId } = useParams<{ sourceId: string }>();
|
const { sourceId } = useParams<{ sourceId: string }>();
|
||||||
|
|
||||||
@@ -226,7 +209,6 @@ export function SourceMangas() {
|
|||||||
}>().state ?? {};
|
}>().state ?? {};
|
||||||
|
|
||||||
const [isFirstRender, setIsFirstRender] = useState(true);
|
const [isFirstRender, setIsFirstRender] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsFirstRender(false);
|
setIsFirstRender(false);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -444,8 +426,8 @@ export function SourceMangas() {
|
|||||||
}, [t, source]);
|
}, [t, source]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledGridWrapper hasContent={!!mangas.length}>
|
<StyledGridWrapper>
|
||||||
<ContentTypeMenu>
|
<ContentTypeMenu sx={{ top: `${appBarHeight}px` }}>
|
||||||
<ContentTypeButton
|
<ContentTypeButton
|
||||||
variant={contentType === SourceContentType.POPULAR ? 'contained' : 'outlined'}
|
variant={contentType === SourceContentType.POPULAR ? 'contained' : 'outlined'}
|
||||||
startIcon={<FavoriteIcon />}
|
startIcon={<FavoriteIcon />}
|
||||||
@@ -473,6 +455,7 @@ export function SourceMangas() {
|
|||||||
</ContentTypeMenu>
|
</ContentTypeMenu>
|
||||||
<BaseMangaGrid
|
<BaseMangaGrid
|
||||||
key={contentType}
|
key={contentType}
|
||||||
|
gridWrapperProps={{ sx: { px: 1, pb: 1 } }}
|
||||||
mangas={mangas}
|
mangas={mangas}
|
||||||
hasNextPage={hasNextPage}
|
hasNextPage={hasNextPage}
|
||||||
loadMore={loadMore}
|
loadMore={loadMore}
|
||||||
|
|||||||
Reference in New Issue
Block a user