items per row setting (#165)

* items per row setting

self explanatory

* make it better for user
This commit is contained in:
robo
2022-04-02 01:34:10 +01:00
committed by GitHub
parent 6215ad4663
commit 9ac66d08ef
3 changed files with 133 additions and 20 deletions

View File

@@ -71,6 +71,7 @@ const truncateText = (str: string, maxLength: number) => {
interface IProps { interface IProps {
manga: IMangaCard manga: IMangaCard
gridLayout: number | undefined gridLayout: number | undefined
dimensions: number
} }
const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref) => { const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref) => {
const { const {
@@ -79,15 +80,19 @@ const MangaCard = React.forwardRef<HTMLDivElement, IProps>((props: IProps, ref)
id, title, thumbnailUrl, downloadCount, unreadCount: unread, inLibrary, id, title, thumbnailUrl, downloadCount, unreadCount: unread, inLibrary,
}, },
gridLayout, gridLayout,
dimensions,
} = props; } = props;
const { options: { showUnreadBadge, showDownloadBadge } } = useLibraryOptionsContext(); const { options: { showUnreadBadge, showDownloadBadge } } = useLibraryOptionsContext();
const [serverAddress] = useLocalStorage<String>('serverBaseURL', ''); const [serverAddress] = useLocalStorage<String>('serverBaseURL', '');
const [useCache] = useLocalStorage<boolean>('useCache', true); const [useCache] = useLocalStorage<boolean>('useCache', true);
const [ItemWidth] = useLocalStorage<number>('ItemWidth', 300);
if (gridLayout !== 2) { if (gridLayout !== 2) {
const colomns = Math.round(dimensions / ItemWidth);
return ( return (
<Grid item xs={6} sm={4} md={3} lg={2}> // @ts-ignore gridsize type isnt allowed to be a decimal but it works fine
<Grid item lg={12 / colomns}>
<Link to={`/manga/${id}/`} style={(gridLayout === 1) ? { textDecoration: 'none' } : {}}> <Link to={`/manga/${id}/`} style={(gridLayout === 1) ? { textDecoration: 'none' } : {}}>
<Box <Box
sx={{ sx={{

View File

@@ -5,7 +5,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* 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, { useEffect, useRef } from 'react'; import React, {
useEffect, useLayoutEffect, useRef, useState,
} from 'react';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import EmptyView from 'components/util/EmptyView'; import EmptyView from 'components/util/EmptyView';
import LoadingPlaceholder from 'components/util/LoadingPlaceholder'; import LoadingPlaceholder from 'components/util/LoadingPlaceholder';
@@ -49,6 +51,23 @@ export default function MangaGrid(props: IMangaGridProps) {
}; };
}, [hasNextPage, mangas]); }, [hasNextPage, mangas]);
const [dimensions, setDimensions] = useState(1);
const gridRef = useRef<HTMLDivElement>(null);
const TestDimensions = () => {
setDimensions(gridRef.current ? gridRef.current.offsetWidth : 0);
};
useLayoutEffect(TestDimensions, []);
let movementTimer: NodeJS.Timeout;
window.addEventListener('resize', () => {
clearInterval(movementTimer);
movementTimer = setTimeout(TestDimensions, 100);
});
if (mangas.length === 0) { if (mangas.length === 0) {
if (isLoading) { if (isLoading) {
mapped = ( mapped = (
@@ -79,6 +98,7 @@ export default function MangaGrid(props: IMangaGridProps) {
manga={it} manga={it}
ref={lastManga} ref={lastManga}
gridLayout={gridLayout} gridLayout={gridLayout}
dimensions={dimensions}
/> />
); );
} }
@@ -87,30 +107,33 @@ export default function MangaGrid(props: IMangaGridProps) {
key={it.id} key={it.id}
manga={it} manga={it}
gridLayout={gridLayout} gridLayout={gridLayout}
dimensions={dimensions}
/> />
); );
}); });
} }
return ( return (
<Grid <div ref={gridRef}>
container <Grid
spacing={1} container
style={horisontal ? { spacing={1}
margin: 0, style={horisontal ? {
width: '100%', margin: 0,
padding: '5px', width: '100%',
overflowX: 'scroll', padding: '5px',
display: '-webkit-inline-box', overflowX: 'scroll',
flexWrap: 'nowrap', display: '-webkit-inline-box',
} : { flexWrap: 'nowrap',
margin: 0, } : {
width: '100%', margin: 0,
padding: '5px', width: '100%',
}} padding: '5px',
> }}
{mapped} >
</Grid> {mapped}
</Grid>
</div>
); );
} }

View File

@@ -27,6 +27,9 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText'; import DialogContentText from '@mui/material/DialogContentText';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import FavoriteIcon from '@mui/icons-material/Favorite'; import FavoriteIcon from '@mui/icons-material/Favorite';
import Slider from '@mui/material/Slider';
import { DialogTitle, ListItemButton } from '@mui/material';
import ViewModuleIcon from '@mui/icons-material/ViewModule';
import NavbarContext from '../components/context/NavbarContext'; import NavbarContext from '../components/context/NavbarContext';
import DarkTheme from '../components/context/DarkTheme'; import DarkTheme from '../components/context/DarkTheme';
import useLocalStorage from '../util/useLocalStorage'; import useLocalStorage from '../util/useLocalStorage';
@@ -43,6 +46,10 @@ export default function Settings() {
const [dialogOpen, setDialogOpen] = useState(false); const [dialogOpen, setDialogOpen] = useState(false);
const [dialogValue, setDialogValue] = useState(serverAddress); const [dialogValue, setDialogValue] = useState(serverAddress);
const [dialogOpenItemWidth, setDialogOpenItemWidth] = useState(false);
const [ItemWidth, setItemWidth] = useLocalStorage<number>('ItemWidth', 6);
const [DialogItemWidth, setDialogItemWidth] = useState(ItemWidth);
const handleDialogOpen = () => { const handleDialogOpen = () => {
setDialogValue(serverAddress); setDialogValue(serverAddress);
setDialogOpen(true); setDialogOpen(true);
@@ -57,6 +64,29 @@ export default function Settings() {
setServerAddress(dialogValue); setServerAddress(dialogValue);
}; };
const handleDialogOpenItemWidth = () => {
setDialogItemWidth(ItemWidth);
setDialogOpenItemWidth(true);
};
const handleDialogCancelItemWidth = () => {
setDialogOpenItemWidth(false);
};
const handleDialogSubmitItemWidth = () => {
setDialogOpenItemWidth(false);
setItemWidth(DialogItemWidth);
};
const handleDialogResetItemWidth = () => {
setDialogOpenItemWidth(false);
setItemWidth(300);
};
const handleChange = (event: Event, newValue: number | number[]) => {
setDialogItemWidth(newValue as number);
};
return ( return (
<> <>
<List sx={{ padding: 0 }}> <List sx={{ padding: 0 }}>
@@ -85,6 +115,18 @@ export default function Settings() {
/> />
</ListItemSecondaryAction> </ListItemSecondaryAction>
</ListItem> </ListItem>
<ListItemButton>
<ListItemIcon>
<ViewModuleIcon />
</ListItemIcon>
<ListItemText
primary="Manga Item width"
secondary={`px:${ItemWidth}`}
onClick={() => {
handleDialogOpenItemWidth();
}}
/>
</ListItemButton>
<ListItem> <ListItem>
<ListItemIcon> <ListItemIcon>
<FavoriteIcon /> <FavoriteIcon />
@@ -168,6 +210,49 @@ export default function Settings() {
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
<Dialog open={dialogOpenItemWidth} onClose={handleDialogCancelItemWidth}>
<DialogTitle>
Manga Item width
</DialogTitle>
<DialogContent
sx={{
width: '98%',
margin: 'auto',
}}
>
<TextField
sx={{
width: '100%',
margin: 'auto',
}}
autoFocus
value={DialogItemWidth}
type="number"
onChange={(e) => setDialogItemWidth(parseInt(e.target.value, 10))}
/>
<Slider
aria-label="Manga Item width"
defaultValue={300}
value={DialogItemWidth}
step={10}
min={100}
max={1000}
onChange={handleChange}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogResetItemWidth} color="primary">
Reset to Default
</Button>
<Button onClick={handleDialogCancelItemWidth} color="primary">
Cancel
</Button>
<Button onClick={handleDialogSubmitItemWidth} color="primary">
OK
</Button>
</DialogActions>
</Dialog>
</> </>
); );
} }