@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
278 lines (276 loc) • 22.4 kB
JavaScript
import { shouldForceTracking } from '@atlaskit/editor-common/utils';
import { accessibilityUtilsPlugin } from '@atlaskit/editor-plugins/accessibility-utils';
import { alignmentPlugin } from '@atlaskit/editor-plugins/alignment';
import { annotationPlugin } from '@atlaskit/editor-plugins/annotation';
import { avatarGroupPlugin } from '@atlaskit/editor-plugins/avatar-group';
import { batchAttributeUpdatesPlugin } from '@atlaskit/editor-plugins/batch-attribute-updates';
import { beforePrimaryToolbarPlugin } from '@atlaskit/editor-plugins/before-primary-toolbar';
import { blockControlsPlugin } from '@atlaskit/editor-plugins/block-controls';
import { borderPlugin } from '@atlaskit/editor-plugins/border';
import { breakoutPlugin } from '@atlaskit/editor-plugins/breakout';
import { captionPlugin } from '@atlaskit/editor-plugins/caption';
import { cardPlugin } from '@atlaskit/editor-plugins/card';
import { codeBidiWarningPlugin } from '@atlaskit/editor-plugins/code-bidi-warning';
import { collabEditPlugin } from '@atlaskit/editor-plugins/collab-edit';
import { contentInsertionPlugin } from '@atlaskit/editor-plugins/content-insertion';
import { contextPanelPlugin } from '@atlaskit/editor-plugins/context-panel';
import { customAutoformatPlugin } from '@atlaskit/editor-plugins/custom-autoformat';
import { dataConsumerPlugin } from '@atlaskit/editor-plugins/data-consumer';
import { datePlugin } from '@atlaskit/editor-plugins/date';
import { emojiPlugin } from '@atlaskit/editor-plugins/emoji';
import { expandPlugin } from '@atlaskit/editor-plugins/expand';
import { extensionPlugin } from '@atlaskit/editor-plugins/extension';
import { feedbackDialogPlugin } from '@atlaskit/editor-plugins/feedback-dialog';
import { findReplacePlugin } from '@atlaskit/editor-plugins/find-replace';
import { fragmentPlugin } from '@atlaskit/editor-plugins/fragment';
import { gridPlugin } from '@atlaskit/editor-plugins/grid';
import { guidelinePlugin } from '@atlaskit/editor-plugins/guideline';
import { helpDialogPlugin } from '@atlaskit/editor-plugins/help-dialog';
import { imageUploadPlugin } from '@atlaskit/editor-plugins/image-upload';
import { indentationPlugin } from '@atlaskit/editor-plugins/indentation';
import { insertBlockPlugin } from '@atlaskit/editor-plugins/insert-block';
import { layoutPlugin } from '@atlaskit/editor-plugins/layout';
import { listPlugin } from '@atlaskit/editor-plugins/list';
import { maxContentSizePlugin } from '@atlaskit/editor-plugins/max-content-size';
import { mediaPlugin } from '@atlaskit/editor-plugins/media';
import { mediaInsertPlugin } from '@atlaskit/editor-plugins/media-insert';
import { mentionsPlugin } from '@atlaskit/editor-plugins/mentions';
import { panelPlugin } from '@atlaskit/editor-plugins/panel';
import { pasteOptionsToolbarPlugin } from '@atlaskit/editor-plugins/paste-options-toolbar';
import { placeholderTextPlugin } from '@atlaskit/editor-plugins/placeholder-text';
import { rulePlugin } from '@atlaskit/editor-plugins/rule';
import { saveOnEnterPlugin } from '@atlaskit/editor-plugins/save-on-enter';
import { scrollIntoViewPlugin } from '@atlaskit/editor-plugins/scroll-into-view';
import { statusPlugin } from '@atlaskit/editor-plugins/status';
import { syncedBlockPlugin } from '@atlaskit/editor-plugins/synced-block';
import { tablesPlugin } from '@atlaskit/editor-plugins/table';
import { tasksAndDecisionsPlugin } from '@atlaskit/editor-plugins/tasks-and-decisions';
import { textColorPlugin } from '@atlaskit/editor-plugins/text-color';
import { toolbarListsIndentationPlugin } from '@atlaskit/editor-plugins/toolbar-lists-indentation';
import { ufoPlugin } from '@atlaskit/editor-plugins/ufo';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { isFullPage as fullPageCheck } from '../utils/is-full-page';
import { version as coreVersion } from '../version-wrapper';
import { createDefaultPreset } from './default';
/**
* Mechanism to configuring plugins as the universal preset blocks direct access
* to configuring plugins.
*
* Note: not all plugins are configurable via this mechanism, and for plugins configured -- it is only doing a subset of the configuration.
*/
/**
* Creates a preset with all of the available plugins.
* Basis for create-plugins-list and can be used to migrate from Editor -> EditorNext (Presets project)
* with minimal friction.
*
* @param appearance
* @param props A subset of full EditorProps for the full feature preset
* @param featureFlags
* @param prevAppearance The appearance of the editor in the previous render
* @returns a full featured preset configured according to the provided props - basis for create-plugins-list
*/
export default function createUniversalPresetInternal({
appearance,
props,
featureFlags,
initialPluginConfiguration,
prevAppearance,
createAnalyticsEvent
}) {
var _initialPluginConfigu, _initialPluginConfigu2, _initialPluginConfigu3, _initialPluginConfigu4, _initialPluginConfigu5, _initialPluginConfigu6, _featureFlags$lpLinkP, _initialPluginConfigu7, _initialPluginConfigu8, _initialPluginConfigu9, _initialPluginConfigu0, _initialPluginConfigu1, _initialPluginConfigu10, _props$media, _props$media2, _props$media3, _props$media4, _props$media5, _props$media6, _props$media7, _props$mention$insert, _props$mention, _props$mention2, _props$mention3, _props$collabEdit$EXP, _props$collabEdit, _props$linking, _props$linking2, _featureFlags$lpLinkP2, _props$linking3, _props$primaryToolbar;
const isComment = appearance === 'comment';
const isChromeless = appearance === 'chromeless';
const isFullPage = fullPageCheck(appearance);
const getEditorFeatureFlags = () => featureFlags;
const defaultPreset = createDefaultPreset({
...props,
blockType: {
...props.blockType,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.blockTypePlugin)
},
blockMenu: {
enabled: (_initialPluginConfigu = initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu2 = initialPluginConfiguration.blockMenuPlugin) === null || _initialPluginConfigu2 === void 0 ? void 0 : _initialPluginConfigu2.enabled) !== null && _initialPluginConfigu !== void 0 ? _initialPluginConfigu : false,
useStandardNodeWidth: (_initialPluginConfigu3 = initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu4 = initialPluginConfiguration.blockMenuPlugin) === null || _initialPluginConfigu4 === void 0 ? void 0 : _initialPluginConfigu4.useStandardNodeWidth) !== null && _initialPluginConfigu3 !== void 0 ? _initialPluginConfigu3 : false,
blockLinkHashPrefix: initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu5 = initialPluginConfiguration.blockMenuPlugin) === null || _initialPluginConfigu5 === void 0 ? void 0 : _initialPluginConfigu5.blockLinkHashPrefix,
getLinkPath: initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu6 = initialPluginConfiguration.blockMenuPlugin) === null || _initialPluginConfigu6 === void 0 ? void 0 : _initialPluginConfigu6.getLinkPath
},
appearance,
createAnalyticsEvent,
hyperlinkOptions: {
lpLinkPicker: (_featureFlags$lpLinkP = featureFlags.lpLinkPicker) !== null && _featureFlags$lpLinkP !== void 0 ? _featureFlags$lpLinkP : false,
...props.hyperlinkOptions
},
__livePage: props.__livePage,
toolbar: initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.toolbarPlugin
});
const statusMenuDisabled = !props.allowStatus ? true : typeof props.allowStatus === 'object' ? Boolean(props.allowStatus.menuDisabled) : false;
const hasBeforePrimaryToolbar = components => {
if (components && 'before' in components) {
return !!components.before;
}
return false;
};
const finalPreset = defaultPreset.add(ufoPlugin).add(dataConsumerPlugin).add(accessibilityUtilsPlugin).add(contentInsertionPlugin).add(batchAttributeUpdatesPlugin).maybeAdd([blockControlsPlugin, {
quickInsertButtonEnabled: (_initialPluginConfigu7 = initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu8 = initialPluginConfiguration.blockControlsPlugin) === null || _initialPluginConfigu8 === void 0 ? void 0 : _initialPluginConfigu8.quickInsertButtonEnabled) !== null && _initialPluginConfigu7 !== void 0 ? _initialPluginConfigu7 : true,
rightSideControlsEnabled: (_initialPluginConfigu9 = initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu0 = initialPluginConfiguration.blockControlsPlugin) === null || _initialPluginConfigu0 === void 0 ? void 0 : _initialPluginConfigu0.rightSideControlsEnabled) !== null && _initialPluginConfigu9 !== void 0 ? _initialPluginConfigu9 : false
}], Boolean((_initialPluginConfigu1 = initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : (_initialPluginConfigu10 = initialPluginConfiguration.blockControlsPlugin) === null || _initialPluginConfigu10 === void 0 ? void 0 : _initialPluginConfigu10.enabled) !== null && _initialPluginConfigu1 !== void 0 ? _initialPluginConfigu1 : false)).maybeAdd([breakoutPlugin, {
allowBreakoutButton: appearance === 'full-page',
appearance: appearance
}], Boolean(props.allowBreakout && (isFullPage || appearance === 'max' && (expValEqualsNoExposure('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEqualsNoExposure('confluence_max_width_content_appearance', 'isEnabled', true)) && fg('platform_editor_breakout_in_universal_preset')))).maybeAdd(alignmentPlugin, Boolean(props.allowTextAlignment)).maybeAdd([textColorPlugin, props.allowTextColor], Boolean(props.allowTextColor)).add(listPlugin).maybeAdd(rulePlugin, Boolean(props.allowRule)).maybeAdd([expandPlugin, {
allowInsertion: isExpandInsertionEnabled(props),
useLongPressSelection: false,
appearance: appearance,
allowInteractiveExpand: typeof props.allowExpand === 'boolean' ? props.allowExpand : Boolean(props.allowExpand && props.allowExpand.allowInteractiveExpand !== false),
__livePage: props.__livePage
}], Boolean(props.allowExpand)).maybeAdd(guidelinePlugin, Boolean(!isComment && !isChromeless && (props.media || props.allowTables) || editorExperiment('platform_editor_breakout_resizing', true, {
exposure: true
}) && (props.allowExpand || props.allowLayouts || props.codeBlock))).maybeAdd([gridPlugin, {
shouldCalcBreakoutGridLines: isFullPage
}], Boolean(props.media)).maybeAdd([annotationPlugin, props.annotationProviders], Boolean(props.annotationProviders)).maybeAdd([mediaPlugin, {
...props.media,
allowLazyLoading: true,
allowBreakoutSnapPoints: isFullPage,
allowAdvancedToolBarOptions: typeof ((_props$media = props.media) === null || _props$media === void 0 ? void 0 : _props$media.allowAdvancedToolBarOptions) !== 'undefined' ? (_props$media2 = props.media) === null || _props$media2 === void 0 ? void 0 : _props$media2.allowAdvancedToolBarOptions : isFullPage || isComment,
allowCommentsOnMedia: isFullPage && Boolean(props.annotationProviders),
allowDropzoneDropLine: isFullPage,
allowMediaSingleEditable: true,
allowRemoteDimensionsFetch: true,
allowMarkingUploadsAsIncomplete: false,
allowImagePreview: typeof ((_props$media3 = props.media) === null || _props$media3 === void 0 ? void 0 : _props$media3.allowImagePreview) !== 'undefined' ? (_props$media4 = props.media) === null || _props$media4 === void 0 ? void 0 : _props$media4.allowImagePreview : isFullPage,
fullWidthEnabled: appearance === 'full-width',
editorAppearance: appearance,
uploadErrorHandler: props.uploadErrorHandler,
waitForMediaUpload: props.waitForMediaUpload,
isCopyPasteEnabled: true,
alignLeftOnInsert: typeof ((_props$media5 = props.media) === null || _props$media5 === void 0 ? void 0 : _props$media5.alignLeftOnInsert) !== 'undefined' ? (_props$media6 = props.media) === null || _props$media6 === void 0 ? void 0 : _props$media6.alignLeftOnInsert : isComment,
getEditorFeatureFlags
}], Boolean(props.media)).maybeAdd(mediaInsertPlugin, Boolean(props.media)).maybeAdd(captionPlugin, Boolean((_props$media7 = props.media) === null || _props$media7 === void 0 ? void 0 : _props$media7.allowCaptions)).maybeAdd([mentionsPlugin, {
sanitizePrivateContent: props.sanitizePrivateContent,
insertDisplayName: (_props$mention$insert = (_props$mention = props.mention) === null || _props$mention === void 0 ? void 0 : _props$mention.insertDisplayName) !== null && _props$mention$insert !== void 0 ? _props$mention$insert : props.mentionInsertDisplayName,
allowZeroWidthSpaceAfter: true,
HighlightComponent: (_props$mention2 = props.mention) === null || _props$mention2 === void 0 ? void 0 : _props$mention2.HighlightComponent,
profilecardProvider: (_props$mention3 = props.mention) === null || _props$mention3 === void 0 ? void 0 : _props$mention3.profilecardProvider,
mentionProvider: props.mentionProvider,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.mentionsPlugin)
}], Boolean(props.mentionProvider)).maybeAdd([emojiPlugin, {
emojiProvider: props.emojiProvider,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.emojiPlugin)
}], Boolean(props.emojiProvider)).maybeAdd([tablesPlugin, {
tableOptions: !props.allowTables || typeof props.allowTables === 'boolean' ? {} : props.allowTables,
isTableScalingEnabled: isFullPage || isComment,
allowContextualMenu: true,
fullWidthEnabled: appearance === 'full-width',
wasFullWidthEnabled: prevAppearance && prevAppearance === 'full-width',
getEditorFeatureFlags,
isCommentEditor: isComment,
isChromelessEditor: isChromeless,
allowFixedColumnWidthOption: fg('platform_editor_table_fixed_column_width_prop') ? props.allowTables && typeof props.allowTables !== 'boolean' && props.allowTables.allowFixedColumnWidthOption : false
}], Boolean(props.allowTables)).maybeAdd([tasksAndDecisionsPlugin, {
allowNestedTasks: props.allowNestedTasks,
consumeTabs: isFullPage,
useLongPressSelection: false,
taskDecisionProvider: props.taskDecisionProvider,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.tasksAndDecisionsPlugin)
}], Boolean(props.allowTasksAndDecisions || props.taskDecisionProvider)).maybeAdd([feedbackDialogPlugin, {
coreVersion,
...props.feedbackInfo
}], Boolean(props.feedbackInfo)).maybeAdd([helpDialogPlugin, !!props.legacyImageUploadProvider], Boolean(props.allowHelpDialog)).maybeAdd([saveOnEnterPlugin, props.onSave], Boolean(props.saveOnEnter && props.onSave)).maybeAdd(imageUploadPlugin, Boolean(props.legacyImageUploadProvider)).maybeAdd(
// duplicate plugin exists because first one if media is enabled
// second one when “media is disabled, and legacyMediaProvider is enabled
// @ts-expect-error
[mediaPlugin, {
allowMediaSingle: {
disableLayout: true
},
allowMediaGroup: false,
isCopyPasteEnabled: true
}], Boolean(props.legacyImageUploadProvider && !props.media)).maybeAdd([collabEditPlugin, {
...props.collabEdit,
sanitizePrivateContent: props.sanitizePrivateContent,
EXPERIMENTAL_allowInternalErrorAnalytics: (_props$collabEdit$EXP = (_props$collabEdit = props.collabEdit) === null || _props$collabEdit === void 0 ? void 0 : _props$collabEdit.EXPERIMENTAL_allowInternalErrorAnalytics) !== null && _props$collabEdit$EXP !== void 0 ? _props$collabEdit$EXP : shouldForceTracking()
}], Boolean(props.collabEdit || props.collabEditProvider)).maybeAdd([maxContentSizePlugin, props.maxContentSize], Boolean(props.maxContentSize)).maybeAdd([panelPlugin, {
useLongPressSelection: false,
allowCustomPanel: typeof props.allowPanel === 'object' ? props.allowPanel.allowCustomPanel : false,
allowCustomPanelEdit: typeof props.allowPanel === 'object' ? props.allowPanel.allowCustomPanelEdit : false
}], Boolean(props.allowPanel)).maybeAdd(contextPanelPlugin, Boolean(isFullPage)).maybeAdd([extensionPlugin, {
breakoutEnabled: appearance === 'full-page' && (typeof props.allowExtension === 'object' ? props.allowExtension.allowBreakout : true) !== false,
extensionHandlers: props.extensionHandlers,
useLongPressSelection: false,
appearance,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.extensionPlugin)
}], Boolean(props.allowExtension)).maybeAdd(
// we are ignoring a duplicate plugin error here
// this error exists because we have two annotation plugins being added
// one with annotationProviders and one with allowConfluenceInlineComment
// long term this and media should be consolidated into adding both only once
// @ts-expect-error
[annotationPlugin, undefined], Boolean(!props.annotationProviders && props.allowConfluenceInlineComment)).maybeAdd([datePlugin, {
weekStartDay: typeof props.allowDate === 'object' ? props.allowDate.weekStartDay : undefined
}], Boolean(props.allowDate)).maybeAdd([placeholderTextPlugin,
// @ts-expect-error 2322: Type 'false | PlaceholderTextOptions | undefined'
props.allowTemplatePlaceholders !== true ? props.allowTemplatePlaceholders : {}], Boolean(props.allowTemplatePlaceholders)).maybeAdd([layoutPlugin, {
...(typeof props.allowLayouts === 'object' ? props.allowLayouts : {}),
useLongPressSelection: false,
UNSAFE_allowSingleColumnLayout: typeof props.allowLayouts === 'object' ? props.allowLayouts.UNSAFE_allowSingleColumnLayout : undefined,
editorAppearance: appearance
}], Boolean(props.allowLayouts)).maybeAdd([cardPlugin, {
...props.UNSAFE_cards,
...props.smartLinks,
...((_props$linking = props.linking) === null || _props$linking === void 0 ? void 0 : _props$linking.smartLinks),
fullWidthMode: appearance === 'full-width',
linkPicker: (_props$linking2 = props.linking) === null || _props$linking2 === void 0 ? void 0 : _props$linking2.linkPicker,
lpLinkPicker: (_featureFlags$lpLinkP2 = featureFlags.lpLinkPicker) !== null && _featureFlags$lpLinkP2 !== void 0 ? _featureFlags$lpLinkP2 : false,
editorAppearance: appearance,
// @ts-ignore Temporary solution to check for Live Page editor.
__livePage: props.__livePage
}], Boolean(((_props$linking3 = props.linking) === null || _props$linking3 === void 0 ? void 0 : _props$linking3.smartLinks) || props.smartLinks || props.UNSAFE_cards)).maybeAdd([customAutoformatPlugin, {
autoformattingProvider: props.autoformattingProvider
}], Boolean(props.autoformattingProvider)).maybeAdd([statusPlugin, {
menuDisabled: statusMenuDisabled,
allowZeroWidthSpaceAfter: true
}], Boolean(props.allowStatus)).maybeAdd([syncedBlockPlugin, props.syncBlock], Boolean(props.syncBlock) && editorExperiment('platform_synced_block', true)).maybeAdd(indentationPlugin, Boolean(props.allowIndentation)).maybeAdd(scrollIntoViewPlugin, Boolean(props.autoScrollIntoView !== false)).add([toolbarListsIndentationPlugin, {
showIndentationButtons: !!props.showIndentationButtons,
allowHeadingAndParagraphIndentation: !!props.allowIndentation
}]).add([insertBlockPlugin, {
allowTables: !!props.allowTables,
allowExpand: isExpandInsertionEnabled(props),
insertMenuItems: props.insertMenuItems,
horizontalRuleEnabled: props.allowRule,
tableSelectorSupported: (featureFlags === null || featureFlags === void 0 ? void 0 : featureFlags.tableSelector) && isFullPage,
nativeStatusSupported: !statusMenuDisabled,
showElementBrowserLink: props.elementBrowser && props.elementBrowser.showModal || false,
appearance,
...(initialPluginConfiguration === null || initialPluginConfiguration === void 0 ? void 0 : initialPluginConfiguration.insertBlockPlugin)
}]).maybeAdd([beforePrimaryToolbarPlugin, {
beforePrimaryToolbarComponents: // @ts-expect-error 2339: Property 'before' does not exist on type 'PrimaryToolbarComponents'.
(_props$primaryToolbar = props.primaryToolbarComponents) === null || _props$primaryToolbar === void 0 ? void 0 : _props$primaryToolbar.before
}], Boolean(hasBeforePrimaryToolbar(props.primaryToolbarComponents) && !featureFlags.twoLineEditorToolbar)).add([avatarGroupPlugin, {
// Avatars are moved to Confluence codebase for Edit in Context
// When Edit in Context is enabled primaryToolbarComponents is undefined
// For more details please check
// https://hello.atlassian.net/wiki/spaces/PCG/pages/2851572180/Editor+toolbar+for+live+pages+and+edit+in+context+projects
collabEdit: props.collabEdit,
takeFullWidth: !hasBeforePrimaryToolbar(props.primaryToolbarComponents),
showAvatarGroup: featureFlags.showAvatarGroupAsPlugin === true && !featureFlags.twoLineEditorToolbar
}]).maybeAdd([findReplacePlugin, {
takeFullWidth: !!featureFlags.showAvatarGroupAsPlugin === false && !hasBeforePrimaryToolbar(props.primaryToolbarComponents),
twoLineEditorToolbar: !!props.primaryToolbarComponents && !!featureFlags.twoLineEditorToolbar
}], Boolean(props.allowFindReplace)).maybeAdd(borderPlugin, Boolean(props.allowBorderMark)).maybeAdd(fragmentPlugin, Boolean(props.allowFragmentMark)).add(pasteOptionsToolbarPlugin).maybeAdd([codeBidiWarningPlugin, {
appearance
}], !expValEquals('platform_editor_remove_bidi_char_warning', 'isEnabled', true));
return finalPreset;
}
export function isExpandInsertionEnabled({
allowExpand
}) {
if (allowExpand === true && expValEquals('platform_editor_expand_paste_in_comment_editor', 'isEnabled', true)) {
return true;
}
if (allowExpand && typeof allowExpand === 'object') {
return !!allowExpand.allowInsertion;
}
return false;
}