UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

916 lines (907 loc) 65.2 kB
/** * @jsxRuntime classic * @jsx jsx */ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports import { css, jsx } from '@emotion/react'; import { bind } from 'bind-event-listener'; import { getDocument } from '@atlaskit/browser-apis'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { getBrowserInfo } from '@atlaskit/editor-common/browser'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { dragToMoveDown, dragToMoveLeft, dragToMoveRight, dragToMoveUp, getAriaKeyshortcuts, TooltipContentWithMultipleShortcuts } from '@atlaskit/editor-common/keymaps'; import { blockControlsMessages } from '@atlaskit/editor-common/messages'; import { DRAG_HANDLE_WIDTH, tableControlsSpacing } from '@atlaskit/editor-common/styles'; import { TextSelection } from '@atlaskit/editor-prosemirror/state'; import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils'; import { akEditorFullPageNarrowBreakout, akEditorTableToolbarSize, relativeSizeToBaseFontSize } from '@atlaskit/editor-shared-styles/consts'; import DragHandleVerticalIcon from '@atlaskit/icon/core/drag-handle-vertical'; import { fg } from '@atlaskit/platform-feature-flags'; import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'; import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview'; // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss import { Box, xcss } from '@atlaskit/primitives'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import Tooltip from '@atlaskit/tooltip'; import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common'; import { key } from '../pm-plugins/main'; import { selectionPreservationPluginKey } from '../pm-plugins/selection-preservation/plugin-key'; import { getMultiSelectAnalyticsAttributes } from '../pm-plugins/utils/analytics'; import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, getNodeHeight, getTopPosition, shouldBeSticky, shouldMaskNodeControls } from '../pm-plugins/utils/drag-handle-positions'; import { expandAndUpdateSelection } from '../pm-plugins/utils/expand-and-update-selection'; import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection'; import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection'; import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts'; import { DragHandleNestedIcon } from './drag-handle-nested-icon'; import { dragPreview } from './drag-preview'; import { refreshAnchorName } from './utils/anchor-name'; import { getAnchorAttrName } from './utils/dom-attr-name'; import { VisibilityContainer } from './visibility-container'; const iconWrapperStyles = xcss({ display: 'flex', justifyContent: 'center', alignItems: 'center' }); const buttonWrapperStyles = css({ display: 'flex', justifyContent: 'center', alignItems: 'center', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { background: `linear-gradient(to bottom, ${"var(--ds-surface, #FFFFFF)"} 90%, transparent)`, marginBottom: "var(--ds-space-negative-200, -16px)", paddingBottom: "var(--ds-space-200, 16px)", marginTop: "var(--ds-space-negative-400, -32px)", paddingTop: `calc(${"var(--ds-space-400, 32px)"} - 1px)`, marginRight: "var(--ds-space-negative-150, -12px)", paddingRight: "var(--ds-space-150, 12px)", boxSizing: 'border-box' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { background: `linear-gradient(to bottom, ${"var(--ds-surface, #FFFFFF)"} 90%, transparent)`, marginBottom: "var(--ds-space-negative-200, -16px)", paddingBottom: "var(--ds-space-200, 16px)", marginTop: "var(--ds-space-negative-400, -32px)", paddingTop: `calc(${"var(--ds-space-400, 32px)"} - 1px)`, marginRight: "var(--ds-space-negative-150, -12px)", paddingRight: "var(--ds-space-150, 12px)", boxSizing: 'border-box' } }); const buttonWrapperStylesPatch = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls [data-number-column="true"] tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values marginRight: -akEditorTableToolbarSize, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values paddingRight: akEditorTableToolbarSize }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls [data-number-column="true"] tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values marginRight: -akEditorTableToolbarSize, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values paddingRight: akEditorTableToolbarSize } }); // update color to match quick insert button for new editor controls const dragHandleColor = css({ color: "var(--ds-icon-subtle, #505258)" }); const dragHandleButtonStyles = css({ display: 'flex', boxSizing: 'border-box', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 height: DRAG_HANDLE_HEIGHT, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 width: DRAG_HANDLE_WIDTH, border: 'none', background: 'transparent', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 borderRadius: DRAG_HANDLE_BORDER_RADIUS, // when platform_editor_controls is enabled, the drag handle color is overridden. Update color here when experiment is cleaned up. color: "var(--ds-icon, #292A2E)", cursor: 'grab', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 zIndex: DRAG_HANDLE_ZINDEX, outline: 'none', '&:hover': { backgroundColor: "var(--ds-background-neutral-subtle-hovered, #0515240F)" }, '&:active': { backgroundColor: "var(--ds-background-neutral-subtle-pressed, #0B120E24)" }, '&:disabled': { color: "var(--ds-icon-disabled, #080F214A)", backgroundColor: 'transparent' }, '&:hover:disabled': { backgroundColor: "var(--ds-background-disabled, #17171708)" } }); // Calculate scaled dimensions based on the base font size using CSS calc() // Default font size is 16px, scale proportionally // Standard: 16px -> 24h x 12w, Dense: 13px -> 18h x 9w, Jira: 14px -> 21h x 12w const dragHandleButtonScaledStyles = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values height: relativeSizeToBaseFontSize(DRAG_HANDLE_HEIGHT), // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values width: relativeSizeToBaseFontSize(DRAG_HANDLE_WIDTH) }); const dragHandleButtonSmallScreenStyles = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-container-queries, @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values [`@container editor-area (max-width: ${akEditorFullPageNarrowBreakout}px)`]: { opacity: 0, visibility: 'hidden' } }); const dragHandleButtonStylesOld = css({ position: 'absolute', paddingTop: `${"var(--ds-space-025, 2px)"}`, paddingBottom: `${"var(--ds-space-025, 2px)"}`, paddingLeft: '0', paddingRight: '0', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 height: DRAG_HANDLE_HEIGHT, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 width: DRAG_HANDLE_WIDTH, border: 'none', background: 'transparent', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 borderRadius: DRAG_HANDLE_BORDER_RADIUS, // when platform_editor_controls is enabled, the drag handle color is overridden. Update color here when experiment is cleaned up. color: "var(--ds-icon, #292A2E)", cursor: 'grab', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 zIndex: DRAG_HANDLE_ZINDEX, outline: 'none', '&:hover': { backgroundColor: "var(--ds-background-neutral-subtle-hovered, #0515240F)" }, '&:active': { backgroundColor: "var(--ds-background-neutral-subtle-pressed, #0B120E24)" }, '&:focus': { outline: `${"var(--ds-border-width-focused, 2px)"} solid ${"var(--ds-border-focused, #4688EC)"}` }, '&:disabled': { color: "var(--ds-icon-disabled, #080F214A)", backgroundColor: 'transparent' }, '&:hover:disabled': { backgroundColor: "var(--ds-background-disabled, #17171708)" } }); const focusedStylesOld = css({ '&:focus': { outline: `${"var(--ds-border-width-focused, 2px)"} solid ${"var(--ds-border-focused, #4688EC)"}` } }); const focusedStyles = css({ '&:focus-visible': { outline: `${"var(--ds-border-width-focused, 2px)"} solid ${"var(--ds-border-focused, #4688EC)"}` } }); const keyboardFocusedDragHandleStyles = css({ outline: `${"var(--ds-border-width-focused, 2px)"} solid ${"var(--ds-border-focused, #4688EC)"}` }); const dragHandleContainerStyles = xcss({ position: 'absolute', boxSizing: 'border-box' }); const tooltipContainerStyles = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values bottom: `-${STICKY_CONTROLS_TOP_MARGIN}px`, position: 'sticky', display: 'block', zIndex: 100 // card = 100 }); const tooltipContainerStylesStickyHeaderWithMask = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN}px`, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { top: '0' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { top: '0' } }); const tooltipContainerStylesImprovedStickyHeaderWithMask = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN}px`, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { top: '0' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { top: '0' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-mark-name="fragment"] >[data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: tableControlsSpacing }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-mark-name="fragment"] >[data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: tableControlsSpacing }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] tr.pm-table-row-native-sticky.pm-table-row-native-sticky-active) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER}px` }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] tr.pm-table-row-native-sticky.pm-table-row-native-sticky-active) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER}px` }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-mark-name="fragment"] >[data-prosemirror-node-name="table"] tr.pm-table-row-native-sticky.pm-table-row-native-sticky-active) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER}px` }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-mark-name="fragment"] > [data-prosemirror-node-name="table"] tr.pm-table-row-native-sticky.pm-table-row-native-sticky-active) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER}px` } }); const tooltipContainerStylesStickyHeaderWithoutMask = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: `${STICKY_CONTROLS_TOP_MARGIN}px`, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-blocks-drag-handle-container]:has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: tableControlsSpacing }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '[data-prosemirror-mark-name="breakout"]:has([data-blocks-drag-handle-container]):has(+ [data-prosemirror-node-name="table"] .pm-table-with-controls tr.sticky) &': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values top: tableControlsSpacing } }); const dragHandleMultiLineSelectionFixFirefox = css({ '&::selection': { backgroundColor: 'transparent' } }); const layoutColumnDragHandleStyles = css({ transform: 'rotate(90deg)' }); const selectedStyles = css({ backgroundColor: "var(--ds-background-selected, #E9F2FE)", color: "var(--ds-icon-selected, #1868DB)" }); // [Chrome only] When selection contains multiple nodes and then drag a drag handle that is within the selection range, // icon span receives dragStart event, instead of button, and since it is not registered as a draggable element // with pragmatic DnD and pragmatic DnD is not triggered const handleIconDragStart = e => { const browser = getBrowserInfo(); if (!browser.chrome) { return; } // prevent dragStart handler triggered by icon e.stopPropagation(); const dragEvent = new DragEvent('dragstart', { bubbles: true, cancelable: true, dataTransfer: e.dataTransfer }); if (e.target instanceof HTMLElement) { var _e$target$closest; // re-dispatch drag event on button so that pragmatic DnD can be triggered properly (_e$target$closest = e.target.closest('button')) === null || _e$target$closest === void 0 ? void 0 : _e$target$closest.dispatchEvent(dragEvent); } }; const getNodeSpacingForPreview = node => { if (!node) { return spacingBetweenNodesForPreview['default']; } const nodeTypeName = node.type.name; if (nodeTypeName === 'heading') { return spacingBetweenNodesForPreview[`heading${node.attrs.level}`] || spacingBetweenNodesForPreview['default']; } return spacingBetweenNodesForPreview[nodeTypeName] || spacingBetweenNodesForPreview['default']; }; const getNodeMargins = node => { if (!node) { return nodeMargins['default']; } const nodeTypeName = node.type.name; if (nodeTypeName === 'heading') { return nodeMargins[`heading${node.attrs.level}`] || nodeMargins['default']; } return nodeMargins[nodeTypeName] || nodeMargins['default']; }; export const DragHandle = ({ view, api, formatMessage, getPos, anchorName, nodeType, handleOptions, isTopLevelNode = true, anchorRectCache }) => { var _api$core4; const buttonRef = useRef(null); const mouseDownRef = useRef(false); const [dragHandleSelected, setDragHandleSelected] = useState(false); const [dragHandleDisabled, setDragHandleDisabled] = useState(false); const [blockCardWidth, setBlockCardWidth] = useState(768); const [recalculatePosition, setRecalculatePosition] = useState(false); const [positionStylesOld, setPositionStylesOld] = useState({ display: 'none' }); const [isFocused, setIsFocused] = useState(Boolean(handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused)); const { macroInteractionUpdates, selection, isShiftDown, interactionState } = useSharedPluginStateWithSelector(api, ['featureFlags', 'selection', 'blockControls', 'interaction'], states => { var _states$featureFlagsS, _states$selectionStat, _states$blockControls, _states$interactionSt; return { macroInteractionUpdates: (_states$featureFlagsS = states.featureFlagsState) === null || _states$featureFlagsS === void 0 ? void 0 : _states$featureFlagsS.macroInteractionUpdates, selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection, isShiftDown: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isShiftDown, interactionState: (_states$interactionSt = states.interactionState) === null || _states$interactionSt === void 0 ? void 0 : _states$interactionSt.interactionState }; }); const start = getPos(); const isLayoutColumn = nodeType === 'layoutColumn'; const isMultiSelect = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true); // Dynamically calculate if node is top-level based on current position (gated by experiment) const isTopLevelNodeDynamic = useMemo(() => { if (!expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true)) { return isTopLevelNode; } const pos = getPos(); if (typeof pos === 'number') { const $pos = view.state.doc.resolve(pos); return ($pos === null || $pos === void 0 ? void 0 : $pos.parent.type.name) === 'doc'; } return true; }, [getPos, view.state.doc, isTopLevelNode]); // Use the dynamic value when experiment is on, otherwise use the prop // When cleaning up the experiment, you can safely remove the isTopLevelNode as an prop and // just rely on the dynamic value (rename it to isTopLevelNode for simplicitiy) const isTopLevelNodeValue = expValEquals('platform_editor_nested_drag_handle_icon', 'isEnabled', true) ? isTopLevelNodeDynamic : isTopLevelNode; useEffect(() => { if (editorExperiment('platform_editor_block_control_optimise_render', true)) { return; } // blockCard/datasource width is rendered correctly after this decoraton does. We need to observe for changes. if (nodeType === 'blockCard') { const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`); const container = dom === null || dom === void 0 ? void 0 : dom.querySelector('.datasourceView-content-inner-wrap'); if (container) { const resizeObserver = new ResizeObserver(entries => { const width = entries[0].contentBoxSize[0].inlineSize; setBlockCardWidth(width); }); resizeObserver.observe(container); return () => resizeObserver.unobserve(container); } } }, [anchorName, nodeType, view.dom]); useEffect(() => { if (!expValEqualsNoExposure('platform_editor_selection_toolbar_block_handle', 'isEnabled', true)) { return; } const unbind = bind(window, { type: 'mouseUp', listener: () => mouseDownRef.current = false }); return () => unbind(); }, []); const handleMouseDown = useCallback(() => { mouseDownRef.current = true; }, []); const handleMouseUp = useCallback(e => { // Stop propagation so that for drag handles in nested scenarios the click is captured // and doesn't propagate to the edge of the element and trigger a node selection // on the parent element if (!expValEqualsNoExposure('platform_editor_selection_toolbar_block_handle', 'isEnabled', true)) { e.stopPropagation(); } // Fixes bug where selection toolbar is blocked when mouse is released on drag handle if (mouseDownRef.current) { e.stopPropagation(); } }, []); const handleOnClickNew = useCallback(e => { var _api$core; api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({ tr }) => { var _selectionPreservatio, _api$analytics, _resolvedStartPos$nod, _api$blockControls, _api$blockControls2; const startPos = getPos(); if (startPos === undefined) { return tr; } const resolvedStartPos = tr.doc.resolve(startPos); const selection = ((_selectionPreservatio = selectionPreservationPluginKey.getState(view.state)) === null || _selectionPreservatio === void 0 ? void 0 : _selectionPreservatio.preservedSelection) || tr.selection; api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.attachAnalyticsEvent({ eventType: EVENT_TYPE.UI, action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.BUTTON, actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE, attributes: { nodeDepth: resolvedStartPos.depth, nodeType: ((_resolvedStartPos$nod = resolvedStartPos.nodeAfter) === null || _resolvedStartPos$nod === void 0 ? void 0 : _resolvedStartPos$nod.type.name) || '' } })(tr); expandAndUpdateSelection({ tr, selection, startPos, isShiftPressed: e.shiftKey, nodeType, api }); api === null || api === void 0 ? void 0 : (_api$blockControls = api.blockControls) === null || _api$blockControls === void 0 ? void 0 : _api$blockControls.commands.startPreservingSelection()({ tr }); api === null || api === void 0 ? void 0 : (_api$blockControls2 = api.blockControls) === null || _api$blockControls2 === void 0 ? void 0 : _api$blockControls2.commands.toggleBlockMenu({ anchorName, openedViaKeyboard: false, triggerByNode: editorExperiment('platform_synced_block', true) ? { nodeType, pos: startPos, rootPos: tr.doc.resolve(startPos).before(1) } : undefined })({ tr }); tr.setMeta('scrollIntoView', false); return tr; }); view.focus(); }, [api, view, getPos, nodeType, anchorName]); const handleOnClick = useCallback(e => { var _api$core2; if (!isMultiSelect) { setDragHandleSelected(!dragHandleSelected); } api === null || api === void 0 ? void 0 : (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(({ tr }) => { var _api$blockControls$sh, _api$analytics2; const startPos = getPos(); if (startPos === undefined) { return tr; } const mSelect = api === null || api === void 0 ? void 0 : (_api$blockControls$sh = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh === void 0 ? void 0 : _api$blockControls$sh.multiSelectDnD; const $anchor = (mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) !== undefined ? tr.doc.resolve(mSelect === null || mSelect === void 0 ? void 0 : mSelect.anchor) : tr.selection.$anchor; if (!isMultiSelect || tr.selection.empty || !e.shiftKey) { tr = selectNode(tr, startPos, nodeType, api); } else if (isTopLevelNodeValue && $anchor.depth <= DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH && e.shiftKey && fg('platform_editor_elements_dnd_shift_click_select')) { var _api$blockControls3; const alignAnchorHeadToSel = alignAnchorHeadInDirectionOfPos(tr.selection, startPos); const selectionWithExpandedHead = expandSelectionHeadToNodeAtPos(alignAnchorHeadToSel, startPos); tr.setSelection(selectionWithExpandedHead); api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.commands.setMultiSelectPositions()({ tr }); } const resolvedMovingNode = tr.doc.resolve(startPos); const maybeNode = resolvedMovingNode.nodeAfter; tr.setMeta('scrollIntoView', false); api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.attachAnalyticsEvent({ eventType: EVENT_TYPE.UI, action: ACTION.CLICKED, actionSubject: ACTION_SUBJECT.BUTTON, actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE, attributes: { nodeDepth: resolvedMovingNode.depth, nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || '' } })(tr); return tr; }); view.focus(); }, [isMultiSelect, api, view, dragHandleSelected, getPos, isTopLevelNodeValue, nodeType]); const handleKeyDown = useCallback(e => { // allow user to use spacebar to select the node if (!e.repeat && e.key === ' ') { var _api$core3; const startPos = getPos(); api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({ tr }) => { if (startPos === undefined) { return tr; } const node = tr.doc.nodeAt(startPos); if (!node) { return tr; } const $startPos = tr.doc.resolve(startPos + node.nodeSize); const selection = new TextSelection($startPos); tr.setSelection(selection); !isMultiSelect && tr.setMeta(key, { pos: startPos }); return tr; }); } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(pressed => pressed)) { // If not trying to press shortcut keys, // return focus to editor to resume editing from caret position view.focus(); } }, [getPos, api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions, isMultiSelect, view]); const handleKeyDownNew = useCallback(e => { // allow user to use spacebar to select the node if (e.key === 'Enter' || !e.repeat && e.key === ' ') { var _getDocument, _api$core5; if (((_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.activeElement) !== buttonRef.current) { return; } e.preventDefault(); e.stopPropagation(); const startPos = getPos(); api === null || api === void 0 ? void 0 : (_api$core5 = api.core) === null || _api$core5 === void 0 ? void 0 : _api$core5.actions.execute(({ tr }) => { var _selectionPreservatio2, _api$blockControls4, _api$blockControls5, _api$userIntent; if (startPos === undefined) { return tr; } const selection = ((_selectionPreservatio2 = selectionPreservationPluginKey.getState(view.state)) === null || _selectionPreservatio2 === void 0 ? void 0 : _selectionPreservatio2.preservedSelection) || tr.selection; expandAndUpdateSelection({ tr, selection, startPos, isShiftPressed: e.shiftKey, nodeType, api }); api === null || api === void 0 ? void 0 : (_api$blockControls4 = api.blockControls) === null || _api$blockControls4 === void 0 ? void 0 : _api$blockControls4.commands.startPreservingSelection()({ tr }); const rootPos = editorExperiment('platform_synced_block', true) ? tr.doc.resolve(startPos).before(1) : undefined; const triggerByNode = editorExperiment('platform_synced_block', true) ? { nodeType, pos: startPos, rootPos } : undefined; api === null || api === void 0 ? void 0 : (_api$blockControls5 = api.blockControls) === null || _api$blockControls5 === void 0 ? void 0 : _api$blockControls5.commands.toggleBlockMenu({ anchorName, triggerByNode, openedViaKeyboard: true })({ tr }); api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('blockMenuOpen')({ tr }); return tr; }); view.focus(); } else if (![e.altKey, e.ctrlKey, e.shiftKey].some(pressed => pressed)) { // If not trying to press shortcut keys, // return focus to editor to resume editing from caret position view.focus(); } }, [getPos, api, nodeType, anchorName, view]); useEffect(() => { const element = buttonRef.current; if (!element) { return; } return draggable({ element, getInitialData: () => ({ type: 'element', start }), onGenerateDragPreview: ({ nativeSetDragImage }) => { var _api$blockControls$sh2; if (isMultiSelect) { var _api$core6; api === null || api === void 0 ? void 0 : (_api$core6 = api.core) === null || _api$core6 === void 0 ? void 0 : _api$core6.actions.execute(({ tr }) => { const handlePos = getPos(); if (typeof handlePos !== 'number') { return tr; } const newHandlePosCheck = isHandleCorrelatedToSelection(view.state, tr.selection, handlePos); if (!tr.selection.empty && newHandlePosCheck) { var _api$blockControls6; api === null || api === void 0 ? void 0 : (_api$blockControls6 = api.blockControls) === null || _api$blockControls6 === void 0 ? void 0 : _api$blockControls6.commands.setMultiSelectPositions()({ tr }); } else { tr = selectNode(tr, handlePos, nodeType, api); } return tr; }); } const startPos = getPos(); const state = view.state; const { doc, selection } = state; let sliceFrom = selection.from; let sliceTo = selection.to; const mSelect = api === null || api === void 0 ? void 0 : (_api$blockControls$sh2 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh2 === void 0 ? void 0 : _api$blockControls$sh2.multiSelectDnD; if (mSelect) { const { anchor, head } = mSelect; sliceFrom = Math.min(anchor, head); sliceTo = Math.max(anchor, head); } const expandedSlice = doc.slice(sliceFrom, sliceTo); const isDraggingMultiLine = isMultiSelect && startPos !== undefined && startPos >= sliceFrom && startPos < sliceTo && expandedSlice.content.childCount > 1; setCustomNativeDragPreview({ getOffset: () => { if (!isDraggingMultiLine) { return { x: 0, y: 0 }; } else { // Calculate the offset of the preview container, // So when drag multiple nodes, the preview align with the position of the selected nodes const domAtPos = view.domAtPos.bind(view); let domElementsHeightBeforeHandle = 0; const nodesStartPos = []; const nodesEndPos = []; let activeNodeMarginTop = 0; for (let i = 0; i < expandedSlice.content.childCount; i++) { if (i === 0) { var _expandedSlice$conten; nodesStartPos[i] = sliceFrom; nodesEndPos[i] = sliceFrom + (((_expandedSlice$conten = expandedSlice.content.maybeChild(i)) === null || _expandedSlice$conten === void 0 ? void 0 : _expandedSlice$conten.nodeSize) || 0); } else { var _expandedSlice$conten2; nodesStartPos[i] = nodesEndPos[i - 1]; nodesEndPos[i] = nodesStartPos[i] + (((_expandedSlice$conten2 = expandedSlice.content.maybeChild(i)) === null || _expandedSlice$conten2 === void 0 ? void 0 : _expandedSlice$conten2.nodeSize) || 0); } // when the node is before the handle, calculate the height of the node if (nodesEndPos[i] <= startPos) { // eslint-disable-next-line @atlaskit/editor/no-as-casting const currentNodeElement = findDomRefAtPos(nodesStartPos[i], domAtPos); const maybeCurrentNode = expandedSlice.content.maybeChild(i); const currentNodeSpacing = maybeCurrentNode ? getNodeMargins(maybeCurrentNode).top + getNodeMargins(maybeCurrentNode).bottom : 0; domElementsHeightBeforeHandle = domElementsHeightBeforeHandle + currentNodeElement.offsetHeight + currentNodeSpacing; } else { // when the node is after the handle, calculate the top margin of the active node const maybeNextNode = expandedSlice.content.maybeChild(i); activeNodeMarginTop = maybeNextNode ? getNodeMargins(maybeNextNode).top : 0; break; } } return { x: 0, y: domElementsHeightBeforeHandle + activeNodeMarginTop }; } }, render: ({ container }) => { const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`); if (!dom) { return; } if (!isDraggingMultiLine) { return dragPreview(container, { dom, nodeType }); } else { const domAtPos = view.domAtPos.bind(view); const previewContent = []; expandedSlice.content.descendants((node, pos) => { // Get the dom element of the node //eslint-disable-next-line @atlaskit/editor/no-as-casting const nodeDomElement = findDomRefAtPos(sliceFrom + pos, domAtPos); const currentNodeSpacing = getNodeSpacingForPreview(node); previewContent.push({ dom: nodeDomElement, nodeType: node.type.name, nodeSpacing: currentNodeSpacing }); return false; // Only iterate through the first level of nodes }); return dragPreview(container, previewContent); } }, nativeSetDragImage }); }, onDragStart() { var _api$core7; if (start === undefined) { return; } api === null || api === void 0 ? void 0 : (_api$core7 = api.core) === null || _api$core7 === void 0 ? void 0 : _api$core7.actions.execute(({ tr }) => { var _api$blockControls$sh3, _api$blockControls7, _api$analytics3; let nodeTypes, hasSelectedMultipleNodes; const resolvedMovingNode = tr.doc.resolve(start); const maybeNode = resolvedMovingNode.nodeAfter; const mSelect = api === null || api === void 0 ? void 0 : (_api$blockControls$sh3 = api.blockControls.sharedState.currentState()) === null || _api$blockControls$sh3 === void 0 ? void 0 : _api$blockControls$sh3.multiSelectDnD; if (mSelect) { const attributes = getMultiSelectAnalyticsAttributes(tr, mSelect.anchor, mSelect.head); nodeTypes = attributes.nodeTypes; hasSelectedMultipleNodes = attributes.hasSelectedMultipleNodes; } else { nodeTypes = maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name; hasSelectedMultipleNodes = false; } api === null || api === void 0 ? void 0 : (_api$blockControls7 = api.blockControls) === null || _api$blockControls7 === void 0 ? void 0 : _api$blockControls7.commands.setNodeDragged(getPos, anchorName, nodeType)({ tr }); tr.setMeta('scrollIntoView', false); api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.attachAnalyticsEvent({ eventType: EVENT_TYPE.UI, action: ACTION.DRAGGED, actionSubject: ACTION_SUBJECT.ELEMENT, actionSubjectId: ACTION_SUBJECT_ID.ELEMENT_DRAG_HANDLE, attributes: { nodeDepth: resolvedMovingNode.depth, nodeType: (maybeNode === null || maybeNode === void 0 ? void 0 : maybeNode.type.name) || '', ...(isMultiSelect && { nodeTypes, hasSelectedMultipleNodes }) } })(tr); return tr; }); view.focus(); } }); }, [anchorName, api, getPos, isMultiSelect, nodeType, start, view]); const positionStyles = useMemo(() => { if (!editorExperiment('platform_editor_block_control_optimise_render', true)) { return {}; } // This is a no-op to allow recalculatePosition to be used as a dependency if (recalculatePosition) { setRecalculatePosition(recalculatePosition); } const pos = getPos(); const $pos = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? typeof pos === 'number' && view.state.doc.resolve(pos) : pos && view.state.doc.resolve(pos); const parentPos = $pos && $pos.depth ? $pos.before() : undefined; const node = parentPos !== undefined ? view.state.doc.nodeAt(parentPos) : undefined; const parentNodeType = node === null || node === void 0 ? void 0 : node.type.name; const supportsAnchor = CSS.supports('top', `anchor(${anchorName} start)`) && CSS.supports('left', `anchor(${anchorName} start)`); const safeAnchorName = editorExperiment('platform_editor_controls', 'variant1') ? refreshAnchorName({ getPos, view, anchorName }) : anchorName; const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${safeAnchorName}"]`); const hasResizer = nodeType === 'table' || nodeType === 'mediaSingle'; const isExtension = nodeType === 'extension' || nodeType === 'bodiedExtension' || nodeType === 'multiBodiedExtension'; const isBlockCard = nodeType === 'blockCard'; const isEmbedCard = nodeType === 'embedCard'; const isMacroInteractionUpdates = macroInteractionUpdates && isExtension; let innerContainer = null; if (dom) { if (isEmbedCard) { innerContainer = dom.querySelector('.rich-media-item'); } else if (hasResizer) { innerContainer = dom.querySelector('.resizer-item'); } else if (isExtension) { innerContainer = dom.querySelector('.extension-container[data-layout]'); } else if (isBlockCard) { //specific to datasource blockCard innerContainer = dom.querySelector('.datasourceView-content-inner-wrap'); } } const isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer; const isSticky = shouldBeSticky(nodeType); if (supportsAnchor) { const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {}; return { left: isEdgeCase ? `calc(anchor(${safeAnchorName} start) + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc((anchor(${safeAnchorName} right) + anchor(${safeAnchorName} left))/2 - ${DRAG_HANDLE_HEIGHT / 2}px)` : `calc(anchor(${safeAnchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType, parentNodeType)}px)`, top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc(anchor(${safeAnchorName} top) - ${DRAG_HANDLE_WIDTH}px)` : `calc(anchor(${safeAnchorName} start) + ${topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType, (dom === null || dom === void 0 ? void 0 : dom.getAttribute('layout')) || '')}px)`, ...bottom }; } const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {}; return { left: isEdgeCase ? `calc(${(dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0}px + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), top: getTopPosition(dom, nodeType), ...height }; }, [anchorName, getPos, view, nodeType, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn, recalculatePosition]); const calculatePositionOld = useCallback(() => { const pos = getPos(); const $pos = expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? typeof pos === 'number' && view.state.doc.resolve(pos) : pos && view.state.doc.resolve(pos); const parentPos = $pos && $pos.depth ? $pos.before() : undefined; const node = parentPos !== undefined ? view.state.doc.nodeAt(parentPos) : undefined; const parentNodeType = node === null || node === void 0 ? void 0 : node.type.name; const supportsAnchor = CSS.supports('top', `anchor(${anchorName} start)`) && CSS.supports('left', `anchor(${anchorName} start)`); const safeAnchorName = editorExperiment('platform_editor_controls', 'variant1') ? refreshAnchorName({ getPos, view, anchorName }) : anchorName; const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${safeAnchorName}"]`); const hasResizer = nodeType === 'table' || nodeType === 'mediaSingle'; const isExtension = nodeType === 'extension' || nodeType === 'bodiedExtension' || nodeType === 'multiBodiedExtension'; const isBlockCard = nodeType === 'blockCard' && !!blockCardWidth; const isEmbedCard = nodeType === 'embedCard'; const isMacroInteractionUpdates = macroInteractionUpdates && isExtension; let innerContainer = null; if (dom) { if (isEmbedCard) { innerContainer = dom.querySelector('.rich-media-item'); } else if (hasResizer) { innerContainer = dom.querySelector('.resizer-item'); } else if (isExtension) { innerContainer = dom.querySelector('.extension-container[data-layout]'); } else if (isBlockCard) { //specific to datasource blockCard innerContainer = dom.querySelector('.datasourceView-content-inner-wrap'); } } const isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer; const isSticky = shouldBeSticky(nodeType); if (supportsAnchor) { const bottom = editorExperiment('platform_editor_controls', 'variant1') ? getControlBottomCSSValue(safeAnchorName, isSticky, isTopLevelNodeValue, isLayoutColumn) : {}; return { left: isEdgeCase ? `calc(anchor(${safeAnchorName} start) + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc((anchor(${safeAnchorName} right) + anchor(${safeAnchorName} left))/2 - ${DRAG_HANDLE_HEIGHT / 2}px)` : `calc(anchor(${safeAnchorName} start) - ${DRAG_HANDLE_WIDTH}px - ${dragHandleGap(nodeType, parentNodeType)}px)`, top: editorExperiment('advanced_layouts', true) && isLayoutColumn ? `calc(anchor(${safeAnchorName} top) - ${DRAG_HANDLE_WIDTH}px)` : `calc(anchor(${safeAnchorName} start) + ${topPositionAdjustment(expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? $pos && $pos.nodeAfter && getNodeTypeWithLevel($pos.nodeAfter) || nodeType : nodeType, (dom === null || dom === void 0 ? void 0 : dom.getAttribute('layout')) || '')}px)`, ...bottom }; } const height = editorExperiment('platform_editor_controls', 'variant1') ? getControlHeightCSSValue(getNodeHeight(dom, safeAnchorName, anchorRectCache) || 0, isSticky, isTopLevelNodeValue, `${DRAG_HANDLE_HEIGHT}`, isLayoutColumn) : {}; return { left: isEdgeCase ? `calc(${(dom === null || dom === void 0 ? void 0 : dom.offsetLeft) || 0}px + ${getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType)})` : getLeftPosition(dom, nodeType, innerContainer, isMacroInteractionUpdates, parentNodeType), top: getTopPosition(dom, nodeType), ...height }; }, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]); useEffect(() => { if (editorExperiment('platform_editor_block_control_optimise_render', true)) { return; } let cleanUpTransitionListener; if (nodeType === 'extension' || nodeType === 'embedCard') { const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`); if (!dom) { return; } cleanUpTransitionListener = bind(dom, { type: 'transitionend', listener: () => { setPositionStylesOld(calculatePositionOld()); } }); } const calcPos = requestAnimationFrame(() => { setPositionStylesOld(calculatePositionOld()); }); return () => { var _cleanUpTransitionLis