@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
319 lines (315 loc) • 14.5 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React from 'react';
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
import { hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
import { DEFAULT_EMBED_CARD_WIDTH } from '@atlaskit/editor-shared-styles';
import AlignImageCenterIcon from '@atlaskit/icon/core/align-image-center';
import AlignImageLeftIcon from '@atlaskit/icon/core/align-image-left';
import AlignImageRightIcon from '@atlaskit/icon/core/align-image-right';
import AlignTextCenterIcon from '@atlaskit/icon/core/align-text-center';
import AlignTextLeftIcon from '@atlaskit/icon/core/align-text-left';
import AlignTextRightIcon from '@atlaskit/icon/core/align-text-right';
import ContentWidthWide from '@atlaskit/icon/core/content-width-wide';
import ContentWrapLeftIcon from '@atlaskit/icon/core/content-wrap-left';
import ContentWrapRightIcon from '@atlaskit/icon/core/content-wrap-right';
import FullWidthIcon from '@atlaskit/icon/core/expand-horizontal';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../analytics';
import { insideTable } from '../../core-utils';
import { alignCenter, alignLeft, alignRight, tooltip } from '../../keymaps';
import commonMessages, { mediaAndEmbedToolbarMessages as toolbarMessages } from '../../messages';
import { Shortcut } from '../../ui';
import { alignAttributes, isInLayoutColumn, nonWrappedLayouts } from '../../utils';
// Workaround as we don't want to import this package into `editor-common`
// We'll get type errors if this gets out of sync with `editor-plugin-width`.
export var alignmentIcons = [{
id: 'editor.media.alignLeft',
value: 'align-start',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignImageLeftIcon, {
color: "currentColor",
spacing: "spacious",
label: "media-toolbar-align-left-icon"
});
}
}, {
id: 'editor.media.alignCenter',
value: 'center',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignImageCenterIcon, {
color: "currentColor",
spacing: "spacious",
label: "media-toolbar-align-center-icon"
});
}
}, {
id: 'editor.media.alignRight',
value: 'align-end',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignImageRightIcon, {
color: "currentColor",
spacing: "spacious",
label: "media-toolbar-align-right-icon"
});
}
}];
var alignmentIconsControls = [{
id: 'editor.media.alignLeft',
value: 'align-start',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignTextLeftIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
});
},
keyboardShortcut: alignLeft
}, {
id: 'editor.media.alignCenter',
value: 'center',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignTextCenterIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
});
},
keyboardShortcut: alignCenter
}, {
id: 'editor.media.alignRight',
value: 'align-end',
icon: function icon() {
return /*#__PURE__*/React.createElement(AlignTextRightIcon, {
color: "currentColor",
spacing: "spacious",
label: ""
});
},
keyboardShortcut: alignRight
}];
export var wrappingIcons = [{
id: 'editor.media.wrapLeft',
value: 'wrap-left',
icon: function icon() {
return /*#__PURE__*/React.createElement(ContentWrapLeftIcon, {
color: "currentColor",
spacing: "spacious",
label: "media-toolbar-wrap-left-icon"
});
}
}, {
id: 'editor.media.wrapRight',
value: 'wrap-right',
icon: function icon() {
return /*#__PURE__*/React.createElement(ContentWrapRightIcon, {
color: "currentColor",
spacing: "spacious",
label: "media-toolbar-wrap-right-icon"
});
}
}];
var breakoutIcons = [{
value: 'wide',
icon: ContentWidthWide
}, {
value: 'full-width',
icon: FullWidthIcon
}];
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export var layoutToMessages = {
'wrap-left': toolbarMessages.wrapLeft,
center: commonMessages.alignImageCenter,
'wrap-right': toolbarMessages.wrapRight,
wide: commonMessages.layoutWide,
'full-width': commonMessages.layoutFullWidth,
'align-end': commonMessages.alignImageRight,
'align-start': commonMessages.alignImageLeft
};
var getNodeWidth = function getNodeWidth(node, schema) {
var embedCard = schema.nodes.embedCard;
if (node.type === embedCard) {
return node.attrs.originalWidth || DEFAULT_EMBED_CARD_WIDTH;
}
return node.firstChild && node.firstChild.attrs.width || node.attrs.width;
};
var makeAlign = function makeAlign(layout, nodeType, widthPluginDependencyApi, analyticsApi, allowPixelResizing) {
return function (state, dispatch) {
var _ref = state.selection,
node = _ref.node;
var previousLayoutType = node.attrs.layout;
var mediaSingle = state.schema.nodes.mediaSingle;
if (!dispatch) {
return false;
}
var widthPluginState = widthPluginDependencyApi === null || widthPluginDependencyApi === void 0 ? void 0 : widthPluginDependencyApi.sharedState.currentState();
if (!node || node.type !== nodeType || !widthPluginState) {
return false;
}
var nodeWidth = getNodeWidth(node, state.schema);
var newAttrs = allowPixelResizing ? // with extended experience, change alignment does not change media single width
_objectSpread(_objectSpread({}, node.attrs), {}, {
layout: layout
}) : alignAttributes(layout, node.attrs, undefined, nodeWidth, widthPluginState.lineLength);
var tr = state.tr.setNodeMarkup(state.selection.from, undefined, newAttrs);
tr.setMeta('scrollIntoView', false);
// when image captions are enabled, the wrong node gets selected after
// setNodeMarkup is called
tr.setSelection(NodeSelection.create(tr.doc, state.selection.from));
var paragraph = tr.doc.type.schema.nodes.paragraph;
// see https://product-fabric.atlassian.net/browse/ED-15518 insert a new paragraph when an embedded card is wrapped left or right
if (layout.startsWith('wrap') && paragraph && !tr.doc.nodeAt(state.selection.to) && (insideTable(state) || isInLayoutColumn(state))) {
var emptyParaghraph = paragraph.createAndFill();
if (emptyParaghraph) {
tr.insert(state.selection.to, emptyParaghraph);
}
}
analyticsApi === null || analyticsApi === void 0 || analyticsApi.attachAnalyticsEvent({
eventType: EVENT_TYPE.TRACK,
action: ACTION.SELECTED,
actionSubject: ACTION_SUBJECT[node.type === mediaSingle ? 'MEDIA_SINGLE' : 'EMBEDS'],
actionSubjectId: ACTION_SUBJECT_ID.RICH_MEDIA_LAYOUT,
attributes: {
previousLayoutType: previousLayoutType,
currentLayoutType: layout
}
})(tr);
dispatch(tr);
return true;
};
};
var getToolbarLayout = function getToolbarLayout(layout, allowPixelResizing) {
if (nonWrappedLayouts.includes(layout) && allowPixelResizing) {
return 'center';
}
return layout;
};
var mapIconsToToolbarItem = function mapIconsToToolbarItem(icons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled, allowPixelResizing) {
return icons.map(function (toolbarItem) {
var id = toolbarItem.id,
value = toolbarItem.value;
return _objectSpread({
id: id,
type: 'button',
icon: toolbarItem.icon,
title: intl.formatMessage(layoutToMessages[value]),
selected: getToolbarLayout(layout, allowPixelResizing) === value,
onClick: makeAlign(value, nodeType, widthPluginDependencyApi, analyticsApi, allowPixelResizing)
}, isChangingLayoutDisabled && {
disabled: value !== 'center'
});
});
};
var mapIconsToDropdownOptions = function mapIconsToDropdownOptions(_ref2) {
var icons = _ref2.icons,
layout = _ref2.layout,
intl = _ref2.intl,
nodeType = _ref2.nodeType,
widthPluginDependencyApi = _ref2.widthPluginDependencyApi,
analyticsApi = _ref2.analyticsApi,
isChangingLayoutDisabled = _ref2.isChangingLayoutDisabled,
allowPixelResizing = _ref2.allowPixelResizing;
return icons.map(function (layoutOption) {
var id = layoutOption.id,
value = layoutOption.value;
return _objectSpread(_objectSpread({
id: id,
icon: /*#__PURE__*/React.createElement(layoutOption.icon, {
label: ""
}),
title: intl.formatMessage(layoutToMessages[value]),
selected: getToolbarLayout(layout, allowPixelResizing) === value,
onClick: makeAlign(value, nodeType, widthPluginDependencyApi, analyticsApi, allowPixelResizing)
}, layoutOption.keyboardShortcut && {
elemAfter: /*#__PURE__*/React.createElement(Shortcut, null, tooltip(layoutOption.keyboardShortcut))
}), isChangingLayoutDisabled && {
disabled: value !== 'center'
});
});
};
var shouldHideLayoutToolbar = function shouldHideLayoutToolbar(selection, _ref3, allowResizingInTables) {
var nodes = _ref3.nodes;
return hasParentNodeOfType([nodes.bodiedExtension, nodes.extensionFrame, nodes.listItem].concat(_toConsumableArray(expValEquals('editor_enable_image_alignment_in_expand', 'isEnabled', true) ? [] : [nodes.expand, nodes.nestedExpand]), _toConsumableArray(allowResizingInTables ? [] : [nodes.table])).filter(Boolean))(selection);
};
var buildLayoutButtons = function buildLayoutButtons(state, intl, nodeType, widthPluginDependencyApi, analyticsApi, allowResizing, allowResizingInTables) {
var allowWrapping = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : true;
var allowAlignment = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true;
var isChangingLayoutDisabled = arguments.length > 9 ? arguments[9] : undefined;
var allowPixelResizing = arguments.length > 10 ? arguments[10] : undefined;
var selection = state.selection;
if (!(selection instanceof NodeSelection) || !selection.node || !nodeType || shouldHideLayoutToolbar(selection, state.schema, allowResizingInTables)) {
return [];
}
var layout = selection.node.attrs.layout;
var alignmentToolbarItems = allowAlignment ? mapIconsToToolbarItem(alignmentIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled, allowPixelResizing) : [];
var wrappingToolbarItems = allowWrapping ? mapIconsToToolbarItem(wrappingIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled, allowPixelResizing) : [];
var breakOutToolbarItems = !allowResizing ? mapIconsToToolbarItem(breakoutIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, allowPixelResizing) : [];
var items = [].concat(_toConsumableArray(alignmentToolbarItems), _toConsumableArray(getSeparatorBetweenAlignmentAndWrapping(allowAlignment, allowWrapping)), _toConsumableArray(wrappingToolbarItems), _toConsumableArray(getSeparatorBeforeBreakoutItems(allowAlignment, allowWrapping, allowResizing)), _toConsumableArray(breakOutToolbarItems));
return items;
};
export var buildLayoutDropdown = function buildLayoutDropdown(state, intl, nodeType, widthPluginDependencyApi, analyticsApi, allowResizing, allowResizingInTables) {
var allowWrapping = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : true;
var allowAlignment = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true;
var isChangingLayoutDisabled = arguments.length > 9 ? arguments[9] : undefined;
var allowPixelResizing = arguments.length > 10 ? arguments[10] : undefined;
var selection = state.selection;
if (!(selection instanceof NodeSelection) || !selection.node || !nodeType || shouldHideLayoutToolbar(selection, state.schema, allowResizingInTables)) {
return [];
}
var layout = selection.node.attrs.layout;
var icons = [];
if (allowAlignment) {
icons.push.apply(icons, alignmentIconsControls);
}
if (allowWrapping) {
icons.push.apply(icons, wrappingIcons);
}
if (!allowResizing) {
icons.push.apply(icons, breakoutIcons);
}
if (icons.length === 0) {
return [];
}
var selectedLayout = getSelectedLayoutIcon(icons, selection.node) || icons[0];
if (!selectedLayout) {
return [];
}
var alignmentDropdownOptions = mapIconsToDropdownOptions({
icons: icons,
layout: layout,
intl: intl,
nodeType: nodeType,
widthPluginDependencyApi: widthPluginDependencyApi,
analyticsApi: analyticsApi,
isChangingLayoutDisabled: isChangingLayoutDisabled,
allowPixelResizing: allowPixelResizing
});
return [{
type: 'dropdown',
title: intl.formatMessage(layoutToMessages[selectedLayout.value]),
icon: selectedLayout.icon,
options: alignmentDropdownOptions,
shouldFitContainer: false,
testId: "".concat(nodeType.name, "-layout-dropdown-trigger-button")
}];
};
var getSelectedLayoutIcon = function getSelectedLayoutIcon(layoutIcons, selectedNode) {
var selectedLayout = selectedNode.attrs.layout;
return layoutIcons.find(function (icon) {
return icon.value === (nonWrappedLayouts.includes(selectedLayout) ? 'center' : selectedLayout);
});
};
var getSeparatorBetweenAlignmentAndWrapping = function getSeparatorBetweenAlignmentAndWrapping(allowAlignment, allowWrapping) {
return allowAlignment && allowWrapping ? [{
type: 'separator'
}] : [];
};
var getSeparatorBeforeBreakoutItems = function getSeparatorBeforeBreakoutItems(allowAlignment, allowWrapping, allowResizing) {
return !allowResizing && (allowAlignment || allowWrapping) ? [{
type: 'separator'
}] : [];
};
export default buildLayoutButtons;