UNPKG

@atlaskit/editor-common

Version:

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

216 lines (212 loc) • 7.95 kB
import _extends from "@babel/runtime/helpers/extends"; /* eslint-disable @atlaskit/design-system/prefer-primitives */ /** @jsx jsx */ import React, { Fragment, useState } from 'react'; import { css, jsx } from '@emotion/react'; import classnames from 'classnames'; import EditorFileIcon from '@atlaskit/icon/glyph/editor/file'; import { useSharedPluginState } from '../../hooks'; import { removeMarginsAndBorder, sharedMultiBodiedExtensionStyles } from '../../ui/MultiBodiedExtension'; import { calculateBreakoutStyles, getExtensionLozengeData } from '../../utils'; import { WithPluginState } from '../../with-plugin-state'; import ExtensionLozenge from '../Extension/Lozenge'; import { useMultiBodiedExtensionActions } from './action-api'; import { mbeExtensionWrapperCSSStyles, overlayStyles } from './styles'; const getContainerCssExtendedStyles = (activeChildIndex, showMacroInteractionDesignUpdates) => css(sharedMultiBodiedExtensionStyles.mbeExtensionContainer, { [`.multiBodiedExtension-content-dom-wrapper > [data-extension-frame='true']:nth-of-type(${activeChildIndex + 1})`]: css(sharedMultiBodiedExtensionStyles.extensionFrameContent, showMacroInteractionDesignUpdates && removeMarginsAndBorder) }); const imageStyles = css({ maxHeight: '24px', maxWidth: '24px' }); // 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 jsx("div", { className: "extension-title" }, jsx("img", _extends({ css: imageStyles, src: url }, rest, { alt: title })), title); } return jsx("div", { className: "extension-title", "data-testid": 'multiBodiedExtension-default-lozenge' }, jsx(EditorFileIcon, { label: title }), title); }; const MultiBodiedExtensionWithWidth = ({ node, handleContentDOMRef, getPos, tryExtensionHandler, editorView, eventDispatcher, widthState, editorAppearance, showMacroInteractionDesignUpdates, isNodeSelected, isNodeHovered, setIsNodeHovered }) => { 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 actions = useMultiBodiedExtensionActions({ updateActiveChild, editorView, getPos, eventDispatcher, node }); const extensionHandlerResult = React.useMemo(() => { return tryExtensionHandler(actions); }, [tryExtensionHandler, actions]); const articleRef = React.useCallback(node => { return handleContentDOMRef(node); }, [handleContentDOMRef]); const shouldBreakout = // Extension should breakout when the layout is set to 'full-width' or 'wide'. ['full-width', 'wide'].includes(node.attrs.layout) && // Extension breakout state should not be respected when the editor appearance is full-width mode editorAppearance !== 'full-width'; 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-margin-styles': showMacroInteractionDesignUpdates, 'with-border': showMacroInteractionDesignUpdates, 'with-hover-border': showMacroInteractionDesignUpdates && isNodeHovered, 'with-danger-overlay': showMacroInteractionDesignUpdates }); const containerClassNames = classnames('multiBodiedExtension--container', { 'remove-padding': showMacroInteractionDesignUpdates }); const navigationClassNames = classnames('multiBodiedExtension--navigation', { 'remove-margins': showMacroInteractionDesignUpdates, 'remove-border': showMacroInteractionDesignUpdates }); const handleMouseEvent = didHover => { if (setIsNodeHovered) { setIsNodeHovered(didHover); } }; return jsx(Fragment, null, showMacroInteractionDesignUpdates && jsx(ExtensionLozenge, { isNodeSelected: isNodeSelected, node: node, showMacroInteractionDesignUpdates: true, customContainerStyles: mbeWrapperStyles, isNodeHovered: isNodeHovered }), jsx("div", { className: wrapperClassNames, css: mbeExtensionWrapperCSSStyles, "data-testid": "multiBodiedExtension--wrapper", style: mbeWrapperStyles, onMouseEnter: () => handleMouseEvent(true), onMouseLeave: () => handleMouseEvent(false) }, jsx("div", { css: overlayStyles, className: "multiBodiedExtension--overlay" }), getWrapperTitleContent(imageData, title, showMacroInteractionDesignUpdates), jsx("div", { className: containerClassNames, css: getContainerCssExtendedStyles(activeChildIndex, showMacroInteractionDesignUpdates), "data-testid": "multiBodiedExtension--container", "data-active-child-index": activeChildIndex }, jsx("nav", { className: navigationClassNames, css: sharedMultiBodiedExtensionStyles.mbeNavigation, "data-testid": "multiBodiedExtension-navigation" }, extensionHandlerResult), jsx("article", { className: "multiBodiedExtension--frames", "data-testid": "multiBodiedExtension--frames", ref: articleRef })))); }; const MultiBodiedExtensionWithSharedState = props => { const { pluginInjectionApi } = props; const { widthState } = useSharedPluginState(pluginInjectionApi, ['width']); return jsx(MultiBodiedExtensionWithWidth, _extends({ widthState: widthState }, props)); }; // Workaround taken from platform/packages/editor/editor-core/src/plugins/extension/ui/Extension/Extension/index.tsx const MultiBodiedExtension = props => { // TODO: ED-17836 This code is here because confluence injects // the `editor-referentiality` plugin via `dangerouslyAppendPlugins` // which cannot access the `pluginInjectionApi`. When we move // Confluence to using presets we can remove this workaround. const { pluginInjectionApi } = props; return pluginInjectionApi === undefined ? jsx(MultiBodiedExtensionDeprecated, props) : jsx(MultiBodiedExtensionWithSharedState, props); }; // TODO: ED-17836 This code is here because Confluence injects // the `editor-referentiality` plugin via `dangerouslyAppendPlugins` // which cannot access the `pluginInjectionApi`. When we move // Confluence to using presets we can remove this workaround. // @ts-ignore const widthPluginKey = { key: 'widthPlugin$', getState: state => { return state['widthPlugin$']; } }; const MultiBodiedExtensionDeprecated = props => { return ( // @ts-ignore - 'WithPluginState' cannot be used as a JSX component. // This error was introduced after upgrading to TypeScript 5 jsx(WithPluginState, { editorView: props.editorView, plugins: { widthState: widthPluginKey }, render: ({ widthState }) => jsx(MultiBodiedExtensionWithWidth, _extends({ widthState: widthState }, props)) }) ); }; /** * End workaround */ export default MultiBodiedExtension;