@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
229 lines (228 loc) • 13.4 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
/**
* @jsxRuntime classic
* @jsx jsx
*/
import React, { useEffect, useMemo, useRef, 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 { jsx } from '@emotion/react';
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
import { SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
import { ContextPanelWidthProvider } from '@atlaskit/editor-common/ui';
import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
import { FULL_PAGE_EDITOR_TOOLBAR_HEIGHT } from '@atlaskit/editor-shared-styles';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { getPrimaryToolbarComponents } from '../../Toolbar/getPrimaryToolbarComponents';
import { FullPageContentArea } from './FullPageContentArea';
import { FullPageToolbar } from './FullPageToolbar';
import { FullPageToolbarNext } from './FullPageToolbarNext';
import { fullPageEditorWrapper } from './StyledComponents';
var SSR_TRACE_SEGMENT_NAME = 'reactEditorView/fullPageAppearance';
var useShowKeyline = function useShowKeyline(contentAreaRef) {
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
showKeyline = _useState2[0],
setShowKeyline = _useState2[1];
useEffect(function () {
var _contentAreaRef$curre;
if (!((_contentAreaRef$curre = contentAreaRef.current) !== null && _contentAreaRef$curre !== void 0 && _contentAreaRef$curre.contentArea)) {
return;
}
var browser = getBrowserInfo();
var intersection = new IntersectionObserver(function (_ref) {
var _ref2 = _slicedToArray(_ref, 1),
entry = _ref2[0];
setShowKeyline(!entry.isIntersecting && entry.boundingClientRect.top < entry.intersectionRect.top);
}, {
root: undefined,
// Safari seems to miss events (on fast scroll) sometimes due
// to differences in IntersectionObserver behaviour between browsers.
// By lowering the threshold a little it gives Safari more
// time to catch these events.
threshold: browser.safari ? 0.98 : 1
});
intersection.observe(contentAreaRef.current.contentArea);
return function () {
intersection.disconnect();
};
}, [contentAreaRef]);
return showKeyline;
};
var hasCustomComponents = function hasCustomComponents(components) {
if (!components) {
return false;
}
if ('before' in components) {
return Array.isArray(components.before) && components.before.length > 0 || !!components.before || Array.isArray(components.after) && components.after.length > 0 || !!components.after;
}
return true;
};
export var FullPageEditor = function FullPageEditor(props) {
var _scrollContentContain, _scrollContentContain2, _scrollContentContain3, _wrapperElementRef$cu;
// Should be always the first statement in the component
var firstRenderStartTimestampRef = useRef(performance.now());
var wrapperElementRef = useMemo(function () {
return props.innerRef;
}, [props.innerRef]);
var scrollContentContainerRef = useRef(null);
var showKeyline = useShowKeyline(scrollContentContainerRef);
var editorAPI = props.editorAPI;
var state = useSharedPluginStateWithSelector(editorAPI, ['primaryToolbar', 'interaction', 'editorViewMode', 'toolbar'], function (states) {
var _states$primaryToolba, _states$interactionSt, _states$editorViewMod, _states$toolbarState;
return {
primaryToolbarComponents: (_states$primaryToolba = states.primaryToolbarState) === null || _states$primaryToolba === void 0 ? void 0 : _states$primaryToolba.components,
interactionState: (_states$interactionSt = states.interactionState) === null || _states$interactionSt === void 0 ? void 0 : _states$interactionSt.interactionState,
editorViewMode: (_states$editorViewMod = states.editorViewModeState) === null || _states$editorViewMod === void 0 ? void 0 : _states$editorViewMod.mode,
contextualFormattingModeOverride: (_states$toolbarState = states.toolbarState) === null || _states$toolbarState === void 0 ? void 0 : _states$toolbarState.contextualFormattingModeOverride
};
});
// Markdown Mode forces `'always-pinned'` while in source / preview view
// (no PM selection to anchor a floating toolbar to). The user's docking
// pref alone is not enough to decide whether to mount the primary toolbar
// in that case — the override has to short-circuit the hide gate below.
var forcePrimaryToolbarPinned = state.contextualFormattingModeOverride === 'always-pinned' && fg('platform_editor_toolbar_mode_override');
var interactionState = state.interactionState;
var primaryToolbarState = getPrimaryToolbarComponents(editorAPI, state.primaryToolbarComponents);
var hasHadInteraction = interactionState !== 'hasNotHadInteraction';
var toolbarDocking = useSharedPluginStateSelector(editorAPI, 'selectionToolbar.toolbarDocking', {
disabled: fg('platform_editor_use_preferences_plugin')
});
if (!fg('platform_editor_use_preferences_plugin')) {
if (!toolbarDocking) {
var _editorAPI$selectionT, _editorAPI$selectionT2;
// This is a workaround for the rendering issue with the selection toolbar
// where using useSharedPluginStateSelector or useSharedPluginState the state are not
// available when the editor is first loaded. and cause the toolbar to blink.
var defaultDocking = props.__livePage ? 'none' : 'top';
toolbarDocking = (_editorAPI$selectionT = editorAPI === null || editorAPI === void 0 || (_editorAPI$selectionT2 = editorAPI.selectionToolbar) === null || _editorAPI$selectionT2 === void 0 || (_editorAPI$selectionT2 = _editorAPI$selectionT2.sharedState.currentState()) === null || _editorAPI$selectionT2 === void 0 ? void 0 : _editorAPI$selectionT2.toolbarDocking) !== null && _editorAPI$selectionT !== void 0 ? _editorAPI$selectionT : defaultDocking;
}
}
var _ref3 = useSharedPluginStateSelector(editorAPI, 'userPreferences.preferences', {
disabled: !fg('platform_editor_use_preferences_plugin')
}) || {},
toolbarDockingPosition = _ref3.toolbarDockingPosition;
if (fg('platform_editor_use_preferences_plugin')) {
if (!toolbarDockingPosition) {
var _editorAPI$userPrefer, _editorAPI$userPrefer2;
// This is a workaround for the rendering issue with the selection toolbar
// when using useSharedPluginStateWithSelector the state is not yet
// available when the editor is first loaded.
// This causes the toolbar to blink creating a layout shift.
var defaultDockingPosition = props.__livePage ? 'none' : 'top';
toolbarDockingPosition = (_editorAPI$userPrefer = editorAPI === null || editorAPI === void 0 || (_editorAPI$userPrefer2 = editorAPI.userPreferences) === null || _editorAPI$userPrefer2 === void 0 || (_editorAPI$userPrefer2 = _editorAPI$userPrefer2.actions.getUserPreferences()) === null || _editorAPI$userPrefer2 === void 0 ? void 0 : _editorAPI$userPrefer2.toolbarDockingPosition) !== null && _editorAPI$userPrefer !== void 0 ? _editorAPI$userPrefer : defaultDockingPosition;
}
}
var primaryToolbarComponents = props.primaryToolbarComponents;
if (Array.isArray(primaryToolbarState === null || primaryToolbarState === void 0 ? void 0 : primaryToolbarState.components) && Array.isArray(primaryToolbarComponents)) {
primaryToolbarComponents = primaryToolbarState.components.concat(primaryToolbarComponents);
}
var isEditorToolbarHidden = state.editorViewMode === 'view';
var customPrimaryToolbarComponents = props.customPrimaryToolbarComponents;
if (editorExperiment('platform_editor_controls', 'variant1', {
exposure: true
})) {
if (fg('platform_editor_use_preferences_plugin')) {
// need to check if the toolbarDockingPosition is set to 'none' or 'top'
if (toolbarDockingPosition === 'none' && !forcePrimaryToolbarPinned) {
primaryToolbarComponents = [];
if (!hasCustomComponents(customPrimaryToolbarComponents)) {
isEditorToolbarHidden = true;
}
}
} else {
if (toolbarDocking === 'none' && !forcePrimaryToolbarPinned) {
primaryToolbarComponents = [];
if (!hasCustomComponents(customPrimaryToolbarComponents)) {
isEditorToolbarHidden = true;
}
}
}
}
var isToolbarAIFCEnabled = Boolean(editorAPI === null || editorAPI === void 0 ? void 0 : editorAPI.toolbar);
var popupsBoundariesElement = props.popupsBoundariesElement || (scrollContentContainerRef === null || scrollContentContainerRef === void 0 || (_scrollContentContain = scrollContentContainerRef.current) === null || _scrollContentContain === void 0 ? void 0 : _scrollContentContain.containerArea) || undefined;
return jsx(SSRRenderMeasure, {
segmentName: SSR_TRACE_SEGMENT_NAME,
startTimestampRef: firstRenderStartTimestampRef,
onSSRMeasure: props.onSSRMeasure
}, jsx(ContextPanelWidthProvider, null, 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: fullPageEditorWrapper
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
,
className: "akEditor",
ref: wrapperElementRef,
style: {
'--ak-editor-fullpage-toolbar-height':
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values
FULL_PAGE_EDITOR_TOOLBAR_HEIGHT(isToolbarAIFCEnabled)
}
}, !isEditorToolbarHidden && (isToolbarAIFCEnabled ? jsx(FullPageToolbarNext, {
disabled: !!props.disabled || !hasHadInteraction && expValEquals('platform_editor_default_toolbar_state', 'isEnabled', true),
toolbarDockingPosition: toolbarDockingPosition !== null && toolbarDockingPosition !== void 0 ? toolbarDockingPosition : toolbarDocking,
beforeIcon: props.primaryToolbarIconBefore,
editorAPI: editorAPI,
editorView: props.editorView,
popupsMountPoint: props.popupsMountPoint,
popupsBoundariesElement: props.popupsBoundariesElement,
popupsScrollableElement: props.popupsScrollableElement,
showKeyline: showKeyline,
customPrimaryToolbarComponents: props.customPrimaryToolbarComponents
}) : jsx(FullPageToolbar, {
appearance: props.appearance,
editorAPI: editorAPI,
beforeIcon: props.primaryToolbarIconBefore,
collabEdit: props.collabEdit,
containerElement: (_scrollContentContain2 = (_scrollContentContain3 = scrollContentContainerRef.current) === null || _scrollContentContain3 === void 0 ? void 0 : _scrollContentContain3.scrollContainer) !== null && _scrollContentContain2 !== void 0 ? _scrollContentContain2 : null,
customPrimaryToolbarComponents: props.customPrimaryToolbarComponents,
disabled: !!props.disabled,
dispatchAnalyticsEvent: props.dispatchAnalyticsEvent,
editorActions: props.editorActions,
editorDOMElement: props.editorDOMElement
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
,
editorView: props.editorView
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
,
eventDispatcher: props.eventDispatcher,
hasMinWidth: props.enableToolbarMinWidth,
popupsBoundariesElement: props.popupsBoundariesElement,
popupsMountPoint: props.popupsMountPoint,
popupsScrollableElement: props.popupsScrollableElement,
primaryToolbarComponents: primaryToolbarComponents,
providerFactory: props.providerFactory,
showKeyline: showKeyline,
featureFlags: props.featureFlags
})), jsx(FullPageContentArea, {
editorAPI: editorAPI,
ref: scrollContentContainerRef,
appearance: props.appearance,
contentComponents: props.contentComponents,
contextPanel: props.contextPanel,
customContentComponents: props.customContentComponents,
disabled: props.disabled,
dispatchAnalyticsEvent: props.dispatchAnalyticsEvent,
editorActions: props.editorActions,
editorDOMElement: props.editorDOMElement
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
,
editorView: props.editorView,
eventDispatcher: props.eventDispatcher,
popupsBoundariesElement: popupsBoundariesElement,
popupsMountPoint: props.popupsMountPoint,
popupsScrollableElement: props.popupsScrollableElement,
providerFactory: props.providerFactory,
wrapperElement: (_wrapperElementRef$cu = wrapperElementRef === null || wrapperElementRef === void 0 ? void 0 : wrapperElementRef.current) !== null && _wrapperElementRef$cu !== void 0 ? _wrapperElementRef$cu : null,
pluginHooks: props.pluginHooks,
featureFlags: props.featureFlags,
isEditorToolbarHidden: isEditorToolbarHidden,
viewMode: state.editorViewMode,
hasHadInteraction: hasHadInteraction,
contentMode: props.contentMode
}))));
};