Remove "forwardRef" usage
This commit is contained in:
@@ -6,16 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import React, {
|
||||
ForwardedRef,
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type JSX,
|
||||
} from 'react';
|
||||
import React, { ForwardedRef, Ref, useCallback, useLayoutEffect, useMemo, useRef, useState, type JSX } from 'react';
|
||||
import Grid, { GridTypeMap } from '@mui/material/Grid';
|
||||
import Box, { BoxProps } from '@mui/material/Box';
|
||||
import { GridItemProps } from 'react-virtuoso';
|
||||
@@ -33,11 +24,11 @@ import { GridLayout } from '@/base/Base.types.ts';
|
||||
import { useMetadataServerSettings } from '@/features/settings/services/ServerSettingsMetadata.ts';
|
||||
import { VirtuosoGridPersisted } from '@/lib/virtuoso/Component/VirtuosoGridPersisted.tsx';
|
||||
|
||||
const GridContainer = React.forwardRef<HTMLDivElement, GridTypeMap['props']>(({ children, ...props }, ref) => (
|
||||
const GridContainer = ({ children, ref, ...props }: GridTypeMap['props'] & { ref?: Ref<HTMLDivElement> }) => (
|
||||
<Grid {...props} ref={ref} container spacing={1}>
|
||||
{children}
|
||||
</Grid>
|
||||
));
|
||||
);
|
||||
|
||||
const GridItemContainerWithDimension = (
|
||||
dimensions: number,
|
||||
@@ -86,115 +77,108 @@ type DefaultGridProps = Pick<MangaCardProps, 'mode'> & {
|
||||
isSelectModeActive?: boolean;
|
||||
selectedMangaIds?: Required<MangaType['id']>[];
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
ref?: ForwardedRef<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
const HorizontalGrid = forwardRef(
|
||||
(
|
||||
{
|
||||
isLoading,
|
||||
mangas,
|
||||
inLibraryIndicator,
|
||||
GridItemContainer,
|
||||
gridLayout,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
}: DefaultGridProps,
|
||||
ref: ForwardedRef<HTMLDivElement | null>,
|
||||
) => (
|
||||
<Grid
|
||||
ref={ref}
|
||||
container
|
||||
spacing={1}
|
||||
sx={{
|
||||
width: '100%',
|
||||
overflowX: 'auto',
|
||||
display: '-webkit-inline-box',
|
||||
flexWrap: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<LoadingPlaceholder />
|
||||
) : (
|
||||
mangas.map((manga) => (
|
||||
<GridItemContainer key={manga.id}>
|
||||
{createMangaCard(
|
||||
manga,
|
||||
gridLayout,
|
||||
inLibraryIndicator,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
)}
|
||||
</GridItemContainer>
|
||||
))
|
||||
)}
|
||||
</Grid>
|
||||
),
|
||||
const HorizontalGrid = ({
|
||||
isLoading,
|
||||
mangas,
|
||||
inLibraryIndicator,
|
||||
GridItemContainer,
|
||||
gridLayout,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
ref,
|
||||
}: DefaultGridProps) => (
|
||||
<Grid
|
||||
ref={ref}
|
||||
container
|
||||
spacing={1}
|
||||
sx={{
|
||||
width: '100%',
|
||||
overflowX: 'auto',
|
||||
display: '-webkit-inline-box',
|
||||
flexWrap: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<LoadingPlaceholder />
|
||||
) : (
|
||||
mangas.map((manga) => (
|
||||
<GridItemContainer key={manga.id}>
|
||||
{createMangaCard(
|
||||
manga,
|
||||
gridLayout,
|
||||
inLibraryIndicator,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
)}
|
||||
</GridItemContainer>
|
||||
))
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
|
||||
export const MANGA_GRID_SNAPSHOT_KEY = 'MangaGrid-snapshot-location';
|
||||
|
||||
const VerticalGrid = forwardRef(
|
||||
(
|
||||
{
|
||||
isLoading,
|
||||
mangas,
|
||||
inLibraryIndicator,
|
||||
GridItemContainer,
|
||||
gridLayout,
|
||||
hasNextPage,
|
||||
loadMore,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
}: DefaultGridProps & {
|
||||
hasNextPage: boolean;
|
||||
loadMore: () => void;
|
||||
},
|
||||
ref: ForwardedRef<HTMLDivElement | null>,
|
||||
) => (
|
||||
<>
|
||||
<Box ref={ref}>
|
||||
<VirtuosoGridPersisted
|
||||
persistKey={MANGA_GRID_SNAPSHOT_KEY}
|
||||
useWindowScroll
|
||||
increaseViewportBy={window.innerHeight * 0.5}
|
||||
totalCount={mangas.length}
|
||||
components={{
|
||||
List: GridContainer,
|
||||
Item: GridItemContainer,
|
||||
}}
|
||||
endReached={() => loadMore()}
|
||||
computeItemKey={(index) => mangas[index].id}
|
||||
itemContent={(index) =>
|
||||
createMangaCard(
|
||||
mangas[index],
|
||||
gridLayout,
|
||||
inLibraryIndicator,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{/* render div to prevent UI jumping around when showing/hiding loading placeholder */
|
||||
/* eslint-disable-next-line no-nested-ternary */}
|
||||
{isSelectModeActive && gridLayout === GridLayout.List ? (
|
||||
<Box sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }} />
|
||||
) : // eslint-disable-next-line no-nested-ternary
|
||||
isLoading ? (
|
||||
<LoadingPlaceholder />
|
||||
) : hasNextPage ? (
|
||||
<div style={{ height: '75px' }} />
|
||||
) : null}
|
||||
</>
|
||||
),
|
||||
const VerticalGrid = ({
|
||||
isLoading,
|
||||
mangas,
|
||||
inLibraryIndicator,
|
||||
GridItemContainer,
|
||||
gridLayout,
|
||||
hasNextPage,
|
||||
loadMore,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
ref,
|
||||
}: DefaultGridProps & {
|
||||
hasNextPage: boolean;
|
||||
loadMore: () => void;
|
||||
}) => (
|
||||
<>
|
||||
<Box ref={ref}>
|
||||
<VirtuosoGridPersisted
|
||||
persistKey={MANGA_GRID_SNAPSHOT_KEY}
|
||||
useWindowScroll
|
||||
increaseViewportBy={window.innerHeight * 0.5}
|
||||
totalCount={mangas.length}
|
||||
components={{
|
||||
List: GridContainer,
|
||||
Item: GridItemContainer,
|
||||
}}
|
||||
endReached={() => loadMore()}
|
||||
computeItemKey={(index) => mangas[index].id}
|
||||
itemContent={(index) =>
|
||||
createMangaCard(
|
||||
mangas[index],
|
||||
gridLayout,
|
||||
inLibraryIndicator,
|
||||
isSelectModeActive,
|
||||
selectedMangaIds,
|
||||
handleSelection,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
{/* render div to prevent UI jumping around when showing/hiding loading placeholder */
|
||||
/* eslint-disable-next-line no-nested-ternary */}
|
||||
{isSelectModeActive && gridLayout === GridLayout.List ? (
|
||||
<Box sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }} />
|
||||
) : // eslint-disable-next-line no-nested-ternary
|
||||
isLoading ? (
|
||||
<LoadingPlaceholder />
|
||||
) : hasNextPage ? (
|
||||
<div style={{ height: '75px' }} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
export interface IMangaGridProps
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BaseSyntheticEvent, ChangeEvent, useMemo, forwardRef, ForwardedRef } from 'react';
|
||||
import { BaseSyntheticEvent, ChangeEvent, useMemo, ForwardedRef } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
@@ -19,94 +19,91 @@ import { SelectableCollectionReturnType } from '@/features/collection/hooks/useS
|
||||
import { MangaType } from '@/lib/graphql/generated/graphql.ts';
|
||||
import { MUIUtil } from '@/lib/mui/MUI.util.ts';
|
||||
|
||||
export const MangaOptionButton = forwardRef(
|
||||
(
|
||||
{
|
||||
id,
|
||||
selected,
|
||||
handleSelection,
|
||||
asCheckbox = false,
|
||||
popupState,
|
||||
}: {
|
||||
id: number;
|
||||
selected?: boolean | null;
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
asCheckbox?: boolean;
|
||||
popupState: PopupState;
|
||||
},
|
||||
ref: ForwardedRef<HTMLButtonElement | null>,
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
export const MangaOptionButton = ({
|
||||
id,
|
||||
selected,
|
||||
handleSelection,
|
||||
asCheckbox = false,
|
||||
popupState,
|
||||
ref,
|
||||
}: {
|
||||
id: number;
|
||||
selected?: boolean | null;
|
||||
handleSelection?: SelectableCollectionReturnType<MangaType['id']>['handleSelection'];
|
||||
asCheckbox?: boolean;
|
||||
popupState: PopupState;
|
||||
ref?: ForwardedRef<HTMLButtonElement | null>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const bindTriggerProps = useMemo(() => bindTrigger(popupState), [popupState]);
|
||||
const bindTriggerProps = useMemo(() => bindTrigger(popupState), [popupState]);
|
||||
|
||||
const preventDefaultAction = (e: BaseSyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
const preventDefaultAction = (e: BaseSyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const handleSelectionChange = (e: ChangeEvent, isSelected: boolean) => {
|
||||
preventDefaultAction(e);
|
||||
handleSelection?.(id, isSelected);
|
||||
};
|
||||
const handleSelectionChange = (e: ChangeEvent, isSelected: boolean) => {
|
||||
preventDefaultAction(e);
|
||||
handleSelection?.(id, isSelected);
|
||||
};
|
||||
|
||||
if (!handleSelection) {
|
||||
if (!handleSelection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isSelected = selected !== null;
|
||||
if (isSelected) {
|
||||
if (!asCheckbox) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isSelected = selected !== null;
|
||||
if (isSelected) {
|
||||
if (!asCheckbox) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomTooltip title={t(selected ? 'global.button.deselect' : 'global.button.select')}>
|
||||
<Checkbox {...MUIUtil.preventRippleProp()} checked={selected} onChange={handleSelectionChange} />
|
||||
</CustomTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (asCheckbox) {
|
||||
return (
|
||||
<CustomTooltip title={t('global.button.options')}>
|
||||
<IconButton
|
||||
ref={ref}
|
||||
{...MUIUtil.preventRippleProp(bindTriggerProps, { onClick: preventDefaultAction })}
|
||||
aria-label="more"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomTooltip title={t('global.button.options')}>
|
||||
<Button
|
||||
ref={ref}
|
||||
{...MUIUtil.preventRippleProp(bindTriggerProps, { onClick: preventDefaultAction })}
|
||||
className="manga-option-button"
|
||||
size="small"
|
||||
variant="contained"
|
||||
sx={{
|
||||
minWidth: 'unset',
|
||||
paddingX: '0',
|
||||
paddingY: '2.5px',
|
||||
visibility: popupState.isOpen ? 'visible' : 'hidden',
|
||||
pointerEvents: 'none',
|
||||
'@media not (pointer: fine)': {
|
||||
visibility: 'hidden',
|
||||
width: 0,
|
||||
height: 0,
|
||||
p: 0,
|
||||
m: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</Button>
|
||||
<CustomTooltip title={t(selected ? 'global.button.deselect' : 'global.button.select')}>
|
||||
<Checkbox {...MUIUtil.preventRippleProp()} checked={selected} onChange={handleSelectionChange} />
|
||||
</CustomTooltip>
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (asCheckbox) {
|
||||
return (
|
||||
<CustomTooltip title={t('global.button.options')}>
|
||||
<IconButton
|
||||
ref={ref}
|
||||
{...MUIUtil.preventRippleProp(bindTriggerProps, { onClick: preventDefaultAction })}
|
||||
aria-label="more"
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomTooltip title={t('global.button.options')}>
|
||||
<Button
|
||||
ref={ref}
|
||||
{...MUIUtil.preventRippleProp(bindTriggerProps, { onClick: preventDefaultAction })}
|
||||
className="manga-option-button"
|
||||
size="small"
|
||||
variant="contained"
|
||||
sx={{
|
||||
minWidth: 'unset',
|
||||
paddingX: '0',
|
||||
paddingY: '2.5px',
|
||||
visibility: popupState.isOpen ? 'visible' : 'hidden',
|
||||
pointerEvents: 'none',
|
||||
'@media not (pointer: fine)': {
|
||||
visibility: 'hidden',
|
||||
width: 0,
|
||||
height: 0,
|
||||
p: 0,
|
||||
m: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</Button>
|
||||
</CustomTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user