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