Skip to content

Commit

Permalink
Fix persistent zoom controls visibility after level selection (#756)
Browse files Browse the repository at this point in the history
This PR addresses the issue where zoom controls remained visible after a
selection from the ZoomLevelSelect.
The PR removes the ButtonGroupLeft component, which was responsible for
detecting mouse events and moving the `.button-group-left-container`
during mouse hover. We now implement a simpler solution that primarily
uses CSS and the `isSelectOpen` state.

Fixes #747 

### Test plan:
- tested regular and rapid interaction with the zoom controls to ensure
smooth functionality
- ensured that the zoom controls no longer appears when the cursor
quickly crosses its area unintentionally
- ensured that controls automatically hide after a selection from the
ZoomLevelSelect

|Before|After|
|-|-|
|<video
src="https://github.com/user-attachments/assets/10922c47-c3b2-453f-b5cc-c9d5f1c64cc7"
/>|<video
src="https://github.com/user-attachments/assets/a7fc3883-4d03-41f6-b447-289ca0236e80"/>|
  • Loading branch information
p-malecki authored Nov 22, 2024
1 parent 644656f commit a0f6b0c
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 72 deletions.
13 changes: 6 additions & 7 deletions packages/vscode-extension/src/webview/components/Preview.css
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,15 @@
transform: translateY(-50%);
}

.button-group-left-container {
.button-group-left {
display: flex;
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
transform: translate(-40px, 21px);
transition: transform 0.3s ease 0.1s;
}

.button-group-left {
display: flex;
transition: 0.2s;
transform: translateX(-40px);
.button-group-left-wrapper:hover .button-group-left,
.button-group-left:has(.zoom-controls.select-open) {
transform: translate(0px, 21px);
}
73 changes: 12 additions & 61 deletions packages/vscode-extension/src/webview/components/Preview.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import {
useState,
useRef,
useEffect,
MouseEvent,
forwardRef,
RefObject,
ReactNode,
useCallback,
} from "react";
import { useState, useRef, useEffect, MouseEvent, forwardRef, RefObject, useCallback } from "react";
import clamp from "lodash/clamp";
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react";
import { Resizable } from "re-resizable";
Expand Down Expand Up @@ -44,9 +35,6 @@ declare module "react" {
}
}

const SHOW_ZOOM_CONTROLS_DELAY_MS = 100;
const HIDE_ZOOM_CONTROLS_DELAY_MS = 200;

function useKeyPresses() {
const pressedKeys = useRef(new Set<number>());
const { project } = useProject();
Expand Down Expand Up @@ -190,46 +178,6 @@ function TouchPointIndicator({ isPressing }: { isPressing: boolean }) {
return <div className={`touch-indicator ${isPressing ? "pressed" : ""}`}></div>;
}

type ButtonGroupLeftProps = {
children: ReactNode;
};

function ButtonGroupLeft({ children }: ButtonGroupLeftProps) {
const [isMouseOver, setIsMouseOver] = useState(false);

const showButtonGroupTimeout = useRef<NodeJS.Timeout | undefined>();
const hideButtonGroupTimeout = useRef<NodeJS.Timeout | undefined>();

const onMouseEnter = () => {
clearTimeout(hideButtonGroupTimeout.current);
showButtonGroupTimeout.current = setTimeout(() => {
setIsMouseOver(true);
}, SHOW_ZOOM_CONTROLS_DELAY_MS);
};

const onMouseLeave = () => {
clearTimeout(showButtonGroupTimeout.current);
hideButtonGroupTimeout.current = setTimeout(() => {
setIsMouseOver(false);
}, HIDE_ZOOM_CONTROLS_DELAY_MS);
};

return (
<div
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
className="button-group-left-wrapper">
<div className="button-group-left-container">
<div
style={isMouseOver ? { transform: "translateX(0px)" } : {}}
className="button-group-left">
{children}
</div>
</div>
</div>
);
}

type Props = {
isInspecting: boolean;
inspectFrame: Frame | null;
Expand Down Expand Up @@ -755,14 +703,17 @@ function Preview({
</Resizable>
)}
</div>
<ButtonGroupLeft>
<ZoomControls
zoomLevel={zoomLevel}
onZoomChanged={onZoomChanged}
device={device}
wrapperDivRef={wrapperDivRef}
/>
</ButtonGroupLeft>

<div className="button-group-left-wrapper">
<div className="button-group-left">
<ZoomControls
zoomLevel={zoomLevel}
onZoomChanged={onZoomChanged}
device={device}
wrapperDivRef={wrapperDivRef}
/>
</div>
</div>
</>
);
}
Expand Down
16 changes: 12 additions & 4 deletions packages/vscode-extension/src/webview/components/ZoomControls.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RefObject, useCallback } from "react";
import { RefObject, useCallback, useState } from "react";
import * as Select from "@radix-ui/react-select";
import IconButton from "./shared/IconButton";
import { ZoomLevelType } from "../../common/Project";
Expand All @@ -15,9 +15,10 @@ type ZoomControlsProps = {
onZoomChanged: (zoom: ZoomLevelType) => void;
device?: DeviceProperties;
wrapperDivRef?: RefObject<HTMLDivElement>;
setIsSelectOpen?: (open: boolean) => void;
};

const ZoomLevelSelect = ({ zoomLevel, onZoomChanged }: ZoomControlsProps) => {
const ZoomLevelSelect = ({ zoomLevel, onZoomChanged, setIsSelectOpen }: ZoomControlsProps) => {
const onValueChange = useCallback(
(e: string) => {
if (e === "Fit") {
Expand All @@ -34,6 +35,7 @@ const ZoomLevelSelect = ({ zoomLevel, onZoomChanged }: ZoomControlsProps) => {

return (
<Select.Root
onOpenChange={setIsSelectOpen}
onValueChange={onValueChange}
value={zoomLevel === "Fit" ? "Fit" : zoomLevel.toString()}>
<Select.Trigger className="zoom-select-trigger" disabled={false}>
Expand Down Expand Up @@ -67,6 +69,8 @@ const ZoomLevelSelect = ({ zoomLevel, onZoomChanged }: ZoomControlsProps) => {
};

function ZoomControls({ zoomLevel, onZoomChanged, device, wrapperDivRef }: ZoomControlsProps) {
const [isSelectOpen, setIsSelectOpen] = useState(false);

function handleZoom(shouldIncrease: boolean) {
let currentZoomLevel;
if (zoomLevel === "Fit") {
Expand All @@ -87,7 +91,7 @@ function ZoomControls({ zoomLevel, onZoomChanged, device, wrapperDivRef }: ZoomC
}

return (
<div className="zoom-controls">
<div className={`zoom-controls ${isSelectOpen ? "select-open" : ""}`}>
<IconButton
className="zoom-in-button"
tooltip={{
Expand All @@ -97,7 +101,11 @@ function ZoomControls({ zoomLevel, onZoomChanged, device, wrapperDivRef }: ZoomC
onClick={() => handleZoom(true)}>
<span className="codicon codicon-zoom-in" />
</IconButton>
<ZoomLevelSelect zoomLevel={zoomLevel} onZoomChanged={onZoomChanged} />
<ZoomLevelSelect
zoomLevel={zoomLevel}
onZoomChanged={onZoomChanged}
setIsSelectOpen={setIsSelectOpen}
/>
<IconButton
className="zoom-out-button"
tooltip={{
Expand Down

0 comments on commit a0f6b0c

Please sign in to comment.