diff --git a/ui/v2.5/src/components/Changelog/versions/v080.md b/ui/v2.5/src/components/Changelog/versions/v080.md index dffaadd0f..fffc6df71 100644 --- a/ui/v2.5/src/components/Changelog/versions/v080.md +++ b/ui/v2.5/src/components/Changelog/versions/v080.md @@ -8,7 +8,7 @@ * Added [DLNA server](/settings?tab=dlna). ([#1364](https://github.com/stashapp/stash/pull/1364)) ### 🎨 Improvements -* Prompt when leaving scene edit page with unsaved changed. ([#1429](https://github.com/stashapp/stash/pull/1429)) +* Prompt when leaving scene edit page with unsaved changes. ([#1429](https://github.com/stashapp/stash/pull/1429)) * Make multi-set mode buttons more obvious in multi-edit dialog. ([#1435](https://github.com/stashapp/stash/pull/1435)) * Filter modifiers and sort by options are now sorted alphabetically. ([#1406](https://github.com/stashapp/stash/pull/1406)) * Add `CreatedAt` and `UpdatedAt` (and `FileModTime` where applicable) to API objects. ([#1421](https://github.com/stashapp/stash/pull/1421)) @@ -18,6 +18,7 @@ * Add button to remove studio stash ID. ([#1378](https://github.com/stashapp/stash/pull/1378)) ### 🐛 Bug fixes +* Fix click/drag to select scenes. ([#1476](https://github.com/stashapp/stash/pull/1476)) * Fix clearing Performer and Movie ratings not working. ([#1429](https://github.com/stashapp/stash/pull/1429)) * Fix scraper date parser failing when parsing time. ([#1431](https://github.com/stashapp/stash/pull/1431)) * Fix quotes in filter labels causing UI errors. ([#1425](https://github.com/stashapp/stash/pull/1425)) diff --git a/ui/v2.5/src/components/Galleries/GalleryCard.tsx b/ui/v2.5/src/components/Galleries/GalleryCard.tsx index 0a1f8c458..6d1d1dec9 100644 --- a/ui/v2.5/src/components/Galleries/GalleryCard.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryCard.tsx @@ -4,13 +4,7 @@ import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; import { FormattedPlural } from "react-intl"; import { useConfiguration } from "src/core/StashService"; -import { - BasicCard, - HoverPopover, - Icon, - TagLink, - TruncatedText, -} from "src/components/Shared"; +import { GridCard, HoverPopover, Icon, TagLink } from "src/components/Shared"; import { TextUtils } from "src/utils"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; @@ -136,9 +130,14 @@ export const GalleryCard: React.FC = (props) => { } return ( - @@ -155,18 +154,6 @@ export const GalleryCard: React.FC = (props) => { overlays={maybeRenderSceneStudioOverlay()} details={ <> - -
- -
- {props.gallery.image_count}  = ( } } - function handleImageClick( - event: React.MouseEvent - ) { - const { shiftKey } = event; - - if (props.selecting) { - props.onSelectedChanged(!props.selected, shiftKey); - event.preventDefault(); - } - } - - function handleDrag(event: React.DragEvent) { - if (props.selecting) { - event.dataTransfer.setData("text/plain", ""); - event.dataTransfer.setDragImage(new Image(), 0, 0); - } - } - - function handleDragOver(event: React.DragEvent) { - const ev = event; - const shiftKey = false; - - if (props.selecting && !props.selected) { - props.onSelectedChanged(true, shiftKey); - } - - ev.dataTransfer.dropEffect = "move"; - ev.preventDefault(); - } - function isPortrait() { const { file } = props.image; const width = file.width ? file.width : 0; @@ -147,31 +111,18 @@ export const ImageCard: React.FC = ( return height > width; } - let shiftKey = false; - return ( - - props.onSelectedChanged(!props.selected, shiftKey)} - onClick={(event: React.MouseEvent) => { - // eslint-disable-next-line prefer-destructuring - shiftKey = event.shiftKey; - event.stopPropagation(); - }} - /> - -
- +
= ( />
{maybeRenderRatingBanner()} - -
-
-
- -
-
- - {maybeRenderPopoverButtonGroup()} -
+ + } + popovers={maybeRenderPopoverButtonGroup()} + selected={props.selected} + selecting={props.selecting} + onSelectedChanged={props.onSelectedChanged} + /> ); }; diff --git a/ui/v2.5/src/components/Images/styles.scss b/ui/v2.5/src/components/Images/styles.scss index fdbfd78cd..b05223cc8 100644 --- a/ui/v2.5/src/components/Images/styles.scss +++ b/ui/v2.5/src/components/Images/styles.scss @@ -19,21 +19,6 @@ padding: 0; } - &-check { - left: 0.5rem; - margin-top: -12px; - opacity: 0; - padding-left: 15px; - position: absolute; - top: 0.7rem; - width: 1.2rem; - z-index: 1; - - &:checked { - opacity: 0.75; - } - } - .rating-banner { transition: opacity 0.5s; } @@ -63,11 +48,6 @@ opacity: 0; transition: opacity 0.5s; } - - .image-card-check { - opacity: 0.75; - transition: opacity 0.5s; - } } } diff --git a/ui/v2.5/src/components/Movies/MovieCard.tsx b/ui/v2.5/src/components/Movies/MovieCard.tsx index 0e616fdc3..919da36da 100644 --- a/ui/v2.5/src/components/Movies/MovieCard.tsx +++ b/ui/v2.5/src/components/Movies/MovieCard.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from "react"; import { FormattedPlural } from "react-intl"; import * as GQL from "src/core/generated-graphql"; -import { BasicCard, TruncatedText } from "src/components/Shared"; +import { GridCard } from "src/components/Shared"; interface IProps { movie: GQL.MovieDataFragment; @@ -45,9 +45,10 @@ export const MovieCard: FunctionComponent = (props: IProps) => { } return ( - @@ -59,14 +60,7 @@ export const MovieCard: FunctionComponent = (props: IProps) => { {maybeRenderRatingBanner()} } - details={ - <> -
- -
- {maybeRenderSceneNumber()} - - } + details={maybeRenderSceneNumber()} selected={props.selected} selecting={props.selecting} onSelectedChanged={props.onSelectedChanged} diff --git a/ui/v2.5/src/components/Performers/PerformerCard.tsx b/ui/v2.5/src/components/Performers/PerformerCard.tsx index ba57ca085..fd9e961a2 100644 --- a/ui/v2.5/src/components/Performers/PerformerCard.tsx +++ b/ui/v2.5/src/components/Performers/PerformerCard.tsx @@ -3,12 +3,11 @@ import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; import { NavUtils, TextUtils } from "src/utils"; import { - BasicCard, + GridCard, CountryFlag, HoverPopover, Icon, TagLink, - TruncatedText, } from "src/components/Shared"; import { Button, ButtonGroup } from "react-bootstrap"; import { @@ -150,9 +149,10 @@ export const PerformerCard: React.FC = ({ } return ( - = ({ } details={ <> -
- -
{age !== 0 ?
{ageString}
: ""} diff --git a/ui/v2.5/src/components/Scenes/SceneCard.tsx b/ui/v2.5/src/components/Scenes/SceneCard.tsx index 338fe54f2..21b9104b9 100644 --- a/ui/v2.5/src/components/Scenes/SceneCard.tsx +++ b/ui/v2.5/src/components/Scenes/SceneCard.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { Button, ButtonGroup, Card, Form } from "react-bootstrap"; +import { Button, ButtonGroup } from "react-bootstrap"; import { Link } from "react-router-dom"; import cx from "classnames"; import * as GQL from "src/core/generated-graphql"; @@ -14,6 +14,7 @@ import { import { TextUtils } from "src/utils"; import { SceneQueue } from "src/models/sceneQueue"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; +import { GridCard } from "../Shared/GridCard"; interface IScenePreviewProps { isPortrait: boolean; @@ -294,36 +295,6 @@ export const SceneCard: React.FC = ( } } - function handleSceneClick( - event: React.MouseEvent - ) { - const { shiftKey } = event; - - if (props.selecting && props.onSelectedChanged) { - props.onSelectedChanged(!props.selected, shiftKey); - event.preventDefault(); - } - } - - function handleDrag(event: React.DragEvent) { - if (props.selecting) { - event.dataTransfer.setData("text/plain", ""); - event.dataTransfer.setDragImage(new Image(), 0, 0); - } - } - - function handleDragOver(event: React.DragEvent) { - const ev = event; - const shiftKey = false; - - if (props.selecting && props.onSelectedChanged && !props.selected) { - props.onSelectedChanged(true, shiftKey); - } - - ev.dataTransfer.dropEffect = "move"; - ev.preventDefault(); - } - function isPortrait() { const { file } = props.scene; const width = file.width ? file.width : 0; @@ -337,35 +308,23 @@ export const SceneCard: React.FC = ( } } - let shiftKey = false; - const sceneLink = props.queue ? props.queue.makeLink(props.scene.id, { sceneIndex: props.index }) : `/scenes/${props.scene.id}`; return ( - - props.onSelectedChanged?.(!props.selected, shiftKey)} - onClick={(event: React.MouseEvent) => { - // eslint-disable-next-line prefer-destructuring - shiftKey = event.shiftKey; - event.stopPropagation(); - }} - /> - -
- + = ( /> {maybeRenderRatingBanner()} {maybeRenderSceneSpecsOverlay()} - - {maybeRenderSceneStudioOverlay()} -
-
-
- - - -
- {props.scene.date} -

- -

-
- - {maybeRenderPopoverButtonGroup()} -
+ + } + overlays={maybeRenderSceneStudioOverlay()} + details={ + <> + {props.scene.date} +

+ +

+ + } + popovers={maybeRenderPopoverButtonGroup()} + selected={props.selected} + selecting={props.selecting} + onSelectedChanged={props.onSelectedChanged} + /> ); }; diff --git a/ui/v2.5/src/components/Scenes/styles.scss b/ui/v2.5/src/components/Scenes/styles.scss index 0fa948a48..b0c500310 100644 --- a/ui/v2.5/src/components/Scenes/styles.scss +++ b/ui/v2.5/src/components/Scenes/styles.scss @@ -164,21 +164,6 @@ textarea.scene-description { text-decoration: none; } - &-check { - left: 0.5rem; - margin-top: -12px; - opacity: 0; - padding-left: 15px; - position: absolute; - top: 0.7rem; - width: 1.2rem; - z-index: 1; - - &:checked { - opacity: 0.75; - } - } - .scene-specs-overlay, .rating-banner, .scene-studio-overlay { diff --git a/ui/v2.5/src/components/Shared/BasicCard.tsx b/ui/v2.5/src/components/Shared/GridCard.tsx similarity index 65% rename from ui/v2.5/src/components/Shared/BasicCard.tsx rename to ui/v2.5/src/components/Shared/GridCard.tsx index a4662281b..4c0320620 100644 --- a/ui/v2.5/src/components/Shared/BasicCard.tsx +++ b/ui/v2.5/src/components/Shared/GridCard.tsx @@ -1,13 +1,16 @@ import React from "react"; import { Card, Form } from "react-bootstrap"; import { Link } from "react-router-dom"; +import TruncatedText from "./TruncatedText"; -interface IBasicCardProps { +interface ICardProps { className?: string; linkClassName?: string; + thumbnailSectionClassName?: string; url: string; + title: string; image: JSX.Element; - details: JSX.Element; + details?: JSX.Element; overlays?: JSX.Element; popovers?: JSX.Element; selecting?: boolean; @@ -15,12 +18,8 @@ interface IBasicCardProps { onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void; } -export const BasicCard: React.FC = ( - props: IBasicCardProps -) => { - function handleImageClick( - event: React.MouseEvent - ) { +export const GridCard: React.FC = (props: ICardProps) => { + function handleImageClick(event: React.MouseEvent) { const { shiftKey } = event; if (!props.onSelectedChanged) { @@ -33,14 +32,14 @@ export const BasicCard: React.FC = ( } } - function handleDrag(event: React.DragEvent) { + function handleDrag(event: React.DragEvent) { if (props.selecting) { event.dataTransfer.setData("text/plain", ""); event.dataTransfer.setDragImage(new Image(), 0, 0); } } - function handleDragOver(event: React.DragEvent) { + function handleDragOver(event: React.DragEvent) { const ev = event; const shiftKey = false; @@ -77,23 +76,33 @@ export const BasicCard: React.FC = ( } return ( - + {maybeRenderCheckbox()} -
+
{props.image} {props.overlays}
-
{props.details}
+
+ +
+ +
+ + {props.details} +
{props.popovers} diff --git a/ui/v2.5/src/components/Shared/index.ts b/ui/v2.5/src/components/Shared/index.ts index 3a711091a..834d1d3e8 100644 --- a/ui/v2.5/src/components/Shared/index.ts +++ b/ui/v2.5/src/components/Shared/index.ts @@ -13,7 +13,7 @@ export { default as CountryFlag } from "./CountryFlag"; export { default as SuccessIcon } from "./SuccessIcon"; export { default as ErrorMessage } from "./ErrorMessage"; export { default as TruncatedText } from "./TruncatedText"; -export { BasicCard } from "./BasicCard"; +export { GridCard } from "./GridCard"; export { RatingStars } from "./RatingStars"; export { ExportDialog } from "./ExportDialog"; export { default as DeleteEntityDialog } from "./DeleteEntityDialog"; diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index 2e1efc0ff..e507b9d11 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -151,7 +151,12 @@ button.collapse-button.btn-primary:not(:disabled):not(.disabled):active { } } -.card { +.grid-card { + a .card-section-title { + color: $text-color; + text-decoration: none; + } + .card-check { left: 0.5rem; margin-top: -12px; diff --git a/ui/v2.5/src/components/Studios/StudioCard.tsx b/ui/v2.5/src/components/Studios/StudioCard.tsx index 6a36b9c9f..4473c94aa 100644 --- a/ui/v2.5/src/components/Studios/StudioCard.tsx +++ b/ui/v2.5/src/components/Studios/StudioCard.tsx @@ -2,7 +2,7 @@ import React from "react"; import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; import { NavUtils } from "src/utils"; -import { BasicCard, TruncatedText } from "src/components/Shared"; +import { GridCard } from "src/components/Shared"; import { ButtonGroup } from "react-bootstrap"; import { PopoverCountButton } from "../Shared/PopoverCountButton"; @@ -119,9 +119,10 @@ export const StudioCard: React.FC = ({ } return ( - = ({ } details={ <> -
- -
{maybeRenderParent(studio, hideParent)} {maybeRenderChildren(studio)} {maybeRenderRatingBanner(studio)} diff --git a/ui/v2.5/src/components/Tags/TagCard.tsx b/ui/v2.5/src/components/Tags/TagCard.tsx index 8d2561389..e03efb309 100644 --- a/ui/v2.5/src/components/Tags/TagCard.tsx +++ b/ui/v2.5/src/components/Tags/TagCard.tsx @@ -3,8 +3,8 @@ import React from "react"; import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; import { NavUtils } from "src/utils"; -import { Icon, TruncatedText } from "../Shared"; -import { BasicCard } from "../Shared/BasicCard"; +import { Icon } from "../Shared"; +import { GridCard } from "../Shared/GridCard"; import { PopoverCountButton } from "../Shared/PopoverCountButton"; interface IProps { @@ -102,9 +102,10 @@ export const TagCard: React.FC = ({ } return ( - = ({ src={tag.image_path ?? ""} /> } - details={ -
- -
- } popovers={maybeRenderPopoverButtonGroup()} selected={selected} selecting={selecting}