diff --git a/ui/v2.5/src/components/List/ListViewOptions.tsx b/ui/v2.5/src/components/List/ListViewOptions.tsx index 3df8640a8..e3841a5b1 100644 --- a/ui/v2.5/src/components/List/ListViewOptions.tsx +++ b/ui/v2.5/src/components/List/ListViewOptions.tsx @@ -1,21 +1,17 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useRef, useState } from "react"; import Mousetrap from "mousetrap"; -import { - Button, - ButtonGroup, - Form, - OverlayTrigger, - Tooltip, -} from "react-bootstrap"; +import { Button, Dropdown, Overlay, Popover } from "react-bootstrap"; import { DisplayMode } from "src/models/list-filter/types"; import { useIntl } from "react-intl"; import { Icon } from "../Shared/Icon"; import { + faChevronDown, faList, faSquare, faTags, faThLarge, } from "@fortawesome/free-solid-svg-icons"; +import { ZoomSelect } from "./ZoomSlider"; interface IListViewOptionsProps { zoomIndex?: number; @@ -25,6 +21,38 @@ interface IListViewOptionsProps { displayModeOptions: DisplayMode[]; } +function getIcon(option: DisplayMode) { + switch (option) { + case DisplayMode.Grid: + return faThLarge; + case DisplayMode.List: + return faList; + case DisplayMode.Wall: + return faSquare; + case DisplayMode.Tagger: + return faTags; + } +} + +function getLabelId(option: DisplayMode) { + let displayModeId = "unknown"; + switch (option) { + case DisplayMode.Grid: + displayModeId = "grid"; + break; + case DisplayMode.List: + displayModeId = "list"; + break; + case DisplayMode.Wall: + displayModeId = "wall"; + break; + case DisplayMode.Tagger: + displayModeId = "tagger"; + break; + } + return `display_mode.${displayModeId}`; +} + export const ListViewOptions: React.FC = ({ zoomIndex, onSetZoom, @@ -37,6 +65,9 @@ export const ListViewOptions: React.FC = ({ const intl = useIntl(); + const overlayTarget = useRef(null); + const [showOptions, setShowOptions] = useState(false); + useEffect(() => { Mousetrap.bind("v g", () => { if (displayModeOptions.includes(DisplayMode.Grid)) { @@ -53,82 +84,16 @@ export const ListViewOptions: React.FC = ({ onSetDisplayMode(DisplayMode.Wall); } }); - Mousetrap.bind("+", () => { - if (onSetZoom && zoomIndex !== undefined && zoomIndex < maxZoom) { - onSetZoom(zoomIndex + 1); - } - }); - Mousetrap.bind("-", () => { - if (onSetZoom && zoomIndex !== undefined && zoomIndex > minZoom) { - onSetZoom(zoomIndex - 1); - } - }); return () => { Mousetrap.unbind("v g"); Mousetrap.unbind("v l"); Mousetrap.unbind("v w"); - Mousetrap.unbind("+"); - Mousetrap.unbind("-"); }; }); - function maybeRenderDisplayModeOptions() { - function getIcon(option: DisplayMode) { - switch (option) { - case DisplayMode.Grid: - return faThLarge; - case DisplayMode.List: - return faList; - case DisplayMode.Wall: - return faSquare; - case DisplayMode.Tagger: - return faTags; - } - } - function getLabel(option: DisplayMode) { - let displayModeId = "unknown"; - switch (option) { - case DisplayMode.Grid: - displayModeId = "grid"; - break; - case DisplayMode.List: - displayModeId = "list"; - break; - case DisplayMode.Wall: - displayModeId = "wall"; - break; - case DisplayMode.Tagger: - displayModeId = "tagger"; - break; - } - return intl.formatMessage({ id: `display_mode.${displayModeId}` }); - } - - if (displayModeOptions.length < 2) { - return; - } - - return ( - - {displayModeOptions.map((option) => ( - {getLabel(option)} - } - > - - - ))} - - ); + function getLabel(option: DisplayMode) { + return intl.formatMessage({ id: getLabelId(option) }); } function onChangeZoom(v: number) { @@ -137,29 +102,60 @@ export const ListViewOptions: React.FC = ({ } } - function maybeRenderZoom() { - if (onSetZoom && displayMode === DisplayMode.Grid) { - return ( -
- ) => - onChangeZoom(Number.parseInt(e.currentTarget.value, 10)) - } - /> -
- ); - } - } - return ( <> - {maybeRenderDisplayModeOptions()} - {maybeRenderZoom()} + + setShowOptions(false)} + > + {({ placement, arrowProps, show: _show, ...props }) => ( +
+ +
+ {onSetZoom && + zoomIndex !== undefined && + displayMode === DisplayMode.Grid ? ( +
+ +
+ ) : null} + {displayModeOptions.map((option) => ( + { + onSetDisplayMode(option); + }} + > + {getLabel(option)} + + ))} +
+
+
+ )} +
); }; diff --git a/ui/v2.5/src/components/List/ZoomSlider.tsx b/ui/v2.5/src/components/List/ZoomSlider.tsx new file mode 100644 index 000000000..dff8e4f57 --- /dev/null +++ b/ui/v2.5/src/components/List/ZoomSlider.tsx @@ -0,0 +1,50 @@ +import React, { useEffect } from "react"; +import Mousetrap from "mousetrap"; +import { Form } from "react-bootstrap"; + +export interface IZoomSelectProps { + minZoom: number; + maxZoom: number; + zoomIndex: number; + onChangeZoom: (v: number) => void; +} + +export const ZoomSelect: React.FC = ({ + minZoom, + maxZoom, + zoomIndex, + onChangeZoom, +}) => { + useEffect(() => { + Mousetrap.bind("+", () => { + if (zoomIndex !== undefined && zoomIndex < maxZoom) { + onChangeZoom(zoomIndex + 1); + } + }); + Mousetrap.bind("-", () => { + if (zoomIndex !== undefined && zoomIndex > minZoom) { + onChangeZoom(zoomIndex - 1); + } + }); + + return () => { + Mousetrap.unbind("+"); + Mousetrap.unbind("-"); + }; + }); + + return ( + ) => { + onChangeZoom(Number.parseInt(e.currentTarget.value, 10)); + e.preventDefault(); + e.stopPropagation(); + }} + /> + ); +}; diff --git a/ui/v2.5/src/components/List/styles.scss b/ui/v2.5/src/components/List/styles.scss index f47bc09f7..3372ee2f3 100644 --- a/ui/v2.5/src/components/List/styles.scss +++ b/ui/v2.5/src/components/List/styles.scss @@ -34,6 +34,68 @@ text-align: center; } +.display-mode-select { + padding-left: 0.375rem; + padding-right: 0.375rem; + text-wrap: nowrap; + + > svg:first-child { + margin-right: 0; + } +} + +.display-mode-menu { + .dropdown-item { + color: #f5f8fa; + font-size: 1rem; + + &:hover { + background-color: rgba(138, 155, 168, 0.15); + cursor: pointer; + } + } + + .zoom-slider-container { + display: flex; + justify-content: center; + margin-bottom: 0.5rem; + min-height: 1rem; + padding-bottom: 0.5rem; + padding-top: 0.25rem; + } + + .zoom-slider { + &::-webkit-slider-thumb { + background-color: $primary; + } + + &::-webkit-slider-runnable-track { + background-color: $body-bg; + } + + &:focus::-webkit-slider-runnable-track { + background-color: lighten($body-bg, 5%); + } + + &::-moz-range-thumb { + background-color: $primary; + } + + &::-moz-range-track { + background-color: $body-bg; + } + + &:focus::-moz-range-track { + background-color: lighten($body-bg, 5%); + } + } +} + +.display-mode-popover { + padding-left: 0; + padding-right: 0; +} + input[type="range"].zoom-slider { height: 100%; margin: 0; @@ -810,6 +872,10 @@ input[type="range"].zoom-slider { justify-content: center; row-gap: 0.5rem; } + + .btn.display-mode-select { + margin-left: 0.5rem; + } } .sidebar-pane .filtered-list-toolbar { diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 4b9b980da..b69e5a763 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -995,6 +995,7 @@ "disambiguation": "Disambiguation", "display_mode": { "grid": "Grid", + "label_current": "Display Mode: {current}", "list": "List", "tagger": "Tagger", "unknown": "Unknown", diff --git a/ui/v2.5/src/styles/_range.scss b/ui/v2.5/src/styles/_range.scss index 4de7caa88..d367ba39d 100644 --- a/ui/v2.5/src/styles/_range.scss +++ b/ui/v2.5/src/styles/_range.scss @@ -15,7 +15,7 @@ input[type="range"] { &::-webkit-slider-runnable-track { animate: 0.2s; - background: #137cbd; + background: $primary; border: 0 solid #000101; border-radius: 25px; box-shadow: 0 0 0 #000; @@ -28,12 +28,12 @@ input[type="range"] { -webkit-appearance: none; background: #394b59; border: 0 solid #000; - border-radius: 5px; + border-radius: 10px; box-shadow: 0 0 0 #000; cursor: pointer; - height: 16px; - margin-top: -5px; - width: 16px; + height: 15px; + margin-top: -4px; + width: 15px; } &:focus::-webkit-slider-runnable-track { @@ -42,7 +42,7 @@ input[type="range"] { &::-moz-range-track { animate: 0.2s; - background: #137cbd; + background: $primary; border: 0 solid #000101; border-radius: 25px; box-shadow: 0 0 0 #000; @@ -54,11 +54,11 @@ input[type="range"] { &::-moz-range-thumb { background: #394b59; border: 0 solid #000; - border-radius: 5px; + border-radius: 10px; box-shadow: 0 0 0 #000; cursor: pointer; - height: 16px; - width: 16px; + height: 15px; + width: 15px; } &::-ms-track { @@ -72,14 +72,14 @@ input[type="range"] { } &::-ms-fill-lower { - background: #137cbd; + background: $primary; border: 0 solid #000101; border-radius: 50px; box-shadow: 0 0 0 #000; } &::-ms-fill-upper { - background: #137cbd; + background: $primary; border: 0 solid #000101; border-radius: 50px; box-shadow: 0 0 0 #000; @@ -88,11 +88,11 @@ input[type="range"] { &::-ms-thumb { background: #394b59; border: 0 solid #000; - border-radius: 5px; + border-radius: 10px; box-shadow: 0 0 0 #000; cursor: pointer; height: 16px; - margin-top: 1px; + margin-top: 2px; width: 16px; }