From 6adfd9b78d7726fea64da71eff6033c080bf46c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Berg=C3=A9?= Date: Sun, 3 Nov 2024 10:24:36 +0100 Subject: [PATCH] feat: changes overlay settings --- .../src/containers/Team/SubscribeDialog.tsx | 4 +- apps/frontend/src/pages/Build/BuildDetail.tsx | 55 ++++++++--- .../src/pages/Build/BuildDetailToolbar.tsx | 3 + .../src/pages/Build/BuildDiffColorState.tsx | 54 ++++++++++ .../src/pages/Build/BuildDiffList.tsx | 13 ++- .../frontend/src/pages/Build/BuildHotkeys.tsx | 6 ++ apps/frontend/src/pages/Build/BuildPage.tsx | 65 ++++++------ .../pages/Build/toolbar/SettingsButton.tsx | 98 +++++++++++++++++++ apps/frontend/src/pages/Signup.tsx | 6 +- apps/frontend/src/ui/ColorPicker.tsx | 53 ++++++++++ apps/frontend/src/ui/FormLabel.tsx | 21 ---- apps/frontend/src/ui/FormTextInput.tsx | 6 +- apps/frontend/src/ui/Label.tsx | 28 ++++++ apps/frontend/src/ui/Menu.tsx | 6 +- apps/frontend/src/ui/Popover.tsx | 11 ++- apps/frontend/src/ui/Slider.tsx | 84 ++++++++++++++++ apps/frontend/src/ui/TwicPicture.tsx | 14 +-- .../frontend/src/util/color-detection/hook.ts | 4 +- 18 files changed, 442 insertions(+), 89 deletions(-) create mode 100644 apps/frontend/src/pages/Build/BuildDiffColorState.tsx create mode 100644 apps/frontend/src/pages/Build/toolbar/SettingsButton.tsx create mode 100644 apps/frontend/src/ui/ColorPicker.tsx delete mode 100644 apps/frontend/src/ui/FormLabel.tsx create mode 100644 apps/frontend/src/ui/Label.tsx create mode 100644 apps/frontend/src/ui/Slider.tsx diff --git a/apps/frontend/src/containers/Team/SubscribeDialog.tsx b/apps/frontend/src/containers/Team/SubscribeDialog.tsx index 641081933..e26cc4b59 100644 --- a/apps/frontend/src/containers/Team/SubscribeDialog.tsx +++ b/apps/frontend/src/containers/Team/SubscribeDialog.tsx @@ -13,7 +13,7 @@ import { DialogTitle, DialogTrigger, } from "@/ui/Dialog"; -import { FormLabel } from "@/ui/FormLabel"; +import { Label } from "@/ui/Label"; import { Modal } from "@/ui/Modal"; import { StripeCheckoutButton } from "@/ui/StripeLink"; @@ -80,7 +80,7 @@ export function TeamSubscribeDialog({ Subscribe to Pro plan
- Team to subscribe + , "width" | "height"> & { - src: string; - width?: number | null | undefined; - height?: number | null | undefined; - onScaleChange?: (scale: number | null) => void; - }, -) { +type ScreenshotPictureProps = Omit< + React.ComponentProps, + "width" | "height" +> & { + src: string; + width?: number | null | undefined; + height?: number | null | undefined; + onScaleChange?: (scale: number | null) => void; +}; + +function ScreenshotPicture(props: ScreenshotPictureProps) { const { src, style, width, height, onScaleChange, ...attrs } = props; const transform = useZoomTransform(); const ref = useRef(null); @@ -413,7 +420,7 @@ const CompareScreenshot = ({ }) => { const { visible } = useBuildDiffVisibleState(); const { contained } = useBuildDiffFitState(); - const opacity = visible ? "" : "opacity-0"; + const opacity = visible ? "opacity-70" : "opacity-0"; switch (diff.status) { case ScreenshotDiffStatus.Added: { return ( @@ -550,7 +557,7 @@ function CompareScreenshotChanged(props: { )} {...getScreenshotPictureProps(diff.compareScreenshot!)} /> - + ); +} + /** * Detects colored areas in the image provided by the URL. */ @@ -607,9 +625,9 @@ function DiffIndicator(props: { style={{ transform: `scaleY(${props.scale})` }} > {rects.map((rect, index) => ( -
) { + const { color } = useBuildDiffColorState(); + return ( +
+ ); +} + const BuildScreenshots = memo( (props: { diff: Diff; build: BuildFragmentDocument }) => { const { viewMode } = useBuildDiffViewModeState(); diff --git a/apps/frontend/src/pages/Build/BuildDetailToolbar.tsx b/apps/frontend/src/pages/Build/BuildDetailToolbar.tsx index 3b94d005a..77412b84e 100644 --- a/apps/frontend/src/pages/Build/BuildDetailToolbar.tsx +++ b/apps/frontend/src/pages/Build/BuildDetailToolbar.tsx @@ -20,6 +20,7 @@ import { ViewportIndicator } from "./metadata/ViewportIndicator"; import { FitToggle } from "./toolbar/FitToggle"; import { NextButton, PreviousButton } from "./toolbar/NavButtons"; import { OverlayToggle } from "./toolbar/OverlayToggle"; +import { SettingsButton } from "./toolbar/SettingsButton"; import { TrackButtons } from "./toolbar/TrackButtons"; import { SplitViewToggle, ViewToggle } from "./toolbar/ViewToggle"; @@ -145,6 +146,8 @@ export const BuildDetailToolbar = memo(function BuildDetailToolbar({ )} /> + +
); diff --git a/apps/frontend/src/pages/Build/BuildDiffColorState.tsx b/apps/frontend/src/pages/Build/BuildDiffColorState.tsx new file mode 100644 index 000000000..a2d0baa3e --- /dev/null +++ b/apps/frontend/src/pages/Build/BuildDiffColorState.tsx @@ -0,0 +1,54 @@ +import { createContext, useContext, useMemo } from "react"; +import { invariant } from "@argos/util/invariant"; + +import { useStorageState } from "@/util/useStorageState"; + +type DiffColorContextValue = { + color: string; + setColor: React.Dispatch>; + opacity: number; + setOpacity: React.Dispatch>; +}; + +const DiffColorContext = createContext(null); + +export function useBuildDiffColorState() { + const context = useContext(DiffColorContext); + invariant( + context, + "useBuildDiffColorState must be used within a BuildDiffColorStateProvider", + ); + return context; +} + +export function useBuildDiffColorStyle() { + const { color, opacity } = useBuildDiffColorState(); + return { + filter: `drop-shadow(0 2000px 0 ${color})`, + transform: "translateY(-2000px)", + overflow: "hidden", + opacity, + }; +} + +export function BuildDiffColorStateProvider(props: { + children: React.ReactNode; +}) { + const [color, setColor] = useStorageState( + "preferences.overlay.color", + "#FD3A4A", + ); + const [opacity, setOpacity] = useStorageState( + "preferences.overlay.opacity", + 0.8, + ); + const value = useMemo( + (): DiffColorContextValue => ({ color, setColor, opacity, setOpacity }), + [color, setColor, opacity, setOpacity], + ); + return ( + + {props.children} + + ); +} diff --git a/apps/frontend/src/pages/Build/BuildDiffList.tsx b/apps/frontend/src/pages/Build/BuildDiffList.tsx index a258b2df9..09fdf62d1 100644 --- a/apps/frontend/src/pages/Build/BuildDiffList.tsx +++ b/apps/frontend/src/pages/Build/BuildDiffList.tsx @@ -30,8 +30,9 @@ import { Badge } from "@/ui/Badge"; import { Button, ButtonIcon, ButtonProps } from "@/ui/Button"; import { HotkeyTooltip } from "@/ui/HotkeyTooltip"; import { Truncable } from "@/ui/Truncable"; -import { TwicPicture } from "@/ui/TwicPicture"; +import { TwicPicture, TwicPictureProps } from "@/ui/TwicPicture"; +import { useBuildDiffColorStyle } from "./BuildDiffColorState"; import { getGroupLabel } from "./BuildDiffGroup"; import { Diff, @@ -309,10 +310,9 @@ const DiffImage = memo(({ diff }: { diff: Diff }) => { -
- { } }); +function DiffPicture(props: TwicPictureProps) { + const style = useBuildDiffColorStyle(); + return ; +} + const CardStack = ({ isFirst, isLast, diff --git a/apps/frontend/src/pages/Build/BuildHotkeys.tsx b/apps/frontend/src/pages/Build/BuildHotkeys.tsx index a9bf871f8..ef6b9449a 100644 --- a/apps/frontend/src/pages/Build/BuildHotkeys.tsx +++ b/apps/frontend/src/pages/Build/BuildHotkeys.tsx @@ -183,6 +183,12 @@ export function useBuildHotkey( return; } + if ( + document.getElementById("root")?.getAttribute("aria-hidden") === "true" + ) { + return; + } + // Ignore key events from menu & menuitem elements if ( event.target instanceof HTMLElement && diff --git a/apps/frontend/src/pages/Build/BuildPage.tsx b/apps/frontend/src/pages/Build/BuildPage.tsx index b7de2de3d..b24fddc9d 100644 --- a/apps/frontend/src/pages/Build/BuildPage.tsx +++ b/apps/frontend/src/pages/Build/BuildPage.tsx @@ -5,6 +5,7 @@ import { PaymentBanner } from "@/containers/PaymentBanner"; import { graphql } from "@/gql"; import { BuildContextProvider } from "./BuildContext"; +import { BuildDiffColorStateProvider } from "./BuildDiffColorState"; import { BuildDiffProvider } from "./BuildDiffState"; import { BuildHotkeysDialog } from "./BuildHotkeys"; import { useBuildHotkeysDialogState } from "./BuildHotkeysDialogState"; @@ -83,39 +84,41 @@ export const BuildPage = ({ params }: { params: BuildParams }) => { return ( - - - {hotkeysDialog && } -
- {data?.project?.account && ( - <> - - - - )} - - {project && build ? ( - + + + {hotkeysDialog && } +
+ {data?.project?.account && ( + <> + + + + )} + - ) : null} -
-
-
+ {project && build ? ( + + ) : null} +
+
+
+
); diff --git a/apps/frontend/src/pages/Build/toolbar/SettingsButton.tsx b/apps/frontend/src/pages/Build/toolbar/SettingsButton.tsx new file mode 100644 index 000000000..a469eb828 --- /dev/null +++ b/apps/frontend/src/pages/Build/toolbar/SettingsButton.tsx @@ -0,0 +1,98 @@ +import { memo } from "react"; +import { invariant } from "@argos/util/invariant"; +import { PaintbrushIcon } from "lucide-react"; +import { Heading } from "react-aria-components"; + +import { + ColorSwatch, + ColorSwatchPicker, + ColorSwatchPickerItem, +} from "@/ui/ColorPicker"; +import { Dialog, DialogBody, DialogTrigger } from "@/ui/Dialog"; +import { IconButton } from "@/ui/IconButton"; +import { Label } from "@/ui/Label"; +import { Popover } from "@/ui/Popover"; +import { Slider, SliderOutput, SliderThumb, SliderTrack } from "@/ui/Slider"; +import { Tooltip } from "@/ui/Tooltip"; + +import { useBuildDiffColorState } from "../BuildDiffColorState"; + +export const SettingsButton = memo(() => { + return ( + + + + + + + + + + + ); +}); + +function OverlaySettingsDialog() { + const { setColor, color, opacity, setOpacity } = useBuildDiffColorState(); + return ( + + + + Customize overlay + +
+
+ + setColor(color.toString("css"))} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ { + invariant(typeof value === "number", "Opacity must be a number"); + setOpacity(value / 100); + }} + > + + + + + + +
+
+
+ ); +} diff --git a/apps/frontend/src/pages/Signup.tsx b/apps/frontend/src/pages/Signup.tsx index cf9c0ae2b..0255b142f 100644 --- a/apps/frontend/src/pages/Signup.tsx +++ b/apps/frontend/src/pages/Signup.tsx @@ -6,7 +6,7 @@ import { useIsLoggedIn } from "@/containers/Auth"; import { LoginButtons } from "@/containers/LoginButtons"; import { Container } from "@/ui/Container"; import { Details, Summary } from "@/ui/Details"; -import { FormLabel } from "@/ui/FormLabel"; +import { Label } from "@/ui/Label"; import { RadioField } from "@/ui/Radio"; import { TextInput } from "@/ui/TextInput"; @@ -90,9 +90,9 @@ const SignupPage = () => { {accountType && (
- + + , +) { + return ( + + ); +}); + +export const ColorSwatchPickerItem = forwardRef(function ColorSwatchPickerItem( + props: ColorSwatchPickerItemProps, + ref: React.ForwardedRef, +) { + return ( + + ); +}); + +export const ColorSwatch = forwardRef(function ColorSwatch( + props: ColorSwatchProps, + ref: React.ForwardedRef, +) { + return ( + + ); +}); diff --git a/apps/frontend/src/ui/FormLabel.tsx b/apps/frontend/src/ui/FormLabel.tsx deleted file mode 100644 index 811d17e99..000000000 --- a/apps/frontend/src/ui/FormLabel.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { HTMLProps } from "react"; -import { clsx } from "clsx"; - -export const FormLabel = ({ - className, - invalid, - ...props -}: { - invalid?: boolean; -} & HTMLProps) => { - return ( -