mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Optimise card width calculation (#5713)
* Add hook for grid card width calculation * Move card width calculation into grid instead of card Now calculates once instead of per card * Debounce resize observer
This commit is contained in:
parent
2541e9d1eb
commit
5d3d02e1e7
17 changed files with 145 additions and 315 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { HoverPopover } from "../Shared/HoverPopover";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
import { SceneLink, TagLink } from "../Shared/TagLink";
|
||||
|
|
@ -12,7 +12,6 @@ import NavUtils from "src/utils/navigation";
|
|||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import { faBox, faPlayCircle, faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
import { galleryTitle } from "src/core/galleries";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
|
||||
import { GalleryPreviewScrubber } from "./GalleryPreviewScrubber";
|
||||
import cx from "classnames";
|
||||
|
|
@ -56,7 +55,7 @@ export const GalleryPreview: React.FC<IGalleryPreviewProps> = ({
|
|||
|
||||
interface IProps {
|
||||
gallery: GQL.SlimGalleryDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
selecting?: boolean;
|
||||
selected?: boolean | undefined;
|
||||
zoomIndex?: number;
|
||||
|
|
@ -65,37 +64,6 @@ interface IProps {
|
|||
|
||||
export const GalleryCard: React.FC<IProps> = (props) => {
|
||||
const history = useHistory();
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!props.containerWidth ||
|
||||
props.zoomIndex === undefined ||
|
||||
ScreenUtils.isMobile()
|
||||
)
|
||||
return;
|
||||
|
||||
let zoomValue = props.zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 280;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 480;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 640;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
props.containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [props.containerWidth, props.zoomIndex]);
|
||||
|
||||
function maybeRenderScenePopoverButton() {
|
||||
if (props.gallery.scenes.length === 0) return;
|
||||
|
|
@ -207,7 +175,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
|
|||
<GridCard
|
||||
className={`gallery-card zoom-${props.zoomIndex}`}
|
||||
url={`/galleries/${props.gallery.id}`}
|
||||
width={cardWidth}
|
||||
width={props.cardWidth}
|
||||
title={galleryTitle(props.gallery)}
|
||||
linkClassName="gallery-card-header"
|
||||
image={
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { GalleryCard } from "./GalleryCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface IGalleryCardGrid {
|
||||
galleries: GQL.SlimGalleryDataFragment[];
|
||||
|
|
@ -10,19 +13,23 @@ interface IGalleryCardGrid {
|
|||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [280, 340, 480, 640];
|
||||
|
||||
export const GalleryCardGrid: React.FC<IGalleryCardGrid> = ({
|
||||
galleries,
|
||||
selectedIds,
|
||||
zoomIndex,
|
||||
onSelectChange,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{galleries.map((gallery) => (
|
||||
<GalleryCard
|
||||
key={gallery.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
gallery={gallery}
|
||||
zoomIndex={zoomIndex}
|
||||
selecting={selectedIds.size > 0}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { HoverPopover } from "../Shared/HoverPopover";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
import { SceneLink, TagLink } from "../Shared/TagLink";
|
||||
|
|
@ -9,7 +9,6 @@ import { TruncatedText } from "../Shared/TruncatedText";
|
|||
import { FormattedMessage } from "react-intl";
|
||||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import { faPlayCircle, faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { RelatedGroupPopoverButton } from "./RelatedGroupPopover";
|
||||
|
||||
const Description: React.FC<{
|
||||
|
|
@ -37,7 +36,7 @@ const Description: React.FC<{
|
|||
|
||||
interface IProps {
|
||||
group: GQL.GroupDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
sceneNumber?: number;
|
||||
selecting?: boolean;
|
||||
selected?: boolean;
|
||||
|
|
@ -50,7 +49,7 @@ interface IProps {
|
|||
export const GroupCard: React.FC<IProps> = ({
|
||||
group,
|
||||
sceneNumber,
|
||||
containerWidth,
|
||||
cardWidth,
|
||||
selecting,
|
||||
selected,
|
||||
zoomIndex,
|
||||
|
|
@ -58,8 +57,6 @@ export const GroupCard: React.FC<IProps> = ({
|
|||
fromGroupId,
|
||||
onMove,
|
||||
}) => {
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
const groupDescription = useMemo(() => {
|
||||
if (!fromGroupId) {
|
||||
return undefined;
|
||||
|
|
@ -72,32 +69,6 @@ export const GroupCard: React.FC<IProps> = ({
|
|||
return containingGroup?.description ?? undefined;
|
||||
}, [fromGroupId, group.containing_groups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerWidth || zoomIndex === undefined || ScreenUtils.isMobile())
|
||||
return;
|
||||
|
||||
let zoomValue = zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 210;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 250;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 300;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 375;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth, zoomIndex]);
|
||||
|
||||
function maybeRenderScenesPopoverButton() {
|
||||
if (group.scenes.length === 0) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { GroupCard } from "./GroupCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface IGroupCardGrid {
|
||||
groups: GQL.GroupDataFragment[];
|
||||
|
|
@ -12,6 +15,8 @@ interface IGroupCardGrid {
|
|||
onMove?: (srcIds: string[], targetId: string, after: boolean) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [210, 250, 300, 375];
|
||||
|
||||
export const GroupCardGrid: React.FC<IGroupCardGrid> = ({
|
||||
groups,
|
||||
selectedIds,
|
||||
|
|
@ -20,13 +25,15 @@ export const GroupCardGrid: React.FC<IGroupCardGrid> = ({
|
|||
fromGroupId,
|
||||
onMove,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{groups.map((p) => (
|
||||
<GroupCard
|
||||
key={p.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
group={p}
|
||||
zoomIndex={zoomIndex}
|
||||
selecting={selectedIds.size > 0}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { MouseEvent, useEffect, useMemo, useState } from "react";
|
||||
import React, { MouseEvent, useMemo } from "react";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import cx from "classnames";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
|
|
@ -7,10 +7,7 @@ import { GalleryLink, TagLink } from "src/components/Shared/TagLink";
|
|||
import { HoverPopover } from "src/components/Shared/HoverPopover";
|
||||
import { SweatDrops } from "src/components/Shared/SweatDrops";
|
||||
import { PerformerPopoverButton } from "src/components/Shared/PerformerPopoverButton";
|
||||
import {
|
||||
GridCard,
|
||||
calculateCardWidth,
|
||||
} from "src/components/Shared/GridCard/GridCard";
|
||||
import { GridCard } from "src/components/Shared/GridCard/GridCard";
|
||||
import { RatingBanner } from "src/components/Shared/RatingBanner";
|
||||
import {
|
||||
faBox,
|
||||
|
|
@ -20,12 +17,11 @@ import {
|
|||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { imageTitle } from "src/core/files";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
|
||||
|
||||
interface IImageCardProps {
|
||||
image: GQL.SlimImageDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
selecting?: boolean;
|
||||
selected?: boolean | undefined;
|
||||
zoomIndex: number;
|
||||
|
|
@ -36,38 +32,6 @@ interface IImageCardProps {
|
|||
export const ImageCard: React.FC<IImageCardProps> = (
|
||||
props: IImageCardProps
|
||||
) => {
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!props.containerWidth ||
|
||||
props.zoomIndex === undefined ||
|
||||
ScreenUtils.isMobile()
|
||||
)
|
||||
return;
|
||||
|
||||
let zoomValue = props.zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 280;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 480;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 640;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
props.containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [props.containerWidth, props.zoomIndex]);
|
||||
|
||||
const file = useMemo(
|
||||
() =>
|
||||
props.image.visual_files.length > 0
|
||||
|
|
@ -196,7 +160,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
|
|||
<GridCard
|
||||
className={`image-card zoom-${props.zoomIndex}`}
|
||||
url={`/images/${props.image.id}`}
|
||||
width={cardWidth}
|
||||
width={props.cardWidth}
|
||||
title={imageTitle(props.image)}
|
||||
linkClassName="image-card-link"
|
||||
image={
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { ImageCard } from "./ImageCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface IImageCardGrid {
|
||||
images: GQL.SlimImageDataFragment[];
|
||||
|
|
@ -11,6 +14,8 @@ interface IImageCardGrid {
|
|||
onPreview: (index: number, ev: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [280, 340, 480, 640];
|
||||
|
||||
export const ImageGridCard: React.FC<IImageCardGrid> = ({
|
||||
images,
|
||||
selectedIds,
|
||||
|
|
@ -18,13 +23,15 @@ export const ImageGridCard: React.FC<IImageCardGrid> = ({
|
|||
onSelectChange,
|
||||
onPreview,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{images.map((image, index) => (
|
||||
<ImageCard
|
||||
key={image.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
image={image}
|
||||
zoomIndex={zoomIndex}
|
||||
selecting={selectedIds.size > 0}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useIntl } from "react-intl";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import NavUtils from "src/utils/navigation";
|
||||
import TextUtils from "src/utils/text";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { CountryFlag } from "../Shared/CountryFlag";
|
||||
import { SweatDrops } from "../Shared/SweatDrops";
|
||||
import { HoverPopover } from "../Shared/HoverPopover";
|
||||
|
|
@ -21,7 +21,6 @@ import { faTag } from "@fortawesome/free-solid-svg-icons";
|
|||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import { usePerformerUpdate } from "src/core/StashService";
|
||||
import { ILabeledId } from "src/models/list-filter/types";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { FavoriteIcon } from "../Shared/FavoriteIcon";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
|
|
@ -35,7 +34,7 @@ export interface IPerformerCardExtraCriteria {
|
|||
|
||||
interface IPerformerCardProps {
|
||||
performer: GQL.PerformerDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
ageFromDate?: string;
|
||||
selecting?: boolean;
|
||||
selected?: boolean;
|
||||
|
|
@ -300,41 +299,13 @@ export const PerformerCard: React.FC<IPerformerCardProps> = PatchComponent(
|
|||
(props) => {
|
||||
const {
|
||||
performer,
|
||||
containerWidth,
|
||||
cardWidth,
|
||||
selecting,
|
||||
selected,
|
||||
onSelectedChanged,
|
||||
zoomIndex,
|
||||
} = props;
|
||||
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerWidth || zoomIndex === undefined || ScreenUtils.isMobile())
|
||||
return;
|
||||
|
||||
let zoomValue = zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 240;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 300;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 375;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 470;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth, zoomIndex]);
|
||||
|
||||
return (
|
||||
<GridCard
|
||||
className={`performer-card zoom-${zoomIndex}`}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { IPerformerCardExtraCriteria, PerformerCard } from "./PerformerCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface IPerformerCardGrid {
|
||||
performers: GQL.PerformerDataFragment[];
|
||||
|
|
@ -11,6 +14,8 @@ interface IPerformerCardGrid {
|
|||
extraCriteria?: IPerformerCardExtraCriteria;
|
||||
}
|
||||
|
||||
const zoomWidths = [240, 300, 375, 470];
|
||||
|
||||
export const PerformerCardGrid: React.FC<IPerformerCardGrid> = ({
|
||||
performers,
|
||||
selectedIds,
|
||||
|
|
@ -18,13 +23,15 @@ export const PerformerCardGrid: React.FC<IPerformerCardGrid> = ({
|
|||
onSelectChange,
|
||||
extraCriteria,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{performers.map((p) => (
|
||||
<PerformerCard
|
||||
key={p.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
performer={p}
|
||||
zoomIndex={zoomIndex}
|
||||
selecting={selectedIds.size > 0}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { useEffect, useMemo, useRef } from "react";
|
||||
import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import cx from "classnames";
|
||||
|
|
@ -13,7 +13,7 @@ import TextUtils from "src/utils/text";
|
|||
import { SceneQueue } from "src/models/sceneQueue";
|
||||
import { ConfigurationContext } from "src/hooks/Config";
|
||||
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import {
|
||||
|
|
@ -27,7 +27,6 @@ import {
|
|||
import { objectPath, objectTitle } from "src/core/files";
|
||||
import { PreviewScrubber } from "./PreviewScrubber";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
|
||||
import { GroupTag } from "../Groups/GroupTag";
|
||||
import { FileSize } from "../Shared/FileSize";
|
||||
|
|
@ -94,7 +93,7 @@ export const ScenePreview: React.FC<IScenePreviewProps> = ({
|
|||
|
||||
interface ISceneCardProps {
|
||||
scene: GQL.SlimSceneDataFragment;
|
||||
containerWidth?: number;
|
||||
width?: number;
|
||||
previewHeight?: number;
|
||||
index?: number;
|
||||
queue?: SceneQueue;
|
||||
|
|
@ -439,7 +438,6 @@ export const SceneCard = PatchComponent(
|
|||
"SceneCard",
|
||||
(props: ISceneCardProps) => {
|
||||
const { configuration } = React.useContext(ConfigurationContext);
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
const file = useMemo(
|
||||
() => (props.scene.files.length > 0 ? props.scene.files[0] : undefined),
|
||||
|
|
@ -462,36 +460,6 @@ export const SceneCard = PatchComponent(
|
|||
return "";
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!props.containerWidth ||
|
||||
props.zoomIndex === undefined ||
|
||||
ScreenUtils.isMobile()
|
||||
)
|
||||
return;
|
||||
|
||||
let zoomValue = props.zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 280;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340; // this value is intentionally higher than 320
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 480;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 640;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
props.containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [props.containerWidth, props.zoomIndex]);
|
||||
|
||||
const cont = configuration?.interface.continuePlaylistDefault ?? false;
|
||||
|
||||
const sceneLink = props.queue
|
||||
|
|
@ -506,7 +474,7 @@ export const SceneCard = PatchComponent(
|
|||
className={`scene-card ${zoomIndex()} ${filelessClass()}`}
|
||||
url={sceneLink}
|
||||
title={objectTitle(props.scene)}
|
||||
width={cardWidth}
|
||||
width={props.width}
|
||||
linkClassName="scene-card-link"
|
||||
thumbnailSectionClassName="video-section"
|
||||
resumeTime={props.scene.resume_time ?? undefined}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import React from "react";
|
|||
import * as GQL from "src/core/generated-graphql";
|
||||
import { SceneQueue } from "src/models/sceneQueue";
|
||||
import { SceneCard } from "./SceneCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface ISceneCardsGrid {
|
||||
scenes: GQL.SlimSceneDataFragment[];
|
||||
|
|
@ -13,6 +16,8 @@ interface ISceneCardsGrid {
|
|||
fromGroupId?: string;
|
||||
}
|
||||
|
||||
const zoomWidths = [280, 340, 480, 640];
|
||||
|
||||
export const SceneCardsGrid: React.FC<ISceneCardsGrid> = ({
|
||||
scenes,
|
||||
queue,
|
||||
|
|
@ -21,13 +26,16 @@ export const SceneCardsGrid: React.FC<ISceneCardsGrid> = ({
|
|||
onSelectChange,
|
||||
fromGroupId,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{scenes.map((scene, index) => (
|
||||
<SceneCard
|
||||
key={scene.id}
|
||||
containerWidth={width}
|
||||
width={cardWidth}
|
||||
scene={scene}
|
||||
queue={queue}
|
||||
index={index}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
|
|
@ -7,9 +7,8 @@ import { HoverPopover } from "../Shared/HoverPopover";
|
|||
import NavUtils from "src/utils/navigation";
|
||||
import TextUtils from "src/utils/text";
|
||||
import { ConfigurationContext } from "src/hooks/Config";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { markerTitle } from "src/core/markers";
|
||||
import { Link } from "react-router-dom";
|
||||
import { objectTitle } from "src/core/files";
|
||||
|
|
@ -19,7 +18,7 @@ import { TruncatedText } from "../Shared/TruncatedText";
|
|||
|
||||
interface ISceneMarkerCardProps {
|
||||
marker: GQL.SceneMarkerDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
previewHeight?: number;
|
||||
index?: number;
|
||||
compact?: boolean;
|
||||
|
|
@ -154,8 +153,6 @@ const SceneMarkerCardImage = (props: ISceneMarkerCardProps) => {
|
|||
};
|
||||
|
||||
export const SceneMarkerCard = (props: ISceneMarkerCardProps) => {
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
function zoomIndex() {
|
||||
if (!props.compact && props.zoomIndex !== undefined) {
|
||||
return `zoom-${props.zoomIndex}`;
|
||||
|
|
@ -164,42 +161,12 @@ export const SceneMarkerCard = (props: ISceneMarkerCardProps) => {
|
|||
return "";
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!props.containerWidth ||
|
||||
props.zoomIndex === undefined ||
|
||||
ScreenUtils.isMobile()
|
||||
)
|
||||
return;
|
||||
|
||||
let zoomValue = props.zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 240;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340; // this value is intentionally higher than 320
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 480;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 640;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
props.containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [props, props.containerWidth, props.zoomIndex]);
|
||||
|
||||
return (
|
||||
<GridCard
|
||||
className={`scene-marker-card ${zoomIndex()}`}
|
||||
url={NavUtils.makeSceneMarkerUrl(props.marker)}
|
||||
title={markerTitle(props.marker)}
|
||||
width={cardWidth}
|
||||
width={props.cardWidth}
|
||||
linkClassName="scene-marker-card-link"
|
||||
thumbnailSectionClassName="video-section"
|
||||
resumeTime={props.marker.seconds}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { SceneMarkerCard } from "./SceneMarkerCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface ISceneMarkerCardsGrid {
|
||||
markers: GQL.SceneMarkerDataFragment[];
|
||||
|
|
@ -10,19 +13,23 @@ interface ISceneMarkerCardsGrid {
|
|||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [240, 340, 480, 640];
|
||||
|
||||
export const SceneMarkerCardsGrid: React.FC<ISceneMarkerCardsGrid> = ({
|
||||
markers,
|
||||
selectedIds,
|
||||
zoomIndex,
|
||||
onSelectChange,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{markers.map((marker, index) => (
|
||||
<SceneMarkerCard
|
||||
key={marker.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
marker={marker}
|
||||
index={index}
|
||||
zoomIndex={zoomIndex}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, {
|
||||
MutableRefObject,
|
||||
PropsWithChildren,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
|
|
@ -13,6 +14,7 @@ import useResizeObserver from "@react-hook/resize-observer";
|
|||
import { Icon } from "../Icon";
|
||||
import { faGripLines } from "@fortawesome/free-solid-svg-icons";
|
||||
import { DragSide, useDragMoveSelect } from "./dragMoveSelect";
|
||||
import { useDebounce } from "src/hooks/debounce";
|
||||
|
||||
interface ICardProps {
|
||||
className?: string;
|
||||
|
|
@ -63,7 +65,7 @@ export const useContainerDimensions = <T extends HTMLElement = HTMLDivElement>(
|
|||
height: 0,
|
||||
});
|
||||
|
||||
useResizeObserver(target, (entry) => {
|
||||
const debouncedSetDimension = useDebounce((entry: ResizeObserverEntry) => {
|
||||
const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
|
||||
let difference = Math.abs(dimension.width - width);
|
||||
// Only adjust when width changed by a significant margin. This addresses the cornercase that sees
|
||||
|
|
@ -73,11 +75,38 @@ export const useContainerDimensions = <T extends HTMLElement = HTMLDivElement>(
|
|||
if (difference > sensitivityThreshold) {
|
||||
setDimension({ width, height });
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
|
||||
useResizeObserver(target, debouncedSetDimension);
|
||||
|
||||
return [target, dimension];
|
||||
};
|
||||
|
||||
export function useCardWidth(
|
||||
containerWidth: number,
|
||||
zoomIndex: number,
|
||||
zoomWidths: number[]
|
||||
) {
|
||||
return useMemo(() => {
|
||||
if (
|
||||
!containerWidth ||
|
||||
zoomIndex === undefined ||
|
||||
zoomIndex < 0 ||
|
||||
zoomIndex >= zoomWidths.length ||
|
||||
ScreenUtils.isMobile()
|
||||
)
|
||||
return;
|
||||
|
||||
let zoomValue = zoomIndex;
|
||||
const preferredCardWidth = zoomWidths[zoomValue];
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
return fittedCardWidth;
|
||||
}, [containerWidth, zoomIndex, zoomWidths]);
|
||||
}
|
||||
|
||||
const Checkbox: React.FC<{
|
||||
selected?: boolean;
|
||||
onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import NavUtils from "src/utils/navigation";
|
||||
import {
|
||||
GridCard,
|
||||
calculateCardWidth,
|
||||
} from "src/components/Shared/GridCard/GridCard";
|
||||
import { GridCard } from "src/components/Shared/GridCard/GridCard";
|
||||
import { HoverPopover } from "../Shared/HoverPopover";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
import { TagLink } from "../Shared/TagLink";
|
||||
|
|
@ -13,14 +10,13 @@ import { Button, ButtonGroup } from "react-bootstrap";
|
|||
import { FormattedMessage } from "react-intl";
|
||||
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { FavoriteIcon } from "../Shared/FavoriteIcon";
|
||||
import { useStudioUpdate } from "src/core/StashService";
|
||||
import { faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
interface IProps {
|
||||
studio: GQL.StudioDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
hideParent?: boolean;
|
||||
selecting?: boolean;
|
||||
selected?: boolean;
|
||||
|
|
@ -75,7 +71,7 @@ function maybeRenderChildren(studio: GQL.StudioDataFragment) {
|
|||
|
||||
export const StudioCard: React.FC<IProps> = ({
|
||||
studio,
|
||||
containerWidth,
|
||||
cardWidth,
|
||||
hideParent,
|
||||
selecting,
|
||||
selected,
|
||||
|
|
@ -83,34 +79,6 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
onSelectedChanged,
|
||||
}) => {
|
||||
const [updateStudio] = useStudioUpdate();
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerWidth || zoomIndex === undefined || ScreenUtils.isMobile())
|
||||
return;
|
||||
|
||||
let zoomValue = zoomIndex;
|
||||
console.log(zoomValue);
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 280;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 420;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 560;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth, zoomIndex]);
|
||||
|
||||
function onToggleFavorite(v: boolean) {
|
||||
if (studio.id) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
import { StudioCard } from "./StudioCard";
|
||||
|
||||
interface IStudioCardGrid {
|
||||
|
|
@ -11,6 +14,8 @@ interface IStudioCardGrid {
|
|||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [280, 340, 420, 560];
|
||||
|
||||
export const StudioCardGrid: React.FC<IStudioCardGrid> = ({
|
||||
studios,
|
||||
fromParent,
|
||||
|
|
@ -18,13 +23,15 @@ export const StudioCardGrid: React.FC<IStudioCardGrid> = ({
|
|||
zoomIndex,
|
||||
onSelectChange,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{studios.map((studio) => (
|
||||
<StudioCard
|
||||
key={studio.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
studio={studio}
|
||||
zoomIndex={zoomIndex}
|
||||
hideParent={fromParent}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { PatchComponent } from "src/patch";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import NavUtils from "src/utils/navigation";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
|
||||
import { GridCard } from "../Shared/GridCard/GridCard";
|
||||
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
import { faHeart } from "@fortawesome/free-solid-svg-icons";
|
||||
import cx from "classnames";
|
||||
|
|
@ -16,7 +15,7 @@ import { useTagUpdate } from "src/core/StashService";
|
|||
|
||||
interface IProps {
|
||||
tag: GQL.TagDataFragment;
|
||||
containerWidth?: number;
|
||||
cardWidth?: number;
|
||||
zoomIndex: number;
|
||||
selecting?: boolean;
|
||||
selected?: boolean;
|
||||
|
|
@ -234,40 +233,8 @@ const TagCardTitle: React.FC<IProps> = PatchComponent(
|
|||
);
|
||||
|
||||
export const TagCard: React.FC<IProps> = PatchComponent("TagCard", (props) => {
|
||||
const {
|
||||
tag,
|
||||
containerWidth,
|
||||
zoomIndex,
|
||||
selecting,
|
||||
selected,
|
||||
onSelectedChanged,
|
||||
} = props;
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
useEffect(() => {
|
||||
if (!containerWidth || zoomIndex === undefined || ScreenUtils.isMobile())
|
||||
return;
|
||||
|
||||
let zoomValue = zoomIndex;
|
||||
let preferredCardWidth: number;
|
||||
switch (zoomValue) {
|
||||
case 0:
|
||||
preferredCardWidth = 280;
|
||||
break;
|
||||
case 1:
|
||||
preferredCardWidth = 340;
|
||||
break;
|
||||
case 2:
|
||||
preferredCardWidth = 480;
|
||||
break;
|
||||
case 3:
|
||||
preferredCardWidth = 640;
|
||||
}
|
||||
let fittedCardWidth = calculateCardWidth(
|
||||
containerWidth,
|
||||
preferredCardWidth!
|
||||
);
|
||||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth, zoomIndex]);
|
||||
const { tag, cardWidth, zoomIndex, selecting, selected, onSelectedChanged } =
|
||||
props;
|
||||
|
||||
return (
|
||||
<GridCard
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
import {
|
||||
useCardWidth,
|
||||
useContainerDimensions,
|
||||
} from "../Shared/GridCard/GridCard";
|
||||
import { TagCard } from "./TagCard";
|
||||
|
||||
interface ITagCardGrid {
|
||||
|
|
@ -10,19 +13,23 @@ interface ITagCardGrid {
|
|||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
const zoomWidths = [280, 340, 480, 640];
|
||||
|
||||
export const TagCardGrid: React.FC<ITagCardGrid> = ({
|
||||
tags,
|
||||
selectedIds,
|
||||
zoomIndex,
|
||||
onSelectChange,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
const [componentRef, { width: containerWidth }] = useContainerDimensions();
|
||||
const cardWidth = useCardWidth(containerWidth, zoomIndex, zoomWidths);
|
||||
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{tags.map((tag) => (
|
||||
<TagCard
|
||||
key={tag.id}
|
||||
containerWidth={width}
|
||||
cardWidth={cardWidth}
|
||||
tag={tag}
|
||||
zoomIndex={zoomIndex}
|
||||
selecting={selectedIds.size > 0}
|
||||
|
|
|
|||
Loading…
Reference in a new issue