@atlaskit/editor-plugin-list 14.1.18 → 14.1.20

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,20 @@
1
1
  # @atlaskit/editor-plugin-list
2
2
 
3
+ ## 14.1.20
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 14.1.19
10
+
11
+ ### Patch Changes
12
+
13
+ - [`3c4994f354f5f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3c4994f354f5f) -
14
+ [ux] Fix content order being corrupted when indenting or outdenting a list item that has content
15
+ placed between its sub-lists (e.g. `li(p, ul, p, ul)`).
16
+ - Updated dependencies
17
+
3
18
  ## 14.1.18
4
19
 
5
20
  ### Patch Changes
@@ -7,6 +7,7 @@ exports.buildReplacementFragment = buildReplacementFragment;
7
7
  exports.flattenList = flattenList;
8
8
  var _lists = require("@atlaskit/editor-common/lists");
9
9
  var _utils = require("@atlaskit/editor-common/utils");
10
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
11
  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; } } }; }
11
12
  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; } }
12
13
  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; }
@@ -30,14 +31,107 @@ function contentSize(listItem) {
30
31
  }
31
32
 
32
33
  /**
33
- * Flatten a root list into a flat array of content-bearing items
34
- * and simultaneously determine which elements intersect the user's selection.
35
- *
36
- * Delegates to the shared `flattenListLike` with list-specific callbacks.
37
- * Selection intersection is checked against each item's content-only
38
- * span (excluding nested lists).
34
+ * Flattens a list, splitting each list item into one item per run of content.
35
+ * An item like `li(p, ul, p, ul)` becomes separate sibling items, so content
36
+ * placed between sub-lists keeps its order. Also records which items the
37
+ * selection touches.
39
38
  */
39
+ function flattenListImpl(options) {
40
+ var doc = options.doc,
41
+ rootListStart = options.rootListStart,
42
+ selectionFrom = options.selectionFrom,
43
+ selectionTo = options.selectionTo,
44
+ indentDelta = options.indentDelta,
45
+ maxDepth = options.maxDepth;
46
+ var rootList = doc.nodeAt(rootListStart);
47
+ if (!rootList) {
48
+ return null;
49
+ }
50
+ var schema = rootList.type.schema;
51
+ var items = [];
52
+ var startIndex = -1;
53
+ var endIndex = -1;
54
+ var exceedsMaxDepth = false;
55
+ var isPointSelection = selectionFrom === selectionTo;
56
+ var pushSegment = function pushSegment(segmentNode, contentStart, contentEnd, depth, parentList) {
57
+ var isSelected = isPointSelection ? selectionFrom >= contentStart && selectionFrom <= contentEnd : contentStart < selectionTo && contentEnd > selectionFrom;
58
+ var effectiveDepth = depth + (isSelected ? indentDelta : 0);
59
+ items.push({
60
+ node: segmentNode,
61
+ pos: contentStart - 1,
62
+ depth: effectiveDepth,
63
+ isSelected: isSelected,
64
+ listType: parentList.type.name,
65
+ parentListAttrs: parentList.attrs
66
+ });
67
+ if (isSelected) {
68
+ var index = items.length - 1;
69
+ if (startIndex === -1) {
70
+ startIndex = index;
71
+ }
72
+ endIndex = index;
73
+ if (maxDepth != null && effectiveDepth >= maxDepth) {
74
+ exceedsMaxDepth = true;
75
+ }
76
+ }
77
+ };
78
+ var _walkList = function walkList(listNode, listPos, depth) {
79
+ var childPos = listPos + 1;
80
+ listNode.forEach(function (listItemNode) {
81
+ if (!(0, _utils.isListItemNode)(listItemNode)) {
82
+ childPos += listItemNode.nodeSize;
83
+ return;
84
+ }
85
+ var itemStart = childPos;
86
+ var innerPos = itemStart + 1;
87
+ // Content collected since the last sub-list. Each run becomes its own item.
88
+ var segmentContent = [];
89
+ var segmentContentStart = innerPos;
90
+ var segmentContentSize = 0;
91
+ var emitSegment = function emitSegment(parentList) {
92
+ if (segmentContent.length === 0) {
93
+ return;
94
+ }
95
+ var segmentItem = schema.nodes.listItem.create(listItemNode.attrs, segmentContent);
96
+ pushSegment(segmentItem, segmentContentStart, segmentContentStart + segmentContentSize, depth, parentList);
97
+ };
98
+ listItemNode.forEach(function (child) {
99
+ if ((0, _utils.isListNode)(child)) {
100
+ // A sub-list ends the current run: emit it, then walk the sub-list deeper.
101
+ emitSegment(listNode);
102
+ segmentContent = [];
103
+ segmentContentSize = 0;
104
+ _walkList(child, innerPos, depth + 1);
105
+ innerPos += child.nodeSize;
106
+ segmentContentStart = innerPos;
107
+ } else {
108
+ if (segmentContent.length === 0) {
109
+ segmentContentStart = innerPos;
110
+ }
111
+ segmentContent.push(child);
112
+ segmentContentSize += child.nodeSize;
113
+ innerPos += child.nodeSize;
114
+ }
115
+ });
116
+ // Emit any remaining content after the last sub-list.
117
+ emitSegment(listNode);
118
+ childPos += listItemNode.nodeSize;
119
+ });
120
+ };
121
+ _walkList(rootList, rootListStart, 0);
122
+ if (items.length === 0 || startIndex === -1 || exceedsMaxDepth) {
123
+ return null;
124
+ }
125
+ return {
126
+ items: items,
127
+ startIndex: startIndex,
128
+ endIndex: endIndex
129
+ };
130
+ }
40
131
  function flattenList(options) {
132
+ if ((0, _platformFeatureFlags.fg)('platform_editor_flexible_list_normalization_fix')) {
133
+ return flattenListImpl(options);
134
+ }
41
135
  return (0, _lists.flattenList)(options, {
42
136
  isContentNode: function isContentNode(node, parent) {
43
137
  return (0, _utils.isListItemNode)(node) && hasContentChildren(node) && (0, _utils.isListNode)(parent);
@@ -1,5 +1,7 @@
1
1
  import { buildReplacementFragment as buildReplacementFragmentBase, flattenList as flattenListBase } from '@atlaskit/editor-common/lists';
2
2
  import { isListItemNode, isListNode } from '@atlaskit/editor-common/utils';
3
+ import { fg } from '@atlaskit/platform-feature-flags';
4
+
3
5
  /**
4
6
  * Returns true if a listItem has at least one non-list child (paragraph, etc.).
5
7
  */
@@ -18,14 +20,109 @@ function contentSize(listItem) {
18
20
  }
19
21
 
20
22
  /**
21
- * Flatten a root list into a flat array of content-bearing items
22
- * and simultaneously determine which elements intersect the user's selection.
23
- *
24
- * Delegates to the shared `flattenListLike` with list-specific callbacks.
25
- * Selection intersection is checked against each item's content-only
26
- * span (excluding nested lists).
23
+ * Flattens a list, splitting each list item into one item per run of content.
24
+ * An item like `li(p, ul, p, ul)` becomes separate sibling items, so content
25
+ * placed between sub-lists keeps its order. Also records which items the
26
+ * selection touches.
27
27
  */
28
+ function flattenListImpl(options) {
29
+ const {
30
+ doc,
31
+ rootListStart,
32
+ selectionFrom,
33
+ selectionTo,
34
+ indentDelta,
35
+ maxDepth
36
+ } = options;
37
+ const rootList = doc.nodeAt(rootListStart);
38
+ if (!rootList) {
39
+ return null;
40
+ }
41
+ const schema = rootList.type.schema;
42
+ const items = [];
43
+ let startIndex = -1;
44
+ let endIndex = -1;
45
+ let exceedsMaxDepth = false;
46
+ const isPointSelection = selectionFrom === selectionTo;
47
+ const pushSegment = (segmentNode, contentStart, contentEnd, depth, parentList) => {
48
+ const isSelected = isPointSelection ? selectionFrom >= contentStart && selectionFrom <= contentEnd : contentStart < selectionTo && contentEnd > selectionFrom;
49
+ const effectiveDepth = depth + (isSelected ? indentDelta : 0);
50
+ items.push({
51
+ node: segmentNode,
52
+ pos: contentStart - 1,
53
+ depth: effectiveDepth,
54
+ isSelected,
55
+ listType: parentList.type.name,
56
+ parentListAttrs: parentList.attrs
57
+ });
58
+ if (isSelected) {
59
+ const index = items.length - 1;
60
+ if (startIndex === -1) {
61
+ startIndex = index;
62
+ }
63
+ endIndex = index;
64
+ if (maxDepth != null && effectiveDepth >= maxDepth) {
65
+ exceedsMaxDepth = true;
66
+ }
67
+ }
68
+ };
69
+ const walkList = (listNode, listPos, depth) => {
70
+ let childPos = listPos + 1;
71
+ listNode.forEach(listItemNode => {
72
+ if (!isListItemNode(listItemNode)) {
73
+ childPos += listItemNode.nodeSize;
74
+ return;
75
+ }
76
+ const itemStart = childPos;
77
+ let innerPos = itemStart + 1;
78
+ // Content collected since the last sub-list. Each run becomes its own item.
79
+ let segmentContent = [];
80
+ let segmentContentStart = innerPos;
81
+ let segmentContentSize = 0;
82
+ const emitSegment = parentList => {
83
+ if (segmentContent.length === 0) {
84
+ return;
85
+ }
86
+ const segmentItem = schema.nodes.listItem.create(listItemNode.attrs, segmentContent);
87
+ pushSegment(segmentItem, segmentContentStart, segmentContentStart + segmentContentSize, depth, parentList);
88
+ };
89
+ listItemNode.forEach(child => {
90
+ if (isListNode(child)) {
91
+ // A sub-list ends the current run: emit it, then walk the sub-list deeper.
92
+ emitSegment(listNode);
93
+ segmentContent = [];
94
+ segmentContentSize = 0;
95
+ walkList(child, innerPos, depth + 1);
96
+ innerPos += child.nodeSize;
97
+ segmentContentStart = innerPos;
98
+ } else {
99
+ if (segmentContent.length === 0) {
100
+ segmentContentStart = innerPos;
101
+ }
102
+ segmentContent.push(child);
103
+ segmentContentSize += child.nodeSize;
104
+ innerPos += child.nodeSize;
105
+ }
106
+ });
107
+ // Emit any remaining content after the last sub-list.
108
+ emitSegment(listNode);
109
+ childPos += listItemNode.nodeSize;
110
+ });
111
+ };
112
+ walkList(rootList, rootListStart, 0);
113
+ if (items.length === 0 || startIndex === -1 || exceedsMaxDepth) {
114
+ return null;
115
+ }
116
+ return {
117
+ items,
118
+ startIndex,
119
+ endIndex
120
+ };
121
+ }
28
122
  export function flattenList(options) {
123
+ if (fg('platform_editor_flexible_list_normalization_fix')) {
124
+ return flattenListImpl(options);
125
+ }
29
126
  return flattenListBase(options, {
30
127
  isContentNode: (node, parent) => isListItemNode(node) && hasContentChildren(node) && isListNode(parent),
31
128
  // +1 shifts from the listItem node boundary to the start of its content children
@@ -3,6 +3,8 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
3
3
  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; }
4
4
  import { buildReplacementFragment as buildReplacementFragmentBase, flattenList as flattenListBase } from '@atlaskit/editor-common/lists';
5
5
  import { isListItemNode, isListNode } from '@atlaskit/editor-common/utils';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
7
+
6
8
  /**
7
9
  * Returns true if a listItem has at least one non-list child (paragraph, etc.).
8
10
  */
@@ -23,14 +25,107 @@ function contentSize(listItem) {
23
25
  }
24
26
 
25
27
  /**
26
- * Flatten a root list into a flat array of content-bearing items
27
- * and simultaneously determine which elements intersect the user's selection.
28
- *
29
- * Delegates to the shared `flattenListLike` with list-specific callbacks.
30
- * Selection intersection is checked against each item's content-only
31
- * span (excluding nested lists).
28
+ * Flattens a list, splitting each list item into one item per run of content.
29
+ * An item like `li(p, ul, p, ul)` becomes separate sibling items, so content
30
+ * placed between sub-lists keeps its order. Also records which items the
31
+ * selection touches.
32
32
  */
33
+ function flattenListImpl(options) {
34
+ var doc = options.doc,
35
+ rootListStart = options.rootListStart,
36
+ selectionFrom = options.selectionFrom,
37
+ selectionTo = options.selectionTo,
38
+ indentDelta = options.indentDelta,
39
+ maxDepth = options.maxDepth;
40
+ var rootList = doc.nodeAt(rootListStart);
41
+ if (!rootList) {
42
+ return null;
43
+ }
44
+ var schema = rootList.type.schema;
45
+ var items = [];
46
+ var startIndex = -1;
47
+ var endIndex = -1;
48
+ var exceedsMaxDepth = false;
49
+ var isPointSelection = selectionFrom === selectionTo;
50
+ var pushSegment = function pushSegment(segmentNode, contentStart, contentEnd, depth, parentList) {
51
+ var isSelected = isPointSelection ? selectionFrom >= contentStart && selectionFrom <= contentEnd : contentStart < selectionTo && contentEnd > selectionFrom;
52
+ var effectiveDepth = depth + (isSelected ? indentDelta : 0);
53
+ items.push({
54
+ node: segmentNode,
55
+ pos: contentStart - 1,
56
+ depth: effectiveDepth,
57
+ isSelected: isSelected,
58
+ listType: parentList.type.name,
59
+ parentListAttrs: parentList.attrs
60
+ });
61
+ if (isSelected) {
62
+ var index = items.length - 1;
63
+ if (startIndex === -1) {
64
+ startIndex = index;
65
+ }
66
+ endIndex = index;
67
+ if (maxDepth != null && effectiveDepth >= maxDepth) {
68
+ exceedsMaxDepth = true;
69
+ }
70
+ }
71
+ };
72
+ var _walkList = function walkList(listNode, listPos, depth) {
73
+ var childPos = listPos + 1;
74
+ listNode.forEach(function (listItemNode) {
75
+ if (!isListItemNode(listItemNode)) {
76
+ childPos += listItemNode.nodeSize;
77
+ return;
78
+ }
79
+ var itemStart = childPos;
80
+ var innerPos = itemStart + 1;
81
+ // Content collected since the last sub-list. Each run becomes its own item.
82
+ var segmentContent = [];
83
+ var segmentContentStart = innerPos;
84
+ var segmentContentSize = 0;
85
+ var emitSegment = function emitSegment(parentList) {
86
+ if (segmentContent.length === 0) {
87
+ return;
88
+ }
89
+ var segmentItem = schema.nodes.listItem.create(listItemNode.attrs, segmentContent);
90
+ pushSegment(segmentItem, segmentContentStart, segmentContentStart + segmentContentSize, depth, parentList);
91
+ };
92
+ listItemNode.forEach(function (child) {
93
+ if (isListNode(child)) {
94
+ // A sub-list ends the current run: emit it, then walk the sub-list deeper.
95
+ emitSegment(listNode);
96
+ segmentContent = [];
97
+ segmentContentSize = 0;
98
+ _walkList(child, innerPos, depth + 1);
99
+ innerPos += child.nodeSize;
100
+ segmentContentStart = innerPos;
101
+ } else {
102
+ if (segmentContent.length === 0) {
103
+ segmentContentStart = innerPos;
104
+ }
105
+ segmentContent.push(child);
106
+ segmentContentSize += child.nodeSize;
107
+ innerPos += child.nodeSize;
108
+ }
109
+ });
110
+ // Emit any remaining content after the last sub-list.
111
+ emitSegment(listNode);
112
+ childPos += listItemNode.nodeSize;
113
+ });
114
+ };
115
+ _walkList(rootList, rootListStart, 0);
116
+ if (items.length === 0 || startIndex === -1 || exceedsMaxDepth) {
117
+ return null;
118
+ }
119
+ return {
120
+ items: items,
121
+ startIndex: startIndex,
122
+ endIndex: endIndex
123
+ };
124
+ }
33
125
  export function flattenList(options) {
126
+ if (fg('platform_editor_flexible_list_normalization_fix')) {
127
+ return flattenListImpl(options);
128
+ }
34
129
  return flattenListBase(options, {
35
130
  isContentNode: function isContentNode(node, parent) {
36
131
  return isListItemNode(node) && hasContentChildren(node) && isListNode(parent);
@@ -1,13 +1,5 @@
1
- import { type BuildResult, type FlattenedItem, type FlattenListOptions, type FlattenListResult } from '@atlaskit/editor-common/lists';
1
+ import { type BuildResult, type FlattenListOptions, type FlattenListResult, type FlattenedItem } from '@atlaskit/editor-common/lists';
2
2
  import type { Schema } from '@atlaskit/editor-prosemirror/model';
3
- /**
4
- * Flatten a root list into a flat array of content-bearing items
5
- * and simultaneously determine which elements intersect the user's selection.
6
- *
7
- * Delegates to the shared `flattenListLike` with list-specific callbacks.
8
- * Selection intersection is checked against each item's content-only
9
- * span (excluding nested lists).
10
- */
11
3
  export declare function flattenList(options: FlattenListOptions): FlattenListResult | null;
12
4
  /**
13
5
  * Build a replacement Fragment from a flat array of `FlattenedItem` objects.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-list",
3
- "version": "14.1.18",
3
+ "version": "14.1.20",
4
4
  "description": "List plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -25,16 +25,16 @@
25
25
  "@atlaskit/editor-plugin-feature-flags": "^11.0.0",
26
26
  "@atlaskit/editor-plugin-toolbar": "^9.0.0",
27
27
  "@atlaskit/editor-prosemirror": "^8.0.0",
28
- "@atlaskit/editor-toolbar": "^2.2.0",
28
+ "@atlaskit/editor-toolbar": "^2.3.0",
29
29
  "@atlaskit/icon": "^37.0.0",
30
30
  "@atlaskit/platform-feature-flags": "^2.0.0",
31
31
  "@atlaskit/prosemirror-history": "^1.0.0",
32
32
  "@atlaskit/prosemirror-input-rules": "^4.0.0",
33
- "@atlaskit/tmp-editor-statsig": "^126.0.0",
33
+ "@atlaskit/tmp-editor-statsig": "^128.0.0",
34
34
  "@babel/runtime": "^7.0.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@atlaskit/editor-common": "^116.30.0",
37
+ "@atlaskit/editor-common": "^116.33.0",
38
38
  "react": "^18.2.0",
39
39
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
40
40
  },
@@ -79,6 +79,9 @@
79
79
  },
80
80
  "platform_editor_block_menu_v2_patch_3": {
81
81
  "type": "boolean"
82
+ },
83
+ "platform_editor_flexible_list_normalization_fix": {
84
+ "type": "boolean"
82
85
  }
83
86
  },
84
87
  "stricter": {