@atlaskit/editor-common 74.32.1 → 74.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/card/MediaAndEmbedsToolbar/index.js +23 -7
  3. package/dist/cjs/guideline/index.js +13 -0
  4. package/dist/cjs/keymaps/index.js +8 -1
  5. package/dist/cjs/keymaps/keymap.js +38 -0
  6. package/dist/cjs/monitoring/error.js +1 -1
  7. package/dist/cjs/styles/shared/media-single.js +2 -1
  8. package/dist/cjs/ui/DropList/index.js +1 -1
  9. package/dist/cjs/ui/MediaSingle/index.js +2 -7
  10. package/dist/cjs/ui/MediaSingle/styled.js +24 -2
  11. package/dist/cjs/ui/index.js +0 -6
  12. package/dist/cjs/utils/commands.js +180 -15
  13. package/dist/cjs/utils/editor-core-utils.js +53 -3
  14. package/dist/cjs/utils/index.js +54 -0
  15. package/dist/cjs/utils/input-rules.js +48 -2
  16. package/dist/cjs/utils/rich-media-utils.js +6 -3
  17. package/dist/cjs/version.json +1 -1
  18. package/dist/es2019/card/MediaAndEmbedsToolbar/index.js +23 -8
  19. package/dist/es2019/guideline/index.js +1 -0
  20. package/dist/es2019/keymaps/index.js +3 -2
  21. package/dist/es2019/keymaps/keymap.js +33 -0
  22. package/dist/es2019/monitoring/error.js +1 -1
  23. package/dist/es2019/styles/shared/media-single.js +6 -5
  24. package/dist/es2019/ui/DropList/index.js +1 -1
  25. package/dist/es2019/ui/MediaSingle/index.js +1 -4
  26. package/dist/es2019/ui/MediaSingle/styled.js +45 -2
  27. package/dist/es2019/ui/index.js +1 -1
  28. package/dist/es2019/utils/commands.js +173 -2
  29. package/dist/es2019/utils/editor-core-utils.js +46 -1
  30. package/dist/es2019/utils/index.js +4 -4
  31. package/dist/es2019/utils/input-rules.js +45 -0
  32. package/dist/es2019/utils/rich-media-utils.js +3 -1
  33. package/dist/es2019/version.json +1 -1
  34. package/dist/esm/card/MediaAndEmbedsToolbar/index.js +24 -8
  35. package/dist/esm/guideline/index.js +1 -0
  36. package/dist/esm/keymaps/index.js +3 -2
  37. package/dist/esm/keymaps/keymap.js +33 -0
  38. package/dist/esm/monitoring/error.js +1 -1
  39. package/dist/esm/styles/shared/media-single.js +2 -1
  40. package/dist/esm/ui/DropList/index.js +1 -1
  41. package/dist/esm/ui/MediaSingle/index.js +1 -4
  42. package/dist/esm/ui/MediaSingle/styled.js +25 -3
  43. package/dist/esm/ui/index.js +1 -1
  44. package/dist/esm/utils/commands.js +170 -14
  45. package/dist/esm/utils/editor-core-utils.js +47 -0
  46. package/dist/esm/utils/index.js +4 -4
  47. package/dist/esm/utils/input-rules.js +44 -0
  48. package/dist/esm/utils/rich-media-utils.js +3 -1
  49. package/dist/esm/version.json +1 -1
  50. package/dist/types/card/MediaAndEmbedsToolbar/index.d.ts +1 -1
  51. package/dist/types/guideline/index.d.ts +1 -0
  52. package/dist/types/keymaps/index.d.ts +1 -0
  53. package/dist/types/keymaps/keymap.d.ts +11 -0
  54. package/dist/types/types/block-type.d.ts +1 -0
  55. package/dist/types/types/feature-flags.d.ts +8 -0
  56. package/dist/types/types/index.d.ts +1 -1
  57. package/dist/types/ui/DropList/index.d.ts +1 -1
  58. package/dist/types/ui/MediaSingle/index.d.ts +1 -2
  59. package/dist/types/ui/MediaSingle/styled.d.ts +3 -1
  60. package/dist/types/ui/index.d.ts +1 -1
  61. package/dist/types/utils/commands.d.ts +11 -5
  62. package/dist/types/utils/editor-core-utils.d.ts +7 -2
  63. package/dist/types/utils/index.d.ts +4 -4
  64. package/dist/types/utils/input-rules.d.ts +12 -4
  65. package/dist/types/utils/rich-media-utils.d.ts +1 -0
  66. package/dist/types-ts4.5/card/MediaAndEmbedsToolbar/index.d.ts +1 -1
  67. package/dist/types-ts4.5/guideline/index.d.ts +1 -0
  68. package/dist/types-ts4.5/keymaps/index.d.ts +1 -0
  69. package/dist/types-ts4.5/keymaps/keymap.d.ts +11 -0
  70. package/dist/types-ts4.5/types/block-type.d.ts +1 -0
  71. package/dist/types-ts4.5/types/feature-flags.d.ts +8 -0
  72. package/dist/types-ts4.5/types/index.d.ts +1 -1
  73. package/dist/types-ts4.5/ui/DropList/index.d.ts +1 -1
  74. package/dist/types-ts4.5/ui/MediaSingle/index.d.ts +1 -2
  75. package/dist/types-ts4.5/ui/MediaSingle/styled.d.ts +3 -1
  76. package/dist/types-ts4.5/ui/index.d.ts +1 -1
  77. package/dist/types-ts4.5/utils/commands.d.ts +11 -5
  78. package/dist/types-ts4.5/utils/editor-core-utils.d.ts +7 -2
  79. package/dist/types-ts4.5/utils/index.d.ts +4 -4
  80. package/dist/types-ts4.5/utils/input-rules.d.ts +12 -4
  81. package/dist/types-ts4.5/utils/rich-media-utils.d.ts +1 -0
  82. package/package.json +7 -3
@@ -1,7 +1,11 @@
1
- import { TextSelection } from '@atlaskit/editor-prosemirror/state';
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
3
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../analytics';
4
+ import { withAnalytics } from '../editor-analytics';
2
5
  import { GapCursorSelection } from '../selection';
3
6
  import { isEmptyParagraph } from './editor-core-utils';
4
- const filter = (predicates, cmd) => {
7
+ import { isMediaNode } from './nodes';
8
+ export const filter = (predicates, cmd) => {
5
9
  return function (state, dispatch, view) {
6
10
  if (!Array.isArray(predicates)) {
7
11
  predicates = [predicates];
@@ -51,6 +55,173 @@ export const walkPrevNode = $startPos => {
51
55
  foundNode: $pos.pos > 0
52
56
  };
53
57
  };
58
+ export function insertNewLine() {
59
+ return function (state, dispatch) {
60
+ const {
61
+ $from
62
+ } = state.selection;
63
+ const parent = $from.parent;
64
+ const {
65
+ hardBreak
66
+ } = state.schema.nodes;
67
+ if (hardBreak) {
68
+ const hardBreakNode = hardBreak.createChecked();
69
+ if (parent && parent.type.validContent(Fragment.from(hardBreakNode))) {
70
+ if (dispatch) {
71
+ dispatch(state.tr.replaceSelectionWith(hardBreakNode, false));
72
+ }
73
+ return true;
74
+ }
75
+ }
76
+ if (state.selection instanceof TextSelection) {
77
+ if (dispatch) {
78
+ dispatch(state.tr.insertText('\n'));
79
+ }
80
+ return true;
81
+ }
82
+ return false;
83
+ };
84
+ }
85
+ export const insertNewLineWithAnalytics = editorAnalyticsAPI => withAnalytics(editorAnalyticsAPI, {
86
+ action: ACTION.INSERTED,
87
+ actionSubject: ACTION_SUBJECT.TEXT,
88
+ actionSubjectId: ACTION_SUBJECT_ID.LINE_BREAK,
89
+ eventType: EVENT_TYPE.TRACK
90
+ })(insertNewLine());
91
+ export const createNewParagraphAbove = (state, dispatch) => {
92
+ const append = false;
93
+ if (!canMoveUp(state) && canCreateParagraphNear(state)) {
94
+ createParagraphNear(append)(state, dispatch);
95
+ return true;
96
+ }
97
+ return false;
98
+ };
99
+ export const createNewParagraphBelow = (state, dispatch) => {
100
+ const append = true;
101
+ if (!canMoveDown(state) && canCreateParagraphNear(state)) {
102
+ createParagraphNear(append)(state, dispatch);
103
+ return true;
104
+ }
105
+ return false;
106
+ };
107
+ function canCreateParagraphNear(state) {
108
+ const {
109
+ selection: {
110
+ $from
111
+ }
112
+ } = state;
113
+ const node = $from.node($from.depth);
114
+ const insideCodeBlock = !!node && node.type === state.schema.nodes.codeBlock;
115
+ const isNodeSelection = state.selection instanceof NodeSelection;
116
+ return $from.depth > 1 || isNodeSelection || insideCodeBlock;
117
+ }
118
+ export function createParagraphNear(append = true) {
119
+ return function (state, dispatch) {
120
+ const paragraph = state.schema.nodes.paragraph;
121
+ if (!paragraph) {
122
+ return false;
123
+ }
124
+ let insertPos;
125
+ if (state.selection instanceof TextSelection) {
126
+ if (topLevelNodeIsEmptyTextBlock(state)) {
127
+ return false;
128
+ }
129
+ insertPos = getInsertPosFromTextBlock(state, append);
130
+ } else {
131
+ insertPos = getInsertPosFromNonTextBlock(state, append);
132
+ }
133
+ const tr = state.tr.insert(insertPos, paragraph.createAndFill());
134
+ tr.setSelection(TextSelection.create(tr.doc, insertPos + 1));
135
+ if (dispatch) {
136
+ dispatch(tr);
137
+ }
138
+ return true;
139
+ };
140
+ }
141
+ function getInsertPosFromTextBlock(state, append) {
142
+ const {
143
+ $from,
144
+ $to
145
+ } = state.selection;
146
+ let pos;
147
+ if (!append) {
148
+ pos = $from.start(0);
149
+ } else {
150
+ pos = $to.end(0);
151
+ }
152
+ return pos;
153
+ }
154
+ function getInsertPosFromNonTextBlock(state, append) {
155
+ const {
156
+ $from,
157
+ $to
158
+ } = state.selection;
159
+ const nodeAtSelection = state.selection instanceof NodeSelection && state.doc.nodeAt(state.selection.$anchor.pos);
160
+ const isMediaSelection = nodeAtSelection && nodeAtSelection.type.name === 'mediaGroup';
161
+ let pos;
162
+ if (!append) {
163
+ // The start position is different with text block because it starts from 0
164
+ pos = $from.start($from.depth);
165
+ // The depth is different with text block because it starts from 0
166
+ pos = $from.depth > 0 && !isMediaSelection ? pos - 1 : pos;
167
+ } else {
168
+ pos = $to.end($to.depth);
169
+ pos = $to.depth > 0 && !isMediaSelection ? pos + 1 : pos;
170
+ }
171
+ return pos;
172
+ }
173
+ function topLevelNodeIsEmptyTextBlock(state) {
174
+ const topLevelNode = state.selection.$from.node(1);
175
+ return topLevelNode.isTextblock && topLevelNode.type !== state.schema.nodes.codeBlock && topLevelNode.nodeSize === 2;
176
+ }
177
+ function canMoveUp(state) {
178
+ const {
179
+ selection
180
+ } = state;
181
+ /**
182
+ * If there's a media element on the selection it will use a gap cursor to move
183
+ */
184
+ if (selection instanceof NodeSelection && isMediaNode(selection.node)) {
185
+ return true;
186
+ }
187
+ if (selection instanceof TextSelection) {
188
+ if (!selection.empty) {
189
+ return true;
190
+ }
191
+ }
192
+ return !atTheBeginningOfDoc(state);
193
+ }
194
+ function canMoveDown(state) {
195
+ const {
196
+ selection
197
+ } = state;
198
+
199
+ /**
200
+ * If there's a media element on the selection it will use a gap cursor to move
201
+ */
202
+ if (selection instanceof NodeSelection && isMediaNode(selection.node)) {
203
+ return true;
204
+ }
205
+ if (selection instanceof TextSelection) {
206
+ if (!selection.empty) {
207
+ return true;
208
+ }
209
+ }
210
+ return !atTheEndOfDoc(state);
211
+ }
212
+ export function atTheEndOfDoc(state) {
213
+ const {
214
+ selection,
215
+ doc
216
+ } = state;
217
+ return doc.nodeSize - selection.$to.pos - 2 === selection.$to.depth;
218
+ }
219
+ export function atTheBeginningOfDoc(state) {
220
+ const {
221
+ selection
222
+ } = state;
223
+ return selection.$from.pos === selection.$from.depth;
224
+ }
54
225
 
55
226
  /**
56
227
  * If the selection is empty, is inside a paragraph node and `canNextNodeMoveUp` is true then delete current paragraph
@@ -85,4 +85,49 @@ export const isValidPosition = (pos, state) => {
85
85
  };
86
86
  export const isInLayoutColumn = state => {
87
87
  return hasParentNodeOfType(state.schema.nodes.layoutSection)(state.selection);
88
- };
88
+ };
89
+ export function filterChildrenBetween(doc, from, to, predicate) {
90
+ const results = [];
91
+ doc.nodesBetween(from, to, (node, pos, parent) => {
92
+ if (predicate(node, pos, parent)) {
93
+ results.push({
94
+ node,
95
+ pos
96
+ });
97
+ }
98
+ });
99
+ return results;
100
+ }
101
+ export const removeBlockMarks = (state, marks) => {
102
+ const {
103
+ selection,
104
+ schema
105
+ } = state;
106
+ let {
107
+ tr
108
+ } = state;
109
+
110
+ // Marks might not exist in Schema
111
+ const marksToRemove = marks.filter(Boolean);
112
+ if (marksToRemove.length === 0) {
113
+ return undefined;
114
+ }
115
+
116
+ /** Saves an extra dispatch */
117
+ let blockMarksExists = false;
118
+ const hasMark = mark => marksToRemove.indexOf(mark.type) > -1;
119
+ /**
120
+ * When you need to toggle the selection
121
+ * when another type which does not allow alignment is applied
122
+ */
123
+ state.doc.nodesBetween(selection.from, selection.to, (node, pos) => {
124
+ if (node.type === schema.nodes.paragraph && node.marks.some(hasMark)) {
125
+ blockMarksExists = true;
126
+ const resolvedPos = state.doc.resolve(pos);
127
+ const withoutBlockMarks = node.marks.filter(not(hasMark));
128
+ tr = tr.setNodeMarkup(resolvedPos.pos, undefined, node.attrs, withoutBlockMarks);
129
+ }
130
+ });
131
+ return blockMarksExists ? tr : undefined;
132
+ };
133
+ const not = fn => arg => !fn(arg);
@@ -3,7 +3,7 @@ export { getExtensionLozengeData } from './macro';
3
3
  export { default as browser } from './browser';
4
4
  export { default as ErrorReporter } from './error-reporter';
5
5
  export { isPastDate, timestampToIsoFormat, timestampToString, timestampToTaskContext, timestampToUTCDate, todayTimestampInUTC } from './date';
6
- export { isElementInTableCell, isTextSelection, isLastItemMediaGroup, setNodeSelection, setTextSelection, nonNullable, stepAddsOneOf, stepHasSlice, extractSliceFromStep, isValidPosition, isEmptyParagraph, isInLayoutColumn } from './editor-core-utils';
6
+ export { isElementInTableCell, isTextSelection, isLastItemMediaGroup, setNodeSelection, setTextSelection, nonNullable, stepAddsOneOf, stepHasSlice, extractSliceFromStep, isValidPosition, isEmptyParagraph, isInLayoutColumn, removeBlockMarks, filterChildrenBetween } from './editor-core-utils';
7
7
  export { withImageLoader } from './imageLoader';
8
8
  export { absoluteBreakoutWidth, calcBreakoutWidth, calcWideWidth, breakoutConsts, calculateBreakoutStyles, calcBreakoutWidthPx, getNextBreakoutMode, getTitle } from './breakout';
9
9
  export { findChangedNodesFromTransaction, validNode, validateNodes, isType, isParagraph, isText, isLinkMark, SelectedState, isNodeSelectedOrInRange, isSupportedInParent, isMediaNode, isNodeBeforeMediaNode } from './nodes';
@@ -42,13 +42,13 @@ export { isFromCurrentDomain, LinkMatcher, normalizeUrl, linkifyContent, getLink
42
42
  export const pmHistoryPluginKey = 'history$';
43
43
  export { gridTypeForLayout } from './grid';
44
44
  export { nodesBetweenChanged, getStepRange, isEmptyDocument, processRawValue, hasDocAsParent, bracketTyped, hasVisibleContent } from './document';
45
- export { floatingLayouts, isRichMediaInsideOfBlockNode, calculateSnapPoints, alignAttributes } from './rich-media-utils';
45
+ export { floatingLayouts, isRichMediaInsideOfBlockNode, calculateSnapPoints, alignAttributes, nonWrappedLayouts } from './rich-media-utils';
46
46
  export { sanitizeNodeForPrivacy } from './filter/privacy-filter';
47
47
  export { canRenderDatasource } from './datasource';
48
- export { filterCommand, walkPrevNode, walkNextNode, isEmptySelectionAtStart, isEmptySelectionAtEnd, insertContentDeleteRange, deleteEmptyParagraphAndMoveBlockUp } from './commands';
48
+ export { filterCommand, isEmptySelectionAtStart, isEmptySelectionAtEnd, insertContentDeleteRange, deleteEmptyParagraphAndMoveBlockUp, insertNewLineWithAnalytics, createNewParagraphAbove, createNewParagraphBelow, createParagraphNear, walkNextNode, walkPrevNode } from './commands';
49
49
  export function shallowEqual(obj1 = {}, obj2 = {}) {
50
50
  const keys1 = Object.keys(obj1);
51
51
  const keys2 = Object.keys(obj2);
52
52
  return keys1.length === keys2.length && keys1.reduce((acc, key) => acc && obj1[key] === obj2[key], true);
53
53
  }
54
- export { inputRuleWithAnalytics } from './input-rules';
54
+ export { inputRuleWithAnalytics, createWrappingJoinRule, createRule } from './input-rules';
@@ -1,3 +1,6 @@
1
+ import { closeHistory } from '@atlaskit/editor-prosemirror/history';
2
+ import { canJoin, findWrapping } from '@atlaskit/editor-prosemirror/transform';
3
+ import { JOIN_SCENARIOS_WHEN_TYPING_TO_INSERT_LIST } from '../analytics';
1
4
  // Roughly based on atlassian-frontend/packages/editor/editor-core/src/utils/input-rules.ts but with the Editor Analytics API that's injected in plugins
2
5
  export const inputRuleWithAnalytics = (getPayload, analyticsApi) => {
3
6
  return originalRule => {
@@ -13,4 +16,46 @@ export const inputRuleWithAnalytics = (getPayload, analyticsApi) => {
13
16
  onHandlerApply
14
17
  };
15
18
  };
19
+ };
20
+ export const createWrappingJoinRule = ({
21
+ match,
22
+ nodeType,
23
+ getAttrs,
24
+ joinPredicate
25
+ }) => {
26
+ const handler = (state, match, start, end) => {
27
+ const attrs = (getAttrs instanceof Function ? getAttrs(match) : getAttrs) || {};
28
+ const tr = state.tr;
29
+ const fixedStart = Math.max(start, 1);
30
+ tr.delete(fixedStart, end);
31
+ const $start = tr.doc.resolve(fixedStart);
32
+ const range = $start.blockRange();
33
+ const wrapping = range && findWrapping(range, nodeType, attrs);
34
+ if (!wrapping || !range) {
35
+ return null;
36
+ }
37
+ const parentNodePosMapped = tr.mapping.map(range.start);
38
+ const parentNode = tr.doc.nodeAt(parentNodePosMapped);
39
+ const lastWrap = wrapping[wrapping.length - 1];
40
+ if (parentNode && lastWrap) {
41
+ const allowedMarks = lastWrap.type.allowedMarks(parentNode.marks) || [];
42
+ tr.setNodeMarkup(parentNodePosMapped, parentNode.type, parentNode.attrs, allowedMarks);
43
+ }
44
+ tr.wrap(range, wrapping);
45
+ const before = tr.doc.resolve(fixedStart - 1).nodeBefore;
46
+ if (before && before.type === nodeType && canJoin(tr.doc, fixedStart - 1) && (!joinPredicate || joinPredicate(match, before, JOIN_SCENARIOS_WHEN_TYPING_TO_INSERT_LIST.JOINED_TO_LIST_ABOVE))) {
47
+ tr.join(fixedStart - 1);
48
+ }
49
+ return tr;
50
+ };
51
+ return createRule(match, handler);
52
+ };
53
+ export const createRule = (match, handler) => {
54
+ return {
55
+ match,
56
+ handler,
57
+ onHandlerApply: (_state, tr) => {
58
+ closeHistory(tr);
59
+ }
60
+ };
16
61
  };
@@ -1,7 +1,9 @@
1
1
  import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
2
2
  import { akEditorBreakoutPadding } from '@atlaskit/editor-shared-styles';
3
- import { shouldAddDefaultWrappedWidth } from '../ui/MediaSingle';
4
3
  import { calcPxFromColumns, wrappedLayouts } from '../ui/MediaSingle/grid';
4
+ export const shouldAddDefaultWrappedWidth = (layout, width, lineLength) => {
5
+ return wrappedLayouts.indexOf(layout) > -1 && lineLength && width && width > 0.5 * lineLength;
6
+ };
5
7
  export const nonWrappedLayouts = ['center', 'wide', 'full-width'];
6
8
  export const floatingLayouts = ['wrap-left', 'wrap-right'];
7
9
  export const isRichMediaInsideOfBlockNode = (view, pos) => {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "74.32.1",
3
+ "version": "74.34.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,4 +1,7 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
4
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
2
5
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
3
6
  import { hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
4
7
  import { DEFAULT_EMBED_CARD_WIDTH } from '@atlaskit/editor-shared-styles';
@@ -9,10 +12,11 @@ import FullWidthIcon from '@atlaskit/icon/glyph/editor/media-full-width';
9
12
  import WideIcon from '@atlaskit/icon/glyph/editor/media-wide';
10
13
  import WrapLeftIcon from '@atlaskit/icon/glyph/editor/media-wrap-left';
11
14
  import WrapRightIcon from '@atlaskit/icon/glyph/editor/media-wrap-right';
15
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
12
16
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '../../analytics';
13
17
  import { insideTable } from '../../core-utils';
14
18
  import commonMessages, { mediaAndEmbedToolbarMessages as toolbarMessages } from '../../messages';
15
- import { alignAttributes, isInLayoutColumn } from '../../utils';
19
+ import { alignAttributes, isInLayoutColumn, nonWrappedLayouts } from '../../utils';
16
20
 
17
21
  // Workaround as we don't want to import this package into `editor-common`
18
22
  // We'll get type errors if this gets out of sync with `editor-plugin-width`.
@@ -75,7 +79,10 @@ var makeAlign = function makeAlign(layout, nodeType, widthPluginDependencyApi, a
75
79
  return false;
76
80
  }
77
81
  var nodeWidth = getNodeWidth(node, state.schema);
78
- var newAttrs = alignAttributes(layout, node.attrs, undefined, nodeWidth, widthPluginState.lineLength);
82
+ var newAttrs = getBooleanFF('platform.editor.media.extended-resize-experience') ? // with extended experience, change alignment does not change media single width
83
+ _objectSpread(_objectSpread({}, node.attrs), {}, {
84
+ layout: layout
85
+ }) : alignAttributes(layout, node.attrs, undefined, nodeWidth, widthPluginState.lineLength);
79
86
  var tr = state.tr.setNodeMarkup(state.selection.from, undefined, newAttrs);
80
87
  tr.setMeta('scrollIntoView', false);
81
88
  // when image captions are enabled, the wrong node gets selected after
@@ -104,18 +111,26 @@ var makeAlign = function makeAlign(layout, nodeType, widthPluginDependencyApi, a
104
111
  return true;
105
112
  };
106
113
  };
107
- var mapIconsToToolbarItem = function mapIconsToToolbarItem(icons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi) {
114
+ var getToolbarLayout = function getToolbarLayout(layout) {
115
+ if (getBooleanFF('platform.editor.media.extended-resize-experience') && nonWrappedLayouts.includes(layout)) {
116
+ return 'center';
117
+ }
118
+ return layout;
119
+ };
120
+ var mapIconsToToolbarItem = function mapIconsToToolbarItem(icons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled) {
108
121
  return icons.map(function (toolbarItem) {
109
122
  var id = toolbarItem.id,
110
123
  value = toolbarItem.value;
111
- return {
124
+ return _objectSpread({
112
125
  id: id,
113
126
  type: 'button',
114
127
  icon: toolbarItem.icon,
115
128
  title: intl.formatMessage(layoutToMessages[value]),
116
- selected: layout === value,
129
+ selected: getToolbarLayout(layout) === value,
117
130
  onClick: makeAlign(value, nodeType, widthPluginDependencyApi, analyticsApi)
118
- };
131
+ }, isChangingLayoutDisabled && {
132
+ disabled: value !== 'center'
133
+ });
119
134
  });
120
135
  };
121
136
  var shouldHideLayoutToolbar = function shouldHideLayoutToolbar(selection, _ref2, allowResizingInTables) {
@@ -125,13 +140,14 @@ var shouldHideLayoutToolbar = function shouldHideLayoutToolbar(selection, _ref2,
125
140
  var buildLayoutButtons = function buildLayoutButtons(state, intl, nodeType, widthPluginDependencyApi, analyticsApi, allowResizing, allowResizingInTables) {
126
141
  var allowWrapping = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : true;
127
142
  var allowAlignment = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true;
143
+ var isChangingLayoutDisabled = arguments.length > 9 ? arguments[9] : undefined;
128
144
  var selection = state.selection;
129
145
  if (!(selection instanceof NodeSelection) || !selection.node || !nodeType || shouldHideLayoutToolbar(selection, state.schema, allowResizingInTables)) {
130
146
  return [];
131
147
  }
132
148
  var layout = selection.node.attrs.layout;
133
- var alignmentToolbarItems = allowAlignment ? mapIconsToToolbarItem(alignmentIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi) : [];
134
- var wrappingToolbarItems = allowWrapping ? mapIconsToToolbarItem(wrappingIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi) : [];
149
+ var alignmentToolbarItems = allowAlignment ? mapIconsToToolbarItem(alignmentIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled) : [];
150
+ var wrappingToolbarItems = allowWrapping ? mapIconsToToolbarItem(wrappingIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi, isChangingLayoutDisabled) : [];
135
151
  var breakOutToolbarItems = !allowResizing ? mapIconsToToolbarItem(breakoutIcons, layout, intl, nodeType, widthPluginDependencyApi, analyticsApi) : [];
136
152
  var items = [].concat(_toConsumableArray(alignmentToolbarItems), _toConsumableArray(getSeparatorBetweenAlignmentAndWrapping(allowAlignment, allowWrapping)), _toConsumableArray(wrappingToolbarItems), _toConsumableArray(getSeparatorBeforeBreakoutItems(allowAlignment, allowWrapping, allowResizing)), _toConsumableArray(breakOutToolbarItems));
137
153
  return items;
@@ -3,4 +3,5 @@ export { createFixedGuidelinesFromLengths, createGuidesFromLengths } from './fix
3
3
  export { generateDefaultGuidelines } from './defaultGuideline';
4
4
  export { getGuidelinesWithHighlights } from './updateGuideline';
5
5
  export { MEDIA_DYNAMIC_GUIDELINE_PREFIX } from './constants';
6
+ export { getSnapWidth, findClosestSnap } from './snapping';
6
7
  export { isVerticalPosition, getContainerWidthOrFullEditorWidth } from './utils';
@@ -77,7 +77,7 @@ var arrowKeysMap = {
77
77
  ARROWUP: "\u2191",
78
78
  ARROWDOWN: "\u2193"
79
79
  };
80
- var tooltipShortcutStyle = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n border-radius: 2px;\n background-color: ", ";\n padding: 0 2px;\n\n /* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */\n /* stylelint-disable-next-line */\n label: tooltip-shortcut;\n"])), "var(--ds-background-inverse-subtle, ".concat(N400, ")"));
80
+ var tooltipShortcutStyle = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n border-radius: 2px;\n background-color: ", ";\n padding: 0 ", ";\n\n /* TODO: fix in develop: https://atlassian.slack.com/archives/CFG3PSQ9E/p1647395052443259?thread_ts=1647394572.556029&cid=CFG3PSQ9E */\n /* stylelint-disable-next-line */\n label: tooltip-shortcut;\n"])), "var(--ds-background-inverse-subtle, ".concat(N400, ")"), "var(--ds-space-025, 2px)");
81
81
  /* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
82
82
 
83
83
  function formatShortcut(keymap) {
@@ -201,4 +201,5 @@ export function findKeyMapForBrowser(keyMap) {
201
201
  }
202
202
  return;
203
203
  }
204
- export { DOWN, HEADING_KEYS, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, LEFT, RIGHT, UP } from './consts';
204
+ export { DOWN, HEADING_KEYS, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, LEFT, RIGHT, UP } from './consts';
205
+ export { keymap } from './keymap';
@@ -0,0 +1,33 @@
1
+ import { base, keyName } from 'w3c-keyname';
2
+ import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
3
+ import { SafePlugin } from '../safe-plugin';
4
+
5
+ /**
6
+ * A workaround for mostly Cyrillic but should have a positive affect
7
+ * on other languages / layouts. Attempts a similar approach to OS X.
8
+ * @see ED-7310
9
+ * @see https://github.com/ProseMirror/prosemirror/issues/957
10
+ * @param bindings
11
+ */
12
+ export function keymap(bindings) {
13
+ return new SafePlugin({
14
+ props: {
15
+ handleKeyDown: function handleKeyDown(view, event) {
16
+ var name = keyName(event);
17
+ var keyboardEvent = event;
18
+ if (event.ctrlKey && name.length === 1 &&
19
+ // Check the unicode of the character to
20
+ // assert that its not an ASCII character.
21
+ // These are characters outside Latin's range.
22
+ /[^\u0000-\u007f]/.test(name)) {
23
+ keyboardEvent = new KeyboardEvent('keydown', {
24
+ key: base[event.keyCode],
25
+ code: event.code,
26
+ ctrlKey: true
27
+ });
28
+ }
29
+ return keydownHandler(bindings)(view, keyboardEvent);
30
+ }
31
+ }
32
+ });
33
+ }
@@ -6,7 +6,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
6
6
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
7
7
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
8
8
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
9
- var packageVersion = "74.32.1";
9
+ var packageVersion = "74.34.0";
10
10
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
11
11
  // Remove URL as it has UGC
12
12
  // TODO: Sanitise the URL instead of just removing it
@@ -1,7 +1,8 @@
1
1
  import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral";
2
2
  var _templateObject;
3
3
  import { css } from '@emotion/react';
4
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
4
5
  var richMediaClassName = 'rich-media-item';
5
6
  var wrappedMediaBreakoutPoint = 410;
6
- var mediaSingleSharedStyle = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n li .", " {\n margin: 0;\n }\n\n /* Hack for chrome to fix media single position\n inside a list when media is the first child */\n &.ua-chrome li > .mediaSingleView-content-wrap::before {\n content: '';\n display: block;\n height: 0;\n }\n\n &.ua-firefox {\n .mediaSingleView-content-wrap {\n user-select: none;\n }\n\n .captionView-content-wrap {\n user-select: text;\n }\n }\n\n .mediaSingleView-content-wrap[layout='center'] {\n clear: both;\n }\n\n table .", " {\n margin-top: 12px;\n margin-bottom: 12px;\n clear: both;\n\n &.image-wrap-left,\n &.image-wrap-right {\n clear: none;\n\n &:first-child {\n margin-top: 12px;\n }\n }\n }\n\n .", ".image-wrap-right\n + .", ".image-wrap-left {\n clear: both;\n }\n\n .", ".image-wrap-left\n + .", ".image-wrap-right,\n .", ".image-wrap-right\n + .", ".image-wrap-left,\n .", ".image-wrap-left\n + .", ".image-wrap-left,\n .", ".image-wrap-right\n + .", ".image-wrap-right {\n margin-right: 0;\n margin-left: 0;\n }\n\n @media all and (max-width: ", "px) {\n div.mediaSingleView-content-wrap[layout='wrap-left'],\n div.mediaSingleView-content-wrap[data-layout='wrap-left'],\n div.mediaSingleView-content-wrap[layout='wrap-right'],\n div.mediaSingleView-content-wrap[data-layout='wrap-right'] {\n float: none;\n overflow: auto;\n margin: 12px 0;\n }\n }\n"])), richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, wrappedMediaBreakoutPoint);
7
+ var mediaSingleSharedStyle = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n li .", " {\n margin: 0;\n }\n\n /* Hack for chrome to fix media single position\n inside a list when media is the first child */\n &.ua-chrome li > .mediaSingleView-content-wrap::before {\n content: '';\n display: block;\n height: 0;\n }\n\n &.ua-firefox {\n .mediaSingleView-content-wrap {\n user-select: none;\n }\n\n .captionView-content-wrap {\n user-select: text;\n }\n }\n\n .mediaSingleView-content-wrap[layout='center'] {\n clear: both;\n }\n\n table .", " {\n margin-top: ", ";\n margin-bottom: ", ";\n clear: both;\n\n &.image-wrap-left,\n &.image-wrap-right {\n clear: none;\n\n &:first-child {\n margin-top: ", ";\n }\n }\n }\n\n .", ".image-wrap-right\n + .", ".image-wrap-left {\n clear: both;\n }\n\n .", ".image-wrap-left\n + .", ".image-wrap-right,\n .", ".image-wrap-right\n + .", ".image-wrap-left,\n .", ".image-wrap-left\n + .", ".image-wrap-left,\n .", ".image-wrap-right\n + .", ".image-wrap-right {\n margin-right: 0;\n margin-left: 0;\n }\n\n ", "\n"])), richMediaClassName, richMediaClassName, "var(--ds-space-150, 12px)", "var(--ds-space-150, 12px)", "var(--ds-space-150, 12px)", richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, richMediaClassName, !getBooleanFF('platform.editor.media.extended-resize-experience') && "@media all and (max-width: ".concat(wrappedMediaBreakoutPoint, "px) {\n div.mediaSingleView-content-wrap[layout='wrap-left'],\n div.mediaSingleView-content-wrap[data-layout='wrap-left'],\n div.mediaSingleView-content-wrap[layout='wrap-right'],\n div.mediaSingleView-content-wrap[data-layout='wrap-right'] {\n float: none;\n overflow: auto;\n margin: 12px 0;\n }\n }"));
7
8
  export { mediaSingleSharedStyle, richMediaClassName };
@@ -18,7 +18,7 @@ import { themed } from '@atlaskit/theme/components';
18
18
  import { borderRadius } from '@atlaskit/theme/constants';
19
19
  import Layer from '../Layer';
20
20
  var packageName = "@atlaskit/editor-common";
21
- var packageVersion = "74.32.1";
21
+ var packageVersion = "74.34.0";
22
22
  var halfFocusRing = 1;
23
23
  var dropOffset = '0, 8';
24
24
  var DropList = /*#__PURE__*/function (_Component) {
@@ -6,7 +6,7 @@ import classnames from 'classnames';
6
6
  import { akEditorMediaResizeHandlerPaddingWide, DEFAULT_EMBED_CARD_WIDTH } from '@atlaskit/editor-shared-styles';
7
7
  import { MEDIA_SINGLE_GUTTER_SIZE } from '../../media-single/constants';
8
8
  import { getMediaSinglePixelWidth } from '../../media-single/utils';
9
- import { wrappedLayouts } from './grid';
9
+ import { shouldAddDefaultWrappedWidth } from '../../utils/rich-media-utils';
10
10
  import { MediaSingleDimensionHelper, MediaWrapper } from './styled';
11
11
  export var DEFAULT_IMAGE_WIDTH = 250;
12
12
  export var DEFAULT_IMAGE_HEIGHT = 200;
@@ -16,9 +16,6 @@ export var DEFAULT_IMAGE_HEIGHT = 200;
16
16
  Read more: https://product-fabric.atlassian.net/browse/MEX-2481
17
17
  */
18
18
  export var IMAGE_AND_BORDER_ADJUSTMENT = 2;
19
- export var shouldAddDefaultWrappedWidth = function shouldAddDefaultWrappedWidth(layout, width, lineLength) {
20
- return wrappedLayouts.indexOf(layout) > -1 && lineLength && width && width > 0.5 * lineLength;
21
- };
22
19
  export default function MediaSingle(_ref) {
23
20
  var layout = _ref.layout,
24
21
  width = _ref.width,
@@ -5,7 +5,8 @@ var _templateObject, _templateObject2, _templateObject3, _templateObject4;
5
5
  /** @jsx jsx */
6
6
  import React from 'react';
7
7
  import { css, jsx } from '@emotion/react';
8
- import { akEditorFullPageMaxWidth, akEditorFullWidthLayoutWidth } from '@atlaskit/editor-shared-styles';
8
+ import { akEditorFullPageMaxWidth, akEditorFullWidthLayoutWidth, akEditorGutterPadding } from '@atlaskit/editor-shared-styles';
9
+ import { nonWrappedLayouts } from '../../utils';
9
10
  import { calcBreakoutWidth, calcWideWidth } from '../../utils/breakout';
10
11
  function float(layout) {
11
12
  switch (layout) {
@@ -77,6 +78,24 @@ function calcMaxWidth(layout, containerWidth) {
77
78
  return '100%';
78
79
  }
79
80
  }
81
+ var getEffectiveFullWidth = function getEffectiveFullWidth(containerWidth, fullWidthMode) {
82
+ if (fullWidthMode) {
83
+ return '100%';
84
+ }
85
+ // There is always padding for renderer, so we don't need padding for it
86
+ var fullWidthPadding = akEditorGutterPadding * 2;
87
+ return "".concat(Math.min(containerWidth - fullWidthPadding, akEditorFullWidthLayoutWidth), "px");
88
+ };
89
+ var calcMaxWidthWhenResizing = function calcMaxWidthWhenResizing(containerWidth, fullWidthMode, isNestedNode) {
90
+ if (isNestedNode) {
91
+ return '100%';
92
+ }
93
+ // non-nested node can resize up to full width
94
+ return getEffectiveFullWidth(containerWidth, fullWidthMode);
95
+ };
96
+ var calcMaxWidthWhenNotResizing = function calcMaxWidthWhenNotResizing(containerWidth, mediaSingleWidth) {
97
+ return "".concat(Math.min(mediaSingleWidth, containerWidth - akEditorGutterPadding * 2), "px");
98
+ };
80
99
  function calcMargin(layout) {
81
100
  switch (layout) {
82
101
  case 'wrap-right':
@@ -109,8 +128,11 @@ export var MediaSingleDimensionHelper = function MediaSingleDimensionHelper(_ref
109
128
  layout = _ref.layout,
110
129
  pctWidth = _ref.pctWidth,
111
130
  mediaSingleWidth = _ref.mediaSingleWidth,
112
- width = _ref.width;
113
- return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n /* For nested rich media items, set max-width to 100% */\n tr &,\n [data-layout-column] &,\n [data-node-type='expand'] & {\n max-width: 100%;\n }\n\n width: ", ";\n ", "\n max-width: ", ";\n float: ", ";\n margin: ", ";\n ", ";\n\n &:not(.is-resizing) {\n transition: width 100ms ease-in;\n }\n"])), mediaSingleWidth || pctWidth ? calcResizedWidth(layout, width || 0, containerWidth) : calcLegacyWidth(layout, width || 0, containerWidth, fullWidthMode, isResized), layout === 'full-width' && css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n min-width: 100%;\n "]))), calcMaxWidth(layout, containerWidth), float(layout), calcMargin(layout), isImageAligned(layout));
131
+ width = _ref.width,
132
+ isExtendedResizeExperienceOn = _ref.isExtendedResizeExperienceOn,
133
+ _ref$isNestedNode = _ref.isNestedNode,
134
+ isNestedNode = _ref$isNestedNode === void 0 ? false : _ref$isNestedNode;
135
+ return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n /* For nested rich media items, set max-width to 100% */\n tr &,\n [data-layout-column] &,\n [data-node-type='expand'] & {\n max-width: 100%;\n }\n\n width: ", ";\n ", "\n max-width: ", ";\n &[class*='is-resizing'] {\n ", "\n }\n\n /* Handles responsiveness of non-nested, not-resizing nodes in editor */\n &[class*='not-resizing'] {\n ", "\n }\n\n float: ", ";\n margin: ", ";\n ", ";\n\n &:not(.is-resizing) {\n transition: width 100ms ease-in;\n }\n"])), mediaSingleWidth || pctWidth ? calcResizedWidth(layout, width || 0, containerWidth) : calcLegacyWidth(layout, width || 0, containerWidth, fullWidthMode, isResized), layout === 'full-width' && css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n min-width: 100%;\n "]))), calcMaxWidth(layout, containerWidth), isExtendedResizeExperienceOn && "max-width: ".concat(calcMaxWidthWhenResizing(containerWidth, fullWidthMode, isNestedNode), ";\n\n ").concat(nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", "\n\n .new-file-experience-wrapper {\n box-shadow: none;\n }"), !isNestedNode && "max-width: ".concat(layout !== 'full-width' && mediaSingleWidth && calcMaxWidthWhenNotResizing(containerWidth, mediaSingleWidth), ";\n\n ").concat(nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", "\n\n // override min-width to counteract max-width set in old experience\n ").concat(layout === 'full-width' && "min-width: ".concat(getEffectiveFullWidth(containerWidth, fullWidthMode), " !important;"), ";"), float(layout), calcMargin(layout), isImageAligned(layout));
114
136
  };
115
137
  var RenderFallbackContainer = function RenderFallbackContainer(_ref2) {
116
138
  var hasFallbackContainer = _ref2.hasFallbackContainer,
@@ -1,5 +1,5 @@
1
1
  export { default as Caption } from './Caption';
2
- export { default as MediaSingle, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, IMAGE_AND_BORDER_ADJUSTMENT, shouldAddDefaultWrappedWidth } from './MediaSingle';
2
+ export { default as MediaSingle, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, IMAGE_AND_BORDER_ADJUSTMENT } from './MediaSingle';
3
3
  export { MediaSingleDimensionHelper } from './MediaSingle/styled';
4
4
  export { layoutSupportsWidth, calcPxFromColumns, calcPctFromPx, calcPxFromPct, calcColumnsFromPx, snapToGrid, calcMediaPxWidth, wrappedLayouts } from './MediaSingle/grid';
5
5
  export { mediaLinkStyle } from './MediaSingle/link';