diff --git a/ui/v2.5/src/components/List/ListOperationButtons.tsx b/ui/v2.5/src/components/List/ListOperationButtons.tsx
index 6bb31339a..bdb87fa3f 100644
--- a/ui/v2.5/src/components/List/ListOperationButtons.tsx
+++ b/ui/v2.5/src/components/List/ListOperationButtons.tsx
@@ -16,22 +16,28 @@ import {
faTrash,
} from "@fortawesome/free-solid-svg-icons";
import cx from "classnames";
+import { createPortal } from "react-dom";
export const OperationDropdown: React.FC<
PropsWithChildren<{
className?: string;
+ menuPortalTarget?: HTMLElement;
}>
-> = ({ className, children }) => {
+> = ({ className, menuPortalTarget, children }) => {
if (!children) return null;
+ const menu = (
+
+ {children}
+
+ );
+
return (
-
- {children}
-
+ {menuPortalTarget ? createPortal(menu, menuPortalTarget) : menu}
);
};
diff --git a/ui/v2.5/src/components/List/ListResultsHeader.tsx b/ui/v2.5/src/components/List/ListResultsHeader.tsx
index 091317ec8..a2583c2e1 100644
--- a/ui/v2.5/src/components/List/ListResultsHeader.tsx
+++ b/ui/v2.5/src/components/List/ListResultsHeader.tsx
@@ -23,15 +23,6 @@ export const ListResultsHeader: React.FC<{
}) => {
return (
-
onChangeFilter(filter.setZoom(zoom))}
/>
+
+
);
};
diff --git a/ui/v2.5/src/components/List/ListToolbar.tsx b/ui/v2.5/src/components/List/ListToolbar.tsx
index 31ef7f7ee..25ee281c0 100644
--- a/ui/v2.5/src/components/List/ListToolbar.tsx
+++ b/ui/v2.5/src/components/List/ListToolbar.tsx
@@ -4,13 +4,15 @@ import { ListFilterModel } from "src/models/list-filter/filter";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { FilterTags } from "../List/FilterTags";
import cx from "classnames";
-import { Button, ButtonToolbar } from "react-bootstrap";
+import { Button, ButtonGroup, ButtonToolbar } from "react-bootstrap";
import { FilterButton } from "../List/Filters/FilterButton";
import { Icon } from "../Shared/Icon";
import { SearchTermInput } from "../List/ListFilter";
import { Criterion } from "src/models/list-filter/criteria/criterion";
import { SidebarToggleButton } from "../Shared/Sidebar";
import { PatchComponent } from "src/patch";
+import { SavedFilterDropdown } from "./SavedFilterList";
+import { View } from "./views";
export const ToolbarFilterSection: React.FC<{
filter: ListFilterModel;
@@ -21,6 +23,7 @@ export const ToolbarFilterSection: React.FC<{
onRemoveAllCriterion: () => void;
onEditSearchTerm: () => void;
onRemoveSearchTerm: () => void;
+ view?: View;
}> = PatchComponent(
"ToolbarFilterSection",
({
@@ -32,6 +35,7 @@ export const ToolbarFilterSection: React.FC<{
onRemoveAllCriterion,
onEditSearchTerm,
onRemoveSearchTerm,
+ view,
}) => {
const { criteria, searchTerm } = filter;
@@ -41,10 +45,19 @@ export const ToolbarFilterSection: React.FC<{
- onEditCriterion()}
- count={criteria.length}
- />
+
+
+
+ onEditCriterion()}
+ count={criteria.length}
+ />
+
-
>
);
@@ -65,28 +77,33 @@ export const ToolbarFilterSection: React.FC<{
export const ToolbarSelectionSection: React.FC<{
selected: number;
onToggleSidebar: () => void;
+ operations?: React.ReactNode;
onSelectAll: () => void;
onSelectNone: () => void;
}> = PatchComponent(
"ToolbarSelectionSection",
- ({ selected, onToggleSidebar, onSelectAll, onSelectNone }) => {
+ ({ selected, onToggleSidebar, operations, onSelectAll, onSelectNone }) => {
const intl = useIntl();
return (
-
-
-
{selected} selected
-
-
+
+
+
+
+ {selected} selected
+
+
+ {operations}
+
);
}
@@ -114,7 +131,11 @@ export const FilteredListToolbar2: React.FC<{
})}
>
{!hasSelection ? filterSection : selectionSection}
-
{operationSection}
+ {!hasSelection ? (
+
+ {operationSection}
+
+ ) : null}
);
};
diff --git a/ui/v2.5/src/components/List/SavedFilterList.tsx b/ui/v2.5/src/components/List/SavedFilterList.tsx
index 7e03404d2..83c6d8a65 100644
--- a/ui/v2.5/src/components/List/SavedFilterList.tsx
+++ b/ui/v2.5/src/components/List/SavedFilterList.tsx
@@ -31,6 +31,7 @@ import { AlertModal } from "../Shared/Alert";
import cx from "classnames";
import { TruncatedInlineText } from "../Shared/TruncatedText";
import { OperationButton } from "../Shared/OperationButton";
+import { createPortal } from "react-dom";
const ExistingSavedFilterList: React.FC<{
name: string;
@@ -243,6 +244,7 @@ interface ISavedFilterListProps {
filter: ListFilterModel;
onSetFilter: (f: ListFilterModel) => void;
view?: View;
+ menuPortalTarget?: Element | DocumentFragment;
}
export const SavedFilterList: React.FC
= ({
@@ -841,8 +843,15 @@ export const SavedFilterDropdown: React.FC = (props) => {
));
SavedFilterDropdownRef.displayName = "SavedFilterDropdown";
+ const menu = (
+
+ );
+
return (
-
+
= (props) => {
-
+ {props.menuPortalTarget
+ ? createPortal(menu, props.menuPortalTarget)
+ : menu}
);
};
diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss
index c42c43a56..aba1f39df 100644
--- a/ui/v2.5/src/components/List/styles.scss
+++ b/ui/v2.5/src/components/List/styles.scss
@@ -1055,7 +1055,7 @@ input[type="range"].zoom-slider {
}
// hide sidebar Edit Filter button on larger screens
-@include media-breakpoint-up(lg) {
+@include media-breakpoint-up(md) {
.sidebar .edit-filter-button {
display: none;
}
@@ -1071,6 +1071,7 @@ input[type="range"].zoom-slider {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
+ margin-bottom: 0;
row-gap: 1rem;
> div {
@@ -1101,10 +1102,6 @@ input[type="range"].zoom-slider {
top: 0;
}
- .selected-items-info .btn {
- margin-right: 0.5rem;
- }
-
// hide drop down menu items for play and create new
// when the buttons are visible
@include media-breakpoint-up(sm) {
@@ -1125,7 +1122,7 @@ input[type="range"].zoom-slider {
}
}
- .selected-items-info,
+ .toolbar-selection-section,
div.filter-section {
border: 1px solid $secondary;
border-radius: 0.25rem;
@@ -1133,13 +1130,69 @@ input[type="range"].zoom-slider {
overflow-x: hidden;
}
- .sidebar-toggle-button {
- margin-left: auto;
+ div.toolbar-selection-section {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ justify-content: center;
+
+ .sidebar-toggle-button {
+ margin-right: 0.5rem;
+ }
+
+ .selected-items-info {
+ align-items: center;
+ display: flex;
+ }
+
+ > div:first-child,
+ > div:last-child {
+ flex: 1;
+ }
+
+ > div:last-child {
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ .scene-list-operations {
+ display: flex;
+ }
+
+ // on smaller viewports move the operation buttons to the right
+ @include media-breakpoint-down(md) {
+ div.scene-list-operations {
+ flex: 1;
+ justify-content: flex-end;
+ order: 3;
+ }
+
+ > div:last-child {
+ flex: 0;
+ order: 2;
+ }
+ }
+ }
+
+ // on larger viewports, move the operation buttons to the center
+ @include media-breakpoint-up(lg) {
+ div.toolbar-selection-section div.scene-list-operations {
+ justify-content: center;
+
+ > .btn-group {
+ gap: 0.5rem;
+ }
+ }
+
+ div.toolbar-selection-section .empty-space {
+ flex: 1;
+ order: 3;
+ }
}
.search-container {
border-right: 1px solid $secondary;
- display: block;
+ display: flex;
margin-right: -0.5rem;
min-width: calc($sidebar-width - 15px);
padding-right: 10px;
@@ -1175,21 +1228,27 @@ input[type="range"].zoom-slider {
}
}
-@include media-breakpoint-up(xl) {
+// hide the search box in the toolbar when sidebar is shown on larger screens
+// larger screens don't overlap the sidebar
+@include media-breakpoint-up(md) {
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .search-container {
display: none;
}
}
+// hide the search box when sidebar is hidden on smaller screens
@include media-breakpoint-down(md) {
.sidebar-pane.hide-sidebar .filtered-list-toolbar .search-container {
display: none;
}
}
-// hide the filter icon button when sidebar is shown on smaller screens
-@include media-breakpoint-down(md) {
- .sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .filter-button {
- display: none;
+// hide the filter and saved filters icon buttons when sidebar is shown on smaller screens
+@include media-breakpoint-down(sm) {
+ .sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar {
+ .filter-button,
+ .saved-filter-dropdown {
+ display: none;
+ }
}
// adjust the width of the filter-tags as well
@@ -1198,8 +1257,8 @@ input[type="range"].zoom-slider {
}
}
-// move the sidebar toggle to the left on xl viewports
-@include media-breakpoint-up(xl) {
+// move the sidebar toggle to the left on larger viewports
+@include media-breakpoint-up(md) {
.filtered-list-toolbar .filter-section {
.sidebar-toggle-button {
margin-left: 0;
@@ -1249,14 +1308,18 @@ input[type="range"].zoom-slider {
align-items: center;
background-color: $body-bg;
display: flex;
- justify-content: space-between;
> div {
align-items: center;
display: flex;
+ flex: 1;
gap: 0.5rem;
justify-content: flex-start;
+ &.pagination-index-container {
+ justify-content: center;
+ }
+
&:last-child {
flex-shrink: 0;
justify-content: flex-end;
@@ -1265,18 +1328,55 @@ input[type="range"].zoom-slider {
}
.list-results-header {
- flex-wrap: wrap-reverse;
- gap: 0.5rem;
+ gap: 0.25rem;
margin-bottom: 0.5rem;
.paginationIndex {
margin: 0;
}
+ // move pagination info to right on medium screens
+ @include media-breakpoint-down(md) {
+ & > .empty-space {
+ flex: 0;
+ }
+
+ & > div.pagination-index-container {
+ justify-content: flex-end;
+ order: 3;
+ }
+ }
+
// center the header on smaller screens
@include media-breakpoint-down(sm) {
& > div,
- & > div:last-child {
+ & > div.pagination-index-container {
+ flex-basis: 100%;
+ justify-content: center;
+ margin-left: auto;
+ margin-right: auto;
+ }
+ }
+}
+
+// sidebar visible styling
+.sidebar-pane:not(.hide-sidebar) .list-results-header {
+ // move pagination info to right on medium screens when sidebar
+ @include media-breakpoint-down(lg) {
+ & > .empty-space {
+ flex: 0;
+ }
+
+ & > div.pagination-index-container {
+ justify-content: flex-end;
+ order: 3;
+ }
+ }
+
+ // center the header on smaller screens when sidebar is visible
+ @include media-breakpoint-down(md) {
+ & > div,
+ & > div.pagination-index-container {
flex-basis: 100%;
justify-content: center;
margin-left: auto;
diff --git a/ui/v2.5/src/components/Scenes/SceneList.tsx b/ui/v2.5/src/components/Scenes/SceneList.tsx
index 982a11fed..f9257c9ad 100644
--- a/ui/v2.5/src/components/Scenes/SceneList.tsx
+++ b/ui/v2.5/src/components/Scenes/SceneList.tsx
@@ -37,7 +37,12 @@ import {
OperationDropdownItem,
} from "../List/ListOperationButtons";
import { useFilteredItemList } from "../List/ItemList";
-import { Sidebar, SidebarPane, useSidebarState } from "../Shared/Sidebar";
+import {
+ Sidebar,
+ SidebarPane,
+ SidebarPaneContent,
+ useSidebarState,
+} from "../Shared/Sidebar";
import { SidebarPerformersFilter } from "../List/Filters/PerformersFilter";
import { SidebarStudiosFilter } from "../List/Filters/StudiosFilter";
import { PerformersCriterionOption } from "src/models/list-filter/criteria/performers";
@@ -355,7 +360,7 @@ const SceneListOperations: React.FC<{
const intl = useIntl();
return (
-
+
{!!items && (
)}
-
+
{operations.map((o) => {
if (o.isDisplayed && !o.isDisplayed()) {
return null;
@@ -666,6 +674,18 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
// render
if (filterLoading || sidebarStateLoading) return null;
+ const operations = (
+
+ );
+
return (
{
focus={searchFocus}
/>
-
+
{
setSearchFocus(true);
}}
onRemoveSearchTerm={() => setFilter(filter.clearSearchTerm())}
+ view={view}
/>
}
selectionSection={
@@ -716,19 +737,10 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
onToggleSidebar={() => setShowSidebar(!showSidebar)}
onSelectAll={() => onSelectAll()}
onSelectNone={() => onSelectNone()}
+ operations={operations}
/>
}
- operationSection={
-
- }
+ operationSection={operations}
/>
{
/>
)}
-
+
diff --git a/ui/v2.5/src/components/Shared/Sidebar.tsx b/ui/v2.5/src/components/Shared/Sidebar.tsx
index 52f9328f0..2fe0c48af 100644
--- a/ui/v2.5/src/components/Shared/Sidebar.tsx
+++ b/ui/v2.5/src/components/Shared/Sidebar.tsx
@@ -16,7 +16,8 @@ import { useIntl } from "react-intl";
import { Icon } from "./Icon";
import { faSliders } from "@fortawesome/free-solid-svg-icons";
-const fixedSidebarMediaQuery = "only screen and (max-width: 1199px)";
+// this needs to correspond to the CSS media query that overlaps the sidebar over content
+const fixedSidebarMediaQuery = "only screen and (max-width: 767px)";
export const Sidebar: React.FC<
PropsWithChildren<{
@@ -56,6 +57,10 @@ export const SidebarPane: React.FC<
);
};
+export const SidebarPaneContent: React.FC = ({ children }) => {
+ return
{children}
;
+};
+
export const SidebarSection: React.FC<
PropsWithChildren<{
text: React.ReactNode;
@@ -87,7 +92,7 @@ export const SidebarToggleButton: React.FC<{
const intl = useIntl();
return (