UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

269 lines (266 loc) • 12.1 kB
import _extends from "@babel/runtime/helpers/extends"; /* eslint-disable @atlaskit/design-system/prefer-primitives */ /** * @jsxRuntime classic * @jsx jsx */ import React, { Fragment, useState } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports -- Ignored via go/DSP-18766; jsx required at runtime for @jsxRuntime classic import { css, jsx } from '@emotion/react'; import classnames from 'classnames'; import EditorFileIcon from '@atlaskit/icon/core/file'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { useSharedPluginStateWithSelector } from '../../hooks'; import { removeMarginsAndBorder, sharedMultiBodiedExtensionStyles } from '../../ui/MultiBodiedExtension'; import { calculateBreakoutStyles, getExtensionLozengeData } from '../../utils'; import ExtensionLozenge from '../Extension/Lozenge'; import { shouldExtensionBreakout } from '../utils/should-extension-breakout'; import { useMultiBodiedExtensionActions } from './action-api'; import { mbeExtensionWrapperCSSStyles, overlayStyles } from './styles'; const getContainerCssExtendedStyles = (activeChildIndex, showMacroInteractionDesignUpdates) => // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 css(sharedMultiBodiedExtensionStyles.mbeExtensionContainer, { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 [`.multiBodiedExtension-content-dom-wrapper > [data-extension-frame='true']:nth-of-type(${activeChildIndex + 1 // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 })`]: css( // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 sharedMultiBodiedExtensionStyles.extensionFrameContent, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 showMacroInteractionDesignUpdates && removeMarginsAndBorder) }); const imageStyles = css({ maxHeight: '24px', maxWidth: '24px' }); const hoverStyles = css({ '&:hover': { boxShadow: `0 0 0 1px ${"var(--ds-border-input, #8C8F97)"}` } }); const MultiBodiedExtensionFrames = ({ articleRef }) => { return jsx("article", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: "multiBodiedExtension--frames", "data-testid": "multiBodiedExtension--frames", ref: articleRef }); }; // Similar to the one in platform/packages/editor/editor-common/src/extensibility/Extension/Lozenge.tsx const getWrapperTitleContent = (imageData, title, showMacroInteractionDesignUpdates) => { if (showMacroInteractionDesignUpdates) { return null; } if (imageData) { const { url, ...rest } = imageData; return ( // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 jsx("div", { className: "extension-title" }, jsx("img", _extends({ css: imageStyles, src: url // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading }, rest, { alt: title })), title) ); } return jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: "extension-title", "data-testid": 'multiBodiedExtension-default-lozenge' }, jsx(EditorFileIcon, { label: title }), title); }; const MultiBodiedExtensionWithWidth = ({ node, handleContentDOMRef, getPos, tryExtensionHandler, editorView, eventDispatcher, widthState, editorAppearance, macroInteractionDesignFeatureFlags, isNodeSelected, isNodeHovered, isNodeNested, setIsNodeHovered, pluginInjectionApi, isLivePageViewMode, allowBodiedOverride = false }) => { const { showMacroInteractionDesignUpdates } = macroInteractionDesignFeatureFlags || {}; const { parameters, extensionKey } = node.attrs; const title = parameters && parameters.extensionTitle || parameters && parameters.macroMetadata && parameters.macroMetadata.title || extensionKey || node.type.name; const imageData = getExtensionLozengeData({ node, type: 'image' }); const [activeChildIndex, setActiveChildIndex] = useState(0); // Adding to avoid aliasing `this` for the callbacks const updateActiveChild = React.useCallback(index => { if (typeof index !== 'number') { setActiveChildIndex(0); throw new Error('Index is not valid'); } setActiveChildIndex(index); return true; }, [setActiveChildIndex]); const articleRef = React.useCallback(node => { return handleContentDOMRef(node); }, [handleContentDOMRef]); const childrenContainer = React.useMemo(() => { return jsx(MultiBodiedExtensionFrames, { articleRef: articleRef }); }, [articleRef]); const actions = useMultiBodiedExtensionActions({ updateActiveChild, editorView, getPos, node, eventDispatcher, allowBodiedOverride, childrenContainer }); const extensionHandlerResult = React.useMemo(() => { return tryExtensionHandler(actions); }, [tryExtensionHandler, actions]); const layout = node.attrs.layout; const legacyShouldBreakout = ['full-width', 'wide'].includes(layout) && editorAppearance !== 'full-width'; const tinymceFullWidthModeEnabled = expValEquals('confluence_max_width_content_appearance', 'isEnabled', true); const breakoutExtensionFixEnabled = expValEquals('confluence_max_width_breakout_extension_fix', 'isEnabled', true); const shouldUseBreakoutFix = tinymceFullWidthModeEnabled && breakoutExtensionFixEnabled; const shouldBreakout = shouldUseBreakoutFix ? shouldExtensionBreakout({ layout, editorAppearance, isTopLevelNode: true }) : legacyShouldBreakout; let mbeWrapperStyles = {}; if (shouldBreakout) { const { ...breakoutStyles } = calculateBreakoutStyles({ mode: node.attrs.layout, widthStateLineLength: widthState === null || widthState === void 0 ? void 0 : widthState.lineLength, widthStateWidth: widthState === null || widthState === void 0 ? void 0 : widthState.width }); mbeWrapperStyles = breakoutStyles; } const wrapperClassNames = classnames('multiBodiedExtension--wrapper', 'extension-container', 'block', { 'with-border': showMacroInteractionDesignUpdates, 'with-danger-overlay': showMacroInteractionDesignUpdates, 'with-padding-background-styles': showMacroInteractionDesignUpdates, 'with-margin-styles': showMacroInteractionDesignUpdates && !isNodeNested, 'with-hover-border': expValEquals('cc_editor_ttvc_release_bundle_one', 'extensionHoverRefactor', true) ? false : showMacroInteractionDesignUpdates && isNodeHovered }); const containerClassNames = classnames('multiBodiedExtension--container', { 'remove-padding': showMacroInteractionDesignUpdates }); const bodyContainerClassNames = classnames('multiBodiedExtension--body-container'); const navigationClassNames = classnames('multiBodiedExtension--navigation', { 'remove-margins': showMacroInteractionDesignUpdates, 'remove-border': showMacroInteractionDesignUpdates }); const overlayClassNames = classnames('multiBodiedExtension--overlay', { 'with-margin': showMacroInteractionDesignUpdates }); const handleMouseEvent = didHover => { if (setIsNodeHovered) { setIsNodeHovered(didHover); } }; return jsx(Fragment, null, showMacroInteractionDesignUpdates && !isLivePageViewMode && jsx(ExtensionLozenge, { isNodeSelected: isNodeSelected, node: node, showMacroInteractionDesignUpdates: true, customContainerStyles: mbeWrapperStyles, isNodeHovered: isNodeHovered, isNodeNested: isNodeNested, setIsNodeHovered: setIsNodeHovered, isBodiedMacro: true, pluginInjectionApi: pluginInjectionApi }), jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: wrapperClassNames, css: [ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 mbeExtensionWrapperCSSStyles, showMacroInteractionDesignUpdates && !isLivePageViewMode && expValEquals('cc_editor_ttvc_release_bundle_one', 'extensionHoverRefactor', true) && hoverStyles], "data-testid": "multiBodiedExtension--wrapper-editor", "data-layout": node.attrs.layout // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 , style: mbeWrapperStyles, onMouseEnter: () => handleMouseEvent(true) // @atlassian/a11y/mouse-events-have-key-events: hover border is also applied via .ak-editor-selected-node // CSS on keyboard selection. No-ops here satisfy the rule without duplicating state updates. , onFocus: expValEquals('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? () => {} : undefined, onMouseLeave: () => handleMouseEvent(false), onBlur: expValEquals('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? () => {} : undefined }, jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 css: overlayStyles // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: overlayClassNames, "data-testid": "multiBodiedExtension--overlay" }), getWrapperTitleContent(imageData, title, showMacroInteractionDesignUpdates), jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: containerClassNames // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 , css: getContainerCssExtendedStyles(activeChildIndex, showMacroInteractionDesignUpdates), "data-testid": "multiBodiedExtension--container", "data-active-child-index": activeChildIndex }, allowBodiedOverride ? jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: bodyContainerClassNames, "data-testid": "multiBodiedExtension--body-container" }, extensionHandlerResult) : jsx(Fragment, null, jsx("nav", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 className: navigationClassNames // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 , css: sharedMultiBodiedExtensionStyles.mbeNavigation, "data-testid": "multiBodiedExtension-navigation" }, extensionHandlerResult), childrenContainer)))); }; const MultiBodiedExtension = props => { const { pluginInjectionApi } = props; const { width, lineLength } = useSharedPluginStateWithSelector(pluginInjectionApi, ['width'], states => { var _states$widthState, _states$widthState2; return { width: (_states$widthState = states.widthState) === null || _states$widthState === void 0 ? void 0 : _states$widthState.width, lineLength: (_states$widthState2 = states.widthState) === null || _states$widthState2 === void 0 ? void 0 : _states$widthState2.lineLength }; }); // Ignored via go/ees005 return jsx(MultiBodiedExtensionWithWidth, _extends({ widthState: width === undefined ? undefined : { width, lineLength } // eslint-disable-next-line react/jsx-props-no-spreading }, props)); }; export default MultiBodiedExtension;