UNPKG

@atlaskit/editor-core

Version:

A package contains Atlassian editor core functionality

349 lines (341 loc) 19.9 kB
/** * @jsxRuntime classic * @jsx jsx */ import React, { useImperativeHandle, useRef } from 'react'; /* eslint-disable @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, useTheme } from '@emotion/react'; import classnames from 'classnames'; import { injectIntl } from 'react-intl'; import { decisionListSelector, taskListSelector } from '@atlaskit/adf-schema'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks'; import { fullPageMessages as messages } from '@atlaskit/editor-common/messages'; import { akEditorFullPageNarrowBreakout, akEditorGutterPaddingDynamic, akEditorGutterPaddingReduced } from '@atlaskit/editor-shared-styles'; import FeatureGates from '@atlaskit/feature-gate-js-client'; import { fg } from '@atlaskit/platform-feature-flags'; import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure'; 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 { ContextPanel } from '../../ContextPanel'; import EditorContentContainer from '../../EditorContentContainer/EditorContentContainer'; import PluginSlot from '../../PluginSlot'; import { contentAreaWrapper, sidebarArea } from './StyledComponents'; const akEditorFullWidthLayoutWidth = 1800; const akEditorUltraWideLayoutWidth = 4000; const akEditorSwoopCubicBezier = `cubic-bezier(0.15, 1, 0.3, 1)`; const tableMarginFullWidthMode = 2; const akLayoutGutterOffset = 12; const SWOOP_ANIMATION = `0.5s ${akEditorSwoopCubicBezier}`; const AK_NESTED_DND_GUTTER_OFFSET = 8; const getTotalPadding = () => akEditorGutterPaddingDynamic() * 2; const editorContentAreaProsemirrorStyle = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '& .ProseMirror': { flexGrow: 1, boxSizing: 'border-box', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '& > *': { clear: 'both' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 [`> p, > ul, > ol:not(${taskListSelector}):not(${decisionListSelector}), > h1, > h2, > h3, > h4, > h5, > h6`]: { clear: 'none' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766 '> p:last-child': { marginBottom: "var(--ds-space-300, 24px)" } } }); const hideEditorContentAreaProsemirrorStyle = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '& .ProseMirror': { display: 'none' } }); const hideEditorContentAreaProsemirrorWithAttributeStyle = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '&[data-markdown-mode-hide-prosemirror="true"] .ProseMirror': { display: 'none' } }); const fullWidthNonChromelessBreakoutBlockTableStyle = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-1 '.fabric-editor--full-width-mode:not(:has(#chromeless-editor))': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.fabric-editor-breakout-mark, .extension-container.block, .pm-table-container': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-important-styles -- Ignored via go/DSP-18766 width: '100% !important' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.fabric-editor-breakout-mark': { // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage/preview, @atlaskit/ui-styling-standard/no-important-styles -- Ignored via go/DSP-18766 marginLeft: 'unset !important', // eslint-disable-next-line @atlaskit/ui-styling-standard/no-important-styles -- Ignored via go/DSP-18766 transform: 'none !important' } } }); // An additional spacing applied at the top of the content area reserving space when the primary toolbar // is hidden – this avoids layout shift when the toolbar is toggled under the editor controls feature const contentAreaReservedPrimaryToolbarSpace = css({ // extra 1px to account for the bottom border on the toolbar marginTop: `calc(${"var(--ds-space-500, 40px)"} + 1px)` }); // A reduced top spacing applied to the content area to compensate for the reserved space at the top // of the page when the primary toolbar is hidden under the editor controls feature const contentAreaReducedHeaderSpace = css({ paddingTop: "var(--ds-space-400, 32px)" }); // new styles const editorContentAreaNew = css({ // eslint-disable-next-line @atlaskit/design-system/use-tokens-typography lineHeight: '24px', paddingTop: "var(--ds-space-600, 48px)", // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors '.ak-editor-content-area-no-toolbar &': { // When the toolbar is hidden, we don't want content to jump up // the extra 1px is to account for the border on the toolbar paddingTop: `calc(${"var(--ds-space-600, 48px)"} + var(--ak-editor-fullpage-toolbar-height) + 1px)` }, paddingBottom: "var(--ds-space-600, 48px)", height: 'calc( 100% - 105px )', width: '100%', margin: 'auto', flexDirection: 'column', flexGrow: 1, maxWidth: 'var(--ak-editor-content-area-max-width)', transition: `max-width ${SWOOP_ANIMATION}` }); const tableFullPageEditorStylesNew = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors '.ProseMirror .pm-table-wrapper > table': { marginLeft: 0, /* 1px border width offset added here to prevent unwanted overflow and scolling - ED-16212 */ // eslint-disable-next-line @atlaskit/design-system/use-tokens-space marginRight: '-1px', width: '100%' } }); const editorContentAreaContainerNestedDndStyle = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors '.fabric-editor--full-width-mode': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors '[data-layout-section]': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 maxWidth: `calc(100% + ${(akLayoutGutterOffset + AK_NESTED_DND_GUTTER_OFFSET) * 2}px)` } } }); /* Prevent horizontal scroll on page in full width mode */ const editorContentAreaContainerStyleExcludeCodeBlockNew = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.fabric-editor--full-width-mode': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.extension-container, .multiBodiedExtension--container': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 maxWidth: `calc(100% - ${tableMarginFullWidthMode * 2}px)` }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.extension-container.inline': { maxWidth: '100%' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 'td .extension-container.inline': { maxWidth: 'inherit' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '[data-layout-section]': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 maxWidth: `calc(100% + ${akLayoutGutterOffset * 2}px)` } } }); /* Prevent horizontal scroll on page in full width mode */ const editorContentAreaContainerStyleNew = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.fabric-editor--full-width-mode': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.code-block, .extension-container, .multiBodiedExtension--container': { maxWidth: `calc(100% - ${tableMarginFullWidthMode * 2}px)` }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '.extension-container.inline': { maxWidth: '100%' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 'td .extension-container.inline': { maxWidth: 'inherit' }, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766 '[data-layout-section]': { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 maxWidth: `calc(100% + ${akLayoutGutterOffset * 2}px)` } } }); const editorContentGutterStyleFG = css({ padding: '0 72px' }); const editorContentGutterStyles = css({ boxSizing: 'border-box', padding: '0 52px' }); const editorContentReducedGutterStyles = css({ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-container-queries, @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values [`@container editor-area (max-width: ${akEditorFullPageNarrowBreakout}px)`]: { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766 padding: `0 ${akEditorGutterPaddingReduced}px` } }); const contentAreaNew = css({ display: 'flex', flexDirection: 'row', height: `calc(100% - var(--ak-editor-fullpage-toolbar-height))`, // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-values '&.ak-editor-content-area-no-toolbar': { // The editor toolbar height is 1px off (from the border) -- so we need to add 1px to the height // to match the toolbar height height: `calc(100% + 1px)` }, boxSizing: 'border-box', margin: 0, padding: 0, transition: `padding 0ms ${akEditorSwoopCubicBezier}` }); const contentAreaHeightNoToolbar = css({ height: '100%' }); export const CONTENT_AREA_TEST_ID = 'ak-editor-fp-content-area'; export const EDITOR_CONTAINER = 'ak-editor-container'; const Content = /*#__PURE__*/React.forwardRef((props, ref) => { var _props$editorAPI, _props$editorAPI$base, _props$editorAPI$base2, _props$editorAPI$bloc, _props$editorAPI2, _props$editorAPI2$blo, _props$editorAPI2$blo2, _props$editorAPI2$blo3, _contentAreaRef$curre, _allowScrollGutter$gu, _allowScrollGutter$gu2; const theme = useTheme(); const fullWidthMode = props.appearance === 'full-width'; const maxWidthMode = props.appearance === 'max'; const scrollContainerRef = useRef(null); const contentAreaRef = useRef(null); const containerRef = useRef(null); const allowScrollGutter = (_props$editorAPI = props.editorAPI) === null || _props$editorAPI === void 0 ? void 0 : (_props$editorAPI$base = _props$editorAPI.base) === null || _props$editorAPI$base === void 0 ? void 0 : (_props$editorAPI$base2 = _props$editorAPI$base.sharedState.currentState()) === null || _props$editorAPI$base2 === void 0 ? void 0 : _props$editorAPI$base2.allowScrollGutter; const contentAreaMaxWidth = getTotalPadding() + (!fullWidthMode ? maxWidthMode ? akEditorUltraWideLayoutWidth : theme.layoutMaxWidth : akEditorFullWidthLayoutWidth); // Get useStandardNodeWidth from block menu plugin shared state // Only access editorAPI when the experiment is enabled to avoid performance impact const useStandardNodeWidth = editorExperiment('platform_editor_controls', 'variant1') && ((_props$editorAPI$bloc = (_props$editorAPI2 = props.editorAPI) === null || _props$editorAPI2 === void 0 ? void 0 : (_props$editorAPI2$blo = _props$editorAPI2.blockMenu) === null || _props$editorAPI2$blo === void 0 ? void 0 : (_props$editorAPI2$blo2 = _props$editorAPI2$blo.sharedState) === null || _props$editorAPI2$blo2 === void 0 ? void 0 : (_props$editorAPI2$blo3 = _props$editorAPI2$blo2.currentState()) === null || _props$editorAPI2$blo3 === void 0 ? void 0 : _props$editorAPI2$blo3.useStandardNodeWidth) !== null && _props$editorAPI$bloc !== void 0 ? _props$editorAPI$bloc : false); useImperativeHandle(ref, () => ({ get scrollContainer() { return scrollContainerRef.current; }, get contentArea() { return contentAreaRef.current; }, get containerArea() { return containerRef.current; } }), []); const markdownPluginCurrentView = useSharedPluginStateWithSelector(props.editorAPI, ['markdownMode'], states => { var _states$markdownModeS; return (_states$markdownModeS = states.markdownModeState) === null || _states$markdownModeS === void 0 ? void 0 : _states$markdownModeS.view; }); const markdownPluginCurrentIsMarkdownMode = useSharedPluginStateWithSelector(props.editorAPI, ['markdownMode'], states => { var _states$markdownModeS2; return (_states$markdownModeS2 = states.markdownModeState) === null || _states$markdownModeS2 === void 0 ? void 0 : _states$markdownModeS2.isMarkdownMode; }); const shouldHideProseMirrorForMarkdownMode = expValEqualsNoExposure('cc-markdown-mode', 'isEnabled', true) && markdownPluginCurrentView !== 'wysiwyg' && markdownPluginCurrentIsMarkdownMode; return jsx("div", { css: [contentAreaNew, props.isEditorToolbarHidden && contentAreaHeightNoToolbar], "data-testid": CONTENT_AREA_TEST_ID, ref: containerRef }, jsx("div", { css: // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values contentAreaWrapper, "data-testid": EDITOR_CONTAINER, "data-editor-container": 'true' }, jsx(EditorContentContainer // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , { className: "fabric-editor-popup-scroll-parent", featureFlags: props.featureFlags, ref: scrollContainerRef, viewMode: props === null || props === void 0 ? void 0 : props.viewMode, isScrollable: true, appearance: props.appearance, contentMode: props.contentMode, useStandardNodeWidth: useStandardNodeWidth }, jsx(ClickAreaBlock, { editorView: props.editorView, editorDisabled: props.disabled }, jsx("div", { "data-markdown-mode-hide-prosemirror": shouldHideProseMirrorForMarkdownMode && fg('platform_editor_delay_markdown_view_mode_eval') ? 'true' : undefined, css: [editorContentAreaNew, editorContentAreaProsemirrorStyle, // EDITOR-6558: hide ProseMirror when the markdown-mode plugin // reports a non-WYSIWYG view. shouldHideProseMirrorForMarkdownMode && (fg('platform_editor_delay_markdown_view_mode_eval') ? hideEditorContentAreaProsemirrorWithAttributeStyle : hideEditorContentAreaProsemirrorStyle), tableFullPageEditorStylesNew, fullWidthNonChromelessBreakoutBlockTableStyle, // for breakout resizing, there's no need to restrict the width of codeblocks as they're always wrapped in a breakout mark expValEqualsNoExposure('platform_editor_breakout_resizing', 'isEnabled', true) ? editorContentAreaContainerStyleExcludeCodeBlockNew : editorContentAreaContainerStyleNew, fg('platform_editor_nested_dnd_styles_changes') && editorContentAreaContainerNestedDndStyle, !fg('platform_editor_controls_no_toolbar_space') && editorExperiment('platform_editor_controls', 'variant1') && contentAreaReducedHeaderSpace, !fg('platform_editor_controls_no_toolbar_space') && props.isEditorToolbarHidden && editorExperiment('platform_editor_controls', 'variant1') && contentAreaReservedPrimaryToolbarSpace], style: { '--ak-editor-content-area-max-width': `${contentAreaMaxWidth}px` } // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop , className: "ak-editor-content-area-region", "data-editor-editable-content": true, role: "region", "aria-label": props.intl.formatMessage(messages.editableContentLabel), ref: contentAreaRef, "data-vc": "editor-content-area-region" }, jsx("div", { css: [editorContentGutterStyles, // eslint-disable-next-line @atlaskit/platform/no-preconditioning fg('platform_editor_controls_increase_full_page_gutter') && editorExperiment('platform_editor_controls', 'variant1') && editorContentGutterStyleFG, editorExperiment('platform_editor_preview_panel_responsiveness', true, { exposure: true }) && editorContentReducedGutterStyles] // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: classnames('ak-editor-content-area', 'appearance-full-page', { 'fabric-editor--full-width-mode': fullWidthMode, 'fabric-editor--max-width-mode': Boolean(maxWidthMode) }), ref: contentAreaRef }, !!props.customContentComponents && 'before' in props.customContentComponents ? contentComponentClickWrapper(props.customContentComponents.before) : contentComponentClickWrapper(props.customContentComponents), jsx(PluginSlot, { editorView: props.editorView, editorActions: props.editorActions, eventDispatcher: props.eventDispatcher, providerFactory: props.providerFactory, appearance: props.appearance, items: props.contentComponents, pluginHooks: props.pluginHooks, contentArea: (_contentAreaRef$curre = contentAreaRef.current) !== null && _contentAreaRef$curre !== void 0 ? _contentAreaRef$curre : undefined, popupsMountPoint: props.popupsMountPoint, popupsBoundariesElement: props.popupsBoundariesElement, popupsScrollableElement: props.popupsScrollableElement, disabled: !!props.disabled, containerElement: scrollContainerRef.current, dispatchAnalyticsEvent: props.dispatchAnalyticsEvent, wrapperElement: props.wrapperElement }), props.editorDOMElement, !!props.customContentComponents && 'after' in props.customContentComponents ? contentComponentClickWrapper(props.customContentComponents.after) : null, allowScrollGutter && (FeatureGates.getExperimentValue('cc_snippets', 'isEnabled', false) ? jsx("div", { id: "editor-scroll-gutter", style: { paddingBottom: `${(_allowScrollGutter$gu = allowScrollGutter.gutterSize) !== null && _allowScrollGutter$gu !== void 0 ? _allowScrollGutter$gu : '120'}px` }, "data-vc": "scroll-gutter", "data-editor-scroll-gutter": "true" }) : jsx("div", { id: "editor-scroll-gutter", style: { paddingBottom: `${(_allowScrollGutter$gu2 = allowScrollGutter.gutterSize) !== null && _allowScrollGutter$gu2 !== void 0 ? _allowScrollGutter$gu2 : '120'}px` }, "data-vc": "scroll-gutter" }))))))), jsx("div", { css: sidebarArea }, props.contextPanel || jsx(ContextPanel, { editorAPI: props.editorAPI, visible: false }))); }); export const FullPageContentArea = injectIntl(Content, { forwardRef: true });