@atlaskit/editor-plugin-list 12.0.0 → 12.0.2

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,25 @@
1
1
  # @atlaskit/editor-plugin-list
2
2
 
3
+ ## 12.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`402738b592e0b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/402738b592e0b) -
8
+ Fix invalid flexible list structures caused by delete, paste, and typing operations.
9
+
10
+ Under platform_editor_flexible_list_schema, operations that remove content spanning list or task
11
+ list items could leave nodes with a nested list as their first child instead of a required
12
+ paragraph/item. Normalisation now runs efficiently on all relevant transactions in
13
+ appendTransaction.
14
+
15
+ - Updated dependencies
16
+
17
+ ## 12.0.1
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 12.0.0
4
24
 
5
25
  ### Major Changes
@@ -13,6 +13,7 @@ var _utils = require("@atlaskit/editor-common/utils");
13
13
  var _state2 = require("@atlaskit/editor-prosemirror/state");
14
14
  var _utils2 = require("@atlaskit/editor-prosemirror/utils");
15
15
  var _view = require("@atlaskit/editor-prosemirror/view");
16
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
16
17
  var _transforms = require("./transforms");
17
18
  var _selection2 = require("./utils/selection");
18
19
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -141,21 +142,23 @@ var createPlugin = exports.createPlugin = function createPlugin(eventDispatch, f
141
142
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
142
143
  key: listPluginKey,
143
144
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
144
- // The paste plugin sets 'listPasteNormalisation' on the transaction when it detects
145
- // a list is being pasted into a list (and the flexible list schema flag is on).
146
- // We use this meta to cheaply trigger normalisation without re-checking flags.
147
- if (transactions.some(function (t) {
148
- return t.getMeta('listPasteNormalisation');
145
+ if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_flexible_list_schema', 'isEnabled', true)) {
146
+ return null;
147
+ }
148
+ if (!transactions.some(function (t) {
149
+ return t.docChanged;
149
150
  })) {
150
- var tr = (0, _transforms.applyListNormalisationFixes)({
151
- tr: newState.tr,
152
- transactions: transactions,
153
- doc: newState.doc,
154
- schema: newState.schema
155
- });
156
- if (tr.docChanged) {
157
- return tr;
158
- }
151
+ return null;
152
+ }
153
+ // Efficiently scans only affected list nodes — exits early if none are found.
154
+ var tr = (0, _transforms.applyListNormalisationFixes)({
155
+ tr: newState.tr,
156
+ transactions: transactions,
157
+ doc: newState.doc,
158
+ schema: newState.schema
159
+ });
160
+ if (tr.docChanged) {
161
+ return tr;
159
162
  }
160
163
  return null;
161
164
  },
@@ -14,7 +14,6 @@ var _utils = require("@atlaskit/editor-common/utils");
14
14
  var _model = require("@atlaskit/editor-prosemirror/model");
15
15
  var _state = require("@atlaskit/editor-prosemirror/state");
16
16
  var _transform = require("@atlaskit/editor-prosemirror/transform");
17
- var _utils2 = require("@atlaskit/editor-prosemirror/utils");
18
17
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
19
18
  var _indentation = require("./utils/indentation");
20
19
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
@@ -136,14 +135,30 @@ function getAffectedListsFromTransactions(transactions, doc, schema) {
136
135
  if (!(step instanceof _transform.ReplaceStep) && !(step instanceof _transform.ReplaceAroundStep)) {
137
136
  continue;
138
137
  }
139
- // Check both the start and end of each changed range, mapped to post-paste positions.
138
+ // Check both the start and end of each changed range, mapped to post-transaction positions.
140
139
  for (var _i = 0, _arr = [step.from, step.to]; _i < _arr.length; _i++) {
141
140
  var rawPos = _arr[_i];
142
141
  var mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
143
142
  var $pos = doc.resolve(mappedPos);
144
- var ancestor = (0, _utils2.findParentNodeOfTypeClosestToPos)($pos, listTypes);
145
- if (ancestor) {
146
- result.set(ancestor.pos, ancestor.node);
143
+ // Walk ancestors from inner to outer, recording the outermost list node.
144
+ // Once we find a list and then exit list structure (hit a non-list ancestor),
145
+ // break early — prevents container nodes (e.g. panel) from causing us to
146
+ // return an outer list that is in a different structural context.
147
+ // $pos.node(depth) is O(1) array access.
148
+ var rootListPos = null;
149
+ var rootListNode = null;
150
+ for (var depth = $pos.depth; depth >= 0; depth--) {
151
+ var node = $pos.node(depth);
152
+ if (listTypes.includes(node.type)) {
153
+ rootListPos = $pos.before(depth);
154
+ rootListNode = node;
155
+ } else if (rootListNode !== null && node.type !== schema.nodes.listItem) {
156
+ // We've exited the list structure — stop walking.
157
+ break;
158
+ }
159
+ }
160
+ if (rootListPos !== null && rootListNode !== null) {
161
+ result.set(rootListPos, rootListNode);
147
162
  }
148
163
  }
149
164
  }
@@ -180,10 +195,14 @@ function applyListNormalisationFixes(_ref) {
180
195
  }
181
196
  var _schema$nodes2 = schema.nodes,
182
197
  listItem = _schema$nodes2.listItem,
183
- paragraph = _schema$nodes2.paragraph;
198
+ paragraph = _schema$nodes2.paragraph,
199
+ bulletList = _schema$nodes2.bulletList,
200
+ orderedList = _schema$nodes2.orderedList,
201
+ taskList = _schema$nodes2.taskList;
184
202
  if (!listItem) {
185
203
  return tr;
186
204
  }
205
+ var nestedListTypes = [bulletList, orderedList, taskList].filter(Boolean);
187
206
 
188
207
  // Process lists in reverse position order so fixes at higher positions
189
208
  // don't shift the positions of fixes at lower positions.
@@ -198,16 +217,22 @@ function applyListNormalisationFixes(_ref) {
198
217
  _step3;
199
218
  try {
200
219
  var _loop = function _loop() {
201
- var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2),
202
- listPos = _step3$value[0],
203
- listNode = _step3$value[1];
204
- // Collect listItem positions in document order, then process in reverse so that
205
- // fixes at higher positions don't shift positions of fixes at lower positions.
220
+ var _step3$value = (0, _slicedToArray2.default)(_step3.value, 1),
221
+ listPos = _step3$value[0];
222
+ // Re-resolve the list node from the current transaction doc (post-paste state),
223
+ // as the original listNode snapshot may be stale after the paste transaction.
224
+ var mappedListPos = tr.mapping.map(listPos);
225
+ var currentListNode = tr.doc.nodeAt(mappedListPos);
226
+ if (!currentListNode) {
227
+ return 1; // continue
228
+ }
229
+
230
+ // Collect all listItem positions at all depths in document order, then process in
231
+ // reverse so that fixes at higher positions don't shift positions of lower ones.
206
232
  var listItemPositions = [];
207
- listNode.descendants(function (node, offsetPos) {
233
+ currentListNode.descendants(function (node, offsetPos) {
208
234
  if (node.type === listItem) {
209
- listItemPositions.push(listPos + 1 + offsetPos);
210
- return false; // Don't descend — inner listItems are handled via their own ancestor list
235
+ listItemPositions.push(mappedListPos + 1 + offsetPos);
211
236
  }
212
237
  return true;
213
238
  });
@@ -237,20 +262,25 @@ function applyListNormalisationFixes(_ref) {
237
262
  }
238
263
  }
239
264
 
240
- // Insert empty paragraph before list-first listItems when _indentation is off.
265
+ // Insert empty paragraph before a list-type first child when _indentation is off.
266
+ // Only list types (bulletList, orderedList, taskList) are invalid as a first child —
267
+ // other non-paragraph types (mediaSingle, codeBlock, extension) are valid per the schema.
241
268
  if (paragraph && !(0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
242
- var currentNode = tr.doc.nodeAt(mappedPos);
243
- if (currentNode && currentNode.firstChild && currentNode.firstChild.type !== paragraph) {
269
+ // Re-map position after any join steps that may have been added above.
270
+ var remappedPos = tr.mapping.map(listItemPositions[i]);
271
+ var currentNode = tr.doc.nodeAt(remappedPos);
272
+ var firstChild = currentNode === null || currentNode === void 0 ? void 0 : currentNode.firstChild;
273
+ if (firstChild && nestedListTypes.includes(firstChild.type)) {
244
274
  var emptyParagraph = paragraph.createAndFill();
245
275
  if (emptyParagraph) {
246
- tr.insert(mappedPos + 1, emptyParagraph);
276
+ tr.insert(remappedPos + 1, emptyParagraph);
247
277
  }
248
278
  }
249
279
  }
250
280
  }
251
281
  };
252
282
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
253
- _loop();
283
+ if (_loop()) continue;
254
284
  }
255
285
  } catch (err) {
256
286
  _iterator3.e(err);
@@ -5,6 +5,7 @@ import { getItemCounterDigitsSize, isListNode, pluginFactory } from '@atlaskit/e
5
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
6
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
7
7
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
8
9
  import { applyListNormalisationFixes } from './transforms';
9
10
  import { isWrappingPossible } from './utils/selection';
10
11
  const listPluginKey = new PluginKey('listPlugin');
@@ -133,19 +134,21 @@ export const createPlugin = (eventDispatch, featureFlags, api) => {
133
134
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
134
135
  key: listPluginKey,
135
136
  appendTransaction(transactions, _oldState, newState) {
136
- // The paste plugin sets 'listPasteNormalisation' on the transaction when it detects
137
- // a list is being pasted into a list (and the flexible list schema flag is on).
138
- // We use this meta to cheaply trigger normalisation without re-checking flags.
139
- if (transactions.some(t => t.getMeta('listPasteNormalisation'))) {
140
- const tr = applyListNormalisationFixes({
141
- tr: newState.tr,
142
- transactions,
143
- doc: newState.doc,
144
- schema: newState.schema
145
- });
146
- if (tr.docChanged) {
147
- return tr;
148
- }
137
+ if (!expValEqualsNoExposure('platform_editor_flexible_list_schema', 'isEnabled', true)) {
138
+ return null;
139
+ }
140
+ if (!transactions.some(t => t.docChanged)) {
141
+ return null;
142
+ }
143
+ // Efficiently scans only affected list nodes — exits early if none are found.
144
+ const tr = applyListNormalisationFixes({
145
+ tr: newState.tr,
146
+ transactions,
147
+ doc: newState.doc,
148
+ schema: newState.schema
149
+ });
150
+ if (tr.docChanged) {
151
+ return tr;
149
152
  }
150
153
  return null;
151
154
  },
@@ -2,7 +2,6 @@ import { isListNode } from '@atlaskit/editor-common/utils';
2
2
  import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
3
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { liftTarget, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
5
- import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
6
5
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
6
  import { getListLiftTarget } from './utils/indentation';
8
7
  function liftListItem(selection, tr) {
@@ -124,13 +123,29 @@ function getAffectedListsFromTransactions(transactions, doc, schema) {
124
123
  if (!(step instanceof ReplaceStep) && !(step instanceof ReplaceAroundStep)) {
125
124
  continue;
126
125
  }
127
- // Check both the start and end of each changed range, mapped to post-paste positions.
126
+ // Check both the start and end of each changed range, mapped to post-transaction positions.
128
127
  for (const rawPos of [step.from, step.to]) {
129
128
  const mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
130
129
  const $pos = doc.resolve(mappedPos);
131
- const ancestor = findParentNodeOfTypeClosestToPos($pos, listTypes);
132
- if (ancestor) {
133
- result.set(ancestor.pos, ancestor.node);
130
+ // Walk ancestors from inner to outer, recording the outermost list node.
131
+ // Once we find a list and then exit list structure (hit a non-list ancestor),
132
+ // break early — prevents container nodes (e.g. panel) from causing us to
133
+ // return an outer list that is in a different structural context.
134
+ // $pos.node(depth) is O(1) array access.
135
+ let rootListPos = null;
136
+ let rootListNode = null;
137
+ for (let depth = $pos.depth; depth >= 0; depth--) {
138
+ const node = $pos.node(depth);
139
+ if (listTypes.includes(node.type)) {
140
+ rootListPos = $pos.before(depth);
141
+ rootListNode = node;
142
+ } else if (rootListNode !== null && node.type !== schema.nodes.listItem) {
143
+ // We've exited the list structure — stop walking.
144
+ break;
145
+ }
146
+ }
147
+ if (rootListPos !== null && rootListNode !== null) {
148
+ result.set(rootListPos, rootListNode);
134
149
  }
135
150
  }
136
151
  }
@@ -158,23 +173,34 @@ export function applyListNormalisationFixes({
158
173
  }
159
174
  const {
160
175
  listItem,
161
- paragraph
176
+ paragraph,
177
+ bulletList,
178
+ orderedList,
179
+ taskList
162
180
  } = schema.nodes;
163
181
  if (!listItem) {
164
182
  return tr;
165
183
  }
184
+ const nestedListTypes = [bulletList, orderedList, taskList].filter(Boolean);
166
185
 
167
186
  // Process lists in reverse position order so fixes at higher positions
168
187
  // don't shift the positions of fixes at lower positions.
169
188
  const sortedEntries = [...affectedLists.entries()].sort(([posA], [posB]) => posB - posA);
170
- for (const [listPos, listNode] of sortedEntries) {
171
- // Collect listItem positions in document order, then process in reverse so that
172
- // fixes at higher positions don't shift positions of fixes at lower positions.
189
+ for (const [listPos] of sortedEntries) {
190
+ // Re-resolve the list node from the current transaction doc (post-paste state),
191
+ // as the original listNode snapshot may be stale after the paste transaction.
192
+ const mappedListPos = tr.mapping.map(listPos);
193
+ const currentListNode = tr.doc.nodeAt(mappedListPos);
194
+ if (!currentListNode) {
195
+ continue;
196
+ }
197
+
198
+ // Collect all listItem positions at all depths in document order, then process in
199
+ // reverse so that fixes at higher positions don't shift positions of lower ones.
173
200
  const listItemPositions = [];
174
- listNode.descendants((node, offsetPos) => {
201
+ currentListNode.descendants((node, offsetPos) => {
175
202
  if (node.type === listItem) {
176
- listItemPositions.push(listPos + 1 + offsetPos);
177
- return false; // Don't descend — inner listItems are handled via their own ancestor list
203
+ listItemPositions.push(mappedListPos + 1 + offsetPos);
178
204
  }
179
205
  return true;
180
206
  });
@@ -204,13 +230,18 @@ export function applyListNormalisationFixes({
204
230
  }
205
231
  }
206
232
 
207
- // Insert empty paragraph before list-first listItems when _indentation is off.
233
+ // Insert empty paragraph before a list-type first child when _indentation is off.
234
+ // Only list types (bulletList, orderedList, taskList) are invalid as a first child —
235
+ // other non-paragraph types (mediaSingle, codeBlock, extension) are valid per the schema.
208
236
  if (paragraph && !expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
209
- const currentNode = tr.doc.nodeAt(mappedPos);
210
- if (currentNode && currentNode.firstChild && currentNode.firstChild.type !== paragraph) {
237
+ // Re-map position after any join steps that may have been added above.
238
+ const remappedPos = tr.mapping.map(listItemPositions[i]);
239
+ const currentNode = tr.doc.nodeAt(remappedPos);
240
+ const firstChild = currentNode === null || currentNode === void 0 ? void 0 : currentNode.firstChild;
241
+ if (firstChild && nestedListTypes.includes(firstChild.type)) {
211
242
  const emptyParagraph = paragraph.createAndFill();
212
243
  if (emptyParagraph) {
213
- tr.insert(mappedPos + 1, emptyParagraph);
244
+ tr.insert(remappedPos + 1, emptyParagraph);
214
245
  }
215
246
  }
216
247
  }
@@ -8,6 +8,7 @@ import { getItemCounterDigitsSize, isListNode, pluginFactory } from '@atlaskit/e
8
8
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
11
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
11
12
  import { applyListNormalisationFixes } from './transforms';
12
13
  import { isWrappingPossible } from './utils/selection';
13
14
  var listPluginKey = new PluginKey('listPlugin');
@@ -134,21 +135,23 @@ export var createPlugin = function createPlugin(eventDispatch, featureFlags, api
134
135
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
135
136
  key: listPluginKey,
136
137
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
137
- // The paste plugin sets 'listPasteNormalisation' on the transaction when it detects
138
- // a list is being pasted into a list (and the flexible list schema flag is on).
139
- // We use this meta to cheaply trigger normalisation without re-checking flags.
140
- if (transactions.some(function (t) {
141
- return t.getMeta('listPasteNormalisation');
138
+ if (!expValEqualsNoExposure('platform_editor_flexible_list_schema', 'isEnabled', true)) {
139
+ return null;
140
+ }
141
+ if (!transactions.some(function (t) {
142
+ return t.docChanged;
142
143
  })) {
143
- var tr = applyListNormalisationFixes({
144
- tr: newState.tr,
145
- transactions: transactions,
146
- doc: newState.doc,
147
- schema: newState.schema
148
- });
149
- if (tr.docChanged) {
150
- return tr;
151
- }
144
+ return null;
145
+ }
146
+ // Efficiently scans only affected list nodes — exits early if none are found.
147
+ var tr = applyListNormalisationFixes({
148
+ tr: newState.tr,
149
+ transactions: transactions,
150
+ doc: newState.doc,
151
+ schema: newState.schema
152
+ });
153
+ if (tr.docChanged) {
154
+ return tr;
152
155
  }
153
156
  return null;
154
157
  },
@@ -7,7 +7,6 @@ import { isListNode } from '@atlaskit/editor-common/utils';
7
7
  import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
8
8
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { liftTarget, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
10
- import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
11
10
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
11
  import { getListLiftTarget } from './utils/indentation';
13
12
  function liftListItem(selection, tr) {
@@ -126,14 +125,30 @@ function getAffectedListsFromTransactions(transactions, doc, schema) {
126
125
  if (!(step instanceof ReplaceStep) && !(step instanceof ReplaceAroundStep)) {
127
126
  continue;
128
127
  }
129
- // Check both the start and end of each changed range, mapped to post-paste positions.
128
+ // Check both the start and end of each changed range, mapped to post-transaction positions.
130
129
  for (var _i = 0, _arr = [step.from, step.to]; _i < _arr.length; _i++) {
131
130
  var rawPos = _arr[_i];
132
131
  var mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
133
132
  var $pos = doc.resolve(mappedPos);
134
- var ancestor = findParentNodeOfTypeClosestToPos($pos, listTypes);
135
- if (ancestor) {
136
- result.set(ancestor.pos, ancestor.node);
133
+ // Walk ancestors from inner to outer, recording the outermost list node.
134
+ // Once we find a list and then exit list structure (hit a non-list ancestor),
135
+ // break early — prevents container nodes (e.g. panel) from causing us to
136
+ // return an outer list that is in a different structural context.
137
+ // $pos.node(depth) is O(1) array access.
138
+ var rootListPos = null;
139
+ var rootListNode = null;
140
+ for (var depth = $pos.depth; depth >= 0; depth--) {
141
+ var node = $pos.node(depth);
142
+ if (listTypes.includes(node.type)) {
143
+ rootListPos = $pos.before(depth);
144
+ rootListNode = node;
145
+ } else if (rootListNode !== null && node.type !== schema.nodes.listItem) {
146
+ // We've exited the list structure — stop walking.
147
+ break;
148
+ }
149
+ }
150
+ if (rootListPos !== null && rootListNode !== null) {
151
+ result.set(rootListPos, rootListNode);
137
152
  }
138
153
  }
139
154
  }
@@ -170,10 +185,14 @@ export function applyListNormalisationFixes(_ref) {
170
185
  }
171
186
  var _schema$nodes2 = schema.nodes,
172
187
  listItem = _schema$nodes2.listItem,
173
- paragraph = _schema$nodes2.paragraph;
188
+ paragraph = _schema$nodes2.paragraph,
189
+ bulletList = _schema$nodes2.bulletList,
190
+ orderedList = _schema$nodes2.orderedList,
191
+ taskList = _schema$nodes2.taskList;
174
192
  if (!listItem) {
175
193
  return tr;
176
194
  }
195
+ var nestedListTypes = [bulletList, orderedList, taskList].filter(Boolean);
177
196
 
178
197
  // Process lists in reverse position order so fixes at higher positions
179
198
  // don't shift the positions of fixes at lower positions.
@@ -188,16 +207,22 @@ export function applyListNormalisationFixes(_ref) {
188
207
  _step3;
189
208
  try {
190
209
  var _loop = function _loop() {
191
- var _step3$value = _slicedToArray(_step3.value, 2),
192
- listPos = _step3$value[0],
193
- listNode = _step3$value[1];
194
- // Collect listItem positions in document order, then process in reverse so that
195
- // fixes at higher positions don't shift positions of fixes at lower positions.
210
+ var _step3$value = _slicedToArray(_step3.value, 1),
211
+ listPos = _step3$value[0];
212
+ // Re-resolve the list node from the current transaction doc (post-paste state),
213
+ // as the original listNode snapshot may be stale after the paste transaction.
214
+ var mappedListPos = tr.mapping.map(listPos);
215
+ var currentListNode = tr.doc.nodeAt(mappedListPos);
216
+ if (!currentListNode) {
217
+ return 1; // continue
218
+ }
219
+
220
+ // Collect all listItem positions at all depths in document order, then process in
221
+ // reverse so that fixes at higher positions don't shift positions of lower ones.
196
222
  var listItemPositions = [];
197
- listNode.descendants(function (node, offsetPos) {
223
+ currentListNode.descendants(function (node, offsetPos) {
198
224
  if (node.type === listItem) {
199
- listItemPositions.push(listPos + 1 + offsetPos);
200
- return false; // Don't descend — inner listItems are handled via their own ancestor list
225
+ listItemPositions.push(mappedListPos + 1 + offsetPos);
201
226
  }
202
227
  return true;
203
228
  });
@@ -227,20 +252,25 @@ export function applyListNormalisationFixes(_ref) {
227
252
  }
228
253
  }
229
254
 
230
- // Insert empty paragraph before list-first listItems when _indentation is off.
255
+ // Insert empty paragraph before a list-type first child when _indentation is off.
256
+ // Only list types (bulletList, orderedList, taskList) are invalid as a first child —
257
+ // other non-paragraph types (mediaSingle, codeBlock, extension) are valid per the schema.
231
258
  if (paragraph && !expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
232
- var currentNode = tr.doc.nodeAt(mappedPos);
233
- if (currentNode && currentNode.firstChild && currentNode.firstChild.type !== paragraph) {
259
+ // Re-map position after any join steps that may have been added above.
260
+ var remappedPos = tr.mapping.map(listItemPositions[i]);
261
+ var currentNode = tr.doc.nodeAt(remappedPos);
262
+ var firstChild = currentNode === null || currentNode === void 0 ? void 0 : currentNode.firstChild;
263
+ if (firstChild && nestedListTypes.includes(firstChild.type)) {
234
264
  var emptyParagraph = paragraph.createAndFill();
235
265
  if (emptyParagraph) {
236
- tr.insert(mappedPos + 1, emptyParagraph);
266
+ tr.insert(remappedPos + 1, emptyParagraph);
237
267
  }
238
268
  }
239
269
  }
240
270
  }
241
271
  };
242
272
  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
243
- _loop();
273
+ if (_loop()) continue;
244
274
  }
245
275
  } catch (err) {
246
276
  _iterator3.e(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-list",
3
- "version": "12.0.0",
3
+ "version": "12.0.2",
4
4
  "description": "List plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -38,11 +38,11 @@
38
38
  "@atlaskit/platform-feature-flags": "^1.1.0",
39
39
  "@atlaskit/prosemirror-history": "^0.2.0",
40
40
  "@atlaskit/prosemirror-input-rules": "^3.6.0",
41
- "@atlaskit/tmp-editor-statsig": "^62.4.0",
41
+ "@atlaskit/tmp-editor-statsig": "^63.0.0",
42
42
  "@babel/runtime": "^7.0.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@atlaskit/editor-common": "^114.0.0",
45
+ "@atlaskit/editor-common": "^114.3.0",
46
46
  "react": "^18.2.0",
47
47
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
48
48
  },