mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add "toolbar" buttons for some list actions (#1673)
* Support adding buttons to list "toolbar" * Show add and remove images to gallery in toolbar * Show button to play selected scenes to list toolbar
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
* Added not equals/greater than/less than modifiers for resolution criteria. ([#1568](https://github.com/stashapp/stash/pull/1568))
|
* Added not equals/greater than/less than modifiers for resolution criteria. ([#1568](https://github.com/stashapp/stash/pull/1568))
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Move Play Selected Scenes, and Add/Remove Gallery Image buttons to button toolbar. ([#1673](https://github.com/stashapp/stash/pull/1673))
|
||||||
* Add image and gallery counts to tag list view. ([#1672](https://github.com/stashapp/stash/pull/1672))
|
* Add image and gallery counts to tag list view. ([#1672](https://github.com/stashapp/stash/pull/1672))
|
||||||
* Prompt when leaving gallery edit page with unsaved changes. ([#1654](https://github.com/stashapp/stash/pull/1654))
|
* Prompt when leaving gallery edit page with unsaved changes. ([#1654](https://github.com/stashapp/stash/pull/1654))
|
||||||
* Show largest duplicates first in scene duplicate checker. ([#1639](https://github.com/stashapp/stash/pull/1639))
|
* Show largest duplicates first in scene duplicate checker. ([#1639](https://github.com/stashapp/stash/pull/1639))
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { mutateAddGalleryImages } from "src/core/StashService";
|
|||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
import { TextUtils } from "src/utils";
|
import { TextUtils } from "src/utils";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
||||||
interface IGalleryAddProps {
|
interface IGalleryAddProps {
|
||||||
gallery: Partial<GQL.GalleryDataFragment>;
|
gallery: Partial<GQL.GalleryDataFragment>;
|
||||||
@@ -87,6 +88,7 @@ export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({ gallery }) => {
|
|||||||
onClick: addImages,
|
onClick: addImages,
|
||||||
isDisplayed: showWhenSelected,
|
isDisplayed: showWhenSelected,
|
||||||
postRefetch: true,
|
postRefetch: true,
|
||||||
|
icon: "plus" as IconProp,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { showWhenSelected, PersistanceLevel } from "src/hooks/ListHook";
|
|||||||
import { useToast } from "src/hooks";
|
import { useToast } from "src/hooks";
|
||||||
import { TextUtils } from "src/utils";
|
import { TextUtils } from "src/utils";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
||||||
interface IGalleryDetailsProps {
|
interface IGalleryDetailsProps {
|
||||||
gallery: GQL.GalleryDataFragment;
|
gallery: GQL.GalleryDataFragment;
|
||||||
@@ -81,6 +82,8 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
|
|||||||
onClick: removeImages,
|
onClick: removeImages,
|
||||||
isDisplayed: showWhenSelected,
|
isDisplayed: showWhenSelected,
|
||||||
postRefetch: true,
|
postRefetch: true,
|
||||||
|
icon: "minus" as IconProp,
|
||||||
|
buttonVariant: "danger",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ import {
|
|||||||
} from "react-bootstrap";
|
} from "react-bootstrap";
|
||||||
import Mousetrap from "mousetrap";
|
import Mousetrap from "mousetrap";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
import { Icon } from "../Shared";
|
import { Icon } from "../Shared";
|
||||||
|
|
||||||
interface IListFilterOperation {
|
interface IListFilterOperation {
|
||||||
text: string;
|
text: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
isDisplayed?: () => boolean;
|
isDisplayed?: () => boolean;
|
||||||
|
icon?: IconProp;
|
||||||
|
buttonVariant?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IListOperationButtonsProps {
|
interface IListOperationButtonsProps {
|
||||||
@@ -60,37 +63,53 @@ export const ListOperationButtons: React.FC<IListOperationButtonsProps> = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function maybeRenderSelectedButtons() {
|
function maybeRenderButtons() {
|
||||||
if (itemsSelected && (onEdit || onDelete)) {
|
const buttons = (otherOperations ?? []).filter((o) => {
|
||||||
|
if (!o.icon) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!o.isDisplayed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.isDisplayed();
|
||||||
|
});
|
||||||
|
if (itemsSelected) {
|
||||||
|
if (onEdit) {
|
||||||
|
buttons.push({
|
||||||
|
icon: "pencil-alt",
|
||||||
|
text: intl.formatMessage({ id: "actions.edit" }),
|
||||||
|
onClick: onEdit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (onDelete) {
|
||||||
|
buttons.push({
|
||||||
|
icon: "trash",
|
||||||
|
text: intl.formatMessage({ id: "actions.delete" }),
|
||||||
|
onClick: onDelete,
|
||||||
|
buttonVariant: "danger",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons.length > 0) {
|
||||||
return (
|
return (
|
||||||
<ButtonGroup className="ml-2 mb-1">
|
<ButtonGroup className="ml-2 mb-1">
|
||||||
{onEdit && (
|
{buttons.map((button) => {
|
||||||
|
return (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
overlay={
|
overlay={<Tooltip id="edit">{button.text}</Tooltip>}
|
||||||
<Tooltip id="edit">
|
|
||||||
{intl.formatMessage({ id: "actions.edit" })}
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Button variant="secondary" onClick={onEdit}>
|
<Button
|
||||||
<Icon icon="pencil-alt" />
|
variant={button.buttonVariant ?? "secondary"}
|
||||||
|
onClick={button.onClick}
|
||||||
|
>
|
||||||
|
<Icon icon={button.icon as IconProp} />
|
||||||
</Button>
|
</Button>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
)}
|
);
|
||||||
|
})}
|
||||||
{onDelete && (
|
|
||||||
<OverlayTrigger
|
|
||||||
overlay={
|
|
||||||
<Tooltip id="delete">
|
|
||||||
{intl.formatMessage({ id: "actions.delete" })}
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button variant="danger" onClick={onDelete}>
|
|
||||||
<Icon icon="trash" />
|
|
||||||
</Button>
|
|
||||||
</OverlayTrigger>
|
|
||||||
)}
|
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -165,7 +184,7 @@ export const ListOperationButtons: React.FC<IListOperationButtonsProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{maybeRenderSelectedButtons()}
|
{maybeRenderButtons()}
|
||||||
|
|
||||||
<div className="mx-2">{renderMore()}</div>
|
<div className="mx-2">{renderMore()}</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import _ from "lodash";
|
|||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import Mousetrap from "mousetrap";
|
import Mousetrap from "mousetrap";
|
||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
import {
|
import {
|
||||||
FindScenesQueryResult,
|
FindScenesQueryResult,
|
||||||
SlimSceneDataFragment,
|
SlimSceneDataFragment,
|
||||||
@@ -44,6 +45,7 @@ export const SceneList: React.FC<ISceneList> = ({
|
|||||||
text: intl.formatMessage({ id: "actions.play_selected" }),
|
text: intl.formatMessage({ id: "actions.play_selected" }),
|
||||||
onClick: playSelected,
|
onClick: playSelected,
|
||||||
isDisplayed: showWhenSelected,
|
isDisplayed: showWhenSelected,
|
||||||
|
icon: "play" as IconProp,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: intl.formatMessage({ id: "actions.play_random" }),
|
text: intl.formatMessage({ id: "actions.play_random" }),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import React, {
|
|||||||
import { ApolloError } from "@apollo/client";
|
import { ApolloError } from "@apollo/client";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
import Mousetrap from "mousetrap";
|
import Mousetrap from "mousetrap";
|
||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
import {
|
import {
|
||||||
SlimSceneDataFragment,
|
SlimSceneDataFragment,
|
||||||
SceneMarkerDataFragment,
|
SceneMarkerDataFragment,
|
||||||
@@ -96,6 +97,8 @@ export interface IListHookOperation<T> {
|
|||||||
selectedIds: Set<string>
|
selectedIds: Set<string>
|
||||||
) => boolean;
|
) => boolean;
|
||||||
postRefetch?: boolean;
|
postRefetch?: boolean;
|
||||||
|
icon?: IconProp;
|
||||||
|
buttonVariant?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PersistanceLevel {
|
export enum PersistanceLevel {
|
||||||
@@ -357,6 +360,8 @@ const RenderList = <
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
icon: o.icon,
|
||||||
|
buttonVariant: o.buttonVariant,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function onEdit() {
|
function onEdit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user