UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

124 lines (121 loc) 6.15 kB
/** * @jsxRuntime classic * @jsx jsx */ import React, { useEffect, 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 { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { akEditorFullPageNarrowBreakout } from '@atlaskit/editor-shared-styles'; // 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 { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; const RIGHT_CONTROL_HIDE_DELAY_MS = 150; const baseStyles = xcss({ transition: 'opacity 0.1s ease-in-out, visibility 0.1s ease-in-out' }); const visibleStyles = xcss({ opacity: 1, visibility: 'visible' }); const hiddenStyles = xcss({ opacity: 0, visibility: 'hidden' }); const baseStylesCSS = css({ transition: 'opacity 0.1s ease-in-out, visibility 0.1s ease-in-out', // 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 visibleStylesCSS = css({ opacity: 1, visibility: 'visible' }); const hiddenStylesCSS = css({ opacity: 0, visibility: 'hidden' }); export const VisibilityContainer = ({ api, children, controlSide, forceVisibleOnMouseOut }) => { const { isTypeAheadOpen, isEditing, isMouseOut, hoverSide, editorViewMode, userIntent, rightSideControlsEnabled } = useSharedPluginStateWithSelector(api, ['typeAhead', 'blockControls', 'editorViewMode', 'userIntent'], states => { var _states$typeAheadStat, _states$blockControls, _states$blockControls2, _states$blockControls3, _states$editorViewMod, _states$userIntentSta, _states$blockControls4; return { isTypeAheadOpen: (_states$typeAheadStat = states.typeAheadState) === null || _states$typeAheadStat === void 0 ? void 0 : _states$typeAheadStat.isOpen, isEditing: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.isEditing, isMouseOut: (_states$blockControls2 = states.blockControlsState) === null || _states$blockControls2 === void 0 ? void 0 : _states$blockControls2.isMouseOut, hoverSide: (_states$blockControls3 = states.blockControlsState) === null || _states$blockControls3 === void 0 ? void 0 : _states$blockControls3.hoverSide, editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode, userIntent: (_states$userIntentSta = states.userIntentState) === null || _states$userIntentSta === void 0 ? void 0 : _states$userIntentSta.currentUserIntent, rightSideControlsEnabled: (_states$blockControls4 = states.blockControlsState) === null || _states$blockControls4 === void 0 ? void 0 : _states$blockControls4.rightSideControlsEnabled }; }); const isViewMode = editorViewMode === 'view'; // rightSideControlsEnabled is the single source of truth (confluence_remix_button_right_side_block_fg from preset) const shouldRestrictBySide = rightSideControlsEnabled && controlSide !== undefined && !isViewMode; // Only restrict by side when hoverSide is known (after mousemove). When undefined, show both // controls so drag handle is visible on load and for keyboard-only users. const sideHidden = shouldRestrictBySide && hoverSide !== undefined ? hoverSide !== controlSide : false; // In view mode with right-side controls, we delay hiding on isMouseOut (see below) so the right-edge // button stays visible when the user moves from the block toward the button (e.g. in edit/live // pages), avoiding flicker as the mouse crosses boundaries. const hideOnMouseOut = isMouseOut; // When forceVisibleOnMouseOut is true (e.g. drag handle focused via keyboard Shift+Ctrl+H), // override the mouse-out condition so the control stays visible regardless of mouse position. const shouldHideWhenMouseOut = forceVisibleOnMouseOut ? false : hideOnMouseOut; const shouldHideImmediate = isTypeAheadOpen || isEditing || shouldHideWhenMouseOut || userIntent === 'aiStreaming' || sideHidden; // Delay hiding the right control in view mode to reduce flickering when moving from block // toward the right-edge button (avoids rapid show/hide as mouse crosses boundaries). const isRightControlViewMode = isViewMode && rightSideControlsEnabled && controlSide === 'right'; // When in right-control view mode, we delay hiding so start visible; useEffect will update after delay const [delayedShouldHide, setDelayedShouldHide] = useState(false); const hideTimeoutRef = useRef(null); useEffect(() => { if (!isRightControlViewMode) { return; } if (!shouldHideImmediate) { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); hideTimeoutRef.current = null; } setDelayedShouldHide(false); return; } hideTimeoutRef.current = setTimeout(() => { hideTimeoutRef.current = null; setDelayedShouldHide(true); }, RIGHT_CONTROL_HIDE_DELAY_MS); return () => { if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } }; }, [shouldHideImmediate, isRightControlViewMode]); const shouldHide = isRightControlViewMode ? delayedShouldHide : shouldHideImmediate; if (editorExperiment('platform_editor_preview_panel_responsiveness', true, { exposure: true })) { return jsx("div", { css: [baseStylesCSS, shouldHide ? hiddenStylesCSS : visibleStylesCSS] }, children); } // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) return jsx(Box, { xcss: [baseStyles, shouldHide ? hiddenStyles : visibleStyles] }, children); };