@atlaskit/editor-plugin-list 11.0.0 → 12.0.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 CHANGED
@@ -1,5 +1,38 @@
1
1
  # @atlaskit/editor-plugin-list
2
2
 
3
+ ## 12.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`901c87a57486e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/901c87a57486e) -
8
+ Removed `react-intl-next` alias and replaced all usages with `react-intl` directly.
9
+
10
+ What changed: The `react-intl-next` npm alias (which resolved to `react-intl@^5`) has been
11
+ removed. All imports now reference `react-intl` directly, and `peerDependencies` have been updated
12
+ to `"^5.25.1 || ^6.0.0 || ^7.0.0"`.
13
+
14
+ How consumer should update their code: Ensure `react-intl` is installed at a version satisfying
15
+ `^5.25.1 || ^6.0.0 || ^7.0.0`. If your application was using `react-intl-next` as an npm alias, it
16
+ can be safely removed. Replace any remaining `react-intl-next` imports with `react-intl`.
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
22
+ ## 11.0.1
23
+
24
+ ### Patch Changes
25
+
26
+ - [`bae5d569f660c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bae5d569f660c) -
27
+ Prevent flexible list creation when platform_editor_flexible_list_schema is on but
28
+ platform_editor_flexible_list_indentation is off. Slice normalisation in paste pipeline.
29
+ appendTransaction normaliser in list plugin.
30
+
31
+ Paste plugin detects list-into-list paste and sets a transaction meta to skip closeHistory,
32
+ keeping the paste and normalisation appendTransaction as a single undo step.
33
+
34
+ - Updated dependencies
35
+
3
36
  ## 11.0.0
4
37
 
5
38
  ### Patch Changes
@@ -11,7 +11,7 @@ var _lists = require("@atlaskit/editor-common/lists");
11
11
  var _preset = require("@atlaskit/editor-common/preset");
12
12
  var _utils = require("@atlaskit/editor-common/utils");
13
13
  var _prosemirrorHistory = require("@atlaskit/prosemirror-history");
14
- var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
14
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
15
  var _types = require("../../types");
16
16
  var _indentListItemsSelected = require("../actions/indent-list-items-selected");
17
17
  var _moveSelectedListItems = require("../actions/move-selected-list-items");
@@ -61,7 +61,7 @@ var indentList = exports.indentList = function indentList(editorAnalyticsAPI) {
61
61
  (0, _prosemirrorHistory.closeHistory)(tr);
62
62
 
63
63
  // Route to new or original implementation based on feature flag
64
- if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
64
+ if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
65
65
  return handleIndentListItems(tr, editorAnalyticsAPI, inputMethod);
66
66
  }
67
67
  var firstListItemSelectedAttributes = (0, _lists.getListItemAttributes)($from);
@@ -11,7 +11,7 @@ var _lists = require("@atlaskit/editor-common/lists");
11
11
  var _preset = require("@atlaskit/editor-common/preset");
12
12
  var _utils = require("@atlaskit/editor-common/utils");
13
13
  var _prosemirrorHistory = require("@atlaskit/prosemirror-history");
14
- var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
14
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
15
  var _moveSelectedListItems = require("../actions/move-selected-list-items");
16
16
  var _outdentListItemsSelected = require("../actions/outdent-list-items-selected");
17
17
  var _analytics2 = require("../utils/analytics");
@@ -79,7 +79,7 @@ var outdentList = exports.outdentList = function outdentList(editorAnalyticsAPI)
79
79
  (0, _prosemirrorHistory.closeHistory)(tr);
80
80
 
81
81
  // Route to new or original implementation based on feature flag
82
- if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
82
+ if ((0, _expValEquals.expValEquals)('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
83
83
  return handleOutdentListItems(tr, editorAnalyticsAPI, inputMethod);
84
84
  }
85
85
  var actionSubjectId = (0, _utils.isBulletList)(parentListNode.node) ? _analytics.ACTION_SUBJECT_ID.FORMAT_LIST_BULLET : _analytics.ACTION_SUBJECT_ID.FORMAT_LIST_NUMBER;
@@ -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 _transforms = require("./transforms");
16
17
  var _selection2 = require("./utils/selection");
17
18
  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; }
18
19
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -139,6 +140,25 @@ var createPlugin = exports.createPlugin = function createPlugin(eventDispatch, f
139
140
  return new _safePlugin.SafePlugin({
140
141
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
141
142
  key: listPluginKey,
143
+ 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');
149
+ })) {
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
+ }
159
+ }
160
+ return null;
161
+ },
142
162
  props: {
143
163
  decorations: function decorations(state) {
144
164
  var _getPluginState = getPluginState(state),
@@ -158,7 +178,8 @@ var createPlugin = exports.createPlugin = function createPlugin(eventDispatch, f
158
178
  if ((nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) === listItem && (nodeAtPos === null || nodeAtPos === void 0 || (_nodeAtPos$firstChild = nodeAtPos.firstChild) === null || _nodeAtPos$firstChild === void 0 ? void 0 : _nodeAtPos$firstChild.type) === codeBlock) {
159
179
  var _document;
160
180
  var bufferPx = 50;
161
- var isCodeBlockNextToListMarker = Boolean((_document = document) === null || _document === void 0 || (_document = _document.elementFromPoint(event.clientX + (_styles.listItemCounterPadding + bufferPx), event.clientY)) === null || _document === void 0 ? void 0 : _document.closest(".".concat(_styles.CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER)));
181
+ var isCodeBlockNextToListMarker = Boolean( // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
182
+ (_document = document) === null || _document === void 0 || (_document = _document.elementFromPoint(event.clientX + (_styles.listItemCounterPadding + bufferPx), event.clientY)) === null || _document === void 0 ? void 0 : _document.closest(".".concat(_styles.CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER)));
162
183
  if (isCodeBlockNextToListMarker) {
163
184
  // +1 needed to put cursor inside li
164
185
  // otherwise gap cursor markup will be injected as immediate child of ul resulting in invalid html
@@ -1,15 +1,25 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
7
+ exports.applyListNormalisationFixes = applyListNormalisationFixes;
6
8
  exports.liftFollowingList = liftFollowingList;
7
9
  exports.liftNodeSelectionList = liftNodeSelectionList;
8
10
  exports.liftTextSelectionList = liftTextSelectionList;
11
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
+ var _utils = require("@atlaskit/editor-common/utils");
9
14
  var _model = require("@atlaskit/editor-prosemirror/model");
10
15
  var _state = require("@atlaskit/editor-prosemirror/state");
11
16
  var _transform = require("@atlaskit/editor-prosemirror/transform");
17
+ var _utils2 = require("@atlaskit/editor-prosemirror/utils");
18
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
12
19
  var _indentation = require("./utils/indentation");
20
+ 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; } } }; }
21
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
22
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
13
23
  function liftListItem(selection, tr) {
14
24
  var $from = selection.$from,
15
25
  $to = selection.$to;
@@ -96,4 +106,156 @@ function liftTextSelectionList(selection, tr) {
96
106
  }
97
107
  }
98
108
  return tr;
109
+ }
110
+
111
+ /**
112
+ * Finds the top-level list nodes (bulletList/orderedList) that contain the positions
113
+ * affected by the given transactions. Returns a map of list node position → list node,
114
+ * so callers can scan only the affected subtrees rather than the entire document.
115
+ */
116
+ function getAffectedListsFromTransactions(transactions, doc, schema) {
117
+ var _schema$nodes = schema.nodes,
118
+ bulletList = _schema$nodes.bulletList,
119
+ orderedList = _schema$nodes.orderedList;
120
+ var listTypes = [bulletList, orderedList].filter(Boolean);
121
+ if (listTypes.length === 0) {
122
+ return new Map();
123
+ }
124
+ var result = new Map();
125
+ var _iterator = _createForOfIteratorHelper(transactions),
126
+ _step;
127
+ try {
128
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
129
+ var tr = _step.value;
130
+ var _iterator2 = _createForOfIteratorHelper(tr.steps),
131
+ _step2;
132
+ try {
133
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
134
+ var step = _step2.value;
135
+ // ReplaceStep and ReplaceAroundStep both have from/to — other step types are skipped.
136
+ if (!(step instanceof _transform.ReplaceStep) && !(step instanceof _transform.ReplaceAroundStep)) {
137
+ continue;
138
+ }
139
+ // Check both the start and end of each changed range, mapped to post-paste positions.
140
+ for (var _i = 0, _arr = [step.from, step.to]; _i < _arr.length; _i++) {
141
+ var rawPos = _arr[_i];
142
+ var mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
143
+ var $pos = doc.resolve(mappedPos);
144
+ var ancestor = (0, _utils2.findParentNodeOfTypeClosestToPos)($pos, listTypes);
145
+ if (ancestor) {
146
+ result.set(ancestor.pos, ancestor.node);
147
+ }
148
+ }
149
+ }
150
+ } catch (err) {
151
+ _iterator2.e(err);
152
+ } finally {
153
+ _iterator2.f();
154
+ }
155
+ }
156
+ } catch (err) {
157
+ _iterator.e(err);
158
+ } finally {
159
+ _iterator.f();
160
+ }
161
+ return result;
162
+ }
163
+ /**
164
+ * Applies list normalisation fixes to the given transaction for all affected list subtrees.
165
+ * Processes nodes in reverse document order so that position offsets from insertions/joins
166
+ * do not affect earlier positions.
167
+ *
168
+ * When platform_editor_flexible_list_indentation is off: inserts an empty paragraph before any listItem whose
169
+ * first child is a list node, and merges adjacent same-type list nodes within a listItem.
170
+ * When platform_editor_flexible_list_indentation is on: only merges adjacent same-type list nodes.
171
+ */
172
+ function applyListNormalisationFixes(_ref) {
173
+ var tr = _ref.tr,
174
+ transactions = _ref.transactions,
175
+ doc = _ref.doc,
176
+ schema = _ref.schema;
177
+ var affectedLists = getAffectedListsFromTransactions(transactions, doc, schema);
178
+ if (affectedLists.size === 0) {
179
+ return tr;
180
+ }
181
+ var _schema$nodes2 = schema.nodes,
182
+ listItem = _schema$nodes2.listItem,
183
+ paragraph = _schema$nodes2.paragraph;
184
+ if (!listItem) {
185
+ return tr;
186
+ }
187
+
188
+ // Process lists in reverse position order so fixes at higher positions
189
+ // don't shift the positions of fixes at lower positions.
190
+ var sortedEntries = (0, _toConsumableArray2.default)(affectedLists.entries()).sort(function (_ref2, _ref3) {
191
+ var _ref4 = (0, _slicedToArray2.default)(_ref2, 1),
192
+ posA = _ref4[0];
193
+ var _ref5 = (0, _slicedToArray2.default)(_ref3, 1),
194
+ posB = _ref5[0];
195
+ return posB - posA;
196
+ });
197
+ var _iterator3 = _createForOfIteratorHelper(sortedEntries),
198
+ _step3;
199
+ try {
200
+ 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.
206
+ var listItemPositions = [];
207
+ listNode.descendants(function (node, offsetPos) {
208
+ 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
211
+ }
212
+ return true;
213
+ });
214
+ for (var i = listItemPositions.length - 1; i >= 0; i--) {
215
+ var mappedPos = tr.mapping.map(listItemPositions[i]);
216
+ var node = tr.doc.nodeAt(mappedPos);
217
+ if (!node || node.type !== listItem) {
218
+ continue;
219
+ }
220
+
221
+ // Merge adjacent same-type list nodes (highest boundary first within the listItem).
222
+ for (var j = node.childCount - 1; j > 0; j--) {
223
+ var child = node.child(j);
224
+ var prevChild = node.child(j - 1);
225
+ if ((0, _utils.isListNode)(child) && child.type === prevChild.type) {
226
+ var offset = 1; // +1 for listItem opening token
227
+ for (var k = 0; k < j; k++) {
228
+ offset += node.child(k).nodeSize;
229
+ }
230
+ try {
231
+ tr.join(mappedPos + offset);
232
+ } catch (e) {
233
+ // join may fail if position is invalid after earlier transforms — skip
234
+ // eslint-disable-next-line no-console
235
+ console.warn('[editor-plugin-list] applyListNormalisationFixes: unexpected join failure', e);
236
+ }
237
+ }
238
+ }
239
+
240
+ // Insert empty paragraph before list-first listItems when _indentation is off.
241
+ 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) {
244
+ var emptyParagraph = paragraph.createAndFill();
245
+ if (emptyParagraph) {
246
+ tr.insert(mappedPos + 1, emptyParagraph);
247
+ }
248
+ }
249
+ }
250
+ }
251
+ };
252
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
253
+ _loop();
254
+ }
255
+ } catch (err) {
256
+ _iterator3.e(err);
257
+ } finally {
258
+ _iterator3.f();
259
+ }
260
+ return tr;
99
261
  }
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.createBulletedListBlockMenuItem = void 0;
8
8
  var _react = _interopRequireDefault(require("react"));
9
- var _reactIntlNext = require("react-intl-next");
9
+ var _reactIntl = require("react-intl");
10
10
  var _analytics = require("@atlaskit/editor-common/analytics");
11
11
  var _messages = require("@atlaskit/editor-common/messages");
12
12
  var _editorToolbar = require("@atlaskit/editor-toolbar");
@@ -14,7 +14,7 @@ var _listBulleted = _interopRequireDefault(require("@atlaskit/icon/core/list-bul
14
14
  var NODE_NAME = 'bulletList';
15
15
  var BulletedListBlockMenuItem = function BulletedListBlockMenuItem(_ref) {
16
16
  var api = _ref.api;
17
- var _useIntl = (0, _reactIntlNext.useIntl)(),
17
+ var _useIntl = (0, _reactIntl.useIntl)(),
18
18
  formatMessage = _useIntl.formatMessage;
19
19
  var handleClick = function handleClick(event) {
20
20
  var triggeredFrom = event.nativeEvent instanceof KeyboardEvent || event.nativeEvent.detail === 0 ? _analytics.INPUT_METHOD.KEYBOARD : _analytics.INPUT_METHOD.MOUSE;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.createNumberedListBlockMenuItem = void 0;
8
8
  var _react = _interopRequireDefault(require("react"));
9
- var _reactIntlNext = require("react-intl-next");
9
+ var _reactIntl = require("react-intl");
10
10
  var _analytics = require("@atlaskit/editor-common/analytics");
11
11
  var _messages = require("@atlaskit/editor-common/messages");
12
12
  var _editorToolbar = require("@atlaskit/editor-toolbar");
@@ -14,7 +14,7 @@ var _listNumbered = _interopRequireDefault(require("@atlaskit/icon/core/list-num
14
14
  var NODE_NAME = 'orderedList';
15
15
  var NumberedListBlockMenuItem = function NumberedListBlockMenuItem(_ref) {
16
16
  var api = _ref.api;
17
- var _useIntl = (0, _reactIntlNext.useIntl)(),
17
+ var _useIntl = (0, _reactIntl.useIntl)(),
18
18
  formatMessage = _useIntl.formatMessage;
19
19
  var handleClick = function handleClick(event) {
20
20
  var triggeredFrom = event.nativeEvent instanceof KeyboardEvent || event.nativeEvent.detail === 0 ? _analytics.INPUT_METHOD.KEYBOARD : _analytics.INPUT_METHOD.MOUSE;
@@ -3,7 +3,7 @@ import { getCommonListAnalyticsAttributes, getListItemAttributes, hasValidListIn
3
3
  import { PassiveTransaction } from '@atlaskit/editor-common/preset';
4
4
  import { isBulletList } from '@atlaskit/editor-common/utils';
5
5
  import { closeHistory } from '@atlaskit/prosemirror-history';
6
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
6
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
7
  import { MAX_NESTED_LIST_INDENTATION } from '../../types';
8
8
  import { indentListItemsSelected as indentListAction } from '../actions/indent-list-items-selected';
9
9
  import { moveSelectedListItems } from '../actions/move-selected-list-items';
@@ -59,7 +59,7 @@ export const indentList = editorAnalyticsAPI => (inputMethod = INPUT_METHOD.KEYB
59
59
  closeHistory(tr);
60
60
 
61
61
  // Route to new or original implementation based on feature flag
62
- if (expValEqualsNoExposure('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
62
+ if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
63
63
  return handleIndentListItems(tr, editorAnalyticsAPI, inputMethod);
64
64
  }
65
65
  const firstListItemSelectedAttributes = getListItemAttributes($from);
@@ -3,7 +3,7 @@ import { getCommonListAnalyticsAttributes } from '@atlaskit/editor-common/lists'
3
3
  import { PassiveTransaction } from '@atlaskit/editor-common/preset';
4
4
  import { isBulletList } from '@atlaskit/editor-common/utils';
5
5
  import { closeHistory } from '@atlaskit/prosemirror-history';
6
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
6
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
7
  import { moveSelectedListItems } from '../actions/move-selected-list-items';
8
8
  import { outdentListItemsSelected as outdentListAction } from '../actions/outdent-list-items-selected';
9
9
  import { getRestartListsAttributes } from '../utils/analytics';
@@ -77,7 +77,7 @@ export const outdentList = editorAnalyticsAPI => (inputMethod = INPUT_METHOD.KEY
77
77
  closeHistory(tr);
78
78
 
79
79
  // Route to new or original implementation based on feature flag
80
- if (expValEqualsNoExposure('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
80
+ if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
81
81
  return handleOutdentListItems(tr, editorAnalyticsAPI, inputMethod);
82
82
  }
83
83
  const actionSubjectId = isBulletList(parentListNode.node) ? ACTION_SUBJECT_ID.FORMAT_LIST_BULLET : ACTION_SUBJECT_ID.FORMAT_LIST_NUMBER;
@@ -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 { applyListNormalisationFixes } from './transforms';
8
9
  import { isWrappingPossible } from './utils/selection';
9
10
  const listPluginKey = new PluginKey('listPlugin');
10
11
  export const pluginKey = listPluginKey;
@@ -131,6 +132,23 @@ export const createPlugin = (eventDispatch, featureFlags, api) => {
131
132
  return new SafePlugin({
132
133
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
133
134
  key: listPluginKey,
135
+ 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
+ }
149
+ }
150
+ return null;
151
+ },
134
152
  props: {
135
153
  decorations(state) {
136
154
  const {
@@ -154,7 +172,8 @@ export const createPlugin = (eventDispatch, featureFlags, api) => {
154
172
  if ((nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) === listItem && (nodeAtPos === null || nodeAtPos === void 0 ? void 0 : (_nodeAtPos$firstChild = nodeAtPos.firstChild) === null || _nodeAtPos$firstChild === void 0 ? void 0 : _nodeAtPos$firstChild.type) === codeBlock) {
155
173
  var _document, _document$elementFrom;
156
174
  const bufferPx = 50;
157
- const isCodeBlockNextToListMarker = Boolean((_document = document) === null || _document === void 0 ? void 0 : (_document$elementFrom = _document.elementFromPoint(event.clientX + (listItemCounterPadding + bufferPx), event.clientY)) === null || _document$elementFrom === void 0 ? void 0 : _document$elementFrom.closest(`.${CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER}`));
175
+ const isCodeBlockNextToListMarker = Boolean( // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
176
+ (_document = document) === null || _document === void 0 ? void 0 : (_document$elementFrom = _document.elementFromPoint(event.clientX + (listItemCounterPadding + bufferPx), event.clientY)) === null || _document$elementFrom === void 0 ? void 0 : _document$elementFrom.closest(`.${CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER}`));
158
177
  if (isCodeBlockNextToListMarker) {
159
178
  // +1 needed to put cursor inside li
160
179
  // otherwise gap cursor markup will be injected as immediate child of ul resulting in invalid html
@@ -1,6 +1,9 @@
1
+ import { isListNode } from '@atlaskit/editor-common/utils';
1
2
  import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
2
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
- import { liftTarget, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
4
+ import { liftTarget, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
5
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
6
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
7
  import { getListLiftTarget } from './utils/indentation';
5
8
  function liftListItem(selection, tr) {
6
9
  const {
@@ -98,4 +101,120 @@ export function liftTextSelectionList(selection, tr) {
98
101
  }
99
102
  }
100
103
  return tr;
104
+ }
105
+
106
+ /**
107
+ * Finds the top-level list nodes (bulletList/orderedList) that contain the positions
108
+ * affected by the given transactions. Returns a map of list node position → list node,
109
+ * so callers can scan only the affected subtrees rather than the entire document.
110
+ */
111
+ function getAffectedListsFromTransactions(transactions, doc, schema) {
112
+ const {
113
+ bulletList,
114
+ orderedList
115
+ } = schema.nodes;
116
+ const listTypes = [bulletList, orderedList].filter(Boolean);
117
+ if (listTypes.length === 0) {
118
+ return new Map();
119
+ }
120
+ const result = new Map();
121
+ for (const tr of transactions) {
122
+ for (const step of tr.steps) {
123
+ // ReplaceStep and ReplaceAroundStep both have from/to — other step types are skipped.
124
+ if (!(step instanceof ReplaceStep) && !(step instanceof ReplaceAroundStep)) {
125
+ continue;
126
+ }
127
+ // Check both the start and end of each changed range, mapped to post-paste positions.
128
+ for (const rawPos of [step.from, step.to]) {
129
+ const mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
130
+ const $pos = doc.resolve(mappedPos);
131
+ const ancestor = findParentNodeOfTypeClosestToPos($pos, listTypes);
132
+ if (ancestor) {
133
+ result.set(ancestor.pos, ancestor.node);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return result;
139
+ }
140
+ /**
141
+ * Applies list normalisation fixes to the given transaction for all affected list subtrees.
142
+ * Processes nodes in reverse document order so that position offsets from insertions/joins
143
+ * do not affect earlier positions.
144
+ *
145
+ * When platform_editor_flexible_list_indentation is off: inserts an empty paragraph before any listItem whose
146
+ * first child is a list node, and merges adjacent same-type list nodes within a listItem.
147
+ * When platform_editor_flexible_list_indentation is on: only merges adjacent same-type list nodes.
148
+ */
149
+ export function applyListNormalisationFixes({
150
+ tr,
151
+ transactions,
152
+ doc,
153
+ schema
154
+ }) {
155
+ const affectedLists = getAffectedListsFromTransactions(transactions, doc, schema);
156
+ if (affectedLists.size === 0) {
157
+ return tr;
158
+ }
159
+ const {
160
+ listItem,
161
+ paragraph
162
+ } = schema.nodes;
163
+ if (!listItem) {
164
+ return tr;
165
+ }
166
+
167
+ // Process lists in reverse position order so fixes at higher positions
168
+ // don't shift the positions of fixes at lower positions.
169
+ 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.
173
+ const listItemPositions = [];
174
+ listNode.descendants((node, offsetPos) => {
175
+ 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
178
+ }
179
+ return true;
180
+ });
181
+ for (let i = listItemPositions.length - 1; i >= 0; i--) {
182
+ const mappedPos = tr.mapping.map(listItemPositions[i]);
183
+ const node = tr.doc.nodeAt(mappedPos);
184
+ if (!node || node.type !== listItem) {
185
+ continue;
186
+ }
187
+
188
+ // Merge adjacent same-type list nodes (highest boundary first within the listItem).
189
+ for (let j = node.childCount - 1; j > 0; j--) {
190
+ const child = node.child(j);
191
+ const prevChild = node.child(j - 1);
192
+ if (isListNode(child) && child.type === prevChild.type) {
193
+ let offset = 1; // +1 for listItem opening token
194
+ for (let k = 0; k < j; k++) {
195
+ offset += node.child(k).nodeSize;
196
+ }
197
+ try {
198
+ tr.join(mappedPos + offset);
199
+ } catch (e) {
200
+ // join may fail if position is invalid after earlier transforms — skip
201
+ // eslint-disable-next-line no-console
202
+ console.warn('[editor-plugin-list] applyListNormalisationFixes: unexpected join failure', e);
203
+ }
204
+ }
205
+ }
206
+
207
+ // Insert empty paragraph before list-first listItems when _indentation is off.
208
+ 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) {
211
+ const emptyParagraph = paragraph.createAndFill();
212
+ if (emptyParagraph) {
213
+ tr.insert(mappedPos + 1, emptyParagraph);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ return tr;
101
220
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { useIntl } from 'react-intl-next';
2
+ import { useIntl } from 'react-intl';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { listMessages } from '@atlaskit/editor-common/messages';
5
5
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { useIntl } from 'react-intl-next';
2
+ import { useIntl } from 'react-intl';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { listMessages } from '@atlaskit/editor-common/messages';
5
5
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
@@ -6,7 +6,7 @@ import { getCommonListAnalyticsAttributes, getListItemAttributes, hasValidListIn
6
6
  import { PassiveTransaction } from '@atlaskit/editor-common/preset';
7
7
  import { isBulletList } from '@atlaskit/editor-common/utils';
8
8
  import { closeHistory } from '@atlaskit/prosemirror-history';
9
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
9
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
10
  import { MAX_NESTED_LIST_INDENTATION } from '../../types';
11
11
  import { indentListItemsSelected as indentListAction } from '../actions/indent-list-items-selected';
12
12
  import { moveSelectedListItems } from '../actions/move-selected-list-items';
@@ -54,7 +54,7 @@ export var indentList = function indentList(editorAnalyticsAPI) {
54
54
  closeHistory(tr);
55
55
 
56
56
  // Route to new or original implementation based on feature flag
57
- if (expValEqualsNoExposure('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
57
+ if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
58
58
  return handleIndentListItems(tr, editorAnalyticsAPI, inputMethod);
59
59
  }
60
60
  var firstListItemSelectedAttributes = getListItemAttributes($from);
@@ -6,7 +6,7 @@ import { getCommonListAnalyticsAttributes } from '@atlaskit/editor-common/lists'
6
6
  import { PassiveTransaction } from '@atlaskit/editor-common/preset';
7
7
  import { isBulletList } from '@atlaskit/editor-common/utils';
8
8
  import { closeHistory } from '@atlaskit/prosemirror-history';
9
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
9
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
10
  import { moveSelectedListItems } from '../actions/move-selected-list-items';
11
11
  import { outdentListItemsSelected as outdentListAction } from '../actions/outdent-list-items-selected';
12
12
  import { getRestartListsAttributes } from '../utils/analytics';
@@ -72,7 +72,7 @@ export var outdentList = function outdentList(editorAnalyticsAPI) {
72
72
  closeHistory(tr);
73
73
 
74
74
  // Route to new or original implementation based on feature flag
75
- if (expValEqualsNoExposure('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
75
+ if (expValEquals('platform_editor_flexible_list_indentation', 'isEnabled', true)) {
76
76
  return handleOutdentListItems(tr, editorAnalyticsAPI, inputMethod);
77
77
  }
78
78
  var actionSubjectId = isBulletList(parentListNode.node) ? ACTION_SUBJECT_ID.FORMAT_LIST_BULLET : ACTION_SUBJECT_ID.FORMAT_LIST_NUMBER;
@@ -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 { applyListNormalisationFixes } from './transforms';
11
12
  import { isWrappingPossible } from './utils/selection';
12
13
  var listPluginKey = new PluginKey('listPlugin');
13
14
  export var pluginKey = listPluginKey;
@@ -132,6 +133,25 @@ export var createPlugin = function createPlugin(eventDispatch, featureFlags, api
132
133
  return new SafePlugin({
133
134
  state: createPluginState(eventDispatch, createInitialState(featureFlags, api)),
134
135
  key: listPluginKey,
136
+ 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');
142
+ })) {
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
+ }
152
+ }
153
+ return null;
154
+ },
135
155
  props: {
136
156
  decorations: function decorations(state) {
137
157
  var _getPluginState = getPluginState(state),
@@ -151,7 +171,8 @@ export var createPlugin = function createPlugin(eventDispatch, featureFlags, api
151
171
  if ((nodeAtPos === null || nodeAtPos === void 0 ? void 0 : nodeAtPos.type) === listItem && (nodeAtPos === null || nodeAtPos === void 0 || (_nodeAtPos$firstChild = nodeAtPos.firstChild) === null || _nodeAtPos$firstChild === void 0 ? void 0 : _nodeAtPos$firstChild.type) === codeBlock) {
152
172
  var _document;
153
173
  var bufferPx = 50;
154
- var isCodeBlockNextToListMarker = Boolean((_document = document) === null || _document === void 0 || (_document = _document.elementFromPoint(event.clientX + (listItemCounterPadding + bufferPx), event.clientY)) === null || _document === void 0 ? void 0 : _document.closest(".".concat(CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER)));
174
+ var isCodeBlockNextToListMarker = Boolean( // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
175
+ (_document = document) === null || _document === void 0 || (_document = _document.elementFromPoint(event.clientX + (listItemCounterPadding + bufferPx), event.clientY)) === null || _document === void 0 ? void 0 : _document.closest(".".concat(CodeBlockSharedCssClassName.CODEBLOCK_CONTAINER)));
155
176
  if (isCodeBlockNextToListMarker) {
156
177
  // +1 needed to put cursor inside li
157
178
  // otherwise gap cursor markup will be injected as immediate child of ul resulting in invalid html
@@ -1,6 +1,14 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ 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; } } }; }
4
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
5
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
6
+ import { isListNode } from '@atlaskit/editor-common/utils';
1
7
  import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
2
8
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
3
- import { liftTarget, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
9
+ import { liftTarget, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
10
+ import { findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
11
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
4
12
  import { getListLiftTarget } from './utils/indentation';
5
13
  function liftListItem(selection, tr) {
6
14
  var $from = selection.$from,
@@ -88,4 +96,156 @@ export function liftTextSelectionList(selection, tr) {
88
96
  }
89
97
  }
90
98
  return tr;
99
+ }
100
+
101
+ /**
102
+ * Finds the top-level list nodes (bulletList/orderedList) that contain the positions
103
+ * affected by the given transactions. Returns a map of list node position → list node,
104
+ * so callers can scan only the affected subtrees rather than the entire document.
105
+ */
106
+ function getAffectedListsFromTransactions(transactions, doc, schema) {
107
+ var _schema$nodes = schema.nodes,
108
+ bulletList = _schema$nodes.bulletList,
109
+ orderedList = _schema$nodes.orderedList;
110
+ var listTypes = [bulletList, orderedList].filter(Boolean);
111
+ if (listTypes.length === 0) {
112
+ return new Map();
113
+ }
114
+ var result = new Map();
115
+ var _iterator = _createForOfIteratorHelper(transactions),
116
+ _step;
117
+ try {
118
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
119
+ var tr = _step.value;
120
+ var _iterator2 = _createForOfIteratorHelper(tr.steps),
121
+ _step2;
122
+ try {
123
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
124
+ var step = _step2.value;
125
+ // ReplaceStep and ReplaceAroundStep both have from/to — other step types are skipped.
126
+ if (!(step instanceof ReplaceStep) && !(step instanceof ReplaceAroundStep)) {
127
+ continue;
128
+ }
129
+ // Check both the start and end of each changed range, mapped to post-paste positions.
130
+ for (var _i = 0, _arr = [step.from, step.to]; _i < _arr.length; _i++) {
131
+ var rawPos = _arr[_i];
132
+ var mappedPos = Math.min(tr.mapping.map(rawPos), doc.content.size - 1);
133
+ var $pos = doc.resolve(mappedPos);
134
+ var ancestor = findParentNodeOfTypeClosestToPos($pos, listTypes);
135
+ if (ancestor) {
136
+ result.set(ancestor.pos, ancestor.node);
137
+ }
138
+ }
139
+ }
140
+ } catch (err) {
141
+ _iterator2.e(err);
142
+ } finally {
143
+ _iterator2.f();
144
+ }
145
+ }
146
+ } catch (err) {
147
+ _iterator.e(err);
148
+ } finally {
149
+ _iterator.f();
150
+ }
151
+ return result;
152
+ }
153
+ /**
154
+ * Applies list normalisation fixes to the given transaction for all affected list subtrees.
155
+ * Processes nodes in reverse document order so that position offsets from insertions/joins
156
+ * do not affect earlier positions.
157
+ *
158
+ * When platform_editor_flexible_list_indentation is off: inserts an empty paragraph before any listItem whose
159
+ * first child is a list node, and merges adjacent same-type list nodes within a listItem.
160
+ * When platform_editor_flexible_list_indentation is on: only merges adjacent same-type list nodes.
161
+ */
162
+ export function applyListNormalisationFixes(_ref) {
163
+ var tr = _ref.tr,
164
+ transactions = _ref.transactions,
165
+ doc = _ref.doc,
166
+ schema = _ref.schema;
167
+ var affectedLists = getAffectedListsFromTransactions(transactions, doc, schema);
168
+ if (affectedLists.size === 0) {
169
+ return tr;
170
+ }
171
+ var _schema$nodes2 = schema.nodes,
172
+ listItem = _schema$nodes2.listItem,
173
+ paragraph = _schema$nodes2.paragraph;
174
+ if (!listItem) {
175
+ return tr;
176
+ }
177
+
178
+ // Process lists in reverse position order so fixes at higher positions
179
+ // don't shift the positions of fixes at lower positions.
180
+ var sortedEntries = _toConsumableArray(affectedLists.entries()).sort(function (_ref2, _ref3) {
181
+ var _ref4 = _slicedToArray(_ref2, 1),
182
+ posA = _ref4[0];
183
+ var _ref5 = _slicedToArray(_ref3, 1),
184
+ posB = _ref5[0];
185
+ return posB - posA;
186
+ });
187
+ var _iterator3 = _createForOfIteratorHelper(sortedEntries),
188
+ _step3;
189
+ try {
190
+ 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.
196
+ var listItemPositions = [];
197
+ listNode.descendants(function (node, offsetPos) {
198
+ 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
201
+ }
202
+ return true;
203
+ });
204
+ for (var i = listItemPositions.length - 1; i >= 0; i--) {
205
+ var mappedPos = tr.mapping.map(listItemPositions[i]);
206
+ var node = tr.doc.nodeAt(mappedPos);
207
+ if (!node || node.type !== listItem) {
208
+ continue;
209
+ }
210
+
211
+ // Merge adjacent same-type list nodes (highest boundary first within the listItem).
212
+ for (var j = node.childCount - 1; j > 0; j--) {
213
+ var child = node.child(j);
214
+ var prevChild = node.child(j - 1);
215
+ if (isListNode(child) && child.type === prevChild.type) {
216
+ var offset = 1; // +1 for listItem opening token
217
+ for (var k = 0; k < j; k++) {
218
+ offset += node.child(k).nodeSize;
219
+ }
220
+ try {
221
+ tr.join(mappedPos + offset);
222
+ } catch (e) {
223
+ // join may fail if position is invalid after earlier transforms — skip
224
+ // eslint-disable-next-line no-console
225
+ console.warn('[editor-plugin-list] applyListNormalisationFixes: unexpected join failure', e);
226
+ }
227
+ }
228
+ }
229
+
230
+ // Insert empty paragraph before list-first listItems when _indentation is off.
231
+ 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) {
234
+ var emptyParagraph = paragraph.createAndFill();
235
+ if (emptyParagraph) {
236
+ tr.insert(mappedPos + 1, emptyParagraph);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ };
242
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
243
+ _loop();
244
+ }
245
+ } catch (err) {
246
+ _iterator3.e(err);
247
+ } finally {
248
+ _iterator3.f();
249
+ }
250
+ return tr;
91
251
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { useIntl } from 'react-intl-next';
2
+ import { useIntl } from 'react-intl';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { listMessages } from '@atlaskit/editor-common/messages';
5
5
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { useIntl } from 'react-intl-next';
2
+ import { useIntl } from 'react-intl';
3
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
4
4
  import { listMessages } from '@atlaskit/editor-common/messages';
5
5
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
@@ -2,8 +2,8 @@ import type { Dispatch } from '@atlaskit/editor-common/event-dispatcher';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import type { ExtractInjectionAPI, FeatureFlags } from '@atlaskit/editor-common/types';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
- import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
5
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
6
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
8
  import type { ListPlugin } from '../listPluginType';
9
9
  import type { ListState } from '../types';
@@ -1,4 +1,22 @@
1
+ import type { Node, Schema } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
2
3
  export declare function liftFollowingList(from: number, to: number, rootListDepth: number, tr: Transaction): Transaction;
3
4
  export declare function liftNodeSelectionList(selection: Selection, tr: Transaction): Transaction;
4
5
  export declare function liftTextSelectionList(selection: Selection, tr: Transaction): Transaction;
6
+ interface ApplyListNormalisationFixesOptions {
7
+ doc: Node;
8
+ schema: Schema;
9
+ tr: Transaction;
10
+ transactions: readonly Transaction[];
11
+ }
12
+ /**
13
+ * Applies list normalisation fixes to the given transaction for all affected list subtrees.
14
+ * Processes nodes in reverse document order so that position offsets from insertions/joins
15
+ * do not affect earlier positions.
16
+ *
17
+ * When platform_editor_flexible_list_indentation is off: inserts an empty paragraph before any listItem whose
18
+ * first child is a list node, and merges adjacent same-type list nodes within a listItem.
19
+ * When platform_editor_flexible_list_indentation is on: only merges adjacent same-type list nodes.
20
+ */
21
+ export declare function applyListNormalisationFixes({ tr, transactions, doc, schema, }: ApplyListNormalisationFixesOptions): Transaction;
22
+ export {};
@@ -2,8 +2,8 @@ import type { Dispatch } from '@atlaskit/editor-common/event-dispatcher';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
3
  import type { ExtractInjectionAPI, FeatureFlags } from '@atlaskit/editor-common/types';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
- import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
5
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
6
+ import { PluginKey } from '@atlaskit/editor-prosemirror/state';
7
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
8
  import type { ListPlugin } from '../listPluginType';
9
9
  import type { ListState } from '../types';
@@ -1,4 +1,22 @@
1
+ import type { Node, Schema } from '@atlaskit/editor-prosemirror/model';
1
2
  import type { Selection, Transaction } from '@atlaskit/editor-prosemirror/state';
2
3
  export declare function liftFollowingList(from: number, to: number, rootListDepth: number, tr: Transaction): Transaction;
3
4
  export declare function liftNodeSelectionList(selection: Selection, tr: Transaction): Transaction;
4
5
  export declare function liftTextSelectionList(selection: Selection, tr: Transaction): Transaction;
6
+ interface ApplyListNormalisationFixesOptions {
7
+ doc: Node;
8
+ schema: Schema;
9
+ tr: Transaction;
10
+ transactions: readonly Transaction[];
11
+ }
12
+ /**
13
+ * Applies list normalisation fixes to the given transaction for all affected list subtrees.
14
+ * Processes nodes in reverse document order so that position offsets from insertions/joins
15
+ * do not affect earlier positions.
16
+ *
17
+ * When platform_editor_flexible_list_indentation is off: inserts an empty paragraph before any listItem whose
18
+ * first child is a list node, and merges adjacent same-type list nodes within a listItem.
19
+ * When platform_editor_flexible_list_indentation is on: only merges adjacent same-type list nodes.
20
+ */
21
+ export declare function applyListNormalisationFixes({ tr, transactions, doc, schema, }: ApplyListNormalisationFixesOptions): Transaction;
22
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-list",
3
- "version": "11.0.0",
3
+ "version": "12.0.0",
4
4
  "description": "List plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -28,12 +28,12 @@
28
28
  "atlaskit:src": "src/index.ts",
29
29
  "dependencies": {
30
30
  "@atlaskit/adf-schema": "^52.5.0",
31
- "@atlaskit/editor-plugin-analytics": "^9.0.0",
32
- "@atlaskit/editor-plugin-block-menu": "^8.0.0",
33
- "@atlaskit/editor-plugin-feature-flags": "^8.0.0",
34
- "@atlaskit/editor-plugin-toolbar": "^6.0.0",
31
+ "@atlaskit/editor-plugin-analytics": "^10.0.0",
32
+ "@atlaskit/editor-plugin-block-menu": "^9.0.0",
33
+ "@atlaskit/editor-plugin-feature-flags": "^9.0.0",
34
+ "@atlaskit/editor-plugin-toolbar": "^7.0.0",
35
35
  "@atlaskit/editor-prosemirror": "^7.3.0",
36
- "@atlaskit/editor-toolbar": "^0.20.0",
36
+ "@atlaskit/editor-toolbar": "^1.0.0",
37
37
  "@atlaskit/icon": "^34.2.0",
38
38
  "@atlaskit/platform-feature-flags": "^1.1.0",
39
39
  "@atlaskit/prosemirror-history": "^0.2.0",
@@ -42,9 +42,9 @@
42
42
  "@babel/runtime": "^7.0.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@atlaskit/editor-common": "^113.0.0",
45
+ "@atlaskit/editor-common": "^114.0.0",
46
46
  "react": "^18.2.0",
47
- "react-intl-next": "npm:react-intl@^5.18.1"
47
+ "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
48
48
  },
49
49
  "techstack": {
50
50
  "@atlassian/frontend": {
@@ -93,5 +93,8 @@
93
93
  "no-unused-dependencies": {
94
94
  "checkDevDependencies": true
95
95
  }
96
+ },
97
+ "devDependencies": {
98
+ "react-intl": "^6.6.2"
96
99
  }
97
100
  }
package/report.api.md CHANGED
@@ -109,7 +109,7 @@ type ToggleOrderedList = (inputMethod: InputMethod) => EditorCommand;
109
109
  ```json
110
110
  {
111
111
  "react": "^16.8.0",
112
- "react-intl-next": "npm:react-intl@^5.18.1"
112
+ "react-intl": "npm:react-intl@^5.18.1"
113
113
  }
114
114
  ```
115
115