@atlaskit/editor-plugin-paste 1.0.13 → 1.0.15

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-paste
2
2
 
3
+ ## 1.0.15
4
+
5
+ ### Patch Changes
6
+
7
+ - [#89978](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/89978) [`6e7143622425`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/6e7143622425) - fix paste markdown table into a table issue
8
+
9
+ ## 1.0.14
10
+
11
+ ### Patch Changes
12
+
13
+ - [#87898](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/87898) [`6d4009f72e36`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/6d4009f72e36) - [ux] [ED-22591] Fix pasting logic for lines with number and dot (but is not a list item) to retain formatting and correct list conversion.
14
+ - Updated dependencies
15
+
3
16
  ## 1.0.13
4
17
 
5
18
  ### Patch Changes
@@ -4,13 +4,14 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.upgradeTextToLists = exports.stopTrackingPastedMacroPositions = exports.startTrackingPastedMacroPositions = exports.splitParagraphs = exports.splitIntoParagraphs = void 0;
7
+ exports.upgradeTextToLists = exports.stopTrackingPastedMacroPositions = exports.startTrackingPastedMacroPositions = exports.splitParagraphs = exports.splitIntoParagraphs = exports._extractListFromParagraphV2 = exports._contentSplitByHardBreaks = void 0;
8
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _utils = require("@atlaskit/editor-common/utils");
11
11
  var _commands = require("@atlaskit/editor-prosemirror/commands");
12
12
  var _model = require("@atlaskit/editor-prosemirror/model");
13
13
  var _state = require("@atlaskit/editor-prosemirror/state");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
15
  var _actions = require("./actions");
15
16
  var _pluginFactory = require("./pm-plugins/plugin-factory");
16
17
  /**
@@ -66,6 +67,135 @@ var getListType = function getListType(node, schema) {
66
67
  return match ? [listType.node, match[0].length] : lastMatch;
67
68
  }, null);
68
69
  };
70
+
71
+ // Splits array of nodes by hardBreak. E.g.:
72
+ // [text "1. ", em "hello", date, hardbreak, text "2. ",
73
+ // subsup "world", hardbreak, text "smile"]
74
+ // => [
75
+ // [ text "1. ", em "hello", date ],
76
+ // [hardbreak, text "2. ", subsup "world"],
77
+ // [hardbreak, text "smile"]
78
+ // ]
79
+ var _contentSplitByHardBreaks = exports._contentSplitByHardBreaks = function _contentSplitByHardBreaks(content, schema) {
80
+ var wrapperContent = [];
81
+ var nextContent = [];
82
+ content.forEach(function (node) {
83
+ if (node.type === schema.nodes.hardBreak) {
84
+ if (nextContent.length !== 0) {
85
+ wrapperContent.push(nextContent);
86
+ }
87
+ nextContent = [node];
88
+ } else {
89
+ nextContent.push(node);
90
+ }
91
+ });
92
+ wrapperContent.push(nextContent);
93
+ return wrapperContent;
94
+ };
95
+ var _extractListFromParagraphV2 = exports._extractListFromParagraphV2 = function _extractListFromParagraphV2(node, parent, schema) {
96
+ var content = (0, _utils.mapChildren)(node.content, function (node) {
97
+ return node;
98
+ });
99
+ var linesSplitByHardbreaks = _contentSplitByHardBreaks(content, schema);
100
+ var splitListsAndParagraphs = [];
101
+ var paragraphParts = [];
102
+ for (var index = 0; index < linesSplitByHardbreaks.length; index = index + 1) {
103
+ var _firstNonHardBreakNod;
104
+ var line = linesSplitByHardbreaks[index];
105
+ var listMatch = void 0;
106
+ if (index === 0) {
107
+ var _line$;
108
+ if (((_line$ = line[0]) === null || _line$ === void 0 ? void 0 : _line$.type) === schema.nodes.hardbreak) {
109
+ paragraphParts.push(line);
110
+ continue;
111
+ } else {
112
+ // the first line the potential list item is at postion 0
113
+ listMatch = getListType(line[0], schema);
114
+ }
115
+ } else {
116
+ // lines after the first the potential list item is at postion 1
117
+ if (line.length === 1) {
118
+ // if the line only has a line break return as is
119
+ paragraphParts.push(line);
120
+ continue;
121
+ }
122
+ listMatch = getListType(line[1], schema);
123
+ }
124
+ if (!listMatch) {
125
+ // if there is not list match return as is
126
+ paragraphParts.push(line);
127
+ continue;
128
+ }
129
+ var _listMatch = listMatch,
130
+ _listMatch2 = (0, _slicedToArray2.default)(_listMatch, 2),
131
+ nodeType = _listMatch2[0],
132
+ length = _listMatch2[1];
133
+ var firstNonHardBreakNode = line.find(function (node) {
134
+ return node.type !== schema.nodes.hardBreak;
135
+ });
136
+ var chunksWithoutLeadingHardBreaks = line.slice(line.findIndex(function (node) {
137
+ return node.type !== schema.nodes.hardBreak;
138
+ }));
139
+
140
+ // retain text after bullet or number-dot e.g. 1. Hello
141
+ var startingText = firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 || (_firstNonHardBreakNod = firstNonHardBreakNode.text) === null || _firstNonHardBreakNod === void 0 ? void 0 : _firstNonHardBreakNod.substr(length);
142
+ var restOfChunk = startingText ? // apply transformation to first entry
143
+ [schema.text(startingText, firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 ? void 0 : firstNonHardBreakNode.marks)].concat((0, _toConsumableArray2.default)(chunksWithoutLeadingHardBreaks.slice(1))) : chunksWithoutLeadingHardBreaks.slice(1);
144
+
145
+ // convert to list
146
+ var listItemNode = schema.nodes.listItem.createAndFill(undefined, schema.nodes.paragraph.createChecked(undefined, restOfChunk));
147
+ if (!listItemNode) {
148
+ paragraphParts.push(line);
149
+ continue;
150
+ }
151
+ var attrs = nodeType === schema.nodes.orderedList ? {
152
+ order: parseInt(firstNonHardBreakNode.text.split('.')[0])
153
+ } : undefined;
154
+ var newList = nodeType.createChecked(attrs, [listItemNode]);
155
+ if (paragraphParts.length !== 0) {
156
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
157
+ paragraphParts = [];
158
+ }
159
+ splitListsAndParagraphs.push(newList);
160
+ }
161
+ if (paragraphParts.length !== 0) {
162
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
163
+ }
164
+ var result = splitListsAndParagraphs.flat();
165
+ // try to join
166
+ var mockState = _state.EditorState.create({
167
+ schema: schema
168
+ });
169
+ var joinedListsTr;
170
+ var mockDispatch = function mockDispatch(tr) {
171
+ joinedListsTr = tr;
172
+ };
173
+
174
+ // Return false to prevent replaceWith from wrapping the text node in a paragraph
175
+ // paragraph since that will be done later. If it's done here, it will fail
176
+ // the paragraph.validContent check.
177
+ // Dont return false if there are lists, as they arent validContent for paragraphs
178
+ // and will result in hanging textNodes
179
+ (0, _commands.autoJoin)(function (state, dispatch) {
180
+ if (!dispatch) {
181
+ return false;
182
+ }
183
+ var containsList = result.some(function (node) {
184
+ return node.type === schema.nodes.bulletList || node.type === schema.nodes.orderedList;
185
+ });
186
+ if (result.some(function (node) {
187
+ return node.isText;
188
+ }) && !containsList) {
189
+ return false;
190
+ }
191
+ dispatch(state.tr.replaceWith(0, 2, result));
192
+ return true;
193
+ }, function (before, after) {
194
+ return (0, _utils.isListNode)(before) && (0, _utils.isListNode)(after);
195
+ })(mockState, mockDispatch);
196
+ var fragment = joinedListsTr ? joinedListsTr.doc.content : _model.Fragment.from(result);
197
+ return fragment;
198
+ };
69
199
  var extractListFromParagraph = function extractListFromParagraph(node, parent, schema) {
70
200
  var _schema$nodes2 = schema.nodes,
71
201
  hardBreak = _schema$nodes2.hardBreak,
@@ -88,9 +218,9 @@ var extractListFromParagraph = function extractListFromParagraph(node, parent, s
88
218
  if (!listMatch || !child.text) {
89
219
  return child;
90
220
  }
91
- var _listMatch = (0, _slicedToArray2.default)(listMatch, 2),
92
- nodeType = _listMatch[0],
93
- length = _listMatch[1];
221
+ var _listMatch3 = (0, _slicedToArray2.default)(listMatch, 2),
222
+ nodeType = _listMatch3[0],
223
+ length = _listMatch3[1];
94
224
 
95
225
  // convert to list item
96
226
  var newText = child.text.substr(length);
@@ -170,7 +300,11 @@ var extractListFromParagraph = function extractListFromParagraph(node, parent, s
170
300
  var upgradeTextToLists = exports.upgradeTextToLists = function upgradeTextToLists(slice, schema) {
171
301
  return (0, _utils.mapSlice)(slice, function (node, parent) {
172
302
  if (node.type === schema.nodes.paragraph) {
173
- return extractListFromParagraph(node, parent, schema);
303
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.extractlistfromparagraphv2')) {
304
+ return _extractListFromParagraphV2(node, parent, schema);
305
+ } else {
306
+ return extractListFromParagraph(node, parent, schema);
307
+ }
174
308
  }
175
309
  return node;
176
310
  });
@@ -335,9 +335,16 @@ function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFlags, pl
335
335
  if ((0, _analytics2.handleSelectedTableWithAnalytics)(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
336
336
  return true;
337
337
  }
338
+ var isNestedMarkdownTable = false;
339
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.paste-markdown-table-in-a-table')) {
340
+ // if paste a markdown table inside a table cell, we should treat it as a table slice
341
+ var isParentNodeTdOrTh = selectionParentType === schema.nodes.tableCell || selectionParentType === schema.nodes.tableHeader;
342
+ isNestedMarkdownTable = !!(markdownSlice && isPlainText && isParentNodeTdOrTh && (0, _analytics2.getContentNodeTypes)(markdownSlice.content).includes(schema.nodes.table.name));
343
+ slice = isNestedMarkdownTable ? markdownSlice : slice;
344
+ }
338
345
 
339
346
  // If the clipboard only contains plain text, attempt to parse it as Markdown
340
- if (isPlainText && markdownSlice) {
347
+ if (isPlainText && markdownSlice && !isNestedMarkdownTable) {
341
348
  if ((0, _analytics2.handlePastePreservingMarksWithAnalytics)(view, event, markdownSlice, _analytics.PasteTypes.markdown, pluginInjectionApi)(state, dispatch)) {
342
349
  return true;
343
350
  }
@@ -362,7 +369,7 @@ function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFlags, pl
362
369
  }
363
370
 
364
371
  // finally, handle rich-text copy-paste
365
- if (isRichText) {
372
+ if (isRichText || isNestedMarkdownTable) {
366
373
  var _pluginInjectionApi$c2, _pluginInjectionApi$e2, _pluginInjectionApi$l;
367
374
  // linkify the text where possible
368
375
  slice = (0, _utils.linkifyContent)(state.schema)(slice);
@@ -2,6 +2,7 @@ import { isListNode, mapChildren, mapSlice } from '@atlaskit/editor-common/utils
2
2
  import { autoJoin } from '@atlaskit/editor-prosemirror/commands';
3
3
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
4
4
  import { EditorState } from '@atlaskit/editor-prosemirror/state';
5
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
6
  import { PastePluginActionTypes as ActionTypes } from './actions';
6
7
  import { createCommand } from './pm-plugins/plugin-factory';
7
8
 
@@ -55,6 +56,121 @@ const getListType = (node, schema) => {
55
56
  return match ? [listType.node, match[0].length] : lastMatch;
56
57
  }, null);
57
58
  };
59
+
60
+ // Splits array of nodes by hardBreak. E.g.:
61
+ // [text "1. ", em "hello", date, hardbreak, text "2. ",
62
+ // subsup "world", hardbreak, text "smile"]
63
+ // => [
64
+ // [ text "1. ", em "hello", date ],
65
+ // [hardbreak, text "2. ", subsup "world"],
66
+ // [hardbreak, text "smile"]
67
+ // ]
68
+ export const _contentSplitByHardBreaks = (content, schema) => {
69
+ const wrapperContent = [];
70
+ let nextContent = [];
71
+ content.forEach(node => {
72
+ if (node.type === schema.nodes.hardBreak) {
73
+ if (nextContent.length !== 0) {
74
+ wrapperContent.push(nextContent);
75
+ }
76
+ nextContent = [node];
77
+ } else {
78
+ nextContent.push(node);
79
+ }
80
+ });
81
+ wrapperContent.push(nextContent);
82
+ return wrapperContent;
83
+ };
84
+ export const _extractListFromParagraphV2 = (node, parent, schema) => {
85
+ const content = mapChildren(node.content, node => node);
86
+ const linesSplitByHardbreaks = _contentSplitByHardBreaks(content, schema);
87
+ const splitListsAndParagraphs = [];
88
+ let paragraphParts = [];
89
+ for (var index = 0; index < linesSplitByHardbreaks.length; index = index + 1) {
90
+ var _firstNonHardBreakNod;
91
+ const line = linesSplitByHardbreaks[index];
92
+ let listMatch;
93
+ if (index === 0) {
94
+ var _line$;
95
+ if (((_line$ = line[0]) === null || _line$ === void 0 ? void 0 : _line$.type) === schema.nodes.hardbreak) {
96
+ paragraphParts.push(line);
97
+ continue;
98
+ } else {
99
+ // the first line the potential list item is at postion 0
100
+ listMatch = getListType(line[0], schema);
101
+ }
102
+ } else {
103
+ // lines after the first the potential list item is at postion 1
104
+ if (line.length === 1) {
105
+ // if the line only has a line break return as is
106
+ paragraphParts.push(line);
107
+ continue;
108
+ }
109
+ listMatch = getListType(line[1], schema);
110
+ }
111
+ if (!listMatch) {
112
+ // if there is not list match return as is
113
+ paragraphParts.push(line);
114
+ continue;
115
+ }
116
+ const [nodeType, length] = listMatch;
117
+ const firstNonHardBreakNode = line.find(node => node.type !== schema.nodes.hardBreak);
118
+ const chunksWithoutLeadingHardBreaks = line.slice(line.findIndex(node => node.type !== schema.nodes.hardBreak));
119
+
120
+ // retain text after bullet or number-dot e.g. 1. Hello
121
+ const startingText = firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 ? void 0 : (_firstNonHardBreakNod = firstNonHardBreakNode.text) === null || _firstNonHardBreakNod === void 0 ? void 0 : _firstNonHardBreakNod.substr(length);
122
+ const restOfChunk = startingText ?
123
+ // apply transformation to first entry
124
+ [schema.text(startingText, firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 ? void 0 : firstNonHardBreakNode.marks), ...chunksWithoutLeadingHardBreaks.slice(1)] : chunksWithoutLeadingHardBreaks.slice(1);
125
+
126
+ // convert to list
127
+ const listItemNode = schema.nodes.listItem.createAndFill(undefined, schema.nodes.paragraph.createChecked(undefined, restOfChunk));
128
+ if (!listItemNode) {
129
+ paragraphParts.push(line);
130
+ continue;
131
+ }
132
+ const attrs = nodeType === schema.nodes.orderedList ? {
133
+ order: parseInt(firstNonHardBreakNode.text.split('.')[0])
134
+ } : undefined;
135
+ const newList = nodeType.createChecked(attrs, [listItemNode]);
136
+ if (paragraphParts.length !== 0) {
137
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
138
+ paragraphParts = [];
139
+ }
140
+ splitListsAndParagraphs.push(newList);
141
+ }
142
+ if (paragraphParts.length !== 0) {
143
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
144
+ }
145
+ const result = splitListsAndParagraphs.flat();
146
+ // try to join
147
+ const mockState = EditorState.create({
148
+ schema
149
+ });
150
+ let joinedListsTr;
151
+ const mockDispatch = tr => {
152
+ joinedListsTr = tr;
153
+ };
154
+
155
+ // Return false to prevent replaceWith from wrapping the text node in a paragraph
156
+ // paragraph since that will be done later. If it's done here, it will fail
157
+ // the paragraph.validContent check.
158
+ // Dont return false if there are lists, as they arent validContent for paragraphs
159
+ // and will result in hanging textNodes
160
+ autoJoin((state, dispatch) => {
161
+ if (!dispatch) {
162
+ return false;
163
+ }
164
+ const containsList = result.some(node => node.type === schema.nodes.bulletList || node.type === schema.nodes.orderedList);
165
+ if (result.some(node => node.isText) && !containsList) {
166
+ return false;
167
+ }
168
+ dispatch(state.tr.replaceWith(0, 2, result));
169
+ return true;
170
+ }, (before, after) => isListNode(before) && isListNode(after))(mockState, mockDispatch);
171
+ const fragment = joinedListsTr ? joinedListsTr.doc.content : Fragment.from(result);
172
+ return fragment;
173
+ };
58
174
  const extractListFromParagraph = (node, parent, schema) => {
59
175
  const {
60
176
  hardBreak,
@@ -152,7 +268,11 @@ const extractListFromParagraph = (node, parent, schema) => {
152
268
  export const upgradeTextToLists = (slice, schema) => {
153
269
  return mapSlice(slice, (node, parent) => {
154
270
  if (node.type === schema.nodes.paragraph) {
155
- return extractListFromParagraph(node, parent, schema);
271
+ if (getBooleanFF('platform.editor.extractlistfromparagraphv2')) {
272
+ return _extractListFromParagraphV2(node, parent, schema);
273
+ } else {
274
+ return extractListFromParagraph(node, parent, schema);
275
+ }
156
276
  }
157
277
  return node;
158
278
  });
@@ -304,9 +304,16 @@ export function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFl
304
304
  if (handleSelectedTableWithAnalytics(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
305
305
  return true;
306
306
  }
307
+ let isNestedMarkdownTable = false;
308
+ if (getBooleanFF('platform.editor.paste-markdown-table-in-a-table')) {
309
+ // if paste a markdown table inside a table cell, we should treat it as a table slice
310
+ const isParentNodeTdOrTh = selectionParentType === schema.nodes.tableCell || selectionParentType === schema.nodes.tableHeader;
311
+ isNestedMarkdownTable = !!(markdownSlice && isPlainText && isParentNodeTdOrTh && getContentNodeTypes(markdownSlice.content).includes(schema.nodes.table.name));
312
+ slice = isNestedMarkdownTable ? markdownSlice : slice;
313
+ }
307
314
 
308
315
  // If the clipboard only contains plain text, attempt to parse it as Markdown
309
- if (isPlainText && markdownSlice) {
316
+ if (isPlainText && markdownSlice && !isNestedMarkdownTable) {
310
317
  if (handlePastePreservingMarksWithAnalytics(view, event, markdownSlice, PasteTypes.markdown, pluginInjectionApi)(state, dispatch)) {
311
318
  return true;
312
319
  }
@@ -333,7 +340,7 @@ export function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFl
333
340
  }
334
341
 
335
342
  // finally, handle rich-text copy-paste
336
- if (isRichText) {
343
+ if (isRichText || isNestedMarkdownTable) {
337
344
  var _pluginInjectionApi$c3, _pluginInjectionApi$c4, _pluginInjectionApi$e3, _pluginInjectionApi$e4, _pluginInjectionApi$l;
338
345
  // linkify the text where possible
339
346
  slice = linkifyContent(state.schema)(slice);
@@ -4,6 +4,7 @@ import { isListNode, mapChildren, mapSlice } from '@atlaskit/editor-common/utils
4
4
  import { autoJoin } from '@atlaskit/editor-prosemirror/commands';
5
5
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
6
6
  import { EditorState } from '@atlaskit/editor-prosemirror/state';
7
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
7
8
  import { PastePluginActionTypes as ActionTypes } from './actions';
8
9
  import { createCommand } from './pm-plugins/plugin-factory';
9
10
 
@@ -60,6 +61,135 @@ var getListType = function getListType(node, schema) {
60
61
  return match ? [listType.node, match[0].length] : lastMatch;
61
62
  }, null);
62
63
  };
64
+
65
+ // Splits array of nodes by hardBreak. E.g.:
66
+ // [text "1. ", em "hello", date, hardbreak, text "2. ",
67
+ // subsup "world", hardbreak, text "smile"]
68
+ // => [
69
+ // [ text "1. ", em "hello", date ],
70
+ // [hardbreak, text "2. ", subsup "world"],
71
+ // [hardbreak, text "smile"]
72
+ // ]
73
+ export var _contentSplitByHardBreaks = function _contentSplitByHardBreaks(content, schema) {
74
+ var wrapperContent = [];
75
+ var nextContent = [];
76
+ content.forEach(function (node) {
77
+ if (node.type === schema.nodes.hardBreak) {
78
+ if (nextContent.length !== 0) {
79
+ wrapperContent.push(nextContent);
80
+ }
81
+ nextContent = [node];
82
+ } else {
83
+ nextContent.push(node);
84
+ }
85
+ });
86
+ wrapperContent.push(nextContent);
87
+ return wrapperContent;
88
+ };
89
+ export var _extractListFromParagraphV2 = function _extractListFromParagraphV2(node, parent, schema) {
90
+ var content = mapChildren(node.content, function (node) {
91
+ return node;
92
+ });
93
+ var linesSplitByHardbreaks = _contentSplitByHardBreaks(content, schema);
94
+ var splitListsAndParagraphs = [];
95
+ var paragraphParts = [];
96
+ for (var index = 0; index < linesSplitByHardbreaks.length; index = index + 1) {
97
+ var _firstNonHardBreakNod;
98
+ var line = linesSplitByHardbreaks[index];
99
+ var listMatch = void 0;
100
+ if (index === 0) {
101
+ var _line$;
102
+ if (((_line$ = line[0]) === null || _line$ === void 0 ? void 0 : _line$.type) === schema.nodes.hardbreak) {
103
+ paragraphParts.push(line);
104
+ continue;
105
+ } else {
106
+ // the first line the potential list item is at postion 0
107
+ listMatch = getListType(line[0], schema);
108
+ }
109
+ } else {
110
+ // lines after the first the potential list item is at postion 1
111
+ if (line.length === 1) {
112
+ // if the line only has a line break return as is
113
+ paragraphParts.push(line);
114
+ continue;
115
+ }
116
+ listMatch = getListType(line[1], schema);
117
+ }
118
+ if (!listMatch) {
119
+ // if there is not list match return as is
120
+ paragraphParts.push(line);
121
+ continue;
122
+ }
123
+ var _listMatch = listMatch,
124
+ _listMatch2 = _slicedToArray(_listMatch, 2),
125
+ nodeType = _listMatch2[0],
126
+ length = _listMatch2[1];
127
+ var firstNonHardBreakNode = line.find(function (node) {
128
+ return node.type !== schema.nodes.hardBreak;
129
+ });
130
+ var chunksWithoutLeadingHardBreaks = line.slice(line.findIndex(function (node) {
131
+ return node.type !== schema.nodes.hardBreak;
132
+ }));
133
+
134
+ // retain text after bullet or number-dot e.g. 1. Hello
135
+ var startingText = firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 || (_firstNonHardBreakNod = firstNonHardBreakNode.text) === null || _firstNonHardBreakNod === void 0 ? void 0 : _firstNonHardBreakNod.substr(length);
136
+ var restOfChunk = startingText ? // apply transformation to first entry
137
+ [schema.text(startingText, firstNonHardBreakNode === null || firstNonHardBreakNode === void 0 ? void 0 : firstNonHardBreakNode.marks)].concat(_toConsumableArray(chunksWithoutLeadingHardBreaks.slice(1))) : chunksWithoutLeadingHardBreaks.slice(1);
138
+
139
+ // convert to list
140
+ var listItemNode = schema.nodes.listItem.createAndFill(undefined, schema.nodes.paragraph.createChecked(undefined, restOfChunk));
141
+ if (!listItemNode) {
142
+ paragraphParts.push(line);
143
+ continue;
144
+ }
145
+ var attrs = nodeType === schema.nodes.orderedList ? {
146
+ order: parseInt(firstNonHardBreakNode.text.split('.')[0])
147
+ } : undefined;
148
+ var newList = nodeType.createChecked(attrs, [listItemNode]);
149
+ if (paragraphParts.length !== 0) {
150
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
151
+ paragraphParts = [];
152
+ }
153
+ splitListsAndParagraphs.push(newList);
154
+ }
155
+ if (paragraphParts.length !== 0) {
156
+ splitListsAndParagraphs.push(schema.nodes.paragraph.createAndFill(undefined, paragraphParts.flat()));
157
+ }
158
+ var result = splitListsAndParagraphs.flat();
159
+ // try to join
160
+ var mockState = EditorState.create({
161
+ schema: schema
162
+ });
163
+ var joinedListsTr;
164
+ var mockDispatch = function mockDispatch(tr) {
165
+ joinedListsTr = tr;
166
+ };
167
+
168
+ // Return false to prevent replaceWith from wrapping the text node in a paragraph
169
+ // paragraph since that will be done later. If it's done here, it will fail
170
+ // the paragraph.validContent check.
171
+ // Dont return false if there are lists, as they arent validContent for paragraphs
172
+ // and will result in hanging textNodes
173
+ autoJoin(function (state, dispatch) {
174
+ if (!dispatch) {
175
+ return false;
176
+ }
177
+ var containsList = result.some(function (node) {
178
+ return node.type === schema.nodes.bulletList || node.type === schema.nodes.orderedList;
179
+ });
180
+ if (result.some(function (node) {
181
+ return node.isText;
182
+ }) && !containsList) {
183
+ return false;
184
+ }
185
+ dispatch(state.tr.replaceWith(0, 2, result));
186
+ return true;
187
+ }, function (before, after) {
188
+ return isListNode(before) && isListNode(after);
189
+ })(mockState, mockDispatch);
190
+ var fragment = joinedListsTr ? joinedListsTr.doc.content : Fragment.from(result);
191
+ return fragment;
192
+ };
63
193
  var extractListFromParagraph = function extractListFromParagraph(node, parent, schema) {
64
194
  var _schema$nodes2 = schema.nodes,
65
195
  hardBreak = _schema$nodes2.hardBreak,
@@ -82,9 +212,9 @@ var extractListFromParagraph = function extractListFromParagraph(node, parent, s
82
212
  if (!listMatch || !child.text) {
83
213
  return child;
84
214
  }
85
- var _listMatch = _slicedToArray(listMatch, 2),
86
- nodeType = _listMatch[0],
87
- length = _listMatch[1];
215
+ var _listMatch3 = _slicedToArray(listMatch, 2),
216
+ nodeType = _listMatch3[0],
217
+ length = _listMatch3[1];
88
218
 
89
219
  // convert to list item
90
220
  var newText = child.text.substr(length);
@@ -164,7 +294,11 @@ var extractListFromParagraph = function extractListFromParagraph(node, parent, s
164
294
  export var upgradeTextToLists = function upgradeTextToLists(slice, schema) {
165
295
  return mapSlice(slice, function (node, parent) {
166
296
  if (node.type === schema.nodes.paragraph) {
167
- return extractListFromParagraph(node, parent, schema);
297
+ if (getBooleanFF('platform.editor.extractlistfromparagraphv2')) {
298
+ return _extractListFromParagraphV2(node, parent, schema);
299
+ } else {
300
+ return extractListFromParagraph(node, parent, schema);
301
+ }
168
302
  }
169
303
  return node;
170
304
  });
@@ -322,9 +322,16 @@ export function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFl
322
322
  if (handleSelectedTableWithAnalytics(editorAnalyticsAPI)(view, event, slice)(state, dispatch)) {
323
323
  return true;
324
324
  }
325
+ var isNestedMarkdownTable = false;
326
+ if (getBooleanFF('platform.editor.paste-markdown-table-in-a-table')) {
327
+ // if paste a markdown table inside a table cell, we should treat it as a table slice
328
+ var isParentNodeTdOrTh = selectionParentType === schema.nodes.tableCell || selectionParentType === schema.nodes.tableHeader;
329
+ isNestedMarkdownTable = !!(markdownSlice && isPlainText && isParentNodeTdOrTh && getContentNodeTypes(markdownSlice.content).includes(schema.nodes.table.name));
330
+ slice = isNestedMarkdownTable ? markdownSlice : slice;
331
+ }
325
332
 
326
333
  // If the clipboard only contains plain text, attempt to parse it as Markdown
327
- if (isPlainText && markdownSlice) {
334
+ if (isPlainText && markdownSlice && !isNestedMarkdownTable) {
328
335
  if (handlePastePreservingMarksWithAnalytics(view, event, markdownSlice, PasteTypes.markdown, pluginInjectionApi)(state, dispatch)) {
329
336
  return true;
330
337
  }
@@ -349,7 +356,7 @@ export function createPlugin(schema, dispatchAnalyticsEvent, dispatch, featureFl
349
356
  }
350
357
 
351
358
  // finally, handle rich-text copy-paste
352
- if (isRichText) {
359
+ if (isRichText || isNestedMarkdownTable) {
353
360
  var _pluginInjectionApi$c2, _pluginInjectionApi$e2, _pluginInjectionApi$l;
354
361
  // linkify the text where possible
355
362
  slice = linkifyContent(state.schema)(slice);
@@ -1,5 +1,5 @@
1
1
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
- import type { Mark, Schema } from '@atlaskit/editor-prosemirror/model';
2
+ import type { Mark, Node, Schema } from '@atlaskit/editor-prosemirror/model';
3
3
  /**
4
4
  * Use this to register macro link positions during a paste operation, that you
5
5
  * want to track in a document over time, through any document changes.
@@ -16,6 +16,8 @@ export declare const startTrackingPastedMacroPositions: (pastedMacroPositions: {
16
16
  [key: string]: number;
17
17
  }) => import("@atlaskit/editor-common/types").Command;
18
18
  export declare const stopTrackingPastedMacroPositions: (pastedMacroPositionKeys: string[]) => import("@atlaskit/editor-common/types").Command;
19
+ export declare const _contentSplitByHardBreaks: (content: Array<Node>, schema: Schema) => Array<Node>[];
20
+ export declare const _extractListFromParagraphV2: (node: Node, parent: Node | null, schema: Schema) => Fragment;
19
21
  export declare const upgradeTextToLists: (slice: Slice, schema: Schema) => Slice;
20
22
  export declare const splitParagraphs: (slice: Slice, schema: Schema) => Slice;
21
23
  /**
@@ -1,5 +1,5 @@
1
1
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
- import type { Mark, Schema } from '@atlaskit/editor-prosemirror/model';
2
+ import type { Mark, Node, Schema } from '@atlaskit/editor-prosemirror/model';
3
3
  /**
4
4
  * Use this to register macro link positions during a paste operation, that you
5
5
  * want to track in a document over time, through any document changes.
@@ -16,6 +16,8 @@ export declare const startTrackingPastedMacroPositions: (pastedMacroPositions: {
16
16
  [key: string]: number;
17
17
  }) => import("@atlaskit/editor-common/types").Command;
18
18
  export declare const stopTrackingPastedMacroPositions: (pastedMacroPositionKeys: string[]) => import("@atlaskit/editor-common/types").Command;
19
+ export declare const _contentSplitByHardBreaks: (content: Array<Node>, schema: Schema) => Array<Node>[];
20
+ export declare const _extractListFromParagraphV2: (node: Node, parent: Node | null, schema: Schema) => Fragment;
19
21
  export declare const upgradeTextToLists: (slice: Slice, schema: Schema) => Slice;
20
22
  export declare const splitParagraphs: (slice: Slice, schema: Schema) => Slice;
21
23
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Paste plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -33,18 +33,18 @@
33
33
  ".": "./src/index.ts"
34
34
  },
35
35
  "dependencies": {
36
- "@atlaskit/editor-common": "^78.22.0",
36
+ "@atlaskit/editor-common": "^78.26.0",
37
37
  "@atlaskit/editor-markdown-transformer": "^5.4.0",
38
38
  "@atlaskit/editor-plugin-analytics": "^1.0.0",
39
39
  "@atlaskit/editor-plugin-annotation": "^1.5.0",
40
40
  "@atlaskit/editor-plugin-better-type-history": "^1.0.0",
41
- "@atlaskit/editor-plugin-card": "^1.4.0",
41
+ "@atlaskit/editor-plugin-card": "^1.6.0",
42
42
  "@atlaskit/editor-plugin-feature-flags": "^1.0.0",
43
43
  "@atlaskit/editor-plugin-list": "^3.1.0",
44
- "@atlaskit/editor-plugin-media": "^1.13.0",
44
+ "@atlaskit/editor-plugin-media": "^1.14.0",
45
45
  "@atlaskit/editor-prosemirror": "3.0.0",
46
46
  "@atlaskit/editor-tables": "^2.6.0",
47
- "@atlaskit/media-client": "^26.2.0",
47
+ "@atlaskit/media-client": "^26.3.0",
48
48
  "@atlaskit/media-common": "^11.1.0",
49
49
  "@atlaskit/platform-feature-flags": "^0.2.0",
50
50
  "@babel/runtime": "^7.0.0",
@@ -135,6 +135,12 @@
135
135
  },
136
136
  "platform.editor.table.copy-paste-in-bodied-extension": {
137
137
  "type": "boolean"
138
+ },
139
+ "platform.editor.paste-markdown-table-in-a-table": {
140
+ "type": "boolean"
141
+ },
142
+ "platform.editor.extractlistfromparagraphv2": {
143
+ "type": "boolean"
138
144
  }
139
145
  }
140
146
  }