From 7808c25c0960e0f522e0b52b3155fc1d1d7b2add Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 4 Jan 2025 00:20:31 +0000 Subject: [PATCH 1/3] fix code selection in property tooltip --- .../features/style-panel/property-label.tsx | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/builder/app/builder/features/style-panel/property-label.tsx b/apps/builder/app/builder/features/style-panel/property-label.tsx index 2a7fdfe74d20..7c0f1fb21bfc 100644 --- a/apps/builder/app/builder/features/style-panel/property-label.tsx +++ b/apps/builder/app/builder/features/style-panel/property-label.tsx @@ -1,6 +1,6 @@ import { atom } from "nanostores"; import { useStore } from "@nanostores/react"; -import { useState, type ReactNode } from "react"; +import { useRef, useState, type ReactNode } from "react"; import { AlertIcon, ResetIcon } from "@webstudio-is/icons"; import { hyphenateProperty, @@ -251,14 +251,26 @@ export const PropertyLabel = ({ batch.publish(); }; const styleConfig = styleConfigByName(properties[0]); + const preventCloseRef = useRef(false); return ( event.preventDefault()} + onOpenChange={(isOpen) => { + if (isOpen === false && preventCloseRef.current) { + return; + } + setIsOpen(isOpen); + }} + onPointerDown={() => { + // Prevent closing tooltip on content click. + // Can't use preventDefault() because it will prevent selecting code for copy/paste. + preventCloseRef.current = true; + requestAnimationFrame(() => { + preventCloseRef.current = false; + }); + }} triggerProps={{ onClick: (event) => { if (event.altKey) { From 337696c9b2574de2fd89595568007300212718c0 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 5 Jan 2025 13:55:33 +0000 Subject: [PATCH 2/3] Allows selecting text and clicking on links in all tooltips --- .../features/style-panel/property-label.tsx | 16 +---------- .../design-system/src/components/tooltip.tsx | 28 +++++++++++++++---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apps/builder/app/builder/features/style-panel/property-label.tsx b/apps/builder/app/builder/features/style-panel/property-label.tsx index 7c0f1fb21bfc..5b6f5c163d36 100644 --- a/apps/builder/app/builder/features/style-panel/property-label.tsx +++ b/apps/builder/app/builder/features/style-panel/property-label.tsx @@ -251,26 +251,12 @@ export const PropertyLabel = ({ batch.publish(); }; const styleConfig = styleConfigByName(properties[0]); - const preventCloseRef = useRef(false); return ( { - if (isOpen === false && preventCloseRef.current) { - return; - } - setIsOpen(isOpen); - }} - onPointerDown={() => { - // Prevent closing tooltip on content click. - // Can't use preventDefault() because it will prevent selecting code for copy/paste. - preventCloseRef.current = true; - requestAnimationFrame(() => { - preventCloseRef.current = false; - }); - }} + onOpenChange={setIsOpen} triggerProps={{ onClick: (event) => { if (event.altKey) { diff --git a/packages/design-system/src/components/tooltip.tsx b/packages/design-system/src/components/tooltip.tsx index 9d8dce4b17e6..11632596eee9 100644 --- a/packages/design-system/src/components/tooltip.tsx +++ b/packages/design-system/src/components/tooltip.tsx @@ -1,4 +1,4 @@ -import type { Ref, ComponentProps, ReactNode } from "react"; +import type { Ref, ComponentProps, ReactNode, MouseEventHandler } from "react"; import { forwardRef, useEffect, useRef, useState } from "react"; import { autoUpdate, @@ -75,6 +75,7 @@ export const Tooltip = forwardRef( onOpenChange?.(open); }, }); + const preventCloseRef = useRef(false); /** * When the mouse leaves Tooltip.Content and hovers over an iframe, the Radix Tooltip stays open. @@ -89,13 +90,19 @@ export const Tooltip = forwardRef( * * The simpler solution with fewer side effects is to close the tooltip on mouse leave. */ - const handleMouseEnterComposed: React.MouseEventHandler = ( - event - ) => { + const handleMouseLeave: MouseEventHandler = (event) => { setOpen(false); props.onMouseLeave?.(event); }; + const handleOpenChange = (open: boolean) => { + if (open === false && preventCloseRef.current) { + return; + } + + setOpen(open); + }; + // There's no way to prevent a rendered trigger from opening. // This causes delay issues when an invisible tooltip forces other tooltips to show immediately. return content == null ? ( @@ -104,7 +111,7 @@ export const Tooltip = forwardRef( @@ -149,7 +156,16 @@ export const Tooltip = forwardRef( collisionPadding={8} arrowPadding={8} {...props} - onMouseLeave={handleMouseEnterComposed} + onMouseLeave={handleMouseLeave} + onPointerDown={() => { + // Allows clicking on links or selecting text inside the tooltip. + // Prevent closing tooltip on content click. + // Can't use preventDefault() because it will prevent selecting code for copy/paste. + preventCloseRef.current = true; + requestAnimationFrame(() => { + preventCloseRef.current = false; + }); + }} > {typeof content === "string" ? {content} : content} From 07baea81144475e67c34e9af440a9c45b1ee002f Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 6 Jan 2025 19:05:09 +0000 Subject: [PATCH 3/3] lint --- .../builder/app/builder/features/style-panel/property-label.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/builder/app/builder/features/style-panel/property-label.tsx b/apps/builder/app/builder/features/style-panel/property-label.tsx index 5b6f5c163d36..ebe4f40892cb 100644 --- a/apps/builder/app/builder/features/style-panel/property-label.tsx +++ b/apps/builder/app/builder/features/style-panel/property-label.tsx @@ -1,6 +1,6 @@ import { atom } from "nanostores"; import { useStore } from "@nanostores/react"; -import { useRef, useState, type ReactNode } from "react"; +import { useState, type ReactNode } from "react"; import { AlertIcon, ResetIcon } from "@webstudio-is/icons"; import { hyphenateProperty,