@atlaskit/editor-plugin-paste 13.2.2 → 13.3.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.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/pm-plugins/util/edge-cases/index.js +32 -6
- package/dist/cjs/pm-plugins/util/handlers.js +27 -6
- package/dist/es2019/pm-plugins/util/edge-cases/index.js +35 -7
- package/dist/es2019/pm-plugins/util/handlers.js +27 -6
- package/dist/esm/pm-plugins/util/edge-cases/index.js +33 -7
- package/dist/esm/pm-plugins/util/handlers.js +27 -6
- package/dist/types/pm-plugins/util/edge-cases/index.d.ts +1 -1
- package/package.json +8 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-paste
|
|
2
2
|
|
|
3
|
+
## 13.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`3f5a55729ccfa`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3f5a55729ccfa) -
|
|
8
|
+
Fix over-nesting (auto-indent) when pasting a task/action into a bullet list item under the
|
|
9
|
+
flexible list schema (EDITOR-6738), behind kill switch platform_editor_flexible_list_kill_switch_1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies
|
|
14
|
+
|
|
15
|
+
## 13.2.3
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
3
21
|
## 13.2.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -13,6 +13,7 @@ var _model = require("@atlaskit/editor-prosemirror/model");
|
|
|
13
13
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
14
14
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
15
15
|
var _utils2 = require("@atlaskit/editor-prosemirror/utils");
|
|
16
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
16
17
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
17
18
|
var _index = require("../index");
|
|
18
19
|
var _lists = require("./lists");
|
|
@@ -139,10 +140,35 @@ function insertSliceForTaskInsideList(_ref5) {
|
|
|
139
140
|
var tr = _ref5.tr,
|
|
140
141
|
slice = _ref5.slice;
|
|
141
142
|
var schema = tr.doc.type.schema;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
|
|
144
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
145
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
146
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
147
|
+
if (!(0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) || (0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1')) {
|
|
148
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
149
|
+
var selectionBeforeReplace = tr.selection.from;
|
|
150
|
+
tr.replaceSelection(new _model.Slice(_model.Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
151
|
+
var nextSelection = _state.Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
152
|
+
tr.setSelection(nextSelection);
|
|
153
|
+
tr.replaceSelection(slice);
|
|
154
|
+
return tr;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
158
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
159
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
160
|
+
var taskList = schema.nodes.taskList;
|
|
161
|
+
var content = slice.content;
|
|
162
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
163
|
+
var _content$firstChild, _content$firstChild$f;
|
|
164
|
+
content = content.firstChild.content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
168
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
169
|
+
tr = (0, _utils2.safeInsert)(content, tr.selection.$to.pos)(tr);
|
|
170
|
+
updateSelectionAfterReplace({
|
|
171
|
+
tr: tr
|
|
172
|
+
});
|
|
173
|
+
return tr;
|
|
148
174
|
}
|
|
@@ -107,7 +107,7 @@ function handleMention(slice, schema) {
|
|
|
107
107
|
}
|
|
108
108
|
function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
109
109
|
return function (state, dispatch) {
|
|
110
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
110
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
111
111
|
var schema = state.schema,
|
|
112
112
|
selection = state.tr.selection;
|
|
113
113
|
var codeMark = schema.marks.code,
|
|
@@ -187,9 +187,21 @@ function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
|
187
187
|
// or the slice's first node is a paragraph,
|
|
188
188
|
// then we can replace the selection with our slice.
|
|
189
189
|
var pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
190
|
-
|
|
190
|
+
|
|
191
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
192
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
193
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
194
|
+
var isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
195
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
196
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
197
|
+
// the normal replaceSelection path.
|
|
198
|
+
var pastingIntoTaskItem = (0, _utils2.hasParentNodeOfType)([taskItem])(selection);
|
|
199
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && !(0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
200
|
+
var unwrappedSlice = new _model.Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
201
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
202
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
191
203
|
// Whole codeblock node has reverse slice depths.
|
|
192
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
204
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
193
205
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
194
206
|
} else {
|
|
195
207
|
var isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -208,14 +220,14 @@ function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
|
208
220
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
209
221
|
}
|
|
210
222
|
} else {
|
|
211
|
-
var _transformedSlice$
|
|
223
|
+
var _transformedSlice$con3;
|
|
212
224
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
213
225
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
214
226
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
215
227
|
}
|
|
216
228
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
217
229
|
(0, _utils2.safeInsert)(transformedSlice.content)(tr).scrollIntoView();
|
|
218
|
-
if (((_transformedSlice$
|
|
230
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 || (_transformedSlice$con3 = _transformedSlice$con3.type) === null || _transformedSlice$con3 === void 0 ? void 0 : _transformedSlice$con3.name) === 'rule') {
|
|
219
231
|
tr.setSelection(_state.TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
220
232
|
} else {
|
|
221
233
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1231,8 +1243,17 @@ function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1231
1243
|
});
|
|
1232
1244
|
if ((0, _utils.insideTableCell)(state) && (0, _utils.isInListItem)(state) && (0, _utils2.canInsert)(selection.$from, slice.content) && (0, _utils2.canInsert)(selection.$to, slice.content) || sliceHasList) {
|
|
1233
1245
|
tr.replaceSelection(slice);
|
|
1246
|
+
} else if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true) && !(0, _platformFeatureFlags.fg)('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1247
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1248
|
+
// experiment and protected by the kill switch.
|
|
1249
|
+
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
|
|
1250
|
+
tr: tr,
|
|
1251
|
+
slice: slice
|
|
1252
|
+
});
|
|
1234
1253
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1235
|
-
(
|
|
1254
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1255
|
+
// behaviour via the function's internal fallback.
|
|
1256
|
+
tr = (0, _edgeCases.insertSliceForTaskInsideList)({
|
|
1236
1257
|
tr: tr,
|
|
1237
1258
|
slice: slice
|
|
1238
1259
|
});
|
|
@@ -3,7 +3,8 @@ import { isListNode } from '@atlaskit/editor-common/utils';
|
|
|
3
3
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
5
5
|
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
|
-
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
6
|
+
import { findParentNodeOfType, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
9
|
import { isCursorSelectionAtTextStartOrEnd, isEmptyNode, isSelectionInsidePanel } from '../index';
|
|
9
10
|
import { insertSliceAtNodeEdge, insertSliceInsideOfPanelNodeSelected, insertSliceIntoEmptyNode, insertSliceIntoRangeSelectionInsideList } from './lists';
|
|
@@ -140,10 +141,37 @@ export function insertSliceForTaskInsideList({
|
|
|
140
141
|
const {
|
|
141
142
|
schema
|
|
142
143
|
} = tr.doc.type;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
|
|
145
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
146
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
147
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
148
|
+
if (!expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) || fg('platform_editor_flexible_list_kill_switch_1')) {
|
|
149
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
150
|
+
const selectionBeforeReplace = tr.selection.from;
|
|
151
|
+
tr.replaceSelection(new Slice(Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
152
|
+
const nextSelection = Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
153
|
+
tr.setSelection(nextSelection);
|
|
154
|
+
tr.replaceSelection(slice);
|
|
155
|
+
return tr;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
159
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
160
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
161
|
+
const {
|
|
162
|
+
taskList
|
|
163
|
+
} = schema.nodes;
|
|
164
|
+
let content = slice.content;
|
|
165
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
166
|
+
var _content$firstChild, _content$firstChild$f;
|
|
167
|
+
content = content.firstChild.content;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
171
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
172
|
+
tr = safeInsert(content, tr.selection.$to.pos)(tr);
|
|
173
|
+
updateSelectionAfterReplace({
|
|
174
|
+
tr
|
|
175
|
+
});
|
|
176
|
+
return tr;
|
|
149
177
|
}
|
|
@@ -67,7 +67,7 @@ export function handleMention(slice, schema) {
|
|
|
67
67
|
}
|
|
68
68
|
export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
69
69
|
return (state, dispatch) => {
|
|
70
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
70
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
71
71
|
const {
|
|
72
72
|
schema,
|
|
73
73
|
tr: {
|
|
@@ -154,9 +154,21 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
154
154
|
// or the slice's first node is a paragraph,
|
|
155
155
|
// then we can replace the selection with our slice.
|
|
156
156
|
const pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
157
|
-
|
|
157
|
+
|
|
158
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
159
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
160
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
161
|
+
const isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
162
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
163
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
164
|
+
// the normal replaceSelection path.
|
|
165
|
+
const pastingIntoTaskItem = hasParentNodeOfType([taskItem])(selection);
|
|
166
|
+
if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
167
|
+
const unwrappedSlice = new Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
168
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
169
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
158
170
|
// Whole codeblock node has reverse slice depths.
|
|
159
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
171
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
160
172
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
161
173
|
} else {
|
|
162
174
|
const isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -175,14 +187,14 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
175
187
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
176
188
|
}
|
|
177
189
|
} else {
|
|
178
|
-
var _transformedSlice$
|
|
190
|
+
var _transformedSlice$con3, _transformedSlice$con4;
|
|
179
191
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
180
192
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
181
193
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
182
194
|
}
|
|
183
195
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
184
196
|
safeInsert(transformedSlice.content)(tr).scrollIntoView();
|
|
185
|
-
if (((_transformedSlice$
|
|
197
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 ? void 0 : (_transformedSlice$con4 = _transformedSlice$con3.type) === null || _transformedSlice$con4 === void 0 ? void 0 : _transformedSlice$con4.name) === 'rule') {
|
|
186
198
|
tr.setSelection(TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
187
199
|
} else {
|
|
188
200
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1206,8 +1218,17 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1206
1218
|
});
|
|
1207
1219
|
if (insideTableCell(state) && isInListItem(state) && canInsert(selection.$from, slice.content) && canInsert(selection.$to, slice.content) || sliceHasList) {
|
|
1208
1220
|
tr.replaceSelection(slice);
|
|
1221
|
+
} else if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1222
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1223
|
+
// experiment and protected by the kill switch.
|
|
1224
|
+
tr = insertSliceForTaskInsideList({
|
|
1225
|
+
tr,
|
|
1226
|
+
slice
|
|
1227
|
+
});
|
|
1209
1228
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1210
|
-
|
|
1229
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1230
|
+
// behaviour via the function's internal fallback.
|
|
1231
|
+
tr = insertSliceForTaskInsideList({
|
|
1211
1232
|
tr,
|
|
1212
1233
|
slice
|
|
1213
1234
|
});
|
|
@@ -3,7 +3,8 @@ import { isListNode } from '@atlaskit/editor-common/utils';
|
|
|
3
3
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
4
4
|
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
5
5
|
import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
|
-
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
6
|
+
import { findParentNodeOfType, safeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
9
|
import { isCursorSelectionAtTextStartOrEnd, isEmptyNode, isSelectionInsidePanel } from '../index';
|
|
9
10
|
import { insertSliceAtNodeEdge, insertSliceInsideOfPanelNodeSelected, insertSliceIntoEmptyNode, insertSliceIntoRangeSelectionInsideList } from './lists';
|
|
@@ -130,10 +131,35 @@ export function insertSliceForTaskInsideList(_ref5) {
|
|
|
130
131
|
var tr = _ref5.tr,
|
|
131
132
|
slice = _ref5.slice;
|
|
132
133
|
var schema = tr.doc.type.schema;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
|
|
135
|
+
// The corrected insertion path only applies for the flexible list indentation dogfooding audience
|
|
136
|
+
// (`platform_editor_flexible_list_indentation`), and the kill switch protects that audience from
|
|
137
|
+
// regressions. Fall back to the legacy path when the experiment is OFF or the kill switch is ON.
|
|
138
|
+
if (!expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) || fg('platform_editor_flexible_list_kill_switch_1')) {
|
|
139
|
+
//To avoid the list being replaced with the tasklist, enclose the slice within a taskItem.
|
|
140
|
+
var selectionBeforeReplace = tr.selection.from;
|
|
141
|
+
tr.replaceSelection(new Slice(Fragment.from(schema.nodes.taskItem.createAndFill()), 0, 0));
|
|
142
|
+
var nextSelection = Selection.near(tr.doc.resolve(selectionBeforeReplace + 1));
|
|
143
|
+
tr.setSelection(nextSelection);
|
|
144
|
+
tr.replaceSelection(slice);
|
|
145
|
+
return tr;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Collapse redundant nested taskList wrappers from the slice context, always keeping a single
|
|
149
|
+
// outermost taskList so the content can be safely inserted as a sibling of the list item's
|
|
150
|
+
// paragraph (a listItem cannot directly contain bare taskItems).
|
|
151
|
+
var taskList = schema.nodes.taskList;
|
|
152
|
+
var content = slice.content;
|
|
153
|
+
while (content.childCount === 1 && ((_content$firstChild = content.firstChild) === null || _content$firstChild === void 0 ? void 0 : _content$firstChild.type) === taskList && ((_content$firstChild$f = content.firstChild.firstChild) === null || _content$firstChild$f === void 0 ? void 0 : _content$firstChild$f.type) === taskList) {
|
|
154
|
+
var _content$firstChild, _content$firstChild$f;
|
|
155
|
+
content = content.firstChild.content;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// safeInsert keeps the task structure intact at the closest schema-valid position, instead of
|
|
159
|
+
// replacing into an empty taskItem which would add an extra taskList level.
|
|
160
|
+
tr = safeInsert(content, tr.selection.$to.pos)(tr);
|
|
161
|
+
updateSelectionAfterReplace({
|
|
162
|
+
tr: tr
|
|
163
|
+
});
|
|
164
|
+
return tr;
|
|
139
165
|
}
|
|
@@ -78,7 +78,7 @@ export function handleMention(slice, schema) {
|
|
|
78
78
|
}
|
|
79
79
|
export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChangedTr) {
|
|
80
80
|
return function (state, dispatch) {
|
|
81
|
-
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con;
|
|
81
|
+
var _slice$content$firstC, _slice$content$firstC2, _slice$content$firstC3, _transformedSlice$con, _transformedSlice$con2;
|
|
82
82
|
var schema = state.schema,
|
|
83
83
|
selection = state.tr.selection;
|
|
84
84
|
var codeMark = schema.marks.code,
|
|
@@ -158,9 +158,21 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
158
158
|
// or the slice's first node is a paragraph,
|
|
159
159
|
// then we can replace the selection with our slice.
|
|
160
160
|
var pastingIntoExtendedPanel = selectionIsPanel && panel.validContent(transformedSlice.content);
|
|
161
|
-
|
|
161
|
+
|
|
162
|
+
// A task slice carrying its own open taskList wrapper would, on a plain replaceSelection,
|
|
163
|
+
// nest that wrapper inside the current item and add an extra taskList level. Unwrapping one
|
|
164
|
+
// level keeps the pasted task(s) at the same level. Guarded by a kill switch.
|
|
165
|
+
var isOpenTaskListSlice = ((_transformedSlice$con = transformedSlice.content.firstChild) === null || _transformedSlice$con === void 0 ? void 0 : _transformedSlice$con.type) === taskList && transformedSlice.openStart >= 2 && transformedSlice.openEnd >= 1;
|
|
166
|
+
// Restrict the unwrap to a taskItem destination: its task content is schema-invalid inside a
|
|
167
|
+
// decisionItem (decisionList only accepts decisionItem), so decisionItem must fall through to
|
|
168
|
+
// the normal replaceSelection path.
|
|
169
|
+
var pastingIntoTaskItem = hasParentNodeOfType([taskItem])(selection);
|
|
170
|
+
if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && isOpenTaskListSlice && pastingIntoTaskItem && !selectionIsPanel) {
|
|
171
|
+
var unwrappedSlice = new Slice(transformedSlice.content.firstChild.content, transformedSlice.openStart - 1, transformedSlice.openEnd - 1);
|
|
172
|
+
tr.replaceSelection(unwrappedSlice).scrollIntoView();
|
|
173
|
+
} else if ((transformedSliceIsValidNode || selectionIsValidNode) && !pastingIntoExtendedPanel && !(transformedSlice.openStart === 1 && transformedSlice.openEnd === 0 ||
|
|
162
174
|
// Whole codeblock node has reverse slice depths.
|
|
163
|
-
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$
|
|
175
|
+
transformedSlice.openStart === 0 && transformedSlice.openEnd === 1) || ((_transformedSlice$con2 = transformedSlice.content.firstChild) === null || _transformedSlice$con2 === void 0 ? void 0 : _transformedSlice$con2.type) === paragraph) {
|
|
164
176
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
165
177
|
} else {
|
|
166
178
|
var isWholeContentSelected = selection.$from.pos === selection.$from.start() && selection.$to.end() === selection.$to.pos;
|
|
@@ -179,14 +191,14 @@ export function handlePasteIntoTaskOrDecisionOrPanel(slice, queueCardsFromChange
|
|
|
179
191
|
tr.replaceSelection(transformedSlice).scrollIntoView();
|
|
180
192
|
}
|
|
181
193
|
} else {
|
|
182
|
-
var _transformedSlice$
|
|
194
|
+
var _transformedSlice$con3;
|
|
183
195
|
if (pastingIntoExtendedPanel && isWholeContentSelected) {
|
|
184
196
|
// if the entire panel content is selected, doing a replaceSelection removes the panel as well. Hence we do delete followed by safeInsert
|
|
185
197
|
tr.delete(selection.$from.pos, selection.$to.pos);
|
|
186
198
|
}
|
|
187
199
|
// This maintains both the selection (destination) and the slice (paste content).
|
|
188
200
|
safeInsert(transformedSlice.content)(tr).scrollIntoView();
|
|
189
|
-
if (((_transformedSlice$
|
|
201
|
+
if (((_transformedSlice$con3 = transformedSlice.content.lastChild) === null || _transformedSlice$con3 === void 0 || (_transformedSlice$con3 = _transformedSlice$con3.type) === null || _transformedSlice$con3 === void 0 ? void 0 : _transformedSlice$con3.name) === 'rule') {
|
|
190
202
|
tr.setSelection(TextSelection.near(tr.doc.resolve(tr.selection.$from.pos + transformedSlice.content.size)));
|
|
191
203
|
} else {
|
|
192
204
|
// safeInsert doesn't set correct cursor position
|
|
@@ -1202,8 +1214,17 @@ export function handleRichText(slice, queueCardsFromChangedTr) {
|
|
|
1202
1214
|
});
|
|
1203
1215
|
if (insideTableCell(state) && isInListItem(state) && canInsert(selection.$from, slice.content) && canInsert(selection.$to, slice.content) || sliceHasList) {
|
|
1204
1216
|
tr.replaceSelection(slice);
|
|
1217
|
+
} else if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true) && !fg('platform_editor_flexible_list_kill_switch_1') && checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1218
|
+
// Corrected task-into-list insertion is gated behind the flexible list indentation
|
|
1219
|
+
// experiment and protected by the kill switch.
|
|
1220
|
+
tr = insertSliceForTaskInsideList({
|
|
1221
|
+
tr: tr,
|
|
1222
|
+
slice: slice
|
|
1223
|
+
});
|
|
1205
1224
|
} else if (checkTaskListInList(state, slice) && !checkIfSelectionInNestedList(state)) {
|
|
1206
|
-
|
|
1225
|
+
// Legacy path (experiment OFF or kill switch ON): preserve the previous over-nesting
|
|
1226
|
+
// behaviour via the function's internal fallback.
|
|
1227
|
+
tr = insertSliceForTaskInsideList({
|
|
1207
1228
|
tr: tr,
|
|
1208
1229
|
slice: slice
|
|
1209
1230
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-paste",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
4
4
|
"description": "Paste plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@atlaskit/adf-schema": "^56.0.0",
|
|
23
23
|
"@atlaskit/adf-utils": "^20.0.0",
|
|
24
|
-
"@atlaskit/code": "^18.
|
|
24
|
+
"@atlaskit/code": "^18.1.0",
|
|
25
25
|
"@atlaskit/editor-markdown-transformer": "^6.0.0",
|
|
26
26
|
"@atlaskit/editor-plugin-analytics": "^12.0.0",
|
|
27
27
|
"@atlaskit/editor-plugin-annotation": "^12.0.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@atlaskit/editor-plugin-feature-flags": "^11.0.0",
|
|
32
32
|
"@atlaskit/editor-plugin-list": "^14.1.0",
|
|
33
33
|
"@atlaskit/editor-plugin-media": "^14.2.0",
|
|
34
|
-
"@atlaskit/editor-plugin-mentions": "^14.
|
|
34
|
+
"@atlaskit/editor-plugin-mentions": "^14.5.0",
|
|
35
35
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
36
36
|
"@atlaskit/editor-tables": "^3.0.0",
|
|
37
37
|
"@atlaskit/flag": "^18.0.0",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"@atlaskit/media-common": "^14.1.0",
|
|
42
42
|
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
43
43
|
"@atlaskit/prosemirror-history": "^1.0.0",
|
|
44
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
44
|
+
"@atlaskit/tmp-editor-statsig": "^114.0.0",
|
|
45
45
|
"@atlaskit/tokens": "^15.0.0",
|
|
46
46
|
"@babel/runtime": "^7.0.0",
|
|
47
47
|
"lodash": "^4.17.21",
|
|
48
48
|
"uuid": "^3.1.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@atlaskit/editor-common": "^116.
|
|
51
|
+
"@atlaskit/editor-common": "^116.13.0",
|
|
52
52
|
"react": "^18.2.0",
|
|
53
53
|
"react-dom": "^18.2.0",
|
|
54
54
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
@@ -127,6 +127,9 @@
|
|
|
127
127
|
},
|
|
128
128
|
"platform_editor_paste_renderer_analytics": {
|
|
129
129
|
"type": "boolean"
|
|
130
|
+
},
|
|
131
|
+
"platform_editor_flexible_list_kill_switch_1": {
|
|
132
|
+
"type": "boolean"
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
}
|