Skip to content

Commit

Permalink
Fit cards properly within their containers (stashapp#4514)
Browse files Browse the repository at this point in the history
* created missing cards grids
  • Loading branch information
cj12312021 authored and dogwithakeyboard committed Feb 18, 2024
1 parent ca30133 commit 6453e6c
Show file tree
Hide file tree
Showing 25 changed files with 460 additions and 124 deletions.
4 changes: 0 additions & 4 deletions ui/v2.5/src/components/FrontPage/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,6 @@
.slick-list .performer-card.card {
width: 16rem;
}

.slick-list .performer-card-image {
height: 24rem;
}
}

/* Icons */
Expand Down
38 changes: 36 additions & 2 deletions ui/v2.5/src/components/Galleries/GalleryCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
import React from "react";
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { GridCard } from "../Shared/GridCard";
import { GridCard, calculateCardWidth } from "../Shared/GridCard";
import { HoverPopover } from "../Shared/HoverPopover";
import { Icon } from "../Shared/Icon";
import { SceneLink, TagLink } from "../Shared/TagLink";
Expand All @@ -14,9 +14,11 @@ import { ConfigurationContext } from "src/hooks/Config";
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";

interface IProps {
gallery: GQL.SlimGalleryDataFragment;
containerWidth?: number;
selecting?: boolean;
selected?: boolean | undefined;
zoomIndex?: number;
Expand All @@ -26,6 +28,37 @@ interface IProps {
export const GalleryCard: React.FC<IProps> = (props) => {
const { configuration } = React.useContext(ConfigurationContext);
const showStudioAsText = configuration?.interface.showStudioAsText ?? false;
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 = 240;
break;
case 1:
preferredCardWidth = 340;
break;
case 2:
preferredCardWidth = 480;
break;
case 3:
preferredCardWidth = 640;
}
let fittedCardWidth = calculateCardWidth(
props.containerWidth,
preferredCardWidth!
);
setCardWidth(fittedCardWidth);
}, [props, props.containerWidth, props.zoomIndex]);

function maybeRenderScenePopoverButton() {
if (props.gallery.scenes.length === 0) return;
Expand Down Expand Up @@ -153,6 +186,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
<GridCard
className={`gallery-card zoom-${props.zoomIndex}`}
url={`/galleries/${props.gallery.id}`}
width={cardWidth}
title={galleryTitle(props.gallery)}
linkClassName="gallery-card-header"
image={
Expand Down
9 changes: 7 additions & 2 deletions ui/v2.5/src/components/Galleries/GalleryList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useRef, useState } from "react";
import { useIntl } from "react-intl";
import cloneDeep from "lodash-es/cloneDeep";
import { useHistory } from "react-router-dom";
Expand All @@ -18,6 +18,7 @@ import { EditGalleriesDialog } from "./EditGalleriesDialog";
import { DeleteGalleriesDialog } from "./DeleteGalleriesDialog";
import { ExportDialog } from "../Shared/ExportDialog";
import { GalleryListTable } from "./GalleryListTable";
import { useContainerDimensions } from "../Shared/GridCard";

const GalleryItemList = makeItemList({
filterMode: GQL.FilterMode.Galleries,
Expand Down Expand Up @@ -106,6 +107,9 @@ export const GalleryList: React.FC<IGalleryList> = ({
setIsExportDialogOpen(true);
}

const componentRef = useRef<HTMLDivElement>(null);
const { width } = useContainerDimensions(componentRef);

function renderContent(
result: GQL.FindGalleriesQueryResult,
filter: ListFilterModel,
Expand Down Expand Up @@ -133,10 +137,11 @@ export const GalleryList: React.FC<IGalleryList> = ({

if (filter.displayMode === DisplayMode.Grid) {
return (
<div className="row justify-content-center">
<div className="row justify-content-center" ref={componentRef}>
{result.data.findGalleries.galleries.map((gallery) => (
<GalleryCard
key={gallery.id}
containerWidth={width}
gallery={gallery}
zoomIndex={filter.zoomIndex}
selecting={selectedIds.size > 0}
Expand Down
39 changes: 37 additions & 2 deletions ui/v2.5/src/components/Images/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { MouseEvent, useMemo } from "react";
import React, { MouseEvent, useEffect, useMemo, useState } from "react";
import { Button, ButtonGroup } from "react-bootstrap";
import cx from "classnames";
import * as GQL from "src/core/generated-graphql";
Expand All @@ -7,7 +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 } from "src/components/Shared/GridCard";
import { GridCard, calculateCardWidth } from "src/components/Shared/GridCard";
import { RatingBanner } from "src/components/Shared/RatingBanner";
import {
faBox,
Expand All @@ -17,9 +17,11 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { objectTitle } from "src/core/files";
import { TruncatedText } from "../Shared/TruncatedText";
import ScreenUtils from "src/utils/screen";

interface IImageCardProps {
image: GQL.SlimImageDataFragment;
containerWidth?: number;
selecting?: boolean;
selected?: boolean | undefined;
zoomIndex: number;
Expand All @@ -30,6 +32,38 @@ 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 = 240;
break;
case 1:
preferredCardWidth = 340;
break;
case 2:
preferredCardWidth = 480;
break;
case 3:
preferredCardWidth = 640;
}
let fittedCardWidth = calculateCardWidth(
props.containerWidth,
preferredCardWidth!
);
setCardWidth(fittedCardWidth);
}, [props, props.containerWidth, props.zoomIndex]);

const file = useMemo(
() =>
props.image.visual_files.length > 0
Expand Down Expand Up @@ -153,6 +187,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
<GridCard
className={`image-card zoom-${props.zoomIndex}`}
url={`/images/${props.image.id}`}
width={cardWidth}
title={objectTitle(props.image)}
linkClassName="image-card-link"
image={
Expand Down
8 changes: 7 additions & 1 deletion ui/v2.5/src/components/Images/ImageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, {
useMemo,
MouseEvent,
useContext,
useRef,
} from "react";
import { FormattedNumber, useIntl } from "react-intl";
import cloneDeep from "lodash-es/cloneDeep";
Expand Down Expand Up @@ -32,6 +33,7 @@ import { objectTitle } from "src/core/files";
import TextUtils from "src/utils/text";
import { ConfigurationContext } from "src/hooks/Config";
import { IUIConfig } from "src/core/config";
import { useContainerDimensions } from "../Shared/GridCard";

interface IImageWallProps {
images: GQL.SlimImageDataFragment[];
Expand Down Expand Up @@ -196,6 +198,9 @@ const ImageListImages: React.FC<IImageListImages> = ({
ev.preventDefault();
}

const componentRef = useRef<HTMLDivElement>(null);
const { width } = useContainerDimensions(componentRef);

function renderImageCard(
index: number,
image: GQL.SlimImageDataFragment,
Expand All @@ -204,6 +209,7 @@ const ImageListImages: React.FC<IImageListImages> = ({
return (
<ImageCard
key={image.id}
containerWidth={width}
image={image}
zoomIndex={zoomIndex}
selecting={selectedIds.size > 0}
Expand All @@ -220,7 +226,7 @@ const ImageListImages: React.FC<IImageListImages> = ({

if (filter.displayMode === DisplayMode.Grid) {
return (
<div className="row justify-content-center">
<div className="row justify-content-center" ref={componentRef}>
{images.map((image, index) =>
renderImageCard(index, image, filter.zoomIndex)
)}
Expand Down
20 changes: 18 additions & 2 deletions ui/v2.5/src/components/Movies/MovieCard.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Button, ButtonGroup } from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { GridCard } from "../Shared/GridCard";
import { GridCard, calculateCardWidth } from "../Shared/GridCard";
import { HoverPopover } from "../Shared/HoverPopover";
import { Icon } from "../Shared/Icon";
import { SceneLink } from "../Shared/TagLink";
import { TruncatedText } from "../Shared/TruncatedText";
import { FormattedMessage } from "react-intl";
import { RatingBanner } from "../Shared/RatingBanner";
import { faPlayCircle } from "@fortawesome/free-solid-svg-icons";
import ScreenUtils from "src/utils/screen";

interface IProps {
movie: GQL.MovieDataFragment;
containerWidth?: number;
sceneIndex?: number;
selecting?: boolean;
selected?: boolean;
onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void;
}

export const MovieCard: React.FC<IProps> = (props: IProps) => {
const [cardWidth, setCardWidth] = useState<number>();

useEffect(() => {
if (!props.containerWidth || ScreenUtils.isMobile()) return;

let preferredCardWidth = 250;
let fittedCardWidth = calculateCardWidth(
props.containerWidth,
preferredCardWidth!
);
setCardWidth(fittedCardWidth);
}, [props, props.containerWidth]);

function maybeRenderSceneNumber() {
if (!props.sceneIndex) return;

Expand Down Expand Up @@ -71,6 +86,7 @@ export const MovieCard: React.FC<IProps> = (props: IProps) => {
<GridCard
className="movie-card"
url={`/movies/${props.movie.id}`}
width={cardWidth}
title={props.movie.name}
linkClassName="movie-card-header"
image={
Expand Down
35 changes: 35 additions & 0 deletions ui/v2.5/src/components/Movies/MovieCardGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useRef } from "react";
import * as GQL from "src/core/generated-graphql";
import { MovieCard } from "./MovieCard";
import { useContainerDimensions } from "../Shared/GridCard";

interface IMovieCardGrid {
movies: GQL.MovieDataFragment[];
selectedIds: Set<string>;
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
}

export const MovieCardGrid: React.FC<IMovieCardGrid> = ({
movies,
selectedIds,
onSelectChange,
}) => {
const componentRef = useRef<HTMLDivElement>(null);
const { width } = useContainerDimensions(componentRef);
return (
<div className="row justify-content-center" ref={componentRef}>
{movies.map((p) => (
<MovieCard
key={p.id}
containerWidth={width}
movie={p}
selecting={selectedIds.size > 0}
selected={selectedIds.has(p.id)}
onSelectedChanged={(selected: boolean, shiftKey: boolean) =>
onSelectChange(p.id, selected, shiftKey)
}
/>
))}
</div>
);
};
20 changes: 6 additions & 14 deletions ui/v2.5/src/components/Movies/MovieList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from "../List/ItemList";
import { ExportDialog } from "../Shared/ExportDialog";
import { DeleteEntityDialog } from "../Shared/DeleteEntityDialog";
import { MovieCard } from "./MovieCard";
import { MovieCardGrid } from "./MovieCardGrid";
import { EditMoviesDialog } from "./EditMoviesDialog";

const MovieItemList = makeItemList({
Expand Down Expand Up @@ -130,19 +130,11 @@ export const MovieList: React.FC<IMovieList> = ({ filterHook, alterQuery }) => {

if (filter.displayMode === DisplayMode.Grid) {
return (
<div className="row justify-content-center">
{result.data.findMovies.movies.map((p) => (
<MovieCard
key={p.id}
movie={p}
selecting={selectedIds.size > 0}
selected={selectedIds.has(p.id)}
onSelectedChanged={(selected: boolean, shiftKey: boolean) =>
onSelectChange(p.id, selected, shiftKey)
}
/>
))}
</div>
<MovieCardGrid
movies={result.data.findMovies.movies}
selectedIds={selectedIds}
onSelectChange={onSelectChange}
/>
);
}
if (filter.displayMode === DisplayMode.List) {
Expand Down
Loading

0 comments on commit 6453e6c

Please sign in to comment.