mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
Scene list toolbar style update (#6215)
* Add saved filter button to toolbar * Rearrange and add portal target * Only overlap sidebar on sm viewports * Hide dropdown button on smaller viewports when sidebar open * Center operations during selection * Restyle results header * Add classname for sidebar pane content * Move sidebar toggle to left during scene selection
This commit is contained in:
parent
fb7bd89834
commit
299e1ac1f9
8 changed files with 235 additions and 82 deletions
|
|
@ -16,22 +16,28 @@ import {
|
||||||
faTrash,
|
faTrash,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
export const OperationDropdown: React.FC<
|
export const OperationDropdown: React.FC<
|
||||||
PropsWithChildren<{
|
PropsWithChildren<{
|
||||||
className?: string;
|
className?: string;
|
||||||
|
menuPortalTarget?: HTMLElement;
|
||||||
}>
|
}>
|
||||||
> = ({ className, children }) => {
|
> = ({ className, menuPortalTarget, children }) => {
|
||||||
if (!children) return null;
|
if (!children) return null;
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Dropdown.Menu className="bg-secondary text-white">
|
||||||
|
{children}
|
||||||
|
</Dropdown.Menu>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown className={className} as={ButtonGroup}>
|
<Dropdown className={className} as={ButtonGroup}>
|
||||||
<Dropdown.Toggle variant="secondary" id="more-menu">
|
<Dropdown.Toggle variant="secondary" id="more-menu">
|
||||||
<Icon icon={faEllipsisH} />
|
<Icon icon={faEllipsisH} />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu className="bg-secondary text-white">
|
{menuPortalTarget ? createPortal(menu, menuPortalTarget) : menu}
|
||||||
{children}
|
|
||||||
</Dropdown.Menu>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,6 @@ export const ListResultsHeader: React.FC<{
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ButtonToolbar className={cx(className, "list-results-header")}>
|
<ButtonToolbar className={cx(className, "list-results-header")}>
|
||||||
<div>
|
|
||||||
<PaginationIndex
|
|
||||||
loading={loading}
|
|
||||||
itemsPerPage={filter.itemsPerPage}
|
|
||||||
currentPage={filter.currentPage}
|
|
||||||
totalItems={totalCount}
|
|
||||||
metadataByline={metadataByline}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<SortBySelect
|
<SortBySelect
|
||||||
options={filter.options.sortByOptions}
|
options={filter.options.sortByOptions}
|
||||||
|
|
@ -61,6 +52,16 @@ export const ListResultsHeader: React.FC<{
|
||||||
onSetZoom={(zoom) => onChangeFilter(filter.setZoom(zoom))}
|
onSetZoom={(zoom) => onChangeFilter(filter.setZoom(zoom))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="pagination-index-container">
|
||||||
|
<PaginationIndex
|
||||||
|
loading={loading}
|
||||||
|
itemsPerPage={filter.itemsPerPage}
|
||||||
|
currentPage={filter.currentPage}
|
||||||
|
totalItems={totalCount}
|
||||||
|
metadataByline={metadataByline}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="empty-space"></div>
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,15 @@ import { ListFilterModel } from "src/models/list-filter/filter";
|
||||||
import { faTimes } from "@fortawesome/free-solid-svg-icons";
|
import { faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FilterTags } from "../List/FilterTags";
|
import { FilterTags } from "../List/FilterTags";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Button, ButtonToolbar } from "react-bootstrap";
|
import { Button, ButtonGroup, ButtonToolbar } from "react-bootstrap";
|
||||||
import { FilterButton } from "../List/Filters/FilterButton";
|
import { FilterButton } from "../List/Filters/FilterButton";
|
||||||
import { Icon } from "../Shared/Icon";
|
import { Icon } from "../Shared/Icon";
|
||||||
import { SearchTermInput } from "../List/ListFilter";
|
import { SearchTermInput } from "../List/ListFilter";
|
||||||
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
import { Criterion } from "src/models/list-filter/criteria/criterion";
|
||||||
import { SidebarToggleButton } from "../Shared/Sidebar";
|
import { SidebarToggleButton } from "../Shared/Sidebar";
|
||||||
import { PatchComponent } from "src/patch";
|
import { PatchComponent } from "src/patch";
|
||||||
|
import { SavedFilterDropdown } from "./SavedFilterList";
|
||||||
|
import { View } from "./views";
|
||||||
|
|
||||||
export const ToolbarFilterSection: React.FC<{
|
export const ToolbarFilterSection: React.FC<{
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
|
|
@ -21,6 +23,7 @@ export const ToolbarFilterSection: React.FC<{
|
||||||
onRemoveAllCriterion: () => void;
|
onRemoveAllCriterion: () => void;
|
||||||
onEditSearchTerm: () => void;
|
onEditSearchTerm: () => void;
|
||||||
onRemoveSearchTerm: () => void;
|
onRemoveSearchTerm: () => void;
|
||||||
|
view?: View;
|
||||||
}> = PatchComponent(
|
}> = PatchComponent(
|
||||||
"ToolbarFilterSection",
|
"ToolbarFilterSection",
|
||||||
({
|
({
|
||||||
|
|
@ -32,6 +35,7 @@ export const ToolbarFilterSection: React.FC<{
|
||||||
onRemoveAllCriterion,
|
onRemoveAllCriterion,
|
||||||
onEditSearchTerm,
|
onEditSearchTerm,
|
||||||
onRemoveSearchTerm,
|
onRemoveSearchTerm,
|
||||||
|
view,
|
||||||
}) => {
|
}) => {
|
||||||
const { criteria, searchTerm } = filter;
|
const { criteria, searchTerm } = filter;
|
||||||
|
|
||||||
|
|
@ -41,10 +45,19 @@ export const ToolbarFilterSection: React.FC<{
|
||||||
<SearchTermInput filter={filter} onFilterUpdate={onSetFilter} />
|
<SearchTermInput filter={filter} onFilterUpdate={onSetFilter} />
|
||||||
</div>
|
</div>
|
||||||
<div className="filter-section">
|
<div className="filter-section">
|
||||||
|
<ButtonGroup>
|
||||||
|
<SidebarToggleButton onClick={onToggleSidebar} />
|
||||||
|
<SavedFilterDropdown
|
||||||
|
filter={filter}
|
||||||
|
onSetFilter={onSetFilter}
|
||||||
|
view={view}
|
||||||
|
menuPortalTarget={document.body}
|
||||||
|
/>
|
||||||
<FilterButton
|
<FilterButton
|
||||||
onClick={() => onEditCriterion()}
|
onClick={() => onEditCriterion()}
|
||||||
count={criteria.length}
|
count={criteria.length}
|
||||||
/>
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
<FilterTags
|
<FilterTags
|
||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
criteria={criteria}
|
criteria={criteria}
|
||||||
|
|
@ -55,7 +68,6 @@ export const ToolbarFilterSection: React.FC<{
|
||||||
onRemoveSearchTerm={onRemoveSearchTerm}
|
onRemoveSearchTerm={onRemoveSearchTerm}
|
||||||
truncateOnOverflow
|
truncateOnOverflow
|
||||||
/>
|
/>
|
||||||
<SidebarToggleButton onClick={onToggleSidebar} />
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
@ -65,15 +77,18 @@ export const ToolbarFilterSection: React.FC<{
|
||||||
export const ToolbarSelectionSection: React.FC<{
|
export const ToolbarSelectionSection: React.FC<{
|
||||||
selected: number;
|
selected: number;
|
||||||
onToggleSidebar: () => void;
|
onToggleSidebar: () => void;
|
||||||
|
operations?: React.ReactNode;
|
||||||
onSelectAll: () => void;
|
onSelectAll: () => void;
|
||||||
onSelectNone: () => void;
|
onSelectNone: () => void;
|
||||||
}> = PatchComponent(
|
}> = PatchComponent(
|
||||||
"ToolbarSelectionSection",
|
"ToolbarSelectionSection",
|
||||||
({ selected, onToggleSidebar, onSelectAll, onSelectNone }) => {
|
({ selected, onToggleSidebar, operations, onSelectAll, onSelectNone }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="toolbar-selection-section">
|
||||||
<div className="selected-items-info">
|
<div className="selected-items-info">
|
||||||
|
<SidebarToggleButton onClick={onToggleSidebar} />
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="minimal"
|
className="minimal"
|
||||||
|
|
@ -86,7 +101,9 @@ export const ToolbarSelectionSection: React.FC<{
|
||||||
<Button variant="link" onClick={() => onSelectAll()}>
|
<Button variant="link" onClick={() => onSelectAll()}>
|
||||||
<FormattedMessage id="actions.select_all" />
|
<FormattedMessage id="actions.select_all" />
|
||||||
</Button>
|
</Button>
|
||||||
<SidebarToggleButton onClick={onToggleSidebar} />
|
</div>
|
||||||
|
{operations}
|
||||||
|
<div className="empty-space" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +131,11 @@ export const FilteredListToolbar2: React.FC<{
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{!hasSelection ? filterSection : selectionSection}
|
{!hasSelection ? filterSection : selectionSection}
|
||||||
<div className="filtered-list-toolbar-operations">{operationSection}</div>
|
{!hasSelection ? (
|
||||||
|
<div className="filtered-list-toolbar-operations">
|
||||||
|
{operationSection}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { AlertModal } from "../Shared/Alert";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { TruncatedInlineText } from "../Shared/TruncatedText";
|
import { TruncatedInlineText } from "../Shared/TruncatedText";
|
||||||
import { OperationButton } from "../Shared/OperationButton";
|
import { OperationButton } from "../Shared/OperationButton";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
const ExistingSavedFilterList: React.FC<{
|
const ExistingSavedFilterList: React.FC<{
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -243,6 +244,7 @@ interface ISavedFilterListProps {
|
||||||
filter: ListFilterModel;
|
filter: ListFilterModel;
|
||||||
onSetFilter: (f: ListFilterModel) => void;
|
onSetFilter: (f: ListFilterModel) => void;
|
||||||
view?: View;
|
view?: View;
|
||||||
|
menuPortalTarget?: Element | DocumentFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SavedFilterList: React.FC<ISavedFilterListProps> = ({
|
export const SavedFilterList: React.FC<ISavedFilterListProps> = ({
|
||||||
|
|
@ -841,8 +843,15 @@ export const SavedFilterDropdown: React.FC<ISavedFilterListProps> = (props) => {
|
||||||
));
|
));
|
||||||
SavedFilterDropdownRef.displayName = "SavedFilterDropdown";
|
SavedFilterDropdownRef.displayName = "SavedFilterDropdown";
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Dropdown.Menu
|
||||||
|
as={SavedFilterDropdownRef}
|
||||||
|
className="saved-filter-list-menu"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown as={ButtonGroup}>
|
<Dropdown as={ButtonGroup} className="saved-filter-dropdown">
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
placement="top"
|
placement="top"
|
||||||
overlay={
|
overlay={
|
||||||
|
|
@ -855,10 +864,9 @@ export const SavedFilterDropdown: React.FC<ISavedFilterListProps> = (props) => {
|
||||||
<Icon icon={faBookmark} />
|
<Icon icon={faBookmark} />
|
||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
<Dropdown.Menu
|
{props.menuPortalTarget
|
||||||
as={SavedFilterDropdownRef}
|
? createPortal(menu, props.menuPortalTarget)
|
||||||
className="saved-filter-list-menu"
|
: menu}
|
||||||
/>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1055,7 +1055,7 @@ input[type="range"].zoom-slider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide sidebar Edit Filter button on larger screens
|
// hide sidebar Edit Filter button on larger screens
|
||||||
@include media-breakpoint-up(lg) {
|
@include media-breakpoint-up(md) {
|
||||||
.sidebar .edit-filter-button {
|
.sidebar .edit-filter-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
@ -1071,6 +1071,7 @@ input[type="range"].zoom-slider {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 0;
|
||||||
row-gap: 1rem;
|
row-gap: 1rem;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
|
|
@ -1101,10 +1102,6 @@ input[type="range"].zoom-slider {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-items-info .btn {
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide drop down menu items for play and create new
|
// hide drop down menu items for play and create new
|
||||||
// when the buttons are visible
|
// when the buttons are visible
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
|
|
@ -1125,7 +1122,7 @@ input[type="range"].zoom-slider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-items-info,
|
.toolbar-selection-section,
|
||||||
div.filter-section {
|
div.filter-section {
|
||||||
border: 1px solid $secondary;
|
border: 1px solid $secondary;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
|
|
@ -1133,13 +1130,69 @@ input[type="range"].zoom-slider {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.toolbar-selection-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
.sidebar-toggle-button {
|
.sidebar-toggle-button {
|
||||||
margin-left: auto;
|
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 {
|
.search-container {
|
||||||
border-right: 1px solid $secondary;
|
border-right: 1px solid $secondary;
|
||||||
display: block;
|
display: flex;
|
||||||
margin-right: -0.5rem;
|
margin-right: -0.5rem;
|
||||||
min-width: calc($sidebar-width - 15px);
|
min-width: calc($sidebar-width - 15px);
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
|
|
@ -1175,22 +1228,28 @@ 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 {
|
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .search-container {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// hide the search box when sidebar is hidden on smaller screens
|
||||||
@include media-breakpoint-down(md) {
|
@include media-breakpoint-down(md) {
|
||||||
.sidebar-pane.hide-sidebar .filtered-list-toolbar .search-container {
|
.sidebar-pane.hide-sidebar .filtered-list-toolbar .search-container {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide the filter icon button when sidebar is shown on smaller screens
|
// hide the filter and saved filters icon buttons when sidebar is shown on smaller screens
|
||||||
@include media-breakpoint-down(md) {
|
@include media-breakpoint-down(sm) {
|
||||||
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .filter-button {
|
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar {
|
||||||
|
.filter-button,
|
||||||
|
.saved-filter-dropdown {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// adjust the width of the filter-tags as well
|
// adjust the width of the filter-tags as well
|
||||||
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .filter-tags {
|
.sidebar-pane:not(.hide-sidebar) .filtered-list-toolbar .filter-tags {
|
||||||
|
|
@ -1198,8 +1257,8 @@ input[type="range"].zoom-slider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move the sidebar toggle to the left on xl viewports
|
// move the sidebar toggle to the left on larger viewports
|
||||||
@include media-breakpoint-up(xl) {
|
@include media-breakpoint-up(md) {
|
||||||
.filtered-list-toolbar .filter-section {
|
.filtered-list-toolbar .filter-section {
|
||||||
.sidebar-toggle-button {
|
.sidebar-toggle-button {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
@ -1249,14 +1308,18 @@ input[type="range"].zoom-slider {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: $body-bg;
|
background-color: $body-bg;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
&.pagination-index-container {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
@ -1265,18 +1328,55 @@ input[type="range"].zoom-slider {
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-results-header {
|
.list-results-header {
|
||||||
flex-wrap: wrap-reverse;
|
gap: 0.25rem;
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
.paginationIndex {
|
.paginationIndex {
|
||||||
margin: 0;
|
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
|
// center the header on smaller screens
|
||||||
@include media-breakpoint-down(sm) {
|
@include media-breakpoint-down(sm) {
|
||||||
& > div,
|
& > 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%;
|
flex-basis: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,12 @@ import {
|
||||||
OperationDropdownItem,
|
OperationDropdownItem,
|
||||||
} from "../List/ListOperationButtons";
|
} from "../List/ListOperationButtons";
|
||||||
import { useFilteredItemList } from "../List/ItemList";
|
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 { SidebarPerformersFilter } from "../List/Filters/PerformersFilter";
|
||||||
import { SidebarStudiosFilter } from "../List/Filters/StudiosFilter";
|
import { SidebarStudiosFilter } from "../List/Filters/StudiosFilter";
|
||||||
import { PerformersCriterionOption } from "src/models/list-filter/criteria/performers";
|
import { PerformersCriterionOption } from "src/models/list-filter/criteria/performers";
|
||||||
|
|
@ -355,7 +360,7 @@ const SceneListOperations: React.FC<{
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="scene-list-operations">
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
{!!items && (
|
{!!items && (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -396,7 +401,10 @@ const SceneListOperations: React.FC<{
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<OperationDropdown className="scene-list-operations">
|
<OperationDropdown
|
||||||
|
className="scene-list-operations"
|
||||||
|
menuPortalTarget={document.body}
|
||||||
|
>
|
||||||
{operations.map((o) => {
|
{operations.map((o) => {
|
||||||
if (o.isDisplayed && !o.isDisplayed()) {
|
if (o.isDisplayed && !o.isDisplayed()) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -666,6 +674,18 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
// render
|
// render
|
||||||
if (filterLoading || sidebarStateLoading) return null;
|
if (filterLoading || sidebarStateLoading) return null;
|
||||||
|
|
||||||
|
const operations = (
|
||||||
|
<SceneListOperations
|
||||||
|
items={items.length}
|
||||||
|
hasSelection={hasSelection}
|
||||||
|
operations={otherOperations}
|
||||||
|
onEdit={onEdit}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onPlay={onPlay}
|
||||||
|
onCreateNew={onCreateNew}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TaggerContext>
|
<TaggerContext>
|
||||||
<div
|
<div
|
||||||
|
|
@ -689,7 +709,7 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
focus={searchFocus}
|
focus={searchFocus}
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<div>
|
<SidebarPaneContent>
|
||||||
<FilteredListToolbar2
|
<FilteredListToolbar2
|
||||||
className="scene-list-toolbar"
|
className="scene-list-toolbar"
|
||||||
hasSelection={hasSelection}
|
hasSelection={hasSelection}
|
||||||
|
|
@ -708,6 +728,7 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
setSearchFocus(true);
|
setSearchFocus(true);
|
||||||
}}
|
}}
|
||||||
onRemoveSearchTerm={() => setFilter(filter.clearSearchTerm())}
|
onRemoveSearchTerm={() => setFilter(filter.clearSearchTerm())}
|
||||||
|
view={view}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
selectionSection={
|
selectionSection={
|
||||||
|
|
@ -716,19 +737,10 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
onToggleSidebar={() => setShowSidebar(!showSidebar)}
|
onToggleSidebar={() => setShowSidebar(!showSidebar)}
|
||||||
onSelectAll={() => onSelectAll()}
|
onSelectAll={() => onSelectAll()}
|
||||||
onSelectNone={() => onSelectNone()}
|
onSelectNone={() => onSelectNone()}
|
||||||
|
operations={operations}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
operationSection={
|
operationSection={operations}
|
||||||
<SceneListOperations
|
|
||||||
items={items.length}
|
|
||||||
hasSelection={hasSelection}
|
|
||||||
operations={otherOperations}
|
|
||||||
onEdit={onEdit}
|
|
||||||
onDelete={onDelete}
|
|
||||||
onPlay={onPlay}
|
|
||||||
onCreateNew={onCreateNew}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListResultsHeader
|
<ListResultsHeader
|
||||||
|
|
@ -761,7 +773,7 @@ export const FilteredSceneList = (props: IFilteredScenes) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</SidebarPaneContent>
|
||||||
</SidebarPane>
|
</SidebarPane>
|
||||||
</div>
|
</div>
|
||||||
</TaggerContext>
|
</TaggerContext>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import { useIntl } from "react-intl";
|
||||||
import { Icon } from "./Icon";
|
import { Icon } from "./Icon";
|
||||||
import { faSliders } from "@fortawesome/free-solid-svg-icons";
|
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<
|
export const Sidebar: React.FC<
|
||||||
PropsWithChildren<{
|
PropsWithChildren<{
|
||||||
|
|
@ -56,6 +57,10 @@ export const SidebarPane: React.FC<
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SidebarPaneContent: React.FC = ({ children }) => {
|
||||||
|
return <div className="sidebar-pane-content">{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
export const SidebarSection: React.FC<
|
export const SidebarSection: React.FC<
|
||||||
PropsWithChildren<{
|
PropsWithChildren<{
|
||||||
text: React.ReactNode;
|
text: React.ReactNode;
|
||||||
|
|
@ -87,7 +92,7 @@ export const SidebarToggleButton: React.FC<{
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className="minimal sidebar-toggle-button ignore-sidebar-outside-click"
|
className="sidebar-toggle-button ignore-sidebar-outside-click"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
title={intl.formatMessage({ id: "actions.sidebar.toggle" })}
|
title={intl.formatMessage({ id: "actions.sidebar.toggle" })}
|
||||||
|
|
|
||||||
|
|
@ -805,7 +805,7 @@ button.btn.favorite-button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(xl) {
|
@include media-breakpoint-up(md) {
|
||||||
transition: margin-left 0.1s;
|
transition: margin-left 0.1s;
|
||||||
|
|
||||||
&:not(.hide-sidebar) {
|
&:not(.hide-sidebar) {
|
||||||
|
|
@ -910,12 +910,12 @@ $sticky-header-height: calc(50px + 3.3rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// on smaller viewports we want the sidebar to overlap content
|
// on smaller viewports we want the sidebar to overlap content
|
||||||
@include media-breakpoint-down(lg) {
|
@include media-breakpoint-down(sm) {
|
||||||
.sidebar-pane:not(.hide-sidebar) .sidebar {
|
.sidebar-pane:not(.hide-sidebar) .sidebar {
|
||||||
margin-right: -$sidebar-width;
|
margin-right: -$sidebar-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-pane > :nth-child(2) {
|
.sidebar-pane > .sidebar-pane-content {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -935,7 +935,7 @@ $sticky-header-height: calc(50px + 3.3rem);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include media-breakpoint-up(xl) {
|
@include media-breakpoint-up(md) {
|
||||||
.sidebar-pane:not(.hide-sidebar) {
|
.sidebar-pane:not(.hide-sidebar) {
|
||||||
> :nth-child(2) {
|
> :nth-child(2) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue