From 3df96ffe178ef1df7afa429c863a5e8ec3473489 Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Tue, 26 Nov 2024 16:00:50 -0800 Subject: [PATCH 01/11] feat: Added backdrop visibility config, on/off toggle in settings --- src/colorizer/constants.ts | 5 ++- src/colorizer/types.ts | 3 +- src/components/CanvasWrapper.tsx | 24 ++++++++---- src/components/Tabs/SettingsTab.tsx | 60 ++++++++++++++++++----------- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/colorizer/constants.ts b/src/colorizer/constants.ts index 005d5a1a..d34405b0 100644 --- a/src/colorizer/constants.ts +++ b/src/colorizer/constants.ts @@ -24,12 +24,13 @@ export const getDefaultViewerConfig = (): ViewerConfig => ({ showLegendDuringExport: true, showHeaderDuringExport: true, keepRangeBetweenDatasets: false, + backdropVisible: false, /** Brightness, as an integer percentage. */ backdropBrightness: 100, /** Saturation, as an integer percentage. */ backdropSaturation: 100, - /** Opacity, as an integer percentage. */ - objectOpacity: 100, + /** Opacity when backdrops are visible, as an integer percentage. */ + objectOpacity: 50, outOfRangeDrawSettings: { mode: DrawMode.USE_COLOR, color: new Color(OUT_OF_RANGE_COLOR_DEFAULT) }, outlierDrawSettings: { mode: DrawMode.USE_COLOR, color: new Color(OUTLIER_COLOR_DEFAULT) }, outlineColor: new Color(OUTLINE_COLOR_DEFAULT), diff --git a/src/colorizer/types.ts b/src/colorizer/types.ts index 89613b23..61535e95 100644 --- a/src/colorizer/types.ts +++ b/src/colorizer/types.ts @@ -163,11 +163,12 @@ export type ViewerConfig = { showLegendDuringExport: boolean; showHeaderDuringExport: boolean; keepRangeBetweenDatasets: boolean; + backdropVisible: boolean; /** Brightness, as an integer percentage. */ backdropBrightness: number; /** Saturation, as an integer percentage. */ backdropSaturation: number; - /** Opacity, as an integer percentage. */ + /** Object opacity when backdrop is visible, as an integer percentage. */ objectOpacity: number; outOfRangeDrawSettings: DrawSettings; outlierDrawSettings: DrawSettings; diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index d77e8586..4feb47a8 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -225,10 +225,22 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem // Update backdrops useMemo(() => { - canv.setBackdropKey(props.selectedBackdropKey); - canv.setBackdropBrightness(props.config.backdropBrightness); - canv.setBackdropSaturation(props.config.backdropSaturation); - }, [props.selectedBackdropKey, props.config.backdropBrightness, props.config.backdropSaturation]); + if (props.selectedBackdropKey !== null && props.config.backdropVisible) { + canv.setBackdropKey(props.selectedBackdropKey); + canv.setBackdropBrightness(props.config.backdropBrightness); + canv.setBackdropSaturation(props.config.backdropSaturation); + canv.setObjectOpacity(props.config.objectOpacity); + } else { + canv.setBackdropKey(null); + canv.setObjectOpacity(100); + } + }, [ + props.selectedBackdropKey, + props.config.backdropVisible, + props.config.backdropBrightness, + props.config.backdropSaturation, + props.config.objectOpacity, + ]); // Update categorical colors useMemo(() => { @@ -246,10 +258,6 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem canv.setOutlierDrawMode(settings.mode, settings.color); }, [props.config.outlierDrawSettings]); - useMemo(() => { - canv.setObjectOpacity(props.config.objectOpacity); - }, [props.config.objectOpacity]); - useMemo(() => { canv.setInRangeLUT(props.inRangeLUT); }, [props.inRangeLUT]); diff --git a/src/components/Tabs/SettingsTab.tsx b/src/components/Tabs/SettingsTab.tsx index 2e2c4670..ec906134 100644 --- a/src/components/Tabs/SettingsTab.tsx +++ b/src/components/Tabs/SettingsTab.tsx @@ -40,20 +40,35 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { return { key, label: data.name }; }) : []; - backdropOptions.unshift(NO_BACKDROP); - const isBackdropDisabled = backdropOptions.length === 1 || !props.selectedBackdropKey; + let isBackdropDisabled = false; + const selectedBackdropKey = props.selectedBackdropKey ?? NO_BACKDROP.key; + if (backdropOptions.length === 0 || props.selectedBackdropKey === null) { + // Add placeholder for no backdrop. + isBackdropDisabled = true; + backdropOptions.push(NO_BACKDROP); + } return ( - + + { + props.updateConfig({ backdropVisible: event.target.checked }); + }} + /> + + @@ -68,7 +83,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { onChange={(brightness: number) => props.updateConfig({ backdropBrightness: brightness })} marks={[100]} numberFormatter={(value?: number) => `${value}%`} - disabled={isBackdropDisabled} + disabled={!props.config.backdropVisible || isBackdropDisabled} /> @@ -85,7 +100,23 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { onChange={(saturation: number) => props.updateConfig({ backdropSaturation: saturation })} marks={[100]} numberFormatter={(value?: number) => `${value}%`} - disabled={isBackdropDisabled} + disabled={!props.config.backdropVisible || isBackdropDisabled} + /> + + + +
+ props.updateConfig({ objectOpacity: objectOpacity })} + marks={[100]} + numberFormatter={(value?: number) => `${value}%`} />
@@ -127,21 +158,6 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { }} /> - -
- props.updateConfig({ objectOpacity: objectOpacity })} - marks={[100]} - numberFormatter={(value?: number) => `${value}%`} - /> -
-
Date: Tue, 26 Nov 2024 16:05:09 -0800 Subject: [PATCH 02/11] feat: Add onscreen toggle for backdrop --- src/Viewer.tsx | 1 + src/components/CanvasWrapper.tsx | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Viewer.tsx b/src/Viewer.tsx index e423604c..b5bfbfee 100644 --- a/src/Viewer.tsx +++ b/src/Viewer.tsx @@ -1032,6 +1032,7 @@ function Viewer(): ReactElement { categoricalColors={categoricalPalette} selectedTrack={selectedTrack} config={config} + updateConfig={updateConfig} onTrackClicked={(track) => { setFindTrackInput(track?.trackId.toString() || ""); setSelectedTrack(track); diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 4feb47a8..bcfd4bfc 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -1,4 +1,4 @@ -import { HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; +import { ExpandOutlined, HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; import { Tooltip, TooltipProps } from "antd"; import React, { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; @@ -84,6 +84,7 @@ type CanvasWrapperProps = { /** Pan and zoom will be reset on collection change. */ collection: Collection | null; config: ViewerConfig; + updateConfig: (settings: Partial) => void; vectorData: Float32Array | null; loading: boolean; @@ -640,6 +641,23 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem Zoom out + + { + props.updateConfig({ backdropVisible: !props.config.backdropVisible }); + }} + disabled={props.selectedBackdropKey === null} + > + + Show backdrop + + ); From 18c59a6dd089a488df1f2588e5988877b8bb7a90 Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Wed, 27 Nov 2024 15:23:43 -0800 Subject: [PATCH 03/11] feat: Added custom icons for backdrop visibility --- src/assets/images/icon-images-slash.svg | 3 +++ src/assets/images/icon-images.svg | 3 +++ src/assets/images/index.ts | 6 +++++- src/components/CanvasWrapper.tsx | 10 +++++----- 4 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 src/assets/images/icon-images-slash.svg create mode 100644 src/assets/images/icon-images.svg diff --git a/src/assets/images/icon-images-slash.svg b/src/assets/images/icon-images-slash.svg new file mode 100644 index 00000000..e9452140 --- /dev/null +++ b/src/assets/images/icon-images-slash.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/icon-images.svg b/src/assets/images/icon-images.svg new file mode 100644 index 00000000..2ca0a826 --- /dev/null +++ b/src/assets/images/icon-images.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/index.ts b/src/assets/images/index.ts index 66427b90..88e15c66 100644 --- a/src/assets/images/index.ts +++ b/src/assets/images/index.ts @@ -1,7 +1,9 @@ -import AicsLogoSVGAsset from "./AICS-logo.svg?react"; import AicsLogoAndNameSVGAsset from "./AICS-logo-and-name.svg?react"; +import AicsLogoSVGAsset from "./AICS-logo.svg?react"; import DropdownSVGAsset from "./dropdown-arrow.svg?react"; import ExportIconSVGAsset from "./icon-export.svg?react"; +import ImagesSlashIconSVGAsset from "./icon-images-slash.svg?react"; +import ImagesIconSVGAsset from "./icon-images.svg?react"; import SwitchIconSVGAsset from "./icon-switch-arrows.svg?react"; import NoImageSVGAsset from "./no-image.svg?react"; import SpinBoxHandleDownSVGAsset from "./spin_box-handle-down.svg?react"; @@ -12,6 +14,8 @@ export const AicsLogoSVG = AicsLogoSVGAsset; export const DropdownSVG = DropdownSVGAsset; export const SwitchIconSVG = SwitchIconSVGAsset; export const NoImageSVG = NoImageSVGAsset; +export const ImagesIconSVG = ImagesIconSVGAsset; +export const ImagesSlashIconSVG = ImagesSlashIconSVGAsset; export const ExportIconSVG = ExportIconSVGAsset; export const SpinBoxHandleDownSVG = SpinBoxHandleDownSVGAsset; export const SpinBoxHandleUpSVG = SpinBoxHandleUpSVGAsset; diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index bcfd4bfc..463aac92 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -1,11 +1,11 @@ -import { ExpandOutlined, HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; +import { HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; import { Tooltip, TooltipProps } from "antd"; import React, { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; import { Color, ColorRepresentation, Vector2 } from "three"; import { clamp } from "three/src/math/MathUtils"; -import { NoImageSVG } from "../assets"; +import { ImagesIconSVG, ImagesSlashIconSVG, NoImageSVG } from "../assets"; import { ColorRamp, Dataset, Track } from "../colorizer"; import { LoadTroubleshooting, ViewerConfig } from "../colorizer/types"; import * as mathUtils from "../colorizer/utils/math_utils"; @@ -642,9 +642,9 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem - + {props.config.backdropVisible ? : } Show backdrop From 6ab99377acb2ed6a55f99ae1629161b6d8e72307 Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Wed, 27 Nov 2024 15:53:16 -0800 Subject: [PATCH 04/11] feat: Added link to viewer settings in backdrop tooltip --- src/components/AppStyle.tsx | 5 ++ src/components/Buttons/LinkStyleButton.tsx | 32 ++++++++++++ src/components/CanvasWrapper.tsx | 60 +++++++++++++++++----- 3 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 src/components/Buttons/LinkStyleButton.tsx diff --git a/src/components/AppStyle.tsx b/src/components/AppStyle.tsx index ac5b0284..20f27721 100644 --- a/src/components/AppStyle.tsx +++ b/src/components/AppStyle.tsx @@ -33,6 +33,7 @@ const palette = { successLight: "#f6ffed", errorMedium: "#ffa39e", errorLight: "#fff2f0", + infoDark: "#69c0ff", infoMedium: "#91d5ff", infoLight: "#e6f4ff", warningMedium: "#ffe58f", @@ -59,6 +60,8 @@ const theme = { theme: palette.theme, link: palette.link, linkHover: palette.linkDark, + darkLink: palette.infoMedium, + darkLinkHover: palette.infoDark, }, layout: { background: palette.gray0, @@ -162,6 +165,8 @@ const CssContainer = styled.div` --color-text-theme-dark: ${theme.color.themeDark}; --color-text-link: ${theme.color.text.link}; --color-text-link-hover: ${theme.color.text.linkHover}; + --color-text-dark-link: ${theme.color.text.darkLink}; + --color-text-dark-link-hover: ${theme.color.text.darkLinkHover}; /* Layout */ --color-background-alt: ${theme.color.layout.backgroundAlt}; diff --git a/src/components/Buttons/LinkStyleButton.tsx b/src/components/Buttons/LinkStyleButton.tsx new file mode 100644 index 00000000..7f752799 --- /dev/null +++ b/src/components/Buttons/LinkStyleButton.tsx @@ -0,0 +1,32 @@ +import styled, { css } from "styled-components"; + +export const LinkStyleButton = styled.button<{ + $color?: string; + $hoverColor?: string; +}>` + margin: 0; + padding: 0; + width: auto; + overflow: visible; + background: transparent; + line-height: normal; + font: inherit; + font-size: inherit; + border: none; + + text-decoration: underline; + + ${(props) => { + return css` + color: ${props.$color || "var(--color-text-link)"}; + + &:hover { + color: ${props.$hoverColor || "var(--color-text-link-hover)"}; + } + + &:focus-visible { + box-shadow: 0 0 0 3px ${props.$color || "var(--color-text-link)"}; + } + `; + }} +`; diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 463aac92..0a168b7b 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -7,7 +7,7 @@ import { clamp } from "three/src/math/MathUtils"; import { ImagesIconSVG, ImagesSlashIconSVG, NoImageSVG } from "../assets"; import { ColorRamp, Dataset, Track } from "../colorizer"; -import { LoadTroubleshooting, ViewerConfig } from "../colorizer/types"; +import { LoadTroubleshooting, TabType, ViewerConfig } from "../colorizer/types"; import * as mathUtils from "../colorizer/utils/math_utils"; import { FlexColumn, FlexColumnAlignCenter, VisuallyHidden } from "../styles/utils"; @@ -15,6 +15,7 @@ import CanvasUIOverlay from "../colorizer/CanvasWithOverlay"; import Collection from "../colorizer/Collection"; import { AppThemeContext } from "./AppStyle"; import { AlertBannerProps } from "./Banner"; +import { LinkStyleButton } from "./Buttons/LinkStyleButton"; import IconButton from "./IconButton"; import LoadingSpinner from "./LoadingSpinner"; @@ -30,19 +31,32 @@ const RIGHT_CLICK_BUTTON = 2; const MAX_INVERSE_ZOOM = 2; // 0.5x zoom const MIN_INVERSE_ZOOM = 0.1; // 10x zoom -function TooltipWithSubtext(props: TooltipProps & { title: ReactNode; subtext: ReactNode }): ReactElement { +function TooltipWithSubtext( + props: TooltipProps & { title: ReactNode; subtext?: ReactNode; subtextList?: ReactNode[] } +): ReactElement { + const divRef = useRef(null); return ( - -

{props.title}

-

{props.subtext}

- - } - > - {props.children} -
+
+ +

{props.title}

+ {props.subtext &&

{props.subtext}

} + {props.subtextList && + props.subtextList.map((subtext, i) => ( +

+ {subtext} +

+ ))} + + } + getPopupContainer={() => divRef.current ?? document.body} + > + {props.children} +
+
); } @@ -583,7 +597,25 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem }, [props.dataset, canv]); // RENDERING ///////////////////////////////////////////////// + canv.render(); + + const onViewerSettingsLinkClicked = (): void => { + props.updateConfig({ openTab: TabType.SETTINGS }); + }; + const backdropTooltipContents: ReactNode[] = [ + props.selectedBackdropKey === null + ? "(No backdrops available)" + : props.dataset?.getBackdropData().get(props.selectedBackdropKey)?.name, + + Viewer settings {">"} Backdrop + , + ]; + return ( Date: Wed, 27 Nov 2024 15:57:02 -0800 Subject: [PATCH 05/11] feat: Added backdrop visibility to URL --- src/colorizer/utils/url_utils.ts | 3 +++ tests/url_utils.test.ts | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/colorizer/utils/url_utils.ts b/src/colorizer/utils/url_utils.ts index a87d3034..bf03659e 100644 --- a/src/colorizer/utils/url_utils.ts +++ b/src/colorizer/utils/url_utils.ts @@ -38,6 +38,7 @@ enum UrlParam { COLOR_RAMP_REVERSED_SUFFIX = "!", PALETTE = "palette", PALETTE_KEY = "palette-key", + SHOW_BACKDROP = "bg", BACKDROP_KEY = "bg-key", BACKDROP_BRIGHTNESS = "bg-brightness", BACKDROP_SATURATION = "bg-sat", @@ -301,6 +302,7 @@ function serializeViewerConfig(config: Partial): string[] { parameters.push(`${UrlParam.OPEN_TAB}=${config.openTab}`); } + tryAddBooleanParam(parameters, config.backdropVisible, UrlParam.SHOW_BACKDROP); tryAddBooleanParam(parameters, config.showScaleBar, UrlParam.SHOW_SCALEBAR); tryAddBooleanParam(parameters, config.showTimestamp, UrlParam.SHOW_TIMESTAMP); tryAddBooleanParam(parameters, config.showTrackPath, UrlParam.SHOW_PATH); @@ -366,6 +368,7 @@ function deserializeViewerConfig(params: URLSearchParams): Partial newConfig.openTab = openTab; } + newConfig.backdropVisible = getBooleanParam(params.get(UrlParam.SHOW_BACKDROP)); newConfig.showScaleBar = getBooleanParam(params.get(UrlParam.SHOW_SCALEBAR)); newConfig.showTimestamp = getBooleanParam(params.get(UrlParam.SHOW_TIMESTAMP)); newConfig.showTrackPath = getBooleanParam(params.get(UrlParam.SHOW_PATH)); diff --git a/tests/url_utils.test.ts b/tests/url_utils.test.ts index e4b8a29c..c9637dd4 100644 --- a/tests/url_utils.test.ts +++ b/tests/url_utils.test.ts @@ -145,6 +145,7 @@ describe("Loading + saving from URL query strings", () => { showScaleBar: true, showTimestamp: false, keepRangeBetweenDatasets: true, + backdropVisible: true, backdropBrightness: 75, backdropSaturation: 50, objectOpacity: 25, @@ -161,7 +162,7 @@ describe("Loading + saving from URL query strings", () => { }; const queryString = paramsToUrlQueryString(originalParams); const expectedQueryString = - "?collection=collection&dataset=dataset&feature=feature&track=25&t=14&filters=f1%3Am%3A0%3A0%2Cf2%3Aum%3ANaN%3ANaN%2Cf3%3Akm%3A0%3A1%2Cf4%3Amm%3A0.501%3A1000.485%2Cf5%3A%3Afff%2Cf6%3A%3A11&range=21.433%2C89.400&color=myMap-1!&palette-key=adobe&bg-sat=50&bg-brightness=75&fg-alpha=25&outlier-color=00ff00&outlier-mode=1&filter-color=ff0000&filter-mode=0&tab=filters&scalebar=1×tamp=0&path=1&keep-range=1&bg-key=some_backdrop&scatter-range=all&scatter-x=x%20axis%20name&scatter-y=y%20axis%20name"; + "?collection=collection&dataset=dataset&feature=feature&track=25&t=14&filters=f1%3Am%3A0%3A0%2Cf2%3Aum%3ANaN%3ANaN%2Cf3%3Akm%3A0%3A1%2Cf4%3Amm%3A0.501%3A1000.485%2Cf5%3A%3Afff%2Cf6%3A%3A11&range=21.433%2C89.400&color=myMap-1!&palette-key=adobe&bg-sat=50&bg-brightness=75&fg-alpha=25&outlier-color=00ff00&outlier-mode=1&filter-color=ff0000&filter-mode=0&tab=filters&bg=1&scalebar=1×tamp=0&path=1&keep-range=1&bg-key=some_backdrop&scatter-range=all&scatter-x=x%20axis%20name&scatter-y=y%20axis%20name"; expect(queryString).equals(expectedQueryString); const parsedParams = loadFromUrlSearchParams(new URLSearchParams(queryString)); From 9dbadf932f61d87d61db4eb07e5adb5fb3736946 Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Wed, 27 Nov 2024 16:27:22 -0800 Subject: [PATCH 06/11] refactor: Fixed linting, removed unused CSS variables --- src/components/AppStyle.tsx | 2 -- src/components/CanvasWrapper.tsx | 29 +++++++++++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/AppStyle.tsx b/src/components/AppStyle.tsx index 20f27721..4daf755f 100644 --- a/src/components/AppStyle.tsx +++ b/src/components/AppStyle.tsx @@ -165,8 +165,6 @@ const CssContainer = styled.div` --color-text-theme-dark: ${theme.color.themeDark}; --color-text-link: ${theme.color.text.link}; --color-text-link-hover: ${theme.color.text.linkHover}; - --color-text-dark-link: ${theme.color.text.darkLink}; - --color-text-dark-link-hover: ${theme.color.text.darkLinkHover}; /* Layout */ --color-background-alt: ${theme.color.layout.backgroundAlt}; diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 0a168b7b..0cb143ff 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -1,4 +1,4 @@ -import { HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; +import { HomeOutlined, SettingOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; import { Tooltip, TooltipProps } from "antd"; import React, { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; @@ -603,18 +603,27 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem const onViewerSettingsLinkClicked = (): void => { props.updateConfig({ openTab: TabType.SETTINGS }); }; - const backdropTooltipContents: ReactNode[] = [ - props.selectedBackdropKey === null - ? "(No backdrops available)" - : props.dataset?.getBackdropData().get(props.selectedBackdropKey)?.name, + + const backdropTooltipContents: ReactNode[] = []; + backdropTooltipContents.push( + + {props.selectedBackdropKey === null + ? "(No backdrops available)" + : props.dataset?.getBackdropData().get(props.selectedBackdropKey)?.name} + + ); + // Link to viewer settings + backdropTooltipContents.push( - Viewer settings {">"} Backdrop - , - ]; + + {" Viewer settings > Backdrop"} + + ); return ( Date: Wed, 27 Nov 2024 16:33:02 -0800 Subject: [PATCH 07/11] fix: Viewer will select the default backdrop for a dataset on load --- src/Viewer.tsx | 6 ++++-- src/colorizer/Dataset.ts | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Viewer.tsx b/src/Viewer.tsx index b5bfbfee..b73b159c 100644 --- a/src/Viewer.tsx +++ b/src/Viewer.tsx @@ -460,9 +460,11 @@ function Viewer(): ReactElement { await setFrame(newFrame); setFindTrackInput(""); - if (selectedBackdropKey && !newDataset.hasBackdrop(selectedBackdropKey)) { - setSelectedBackdropKey(null); + + if (!selectedBackdropKey || (selectedBackdropKey && !newDataset.hasBackdrop(selectedBackdropKey))) { + setSelectedBackdropKey(newDataset.getDefaultBackdropKey()); } + setSelectedTrack(null); setDatasetOpen(true); setFeatureThresholds(validateThresholds(newDataset, featureThresholds)); diff --git a/src/colorizer/Dataset.ts b/src/colorizer/Dataset.ts index ab39c08a..06682d70 100644 --- a/src/colorizer/Dataset.ts +++ b/src/colorizer/Dataset.ts @@ -330,6 +330,10 @@ export default class Dataset { return loadedFrame; } + public getDefaultBackdropKey(): string | null { + return this.backdropData.keys().next().value ?? null; + } + public hasBackdrop(key: string): boolean { return this.backdropData.has(key); } From b9a6be66ca2b0c8887ca1124d9c6b68d7493e28a Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Mon, 2 Dec 2024 14:14:44 -0800 Subject: [PATCH 08/11] refactor: Cleanup on SVG icons, changed link color and styling, comments --- src/assets/images/icon-images-slash.svg | 8 +++++--- src/assets/images/icon-images.svg | 8 +++++--- src/assets/images/index.ts | 4 ++-- src/components/AppStyle.tsx | 5 ++--- src/components/Buttons/LinkStyleButton.tsx | 4 ++++ src/components/CanvasWrapper.tsx | 19 +++++++++---------- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/assets/images/icon-images-slash.svg b/src/assets/images/icon-images-slash.svg index e9452140..24158ba5 100644 --- a/src/assets/images/icon-images-slash.svg +++ b/src/assets/images/icon-images-slash.svg @@ -1,3 +1,5 @@ - - - + \ No newline at end of file diff --git a/src/assets/images/icon-images.svg b/src/assets/images/icon-images.svg index 2ca0a826..db2b2965 100644 --- a/src/assets/images/icon-images.svg +++ b/src/assets/images/icon-images.svg @@ -1,3 +1,5 @@ - - - + \ No newline at end of file diff --git a/src/assets/images/index.ts b/src/assets/images/index.ts index 88e15c66..ae0f0081 100644 --- a/src/assets/images/index.ts +++ b/src/assets/images/index.ts @@ -1,9 +1,9 @@ -import AicsLogoAndNameSVGAsset from "./AICS-logo-and-name.svg?react"; import AicsLogoSVGAsset from "./AICS-logo.svg?react"; +import AicsLogoAndNameSVGAsset from "./AICS-logo-and-name.svg?react"; import DropdownSVGAsset from "./dropdown-arrow.svg?react"; import ExportIconSVGAsset from "./icon-export.svg?react"; -import ImagesSlashIconSVGAsset from "./icon-images-slash.svg?react"; import ImagesIconSVGAsset from "./icon-images.svg?react"; +import ImagesSlashIconSVGAsset from "./icon-images-slash.svg?react"; import SwitchIconSVGAsset from "./icon-switch-arrows.svg?react"; import NoImageSVGAsset from "./no-image.svg?react"; import SpinBoxHandleDownSVGAsset from "./spin_box-handle-down.svg?react"; diff --git a/src/components/AppStyle.tsx b/src/components/AppStyle.tsx index 4daf755f..6a8e7f89 100644 --- a/src/components/AppStyle.tsx +++ b/src/components/AppStyle.tsx @@ -33,7 +33,6 @@ const palette = { successLight: "#f6ffed", errorMedium: "#ffa39e", errorLight: "#fff2f0", - infoDark: "#69c0ff", infoMedium: "#91d5ff", infoLight: "#e6f4ff", warningMedium: "#ffe58f", @@ -60,8 +59,8 @@ const theme = { theme: palette.theme, link: palette.link, linkHover: palette.linkDark, - darkLink: palette.infoMedium, - darkLinkHover: palette.infoDark, + darkLink: palette.gray20, + darkLinkHover: palette.gray10, }, layout: { background: palette.gray0, diff --git a/src/components/Buttons/LinkStyleButton.tsx b/src/components/Buttons/LinkStyleButton.tsx index 7f752799..c4dbc56f 100644 --- a/src/components/Buttons/LinkStyleButton.tsx +++ b/src/components/Buttons/LinkStyleButton.tsx @@ -1,5 +1,9 @@ import styled, { css } from "styled-components"; +/** + * A button styled to remove default styles and visually resemble a link. + * This ensures the button still follows accessibility semantics. + */ export const LinkStyleButton = styled.button<{ $color?: string; $hoverColor?: string; diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 0cb143ff..268a4fe2 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -1,4 +1,4 @@ -import { HomeOutlined, SettingOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; +import { HomeOutlined, ZoomInOutlined, ZoomOutOutlined } from "@ant-design/icons"; import { Tooltip, TooltipProps } from "antd"; import React, { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; @@ -32,7 +32,7 @@ const MAX_INVERSE_ZOOM = 2; // 0.5x zoom const MIN_INVERSE_ZOOM = 0.1; // 10x zoom function TooltipWithSubtext( - props: TooltipProps & { title: ReactNode; subtext?: ReactNode; subtextList?: ReactNode[] } + props: TooltipProps & { title: ReactNode; subtitle?: ReactNode; subtitleList?: ReactNode[] } ): ReactElement { const divRef = useRef(null); return ( @@ -43,11 +43,11 @@ function TooltipWithSubtext( title={ <>

{props.title}

- {props.subtext &&

{props.subtext}

} - {props.subtextList && - props.subtextList.map((subtext, i) => ( + {props.subtitle &&

{props.subtitle}

} + {props.subtitleList && + props.subtitleList.map((text, i) => (

- {subtext} + {text}

))} @@ -620,7 +620,6 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem $hoverColor={theme.color.text.darkLinkHover} onClick={onViewerSettingsLinkClicked} > - {" Viewer settings > Backdrop"} ); @@ -657,7 +656,7 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem Reset view
- + { @@ -668,7 +667,7 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem Zoom in - + { @@ -685,7 +684,7 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem Date: Mon, 2 Dec 2024 14:14:58 -0800 Subject: [PATCH 09/11] fix: Backdrop visibility turns off when switching to datasets that don't have backdrops --- src/Viewer.tsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Viewer.tsx b/src/Viewer.tsx index b73b159c..e28098eb 100644 --- a/src/Viewer.tsx +++ b/src/Viewer.tsx @@ -95,8 +95,22 @@ function Viewer(): ReactElement { const [featureKey, setFeatureKey] = useState(""); const [selectedTrack, setSelectedTrack] = useState(null); const [currentFrame, setCurrentFrame] = useState(0); + /** Backdrop key is null if the dataset has no backdrops, or during initialization. */ const [selectedBackdropKey, setSelectedBackdropKey] = useState(null); + useEffect(() => { + // Switch to default backdrop if the dataset has one and none is currently selected. + // If the dataset has no backdrops, hide the backdrop. + if (dataset && selectedBackdropKey === null) { + const defaultBackdropKey = dataset.getDefaultBackdropKey(); + if (defaultBackdropKey) { + setSelectedBackdropKey(defaultBackdropKey); + } else { + updateConfig({ backdropVisible: false }); + } + } + }); + // TODO: Save these settings in local storage // Use reducer here in case multiple updates happen simultaneously const [config, updateConfig] = useReducer( @@ -461,7 +475,12 @@ function Viewer(): ReactElement { setFindTrackInput(""); - if (!selectedBackdropKey || (selectedBackdropKey && !newDataset.hasBackdrop(selectedBackdropKey))) { + // Switch to the new dataset's default backdrop if the current one is not in the + // new dataset. `selectedBackdropKey` is null only if the current dataset has no backdrops. + if ( + selectedBackdropKey === null || + (selectedBackdropKey !== null && !newDataset.hasBackdrop(selectedBackdropKey)) + ) { setSelectedBackdropKey(newDataset.getDefaultBackdropKey()); } From 83146ffcd3e0923b365e6f21933f7d372df0cd13 Mon Sep 17 00:00:00 2001 From: Peyton Lee Date: Mon, 2 Dec 2024 14:23:49 -0800 Subject: [PATCH 10/11] refactor: Code cleanup --- src/Viewer.tsx | 2 +- src/components/CanvasWrapper.tsx | 2 +- src/components/Tabs/SettingsTab.tsx | 17 ++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Viewer.tsx b/src/Viewer.tsx index e28098eb..9bbd354b 100644 --- a/src/Viewer.tsx +++ b/src/Viewer.tsx @@ -101,7 +101,7 @@ function Viewer(): ReactElement { useEffect(() => { // Switch to default backdrop if the dataset has one and none is currently selected. // If the dataset has no backdrops, hide the backdrop. - if (dataset && selectedBackdropKey === null) { + if (dataset && (selectedBackdropKey === null || !dataset.hasBackdrop(selectedBackdropKey))) { const defaultBackdropKey = dataset.getDefaultBackdropKey(); if (defaultBackdropKey) { setSelectedBackdropKey(defaultBackdropKey); diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 268a4fe2..6b7e99ed 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -695,7 +695,7 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem disabled={props.selectedBackdropKey === null} > {props.config.backdropVisible ? : } - Show backdrop + {props.config.backdropVisible ? "Hide backdrop" : "Show backdrop"} diff --git a/src/components/Tabs/SettingsTab.tsx b/src/components/Tabs/SettingsTab.tsx index ec906134..ea58aa68 100644 --- a/src/components/Tabs/SettingsTab.tsx +++ b/src/components/Tabs/SettingsTab.tsx @@ -41,11 +41,10 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { }) : []; - let isBackdropDisabled = false; - const selectedBackdropKey = props.selectedBackdropKey ?? NO_BACKDROP.key; - if (backdropOptions.length === 0 || props.selectedBackdropKey === null) { - // Add placeholder for no backdrop. - isBackdropDisabled = true; + const isBackdropDisabled = backdropOptions.length === 0 || props.selectedBackdropKey === null; + const isBackdropOptionsDisabled = isBackdropDisabled || !props.config.backdropVisible; + let selectedBackdropKey = props.selectedBackdropKey ?? NO_BACKDROP.key; + if (isBackdropDisabled) { backdropOptions.push(NO_BACKDROP); } @@ -68,7 +67,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { selected={selectedBackdropKey} items={backdropOptions} onChange={props.setSelectedBackdropKey} - disabled={!props.config.backdropVisible || isBackdropDisabled} + disabled={isBackdropOptionsDisabled} />
@@ -83,7 +82,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { onChange={(brightness: number) => props.updateConfig({ backdropBrightness: brightness })} marks={[100]} numberFormatter={(value?: number) => `${value}%`} - disabled={!props.config.backdropVisible || isBackdropDisabled} + disabled={isBackdropOptionsDisabled} /> @@ -100,7 +99,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { onChange={(saturation: number) => props.updateConfig({ backdropSaturation: saturation })} marks={[100]} numberFormatter={(value?: number) => `${value}%`} - disabled={!props.config.backdropVisible || isBackdropDisabled} + disabled={isBackdropOptionsDisabled} /> @@ -108,7 +107,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement {
Date: Mon, 2 Dec 2024 14:33:42 -0800 Subject: [PATCH 11/11] refactor: Code cleanup --- src/Viewer.tsx | 7 +++---- src/components/CanvasWrapper.tsx | 2 +- src/components/Tabs/SettingsTab.tsx | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Viewer.tsx b/src/Viewer.tsx index 9bbd354b..74f388aa 100644 --- a/src/Viewer.tsx +++ b/src/Viewer.tsx @@ -103,13 +103,12 @@ function Viewer(): ReactElement { // If the dataset has no backdrops, hide the backdrop. if (dataset && (selectedBackdropKey === null || !dataset.hasBackdrop(selectedBackdropKey))) { const defaultBackdropKey = dataset.getDefaultBackdropKey(); - if (defaultBackdropKey) { - setSelectedBackdropKey(defaultBackdropKey); - } else { + setSelectedBackdropKey(defaultBackdropKey); + if (!defaultBackdropKey) { updateConfig({ backdropVisible: false }); } } - }); + }, [dataset, selectedBackdropKey]); // TODO: Save these settings in local storage // Use reducer here in case multiple updates happen simultaneously diff --git a/src/components/CanvasWrapper.tsx b/src/components/CanvasWrapper.tsx index 6b7e99ed..d9b0e27f 100644 --- a/src/components/CanvasWrapper.tsx +++ b/src/components/CanvasWrapper.tsx @@ -620,7 +620,7 @@ export default function CanvasWrapper(inputProps: CanvasWrapperProps): ReactElem $hoverColor={theme.color.text.darkLinkHover} onClick={onViewerSettingsLinkClicked} > - {" Viewer settings > Backdrop"} + {"Viewer settings > Backdrop"} (opens settings tab) ); diff --git a/src/components/Tabs/SettingsTab.tsx b/src/components/Tabs/SettingsTab.tsx index ea58aa68..8a3abfa9 100644 --- a/src/components/Tabs/SettingsTab.tsx +++ b/src/components/Tabs/SettingsTab.tsx @@ -46,6 +46,7 @@ export default function SettingsTab(props: SettingsTabProps): ReactElement { let selectedBackdropKey = props.selectedBackdropKey ?? NO_BACKDROP.key; if (isBackdropDisabled) { backdropOptions.push(NO_BACKDROP); + selectedBackdropKey = NO_BACKDROP.key; } return (