From b867e67a9133b236d4f8e9f81d8ed6109ca5f5ab Mon Sep 17 00:00:00 2001 From: Ivan Starkov Date: Thu, 12 Dec 2024 20:43:24 +0700 Subject: [PATCH] feat: Experimental INP fix (#4560) ## Description https://vercel.com/blog/demystifying-inp-new-tools-and-actionable-insights ## new https://inp-inp-inp-inp.wstd.work/ ## prev https://no-inp-no-no.wstd.work/ ## 20x slowdown new (click) image ## 20x slowdown new (keyboard) image ## 20x slowdown old (click) image ## 20x slowdown old (keyboard) image ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file --- .../sdk-components-react-radix/package.json | 4 +- .../sdk-components-react-radix/src/dialog.tsx | 30 +++++++++++-- .../sdk-components-react-radix/src/tabs.tsx | 43 ++++++++++++++----- packages/sdk-components-react/package.json | 3 +- .../src/vimeo-play-button.tsx | 11 ++++- pnpm-lock.yaml | 14 ++++++ 6 files changed, 88 insertions(+), 17 deletions(-) diff --git a/packages/sdk-components-react-radix/package.json b/packages/sdk-components-react-radix/package.json index d60459e0490b..952525fb0218 100644 --- a/packages/sdk-components-react-radix/package.json +++ b/packages/sdk-components-react-radix/package.json @@ -59,10 +59,12 @@ "@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.4", + "@radix-ui/react-use-controllable-state": "^1.1.0", "@webstudio-is/css-engine": "workspace:*", "@webstudio-is/icons": "workspace:*", "@webstudio-is/react-sdk": "workspace:*", - "@webstudio-is/sdk": "workspace:*" + "@webstudio-is/sdk": "workspace:*", + "await-interaction-response": "^0.0.2" }, "devDependencies": { "@types/node": "^22.9.3", diff --git a/packages/sdk-components-react-radix/src/dialog.tsx b/packages/sdk-components-react-radix/src/dialog.tsx index 2541bcc822ef..af249abd2b5b 100644 --- a/packages/sdk-components-react-radix/src/dialog.tsx +++ b/packages/sdk-components-react-radix/src/dialog.tsx @@ -7,6 +7,7 @@ import { useEffect, useRef, useContext, + useCallback, } from "react"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { @@ -14,6 +15,8 @@ import { getClosestInstance, type Hook, } from "@webstudio-is/react-sdk/runtime"; +import { useControllableState } from "@radix-ui/react-use-controllable-state"; +import interactionResponse from "await-interaction-response"; /** * Naive heuristic to determine if a click event will cause navigate @@ -49,9 +52,22 @@ export const Dialog = forwardRef< HTMLDivElement, Omit, "defaultOpen"> >((props, _ref) => { - const { open, onOpenChange } = props; const { renderer } = useContext(ReactSdkContext); + const [open, onOpenChange] = useControllableState({ + prop: props.open, + defaultProp: false, + onChange: props.onOpenChange, + }); + + const onOpenChangeHandler = useCallback( + async (open: boolean) => { + await interactionResponse(); + onOpenChange(open); + }, + [onOpenChange] + ); + /** * Close the dialog when a navigable link within it is clicked. */ @@ -76,15 +92,21 @@ export const Dialog = forwardRef< } if (target.closest('[role="dialog"]')) { - onOpenChange?.(false); + onOpenChangeHandler?.(false); } }; window.addEventListener("click", handleClick); return () => window.removeEventListener("click", handleClick); - }, [open, onOpenChange, renderer]); + }, [open, onOpenChangeHandler, renderer]); - return ; + return ( + + ); }); /** diff --git a/packages/sdk-components-react-radix/src/tabs.tsx b/packages/sdk-components-react-radix/src/tabs.tsx index b0183d57b1da..4e3f771f6021 100644 --- a/packages/sdk-components-react-radix/src/tabs.tsx +++ b/packages/sdk-components-react-radix/src/tabs.tsx @@ -1,20 +1,43 @@ -import { - type ComponentPropsWithoutRef, - type ForwardRefExoticComponent, - forwardRef, - type ComponentProps, - type RefAttributes, -} from "react"; +import { type ComponentPropsWithoutRef, forwardRef, useCallback } from "react"; import { Root, List, Trigger, Content } from "@radix-ui/react-tabs"; import { getClosestInstance, getIndexWithinAncestorFromComponentProps, type Hook, } from "@webstudio-is/react-sdk/runtime"; +import { useControllableState } from "@radix-ui/react-use-controllable-state"; +import interactionResponse from "await-interaction-response"; + +export const Tabs = forwardRef< + HTMLDivElement, + Omit, "value" | "onValueChange"> & { + value?: string; + onValueChange?: (value: string) => void; + } +>(({ defaultValue, ...props }, ref) => { + const [value, onValueChange] = useControllableState({ + prop: props.value, + defaultProp: defaultValue, + onChange: props.onValueChange, + }); -export const Tabs: ForwardRefExoticComponent< - Omit, "asChild"> & RefAttributes -> = Root; + const handleValueChange = useCallback( + async (value: string) => { + await interactionResponse(); + onValueChange(value); + }, + [onValueChange] + ); + + return ( + + ); +}); export const TabsList = List; diff --git a/packages/sdk-components-react/package.json b/packages/sdk-components-react/package.json index ceaec2aaa469..c1271c32c0a3 100644 --- a/packages/sdk-components-react/package.json +++ b/packages/sdk-components-react/package.json @@ -52,7 +52,8 @@ "@webstudio-is/react-sdk": "workspace:*", "@webstudio-is/sdk": "workspace:*", "colord": "^2.9.3", - "micromark": "^4.0.0" + "micromark": "^4.0.0", + "await-interaction-response": "^0.0.2" }, "devDependencies": { "@testing-library/react": "^14.2.2", diff --git a/packages/sdk-components-react/src/vimeo-play-button.tsx b/packages/sdk-components-react/src/vimeo-play-button.tsx index e12e440b09c3..0567d6eb7249 100644 --- a/packages/sdk-components-react/src/vimeo-play-button.tsx +++ b/packages/sdk-components-react/src/vimeo-play-button.tsx @@ -3,9 +3,11 @@ import { type ElementRef, type ComponentProps, useContext, + useCallback, } from "react"; import { VimeoContext } from "./vimeo"; import { Button, defaultTag } from "./button"; +import interactionResponse from "await-interaction-response"; export { defaultTag }; @@ -14,10 +16,17 @@ type Props = ComponentProps; export const VimeoPlayButton = forwardRef, Props>( (props, ref) => { const vimeoContext = useContext(VimeoContext); + + const handleClick = useCallback(async () => { + await interactionResponse(); + vimeoContext.onInitPlayer(); + }, [vimeoContext]); + if (vimeoContext.status !== "initial") { return; } - return