@atlaskit/editor-core 190.1.24 → 190.1.26

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 (103) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/afm-cc/tsconfig.json +0 -12
  3. package/dist/cjs/plugins/index.js +0 -7
  4. package/dist/cjs/presets/default.js +2 -3
  5. package/dist/cjs/ui/ConfigPanel/Fields/ColorPicker.js +13 -0
  6. package/dist/cjs/version-wrapper.js +1 -1
  7. package/dist/es2019/plugins/index.js +0 -1
  8. package/dist/es2019/presets/default.js +1 -1
  9. package/dist/es2019/ui/ConfigPanel/Fields/ColorPicker.js +13 -0
  10. package/dist/es2019/version-wrapper.js +1 -1
  11. package/dist/esm/plugins/index.js +0 -1
  12. package/dist/esm/presets/default.js +1 -1
  13. package/dist/esm/ui/ConfigPanel/Fields/ColorPicker.js +13 -0
  14. package/dist/esm/version-wrapper.js +1 -1
  15. package/dist/types/plugins/index.d.ts +0 -1
  16. package/dist/types/presets/default.d.ts +1 -1
  17. package/dist/types/ui/Appearance/FullPage/StyledComponents.d.ts +2 -2
  18. package/dist/types/ui/ContentStyles/index.d.ts +2 -2
  19. package/dist/types-ts4.5/plugins/index.d.ts +0 -1
  20. package/dist/types-ts4.5/presets/default.d.ts +1 -1
  21. package/dist/types-ts4.5/ui/Appearance/FullPage/StyledComponents.d.ts +2 -2
  22. package/dist/types-ts4.5/ui/ContentStyles/index.d.ts +2 -2
  23. package/package.json +9 -11
  24. package/dist/cjs/plugins/paste/actions.js +0 -12
  25. package/dist/cjs/plugins/paste/commands.js +0 -255
  26. package/dist/cjs/plugins/paste/edge-cases/index.js +0 -88
  27. package/dist/cjs/plugins/paste/edge-cases/lists.js +0 -107
  28. package/dist/cjs/plugins/paste/handlers.js +0 -935
  29. package/dist/cjs/plugins/paste/index.js +0 -14
  30. package/dist/cjs/plugins/paste/plugin.js +0 -43
  31. package/dist/cjs/plugins/paste/plugins/media.js +0 -207
  32. package/dist/cjs/plugins/paste/pm-plugins/analytics.js +0 -376
  33. package/dist/cjs/plugins/paste/pm-plugins/clipboard-text-serializer.js +0 -43
  34. package/dist/cjs/plugins/paste/pm-plugins/main.js +0 -482
  35. package/dist/cjs/plugins/paste/pm-plugins/plugin-factory.js +0 -42
  36. package/dist/cjs/plugins/paste/reducer.js +0 -41
  37. package/dist/cjs/plugins/paste/types.js +0 -5
  38. package/dist/cjs/plugins/paste/util/index.js +0 -214
  39. package/dist/cjs/plugins/paste/util/tinyMCE.js +0 -183
  40. package/dist/es2019/plugins/paste/actions.js +0 -6
  41. package/dist/es2019/plugins/paste/commands.js +0 -235
  42. package/dist/es2019/plugins/paste/edge-cases/index.js +0 -87
  43. package/dist/es2019/plugins/paste/edge-cases/lists.js +0 -113
  44. package/dist/es2019/plugins/paste/handlers.js +0 -915
  45. package/dist/es2019/plugins/paste/index.js +0 -3
  46. package/dist/es2019/plugins/paste/plugin.js +0 -38
  47. package/dist/es2019/plugins/paste/plugins/media.js +0 -204
  48. package/dist/es2019/plugins/paste/pm-plugins/analytics.js +0 -332
  49. package/dist/es2019/plugins/paste/pm-plugins/clipboard-text-serializer.js +0 -37
  50. package/dist/es2019/plugins/paste/pm-plugins/main.js +0 -451
  51. package/dist/es2019/plugins/paste/pm-plugins/plugin-factory.js +0 -30
  52. package/dist/es2019/plugins/paste/reducer.js +0 -32
  53. package/dist/es2019/plugins/paste/types.js +0 -1
  54. package/dist/es2019/plugins/paste/util/index.js +0 -209
  55. package/dist/es2019/plugins/paste/util/tinyMCE.js +0 -168
  56. package/dist/esm/plugins/paste/actions.js +0 -6
  57. package/dist/esm/plugins/paste/commands.js +0 -248
  58. package/dist/esm/plugins/paste/edge-cases/index.js +0 -81
  59. package/dist/esm/plugins/paste/edge-cases/lists.js +0 -98
  60. package/dist/esm/plugins/paste/handlers.js +0 -914
  61. package/dist/esm/plugins/paste/index.js +0 -3
  62. package/dist/esm/plugins/paste/plugin.js +0 -37
  63. package/dist/esm/plugins/paste/plugins/media.js +0 -199
  64. package/dist/esm/plugins/paste/pm-plugins/analytics.js +0 -364
  65. package/dist/esm/plugins/paste/pm-plugins/clipboard-text-serializer.js +0 -37
  66. package/dist/esm/plugins/paste/pm-plugins/main.js +0 -469
  67. package/dist/esm/plugins/paste/pm-plugins/plugin-factory.js +0 -36
  68. package/dist/esm/plugins/paste/reducer.js +0 -34
  69. package/dist/esm/plugins/paste/types.js +0 -1
  70. package/dist/esm/plugins/paste/util/index.js +0 -194
  71. package/dist/esm/plugins/paste/util/tinyMCE.js +0 -176
  72. package/dist/types/plugins/paste/actions.d.ts +0 -21
  73. package/dist/types/plugins/paste/commands.d.ts +0 -30
  74. package/dist/types/plugins/paste/edge-cases/index.d.ts +0 -11
  75. package/dist/types/plugins/paste/edge-cases/lists.d.ts +0 -18
  76. package/dist/types/plugins/paste/handlers.d.ts +0 -55
  77. package/dist/types/plugins/paste/index.d.ts +0 -4
  78. package/dist/types/plugins/paste/plugin.d.ts +0 -2
  79. package/dist/types/plugins/paste/plugins/media.d.ts +0 -23
  80. package/dist/types/plugins/paste/pm-plugins/analytics.d.ts +0 -44
  81. package/dist/types/plugins/paste/pm-plugins/clipboard-text-serializer.d.ts +0 -13
  82. package/dist/types/plugins/paste/pm-plugins/main.d.ts +0 -12
  83. package/dist/types/plugins/paste/pm-plugins/plugin-factory.d.ts +0 -3
  84. package/dist/types/plugins/paste/reducer.d.ts +0 -3
  85. package/dist/types/plugins/paste/types.d.ts +0 -1
  86. package/dist/types/plugins/paste/util/index.d.ts +0 -21
  87. package/dist/types/plugins/paste/util/tinyMCE.d.ts +0 -32
  88. package/dist/types-ts4.5/plugins/paste/actions.d.ts +0 -21
  89. package/dist/types-ts4.5/plugins/paste/commands.d.ts +0 -30
  90. package/dist/types-ts4.5/plugins/paste/edge-cases/index.d.ts +0 -11
  91. package/dist/types-ts4.5/plugins/paste/edge-cases/lists.d.ts +0 -18
  92. package/dist/types-ts4.5/plugins/paste/handlers.d.ts +0 -55
  93. package/dist/types-ts4.5/plugins/paste/index.d.ts +0 -4
  94. package/dist/types-ts4.5/plugins/paste/plugin.d.ts +0 -2
  95. package/dist/types-ts4.5/plugins/paste/plugins/media.d.ts +0 -23
  96. package/dist/types-ts4.5/plugins/paste/pm-plugins/analytics.d.ts +0 -44
  97. package/dist/types-ts4.5/plugins/paste/pm-plugins/clipboard-text-serializer.d.ts +0 -13
  98. package/dist/types-ts4.5/plugins/paste/pm-plugins/main.d.ts +0 -12
  99. package/dist/types-ts4.5/plugins/paste/pm-plugins/plugin-factory.d.ts +0 -3
  100. package/dist/types-ts4.5/plugins/paste/reducer.d.ts +0 -3
  101. package/dist/types-ts4.5/plugins/paste/types.d.ts +0 -1
  102. package/dist/types-ts4.5/plugins/paste/util/index.d.ts +0 -21
  103. package/dist/types-ts4.5/plugins/paste/util/tinyMCE.d.ts +0 -32
@@ -1,915 +0,0 @@
1
- import { closeHistory } from '@atlaskit/editor-prosemirror/history';
2
- import { Fragment, Node as PMNode, Slice } from '@atlaskit/editor-prosemirror/model';
3
- import { TextSelection, NodeSelection } from '@atlaskit/editor-prosemirror/state';
4
- import { canInsert, findParentNodeOfType, findParentNodeOfTypeClosestToPos, hasParentNodeOfType, safeInsert } from '@atlaskit/editor-prosemirror/utils';
5
- import uuid from 'uuid/v4';
6
- import { replaceSelectedTable } from '@atlaskit/editor-tables/utils';
7
- import { canLinkBeCreatedInRange, insideTableCell, isInListItem, isLinkMark, isListItemNode, isListNode, isParagraph, isText, linkifyContent, mapSlice } from '@atlaskit/editor-common/utils';
8
- import { insideTable } from '@atlaskit/editor-common/core-utils';
9
- import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
10
- import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
11
- // TODO: ED-20519 Needs Macro extraction
12
-
13
- import { addReplaceSelectedTableAnalytics, applyTextMarksToSlice, hasOnlyNodesOfType } from './util';
14
- import { insertSliceForLists, insertSliceForListsInsideBlockquote } from './edge-cases';
15
- import { startTrackingPastedMacroPositions, stopTrackingPastedMacroPositions } from './commands';
16
- import { getPluginState as getPastePluginState } from './pm-plugins/plugin-factory';
17
- import { anyMarkActive } from '@atlaskit/editor-common/mark';
18
-
19
- /** Helper type for single arg function */
20
-
21
- /**
22
- * Compose 1 to n functions.
23
- * @param func first function
24
- * @param funcs additional functions
25
- */
26
- function compose(func, ...funcs) {
27
- const allFuncs = [func, ...funcs];
28
- return function composed(raw) {
29
- return allFuncs.reduceRight((memo, func) => func(memo), raw);
30
- };
31
- }
32
-
33
- // remove text attribute from mention for copy/paste (GDPR)
34
- export function handleMention(slice, schema) {
35
- return mapSlice(slice, node => {
36
- if (node.type.name === schema.nodes.mention.name) {
37
- const mention = node.attrs;
38
- const newMention = {
39
- ...mention,
40
- text: ''
41
- };
42
- return schema.nodes.mention.create(newMention, node.content, node.marks);
43
- }
44
- return node;
45
- });
46
- }
47
- export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
48
- return (state, dispatch) => {
49
- var _slice$content$firstC, _transformedSlice$con;
50
- const {
51
- schema,
52
- tr: {
53
- selection
54
- }
55
- } = state;
56
- const {
57
- marks: {
58
- code: codeMark
59
- },
60
- nodes: {
61
- decisionItem,
62
- emoji,
63
- hardBreak,
64
- mention,
65
- paragraph,
66
- taskItem,
67
- text,
68
- panel,
69
- bulletList,
70
- orderedList,
71
- taskList,
72
- listItem,
73
- expand,
74
- heading
75
- }
76
- } = schema;
77
- const selectionIsValidNode = state.selection instanceof NodeSelection && ['decisionList', 'decisionItem', 'taskList', 'taskItem'].includes(state.selection.node.type.name);
78
- const selectionHasValidParentNode = hasParentNodeOfType([decisionItem, taskItem, panel])(state.selection);
79
- const selectionIsPanel = hasParentNodeOfType([panel])(state.selection);
80
-
81
- // Some types of content should be handled by the default handler, not this function.
82
- // Check through slice content to see if it contains an invalid node.
83
- let sliceIsInvalid = false;
84
- slice.content.nodesBetween(0, slice.content.size, node => {
85
- if (node.type === bulletList || node.type === orderedList || node.type === expand || node.type === heading || node.type === listItem) {
86
- sliceIsInvalid = true;
87
- }
88
- if (selectionIsPanel && node.type === taskList) {
89
- sliceIsInvalid = true;
90
- }
91
- });
92
-
93
- // If the selection is a panel,
94
- // and the slice's first node is a paragraph
95
- // and it is not from a depth that would indicate it being from inside from another node (e.g. text from a decision)
96
- // then we can rely on the default behaviour.
97
- const sliceIsAPanelReceivingLowDepthText = selectionIsPanel && ((_slice$content$firstC = slice.content.firstChild) === null || _slice$content$firstC === void 0 ? void 0 : _slice$content$firstC.type) === paragraph && slice.openEnd < 2;
98
- if (sliceIsInvalid || sliceIsAPanelReceivingLowDepthText || !selectionIsValidNode && !selectionHasValidParentNode) {
99
- return false;
100
- }
101
- const filters = [linkifyContent(schema)];
102
- const selectionMarks = selection.$head.marks();
103
- if (selection instanceof TextSelection && Array.isArray(selectionMarks) && selectionMarks.length > 0 && hasOnlyNodesOfType(paragraph, text, emoji, mention, hardBreak)(slice) && (!codeMark.isInSet(selectionMarks) || anyMarkActive(state, codeMark)) // check if there is a code mark anywhere in the selection
104
- ) {
105
- filters.push(applyTextMarksToSlice(schema, selection.$head.marks()));
106
- }
107
- const transformedSlice = compose.apply(null, filters)(slice);
108
- const tr = closeHistory(state.tr);
109
- const transformedSliceIsValidNode = transformedSlice.content.firstChild.type.inlineContent || ['decisionList', 'decisionItem', 'taskList', 'taskItem', 'panel'].includes(transformedSlice.content.firstChild.type.name) && !isInListItem(state);
110
- // If the slice or the selection are valid nodes to handle,
111
- // and the slice is not a whole node (i.e. openStart is 1 and openEnd is 0)
112
- // or the slice's first node is a paragraph,
113
- // then we can replace the selection with our slice.
114
- if ((transformedSliceIsValidNode || selectionIsValidNode) && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
115
- // Whole codeblock node has reverse slice depths.
116
- transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === paragraph) {
117
- tr.replaceSelection(transformedSlice).scrollIntoView();
118
- } else {
119
- // This maintains both the selection (destination) and the slice (paste content).
120
- safeInsert(transformedSlice.content)(tr).scrollIntoView();
121
- }
122
- queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 ? void 0 : queueCardsFromChangedTr(state, tr, INPUT_METHOD.CLIPBOARD);
123
- if (dispatch) {
124
- dispatch(tr);
125
- }
126
- return true;
127
- };
128
- }
129
- export function handlePasteNonNestableBlockNodesIntoList(slice) {
130
- return (state, dispatch) => {
131
- var _tr$doc$nodeAt, _sliceContent$firstCh, _findParentNodeOfType;
132
- const {
133
- tr
134
- } = state;
135
- const {
136
- selection
137
- } = tr;
138
- const {
139
- $from,
140
- $to,
141
- from,
142
- to
143
- } = selection;
144
- const {
145
- orderedList,
146
- bulletList,
147
- listItem
148
- } = state.schema.nodes;
149
-
150
- // Selected nodes
151
- const selectionParentListItemNode = findParentNodeOfType(listItem)(selection);
152
- const selectionParentListNodeWithPos = findParentNodeOfType([bulletList, orderedList])(selection);
153
- const selectionParentListNode = selectionParentListNodeWithPos === null || selectionParentListNodeWithPos === void 0 ? void 0 : selectionParentListNodeWithPos.node;
154
-
155
- // Slice info
156
- const sliceContent = slice.content;
157
- const sliceIsListItems = isListNode(sliceContent.firstChild) && isListNode(sliceContent.lastChild);
158
-
159
- // Find case of slices that can be inserted into a list item
160
- // (eg. paragraphs, list items, code blocks, media single)
161
- // These scenarios already get handled elsewhere and don't need to split the list
162
- let sliceContainsBlockNodesOtherThanThoseAllowedInListItem = false;
163
- slice.content.forEach(child => {
164
- var _listItem$spec$conten;
165
- if (!listItem || child.isBlock && !((_listItem$spec$conten = listItem.spec.content) !== null && _listItem$spec$conten !== void 0 && _listItem$spec$conten.includes(child.type.name))) {
166
- sliceContainsBlockNodesOtherThanThoseAllowedInListItem = true;
167
- }
168
- });
169
- if (!selectionParentListItemNode || !sliceContent || canInsert($from, sliceContent) ||
170
- // eg. inline nodes that can be inserted in a list item
171
- !sliceContainsBlockNodesOtherThanThoseAllowedInListItem || sliceIsListItems || !selectionParentListNodeWithPos) {
172
- return false;
173
- }
174
-
175
- // Offsets
176
- const listWrappingOffset = $to.depth - selectionParentListNodeWithPos.depth + 1; // difference in depth between to position and list node
177
- const listItemWrappingOffset = $to.depth - selectionParentListNodeWithPos.depth; // difference in depth between to position and list item node
178
-
179
- // Anything to do with nested lists should safeInsert and not be handled here
180
- const grandParentListNode = findParentNodeOfTypeClosestToPos(tr.doc.resolve(selectionParentListNodeWithPos.pos), [bulletList, orderedList]);
181
- const selectionIsInNestedList = !!grandParentListNode;
182
- let selectedListItemHasNestedList = false;
183
- selectionParentListItemNode.node.content.forEach(child => {
184
- if (isListNode(child)) {
185
- selectedListItemHasNestedList = true;
186
- }
187
- });
188
- if (selectedListItemHasNestedList || selectionIsInNestedList) {
189
- return false;
190
- }
191
-
192
- // Node after the insert position
193
- const nodeAfterInsertPositionIsListItem = ((_tr$doc$nodeAt = tr.doc.nodeAt(to + listItemWrappingOffset)) === null || _tr$doc$nodeAt === void 0 ? void 0 : _tr$doc$nodeAt.type.name) === 'listItem';
194
-
195
- // Get the next list items position (used later to find the split out ordered list)
196
- const indexOfNextListItem = $to.indexAfter($to.depth - listItemWrappingOffset);
197
- const positionOfNextListItem = tr.doc.resolve(selectionParentListNodeWithPos.pos + 1).posAtIndex(indexOfNextListItem);
198
-
199
- // These nodes paste as plain text by default so need to be handled differently
200
- const sliceContainsNodeThatPastesAsPlainText = sliceContent.firstChild && ['taskItem', 'taskList', 'heading', 'blockquote'].includes(sliceContent.firstChild.type.name);
201
-
202
- // Work out position to replace up to
203
- let replaceTo;
204
- if (sliceContainsNodeThatPastesAsPlainText && nodeAfterInsertPositionIsListItem) {
205
- replaceTo = to + listItemWrappingOffset;
206
- } else if (sliceContainsNodeThatPastesAsPlainText || !nodeAfterInsertPositionIsListItem) {
207
- replaceTo = to;
208
- } else {
209
- replaceTo = to + listWrappingOffset;
210
- }
211
-
212
- // handle the insertion of the slice
213
- if (sliceContainsNodeThatPastesAsPlainText || nodeAfterInsertPositionIsListItem || sliceContent.childCount > 1 && ((_sliceContent$firstCh = sliceContent.firstChild) === null || _sliceContent$firstCh === void 0 ? void 0 : _sliceContent$firstCh.type.name) !== 'paragraph') {
214
- tr.replaceWith(from, replaceTo, sliceContent).scrollIntoView();
215
- } else {
216
- // When the selection is not at the end of a list item
217
- // eg. middle of list item, start of list item
218
- tr.replaceSelection(slice).scrollIntoView();
219
- }
220
-
221
- // Find the ordered list node after the pasted content so we can set it's order
222
- const mappedPositionOfNextListItem = tr.mapping.map(positionOfNextListItem);
223
- if (mappedPositionOfNextListItem > tr.doc.nodeSize) {
224
- return false;
225
- }
226
- const nodeAfterPastedContentResolvedPos = findParentNodeOfTypeClosestToPos(tr.doc.resolve(mappedPositionOfNextListItem), [orderedList]);
227
-
228
- // Work out the new split out lists 'order' (the number it starts from)
229
- const originalParentOrderedListNodeOrder = selectionParentListNode === null || selectionParentListNode === void 0 ? void 0 : selectionParentListNode.attrs.order;
230
- const numOfListItemsInOriginalList = (_findParentNodeOfType = findParentNodeOfTypeClosestToPos(tr.doc.resolve(from - 1), [orderedList])) === null || _findParentNodeOfType === void 0 ? void 0 : _findParentNodeOfType.node.childCount;
231
-
232
- // Set the new split out lists order attribute
233
- if (typeof originalParentOrderedListNodeOrder === 'number' && numOfListItemsInOriginalList && nodeAfterPastedContentResolvedPos) {
234
- tr.setNodeMarkup(nodeAfterPastedContentResolvedPos.pos, orderedList, {
235
- ...nodeAfterPastedContentResolvedPos.node.attrs,
236
- order: originalParentOrderedListNodeOrder + numOfListItemsInOriginalList
237
- });
238
- }
239
-
240
- // dispatch transaction
241
- if (tr.docChanged) {
242
- if (dispatch) {
243
- dispatch(tr);
244
- }
245
- return true;
246
- }
247
- return false;
248
- };
249
- }
250
- export const doesSelectionWhichStartsOrEndsInListContainEntireList = (selection, findRootParentListNode) => {
251
- const {
252
- $from,
253
- $to,
254
- from,
255
- to
256
- } = selection;
257
- const selectionParentListItemNodeResolvedPos = findRootParentListNode ? findRootParentListNode($from) || findRootParentListNode($to) : null;
258
- const selectionParentListNode = selectionParentListItemNodeResolvedPos === null || selectionParentListItemNodeResolvedPos === void 0 ? void 0 : selectionParentListItemNodeResolvedPos.parent;
259
- if (!selectionParentListItemNodeResolvedPos || !selectionParentListNode) {
260
- return false;
261
- }
262
- const startOfEntireList = $from.pos < $to.pos ? selectionParentListItemNodeResolvedPos.pos + $from.depth - 1 : selectionParentListItemNodeResolvedPos.pos + $to.depth - 1;
263
- const endOfEntireList = $from.pos < $to.pos ? selectionParentListItemNodeResolvedPos.pos + selectionParentListNode.nodeSize - $to.depth - 1 : selectionParentListItemNodeResolvedPos.pos + selectionParentListNode.nodeSize - $from.depth - 1;
264
- if (!startOfEntireList || !endOfEntireList) {
265
- return false;
266
- }
267
- if (from < to) {
268
- return startOfEntireList >= $from.pos && endOfEntireList <= $to.pos;
269
- } else if (from > to) {
270
- return startOfEntireList >= $to.pos && endOfEntireList <= $from.pos;
271
- } else {
272
- return false;
273
- }
274
- };
275
- export function handlePastePanelOrDecisionContentIntoList(slice, findRootParentListNode) {
276
- return (state, dispatch) => {
277
- const {
278
- schema,
279
- tr
280
- } = state;
281
- const {
282
- selection
283
- } = tr;
284
- // Check this pasting action is related to copy content from panel node into a selected the list node
285
- const blockNode = slice.content.firstChild;
286
- const isSliceWholeNode = slice.openStart === 0 && slice.openEnd === 0;
287
- const selectionParentListItemNode = selection.$to.node(selection.$to.depth - 1);
288
- const sliceIsWholeNodeButShouldNotReplaceSelection = isSliceWholeNode && !doesSelectionWhichStartsOrEndsInListContainEntireList(selection, findRootParentListNode);
289
- if (!selectionParentListItemNode || (selectionParentListItemNode === null || selectionParentListItemNode === void 0 ? void 0 : selectionParentListItemNode.type) !== schema.nodes.listItem || !blockNode || !['panel', 'decisionList'].includes(blockNode === null || blockNode === void 0 ? void 0 : blockNode.type.name) || slice.content.childCount > 1 || (blockNode === null || blockNode === void 0 ? void 0 : blockNode.content.firstChild) === undefined || sliceIsWholeNodeButShouldNotReplaceSelection) {
290
- return false;
291
- }
292
-
293
- // Paste the panel node contents extracted instead of pasting the entire panel node
294
- tr.replaceSelection(slice).scrollIntoView();
295
- if (dispatch) {
296
- dispatch(tr);
297
- }
298
- return true;
299
- };
300
- }
301
-
302
- // If we paste a link onto some selected text, apply the link as a mark
303
- export function handlePasteLinkOnSelectedText(slice) {
304
- return (state, dispatch) => {
305
- const {
306
- schema,
307
- selection,
308
- selection: {
309
- from,
310
- to
311
- },
312
- tr
313
- } = state;
314
- let linkMark;
315
-
316
- // check if we have a link on the clipboard
317
- if (slice.content.childCount === 1 && isParagraph(slice.content.child(0), schema)) {
318
- const paragraph = slice.content.child(0);
319
- if (paragraph.content.childCount === 1 && isText(paragraph.content.child(0), schema)) {
320
- const text = paragraph.content.child(0);
321
-
322
- // If pasteType is plain text, then
323
- // @atlaskit/editor-markdown-transformer in getMarkdownSlice decode
324
- // url before setting text property of text node.
325
- // However href of marks will be without decoding.
326
- // So, if there is character (e.g space) in url eligible escaping then
327
- // mark.attrs.href will not be equal to text.text.
328
- // That's why decoding mark.attrs.href before comparing.
329
- // However, if pasteType is richText, that means url in text.text
330
- // and href in marks, both won't be decoded.
331
- linkMark = text.marks.find(mark => isLinkMark(mark, schema) && (mark.attrs.href === text.text || decodeURI(mark.attrs.href) === text.text));
332
- }
333
- }
334
-
335
- // if we have a link, apply it to the selected text if we have any and it's allowed
336
- if (linkMark && selection instanceof TextSelection && !selection.empty && canLinkBeCreatedInRange(from, to)(state)) {
337
- tr.addMark(from, to, linkMark);
338
- if (dispatch) {
339
- dispatch(tr);
340
- }
341
- return true;
342
- }
343
- return false;
344
- };
345
- }
346
- export function handlePasteAsPlainText(slice, _event, editorAnalyticsAPI) {
347
- return (state, dispatch, view) => {
348
- var _input;
349
- if (!view) {
350
- return false;
351
- }
352
-
353
- // prosemirror-bump-fix
354
- // Yes, this is wrong by default. But, we need to keep the private PAI usage to unblock the prosemirror bump
355
- // So, this code will make sure we are checking for both version (current and the newest prosemirror-view version
356
- const isShiftKeyPressed = view.shiftKey || ((_input = view.input) === null || _input === void 0 ? void 0 : _input.shiftKey);
357
- // In case of SHIFT+CMD+V ("Paste and Match Style") we don't want to run the usual
358
- // fuzzy matching of content. ProseMirror already handles this scenario and will
359
- // provide us with slice containing paragraphs with plain text, which we decorate
360
- // with "stored marks".
361
- // @see prosemirror-view/src/clipboard.js:parseFromClipboard()).
362
- // @see prosemirror-view/src/input.js:doPaste().
363
- if (isShiftKeyPressed) {
364
- let tr = closeHistory(state.tr);
365
- const {
366
- selection
367
- } = tr;
368
-
369
- // <- using the same internal flag that prosemirror-view is using
370
-
371
- // if user has selected table we need custom logic to replace the table
372
- tr = replaceSelectedTable(state, slice);
373
-
374
- // add analytics after replacing selected table
375
- tr = addReplaceSelectedTableAnalytics(state, tr, editorAnalyticsAPI);
376
-
377
- // otherwise just replace the selection
378
- if (!tr.docChanged) {
379
- tr.replaceSelection(slice);
380
- }
381
- (state.storedMarks || []).forEach(mark => {
382
- tr.addMark(selection.from, selection.from + slice.size, mark);
383
- });
384
- tr.scrollIntoView();
385
- if (dispatch) {
386
- dispatch(tr);
387
- }
388
- return true;
389
- }
390
- return false;
391
- };
392
- }
393
- export function handlePastePreservingMarks(slice, queueCardsFromChangedTr) {
394
- return (state, dispatch) => {
395
- const {
396
- schema,
397
- tr: {
398
- selection
399
- }
400
- } = state;
401
- const {
402
- marks: {
403
- code: codeMark,
404
- annotation: annotationMark
405
- },
406
- nodes: {
407
- bulletList,
408
- emoji,
409
- hardBreak,
410
- heading,
411
- listItem,
412
- mention,
413
- orderedList,
414
- text
415
- }
416
- } = schema;
417
- if (!(selection instanceof TextSelection)) {
418
- return false;
419
- }
420
- const selectionMarks = selection.$head.marks();
421
- if (selectionMarks.length === 0) {
422
- return false;
423
- }
424
-
425
- // special case for codeMark: will preserve mark only if codeMark is currently active
426
- // won't preserve mark if cursor is on the edge on the mark (namely inactive)
427
- const hasActiveCodeMark = codeMark && codeMark.isInSet(selectionMarks) && anyMarkActive(state, codeMark);
428
- const hasAnnotationMark = annotationMark && annotationMark.isInSet(selectionMarks);
429
- const selectionIsHeading = hasParentNodeOfType([heading])(state.selection);
430
-
431
- // if the pasted data is one of the node types below
432
- // we apply current selection marks to the pasted slice
433
- if (hasOnlyNodesOfType(bulletList, hardBreak, heading, listItem, text, emoji, mention, orderedList)(slice) || selectionIsHeading || hasActiveCodeMark || hasAnnotationMark) {
434
- const transformedSlice = applyTextMarksToSlice(schema, selectionMarks)(slice);
435
- const tr = closeHistory(state.tr).replaceSelection(transformedSlice).setStoredMarks(selectionMarks).scrollIntoView();
436
- queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 ? void 0 : queueCardsFromChangedTr(state, tr, INPUT_METHOD.CLIPBOARD);
437
- if (dispatch) {
438
- dispatch(tr);
439
- }
440
- return true;
441
- }
442
- return false;
443
- };
444
- }
445
- async function getSmartLinkAdf(text, type, cardOptions) {
446
- if (!cardOptions.provider) {
447
- throw Error('No card provider found');
448
- }
449
- const provider = await cardOptions.provider;
450
- return await provider.resolve(text, type);
451
- }
452
- function insertAutoMacro(slice, macro, view, from, to) {
453
- if (view) {
454
- // insert the text or linkified/md-converted clipboard data
455
- const selection = view.state.tr.selection;
456
- let tr;
457
- let before;
458
- if (typeof from === 'number' && typeof to === 'number') {
459
- tr = view.state.tr.replaceRange(from, to, slice);
460
- before = tr.mapping.map(from, -1);
461
- } else {
462
- tr = view.state.tr.replaceSelection(slice);
463
- before = tr.mapping.map(selection.from, -1);
464
- }
465
- view.dispatch(tr);
466
-
467
- // replace the text with the macro as a separate transaction
468
- // so the autoconversion generates 2 undo steps
469
- view.dispatch(closeHistory(view.state.tr).replaceRangeWith(before, before + slice.size, macro).scrollIntoView());
470
- return true;
471
- }
472
- return false;
473
- }
474
- export function handleMacroAutoConvert(text, slice, queueCardsFromChangedTr, runMacroAutoConvert, cardsOptions, extensionAutoConverter) {
475
- return (state, dispatch, view) => {
476
- let macro = null;
477
-
478
- // try to use auto convert from extension provider first
479
- if (extensionAutoConverter) {
480
- const extension = extensionAutoConverter(text);
481
- if (extension) {
482
- macro = PMNode.fromJSON(state.schema, extension);
483
- }
484
- }
485
-
486
- // then try from macro provider (which will be removed some time in the future)
487
- if (!macro) {
488
- var _runMacroAutoConvert;
489
- macro = (_runMacroAutoConvert = runMacroAutoConvert === null || runMacroAutoConvert === void 0 ? void 0 : runMacroAutoConvert(state, text)) !== null && _runMacroAutoConvert !== void 0 ? _runMacroAutoConvert : null;
490
- }
491
- if (macro) {
492
- /**
493
- * if FF enabled, run through smart links and check for result
494
- */
495
- if (cardsOptions && cardsOptions.resolveBeforeMacros && cardsOptions.resolveBeforeMacros.length) {
496
- if (cardsOptions.resolveBeforeMacros.indexOf(macro.attrs.extensionKey) < 0) {
497
- return insertAutoMacro(slice, macro, view);
498
- }
499
- if (!view) {
500
- throw new Error('View is missing');
501
- }
502
- const trackingId = uuid();
503
- const trackingFrom = `handleMacroAutoConvert-from-${trackingId}`;
504
- const trackingTo = `handleMacroAutoConvert-to-${trackingId}`;
505
- startTrackingPastedMacroPositions({
506
- [trackingFrom]: state.selection.from,
507
- [trackingTo]: state.selection.to
508
- })(state, dispatch);
509
- getSmartLinkAdf(text, 'inline', cardsOptions).then(() => {
510
- // we use view.state rather than state because state becomes a stale
511
- // state reference after getSmartLinkAdf's async work
512
- const {
513
- pastedMacroPositions
514
- } = getPastePluginState(view.state);
515
- if (dispatch) {
516
- handleMarkdown(slice, queueCardsFromChangedTr, pastedMacroPositions[trackingFrom], pastedMacroPositions[trackingTo])(view.state, dispatch);
517
- }
518
- }).catch(() => {
519
- const {
520
- pastedMacroPositions
521
- } = getPastePluginState(view.state);
522
- insertAutoMacro(slice, macro, view, pastedMacroPositions[trackingFrom], pastedMacroPositions[trackingTo]);
523
- }).finally(() => {
524
- stopTrackingPastedMacroPositions([trackingFrom, trackingTo])(view.state, dispatch);
525
- });
526
- return true;
527
- }
528
- return insertAutoMacro(slice, macro, view);
529
- }
530
- return !!macro;
531
- };
532
- }
533
- export function handleCodeBlock(text) {
534
- return (state, dispatch) => {
535
- const {
536
- codeBlock
537
- } = state.schema.nodes;
538
- if (text && hasParentNodeOfType(codeBlock)(state.selection)) {
539
- const tr = closeHistory(state.tr);
540
- tr.scrollIntoView();
541
- if (dispatch) {
542
- dispatch(tr.insertText(text));
543
- }
544
- return true;
545
- }
546
- return false;
547
- };
548
- }
549
- function isOnlyMedia(state, slice) {
550
- const {
551
- media
552
- } = state.schema.nodes;
553
- return slice.content.childCount === 1 && slice.content.firstChild.type === media;
554
- }
555
- function isOnlyMediaSingle(state, slice) {
556
- const {
557
- mediaSingle
558
- } = state.schema.nodes;
559
- return mediaSingle && slice.content.childCount === 1 && slice.content.firstChild.type === mediaSingle;
560
- }
561
- export function handleMediaSingle(inputMethod, insertMediaAsMediaSingle) {
562
- return function (slice) {
563
- return (state, dispatch, view) => {
564
- if (view) {
565
- if (isOnlyMedia(state, slice)) {
566
- var _insertMediaAsMediaSi;
567
- return (_insertMediaAsMediaSi = insertMediaAsMediaSingle === null || insertMediaAsMediaSingle === void 0 ? void 0 : insertMediaAsMediaSingle(view, slice.content.firstChild, inputMethod)) !== null && _insertMediaAsMediaSi !== void 0 ? _insertMediaAsMediaSi : false;
568
- }
569
- if (insideTable(state) && isOnlyMediaSingle(state, slice)) {
570
- const tr = state.tr.replaceSelection(slice);
571
- const nextPos = tr.doc.resolve(tr.mapping.map(state.selection.$from.pos));
572
- if (dispatch) {
573
- dispatch(tr.setSelection(new GapCursorSelection(nextPos, Side.RIGHT)));
574
- }
575
- return true;
576
- }
577
- }
578
- return false;
579
- };
580
- };
581
- }
582
- const checkExpand = slice => {
583
- let hasExpand = false;
584
- slice.content.forEach(node => {
585
- if (node.type.name === 'expand') {
586
- hasExpand = true;
587
- }
588
- });
589
- return hasExpand;
590
- };
591
- export function handleExpandPasteInTable(slice) {
592
- return (state, dispatch) => {
593
- // Do not handle expand if it's not being pasted into a table
594
- // OR if it's nested within another node when being pasted into a table
595
- if (!insideTable(state) || !checkExpand(slice)) {
596
- return false;
597
- }
598
- const {
599
- expand,
600
- nestedExpand
601
- } = state.schema.nodes;
602
- let {
603
- tr
604
- } = state;
605
- let hasExpand = false;
606
- const newSlice = mapSlice(slice, maybeNode => {
607
- if (maybeNode.type === expand) {
608
- hasExpand = true;
609
- try {
610
- return nestedExpand.createChecked(maybeNode.attrs, maybeNode.content, maybeNode.marks);
611
- } catch (e) {
612
- tr = safeInsert(maybeNode, tr.selection.$to.pos)(tr);
613
- return Fragment.empty;
614
- }
615
- }
616
- return maybeNode;
617
- });
618
- if (hasExpand && dispatch) {
619
- // If the slice is a subset, we can let PM replace the selection
620
- // it will insert as text where it can't place the node.
621
- // Otherwise we use safeInsert to insert below instead of
622
- // replacing/splitting the current node.
623
- if (slice.openStart > 1 && slice.openEnd > 1) {
624
- dispatch(tr.replaceSelection(newSlice));
625
- } else {
626
- dispatch(safeInsert(newSlice.content)(tr));
627
- }
628
- return true;
629
- }
630
- return false;
631
- };
632
- }
633
- export function handleMarkdown(markdownSlice, queueCardsFromChangedTr, from, to) {
634
- return (state, dispatch) => {
635
- const tr = closeHistory(state.tr);
636
- const pastesFrom = typeof from === 'number' ? from : tr.selection.from;
637
- if (typeof from === 'number' && typeof to === 'number') {
638
- tr.replaceRange(from, to, markdownSlice);
639
- } else {
640
- tr.replaceSelection(markdownSlice);
641
- }
642
- const textPosition = tr.doc.resolve(Math.min(pastesFrom + markdownSlice.size, tr.doc.content.size));
643
- tr.setSelection(TextSelection.near(textPosition, -1));
644
- queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 ? void 0 : queueCardsFromChangedTr(state, tr, INPUT_METHOD.CLIPBOARD);
645
- if (dispatch) {
646
- dispatch(tr.scrollIntoView());
647
- }
648
- return true;
649
- };
650
- }
651
- function removePrecedingBackTick(tr) {
652
- const {
653
- $from: {
654
- nodeBefore
655
- },
656
- from
657
- } = tr.selection;
658
- if (nodeBefore && nodeBefore.isText && nodeBefore.text.endsWith('`')) {
659
- tr.delete(from - 1, from);
660
- }
661
- }
662
- function hasInlineCode(state, slice) {
663
- return slice.content.firstChild && slice.content.firstChild.marks.some(m => m.type === state.schema.marks.code);
664
- }
665
- function rollupLeafListItems(list, leafListItems) {
666
- list.content.forEach(child => {
667
- if (isListNode(child) || isListItemNode(child) && isListNode(child.firstChild)) {
668
- rollupLeafListItems(child, leafListItems);
669
- } else {
670
- leafListItems.push(child);
671
- }
672
- });
673
- }
674
- function shouldFlattenList(state, slice) {
675
- const node = slice.content.firstChild;
676
- return node && insideTable(state) && isListNode(node) && slice.openStart > slice.openEnd;
677
- }
678
- function sliceHasTopLevelMarks(slice) {
679
- let hasTopLevelMarks = false;
680
- slice.content.descendants(node => {
681
- if (node.marks.length > 0) {
682
- hasTopLevelMarks = true;
683
- }
684
- return false;
685
- });
686
- return hasTopLevelMarks;
687
- }
688
- function getTopLevelMarkTypesInSlice(slice) {
689
- const markTypes = new Set();
690
- slice.content.descendants(node => {
691
- node.marks.map(mark => mark.type).forEach(markType => markTypes.add(markType));
692
- return false;
693
- });
694
- return markTypes;
695
- }
696
- export function handleParagraphBlockMarks(state, slice) {
697
- if (slice.content.size === 0) {
698
- return slice;
699
- }
700
- const {
701
- schema,
702
- selection: {
703
- $from
704
- }
705
- } = state;
706
-
707
- // If no paragraph in the slice contains marks, there's no need for special handling
708
- // Note: this doesn't check for marks applied to lower level nodes such as text
709
- if (!sliceHasTopLevelMarks(slice)) {
710
- return slice;
711
- }
712
-
713
- // If pasting a single paragraph into pre-existing content, match destination formatting
714
- const destinationHasContent = $from.parent.textContent.length > 0;
715
- if (slice.content.childCount === 1 && destinationHasContent) {
716
- return slice;
717
- }
718
-
719
- // Check the parent of (paragraph -> text) because block marks are assigned to a wrapper
720
- // element around the paragraph node
721
- const grandparent = $from.node(Math.max(0, $from.depth - 1));
722
- const markTypesInSlice = getTopLevelMarkTypesInSlice(slice);
723
- let forbiddenMarkTypes = [];
724
- for (let markType of markTypesInSlice) {
725
- if (!grandparent.type.allowsMarkType(markType)) {
726
- forbiddenMarkTypes.push(markType);
727
- }
728
- }
729
- if (forbiddenMarkTypes.length === 0) {
730
- // In a slice containing one or more paragraphs at the document level (not wrapped in
731
- // another node), the first paragraph will only have its text content captured and pasted
732
- // since openStart is 1. We decrement the open depth of the slice so it retains any block
733
- // marks applied to it. We only care about the depth at the start of the selection so
734
- // there's no need to change openEnd - the rest of the slice gets pasted correctly.
735
- const openStart = Math.max(0, slice.openStart - 1);
736
- return new Slice(slice.content, openStart, slice.openEnd);
737
- }
738
-
739
- // If the paragraph contains marks forbidden by the parent node (e.g. alignment/indentation),
740
- // drop those marks from the slice
741
- return mapSlice(slice, node => {
742
- if (node.type === schema.nodes.paragraph) {
743
- return schema.nodes.paragraph.createChecked(undefined, node.content, node.marks.filter(mark => !forbiddenMarkTypes.includes(mark.type)));
744
- }
745
- return node;
746
- });
747
- }
748
-
749
- /**
750
- * ED-6300: When a nested list is pasted in a table cell and the slice has openStart > openEnd,
751
- * it splits the table. As a workaround, we flatten the list to even openStart and openEnd.
752
- *
753
- * Note: this only happens if the first child is a list
754
- *
755
- * Example: copying "one" and "two"
756
- * - zero
757
- * - one
758
- * - two
759
- *
760
- * Before:
761
- * ul
762
- * ┗━ li
763
- * ┗━ ul
764
- * ┗━ li
765
- * ┗━ p -> "one"
766
- * ┗━ li
767
- * ┗━ p -> "two"
768
- *
769
- * After:
770
- * ul
771
- * ┗━ li
772
- * ┗━ p -> "one"
773
- * ┗━ li
774
- * ┗━p -> "two"
775
- */
776
- export function flattenNestedListInSlice(slice) {
777
- if (!slice.content.firstChild) {
778
- return slice;
779
- }
780
- const listToFlatten = slice.content.firstChild;
781
- const leafListItems = [];
782
- rollupLeafListItems(listToFlatten, leafListItems);
783
- const contentWithFlattenedList = slice.content.replaceChild(0, listToFlatten.type.createChecked(listToFlatten.attrs, leafListItems));
784
- return new Slice(contentWithFlattenedList, slice.openEnd, slice.openEnd);
785
- }
786
- export function handleRichText(slice, queueCardsFromChangedTr) {
787
- return (state, dispatch) => {
788
- var _slice$content, _slice$content2, _firstChildOfSlice$ty, _lastChildOfSlice$typ, _panelParentOverCurre;
789
- const {
790
- codeBlock,
791
- heading,
792
- paragraph,
793
- panel
794
- } = state.schema.nodes;
795
- const {
796
- selection,
797
- schema
798
- } = state;
799
- const firstChildOfSlice = (_slice$content = slice.content) === null || _slice$content === void 0 ? void 0 : _slice$content.firstChild;
800
- const lastChildOfSlice = (_slice$content2 = slice.content) === null || _slice$content2 === void 0 ? void 0 : _slice$content2.lastChild;
801
-
802
- // In case user is pasting inline code,
803
- // any backtick ` immediately preceding it should be removed.
804
- let tr = state.tr;
805
- if (hasInlineCode(state, slice)) {
806
- removePrecedingBackTick(tr);
807
- }
808
- if (shouldFlattenList(state, slice)) {
809
- slice = flattenNestedListInSlice(slice);
810
- }
811
- closeHistory(tr);
812
- const isFirstChildListNode = isListNode(firstChildOfSlice);
813
- const isLastChildListNode = isListNode(lastChildOfSlice);
814
- const isSliceContentListNodes = isFirstChildListNode || isLastChildListNode;
815
- const isFirstChildTaskListNode = (firstChildOfSlice === null || firstChildOfSlice === void 0 ? void 0 : (_firstChildOfSlice$ty = firstChildOfSlice.type) === null || _firstChildOfSlice$ty === void 0 ? void 0 : _firstChildOfSlice$ty.name) === 'taskList';
816
- const isLastChildTaskListNode = (lastChildOfSlice === null || lastChildOfSlice === void 0 ? void 0 : (_lastChildOfSlice$typ = lastChildOfSlice.type) === null || _lastChildOfSlice$typ === void 0 ? void 0 : _lastChildOfSlice$typ.name) === 'taskList';
817
- const isSliceContentTaskListNodes = isFirstChildTaskListNode || isLastChildTaskListNode;
818
-
819
- // We want to use safeInsert to insert invalid content, as it inserts at the closest non schema violating position
820
- // rather than spliting the selection parent node in half (which is what replaceSelection does)
821
- // Exception is paragraph and heading nodes, these should be split, provided their parent supports the pasted content
822
- const textNodes = [heading, paragraph];
823
- const selectionParent = selection.$to.node(selection.$to.depth - 1);
824
- const noNeedForSafeInsert = selection.$to.node().type.validContent(slice.content) || textNodes.includes(selection.$to.node().type) && selectionParent.type.validContent(slice.content);
825
- let panelParentOverCurrentSelection = findParentNodeOfType(panel)(tr.selection);
826
- const isTargetPanelEmpty = panelParentOverCurrentSelection && ((_panelParentOverCurre = panelParentOverCurrentSelection.node) === null || _panelParentOverCurre === void 0 ? void 0 : _panelParentOverCurre.content.size) === 2;
827
- if (!isSliceContentTaskListNodes && (isSliceContentListNodes || isTargetPanelEmpty)) {
828
- insertSliceForLists({
829
- tr,
830
- slice,
831
- schema
832
- });
833
- } else if (noNeedForSafeInsert) {
834
- var _firstChildOfSlice$ty2, _firstChildOfSlice$co;
835
- if ((firstChildOfSlice === null || firstChildOfSlice === void 0 ? void 0 : (_firstChildOfSlice$ty2 = firstChildOfSlice.type) === null || _firstChildOfSlice$ty2 === void 0 ? void 0 : _firstChildOfSlice$ty2.name) === 'blockquote' && (firstChildOfSlice === null || firstChildOfSlice === void 0 ? void 0 : (_firstChildOfSlice$co = firstChildOfSlice.content.firstChild) === null || _firstChildOfSlice$co === void 0 ? void 0 : _firstChildOfSlice$co.type.name) === ('bulletList' || 'orderedList')) {
836
- // checks if parent node is a blockquote and child node is either a bulletlist or orderedlist
837
- insertSliceForListsInsideBlockquote({
838
- tr,
839
- slice
840
- });
841
- } else {
842
- var _slice$content$lastCh, _slice$content$lastCh2;
843
- tr.replaceSelection(slice);
844
- // when cursor is inside a table cell, and slice.content.lastChild is a panel, expand, or decisionList
845
- // need to make sure the cursor position is is right after the panel, expand, or decisionList
846
- // still in the same table cell, see issue: https://product-fabric.atlassian.net/browse/ED-17862
847
- const shouldUpdateCursorPosAfterPaste = ['panel', 'nestedExpand', 'decisionList', 'codeBlock'].includes(((_slice$content$lastCh = slice.content.lastChild) === null || _slice$content$lastCh === void 0 ? void 0 : (_slice$content$lastCh2 = _slice$content$lastCh.type) === null || _slice$content$lastCh2 === void 0 ? void 0 : _slice$content$lastCh2.name) || '');
848
- if (insideTableCell(state) && shouldUpdateCursorPosAfterPaste) {
849
- const nextPos = tr.doc.resolve(tr.mapping.map(selection.$from.pos));
850
- tr.setSelection(new GapCursorSelection(nextPos, Side.RIGHT));
851
- }
852
- }
853
- } else {
854
- // need to scan the slice if there's a block node or list items inside it
855
- let sliceHasList = false;
856
- slice.content.nodesBetween(0, slice.content.size, (node, start) => {
857
- if (node.type === state.schema.nodes.listItem) {
858
- sliceHasList = true;
859
- return false;
860
- }
861
- });
862
- if (insideTableCell(state) && isInListItem(state) && canInsert(selection.$from, slice.content) && canInsert(selection.$to, slice.content) || sliceHasList) {
863
- tr.replaceSelection(slice);
864
- } else {
865
- // need safeInsert rather than replaceSelection, so that nodes aren't split in half
866
- // e.g. when pasting a layout into a table, replaceSelection splits the table in half and adds the layout in the middle
867
- tr = safeInsert(slice.content, tr.selection.$to.pos)(tr);
868
- }
869
- }
870
- tr.setStoredMarks([]);
871
- if (tr.selection.empty && tr.selection.$from.parent.type === codeBlock) {
872
- tr.setSelection(TextSelection.near(tr.selection.$from, 1));
873
- }
874
- tr.scrollIntoView();
875
-
876
- // queue link cards, ignoring any errors
877
- queueCardsFromChangedTr === null || queueCardsFromChangedTr === void 0 ? void 0 : queueCardsFromChangedTr(state, tr, INPUT_METHOD.CLIPBOARD);
878
- if (dispatch) {
879
- dispatch(tr);
880
- }
881
- return true;
882
- };
883
- }
884
- export function handlePasteIntoCaption(slice) {
885
- return (state, dispatch) => {
886
- const {
887
- caption
888
- } = state.schema.nodes;
889
- const tr = state.tr;
890
- if (hasParentNodeOfType(caption)(state.selection)) {
891
- // We let PM replace the selection and it will insert as text where it can't place the node
892
- // This is totally fine as caption is just a simple block that only contains inline contents
893
- // And it is more in line with WYSIWYG expectations
894
- tr.replaceSelection(slice).scrollIntoView();
895
- if (dispatch) {
896
- dispatch(tr);
897
- }
898
- return true;
899
- }
900
- return false;
901
- };
902
- }
903
- export const handleSelectedTable = editorAnalyticsAPI => slice => (state, dispatch) => {
904
- let tr = replaceSelectedTable(state, slice);
905
-
906
- // add analytics after replacing selected table
907
- tr = addReplaceSelectedTableAnalytics(state, tr, editorAnalyticsAPI);
908
- if (tr.docChanged) {
909
- if (dispatch) {
910
- dispatch(tr);
911
- }
912
- return true;
913
- }
914
- return false;
915
- };