mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
This reverts commit 21ee88b149.
This commit is contained in:
parent
21ee88b149
commit
14be3c24ff
7 changed files with 37 additions and 103 deletions
|
|
@ -9,37 +9,31 @@ import { Badge, BadgeProps, Button, Overlay, Popover } from "react-bootstrap";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { Icon } from "../Shared/Icon";
|
import { Icon } from "../Shared/Icon";
|
||||||
import { faMagnifyingGlass, faTimes } from "@fortawesome/free-solid-svg-icons";
|
import { faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { BsPrefixProps, ReplaceProps } from "react-bootstrap/esm/helpers";
|
import { BsPrefixProps, ReplaceProps } from "react-bootstrap/esm/helpers";
|
||||||
import { CustomFieldsCriterion } from "src/models/list-filter/criteria/custom-fields";
|
import { CustomFieldsCriterion } from "src/models/list-filter/criteria/custom-fields";
|
||||||
import { useDebounce } from "src/hooks/debounce";
|
import { useDebounce } from "src/hooks/debounce";
|
||||||
import cx from "classnames";
|
|
||||||
|
|
||||||
type TagItemProps = PropsWithChildren<
|
type TagItemProps = PropsWithChildren<
|
||||||
ReplaceProps<"span", BsPrefixProps<"span"> & BadgeProps>
|
ReplaceProps<"span", BsPrefixProps<"span"> & BadgeProps>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const TagItem: React.FC<TagItemProps> = (props) => {
|
export const TagItem: React.FC<TagItemProps> = (props) => {
|
||||||
const { className, children, ...others } = props;
|
const { children } = props;
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge className="tag-item" variant="secondary" {...props}>
|
||||||
className={cx("tag-item", className)}
|
|
||||||
variant="secondary"
|
|
||||||
{...others}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterTag: React.FC<{
|
export const FilterTag: React.FC<{
|
||||||
className?: string;
|
|
||||||
label: React.ReactNode;
|
label: React.ReactNode;
|
||||||
onClick: React.MouseEventHandler<HTMLSpanElement>;
|
onClick: React.MouseEventHandler<HTMLSpanElement>;
|
||||||
onRemove: React.MouseEventHandler<HTMLElement>;
|
onRemove: React.MouseEventHandler<HTMLElement>;
|
||||||
}> = ({ className, label, onClick, onRemove }) => {
|
}> = ({ label, onClick, onRemove }) => {
|
||||||
return (
|
return (
|
||||||
<TagItem className={className} onClick={onClick}>
|
<TagItem onClick={onClick}>
|
||||||
{label}
|
{label}
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
|
@ -102,24 +96,18 @@ const MoreFilterTags: React.FC<{
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IFilterTagsProps {
|
interface IFilterTagsProps {
|
||||||
searchTerm?: string;
|
|
||||||
criteria: Criterion[];
|
criteria: Criterion[];
|
||||||
onEditSearchTerm?: () => void;
|
|
||||||
onEditCriterion: (c: Criterion) => void;
|
onEditCriterion: (c: Criterion) => void;
|
||||||
onRemoveCriterion: (c: Criterion, valueIndex?: number) => void;
|
onRemoveCriterion: (c: Criterion, valueIndex?: number) => void;
|
||||||
onRemoveAll: () => void;
|
onRemoveAll: () => void;
|
||||||
onRemoveSearchTerm?: () => void;
|
|
||||||
truncateOnOverflow?: boolean;
|
truncateOnOverflow?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterTags: React.FC<IFilterTagsProps> = ({
|
export const FilterTags: React.FC<IFilterTagsProps> = ({
|
||||||
searchTerm,
|
|
||||||
criteria,
|
criteria,
|
||||||
onEditCriterion,
|
onEditCriterion,
|
||||||
onRemoveCriterion,
|
onRemoveCriterion,
|
||||||
onRemoveAll,
|
onRemoveAll,
|
||||||
onEditSearchTerm,
|
|
||||||
onRemoveSearchTerm,
|
|
||||||
truncateOnOverflow = false,
|
truncateOnOverflow = false,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
@ -277,7 +265,7 @@ export const FilterTags: React.FC<IFilterTagsProps> = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (criteria.length === 0 && !searchTerm) {
|
if (criteria.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,31 +273,31 @@ export const FilterTags: React.FC<IFilterTagsProps> = ({
|
||||||
|
|
||||||
const filterTags = criteria.map((c) => getFilterTags(c)).flat();
|
const filterTags = criteria.map((c) => getFilterTags(c)).flat();
|
||||||
|
|
||||||
if (searchTerm && searchTerm.length > 0) {
|
if (cutoff && filterTags.length > cutoff) {
|
||||||
filterTags.unshift(
|
const visibleCriteria = filterTags.slice(0, cutoff);
|
||||||
<FilterTag
|
const hiddenCriteria = filterTags.slice(cutoff);
|
||||||
key="search-term"
|
|
||||||
className="search-term-filter-tag"
|
|
||||||
label={
|
|
||||||
<span className="search-term">
|
|
||||||
<Icon icon={faMagnifyingGlass} />
|
|
||||||
{searchTerm}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
onClick={() => onEditSearchTerm?.()}
|
|
||||||
onRemove={() => onRemoveSearchTerm?.()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const visibleCriteria = cutoff ? filterTags.slice(0, cutoff) : filterTags;
|
|
||||||
const hiddenCriteria = cutoff ? filterTags.slice(cutoff) : [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} ref={ref}>
|
<div className={className} ref={ref}>
|
||||||
{visibleCriteria}
|
{visibleCriteria}
|
||||||
<MoreFilterTags tags={hiddenCriteria} />
|
<MoreFilterTags tags={hiddenCriteria} />
|
||||||
{filterTags.length >= 3 && (
|
{criteria.length >= 3 && (
|
||||||
|
<Button
|
||||||
|
variant="minimal"
|
||||||
|
className="clear-all-button"
|
||||||
|
onClick={() => onRemoveAll()}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="actions.clear" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} ref={ref}>
|
||||||
|
{filterTags}
|
||||||
|
{criteria.length >= 3 && (
|
||||||
<Button
|
<Button
|
||||||
variant="minimal"
|
variant="minimal"
|
||||||
className="clear-all-button"
|
className="clear-all-button"
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,8 @@ export const FilteredSidebarHeader: React.FC<{
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
setFilter: (filter: ListFilterModel) => void;
|
setFilter: (filter: ListFilterModel) => void;
|
||||||
view?: View;
|
view?: View;
|
||||||
focus?: ReturnType<typeof useFocus>;
|
}> = ({ sidebarOpen, showEditFilter, filter, setFilter, view }) => {
|
||||||
}> = ({
|
const focus = useFocus();
|
||||||
sidebarOpen,
|
|
||||||
showEditFilter,
|
|
||||||
filter,
|
|
||||||
setFilter,
|
|
||||||
view,
|
|
||||||
focus: providedFocus,
|
|
||||||
}) => {
|
|
||||||
const localFocus = useFocus();
|
|
||||||
const focus = providedFocus ?? localFocus;
|
|
||||||
const [, setFocus] = focus;
|
const [, setFocus] = focus;
|
||||||
|
|
||||||
// Set the focus on the input field when the sidebar is opened
|
// Set the focus on the input field when the sidebar is opened
|
||||||
|
|
|
||||||
|
|
@ -196,12 +196,9 @@ export function useFilterOperations(props: {
|
||||||
[setFilter]
|
[setFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
const clearAllCriteria = useCallback(
|
const clearAllCriteria = useCallback(() => {
|
||||||
(includeSearchTerm = false) => {
|
setFilter((cv) => cv.clearCriteria());
|
||||||
setFilter((cv) => cv.clearCriteria(includeSearchTerm));
|
}, [setFilter]);
|
||||||
},
|
|
||||||
[setFilter]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setPage,
|
setPage,
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ import { Icon } from "../Shared/Icon";
|
||||||
import { ListViewOptions } from "../List/ListViewOptions";
|
import { ListViewOptions } from "../List/ListViewOptions";
|
||||||
import { PageSizeSelector, SortBySelect } from "../List/ListFilter";
|
import { PageSizeSelector, SortBySelect } from "../List/ListFilter";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
||||||
import useFocus from "src/utils/focus";
|
|
||||||
|
|
||||||
function renderMetadataByline(result: GQL.FindScenesQueryResult) {
|
function renderMetadataByline(result: GQL.FindScenesQueryResult) {
|
||||||
const duration = result?.data?.findScenes?.duration;
|
const duration = result?.data?.findScenes?.duration;
|
||||||
|
|
@ -242,7 +241,6 @@ const SidebarContent: React.FC<{
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
showEditFilter: (editingCriterion?: string) => void;
|
showEditFilter: (editingCriterion?: string) => void;
|
||||||
count?: number;
|
count?: number;
|
||||||
focus?: ReturnType<typeof useFocus>;
|
|
||||||
}> = ({
|
}> = ({
|
||||||
filter,
|
filter,
|
||||||
setFilter,
|
setFilter,
|
||||||
|
|
@ -252,7 +250,6 @@ const SidebarContent: React.FC<{
|
||||||
sidebarOpen,
|
sidebarOpen,
|
||||||
onClose,
|
onClose,
|
||||||
count,
|
count,
|
||||||
focus,
|
|
||||||
}) => {
|
}) => {
|
||||||
const showResultsId =
|
const showResultsId =
|
||||||
count !== undefined ? "actions.show_count_results" : "actions.show_results";
|
count !== undefined ? "actions.show_count_results" : "actions.show_results";
|
||||||
|
|
@ -267,7 +264,6 @@ const SidebarContent: React.FC<{
|
||||||
filter={filter}
|
filter={filter}
|
||||||
setFilter={setFilter}
|
setFilter={setFilter}
|
||||||
view={view}
|
view={view}
|
||||||
focus={focus}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScenesFilterSidebarSections>
|
<ScenesFilterSidebarSections>
|
||||||
|
|
@ -330,7 +326,6 @@ interface IOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListToolbarContent: React.FC<{
|
const ListToolbarContent: React.FC<{
|
||||||
searchTerm: string;
|
|
||||||
criteria: Criterion[];
|
criteria: Criterion[];
|
||||||
items: GQL.SlimSceneDataFragment[];
|
items: GQL.SlimSceneDataFragment[];
|
||||||
selectedIds: Set<string>;
|
selectedIds: Set<string>;
|
||||||
|
|
@ -339,8 +334,6 @@ const ListToolbarContent: React.FC<{
|
||||||
onEditCriterion: (c: Criterion) => void;
|
onEditCriterion: (c: Criterion) => void;
|
||||||
onRemoveCriterion: (criterion: Criterion, valueIndex?: number) => void;
|
onRemoveCriterion: (criterion: Criterion, valueIndex?: number) => void;
|
||||||
onRemoveAllCriterion: () => void;
|
onRemoveAllCriterion: () => void;
|
||||||
onEditSearchTerm: () => void;
|
|
||||||
onRemoveSearchTerm: () => void;
|
|
||||||
onSelectAll: () => void;
|
onSelectAll: () => void;
|
||||||
onSelectNone: () => void;
|
onSelectNone: () => void;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
|
|
@ -348,7 +341,6 @@ const ListToolbarContent: React.FC<{
|
||||||
onPlay: () => void;
|
onPlay: () => void;
|
||||||
onCreateNew: () => void;
|
onCreateNew: () => void;
|
||||||
}> = ({
|
}> = ({
|
||||||
searchTerm,
|
|
||||||
criteria,
|
criteria,
|
||||||
items,
|
items,
|
||||||
selectedIds,
|
selectedIds,
|
||||||
|
|
@ -357,8 +349,6 @@ const ListToolbarContent: React.FC<{
|
||||||
onEditCriterion,
|
onEditCriterion,
|
||||||
onRemoveCriterion,
|
onRemoveCriterion,
|
||||||
onRemoveAllCriterion,
|
onRemoveAllCriterion,
|
||||||
onEditSearchTerm,
|
|
||||||
onRemoveSearchTerm,
|
|
||||||
onSelectAll,
|
onSelectAll,
|
||||||
onSelectNone,
|
onSelectNone,
|
||||||
onEdit,
|
onEdit,
|
||||||
|
|
@ -380,13 +370,10 @@ const ListToolbarContent: React.FC<{
|
||||||
title={intl.formatMessage({ id: "actions.sidebar.toggle" })}
|
title={intl.formatMessage({ id: "actions.sidebar.toggle" })}
|
||||||
/>
|
/>
|
||||||
<FilterTags
|
<FilterTags
|
||||||
searchTerm={searchTerm}
|
|
||||||
criteria={criteria}
|
criteria={criteria}
|
||||||
onEditCriterion={onEditCriterion}
|
onEditCriterion={onEditCriterion}
|
||||||
onRemoveCriterion={onRemoveCriterion}
|
onRemoveCriterion={onRemoveCriterion}
|
||||||
onRemoveAll={onRemoveAllCriterion}
|
onRemoveAll={onRemoveAllCriterion}
|
||||||
onEditSearchTerm={onEditSearchTerm}
|
|
||||||
onRemoveSearchTerm={onRemoveSearchTerm}
|
|
||||||
truncateOnOverflow
|
truncateOnOverflow
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -533,9 +520,6 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const searchFocus = useFocus();
|
|
||||||
const [, setSearchFocus] = searchFocus;
|
|
||||||
|
|
||||||
const { filterHook, defaultSort, view, alterQuery, fromGroupId } = props;
|
const { filterHook, defaultSort, view, alterQuery, fromGroupId } = props;
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
|
@ -790,7 +774,6 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
sidebarOpen={showSidebar}
|
sidebarOpen={showSidebar}
|
||||||
onClose={() => setShowSidebar(false)}
|
onClose={() => setShowSidebar(false)}
|
||||||
count={cachedResult.loading ? undefined : totalCount}
|
count={cachedResult.loading ? undefined : totalCount}
|
||||||
focus={searchFocus}
|
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -800,7 +783,6 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ListToolbarContent
|
<ListToolbarContent
|
||||||
searchTerm={filter.searchTerm}
|
|
||||||
criteria={filter.criteria}
|
criteria={filter.criteria}
|
||||||
items={items}
|
items={items}
|
||||||
selectedIds={selectedIds}
|
selectedIds={selectedIds}
|
||||||
|
|
@ -808,12 +790,7 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
onToggleSidebar={() => setShowSidebar(!showSidebar)}
|
onToggleSidebar={() => setShowSidebar(!showSidebar)}
|
||||||
onEditCriterion={(c) => showEditFilter(c.criterionOption.type)}
|
onEditCriterion={(c) => showEditFilter(c.criterionOption.type)}
|
||||||
onRemoveCriterion={removeCriterion}
|
onRemoveCriterion={removeCriterion}
|
||||||
onRemoveAllCriterion={() => clearAllCriteria(true)}
|
onRemoveAllCriterion={() => clearAllCriteria()}
|
||||||
onEditSearchTerm={() => {
|
|
||||||
setShowSidebar(true);
|
|
||||||
setSearchFocus(true);
|
|
||||||
}}
|
|
||||||
onRemoveSearchTerm={() => setFilter(filter.clearSearchTerm())}
|
|
||||||
onSelectAll={() => onSelectAll()}
|
onSelectAll={() => onSelectAll()}
|
||||||
onSelectNone={() => onSelectNone()}
|
onSelectNone={() => onSelectNone()}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
|
|
|
||||||
|
|
@ -698,10 +698,8 @@ div.dropdown-menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-item {
|
.tag-item {
|
||||||
align-items: center;
|
|
||||||
background-color: $muted-gray;
|
background-color: $muted-gray;
|
||||||
color: $dark-text;
|
color: $dark-text;
|
||||||
display: flex;
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
|
|
@ -712,20 +710,17 @@ div.dropdown-menu {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-term svg {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
bottom: 2px;
|
bottom: 2px;
|
||||||
color: $dark-text;
|
color: $dark-text;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 16px;
|
line-height: 1rem;
|
||||||
margin-right: -0.5rem;
|
margin-right: -0.5rem;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
||||||
|
|
@ -476,23 +476,13 @@ export class ListFilterModel {
|
||||||
return this.setCriteria(criteria);
|
return this.setCriteria(criteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearCriteria(clearSearchTerm = false) {
|
public clearCriteria() {
|
||||||
const ret = this.clone();
|
const ret = this.clone();
|
||||||
if (clearSearchTerm) {
|
|
||||||
ret.searchTerm = "";
|
|
||||||
}
|
|
||||||
ret.criteria = [];
|
ret.criteria = [];
|
||||||
ret.currentPage = 1;
|
ret.currentPage = 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearSearchTerm() {
|
|
||||||
const ret = this.clone();
|
|
||||||
ret.searchTerm = "";
|
|
||||||
ret.currentPage = 1; // reset to first page
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setCriteria(criteria: Criterion[]) {
|
public setCriteria(criteria: Criterion[]) {
|
||||||
const ret = this.clone();
|
const ret = this.clone();
|
||||||
ret.criteria = criteria;
|
ret.criteria = criteria;
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,11 @@ import { useRef, useEffect, useCallback } from "react";
|
||||||
|
|
||||||
const useFocus = () => {
|
const useFocus = () => {
|
||||||
const htmlElRef = useRef<HTMLInputElement | null>(null);
|
const htmlElRef = useRef<HTMLInputElement | null>(null);
|
||||||
const setFocus = useCallback((selectAll?: boolean) => {
|
const setFocus = useCallback(() => {
|
||||||
const currentEl = htmlElRef.current;
|
const currentEl = htmlElRef.current;
|
||||||
if (currentEl) {
|
if (currentEl) {
|
||||||
if (selectAll) {
|
|
||||||
currentEl.select();
|
|
||||||
} else {
|
|
||||||
currentEl.focus();
|
currentEl.focus();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue