@atlaskit/editor-plugin-list 8.1.1 → 8.1.3

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,23 @@
1
1
  # @atlaskit/editor-plugin-list
2
2
 
3
+ ## 8.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`8abe663d8d5db`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8abe663d8d5db) -
8
+ Add new list decorations logic
9
+ - Updated dependencies
10
+
11
+ ## 8.1.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`ef001bf65d48f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ef001bf65d48f) -
16
+ Remove usage of `platform_editor_toolbar_aifc` inside editor packages - instead rely on checking
17
+ for new toolbar plugin option, make `enableNewToolbarExperience` mandatory for consumers to opt in
18
+ to new toolbar experience
19
+ - Updated dependencies
20
+
3
21
  ## 8.1.1
4
22
 
5
23
  ### Patch Changes
@@ -320,7 +338,6 @@
320
338
  shared context or singletons.
321
339
 
322
340
  **HOW TO ADJUST:**
323
-
324
341
  - Consumers must now explicitly install `@atlaskit/editor-common` in their own project if they use
325
342
  any of these editor plugins.
326
343
  - Ensure the version you install matches the version required by the plugins.
@@ -998,7 +1015,6 @@
998
1015
 
999
1016
  These are no longer available via `@atlaskit/prosemirror-input-rules` but are available from
1000
1017
  `@atlaskit/editor-common/types`:
1001
-
1002
1018
  - InputRuleWrapper
1003
1019
  - InputRuleHandler
1004
1020
  - OnHandlerApply
@@ -1008,7 +1024,6 @@
1008
1024
  need to instantiate a `SafePlugin` (ie. `new SafePlugin(createPlugin( ... ))`).
1009
1025
 
1010
1026
  `SafePlugin` exists in `@atlaskit/editor-common/safe-plugin`.
1011
-
1012
1027
  - createPlugin
1013
1028
  - createInputRulePlugin
1014
1029
 
@@ -1219,19 +1234,13 @@
1219
1234
  auto-join lists together if the order numbers match up.
1220
1235
 
1221
1236
  Eg.
1222
-
1223
1237
  1. A list item
1224
-
1225
1238
  -
1226
-
1227
1239
  50. Another list item
1228
1240
 
1229
1241
  Should NOT auto join to be 1 & 2 in a single list however...
1230
-
1231
1242
  49. A list item
1232
-
1233
1243
  -
1234
-
1235
1244
  50. Another list item
1236
1245
 
1237
1246
  Should join to be 49 & 50 in a single list.
@@ -1253,7 +1262,6 @@
1253
1262
  - [#43646](https://bitbucket.org/atlassian/atlassian-frontend/pull-requests/43646)
1254
1263
  [`d43f8e9402f`](https://bitbucket.org/atlassian/atlassian-frontend/commits/d43f8e9402f) - Make
1255
1264
  feature flags plugin optional in all plugins including:
1256
-
1257
1265
  - analytics
1258
1266
  - base
1259
1267
  - card
@@ -83,7 +83,7 @@ var listPlugin = exports.listPlugin = function listPlugin(_ref) {
83
83
  name: 'list',
84
84
  plugin: function plugin(_ref2) {
85
85
  var dispatch = _ref2.dispatch;
86
- return (0, _main.createPlugin)(dispatch, featureFlags);
86
+ return (0, _main.createPlugin)(dispatch, featureFlags, api);
87
87
  }
88
88
  }, {
89
89
  name: 'listInputRule',
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.pluginKey = exports.getDecorations = exports.createPlugin = void 0;
7
+ exports.updateListDecorations = exports.pluginKey = exports.getDecorations = exports.createPlugin = exports.computeListDecorations = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
10
10
  var _selection = require("@atlaskit/editor-common/selection");
@@ -13,8 +13,8 @@ var _utils = require("@atlaskit/editor-common/utils");
13
13
  var _state = 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 _insm = require("@atlaskit/insm");
16
17
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
17
- var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
18
18
  var _selection2 = require("./utils/selection");
19
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; }
20
20
  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; }
@@ -27,6 +27,79 @@ var initialState = {
27
27
  orderedListDisabled: false,
28
28
  decorationSet: _view.DecorationSet.empty
29
29
  };
30
+ var computeListDecorations = exports.computeListDecorations = function computeListDecorations(doc) {
31
+ var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
32
+ var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : doc.content.size;
33
+ var decorations = [];
34
+
35
+ // this stack keeps track of each (nested) list to calculate the indentation level
36
+ var processedListsStack = [];
37
+ doc.nodesBetween(from, to, function (node, currentNodeStartPos) {
38
+ if (processedListsStack.length > 0) {
39
+ var isOutsideLastList = true;
40
+ while (isOutsideLastList && processedListsStack.length > 0) {
41
+ var lastList = processedListsStack[processedListsStack.length - 1];
42
+ var lastListEndPos = lastList.startPos + lastList.node.nodeSize;
43
+ isOutsideLastList = currentNodeStartPos >= lastListEndPos;
44
+ // once we finish iterating over each innermost list, pop the stack to
45
+ // decrease the indent level attribute accordingly
46
+ if (isOutsideLastList) {
47
+ processedListsStack.pop();
48
+ }
49
+ }
50
+ }
51
+ if ((0, _utils.isListNode)(node)) {
52
+ processedListsStack.push({
53
+ node: node,
54
+ startPos: currentNodeStartPos
55
+ });
56
+ var _from = currentNodeStartPos;
57
+ var _to = currentNodeStartPos + node.nodeSize;
58
+ var depth = processedListsStack.length;
59
+ decorations.push(_view.Decoration.node(_from, _to, {
60
+ 'data-indent-level': "".concat(depth)
61
+ }));
62
+ if (node.type.name === 'orderedList') {
63
+ var _node$attrs;
64
+ // If a numbered list has item counters numbering >= 100, we'll need to add special
65
+ // spacing to account for the extra digit chars
66
+ var digitsSize = (0, _utils.getItemCounterDigitsSize)({
67
+ itemsCount: node === null || node === void 0 ? void 0 : node.childCount,
68
+ order: node === null || node === void 0 || (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.order
69
+ });
70
+ if (digitsSize && digitsSize > 1) {
71
+ decorations.push(_view.Decoration.node(_from, _to, {
72
+ style: (0, _styles.getOrderedListInlineStyles)(digitsSize, 'string')
73
+ }));
74
+ }
75
+ }
76
+ }
77
+ });
78
+ return decorations;
79
+ };
80
+ var updateListDecorations = exports.updateListDecorations = function updateListDecorations(decorationSet, tr) {
81
+ var nextDecorationSet = decorationSet.map(tr.mapping, tr.doc);
82
+ tr.mapping.maps.forEach(function (stepMap, index) {
83
+ stepMap.forEach(function (oldStart, oldEnd) {
84
+ var start = tr.mapping.slice(index).map(oldStart, -1);
85
+ var end = tr.mapping.slice(index).map(oldEnd);
86
+
87
+ // Remove decorations in this range
88
+ var decorationsToRemove = nextDecorationSet.find(start, end);
89
+ nextDecorationSet = nextDecorationSet.remove(decorationsToRemove);
90
+
91
+ // Recompute decorations for this range
92
+ // Expand the range by 1 on each side to catch adjacent list nodes
93
+ var from = Math.max(0, start - 1);
94
+ var to = Math.min(tr.doc.content.size, end + 1);
95
+ var decorationsToAdd = computeListDecorations(tr.doc, from, to);
96
+ nextDecorationSet = nextDecorationSet.add(tr.doc, decorationsToAdd);
97
+ });
98
+ });
99
+ return nextDecorationSet;
100
+ };
101
+
102
+ // delete getDecorations during platform_editor_new_list_decorations_logic experiment clean up
30
103
  var getDecorations = exports.getDecorations = function getDecorations(doc, state, featureFlags) {
31
104
  var decorations = [];
32
105
 
@@ -58,12 +131,12 @@ var getDecorations = exports.getDecorations = function getDecorations(doc, state
58
131
  'data-indent-level': "".concat(depth)
59
132
  }));
60
133
  if (node.type.name === 'orderedList') {
61
- var _node$attrs;
134
+ var _node$attrs2;
62
135
  // If a numbered list has item counters numbering >= 100, we'll need to add special
63
136
  // spacing to account for the extra digit chars
64
137
  var digitsSize = (0, _utils.getItemCounterDigitsSize)({
65
138
  itemsCount: node === null || node === void 0 ? void 0 : node.childCount,
66
- order: node === null || node === void 0 || (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.order
139
+ order: node === null || node === void 0 || (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.order
67
140
  });
68
141
  if (digitsSize && digitsSize > 1) {
69
142
  decorations.push(_view.Decoration.node(from, to, {
@@ -123,27 +196,61 @@ var reducer = function reducer() {
123
196
  return state;
124
197
  };
125
198
  };
126
- var createInitialState = function createInitialState(featureFlags) {
199
+ var createInitialState = function createInitialState(featureFlags, api) {
127
200
  return function (state) {
128
- return _objectSpread(_objectSpread({}, (0, _experiments.editorExperiment)('platform_editor_toolbar_aifc', true) && (0, _expValEquals.expValEquals)('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState), {}, {
129
- decorationSet: getDecorations(state.doc, state, featureFlags)
201
+ var isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar);
202
+ var getInitialDecorations = function getInitialDecorations() {
203
+ var _insm$session, _insm$session2;
204
+ (_insm$session = _insm.insm.session) === null || _insm$session === void 0 || _insm$session.startFeature('listDecorationsInit');
205
+ var decorations = computeListDecorations(state.doc);
206
+ (_insm$session2 = _insm.insm.session) === null || _insm$session2 === void 0 || _insm$session2.endFeature('listDecorationsInit');
207
+ return decorations;
208
+ };
209
+ return _objectSpread(_objectSpread({}, isToolbarAIFCEnabled && (0, _expValEquals.expValEquals)('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState), {}, {
210
+ decorationSet: (0, _expValEquals.expValEquals)('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? _view.DecorationSet.empty.add(state.doc, getInitialDecorations()) : getDecorations(state.doc, state, featureFlags)
130
211
  });
131
212
  };
132
213
  };
133
- var createPlugin = exports.createPlugin = function createPlugin(eventDispatch, featureFlags) {
214
+ var createPlugin = exports.createPlugin = function createPlugin(eventDispatch, featureFlags, api) {
134
215
  var _pluginFactory = (0, _utils.pluginFactory)(listPluginKey, reducer(), {
135
216
  onDocChanged: handleDocChanged(featureFlags),
136
217
  onSelectionChanged: handleSelectionChanged
137
218
  }),
138
219
  getPluginState = _pluginFactory.getPluginState,
139
220
  createPluginState = _pluginFactory.createPluginState;
221
+ var pluginState = createPluginState(eventDispatch, createInitialState(featureFlags, api));
222
+ var pluginStateInit = function pluginStateInit(_, state) {
223
+ return createInitialState(featureFlags)(state);
224
+ };
225
+ var pluginStateApply = function pluginStateApply(tr, oldPluginState, _oldEditorState, _newEditorState) {
226
+ var nextPluginState = oldPluginState;
227
+ if (tr.docChanged) {
228
+ var _insm$session3, _insm$session4;
229
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
230
+ (_insm$session3 = _insm.insm.session) === null || _insm$session3 === void 0 || _insm$session3.startFeature('listDecorationUpdate');
231
+ var nextDecorationSet = updateListDecorations(nextPluginState.decorationSet, tr);
232
+ (_insm$session4 = _insm.insm.session) === null || _insm$session4 === void 0 || _insm$session4.endFeature('listDecorationUpdate');
233
+ nextPluginState = _objectSpread(_objectSpread({}, nextPluginState), {}, {
234
+ decorationSet: nextDecorationSet
235
+ });
236
+ } else if (tr.selectionSet) {
237
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
238
+ }
239
+ if (nextPluginState !== oldPluginState) {
240
+ eventDispatch(listPluginKey, nextPluginState);
241
+ }
242
+ return nextPluginState;
243
+ };
140
244
  return new _safePlugin.SafePlugin({
141
- state: createPluginState(eventDispatch, createInitialState(featureFlags)),
245
+ state: {
246
+ init: (0, _expValEquals.expValEquals)('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateInit : pluginState.init,
247
+ apply: (0, _expValEquals.expValEquals)('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateApply : pluginState.apply
248
+ },
142
249
  key: listPluginKey,
143
250
  props: {
144
251
  decorations: function decorations(state) {
145
- var _getPluginState = getPluginState(state),
146
- decorationSet = _getPluginState.decorationSet;
252
+ var _ref = (0, _expValEquals.expValEquals)('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? listPluginKey.getState(state) : getPluginState(state),
253
+ decorationSet = _ref.decorationSet;
147
254
  return decorationSet;
148
255
  },
149
256
  handleClick: function handleClick(view, pos, event) {
@@ -74,7 +74,7 @@ export const listPlugin = ({
74
74
  name: 'list',
75
75
  plugin: ({
76
76
  dispatch
77
- }) => createPlugin(dispatch, featureFlags)
77
+ }) => createPlugin(dispatch, featureFlags, api)
78
78
  }, {
79
79
  name: 'listInputRule',
80
80
  plugin: ({
@@ -5,8 +5,8 @@ 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 { insm } from '@atlaskit/insm';
8
9
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
- import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
10
  import { isWrappingPossible } from './utils/selection';
11
11
  const listPluginKey = new PluginKey('listPlugin');
12
12
  export const pluginKey = listPluginKey;
@@ -17,12 +17,12 @@ const initialState = {
17
17
  orderedListDisabled: false,
18
18
  decorationSet: DecorationSet.empty
19
19
  };
20
- export const getDecorations = (doc, state, featureFlags) => {
20
+ export const computeListDecorations = (doc, from = 0, to = doc.content.size) => {
21
21
  const decorations = [];
22
22
 
23
23
  // this stack keeps track of each (nested) list to calculate the indentation level
24
24
  const processedListsStack = [];
25
- doc.nodesBetween(0, doc.content.size, (node, currentNodeStartPos) => {
25
+ doc.nodesBetween(from, to, (node, currentNodeStartPos) => {
26
26
  if (processedListsStack.length > 0) {
27
27
  let isOutsideLastList = true;
28
28
  while (isOutsideLastList && processedListsStack.length > 0) {
@@ -63,6 +63,77 @@ export const getDecorations = (doc, state, featureFlags) => {
63
63
  }
64
64
  }
65
65
  });
66
+ return decorations;
67
+ };
68
+ export const updateListDecorations = (decorationSet, tr) => {
69
+ let nextDecorationSet = decorationSet.map(tr.mapping, tr.doc);
70
+ tr.mapping.maps.forEach((stepMap, index) => {
71
+ stepMap.forEach((oldStart, oldEnd) => {
72
+ const start = tr.mapping.slice(index).map(oldStart, -1);
73
+ const end = tr.mapping.slice(index).map(oldEnd);
74
+
75
+ // Remove decorations in this range
76
+ const decorationsToRemove = nextDecorationSet.find(start, end);
77
+ nextDecorationSet = nextDecorationSet.remove(decorationsToRemove);
78
+
79
+ // Recompute decorations for this range
80
+ // Expand the range by 1 on each side to catch adjacent list nodes
81
+ const from = Math.max(0, start - 1);
82
+ const to = Math.min(tr.doc.content.size, end + 1);
83
+ const decorationsToAdd = computeListDecorations(tr.doc, from, to);
84
+ nextDecorationSet = nextDecorationSet.add(tr.doc, decorationsToAdd);
85
+ });
86
+ });
87
+ return nextDecorationSet;
88
+ };
89
+
90
+ // delete getDecorations during platform_editor_new_list_decorations_logic experiment clean up
91
+ export const getDecorations = (doc, state, featureFlags) => {
92
+ const decorations = [];
93
+
94
+ // this stack keeps track of each (nested) list to calculate the indentation level
95
+ const processedListsStack = [];
96
+ doc.nodesBetween(0, doc.content.size, (node, currentNodeStartPos) => {
97
+ if (processedListsStack.length > 0) {
98
+ let isOutsideLastList = true;
99
+ while (isOutsideLastList && processedListsStack.length > 0) {
100
+ const lastList = processedListsStack[processedListsStack.length - 1];
101
+ const lastListEndPos = lastList.startPos + lastList.node.nodeSize;
102
+ isOutsideLastList = currentNodeStartPos >= lastListEndPos;
103
+ // once we finish iterating over each innermost list, pop the stack to
104
+ // decrease the indent level attribute accordingly
105
+ if (isOutsideLastList) {
106
+ processedListsStack.pop();
107
+ }
108
+ }
109
+ }
110
+ if (isListNode(node)) {
111
+ processedListsStack.push({
112
+ node,
113
+ startPos: currentNodeStartPos
114
+ });
115
+ const from = currentNodeStartPos;
116
+ const to = currentNodeStartPos + node.nodeSize;
117
+ const depth = processedListsStack.length;
118
+ decorations.push(Decoration.node(from, to, {
119
+ 'data-indent-level': `${depth}`
120
+ }));
121
+ if (node.type.name === 'orderedList') {
122
+ var _node$attrs2;
123
+ // If a numbered list has item counters numbering >= 100, we'll need to add special
124
+ // spacing to account for the extra digit chars
125
+ const digitsSize = getItemCounterDigitsSize({
126
+ itemsCount: node === null || node === void 0 ? void 0 : node.childCount,
127
+ order: node === null || node === void 0 ? void 0 : (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.order
128
+ });
129
+ if (digitsSize && digitsSize > 1) {
130
+ decorations.push(Decoration.node(from, to, {
131
+ style: getOrderedListInlineStyles(digitsSize, 'string')
132
+ }));
133
+ }
134
+ }
135
+ }
136
+ });
66
137
  return DecorationSet.empty.add(doc, decorations);
67
138
  };
68
139
  const getListState = (doc, selection) => {
@@ -113,15 +184,23 @@ const handleSelectionChanged = (tr, pluginState) => {
113
184
  const reducer = () => state => {
114
185
  return state;
115
186
  };
116
- const createInitialState = featureFlags => state => {
187
+ const createInitialState = (featureFlags, api) => state => {
188
+ const isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar);
189
+ const getInitialDecorations = () => {
190
+ var _insm$session, _insm$session2;
191
+ (_insm$session = insm.session) === null || _insm$session === void 0 ? void 0 : _insm$session.startFeature('listDecorationsInit');
192
+ const decorations = computeListDecorations(state.doc);
193
+ (_insm$session2 = insm.session) === null || _insm$session2 === void 0 ? void 0 : _insm$session2.endFeature('listDecorationsInit');
194
+ return decorations;
195
+ };
117
196
  return {
118
197
  // When plugin is initialised, editor state is defined with selection
119
198
  // hence returning the list state based on the selection to avoid list button in primary toolbar flickering during initial load
120
- ...(editorExperiment('platform_editor_toolbar_aifc', true) && expValEquals('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState),
121
- decorationSet: getDecorations(state.doc, state, featureFlags)
199
+ ...(isToolbarAIFCEnabled && expValEquals('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState),
200
+ decorationSet: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? DecorationSet.empty.add(state.doc, getInitialDecorations()) : getDecorations(state.doc, state, featureFlags)
122
201
  };
123
202
  };
124
- export const createPlugin = (eventDispatch, featureFlags) => {
203
+ export const createPlugin = (eventDispatch, featureFlags, api) => {
125
204
  const {
126
205
  getPluginState,
127
206
  createPluginState
@@ -129,14 +208,41 @@ export const createPlugin = (eventDispatch, featureFlags) => {
129
208
  onDocChanged: handleDocChanged(featureFlags),
130
209
  onSelectionChanged: handleSelectionChanged
131
210
  });
211
+ const pluginState = createPluginState(eventDispatch, createInitialState(featureFlags, api));
212
+ const pluginStateInit = (_, state) => {
213
+ return createInitialState(featureFlags)(state);
214
+ };
215
+ const pluginStateApply = (tr, oldPluginState, _oldEditorState, _newEditorState) => {
216
+ let nextPluginState = oldPluginState;
217
+ if (tr.docChanged) {
218
+ var _insm$session3, _insm$session4;
219
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
220
+ (_insm$session3 = insm.session) === null || _insm$session3 === void 0 ? void 0 : _insm$session3.startFeature('listDecorationUpdate');
221
+ const nextDecorationSet = updateListDecorations(nextPluginState.decorationSet, tr);
222
+ (_insm$session4 = insm.session) === null || _insm$session4 === void 0 ? void 0 : _insm$session4.endFeature('listDecorationUpdate');
223
+ nextPluginState = {
224
+ ...nextPluginState,
225
+ decorationSet: nextDecorationSet
226
+ };
227
+ } else if (tr.selectionSet) {
228
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
229
+ }
230
+ if (nextPluginState !== oldPluginState) {
231
+ eventDispatch(listPluginKey, nextPluginState);
232
+ }
233
+ return nextPluginState;
234
+ };
132
235
  return new SafePlugin({
133
- state: createPluginState(eventDispatch, createInitialState(featureFlags)),
236
+ state: {
237
+ init: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateInit : pluginState.init,
238
+ apply: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateApply : pluginState.apply
239
+ },
134
240
  key: listPluginKey,
135
241
  props: {
136
242
  decorations(state) {
137
243
  const {
138
244
  decorationSet
139
- } = getPluginState(state);
245
+ } = expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? listPluginKey.getState(state) : getPluginState(state);
140
246
  return decorationSet;
141
247
  },
142
248
  handleClick: (view, pos, event) => {
@@ -78,7 +78,7 @@ export var listPlugin = function listPlugin(_ref) {
78
78
  name: 'list',
79
79
  plugin: function plugin(_ref2) {
80
80
  var dispatch = _ref2.dispatch;
81
- return createPlugin(dispatch, featureFlags);
81
+ return createPlugin(dispatch, featureFlags, api);
82
82
  }
83
83
  }, {
84
84
  name: 'listInputRule',
@@ -8,8 +8,8 @@ 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 { insm } from '@atlaskit/insm';
11
12
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
- import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
13
  import { isWrappingPossible } from './utils/selection';
14
14
  var listPluginKey = new PluginKey('listPlugin');
15
15
  export var pluginKey = listPluginKey;
@@ -20,6 +20,79 @@ var initialState = {
20
20
  orderedListDisabled: false,
21
21
  decorationSet: DecorationSet.empty
22
22
  };
23
+ export var computeListDecorations = function computeListDecorations(doc) {
24
+ var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
25
+ var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : doc.content.size;
26
+ var decorations = [];
27
+
28
+ // this stack keeps track of each (nested) list to calculate the indentation level
29
+ var processedListsStack = [];
30
+ doc.nodesBetween(from, to, function (node, currentNodeStartPos) {
31
+ if (processedListsStack.length > 0) {
32
+ var isOutsideLastList = true;
33
+ while (isOutsideLastList && processedListsStack.length > 0) {
34
+ var lastList = processedListsStack[processedListsStack.length - 1];
35
+ var lastListEndPos = lastList.startPos + lastList.node.nodeSize;
36
+ isOutsideLastList = currentNodeStartPos >= lastListEndPos;
37
+ // once we finish iterating over each innermost list, pop the stack to
38
+ // decrease the indent level attribute accordingly
39
+ if (isOutsideLastList) {
40
+ processedListsStack.pop();
41
+ }
42
+ }
43
+ }
44
+ if (isListNode(node)) {
45
+ processedListsStack.push({
46
+ node: node,
47
+ startPos: currentNodeStartPos
48
+ });
49
+ var _from = currentNodeStartPos;
50
+ var _to = currentNodeStartPos + node.nodeSize;
51
+ var depth = processedListsStack.length;
52
+ decorations.push(Decoration.node(_from, _to, {
53
+ 'data-indent-level': "".concat(depth)
54
+ }));
55
+ if (node.type.name === 'orderedList') {
56
+ var _node$attrs;
57
+ // If a numbered list has item counters numbering >= 100, we'll need to add special
58
+ // spacing to account for the extra digit chars
59
+ var digitsSize = getItemCounterDigitsSize({
60
+ itemsCount: node === null || node === void 0 ? void 0 : node.childCount,
61
+ order: node === null || node === void 0 || (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.order
62
+ });
63
+ if (digitsSize && digitsSize > 1) {
64
+ decorations.push(Decoration.node(_from, _to, {
65
+ style: getOrderedListInlineStyles(digitsSize, 'string')
66
+ }));
67
+ }
68
+ }
69
+ }
70
+ });
71
+ return decorations;
72
+ };
73
+ export var updateListDecorations = function updateListDecorations(decorationSet, tr) {
74
+ var nextDecorationSet = decorationSet.map(tr.mapping, tr.doc);
75
+ tr.mapping.maps.forEach(function (stepMap, index) {
76
+ stepMap.forEach(function (oldStart, oldEnd) {
77
+ var start = tr.mapping.slice(index).map(oldStart, -1);
78
+ var end = tr.mapping.slice(index).map(oldEnd);
79
+
80
+ // Remove decorations in this range
81
+ var decorationsToRemove = nextDecorationSet.find(start, end);
82
+ nextDecorationSet = nextDecorationSet.remove(decorationsToRemove);
83
+
84
+ // Recompute decorations for this range
85
+ // Expand the range by 1 on each side to catch adjacent list nodes
86
+ var from = Math.max(0, start - 1);
87
+ var to = Math.min(tr.doc.content.size, end + 1);
88
+ var decorationsToAdd = computeListDecorations(tr.doc, from, to);
89
+ nextDecorationSet = nextDecorationSet.add(tr.doc, decorationsToAdd);
90
+ });
91
+ });
92
+ return nextDecorationSet;
93
+ };
94
+
95
+ // delete getDecorations during platform_editor_new_list_decorations_logic experiment clean up
23
96
  export var getDecorations = function getDecorations(doc, state, featureFlags) {
24
97
  var decorations = [];
25
98
 
@@ -51,12 +124,12 @@ export var getDecorations = function getDecorations(doc, state, featureFlags) {
51
124
  'data-indent-level': "".concat(depth)
52
125
  }));
53
126
  if (node.type.name === 'orderedList') {
54
- var _node$attrs;
127
+ var _node$attrs2;
55
128
  // If a numbered list has item counters numbering >= 100, we'll need to add special
56
129
  // spacing to account for the extra digit chars
57
130
  var digitsSize = getItemCounterDigitsSize({
58
131
  itemsCount: node === null || node === void 0 ? void 0 : node.childCount,
59
- order: node === null || node === void 0 || (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.order
132
+ order: node === null || node === void 0 || (_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.order
60
133
  });
61
134
  if (digitsSize && digitsSize > 1) {
62
135
  decorations.push(Decoration.node(from, to, {
@@ -116,27 +189,61 @@ var reducer = function reducer() {
116
189
  return state;
117
190
  };
118
191
  };
119
- var createInitialState = function createInitialState(featureFlags) {
192
+ var createInitialState = function createInitialState(featureFlags, api) {
120
193
  return function (state) {
121
- return _objectSpread(_objectSpread({}, editorExperiment('platform_editor_toolbar_aifc', true) && expValEquals('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState), {}, {
122
- decorationSet: getDecorations(state.doc, state, featureFlags)
194
+ var isToolbarAIFCEnabled = Boolean(api === null || api === void 0 ? void 0 : api.toolbar);
195
+ var getInitialDecorations = function getInitialDecorations() {
196
+ var _insm$session, _insm$session2;
197
+ (_insm$session = insm.session) === null || _insm$session === void 0 || _insm$session.startFeature('listDecorationsInit');
198
+ var decorations = computeListDecorations(state.doc);
199
+ (_insm$session2 = insm.session) === null || _insm$session2 === void 0 || _insm$session2.endFeature('listDecorationsInit');
200
+ return decorations;
201
+ };
202
+ return _objectSpread(_objectSpread({}, isToolbarAIFCEnabled && expValEquals('platform_editor_toolbar_aifc_patch_3', 'isEnabled', true) ? getListState(state.doc, state.selection) : initialState), {}, {
203
+ decorationSet: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? DecorationSet.empty.add(state.doc, getInitialDecorations()) : getDecorations(state.doc, state, featureFlags)
123
204
  });
124
205
  };
125
206
  };
126
- export var createPlugin = function createPlugin(eventDispatch, featureFlags) {
207
+ export var createPlugin = function createPlugin(eventDispatch, featureFlags, api) {
127
208
  var _pluginFactory = pluginFactory(listPluginKey, reducer(), {
128
209
  onDocChanged: handleDocChanged(featureFlags),
129
210
  onSelectionChanged: handleSelectionChanged
130
211
  }),
131
212
  getPluginState = _pluginFactory.getPluginState,
132
213
  createPluginState = _pluginFactory.createPluginState;
214
+ var pluginState = createPluginState(eventDispatch, createInitialState(featureFlags, api));
215
+ var pluginStateInit = function pluginStateInit(_, state) {
216
+ return createInitialState(featureFlags)(state);
217
+ };
218
+ var pluginStateApply = function pluginStateApply(tr, oldPluginState, _oldEditorState, _newEditorState) {
219
+ var nextPluginState = oldPluginState;
220
+ if (tr.docChanged) {
221
+ var _insm$session3, _insm$session4;
222
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
223
+ (_insm$session3 = insm.session) === null || _insm$session3 === void 0 || _insm$session3.startFeature('listDecorationUpdate');
224
+ var nextDecorationSet = updateListDecorations(nextPluginState.decorationSet, tr);
225
+ (_insm$session4 = insm.session) === null || _insm$session4 === void 0 || _insm$session4.endFeature('listDecorationUpdate');
226
+ nextPluginState = _objectSpread(_objectSpread({}, nextPluginState), {}, {
227
+ decorationSet: nextDecorationSet
228
+ });
229
+ } else if (tr.selectionSet) {
230
+ nextPluginState = handleSelectionChanged(tr, nextPluginState);
231
+ }
232
+ if (nextPluginState !== oldPluginState) {
233
+ eventDispatch(listPluginKey, nextPluginState);
234
+ }
235
+ return nextPluginState;
236
+ };
133
237
  return new SafePlugin({
134
- state: createPluginState(eventDispatch, createInitialState(featureFlags)),
238
+ state: {
239
+ init: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateInit : pluginState.init,
240
+ apply: expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? pluginStateApply : pluginState.apply
241
+ },
135
242
  key: listPluginKey,
136
243
  props: {
137
244
  decorations: function decorations(state) {
138
- var _getPluginState = getPluginState(state),
139
- decorationSet = _getPluginState.decorationSet;
245
+ var _ref = expValEquals('platform_editor_new_list_decorations_logic', 'isEnabled', true) ? listPluginKey.getState(state) : getPluginState(state),
246
+ decorationSet = _ref.decorationSet;
140
247
  return decorationSet;
141
248
  },
142
249
  handleClick: function handleClick(view, pos, event) {
@@ -2,11 +2,13 @@ import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/t
2
2
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
3
  import type { BlockMenuPlugin } from '@atlaskit/editor-plugin-block-menu';
4
4
  import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
5
+ import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
5
6
  import type { FindRootParentListNode, IndentList, IsInsideListItem, ListState, OutdentList, ToggleBulletList, ToggleOrderedList } from './types';
6
7
  export type ListPluginDependencies = [
7
8
  OptionalPlugin<FeatureFlagsPlugin>,
8
9
  OptionalPlugin<AnalyticsPlugin>,
9
- OptionalPlugin<BlockMenuPlugin>
10
+ OptionalPlugin<BlockMenuPlugin>,
11
+ OptionalPlugin<ToolbarPlugin>
10
12
  ];
11
13
  export type ListPluginActions = {
12
14
  findRootParentListNode: FindRootParentListNode;
@@ -1,11 +1,14 @@
1
1
  import type { Dispatch } from '@atlaskit/editor-common/event-dispatcher';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
- import type { FeatureFlags } from '@atlaskit/editor-common/types';
3
+ import type { ExtractInjectionAPI, FeatureFlags } from '@atlaskit/editor-common/types';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
- import type { EditorState } from '@atlaskit/editor-prosemirror/state';
7
- import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
6
+ import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
7
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
+ import type { ListPlugin } from '../listPluginType';
8
9
  import type { ListState } from '../types';
9
10
  export declare const pluginKey: PluginKey<ListState>;
11
+ export declare const computeListDecorations: (doc: Node, from?: number, to?: number) => Decoration[];
12
+ export declare const updateListDecorations: (decorationSet: DecorationSet, tr: ReadonlyTransaction) => DecorationSet;
10
13
  export declare const getDecorations: (doc: Node, state: EditorState, featureFlags: FeatureFlags) => DecorationSet;
11
- export declare const createPlugin: (eventDispatch: Dispatch, featureFlags: FeatureFlags) => SafePlugin;
14
+ export declare const createPlugin: (eventDispatch: Dispatch, featureFlags: FeatureFlags, api?: ExtractInjectionAPI<ListPlugin>) => SafePlugin;
@@ -2,11 +2,13 @@ import type { NextEditorPlugin, OptionalPlugin } from '@atlaskit/editor-common/t
2
2
  import type { AnalyticsPlugin } from '@atlaskit/editor-plugin-analytics';
3
3
  import type { BlockMenuPlugin } from '@atlaskit/editor-plugin-block-menu';
4
4
  import type { FeatureFlagsPlugin } from '@atlaskit/editor-plugin-feature-flags';
5
+ import type { ToolbarPlugin } from '@atlaskit/editor-plugin-toolbar';
5
6
  import type { FindRootParentListNode, IndentList, IsInsideListItem, ListState, OutdentList, ToggleBulletList, ToggleOrderedList } from './types';
6
7
  export type ListPluginDependencies = [
7
8
  OptionalPlugin<FeatureFlagsPlugin>,
8
9
  OptionalPlugin<AnalyticsPlugin>,
9
- OptionalPlugin<BlockMenuPlugin>
10
+ OptionalPlugin<BlockMenuPlugin>,
11
+ OptionalPlugin<ToolbarPlugin>
10
12
  ];
11
13
  export type ListPluginActions = {
12
14
  findRootParentListNode: FindRootParentListNode;
@@ -1,11 +1,14 @@
1
1
  import type { Dispatch } from '@atlaskit/editor-common/event-dispatcher';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
- import type { FeatureFlags } from '@atlaskit/editor-common/types';
3
+ import type { ExtractInjectionAPI, FeatureFlags } from '@atlaskit/editor-common/types';
4
4
  import type { Node } from '@atlaskit/editor-prosemirror/model';
5
5
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
6
- import type { EditorState } from '@atlaskit/editor-prosemirror/state';
7
- import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
6
+ import type { EditorState, ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
7
+ import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
+ import type { ListPlugin } from '../listPluginType';
8
9
  import type { ListState } from '../types';
9
10
  export declare const pluginKey: PluginKey<ListState>;
11
+ export declare const computeListDecorations: (doc: Node, from?: number, to?: number) => Decoration[];
12
+ export declare const updateListDecorations: (decorationSet: DecorationSet, tr: ReadonlyTransaction) => DecorationSet;
10
13
  export declare const getDecorations: (doc: Node, state: EditorState, featureFlags: FeatureFlags) => DecorationSet;
11
- export declare const createPlugin: (eventDispatch: Dispatch, featureFlags: FeatureFlags) => SafePlugin;
14
+ export declare const createPlugin: (eventDispatch: Dispatch, featureFlags: FeatureFlags, api?: ExtractInjectionAPI<ListPlugin>) => SafePlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-list",
3
- "version": "8.1.1",
3
+ "version": "8.1.3",
4
4
  "description": "List plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -31,17 +31,19 @@
31
31
  "@atlaskit/editor-plugin-analytics": "^6.1.0",
32
32
  "@atlaskit/editor-plugin-block-menu": "^4.0.0",
33
33
  "@atlaskit/editor-plugin-feature-flags": "^5.0.0",
34
+ "@atlaskit/editor-plugin-toolbar": "^3.3.0",
34
35
  "@atlaskit/editor-prosemirror": "7.0.0",
35
36
  "@atlaskit/editor-toolbar": "^0.15.0",
36
37
  "@atlaskit/icon": "^28.5.0",
38
+ "@atlaskit/insm": "^0.1.0",
37
39
  "@atlaskit/platform-feature-flags": "^1.1.0",
38
40
  "@atlaskit/prosemirror-history": "^0.2.0",
39
41
  "@atlaskit/prosemirror-input-rules": "^3.5.0",
40
- "@atlaskit/tmp-editor-statsig": "^13.8.0",
42
+ "@atlaskit/tmp-editor-statsig": "^13.12.0",
41
43
  "@babel/runtime": "^7.0.0"
42
44
  },
43
45
  "peerDependencies": {
44
- "@atlaskit/editor-common": "^110.10.0",
46
+ "@atlaskit/editor-common": "^110.13.0",
45
47
  "react": "^18.2.0",
46
48
  "react-intl-next": "npm:react-intl@^5.18.1"
47
49
  },