@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
216 lines (212 loc) • 7.95 kB
JavaScript
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;