Skip to content

Commit 71e0be8

Browse files
authored
feat: Transform Plus button to Minus in case of Alt button is pressed (#4490)
## Description ref #3994 <img width="260" alt="image" src="https://github.com/user-attachments/assets/88652fef-7229-42cb-bf64-33ec07249426"> <img width="260" alt="image" src="https://github.com/user-attachments/assets/e8199a7d-ebbc-4971-8641-d83424643ac8"> <img width="245" alt="image" src="https://github.com/user-attachments/assets/804b8ef5-4f71-42b3-b9d1-5cf034706a02"> ## 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
1 parent 2f15c21 commit 71e0be8

File tree

14 files changed

+726
-558
lines changed

14 files changed

+726
-558
lines changed

apps/builder/app/builder/builder.tsx

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useState, type ReactNode } from "react";
1+
import { useEffect, useState, type ReactNode } from "react";
22
import { useStore } from "@nanostores/react";
33
import { TooltipProvider } from "@radix-ui/react-tooltip";
44
import { usePublish, $publisher } from "~/shared/pubsub";
@@ -32,10 +32,10 @@ import {
3232
$authTokenPermissions,
3333
$publisherHost,
3434
$imageLoader,
35-
$textEditingInstanceSelector,
3635
$isDesignMode,
3736
$isContentMode,
3837
$userPlanFeatures,
38+
subscribeModifierKeys,
3939
} from "~/shared/nano-states";
4040
import { $settings, type Settings } from "./shared/client-settings";
4141
import { builderUrl, getCanvasUrl } from "~/shared/router-utils";
@@ -56,7 +56,6 @@ import {
5656
import { CloneProjectDialog } from "~/shared/clone-project";
5757
import type { TokenPermissions } from "@webstudio-is/authorization-token";
5858
import { useToastErrors } from "~/shared/error/toast-error";
59-
import { canvasApi } from "~/shared/canvas-api";
6059
import { loadBuilderData, setBuilderData } from "~/shared/builder-data";
6160
import { initBuilderApi } from "~/shared/builder-api";
6261
import { updateWebstudioData } from "~/shared/instance-utils";
@@ -68,6 +67,7 @@ import {
6867
initCopyPaste,
6968
initCopyPasteForContentEditMode,
7069
} from "~/shared/copy-paste/init-copy-paste";
70+
import { useInertHandlers } from "./shared/inert-handlers";
7171

7272
registerContainers();
7373

@@ -320,10 +320,12 @@ export const Builder = ({
320320
// @todo we need to forward the events from canvas to builder and avoid importing this
321321
// in both places
322322
initCopyPaste(abortController);
323+
subscribeModifierKeys({ signal: abortController.signal });
323324
}
324325

325326
if (isContentMode) {
326327
initCopyPasteForContentEditMode(abortController);
328+
subscribeModifierKeys({ signal: abortController.signal });
327329
}
328330

329331
return () => {
@@ -344,57 +346,15 @@ export const Builder = ({
344346

345347
const canvasUrl = getCanvasUrl();
346348

347-
/**
348-
* Prevents Lexical text editor from stealing focus during rendering.
349-
* Sets the inert attribute on the canvas body element and disables the text editor.
350-
*
351-
* This must be done synchronously to avoid the following issue:
352-
*
353-
* 1. Text editor is in edit state.
354-
* 2. User focuses on the builder (e.g., clicks any input).
355-
* 3. The text editor blur event triggers, causing a rerender on data change (data saved in onBlur).
356-
* 4. Text editor rerenders, stealing focus from the builder.
357-
* 5. Inert attribute is set asynchronously, but focus is already lost.
358-
*
359-
* Synchronous focusing and setInert prevent the text editor from focusing on render.
360-
* This cannot be handled inside the canvas because the text editor toolbar is in the builder and focus events in the canvas should be ignored.
361-
*
362-
* Use onPointerDown instead of onFocus because Radix focus lock triggers on text edit blur
363-
* before the focusin event when editing text inside a Radix dialog.
364-
*/
365-
const handlePointerDown = useCallback((event: React.PointerEvent) => {
366-
// Ignore toolbar focus events. See the onFocus handler in text-toolbar.tsx
367-
if (false === event.defaultPrevented) {
368-
canvasApi.setInert();
369-
$textEditingInstanceSelector.set(undefined);
370-
}
371-
}, []);
372-
373-
/**
374-
* Prevent Radix from stealing focus during editing in the style sources
375-
* For example, when the user select or create new style source item inside a dialog.
376-
*/
377-
const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
378-
if (event.target instanceof HTMLInputElement) {
379-
canvasApi.setInert();
380-
}
381-
}, []);
382-
383-
/**
384-
* Prevent Radix from stealing focus during editing in the settings panel.
385-
* For example, when the user modifies the text content of an H1 element inside a dialog.
386-
*/
387-
const handleInput = useCallback(() => {
388-
canvasApi.setInert();
389-
}, []);
349+
const inertHandlers = useInertHandlers();
390350

391351
return (
392352
<TooltipProvider>
393353
<div
394354
style={{ display: "contents" }}
395-
onPointerDown={handlePointerDown}
396-
onInput={handleInput}
397-
onKeyDown={handleKeyDown}
355+
onPointerDown={inertHandlers.onPointerDown}
356+
onInput={inertHandlers.onInput}
357+
onKeyDown={inertHandlers.onKeyDown}
398358
>
399359
<ChromeWrapper
400360
isPreviewMode={isPreviewMode}

apps/builder/app/builder/features/workspace/canvas-tools/canvas-tools.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { ResizeHandles } from "./resize-handles";
1919
import { MediaBadge } from "./media-badge";
2020
import { applyScale } from "./outline";
2121
import { $scale } from "~/builder/shared/nano-states";
22-
import { BlockChildHoveredInstanceOutline } from "./outline/editable-block-instance-outline";
22+
import { BlockChildHoveredInstanceOutline } from "./outline/block-instance-outline";
2323

2424
const containerStyle = css({
2525
position: "absolute",

0 commit comments

Comments
 (0)