@atlaskit/editor-plugin-code-block 3.3.9 → 3.3.10

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,13 @@
1
1
  # @atlaskit/editor-plugin-code-block
2
2
 
3
+ ## 3.3.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [`cbe3b04ebb0b6`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/cbe3b04ebb0b6) -
8
+ ED-24730 - Address bug where word wrap decorator broke on drag and drop. Required some refactoring
9
+ of decoration functions for code block. Moved all of them to decorators.ts and added unit tests.
10
+
3
11
  ## 3.3.9
4
12
 
5
13
  ### Patch Changes
@@ -89,6 +89,12 @@ var CodeBlockView = exports.CodeBlockView = /*#__PURE__*/function () {
89
89
  this.api = api;
90
90
  if ((0, _platformFeatureFlags.fg)('editor_support_code_block_wrapping')) {
91
91
  this.maintainDynamicGutterSize();
92
+
93
+ // Ensure the code block node has a wrapped state.
94
+ // Wrapped state may already exist from breakout's recreating the node.
95
+ if (!_codeBlock.codeBlockWrappedStates.has(_node)) {
96
+ _codeBlock.codeBlockWrappedStates.set(_node, _codeBlock.defaultWordWrapState);
97
+ }
92
98
  } else {
93
99
  this.ensureLineNumbers();
94
100
  }
@@ -4,14 +4,65 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.generateLineAttributesFromNode = exports.createLineNumbersDecorations = exports.createDecorationSetFromLineAttributes = exports.DECORATION_WIDGET_TYPE = void 0;
7
+ exports.validateWordWrappedDecorators = exports.updateDecorationSetWithWordWrappedDecorator = exports.updateDecorationSetWithLineNumberDecorators = exports.updateCodeBlockDecorations = exports.getWordWrapDecoratorsFromNodePos = exports.generateLineAttributesFromNode = exports.generateInitialDecorations = exports.createLineNumbersDecorations = exports.createDecorationSetFromLineAttributes = exports.DECORATION_WRAPPED_BLOCK_NODE_TYPE = exports.DECORATION_WIDGET_TYPE = void 0;
8
8
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ var _codeBlock = require("@atlaskit/editor-common/code-block");
9
10
  var _view = require("@atlaskit/editor-prosemirror/view");
10
11
  var _classNames = require("../ui/class-names");
12
+ var _utils = require("../utils");
11
13
  var DECORATION_WIDGET_TYPE = exports.DECORATION_WIDGET_TYPE = 'decorationWidgetType';
12
- var generateLineAttributesFromNode = exports.generateLineAttributesFromNode = function generateLineAttributesFromNode(pos, node) {
14
+ var DECORATION_WRAPPED_BLOCK_NODE_TYPE = exports.DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
15
+
16
+ /**
17
+ * Generate the initial decorations for the code block.
18
+ */
19
+ var generateInitialDecorations = exports.generateInitialDecorations = function generateInitialDecorations(state) {
20
+ var codeBlockNodes = (0, _utils.getAllCodeBlockNodesInDoc)(state);
21
+ return codeBlockNodes.flatMap(function (node) {
22
+ return createLineNumbersDecorations(node);
23
+ });
24
+ };
25
+
26
+ /**
27
+ * Update all the decorations used by the code block.
28
+ */
29
+ var updateCodeBlockDecorations = exports.updateCodeBlockDecorations = function updateCodeBlockDecorations(tr, state, decorationSet) {
30
+ var updatedDecorationSet = decorationSet;
31
+ var codeBlockNodes = (0, _utils.getAllCodeBlockNodesInDoc)(state);
32
+
33
+ // All the line numbers decorators are refreshed on doc change.
34
+ updatedDecorationSet = updateDecorationSetWithLineNumberDecorators(tr, codeBlockNodes, updatedDecorationSet);
35
+
36
+ // Check to make sure the word wrap decorators are still valid.
37
+ updatedDecorationSet = validateWordWrappedDecorators(tr, codeBlockNodes, updatedDecorationSet);
38
+ return updatedDecorationSet;
39
+ };
40
+
41
+ /**
42
+ * Update the decorations set with the line number decorators.
43
+ */
44
+ var updateDecorationSetWithLineNumberDecorators = exports.updateDecorationSetWithLineNumberDecorators = function updateDecorationSetWithLineNumberDecorators(tr, codeBlockNodes, decorationSet) {
45
+ var updatedDecorationSet = decorationSet;
46
+ // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
47
+ var children = updatedDecorationSet.find(undefined, undefined, function (spec) {
48
+ return spec.type === DECORATION_WIDGET_TYPE;
49
+ });
50
+ updatedDecorationSet = updatedDecorationSet.remove(children);
51
+
52
+ // regenerate all the line number for the documents code blocks
53
+ var lineNumberDecorators = [];
54
+ codeBlockNodes.forEach(function (node) {
55
+ lineNumberDecorators.push.apply(lineNumberDecorators, (0, _toConsumableArray2.default)(createLineNumbersDecorations(node)));
56
+ });
57
+
58
+ // add the newly generated line numbers to the decorations set
59
+ return updatedDecorationSet.add(tr.doc, [].concat(lineNumberDecorators));
60
+ };
61
+ var generateLineAttributesFromNode = exports.generateLineAttributesFromNode = function generateLineAttributesFromNode(node) {
62
+ var innerNode = node.node,
63
+ pos = node.pos;
13
64
  // Get content node
14
- var contentNode = node.content;
65
+ var contentNode = innerNode.content;
15
66
 
16
67
  // Get node text content
17
68
  var lineAttributes = [];
@@ -64,6 +115,60 @@ var createDecorationSetFromLineAttributes = exports.createDecorationSetFromLineA
64
115
  });
65
116
  return widgetDecorations;
66
117
  };
67
- var createLineNumbersDecorations = exports.createLineNumbersDecorations = function createLineNumbersDecorations(pos, node) {
68
- return createDecorationSetFromLineAttributes(generateLineAttributesFromNode(pos, node));
118
+ var createLineNumbersDecorations = exports.createLineNumbersDecorations = function createLineNumbersDecorations(node) {
119
+ return createDecorationSetFromLineAttributes(generateLineAttributesFromNode(node));
120
+ };
121
+
122
+ /**
123
+ * There are edge cases like when a user drags and drops a code block node where the decorator breaks and no longer reflects
124
+ * the correct word wrap state. This function validates that the decorator and the state are in line, otherwise it will
125
+ * retrigger the logic to apply the word wrap decorator.
126
+ */
127
+ var validateWordWrappedDecorators = exports.validateWordWrappedDecorators = function validateWordWrappedDecorators(tr, codeBlockNodes, decorationSet) {
128
+ var updatedDecorationSet = decorationSet;
129
+ codeBlockNodes.forEach(function (node) {
130
+ var isCodeBlockWrappedInState = (0, _codeBlock.isCodeBlockWordWrapEnabled)(node.node);
131
+ var isCodeBlockWrappedByDecorator = getWordWrapDecoratorsFromNodePos(node.pos, decorationSet).length !== 0;
132
+ if (isCodeBlockWrappedInState && !isCodeBlockWrappedByDecorator) {
133
+ updatedDecorationSet = updateDecorationSetWithWordWrappedDecorator(decorationSet, tr, node);
134
+ }
135
+ });
136
+ return updatedDecorationSet;
137
+ };
138
+
139
+ /**
140
+ * Update the decoration set with the word wrap decorator.
141
+ */
142
+ var updateDecorationSetWithWordWrappedDecorator = exports.updateDecorationSetWithWordWrappedDecorator = function updateDecorationSetWithWordWrappedDecorator(decorationSet, tr, node) {
143
+ if (!node || node.pos === undefined) {
144
+ return decorationSet;
145
+ }
146
+ var updatedDecorationSet = decorationSet;
147
+ var pos = node.pos,
148
+ innerNode = node.node;
149
+ var isNodeWrapped = (0, _codeBlock.isCodeBlockWordWrapEnabled)(innerNode);
150
+ if (!isNodeWrapped) {
151
+ var currentWrappedBlockDecorationSet = getWordWrapDecoratorsFromNodePos(pos, updatedDecorationSet);
152
+ updatedDecorationSet = updatedDecorationSet.remove(currentWrappedBlockDecorationSet);
153
+ } else {
154
+ var wrappedBlock = _view.Decoration.node(pos, pos + innerNode.nodeSize, {
155
+ class: _classNames.codeBlockClassNames.contentFgWrapped
156
+ }, {
157
+ type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
158
+ } // Allows for quick filtering of decorations while using `find`
159
+ );
160
+ updatedDecorationSet = updatedDecorationSet.add(tr.doc, [wrappedBlock]);
161
+ }
162
+ return updatedDecorationSet;
163
+ };
164
+
165
+ /**
166
+ * Get the word wrap decorators for the given node position.
167
+ */
168
+ var getWordWrapDecoratorsFromNodePos = exports.getWordWrapDecoratorsFromNodePos = function getWordWrapDecoratorsFromNodePos(pos, decorationSet) {
169
+ var codeBlockNodePosition = pos + 1; // We need to add 1 to the position to get the start of the node.
170
+ var currentWrappedBlockDecorationSet = decorationSet.find(codeBlockNodePosition, codeBlockNodePosition, function (spec) {
171
+ return spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE;
172
+ });
173
+ return currentWrappedBlockDecorationSet;
69
174
  };
@@ -6,8 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.createPlugin = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
- var _codeBlock = require("@atlaskit/editor-common/code-block");
11
9
  var _messages = require("@atlaskit/editor-common/messages");
12
10
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
13
11
  var _selection = require("@atlaskit/editor-common/selection");
@@ -16,7 +14,7 @@ var _state = require("@atlaskit/editor-prosemirror/state");
16
14
  var _view = require("@atlaskit/editor-prosemirror/view");
17
15
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
16
  var _actions = require("../actions");
19
- var _codeBlock2 = require("../nodeviews/code-block");
17
+ var _codeBlock = require("../nodeviews/code-block");
20
18
  var _pluginKey = require("../plugin-key");
21
19
  var _classNames = require("../ui/class-names");
22
20
  var _utils2 = require("../utils");
@@ -24,7 +22,6 @@ var _actions2 = require("./actions");
24
22
  var _decorators = require("./decorators");
25
23
  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; }
26
24
  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
- var DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
28
25
  var createPlugin = exports.createPlugin = function createPlugin(_ref) {
29
26
  var _ref$useLongPressSele = _ref.useLongPressSelection,
30
27
  useLongPressSelection = _ref$useLongPressSele === void 0 ? false : _ref$useLongPressSele,
@@ -33,57 +30,6 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
33
30
  allowCompositionInputOverride = _ref$allowComposition === void 0 ? false : _ref$allowComposition,
34
31
  api = _ref.api;
35
32
  var handleDOMEvents = {};
36
- var createLineNumberDecoratorsFromDecendants = function createLineNumberDecoratorsFromDecendants(editorState) {
37
- var lineNumberDecorators = [];
38
- editorState.doc.descendants(function (node, pos) {
39
- if (node.type === editorState.schema.nodes.codeBlock) {
40
- lineNumberDecorators.push.apply(lineNumberDecorators, (0, _toConsumableArray2.default)((0, _decorators.createLineNumbersDecorations)(pos, node)));
41
- return false;
42
- }
43
- return true;
44
- });
45
- return lineNumberDecorators;
46
- };
47
- var createWordWrappedDecoratorPluginState = function createWordWrappedDecoratorPluginState(pluginState, tr, node) {
48
- var decorationSetFromState = pluginState.decorations;
49
- if (node) {
50
- var pos = node.pos,
51
- innerNode = node.node;
52
- if (pos !== undefined) {
53
- var isNodeWrapped = (0, _codeBlock.isCodeBlockWordWrapEnabled)(innerNode);
54
- if (!isNodeWrapped) {
55
- // Restricts the range of decorators to the current node while not including the previous nodes position in range
56
- var codeBlockNodePosition = pos + 1;
57
- var currentWrappedBlockDecorationSet = decorationSetFromState.find(codeBlockNodePosition, codeBlockNodePosition, function (spec) {
58
- return spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE;
59
- });
60
- decorationSetFromState = decorationSetFromState.remove(currentWrappedBlockDecorationSet);
61
- } else {
62
- var wrappedBlock = _view.Decoration.node(pos, pos + innerNode.nodeSize, {
63
- class: _classNames.codeBlockClassNames.contentFgWrapped
64
- }, {
65
- type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
66
- } // Allows for quick filtering of decorations while using `find`
67
- );
68
- decorationSetFromState = decorationSetFromState.add(tr.doc, [wrappedBlock]);
69
- }
70
- }
71
- }
72
- return decorationSetFromState;
73
- };
74
- var updateLineDecorationSet = function updateLineDecorationSet(tr, state, decorationSet) {
75
- // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
76
- var children = decorationSet.find(undefined, undefined, function (spec) {
77
- return spec.type === _decorators.DECORATION_WIDGET_TYPE;
78
- });
79
- decorationSet = decorationSet.remove(children);
80
-
81
- // regenerate all the line number for the documents code blocks
82
- var lineDecorators = createLineNumberDecoratorsFromDecendants(state);
83
-
84
- // add the newly generated line numbers to the decorations set
85
- return decorationSet.add(tr.doc, (0, _toConsumableArray2.default)(lineDecorators));
86
- };
87
33
 
88
34
  // ME-1599: Composition on mobile was causing the DOM observer to mutate the code block
89
35
  // incorrecly and lose content when pressing enter in the middle of a code block line.
@@ -127,13 +73,13 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
127
73
  state: {
128
74
  init: function init(_, state) {
129
75
  var node = (0, _utils2.findCodeBlock)(state, state.selection);
130
- var lineNumberDecorators = (0, _platformFeatureFlags.fg)('editor_support_code_block_wrapping') ? createLineNumberDecoratorsFromDecendants(state) : [];
76
+ var initialDecorations = (0, _platformFeatureFlags.fg)('editor_support_code_block_wrapping') ? (0, _decorators.generateInitialDecorations)(state) : [];
131
77
  return {
132
78
  pos: node ? node.pos : null,
133
79
  contentCopied: false,
134
80
  isNodeSelected: false,
135
81
  shouldIgnoreFollowingMutations: false,
136
- decorations: _view.DecorationSet.create(state.doc, lineNumberDecorators)
82
+ decorations: _view.DecorationSet.create(state.doc, initialDecorations)
137
83
  };
138
84
  },
139
85
  apply: function apply(tr, pluginState, _oldState, newState) {
@@ -141,18 +87,18 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
141
87
  if ((meta === null || meta === void 0 ? void 0 : meta.type) === _actions2.ACTIONS.SET_IS_WRAPPED && (0, _platformFeatureFlags.fg)('editor_support_code_block_wrapping')) {
142
88
  var node = (0, _utils2.findCodeBlock)(newState, tr.selection);
143
89
  return _objectSpread(_objectSpread({}, pluginState), {}, {
144
- decorations: createWordWrappedDecoratorPluginState(pluginState, tr, node)
90
+ decorations: (0, _decorators.updateDecorationSetWithWordWrappedDecorator)(pluginState.decorations, tr, node)
145
91
  });
146
92
  }
147
93
  if (tr.docChanged) {
148
94
  var _node = (0, _utils2.findCodeBlock)(newState, tr.selection);
149
95
 
150
96
  // Updates mapping position of all existing decorations to new positions
151
- // specifically used for updating word wrap node decorators
97
+ // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
152
98
  var updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
153
-
154
- // Wipe and regenerate all line numbers in the document
155
- updatedDecorationSet = updateLineDecorationSet(tr, newState, updatedDecorationSet);
99
+ if ((0, _platformFeatureFlags.fg)('editor_support_code_block_wrapping')) {
100
+ updatedDecorationSet = (0, _decorators.updateCodeBlockDecorations)(tr, newState, updatedDecorationSet);
101
+ }
156
102
  var newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
157
103
  pos: _node ? _node.pos : null,
158
104
  isNodeSelected: tr.selection instanceof _state.NodeSelection,
@@ -193,7 +139,7 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
193
139
  var _getIntl = getIntl(),
194
140
  formatMessage = _getIntl.formatMessage;
195
141
  var formattedAriaLabel = formatMessage(_messages.blockTypeMessages.codeblock);
196
- return (0, _codeBlock2.codeBlockNodeView)(node, view, getPos, formattedAriaLabel, api);
142
+ return (0, _codeBlock.codeBlockNodeView)(node, view, getPos, formattedAriaLabel, api);
197
143
  }
198
144
  },
199
145
  handleClickOn: (0, _selection.createSelectionClickHandler)(['codeBlock'], function (target) {
@@ -109,7 +109,6 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
109
109
  supportsViewMode: true,
110
110
  icon: _WrapIcon.WrapIcon,
111
111
  onClick: (0, _actions.toggleWordWrapStateForCodeBlockNode)(editorAnalyticsAPI),
112
- // Hooking up here for demo purposes. To be revisited with ED-24222.
113
112
  title: isWrapped ? formatMessage(_messages.codeBlockButtonMessages.unwrapCode) : formatMessage(_messages.codeBlockButtonMessages.wrapCode),
114
113
  tabIndex: null,
115
114
  selected: isWrapped
package/dist/cjs/utils.js CHANGED
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "findCodeBlock", {
9
9
  return _transforms.findCodeBlock;
10
10
  }
11
11
  });
12
+ exports.getAllCodeBlockNodesInDoc = getAllCodeBlockNodesInDoc;
12
13
  exports.getCursor = getCursor;
13
14
  Object.defineProperty(exports, "transformSingleLineCodeBlockToCodeMark", {
14
15
  enumerable: true,
@@ -25,4 +26,18 @@ Object.defineProperty(exports, "transformSliceToJoinAdjacentCodeBlocks", {
25
26
  var _transforms = require("@atlaskit/editor-common/transforms");
26
27
  function getCursor(selection) {
27
28
  return selection.$cursor || undefined;
29
+ }
30
+ function getAllCodeBlockNodesInDoc(state) {
31
+ var codeBlockNodes = [];
32
+ state.doc.descendants(function (node, pos) {
33
+ if (node.type === state.schema.nodes.codeBlock) {
34
+ codeBlockNodes.push({
35
+ node: node,
36
+ pos: pos
37
+ });
38
+ return false;
39
+ }
40
+ return true;
41
+ });
42
+ return codeBlockNodes;
28
43
  }
@@ -1,6 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import rafSchedule from 'raf-schd';
3
- import { transferCodeBlockWrappedValue } from '@atlaskit/editor-common/code-block';
3
+ import { codeBlockWrappedStates, defaultWordWrapState, transferCodeBlockWrappedValue } from '@atlaskit/editor-common/code-block';
4
4
  import { browser } from '@atlaskit/editor-common/utils';
5
5
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
6
6
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -77,6 +77,12 @@ export class CodeBlockView {
77
77
  this.api = api;
78
78
  if (fg('editor_support_code_block_wrapping')) {
79
79
  this.maintainDynamicGutterSize();
80
+
81
+ // Ensure the code block node has a wrapped state.
82
+ // Wrapped state may already exist from breakout's recreating the node.
83
+ if (!codeBlockWrappedStates.has(_node)) {
84
+ codeBlockWrappedStates.set(_node, defaultWordWrapState);
85
+ }
80
86
  } else {
81
87
  this.ensureLineNumbers();
82
88
  }
@@ -1,9 +1,58 @@
1
+ import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
1
2
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
2
3
  import { codeBlockClassNames } from '../ui/class-names';
4
+ import { getAllCodeBlockNodesInDoc } from '../utils';
3
5
  export const DECORATION_WIDGET_TYPE = 'decorationWidgetType';
4
- export const generateLineAttributesFromNode = (pos, node) => {
6
+ export const DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
7
+
8
+ /**
9
+ * Generate the initial decorations for the code block.
10
+ */
11
+ export const generateInitialDecorations = state => {
12
+ const codeBlockNodes = getAllCodeBlockNodesInDoc(state);
13
+ return codeBlockNodes.flatMap(node => createLineNumbersDecorations(node));
14
+ };
15
+
16
+ /**
17
+ * Update all the decorations used by the code block.
18
+ */
19
+ export const updateCodeBlockDecorations = (tr, state, decorationSet) => {
20
+ let updatedDecorationSet = decorationSet;
21
+ const codeBlockNodes = getAllCodeBlockNodesInDoc(state);
22
+
23
+ // All the line numbers decorators are refreshed on doc change.
24
+ updatedDecorationSet = updateDecorationSetWithLineNumberDecorators(tr, codeBlockNodes, updatedDecorationSet);
25
+
26
+ // Check to make sure the word wrap decorators are still valid.
27
+ updatedDecorationSet = validateWordWrappedDecorators(tr, codeBlockNodes, updatedDecorationSet);
28
+ return updatedDecorationSet;
29
+ };
30
+
31
+ /**
32
+ * Update the decorations set with the line number decorators.
33
+ */
34
+ export const updateDecorationSetWithLineNumberDecorators = (tr, codeBlockNodes, decorationSet) => {
35
+ let updatedDecorationSet = decorationSet;
36
+ // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
37
+ const children = updatedDecorationSet.find(undefined, undefined, spec => spec.type === DECORATION_WIDGET_TYPE);
38
+ updatedDecorationSet = updatedDecorationSet.remove(children);
39
+
40
+ // regenerate all the line number for the documents code blocks
41
+ const lineNumberDecorators = [];
42
+ codeBlockNodes.forEach(node => {
43
+ lineNumberDecorators.push(...createLineNumbersDecorations(node));
44
+ });
45
+
46
+ // add the newly generated line numbers to the decorations set
47
+ return updatedDecorationSet.add(tr.doc, [...lineNumberDecorators]);
48
+ };
49
+ export const generateLineAttributesFromNode = node => {
50
+ const {
51
+ node: innerNode,
52
+ pos
53
+ } = node;
5
54
  // Get content node
6
- const contentNode = node.content;
55
+ const contentNode = innerNode.content;
7
56
 
8
57
  // Get node text content
9
58
  let lineAttributes = [];
@@ -58,4 +107,58 @@ export const createDecorationSetFromLineAttributes = lineAttributes => {
58
107
  });
59
108
  return widgetDecorations;
60
109
  };
61
- export const createLineNumbersDecorations = (pos, node) => createDecorationSetFromLineAttributes(generateLineAttributesFromNode(pos, node));
110
+ export const createLineNumbersDecorations = node => createDecorationSetFromLineAttributes(generateLineAttributesFromNode(node));
111
+
112
+ /**
113
+ * There are edge cases like when a user drags and drops a code block node where the decorator breaks and no longer reflects
114
+ * the correct word wrap state. This function validates that the decorator and the state are in line, otherwise it will
115
+ * retrigger the logic to apply the word wrap decorator.
116
+ */
117
+ export const validateWordWrappedDecorators = (tr, codeBlockNodes, decorationSet) => {
118
+ let updatedDecorationSet = decorationSet;
119
+ codeBlockNodes.forEach(node => {
120
+ const isCodeBlockWrappedInState = isCodeBlockWordWrapEnabled(node.node);
121
+ const isCodeBlockWrappedByDecorator = getWordWrapDecoratorsFromNodePos(node.pos, decorationSet).length !== 0;
122
+ if (isCodeBlockWrappedInState && !isCodeBlockWrappedByDecorator) {
123
+ updatedDecorationSet = updateDecorationSetWithWordWrappedDecorator(decorationSet, tr, node);
124
+ }
125
+ });
126
+ return updatedDecorationSet;
127
+ };
128
+
129
+ /**
130
+ * Update the decoration set with the word wrap decorator.
131
+ */
132
+ export const updateDecorationSetWithWordWrappedDecorator = (decorationSet, tr, node) => {
133
+ if (!node || node.pos === undefined) {
134
+ return decorationSet;
135
+ }
136
+ let updatedDecorationSet = decorationSet;
137
+ const {
138
+ pos,
139
+ node: innerNode
140
+ } = node;
141
+ const isNodeWrapped = isCodeBlockWordWrapEnabled(innerNode);
142
+ if (!isNodeWrapped) {
143
+ const currentWrappedBlockDecorationSet = getWordWrapDecoratorsFromNodePos(pos, updatedDecorationSet);
144
+ updatedDecorationSet = updatedDecorationSet.remove(currentWrappedBlockDecorationSet);
145
+ } else {
146
+ const wrappedBlock = Decoration.node(pos, pos + innerNode.nodeSize, {
147
+ class: codeBlockClassNames.contentFgWrapped
148
+ }, {
149
+ type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
150
+ } // Allows for quick filtering of decorations while using `find`
151
+ );
152
+ updatedDecorationSet = updatedDecorationSet.add(tr.doc, [wrappedBlock]);
153
+ }
154
+ return updatedDecorationSet;
155
+ };
156
+
157
+ /**
158
+ * Get the word wrap decorators for the given node position.
159
+ */
160
+ export const getWordWrapDecoratorsFromNodePos = (pos, decorationSet) => {
161
+ const codeBlockNodePosition = pos + 1; // We need to add 1 to the position to get the start of the node.
162
+ const currentWrappedBlockDecorationSet = decorationSet.find(codeBlockNodePosition, codeBlockNodePosition, spec => spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE);
163
+ return currentWrappedBlockDecorationSet;
164
+ };
@@ -1,10 +1,9 @@
1
- import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
2
1
  import { blockTypeMessages } from '@atlaskit/editor-common/messages';
3
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
4
3
  import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
5
4
  import { browser } from '@atlaskit/editor-common/utils';
6
5
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
7
- import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
6
+ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
7
  import { fg } from '@atlaskit/platform-feature-flags';
9
8
  import { ignoreFollowingMutations, resetShouldIgnoreFollowingMutations } from '../actions';
10
9
  import { codeBlockNodeView } from '../nodeviews/code-block';
@@ -12,8 +11,7 @@ import { pluginKey } from '../plugin-key';
12
11
  import { codeBlockClassNames } from '../ui/class-names';
13
12
  import { findCodeBlock } from '../utils';
14
13
  import { ACTIONS } from './actions';
15
- import { createLineNumbersDecorations, DECORATION_WIDGET_TYPE } from './decorators';
16
- const DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
14
+ import { generateInitialDecorations, updateCodeBlockDecorations, updateDecorationSetWithWordWrappedDecorator } from './decorators';
17
15
  export const createPlugin = ({
18
16
  useLongPressSelection = false,
19
17
  getIntl,
@@ -21,55 +19,6 @@ export const createPlugin = ({
21
19
  api
22
20
  }) => {
23
21
  const handleDOMEvents = {};
24
- const createLineNumberDecoratorsFromDecendants = editorState => {
25
- let lineNumberDecorators = [];
26
- editorState.doc.descendants((node, pos) => {
27
- if (node.type === editorState.schema.nodes.codeBlock) {
28
- lineNumberDecorators.push(...createLineNumbersDecorations(pos, node));
29
- return false;
30
- }
31
- return true;
32
- });
33
- return lineNumberDecorators;
34
- };
35
- const createWordWrappedDecoratorPluginState = (pluginState, tr, node) => {
36
- let decorationSetFromState = pluginState.decorations;
37
- if (node) {
38
- const {
39
- pos,
40
- node: innerNode
41
- } = node;
42
- if (pos !== undefined) {
43
- const isNodeWrapped = isCodeBlockWordWrapEnabled(innerNode);
44
- if (!isNodeWrapped) {
45
- // Restricts the range of decorators to the current node while not including the previous nodes position in range
46
- const codeBlockNodePosition = pos + 1;
47
- const currentWrappedBlockDecorationSet = decorationSetFromState.find(codeBlockNodePosition, codeBlockNodePosition, spec => spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE);
48
- decorationSetFromState = decorationSetFromState.remove(currentWrappedBlockDecorationSet);
49
- } else {
50
- const wrappedBlock = Decoration.node(pos, pos + innerNode.nodeSize, {
51
- class: codeBlockClassNames.contentFgWrapped
52
- }, {
53
- type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
54
- } // Allows for quick filtering of decorations while using `find`
55
- );
56
- decorationSetFromState = decorationSetFromState.add(tr.doc, [wrappedBlock]);
57
- }
58
- }
59
- }
60
- return decorationSetFromState;
61
- };
62
- const updateLineDecorationSet = (tr, state, decorationSet) => {
63
- // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
64
- const children = decorationSet.find(undefined, undefined, spec => spec.type === DECORATION_WIDGET_TYPE);
65
- decorationSet = decorationSet.remove(children);
66
-
67
- // regenerate all the line number for the documents code blocks
68
- const lineDecorators = createLineNumberDecoratorsFromDecendants(state);
69
-
70
- // add the newly generated line numbers to the decorations set
71
- return decorationSet.add(tr.doc, [...lineDecorators]);
72
- };
73
22
 
74
23
  // ME-1599: Composition on mobile was causing the DOM observer to mutate the code block
75
24
  // incorrecly and lose content when pressing enter in the middle of a code block line.
@@ -111,13 +60,13 @@ export const createPlugin = ({
111
60
  state: {
112
61
  init(_, state) {
113
62
  const node = findCodeBlock(state, state.selection);
114
- const lineNumberDecorators = fg('editor_support_code_block_wrapping') ? createLineNumberDecoratorsFromDecendants(state) : [];
63
+ const initialDecorations = fg('editor_support_code_block_wrapping') ? generateInitialDecorations(state) : [];
115
64
  return {
116
65
  pos: node ? node.pos : null,
117
66
  contentCopied: false,
118
67
  isNodeSelected: false,
119
68
  shouldIgnoreFollowingMutations: false,
120
- decorations: DecorationSet.create(state.doc, lineNumberDecorators)
69
+ decorations: DecorationSet.create(state.doc, initialDecorations)
121
70
  };
122
71
  },
123
72
  apply(tr, pluginState, _oldState, newState) {
@@ -126,18 +75,18 @@ export const createPlugin = ({
126
75
  const node = findCodeBlock(newState, tr.selection);
127
76
  return {
128
77
  ...pluginState,
129
- decorations: createWordWrappedDecoratorPluginState(pluginState, tr, node)
78
+ decorations: updateDecorationSetWithWordWrappedDecorator(pluginState.decorations, tr, node)
130
79
  };
131
80
  }
132
81
  if (tr.docChanged) {
133
82
  const node = findCodeBlock(newState, tr.selection);
134
83
 
135
84
  // Updates mapping position of all existing decorations to new positions
136
- // specifically used for updating word wrap node decorators
85
+ // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
137
86
  let updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
138
-
139
- // Wipe and regenerate all line numbers in the document
140
- updatedDecorationSet = updateLineDecorationSet(tr, newState, updatedDecorationSet);
87
+ if (fg('editor_support_code_block_wrapping')) {
88
+ updatedDecorationSet = updateCodeBlockDecorations(tr, newState, updatedDecorationSet);
89
+ }
141
90
  const newPluginState = {
142
91
  ...pluginState,
143
92
  pos: node ? node.pos : null,
@@ -90,7 +90,6 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api) => (state, {
90
90
  supportsViewMode: true,
91
91
  icon: WrapIcon,
92
92
  onClick: toggleWordWrapStateForCodeBlockNode(editorAnalyticsAPI),
93
- // Hooking up here for demo purposes. To be revisited with ED-24222.
94
93
  title: isWrapped ? formatMessage(codeBlockButtonMessages.unwrapCode) : formatMessage(codeBlockButtonMessages.wrapCode),
95
94
  tabIndex: null,
96
95
  selected: isWrapped
@@ -1,4 +1,18 @@
1
1
  export { findCodeBlock, transformSliceToJoinAdjacentCodeBlocks, transformSingleLineCodeBlockToCodeMark } from '@atlaskit/editor-common/transforms';
2
2
  export function getCursor(selection) {
3
3
  return selection.$cursor || undefined;
4
+ }
5
+ export function getAllCodeBlockNodesInDoc(state) {
6
+ const codeBlockNodes = [];
7
+ state.doc.descendants((node, pos) => {
8
+ if (node.type === state.schema.nodes.codeBlock) {
9
+ codeBlockNodes.push({
10
+ node,
11
+ pos
12
+ });
13
+ return false;
14
+ }
15
+ return true;
16
+ });
17
+ return codeBlockNodes;
4
18
  }
@@ -2,7 +2,7 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  import rafSchedule from 'raf-schd';
5
- import { transferCodeBlockWrappedValue } from '@atlaskit/editor-common/code-block';
5
+ import { codeBlockWrappedStates, defaultWordWrapState, transferCodeBlockWrappedValue } from '@atlaskit/editor-common/code-block';
6
6
  import { browser } from '@atlaskit/editor-common/utils';
7
7
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -82,6 +82,12 @@ export var CodeBlockView = /*#__PURE__*/function () {
82
82
  this.api = api;
83
83
  if (fg('editor_support_code_block_wrapping')) {
84
84
  this.maintainDynamicGutterSize();
85
+
86
+ // Ensure the code block node has a wrapped state.
87
+ // Wrapped state may already exist from breakout's recreating the node.
88
+ if (!codeBlockWrappedStates.has(_node)) {
89
+ codeBlockWrappedStates.set(_node, defaultWordWrapState);
90
+ }
85
91
  } else {
86
92
  this.ensureLineNumbers();
87
93
  }
@@ -1,10 +1,61 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
2
3
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
4
  import { codeBlockClassNames } from '../ui/class-names';
5
+ import { getAllCodeBlockNodesInDoc } from '../utils';
4
6
  export var DECORATION_WIDGET_TYPE = 'decorationWidgetType';
5
- export var generateLineAttributesFromNode = function generateLineAttributesFromNode(pos, node) {
7
+ export var DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
8
+
9
+ /**
10
+ * Generate the initial decorations for the code block.
11
+ */
12
+ export var generateInitialDecorations = function generateInitialDecorations(state) {
13
+ var codeBlockNodes = getAllCodeBlockNodesInDoc(state);
14
+ return codeBlockNodes.flatMap(function (node) {
15
+ return createLineNumbersDecorations(node);
16
+ });
17
+ };
18
+
19
+ /**
20
+ * Update all the decorations used by the code block.
21
+ */
22
+ export var updateCodeBlockDecorations = function updateCodeBlockDecorations(tr, state, decorationSet) {
23
+ var updatedDecorationSet = decorationSet;
24
+ var codeBlockNodes = getAllCodeBlockNodesInDoc(state);
25
+
26
+ // All the line numbers decorators are refreshed on doc change.
27
+ updatedDecorationSet = updateDecorationSetWithLineNumberDecorators(tr, codeBlockNodes, updatedDecorationSet);
28
+
29
+ // Check to make sure the word wrap decorators are still valid.
30
+ updatedDecorationSet = validateWordWrappedDecorators(tr, codeBlockNodes, updatedDecorationSet);
31
+ return updatedDecorationSet;
32
+ };
33
+
34
+ /**
35
+ * Update the decorations set with the line number decorators.
36
+ */
37
+ export var updateDecorationSetWithLineNumberDecorators = function updateDecorationSetWithLineNumberDecorators(tr, codeBlockNodes, decorationSet) {
38
+ var updatedDecorationSet = decorationSet;
39
+ // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
40
+ var children = updatedDecorationSet.find(undefined, undefined, function (spec) {
41
+ return spec.type === DECORATION_WIDGET_TYPE;
42
+ });
43
+ updatedDecorationSet = updatedDecorationSet.remove(children);
44
+
45
+ // regenerate all the line number for the documents code blocks
46
+ var lineNumberDecorators = [];
47
+ codeBlockNodes.forEach(function (node) {
48
+ lineNumberDecorators.push.apply(lineNumberDecorators, _toConsumableArray(createLineNumbersDecorations(node)));
49
+ });
50
+
51
+ // add the newly generated line numbers to the decorations set
52
+ return updatedDecorationSet.add(tr.doc, [].concat(lineNumberDecorators));
53
+ };
54
+ export var generateLineAttributesFromNode = function generateLineAttributesFromNode(node) {
55
+ var innerNode = node.node,
56
+ pos = node.pos;
6
57
  // Get content node
7
- var contentNode = node.content;
58
+ var contentNode = innerNode.content;
8
59
 
9
60
  // Get node text content
10
61
  var lineAttributes = [];
@@ -57,6 +108,60 @@ export var createDecorationSetFromLineAttributes = function createDecorationSetF
57
108
  });
58
109
  return widgetDecorations;
59
110
  };
60
- export var createLineNumbersDecorations = function createLineNumbersDecorations(pos, node) {
61
- return createDecorationSetFromLineAttributes(generateLineAttributesFromNode(pos, node));
111
+ export var createLineNumbersDecorations = function createLineNumbersDecorations(node) {
112
+ return createDecorationSetFromLineAttributes(generateLineAttributesFromNode(node));
113
+ };
114
+
115
+ /**
116
+ * There are edge cases like when a user drags and drops a code block node where the decorator breaks and no longer reflects
117
+ * the correct word wrap state. This function validates that the decorator and the state are in line, otherwise it will
118
+ * retrigger the logic to apply the word wrap decorator.
119
+ */
120
+ export var validateWordWrappedDecorators = function validateWordWrappedDecorators(tr, codeBlockNodes, decorationSet) {
121
+ var updatedDecorationSet = decorationSet;
122
+ codeBlockNodes.forEach(function (node) {
123
+ var isCodeBlockWrappedInState = isCodeBlockWordWrapEnabled(node.node);
124
+ var isCodeBlockWrappedByDecorator = getWordWrapDecoratorsFromNodePos(node.pos, decorationSet).length !== 0;
125
+ if (isCodeBlockWrappedInState && !isCodeBlockWrappedByDecorator) {
126
+ updatedDecorationSet = updateDecorationSetWithWordWrappedDecorator(decorationSet, tr, node);
127
+ }
128
+ });
129
+ return updatedDecorationSet;
130
+ };
131
+
132
+ /**
133
+ * Update the decoration set with the word wrap decorator.
134
+ */
135
+ export var updateDecorationSetWithWordWrappedDecorator = function updateDecorationSetWithWordWrappedDecorator(decorationSet, tr, node) {
136
+ if (!node || node.pos === undefined) {
137
+ return decorationSet;
138
+ }
139
+ var updatedDecorationSet = decorationSet;
140
+ var pos = node.pos,
141
+ innerNode = node.node;
142
+ var isNodeWrapped = isCodeBlockWordWrapEnabled(innerNode);
143
+ if (!isNodeWrapped) {
144
+ var currentWrappedBlockDecorationSet = getWordWrapDecoratorsFromNodePos(pos, updatedDecorationSet);
145
+ updatedDecorationSet = updatedDecorationSet.remove(currentWrappedBlockDecorationSet);
146
+ } else {
147
+ var wrappedBlock = Decoration.node(pos, pos + innerNode.nodeSize, {
148
+ class: codeBlockClassNames.contentFgWrapped
149
+ }, {
150
+ type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
151
+ } // Allows for quick filtering of decorations while using `find`
152
+ );
153
+ updatedDecorationSet = updatedDecorationSet.add(tr.doc, [wrappedBlock]);
154
+ }
155
+ return updatedDecorationSet;
156
+ };
157
+
158
+ /**
159
+ * Get the word wrap decorators for the given node position.
160
+ */
161
+ export var getWordWrapDecoratorsFromNodePos = function getWordWrapDecoratorsFromNodePos(pos, decorationSet) {
162
+ var codeBlockNodePosition = pos + 1; // We need to add 1 to the position to get the start of the node.
163
+ var currentWrappedBlockDecorationSet = decorationSet.find(codeBlockNodePosition, codeBlockNodePosition, function (spec) {
164
+ return spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE;
165
+ });
166
+ return currentWrappedBlockDecorationSet;
62
167
  };
@@ -1,14 +1,12 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
2
  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; }
4
3
  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) { _defineProperty(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; }
5
- import { isCodeBlockWordWrapEnabled } from '@atlaskit/editor-common/code-block';
6
4
  import { blockTypeMessages } from '@atlaskit/editor-common/messages';
7
5
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
8
6
  import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
9
7
  import { browser } from '@atlaskit/editor-common/utils';
10
8
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
11
- import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
9
+ import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
12
10
  import { fg } from '@atlaskit/platform-feature-flags';
13
11
  import { ignoreFollowingMutations, resetShouldIgnoreFollowingMutations } from '../actions';
14
12
  import { codeBlockNodeView } from '../nodeviews/code-block';
@@ -16,8 +14,7 @@ import { pluginKey } from '../plugin-key';
16
14
  import { codeBlockClassNames } from '../ui/class-names';
17
15
  import { findCodeBlock } from '../utils';
18
16
  import { ACTIONS } from './actions';
19
- import { createLineNumbersDecorations, DECORATION_WIDGET_TYPE } from './decorators';
20
- var DECORATION_WRAPPED_BLOCK_NODE_TYPE = 'decorationNodeType';
17
+ import { generateInitialDecorations, updateCodeBlockDecorations, updateDecorationSetWithWordWrappedDecorator } from './decorators';
21
18
  export var createPlugin = function createPlugin(_ref) {
22
19
  var _ref$useLongPressSele = _ref.useLongPressSelection,
23
20
  useLongPressSelection = _ref$useLongPressSele === void 0 ? false : _ref$useLongPressSele,
@@ -26,57 +23,6 @@ export var createPlugin = function createPlugin(_ref) {
26
23
  allowCompositionInputOverride = _ref$allowComposition === void 0 ? false : _ref$allowComposition,
27
24
  api = _ref.api;
28
25
  var handleDOMEvents = {};
29
- var createLineNumberDecoratorsFromDecendants = function createLineNumberDecoratorsFromDecendants(editorState) {
30
- var lineNumberDecorators = [];
31
- editorState.doc.descendants(function (node, pos) {
32
- if (node.type === editorState.schema.nodes.codeBlock) {
33
- lineNumberDecorators.push.apply(lineNumberDecorators, _toConsumableArray(createLineNumbersDecorations(pos, node)));
34
- return false;
35
- }
36
- return true;
37
- });
38
- return lineNumberDecorators;
39
- };
40
- var createWordWrappedDecoratorPluginState = function createWordWrappedDecoratorPluginState(pluginState, tr, node) {
41
- var decorationSetFromState = pluginState.decorations;
42
- if (node) {
43
- var pos = node.pos,
44
- innerNode = node.node;
45
- if (pos !== undefined) {
46
- var isNodeWrapped = isCodeBlockWordWrapEnabled(innerNode);
47
- if (!isNodeWrapped) {
48
- // Restricts the range of decorators to the current node while not including the previous nodes position in range
49
- var codeBlockNodePosition = pos + 1;
50
- var currentWrappedBlockDecorationSet = decorationSetFromState.find(codeBlockNodePosition, codeBlockNodePosition, function (spec) {
51
- return spec.type === DECORATION_WRAPPED_BLOCK_NODE_TYPE;
52
- });
53
- decorationSetFromState = decorationSetFromState.remove(currentWrappedBlockDecorationSet);
54
- } else {
55
- var wrappedBlock = Decoration.node(pos, pos + innerNode.nodeSize, {
56
- class: codeBlockClassNames.contentFgWrapped
57
- }, {
58
- type: DECORATION_WRAPPED_BLOCK_NODE_TYPE
59
- } // Allows for quick filtering of decorations while using `find`
60
- );
61
- decorationSetFromState = decorationSetFromState.add(tr.doc, [wrappedBlock]);
62
- }
63
- }
64
- }
65
- return decorationSetFromState;
66
- };
67
- var updateLineDecorationSet = function updateLineDecorationSet(tr, state, decorationSet) {
68
- // remove all the line number children from the decorations set. 'undefined, undefined' is used to find() the whole doc.
69
- var children = decorationSet.find(undefined, undefined, function (spec) {
70
- return spec.type === DECORATION_WIDGET_TYPE;
71
- });
72
- decorationSet = decorationSet.remove(children);
73
-
74
- // regenerate all the line number for the documents code blocks
75
- var lineDecorators = createLineNumberDecoratorsFromDecendants(state);
76
-
77
- // add the newly generated line numbers to the decorations set
78
- return decorationSet.add(tr.doc, _toConsumableArray(lineDecorators));
79
- };
80
26
 
81
27
  // ME-1599: Composition on mobile was causing the DOM observer to mutate the code block
82
28
  // incorrecly and lose content when pressing enter in the middle of a code block line.
@@ -120,13 +66,13 @@ export var createPlugin = function createPlugin(_ref) {
120
66
  state: {
121
67
  init: function init(_, state) {
122
68
  var node = findCodeBlock(state, state.selection);
123
- var lineNumberDecorators = fg('editor_support_code_block_wrapping') ? createLineNumberDecoratorsFromDecendants(state) : [];
69
+ var initialDecorations = fg('editor_support_code_block_wrapping') ? generateInitialDecorations(state) : [];
124
70
  return {
125
71
  pos: node ? node.pos : null,
126
72
  contentCopied: false,
127
73
  isNodeSelected: false,
128
74
  shouldIgnoreFollowingMutations: false,
129
- decorations: DecorationSet.create(state.doc, lineNumberDecorators)
75
+ decorations: DecorationSet.create(state.doc, initialDecorations)
130
76
  };
131
77
  },
132
78
  apply: function apply(tr, pluginState, _oldState, newState) {
@@ -134,18 +80,18 @@ export var createPlugin = function createPlugin(_ref) {
134
80
  if ((meta === null || meta === void 0 ? void 0 : meta.type) === ACTIONS.SET_IS_WRAPPED && fg('editor_support_code_block_wrapping')) {
135
81
  var node = findCodeBlock(newState, tr.selection);
136
82
  return _objectSpread(_objectSpread({}, pluginState), {}, {
137
- decorations: createWordWrappedDecoratorPluginState(pluginState, tr, node)
83
+ decorations: updateDecorationSetWithWordWrappedDecorator(pluginState.decorations, tr, node)
138
84
  });
139
85
  }
140
86
  if (tr.docChanged) {
141
87
  var _node = findCodeBlock(newState, tr.selection);
142
88
 
143
89
  // Updates mapping position of all existing decorations to new positions
144
- // specifically used for updating word wrap node decorators
90
+ // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
145
91
  var updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
146
-
147
- // Wipe and regenerate all line numbers in the document
148
- updatedDecorationSet = updateLineDecorationSet(tr, newState, updatedDecorationSet);
92
+ if (fg('editor_support_code_block_wrapping')) {
93
+ updatedDecorationSet = updateCodeBlockDecorations(tr, newState, updatedDecorationSet);
94
+ }
149
95
  var newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
150
96
  pos: _node ? _node.pos : null,
151
97
  isNodeSelected: tr.selection instanceof NodeSelection,
@@ -99,7 +99,6 @@ export var getToolbarConfig = function getToolbarConfig() {
99
99
  supportsViewMode: true,
100
100
  icon: WrapIcon,
101
101
  onClick: toggleWordWrapStateForCodeBlockNode(editorAnalyticsAPI),
102
- // Hooking up here for demo purposes. To be revisited with ED-24222.
103
102
  title: isWrapped ? formatMessage(codeBlockButtonMessages.unwrapCode) : formatMessage(codeBlockButtonMessages.wrapCode),
104
103
  tabIndex: null,
105
104
  selected: isWrapped
package/dist/esm/utils.js CHANGED
@@ -1,4 +1,18 @@
1
1
  export { findCodeBlock, transformSliceToJoinAdjacentCodeBlocks, transformSingleLineCodeBlockToCodeMark } from '@atlaskit/editor-common/transforms';
2
2
  export function getCursor(selection) {
3
3
  return selection.$cursor || undefined;
4
+ }
5
+ export function getAllCodeBlockNodesInDoc(state) {
6
+ var codeBlockNodes = [];
7
+ state.doc.descendants(function (node, pos) {
8
+ if (node.type === state.schema.nodes.codeBlock) {
9
+ codeBlockNodes.push({
10
+ node: node,
11
+ pos: pos
12
+ });
13
+ return false;
14
+ }
15
+ return true;
16
+ });
17
+ return codeBlockNodes;
4
18
  }
@@ -1,7 +1,35 @@
1
- import type { Node } from '@atlaskit/editor-prosemirror/model';
2
- import { Decoration } from '@atlaskit/editor-prosemirror/view';
1
+ import { type EditorState, type ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
2
+ import { type NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
3
+ import { Decoration, type DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
4
  import type { CodeBlockLineAttributes } from '../types';
4
5
  export declare const DECORATION_WIDGET_TYPE = "decorationWidgetType";
5
- export declare const generateLineAttributesFromNode: (pos: number, node: Node) => CodeBlockLineAttributes[];
6
+ export declare const DECORATION_WRAPPED_BLOCK_NODE_TYPE = "decorationNodeType";
7
+ /**
8
+ * Generate the initial decorations for the code block.
9
+ */
10
+ export declare const generateInitialDecorations: (state: EditorState) => Decoration[];
11
+ /**
12
+ * Update all the decorations used by the code block.
13
+ */
14
+ export declare const updateCodeBlockDecorations: (tr: ReadonlyTransaction, state: EditorState, decorationSet: DecorationSet) => DecorationSet;
15
+ /**
16
+ * Update the decorations set with the line number decorators.
17
+ */
18
+ export declare const updateDecorationSetWithLineNumberDecorators: (tr: ReadonlyTransaction, codeBlockNodes: NodeWithPos[], decorationSet: DecorationSet) => DecorationSet;
19
+ export declare const generateLineAttributesFromNode: (node: NodeWithPos) => CodeBlockLineAttributes[];
6
20
  export declare const createDecorationSetFromLineAttributes: (lineAttributes: CodeBlockLineAttributes[]) => Decoration[];
7
- export declare const createLineNumbersDecorations: (pos: number, node: Node) => Decoration[];
21
+ export declare const createLineNumbersDecorations: (node: NodeWithPos) => Decoration[];
22
+ /**
23
+ * There are edge cases like when a user drags and drops a code block node where the decorator breaks and no longer reflects
24
+ * the correct word wrap state. This function validates that the decorator and the state are in line, otherwise it will
25
+ * retrigger the logic to apply the word wrap decorator.
26
+ */
27
+ export declare const validateWordWrappedDecorators: (tr: ReadonlyTransaction, codeBlockNodes: NodeWithPos[], decorationSet: DecorationSet) => DecorationSet;
28
+ /**
29
+ * Update the decoration set with the word wrap decorator.
30
+ */
31
+ export declare const updateDecorationSetWithWordWrappedDecorator: (decorationSet: DecorationSet, tr: ReadonlyTransaction, node: NodeWithPos | undefined) => DecorationSet;
32
+ /**
33
+ * Get the word wrap decorators for the given node position.
34
+ */
35
+ export declare const getWordWrapDecoratorsFromNodePos: (pos: number, decorationSet: DecorationSet) => Decoration[];
@@ -1,4 +1,6 @@
1
1
  export { findCodeBlock, transformSliceToJoinAdjacentCodeBlocks, transformSingleLineCodeBlockToCodeMark, } from '@atlaskit/editor-common/transforms';
2
2
  import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
- import type { Selection } from '@atlaskit/editor-prosemirror/state';
3
+ import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
4
+ import type { NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
4
5
  export declare function getCursor(selection: Selection): ResolvedPos | undefined;
6
+ export declare function getAllCodeBlockNodesInDoc(state: EditorState): NodeWithPos[];
@@ -1,7 +1,35 @@
1
- import type { Node } from '@atlaskit/editor-prosemirror/model';
2
- import { Decoration } from '@atlaskit/editor-prosemirror/view';
1
+ import { type EditorState, type ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
2
+ import { type NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
3
+ import { Decoration, type DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
4
  import type { CodeBlockLineAttributes } from '../types';
4
5
  export declare const DECORATION_WIDGET_TYPE = "decorationWidgetType";
5
- export declare const generateLineAttributesFromNode: (pos: number, node: Node) => CodeBlockLineAttributes[];
6
+ export declare const DECORATION_WRAPPED_BLOCK_NODE_TYPE = "decorationNodeType";
7
+ /**
8
+ * Generate the initial decorations for the code block.
9
+ */
10
+ export declare const generateInitialDecorations: (state: EditorState) => Decoration[];
11
+ /**
12
+ * Update all the decorations used by the code block.
13
+ */
14
+ export declare const updateCodeBlockDecorations: (tr: ReadonlyTransaction, state: EditorState, decorationSet: DecorationSet) => DecorationSet;
15
+ /**
16
+ * Update the decorations set with the line number decorators.
17
+ */
18
+ export declare const updateDecorationSetWithLineNumberDecorators: (tr: ReadonlyTransaction, codeBlockNodes: NodeWithPos[], decorationSet: DecorationSet) => DecorationSet;
19
+ export declare const generateLineAttributesFromNode: (node: NodeWithPos) => CodeBlockLineAttributes[];
6
20
  export declare const createDecorationSetFromLineAttributes: (lineAttributes: CodeBlockLineAttributes[]) => Decoration[];
7
- export declare const createLineNumbersDecorations: (pos: number, node: Node) => Decoration[];
21
+ export declare const createLineNumbersDecorations: (node: NodeWithPos) => Decoration[];
22
+ /**
23
+ * There are edge cases like when a user drags and drops a code block node where the decorator breaks and no longer reflects
24
+ * the correct word wrap state. This function validates that the decorator and the state are in line, otherwise it will
25
+ * retrigger the logic to apply the word wrap decorator.
26
+ */
27
+ export declare const validateWordWrappedDecorators: (tr: ReadonlyTransaction, codeBlockNodes: NodeWithPos[], decorationSet: DecorationSet) => DecorationSet;
28
+ /**
29
+ * Update the decoration set with the word wrap decorator.
30
+ */
31
+ export declare const updateDecorationSetWithWordWrappedDecorator: (decorationSet: DecorationSet, tr: ReadonlyTransaction, node: NodeWithPos | undefined) => DecorationSet;
32
+ /**
33
+ * Get the word wrap decorators for the given node position.
34
+ */
35
+ export declare const getWordWrapDecoratorsFromNodePos: (pos: number, decorationSet: DecorationSet) => Decoration[];
@@ -1,4 +1,6 @@
1
1
  export { findCodeBlock, transformSliceToJoinAdjacentCodeBlocks, transformSingleLineCodeBlockToCodeMark, } from '@atlaskit/editor-common/transforms';
2
2
  import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
- import type { Selection } from '@atlaskit/editor-prosemirror/state';
3
+ import type { EditorState, Selection } from '@atlaskit/editor-prosemirror/state';
4
+ import type { NodeWithPos } from '@atlaskit/editor-prosemirror/utils';
4
5
  export declare function getCursor(selection: Selection): ResolvedPos | undefined;
6
+ export declare function getAllCodeBlockNodesInDoc(state: EditorState): NodeWithPos[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-code-block",
3
- "version": "3.3.9",
3
+ "version": "3.3.10",
4
4
  "description": "Code block plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -10,8 +10,6 @@
10
10
  "atlassian": {
11
11
  "team": "Editor: Core Experiences",
12
12
  "singleton": true,
13
- "inPublicMirror": false,
14
- "releaseModel": "continuous",
15
13
  "runReact18": false
16
14
  },
17
15
  "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
@@ -35,7 +33,7 @@
35
33
  "dependencies": {
36
34
  "@atlaskit/adf-schema": "^40.9.0",
37
35
  "@atlaskit/code": "^15.6.0",
38
- "@atlaskit/editor-common": "^88.4.0",
36
+ "@atlaskit/editor-common": "^88.5.0",
39
37
  "@atlaskit/editor-plugin-analytics": "^1.8.0",
40
38
  "@atlaskit/editor-plugin-composition": "^1.2.0",
41
39
  "@atlaskit/editor-plugin-decorations": "^1.3.0",