UNPKG

@atlaskit/editor-core

Version:

A package contains Atlassian editor core functionality

335 lines (330 loc) • 17.1 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; /** * @jsxRuntime classic * @jsx jsx */ import React, { useCallback, useEffect, useMemo, 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 { useIntl } from 'react-intl'; import ButtonGroup from '@atlaskit/button/button-group'; import Button from '@atlaskit/button/new'; import { useSharedPluginState, useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import messages from '@atlaskit/editor-common/messages'; import { WidthConsumer, WidthProvider } from '@atlaskit/editor-common/ui'; import { ToolbarArrowKeyNavigationProvider } from '@atlaskit/editor-common/ui-menu'; import { akEditorMobileBreakoutPoint } from '@atlaskit/editor-shared-styles'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; // Ignored via go/ees005 // eslint-disable-next-line import/no-named-as-default import ClickAreaBlock from '../../Addon/ClickAreaBlock'; import { contentComponentClickWrapper } from '../../Addon/ClickAreaBlock/contentComponentWrapper'; import EditorContentContainer from '../../EditorContentContainer/EditorContentContainer'; import PluginSlot from '../../PluginSlot'; import { getPrimaryToolbarComponents } from '../../Toolbar/getPrimaryToolbarComponents'; import { ToolbarWithSizeDetector as Toolbar } from '../../Toolbar/ToolbarWithSizeDetector'; import WithFlash from '../../WithFlash'; import { CommentToolbar } from './CommentToolbar'; import { MainToolbar } from './Toolbar'; var MAXIMUM_TWO_LINE_TOOLBAR_BREAKPOINT = 490; // Remove when platform_editor_comment_editor_border_radius is cleaned up var commentEditorStylesOld = css({ display: 'flex', flexDirection: 'column', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.less-margin > .ProseMirror': { margin: "var(--ds-space-150, 12px)".concat(" ", "var(--ds-space-100, 8px)", " ", "var(--ds-space-100, 8px)") }, minWidth: '272px', height: 'auto', backgroundColor: "var(--ds-background-input, #FFFFFF)", border: "var(--ds-border-width, 1px)".concat(" solid ", "var(--ds-border-input, #8C8F97)"), boxSizing: 'border-box', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 borderRadius: "var(--ds-radius-small, 3px)", maxWidth: 'inherit', wordWrap: 'break-word' }); var commentEditorStyles = css({ display: 'flex', flexDirection: 'column', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.less-margin > .ProseMirror': { margin: "var(--ds-space-150, 12px)".concat(" ", "var(--ds-space-100, 8px)", " ", "var(--ds-space-100, 8px)") }, minWidth: '272px', height: 'auto', backgroundColor: "var(--ds-background-input, #FFFFFF)", border: "var(--ds-border-width, 1px)".concat(" solid ", "var(--ds-border-input, #8C8F97)"), boxSizing: 'border-box', borderRadius: "var(--ds-radius-medium, 6px)", maxWidth: 'inherit', wordWrap: 'break-word' }); var secondaryToolbarStyles = css({ boxSizing: 'border-box', justifyContent: 'flex-end', alignItems: 'center', display: 'flex', padding: "var(--ds-space-150, 12px)".concat(" ", "var(--ds-space-025, 2px)") }); var mainToolbarCustomComponentsSlotStyleNew = css({ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', flexGrow: 1, paddingRight: "var(--ds-space-250, 20px)", // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors '> div': { display: 'flex', flexShrink: 0 } }); var mainToolbarCustomComponentsSlotStylePaddingOverride = css({ paddingRight: 0 }); var mainToolbarCustomComponentsSlotStyleTwoLineToolbarNew = css(_defineProperty({}, "@media (max-width: ".concat(MAXIMUM_TWO_LINE_TOOLBAR_BREAKPOINT, "px)"), { paddingRight: 0 })); var appearance = 'comment'; export var CommentEditorWithIntl = function CommentEditorWithIntl(props) { var _editorAPI$blockMenu$, _editorAPI$blockMenu; var editorAPI = props.editorAPI; // Get useStandardNodeWidth from block menu plugin shared state // Only access editorAPI when the experiment is enabled to avoid performance impact var useStandardNodeWidth = editorExperiment('platform_editor_controls', 'variant1') && ((_editorAPI$blockMenu$ = editorAPI === null || editorAPI === void 0 || (_editorAPI$blockMenu = editorAPI.blockMenu) === null || _editorAPI$blockMenu === void 0 || (_editorAPI$blockMenu = _editorAPI$blockMenu.sharedState) === null || _editorAPI$blockMenu === void 0 || (_editorAPI$blockMenu = _editorAPI$blockMenu.currentState()) === null || _editorAPI$blockMenu === void 0 ? void 0 : _editorAPI$blockMenu.useStandardNodeWidth) !== null && _editorAPI$blockMenu$ !== void 0 ? _editorAPI$blockMenu$ : false); var _useSharedPluginState = useSharedPluginStateWithSelector(editorAPI, ['maxContentSize', 'primaryToolbar', 'editorViewMode'], function (states) { var _states$maxContentSiz, _states$primaryToolba, _states$editorViewMod; return { maxContentSizeReached: !!((_states$maxContentSiz = states.maxContentSizeState) !== null && _states$maxContentSiz !== void 0 && _states$maxContentSiz.maxContentSizeReached), primaryToolbarComponentsState: (_states$primaryToolba = states.primaryToolbarState) === null || _states$primaryToolba === void 0 ? void 0 : _states$primaryToolba.components, editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode }; }), editorViewMode = _useSharedPluginState.editorViewMode, primaryToolbarComponentsState = _useSharedPluginState.primaryToolbarComponentsState, maxContentSizeReached = _useSharedPluginState.maxContentSizeReached; var primaryToolbarState = getPrimaryToolbarComponents(editorAPI, primaryToolbarComponentsState); var _useSharedPluginState2 = useSharedPluginState(editorAPI, ['media']), mediaState = _useSharedPluginState2.mediaState; var intl = useIntl(); var editorDOMElement = props.editorDOMElement, editorView = props.editorView, editorActions = props.editorActions, eventDispatcher = props.eventDispatcher, providerFactory = props.providerFactory, contentComponents = props.contentComponents, customContentComponents = props.customContentComponents, customPrimaryToolbarComponents = props.customPrimaryToolbarComponents, primaryToolbarComponentsProp = props.primaryToolbarComponents, customSecondaryToolbarComponents = props.customSecondaryToolbarComponents, popupsMountPoint = props.popupsMountPoint, popupsBoundariesElement = props.popupsBoundariesElement, popupsScrollableElement = props.popupsScrollableElement, maxHeight = props.maxHeight, _props$minHeight = props.minHeight, minHeight = _props$minHeight === void 0 ? 150 : _props$minHeight, onSave = props.onSave, onCancel = props.onCancel, disabled = props.disabled, dispatchAnalyticsEvent = props.dispatchAnalyticsEvent, useStickyToolbar = props.useStickyToolbar, pluginHooks = props.pluginHooks, featureFlags = props.featureFlags, innerRef = props.innerRef; var showSecondaryToolbar = !!onSave || !!onCancel || !!customSecondaryToolbarComponents; var containerElement = React.useRef(null); // Wrapper container for toolbar and content area var wrapperElementRef = useMemo(function () { return innerRef || /*#__PURE__*/React.createRef(); }, [innerRef]); var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), saveButtonDisabled = _useState2[0], setSaveButtonDisabled = _useState2[1]; useEffect(function () { if (mediaState) { mediaState.subscribeToUploadInProgressState(setSaveButtonDisabled); } return function () { return mediaState === null || mediaState === void 0 ? void 0 : mediaState.unsubscribeFromUploadInProgressState(setSaveButtonDisabled); }; }, [mediaState]); var handleSave = useCallback(function () { if (editorView && onSave) { onSave(editorView); } }, [editorView, onSave]); var handleCancel = useCallback(function () { if (editorView && onCancel) { onCancel(editorView); } }, [editorView, onCancel]); var isShortcutToFocusToolbar = useCallback(function (event) { //Alt + F9 to reach first element in this main toolbar return event.altKey && (event.key === 'F9' || event.keyCode === 120); }, []); // When primary toolbar components is undefined, do not show two line editor toolbar var isTwoLineToolbarEnabled = !!customPrimaryToolbarComponents; var handleEscape = useCallback(function (event) { if (!(editorView !== null && editorView !== void 0 && editorView.hasFocus())) { editorView === null || editorView === void 0 || editorView.focus(); } event.preventDefault(); event.stopPropagation(); }, [editorView]); var primaryToolbarComponents = primaryToolbarComponentsProp; if (Array.isArray(primaryToolbarState === null || primaryToolbarState === void 0 ? void 0 : primaryToolbarState.components) && Array.isArray(primaryToolbarComponents)) { primaryToolbarComponents = primaryToolbarState.components.concat(primaryToolbarComponents); } var isToolbarAIFCEnabled = Boolean(editorAPI === null || editorAPI === void 0 ? void 0 : editorAPI.toolbar); var memoizedContentAreaStyles = useMemo(function () { return [maxHeight ? // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 maxHeight: "".concat(maxHeight, "px"), // When maxHeight is set, content area should have overflow-y explicitly set as auto // As we have overflow-x: clip for the content area, and when maxHeight prop is set, overflow-y will be computed as visible by default. // This will cause the content area to have content overflowing the container // so need to set overflow-y as auto to make sure the content area is scrollable overflowY: 'auto' }) : null]; }, [maxHeight]); var contentAreaStyles = expValEquals('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedContentAreaStyles : [maxHeight ? // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 maxHeight: "".concat(maxHeight, "px"), // When maxHeight is set, content area should have overflow-y explicitly set as auto // As we have overflow-x: clip for the content area, and when maxHeight prop is set, overflow-y will be computed as visible by default. // This will cause the content area to have content overflowing the container // so need to set overflow-y as auto to make sure the content area is scrollable overflowY: 'auto' }) : null]; var customToolbarSlot = jsx("div", { css: [mainToolbarCustomComponentsSlotStyleNew, isTwoLineToolbarEnabled && mainToolbarCustomComponentsSlotStyleTwoLineToolbarNew, isToolbarAIFCEnabled && mainToolbarCustomComponentsSlotStylePaddingOverride] }, customPrimaryToolbarComponents); return jsx(WithFlash, { animate: maxContentSizeReached }, jsx(WidthProvider, null, jsx("div", { css: [expValEquals('platform_editor_comment_editor_border_radius', 'isEnabled', true) ? commentEditorStyles : commentEditorStylesOld, // 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({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 minHeight: "".concat(minHeight, "px") })] // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: "akEditor", ref: wrapperElementRef }, jsx(MainToolbar, { useStickyToolbar: useStickyToolbar, twoLineEditorToolbar: isTwoLineToolbarEnabled, isNewToolbarEnabled: isToolbarAIFCEnabled }, isToolbarAIFCEnabled ? jsx(ToolbarArrowKeyNavigationProvider, { editorView: editorView, childComponentSelector: "[data-testid='ak-editor-main-toolbar']", isShortcutToFocusToolbar: isShortcutToFocusToolbar, handleEscape: handleEscape, editorAppearance: appearance, useStickyToolbar: useStickyToolbar, intl: intl }, jsx(CommentToolbar, { editorAPI: editorAPI, editorView: editorView, editorAppearance: appearance, disabled: !!disabled, popupsBoundariesElement: popupsBoundariesElement, popupsScrollableElement: popupsScrollableElement, popupsMountPoint: popupsMountPoint }), customPrimaryToolbarComponents ? customToolbarSlot : null) : jsx(ToolbarArrowKeyNavigationProvider, { editorView: editorView, childComponentSelector: "[data-testid='ak-editor-main-toolbar']", isShortcutToFocusToolbar: isShortcutToFocusToolbar, handleEscape: handleEscape, editorAppearance: appearance, useStickyToolbar: useStickyToolbar, intl: intl }, jsx(Toolbar // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , { editorView: editorView, editorActions: editorActions // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , eventDispatcher: eventDispatcher // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , providerFactory: providerFactory, appearance: appearance, items: primaryToolbarComponents, popupsMountPoint: popupsMountPoint, popupsBoundariesElement: popupsBoundariesElement, popupsScrollableElement: popupsScrollableElement, disabled: !!disabled, dispatchAnalyticsEvent: dispatchAnalyticsEvent, containerElement: containerElement.current, twoLineEditorToolbar: isTwoLineToolbarEnabled }), customToolbarSlot)), jsx(ClickAreaBlock, { editorView: editorView, editorDisabled: disabled }, jsx(WidthConsumer, null, function (_ref) { var width = _ref.width; return jsx(EditorContentContainer, { ref: containerElement, css: contentAreaStyles, isScrollable: maxHeight ? true : undefined // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: classnames('ak-editor-content-area', { 'less-margin': width < akEditorMobileBreakoutPoint }), featureFlags: featureFlags, viewMode: editorViewMode, appearance: appearance, useStandardNodeWidth: useStandardNodeWidth }, customContentComponents && 'before' in customContentComponents ? contentComponentClickWrapper(customContentComponents.before) : contentComponentClickWrapper(customContentComponents), jsx(PluginSlot, { editorView: editorView, editorActions: editorActions, eventDispatcher: eventDispatcher, dispatchAnalyticsEvent: dispatchAnalyticsEvent, providerFactory: providerFactory, appearance: appearance, items: contentComponents, popupsMountPoint: popupsMountPoint, popupsBoundariesElement: popupsBoundariesElement, popupsScrollableElement: popupsScrollableElement, containerElement: containerElement.current, disabled: !!disabled, wrapperElement: wrapperElementRef.current, pluginHooks: pluginHooks }), editorDOMElement, customContentComponents && 'after' in customContentComponents ? contentComponentClickWrapper(customContentComponents.after) : null); }))), showSecondaryToolbar && jsx("div", { css: secondaryToolbarStyles, "data-testid": "ak-editor-secondary-toolbar" }, jsx(ButtonGroup, null, !!onSave && jsx(Button, { appearance: "primary", onClick: handleSave, testId: "comment-save-button", isDisabled: disabled || saveButtonDisabled, interactionName: "editor-comment-save-button" }, intl.formatMessage(messages.saveButton)), !!onCancel && jsx(Button, { appearance: "subtle", onClick: handleCancel, testId: "comment-cancel-button", isDisabled: disabled, interactionName: "editor-comment-cancel-button" }, intl.formatMessage(messages.cancelButton))), jsx("span", { style: { flexGrow: 1 } }), customSecondaryToolbarComponents))); }; CommentEditorWithIntl.displayName = 'CommentEditorAppearance';