From 737e42c758d47124298f41ae19905c46a78bfef7 Mon Sep 17 00:00:00 2001 From: Aleh Makaranka <29537473+cpoftea@users.noreply.github.com> Date: Thu, 26 Mar 2026 02:34:28 +0400 Subject: [PATCH] [RTE] Fix floating toolbar position in shadow DOM * Removed `SelectionUtils` from `helpers` (shadow selection helpers no longer used for toolbar placement) * Updated `PositionedToolbar`: positioned the floating toolbar using Plate `toDOMRange`; removed the obsolete props & funcs; added a note about Safari in Shadow DOM and `useEventEditorSelectors` * Updated `Toolbars` and `tablePlugin`: stopped passing `editor` into `FloatingToolbar` --- changelog.md | 1 + uui-editor/src/helpers.ts | 25 -------- .../src/implementation/PositionedToolbar.tsx | 58 ++++--------------- uui-editor/src/implementation/Toolbars.tsx | 2 +- .../src/plugins/tablePlugin/tablePlugin.tsx | 1 - 5 files changed, 14 insertions(+), 73 deletions(-) diff --git a/changelog.md b/changelog.md index eadccebba3..3947612421 100644 --- a/changelog.md +++ b/changelog.md @@ -32,6 +32,7 @@ * Fixed Property Explorer by providing values for properties that are not automatically resolved ([#2832](https://github.com/epam/UUI/issues/2832)) * Fixed tree table indentation when child rows have no checkbox: child rows now reserve consistent checkbox space for alignment ([#2844](https://github.com/epam/UUI/issues/2844)) * [Tooltip]: fixed tooltip not showing on keyboard focus for complex elements with focusable children (e.g. Switch) ([#2959](https://github.com/epam/UUI/issues/2959)) +* [RTE]: fixed incorrect floating toolbar position when in shadow DOM ([#3073](https://github.com/epam/UUI/issues/3073)) # 6.4.3 - 04.02.2026 diff --git a/uui-editor/src/helpers.ts b/uui-editor/src/helpers.ts index 292f634814..0f5c72361e 100644 --- a/uui-editor/src/helpers.ts +++ b/uui-editor/src/helpers.ts @@ -63,31 +63,6 @@ export const isEditorValueEmpty = (value: Value) => { return false; }; -export class SelectionUtils { - static getSelection(params: { shadowRoot: ShadowRoot | undefined }) { - const { shadowRoot } = params; - if (shadowRoot) { - if ((shadowRoot as any).getSelection) { - // Chrome/Edge. See details here: https://caniuse.com/mdn-api_shadowroot_getselection - return (shadowRoot as any).getSelection(); - } - } - // Works fine in other cases - return window.getSelection(); - } - - static getSelectionRange0(params: { selection: Selection; shadowRoot: ShadowRoot | undefined }) { - const { selection, shadowRoot } = params; - if (shadowRoot) { - if ((selection as any).getComposedRanges) { - // Webkit. https://w3c.github.io/selection-api/#dom-selection-getcomposedranges - return (selection as any).getComposedRanges(shadowRoot)[0]; - } - } - return selection.getRangeAt(0); - } -} - export const createTempEditor = (plugins: PlatePlugin[]): PlateEditor => { return createPlateEditor({ plugins: createPlugins((plugins).flat(), { diff --git a/uui-editor/src/implementation/PositionedToolbar.tsx b/uui-editor/src/implementation/PositionedToolbar.tsx index 39bfdacc6f..935a925744 100644 --- a/uui-editor/src/implementation/PositionedToolbar.tsx +++ b/uui-editor/src/implementation/PositionedToolbar.tsx @@ -1,16 +1,15 @@ import { Dropdown } from '@epam/uui'; -import { findNode, toDOMNode, useEditorState, useEventEditorSelectors } from '@udecode/plate-common'; +import { findNode, toDOMNode, toDOMRange, useEditorState, useEventEditorSelectors } from '@udecode/plate-common'; import { getCellTypes } from '@udecode/plate-table'; import cx from 'classnames'; -import React, { useRef } from 'react'; +import React from 'react'; import { Range } from 'slate'; import { offset } from '@floating-ui/react'; -import { isImageSelected, isTextSelected, SelectionUtils } from '../helpers'; +import { isImageSelected, isTextSelected } from '../helpers'; import css from './PositionedToolbar.module.scss'; interface ToolbarProps { - editor: any; children: any; isImage?: boolean; isTable?: boolean; @@ -18,8 +17,11 @@ interface ToolbarProps { } export function FloatingToolbar(props: ToolbarProps): any { - const ref = useRef(undefined); - const editor = useEditorState(); // TODO: use useEditorRef + const editor = useEditorState(); + + // useEventEditorSelectors.focus() hook is not working correctly at Safari in ShadowDOM, + // editor focus state changes on tripple click + // TODO: Consider upgrading Plate @udecode/* to check if this is fixed const inFocus = useEventEditorSelectors.focus() === editor.id; const getVirtualReferenceElement = () => { @@ -30,11 +32,11 @@ export function FloatingToolbar(props: ToolbarProps): any { }); const domNode = toDOMNode(editor, selectedNode); - + if (!domNode) { return null; } - + return { getBoundingClientRect(): DOMRect { return domNode.getBoundingClientRect(); @@ -44,18 +46,9 @@ export function FloatingToolbar(props: ToolbarProps): any { return { getBoundingClientRect(): DOMRect { - const shadowRoot = (() => { - if (ref.current) { - const rootNode = ref.current.getRootNode(); - const isShadow = rootNode instanceof ShadowRoot; + const range = toDOMRange(editor, editor.selection); - if (isShadow) { - return rootNode; - } - } - })(); - - return getSelectionBoundingClientRect({ shadowRoot }); + return range.getBoundingClientRect(); }, }; }; @@ -95,30 +88,3 @@ export function FloatingToolbar(props: ToolbarProps): any { /> ); } - -const getDefaultBoundingClientRect = () => (({ - width: 0, - height: 0, - x: 0, - y: 0, - top: -9999, - left: -9999, - right: 9999, - bottom: 9999, -}) as DOMRect); - -/** - * Get bounding client rect of the window selection - */ -const getSelectionBoundingClientRect = (params: { shadowRoot: ShadowRoot | undefined }): DOMRect => { - const { shadowRoot } = params; - const selection = SelectionUtils.getSelection({ shadowRoot }); - - if (!selection || selection.rangeCount < 1) { - return getDefaultBoundingClientRect(); - } - - const domRange = SelectionUtils.getSelectionRange0({ selection, shadowRoot }); - - return domRange.getBoundingClientRect(); -}; diff --git a/uui-editor/src/implementation/Toolbars.tsx b/uui-editor/src/implementation/Toolbars.tsx index a352b7cb2e..af24abbad6 100644 --- a/uui-editor/src/implementation/Toolbars.tsx +++ b/uui-editor/src/implementation/Toolbars.tsx @@ -51,7 +51,7 @@ export function Toolbars({ return ( { toolbarPosition === 'floating' && ( - + { floating } ) } diff --git a/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx b/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx index 38ee49d5b9..6b0fc939a5 100644 --- a/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx +++ b/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx @@ -75,7 +75,6 @@ function TableRenderer(props: any) { ) } - editor={ editor } isTable /> ) }