@atlaskit/editor-plugin-tasks-and-decisions 6.2.2 → 6.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-tasks-and-decisions
2
2
 
3
+ ## 6.2.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`af3ebe7fda754`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/af3ebe7fda754) -
8
+ [EDITOR-1153] Handle when user presses "Tab" in a blockTaskItem
9
+
10
+ ## 6.2.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [#201339](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/201339)
15
+ [`b734e816f63ba`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b734e816f63ba) -
16
+ [EDITOR-1150] Handle pressing Enter in blockTaskItems
17
+
3
18
  ## 6.2.2
4
19
 
5
20
  ### Patch Changes
@@ -5,11 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.wrapSelectionInTaskList = exports.liftSelection = exports.joinAtCut = void 0;
7
7
  var _commands = require("@atlaskit/editor-common/commands");
8
+ var _utils = require("@atlaskit/editor-common/utils");
9
+ var _model = require("@atlaskit/editor-prosemirror/model");
8
10
  var _transform = require("@atlaskit/editor-prosemirror/transform");
11
+ var _utils2 = require("@atlaskit/editor-prosemirror/utils");
9
12
  var _helpers = require("./helpers");
10
- var _utils = require("./utils");
13
+ var _utils3 = require("./utils");
11
14
  var liftSelection = exports.liftSelection = function liftSelection(state, dispatch) {
12
- var normalizedSelection = (0, _utils.normalizeTaskItemsSelection)(state.selection);
15
+ var normalizedSelection = (0, _utils3.normalizeTaskItemsSelection)(state.selection);
13
16
  var $from = normalizedSelection.$from,
14
17
  $to = normalizedSelection.$to;
15
18
  var tr = (0, _helpers.liftBlock)(state.tr, $from, $to);
@@ -18,20 +21,60 @@ var liftSelection = exports.liftSelection = function liftSelection(state, dispat
18
21
  }
19
22
  return !!tr;
20
23
  };
24
+
25
+ /**
26
+ * Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
27
+ *
28
+ * - Normalizes the selection to ensure it covers complete task items.
29
+ * - Determines the maximum depth of task list nesting within the selection.
30
+ * - If the selection is already nested at or beyond the maximum depth, the command does nothing.
31
+ * - Calculates the block range to wrap, handling both regular and block task items.
32
+ * - Wraps the block in a task list to increase indentation or create a new task list if necessary.
33
+ *
34
+ * @param state - The current editor state.
35
+ * @param dispatch - The dispatch function to apply the transaction.
36
+ * @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
37
+ * @example
38
+ * ```typescript
39
+ * autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
40
+ * ```
41
+ */
21
42
  var wrapSelectionInTaskList = exports.wrapSelectionInTaskList = function wrapSelectionInTaskList(state, dispatch) {
22
- var _normalizeTaskItemsSe = (0, _utils.normalizeTaskItemsSelection)(state.selection),
43
+ var _normalizeTaskItemsSe = (0, _utils3.normalizeTaskItemsSelection)(state.selection),
23
44
  $from = _normalizeTaskItemsSe.$from,
24
45
  $to = _normalizeTaskItemsSe.$to;
25
46
 
26
47
  // limit ui indentation to 6 levels
27
48
  var _state$schema$nodes = state.schema.nodes,
28
49
  taskList = _state$schema$nodes.taskList,
29
- taskItem = _state$schema$nodes.taskItem;
50
+ taskItem = _state$schema$nodes.taskItem,
51
+ blockTaskItem = _state$schema$nodes.blockTaskItem;
30
52
  var maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, taskItem]);
53
+ var isBlockTaskItem = (0, _utils2.hasParentNodeOfType)([blockTaskItem])(state.selection);
54
+ var blockTaskItemNode = (0, _utils.findFarthestParentNode)(function (node) {
55
+ return node.type === blockTaskItem;
56
+ })($from);
57
+ if (blockTaskItem && isBlockTaskItem && blockTaskItemNode) {
58
+ // If the selection is inside a nested node inside the blockTaskItem
59
+ // Remove the difference in depth between the selection and the blockTaskItemNode
60
+ if ($from.depth > blockTaskItemNode.depth) {
61
+ maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, blockTaskItem]) - ($from.depth - blockTaskItemNode.depth);
62
+ } else {
63
+ maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, blockTaskItem]);
64
+ }
65
+ }
31
66
  if (maxDepth >= 6) {
32
67
  return true;
33
68
  }
34
69
  var blockRange = (0, _helpers.getBlockRange)($from, $to);
70
+ if (blockTaskItem) {
71
+ if (isBlockTaskItem && blockTaskItemNode) {
72
+ // Get the Resolved $from and $to of the node nested inside the blockTaskItem
73
+ var startOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start);
74
+ var endOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start + blockTaskItemNode.node.nodeSize - 1);
75
+ blockRange = new _model.NodeRange(startOfNodeInBlockTaskItem, endOfNodeInBlockTaskItem, blockTaskItemNode.depth - 1);
76
+ }
77
+ }
35
78
  if (!blockRange) {
36
79
  return true;
37
80
  }
@@ -25,8 +25,9 @@ var _types = require("./types");
25
25
  var isInsideTaskOrDecisionItem = exports.isInsideTaskOrDecisionItem = function isInsideTaskOrDecisionItem(state) {
26
26
  var _state$schema$nodes = state.schema.nodes,
27
27
  decisionItem = _state$schema$nodes.decisionItem,
28
- taskItem = _state$schema$nodes.taskItem;
29
- return (0, _utils2.hasParentNodeOfType)([decisionItem, taskItem])(state.selection);
28
+ taskItem = _state$schema$nodes.taskItem,
29
+ blockTaskItem = _state$schema$nodes.blockTaskItem;
30
+ return (0, _utils2.hasParentNodeOfType)([decisionItem, taskItem, blockTaskItem])(state.selection);
30
31
  };
31
32
  var isActionOrDecisionList = exports.isActionOrDecisionList = function isActionOrDecisionList(node) {
32
33
  var _node$type$schema$nod = node.type.schema.nodes,
@@ -41,8 +42,10 @@ var isActionOrDecisionItem = exports.isActionOrDecisionItem = function isActionO
41
42
  return [taskItem, decisionItem].indexOf(node.type) > -1;
42
43
  };
43
44
  var isInsideTask = exports.isInsideTask = function isInsideTask(state) {
44
- var taskItem = state.schema.nodes.taskItem;
45
- return (0, _utils2.hasParentNodeOfType)([taskItem])(state.selection);
45
+ var _state$schema$nodes2 = state.schema.nodes,
46
+ taskItem = _state$schema$nodes2.taskItem,
47
+ blockTaskItem = _state$schema$nodes2.blockTaskItem;
48
+ return (0, _utils2.hasParentNodeOfType)([taskItem, blockTaskItem])(state.selection);
46
49
  };
47
50
  var isInsideDecision = exports.isInsideDecision = function isInsideDecision(state) {
48
51
  var decisionItem = state.schema.nodes.decisionItem;
@@ -80,17 +83,43 @@ var getBlockRange = exports.getBlockRange = function getBlockRange($from, $to) {
80
83
  };
81
84
 
82
85
  /**
83
- * Finds the distance between the current $from and the root of the taskList.
86
+ * Calculates the current indent level of the selection within a task list in the ProseMirror document.
87
+ *
88
+ * The indent level is determined by finding the depth difference between the selection and the furthest parent
89
+ * node of type `taskList`. If the selection is inside a `blockTaskItem`, the calculation is adjusted to avoid
90
+ * counting nested block items as additional indent levels.
91
+ *
92
+ * @param selection - The current ProseMirror selection.
93
+ * @returns The indent level as a number, or `null` if the selection is not inside a task list.
94
+ * @example
95
+ * ```typescript
96
+ * const indentLevel = getCurrentIndentLevel(editorState.selection);
97
+ * ```
84
98
  */
85
99
  var getCurrentIndentLevel = exports.getCurrentIndentLevel = function getCurrentIndentLevel(selection) {
86
100
  var $from = selection.$from;
87
- var taskList = $from.doc.type.schema.nodes.taskList;
101
+ var _$from$doc$type$schem = $from.doc.type.schema.nodes,
102
+ taskList = _$from$doc$type$schem.taskList,
103
+ blockTaskItem = _$from$doc$type$schem.blockTaskItem;
88
104
  var furthestParent = (0, _utils.findFarthestParentNode)(function (node) {
89
105
  return node.type === taskList;
90
106
  })($from);
91
107
  if (!furthestParent) {
92
108
  return null;
93
109
  }
110
+ if ((0, _utils2.hasParentNodeOfType)([blockTaskItem])(selection)) {
111
+ var blockTaskItemNode = (0, _utils.findFarthestParentNode)(function (node) {
112
+ return node.type === blockTaskItem;
113
+ })($from);
114
+ if (blockTaskItemNode) {
115
+ /**
116
+ * If we are inside a blockTaskItem, calculate the indent level from the
117
+ * blockTaskItemNode instead of the selection, in case the selection is
118
+ * nested inside a blockTaskItem.
119
+ */
120
+ return blockTaskItemNode.depth - furthestParent.depth;
121
+ }
122
+ }
94
123
  return $from.depth - furthestParent.depth;
95
124
  };
96
125
 
@@ -348,12 +348,25 @@ var enter = function enter(editorAnalyticsAPI, getContextIdentifier) {
348
348
  return (0, _utils.filterCommand)(_helpers.isInsideTaskOrDecisionItem, (0, _commands.chainCommands)((0, _utils.filterCommand)(_helpers.isEmptyTaskDecision, (0, _commands.chainCommands)(getUnindentCommand(editorAnalyticsAPI)(), splitListItem)), function (state, dispatch) {
349
349
  var selection = state.selection,
350
350
  schema = state.schema;
351
- var taskItem = schema.nodes.taskItem;
351
+ var _schema$nodes = schema.nodes,
352
+ decisionItem = _schema$nodes.decisionItem,
353
+ taskItem = _schema$nodes.taskItem,
354
+ blockTaskItem = _schema$nodes.blockTaskItem;
352
355
  var $from = selection.$from,
353
356
  $to = selection.$to;
354
357
  var node = $from.node($from.depth);
355
358
  var nodeType = node && node.type;
356
- var listType = nodeType === taskItem ? 'taskList' : 'decisionList';
359
+
360
+ // Get the parent node type if the current node type is not one of the task or decision items
361
+ // This is required to handle blockTaskItem
362
+ if (![decisionItem, taskItem, blockTaskItem].includes(nodeType)) {
363
+ var _findParentNodeOfType;
364
+ var possibleNodeType = (_findParentNodeOfType = (0, _utils2.findParentNodeOfType)([decisionItem, taskItem, blockTaskItem])(selection)) === null || _findParentNodeOfType === void 0 || (_findParentNodeOfType = _findParentNodeOfType.node) === null || _findParentNodeOfType === void 0 ? void 0 : _findParentNodeOfType.type;
365
+ if (possibleNodeType) {
366
+ nodeType = possibleNodeType;
367
+ }
368
+ }
369
+ var listType = [taskItem, blockTaskItem].includes(nodeType) ? 'taskList' : 'decisionList';
357
370
  var addItem = function addItem(_ref) {
358
371
  var tr = _ref.tr,
359
372
  itemLocalId = _ref.itemLocalId;
@@ -363,10 +376,55 @@ var enter = function enter(editorAnalyticsAPI, getContextIdentifier) {
363
376
  localId: itemLocalId
364
377
  });
365
378
  if (newTask) {
379
+ if (nodeType === blockTaskItem) {
380
+ var blockTaskItemNode = (0, _utils2.findParentNodeOfType)([blockTaskItem])(selection);
381
+
382
+ // New task items for blockTaskItem should be taskItem
383
+ // We want to prevent creating new blockTaskItems as much as possible
384
+ var newTaskItem = taskItem.createAndFill({
385
+ localId: itemLocalId
386
+ });
387
+ if (!blockTaskItemNode || !newTaskItem) {
388
+ return tr;
389
+ }
390
+ return tr.insert(blockTaskItemNode.pos, newTaskItem);
391
+ }
366
392
  // Current position will point to text node, but we want to insert above the taskItem node
367
393
  return tr.insert($from.pos - 1, newTask);
368
394
  }
369
395
  }
396
+ /**
397
+ * For blockTaskItem, must handle it differently because it can have a different depth
398
+ */
399
+ if (nodeType === blockTaskItem) {
400
+ var _blockTaskItemNode = (0, _utils2.findParentNodeOfType)([blockTaskItem])(selection);
401
+ if (!_blockTaskItemNode) {
402
+ return tr;
403
+ }
404
+
405
+ // If the selection is a gap cursor at the end of the blockTaskItem,
406
+ // we should insert a new taskItem.
407
+ if ($from.parentOffset === $from.parent.nodeSize - 2) {
408
+ var _newTaskItem = taskItem.createAndFill({
409
+ localId: itemLocalId
410
+ });
411
+ if (_newTaskItem) {
412
+ tr.insert(_blockTaskItemNode.pos + _blockTaskItemNode.node.nodeSize, _newTaskItem);
413
+
414
+ // Move the cursor to the end of the newly inserted blockTaskItem
415
+ tr.setSelection(_state.TextSelection.create(tr.doc, _blockTaskItemNode.pos + _blockTaskItemNode.node.nodeSize + 1));
416
+ return tr;
417
+ }
418
+ }
419
+
420
+ // Split near the depth of the current selection
421
+ return tr.split($from.pos, $from.depth - 1, [{
422
+ type: blockTaskItem,
423
+ attrs: {
424
+ localId: itemLocalId
425
+ }
426
+ }]);
427
+ }
370
428
  return tr.split($from.pos, 1, [{
371
429
  type: nodeType,
372
430
  attrs: {
@@ -1,5 +1,8 @@
1
1
  import { findCutBefore } from '@atlaskit/editor-common/commands';
2
+ import { findFarthestParentNode } from '@atlaskit/editor-common/utils';
3
+ import { NodeRange } from '@atlaskit/editor-prosemirror/model';
2
4
  import { findWrapping, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
5
+ import { hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
6
  import { getBlockRange, isActionOrDecisionItem, isActionOrDecisionList, liftBlock, subtreeHeight } from './helpers';
4
7
  import { normalizeTaskItemsSelection } from './utils';
5
8
  export const liftSelection = (state, dispatch) => {
@@ -14,6 +17,24 @@ export const liftSelection = (state, dispatch) => {
14
17
  }
15
18
  return !!tr;
16
19
  };
20
+
21
+ /**
22
+ * Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
23
+ *
24
+ * - Normalizes the selection to ensure it covers complete task items.
25
+ * - Determines the maximum depth of task list nesting within the selection.
26
+ * - If the selection is already nested at or beyond the maximum depth, the command does nothing.
27
+ * - Calculates the block range to wrap, handling both regular and block task items.
28
+ * - Wraps the block in a task list to increase indentation or create a new task list if necessary.
29
+ *
30
+ * @param state - The current editor state.
31
+ * @param dispatch - The dispatch function to apply the transaction.
32
+ * @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
33
+ * @example
34
+ * ```typescript
35
+ * autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
36
+ * ```
37
+ */
17
38
  export const wrapSelectionInTaskList = (state, dispatch) => {
18
39
  const {
19
40
  $from,
@@ -23,13 +44,33 @@ export const wrapSelectionInTaskList = (state, dispatch) => {
23
44
  // limit ui indentation to 6 levels
24
45
  const {
25
46
  taskList,
26
- taskItem
47
+ taskItem,
48
+ blockTaskItem
27
49
  } = state.schema.nodes;
28
- const maxDepth = subtreeHeight($from, $to, [taskList, taskItem]);
50
+ let maxDepth = subtreeHeight($from, $to, [taskList, taskItem]);
51
+ const isBlockTaskItem = hasParentNodeOfType([blockTaskItem])(state.selection);
52
+ const blockTaskItemNode = findFarthestParentNode(node => node.type === blockTaskItem)($from);
53
+ if (blockTaskItem && isBlockTaskItem && blockTaskItemNode) {
54
+ // If the selection is inside a nested node inside the blockTaskItem
55
+ // Remove the difference in depth between the selection and the blockTaskItemNode
56
+ if ($from.depth > blockTaskItemNode.depth) {
57
+ maxDepth = subtreeHeight($from, $to, [taskList, blockTaskItem]) - ($from.depth - blockTaskItemNode.depth);
58
+ } else {
59
+ maxDepth = subtreeHeight($from, $to, [taskList, blockTaskItem]);
60
+ }
61
+ }
29
62
  if (maxDepth >= 6) {
30
63
  return true;
31
64
  }
32
- const blockRange = getBlockRange($from, $to);
65
+ let blockRange = getBlockRange($from, $to);
66
+ if (blockTaskItem) {
67
+ if (isBlockTaskItem && blockTaskItemNode) {
68
+ // Get the Resolved $from and $to of the node nested inside the blockTaskItem
69
+ const startOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start);
70
+ const endOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start + blockTaskItemNode.node.nodeSize - 1);
71
+ blockRange = new NodeRange(startOfNodeInBlockTaskItem, endOfNodeInBlockTaskItem, blockTaskItemNode.depth - 1);
72
+ }
73
+ }
33
74
  if (!blockRange) {
34
75
  return true;
35
76
  }
@@ -7,9 +7,10 @@ import { ACTIONS } from './types';
7
7
  export const isInsideTaskOrDecisionItem = state => {
8
8
  const {
9
9
  decisionItem,
10
- taskItem
10
+ taskItem,
11
+ blockTaskItem
11
12
  } = state.schema.nodes;
12
- return hasParentNodeOfType([decisionItem, taskItem])(state.selection);
13
+ return hasParentNodeOfType([decisionItem, taskItem, blockTaskItem])(state.selection);
13
14
  };
14
15
  export const isActionOrDecisionList = node => {
15
16
  const {
@@ -27,9 +28,10 @@ export const isActionOrDecisionItem = node => {
27
28
  };
28
29
  export const isInsideTask = state => {
29
30
  const {
30
- taskItem
31
+ taskItem,
32
+ blockTaskItem
31
33
  } = state.schema.nodes;
32
- return hasParentNodeOfType([taskItem])(state.selection);
34
+ return hasParentNodeOfType([taskItem, blockTaskItem])(state.selection);
33
35
  };
34
36
  export const isInsideDecision = state => {
35
37
  const {
@@ -72,19 +74,42 @@ export const getBlockRange = ($from, $to) => {
72
74
  };
73
75
 
74
76
  /**
75
- * Finds the distance between the current $from and the root of the taskList.
77
+ * Calculates the current indent level of the selection within a task list in the ProseMirror document.
78
+ *
79
+ * The indent level is determined by finding the depth difference between the selection and the furthest parent
80
+ * node of type `taskList`. If the selection is inside a `blockTaskItem`, the calculation is adjusted to avoid
81
+ * counting nested block items as additional indent levels.
82
+ *
83
+ * @param selection - The current ProseMirror selection.
84
+ * @returns The indent level as a number, or `null` if the selection is not inside a task list.
85
+ * @example
86
+ * ```typescript
87
+ * const indentLevel = getCurrentIndentLevel(editorState.selection);
88
+ * ```
76
89
  */
77
90
  export const getCurrentIndentLevel = selection => {
78
91
  const {
79
92
  $from
80
93
  } = selection;
81
94
  const {
82
- taskList
95
+ taskList,
96
+ blockTaskItem
83
97
  } = $from.doc.type.schema.nodes;
84
98
  const furthestParent = findFarthestParentNode(node => node.type === taskList)($from);
85
99
  if (!furthestParent) {
86
100
  return null;
87
101
  }
102
+ if (hasParentNodeOfType([blockTaskItem])(selection)) {
103
+ const blockTaskItemNode = findFarthestParentNode(node => node.type === blockTaskItem)($from);
104
+ if (blockTaskItemNode) {
105
+ /**
106
+ * If we are inside a blockTaskItem, calculate the indent level from the
107
+ * blockTaskItemNode instead of the selection, in case the selection is
108
+ * nested inside a blockTaskItem.
109
+ */
110
+ return blockTaskItemNode.depth - furthestParent.depth;
111
+ }
112
+ }
88
113
  return $from.depth - furthestParent.depth;
89
114
  };
90
115
 
@@ -337,15 +337,27 @@ const enter = (editorAnalyticsAPI, getContextIdentifier) => filter(isInsideTaskO
337
337
  schema
338
338
  } = state;
339
339
  const {
340
- taskItem
340
+ decisionItem,
341
+ taskItem,
342
+ blockTaskItem
341
343
  } = schema.nodes;
342
344
  const {
343
345
  $from,
344
346
  $to
345
347
  } = selection;
346
348
  const node = $from.node($from.depth);
347
- const nodeType = node && node.type;
348
- const listType = nodeType === taskItem ? 'taskList' : 'decisionList';
349
+ let nodeType = node && node.type;
350
+
351
+ // Get the parent node type if the current node type is not one of the task or decision items
352
+ // This is required to handle blockTaskItem
353
+ if (![decisionItem, taskItem, blockTaskItem].includes(nodeType)) {
354
+ var _findParentNodeOfType, _findParentNodeOfType2;
355
+ const possibleNodeType = (_findParentNodeOfType = findParentNodeOfType([decisionItem, taskItem, blockTaskItem])(selection)) === null || _findParentNodeOfType === void 0 ? void 0 : (_findParentNodeOfType2 = _findParentNodeOfType.node) === null || _findParentNodeOfType2 === void 0 ? void 0 : _findParentNodeOfType2.type;
356
+ if (possibleNodeType) {
357
+ nodeType = possibleNodeType;
358
+ }
359
+ }
360
+ const listType = [taskItem, blockTaskItem].includes(nodeType) ? 'taskList' : 'decisionList';
349
361
  const addItem = ({
350
362
  tr,
351
363
  itemLocalId
@@ -356,10 +368,55 @@ const enter = (editorAnalyticsAPI, getContextIdentifier) => filter(isInsideTaskO
356
368
  localId: itemLocalId
357
369
  });
358
370
  if (newTask) {
371
+ if (nodeType === blockTaskItem) {
372
+ const blockTaskItemNode = findParentNodeOfType([blockTaskItem])(selection);
373
+
374
+ // New task items for blockTaskItem should be taskItem
375
+ // We want to prevent creating new blockTaskItems as much as possible
376
+ const newTaskItem = taskItem.createAndFill({
377
+ localId: itemLocalId
378
+ });
379
+ if (!blockTaskItemNode || !newTaskItem) {
380
+ return tr;
381
+ }
382
+ return tr.insert(blockTaskItemNode.pos, newTaskItem);
383
+ }
359
384
  // Current position will point to text node, but we want to insert above the taskItem node
360
385
  return tr.insert($from.pos - 1, newTask);
361
386
  }
362
387
  }
388
+ /**
389
+ * For blockTaskItem, must handle it differently because it can have a different depth
390
+ */
391
+ if (nodeType === blockTaskItem) {
392
+ const blockTaskItemNode = findParentNodeOfType([blockTaskItem])(selection);
393
+ if (!blockTaskItemNode) {
394
+ return tr;
395
+ }
396
+
397
+ // If the selection is a gap cursor at the end of the blockTaskItem,
398
+ // we should insert a new taskItem.
399
+ if ($from.parentOffset === $from.parent.nodeSize - 2) {
400
+ const newTaskItem = taskItem.createAndFill({
401
+ localId: itemLocalId
402
+ });
403
+ if (newTaskItem) {
404
+ tr.insert(blockTaskItemNode.pos + blockTaskItemNode.node.nodeSize, newTaskItem);
405
+
406
+ // Move the cursor to the end of the newly inserted blockTaskItem
407
+ tr.setSelection(TextSelection.create(tr.doc, blockTaskItemNode.pos + blockTaskItemNode.node.nodeSize + 1));
408
+ return tr;
409
+ }
410
+ }
411
+
412
+ // Split near the depth of the current selection
413
+ return tr.split($from.pos, $from.depth - 1, [{
414
+ type: blockTaskItem,
415
+ attrs: {
416
+ localId: itemLocalId
417
+ }
418
+ }]);
419
+ }
363
420
  return tr.split($from.pos, 1, [{
364
421
  type: nodeType,
365
422
  attrs: {
@@ -1,5 +1,8 @@
1
1
  import { findCutBefore } from '@atlaskit/editor-common/commands';
2
+ import { findFarthestParentNode } from '@atlaskit/editor-common/utils';
3
+ import { NodeRange } from '@atlaskit/editor-prosemirror/model';
2
4
  import { findWrapping, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
5
+ import { hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
6
  import { getBlockRange, isActionOrDecisionItem, isActionOrDecisionList, liftBlock, subtreeHeight } from './helpers';
4
7
  import { normalizeTaskItemsSelection } from './utils';
5
8
  export var liftSelection = function liftSelection(state, dispatch) {
@@ -12,6 +15,24 @@ export var liftSelection = function liftSelection(state, dispatch) {
12
15
  }
13
16
  return !!tr;
14
17
  };
18
+
19
+ /**
20
+ * Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
21
+ *
22
+ * - Normalizes the selection to ensure it covers complete task items.
23
+ * - Determines the maximum depth of task list nesting within the selection.
24
+ * - If the selection is already nested at or beyond the maximum depth, the command does nothing.
25
+ * - Calculates the block range to wrap, handling both regular and block task items.
26
+ * - Wraps the block in a task list to increase indentation or create a new task list if necessary.
27
+ *
28
+ * @param state - The current editor state.
29
+ * @param dispatch - The dispatch function to apply the transaction.
30
+ * @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
31
+ * @example
32
+ * ```typescript
33
+ * autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
34
+ * ```
35
+ */
15
36
  export var wrapSelectionInTaskList = function wrapSelectionInTaskList(state, dispatch) {
16
37
  var _normalizeTaskItemsSe = normalizeTaskItemsSelection(state.selection),
17
38
  $from = _normalizeTaskItemsSe.$from,
@@ -20,12 +41,34 @@ export var wrapSelectionInTaskList = function wrapSelectionInTaskList(state, dis
20
41
  // limit ui indentation to 6 levels
21
42
  var _state$schema$nodes = state.schema.nodes,
22
43
  taskList = _state$schema$nodes.taskList,
23
- taskItem = _state$schema$nodes.taskItem;
44
+ taskItem = _state$schema$nodes.taskItem,
45
+ blockTaskItem = _state$schema$nodes.blockTaskItem;
24
46
  var maxDepth = subtreeHeight($from, $to, [taskList, taskItem]);
47
+ var isBlockTaskItem = hasParentNodeOfType([blockTaskItem])(state.selection);
48
+ var blockTaskItemNode = findFarthestParentNode(function (node) {
49
+ return node.type === blockTaskItem;
50
+ })($from);
51
+ if (blockTaskItem && isBlockTaskItem && blockTaskItemNode) {
52
+ // If the selection is inside a nested node inside the blockTaskItem
53
+ // Remove the difference in depth between the selection and the blockTaskItemNode
54
+ if ($from.depth > blockTaskItemNode.depth) {
55
+ maxDepth = subtreeHeight($from, $to, [taskList, blockTaskItem]) - ($from.depth - blockTaskItemNode.depth);
56
+ } else {
57
+ maxDepth = subtreeHeight($from, $to, [taskList, blockTaskItem]);
58
+ }
59
+ }
25
60
  if (maxDepth >= 6) {
26
61
  return true;
27
62
  }
28
63
  var blockRange = getBlockRange($from, $to);
64
+ if (blockTaskItem) {
65
+ if (isBlockTaskItem && blockTaskItemNode) {
66
+ // Get the Resolved $from and $to of the node nested inside the blockTaskItem
67
+ var startOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start);
68
+ var endOfNodeInBlockTaskItem = state.doc.resolve(blockTaskItemNode.start + blockTaskItemNode.node.nodeSize - 1);
69
+ blockRange = new NodeRange(startOfNodeInBlockTaskItem, endOfNodeInBlockTaskItem, blockTaskItemNode.depth - 1);
70
+ }
71
+ }
29
72
  if (!blockRange) {
30
73
  return true;
31
74
  }
@@ -7,8 +7,9 @@ import { ACTIONS } from './types';
7
7
  export var isInsideTaskOrDecisionItem = function isInsideTaskOrDecisionItem(state) {
8
8
  var _state$schema$nodes = state.schema.nodes,
9
9
  decisionItem = _state$schema$nodes.decisionItem,
10
- taskItem = _state$schema$nodes.taskItem;
11
- return hasParentNodeOfType([decisionItem, taskItem])(state.selection);
10
+ taskItem = _state$schema$nodes.taskItem,
11
+ blockTaskItem = _state$schema$nodes.blockTaskItem;
12
+ return hasParentNodeOfType([decisionItem, taskItem, blockTaskItem])(state.selection);
12
13
  };
13
14
  export var isActionOrDecisionList = function isActionOrDecisionList(node) {
14
15
  var _node$type$schema$nod = node.type.schema.nodes,
@@ -23,8 +24,10 @@ export var isActionOrDecisionItem = function isActionOrDecisionItem(node) {
23
24
  return [taskItem, decisionItem].indexOf(node.type) > -1;
24
25
  };
25
26
  export var isInsideTask = function isInsideTask(state) {
26
- var taskItem = state.schema.nodes.taskItem;
27
- return hasParentNodeOfType([taskItem])(state.selection);
27
+ var _state$schema$nodes2 = state.schema.nodes,
28
+ taskItem = _state$schema$nodes2.taskItem,
29
+ blockTaskItem = _state$schema$nodes2.blockTaskItem;
30
+ return hasParentNodeOfType([taskItem, blockTaskItem])(state.selection);
28
31
  };
29
32
  export var isInsideDecision = function isInsideDecision(state) {
30
33
  var decisionItem = state.schema.nodes.decisionItem;
@@ -62,17 +65,43 @@ export var getBlockRange = function getBlockRange($from, $to) {
62
65
  };
63
66
 
64
67
  /**
65
- * Finds the distance between the current $from and the root of the taskList.
68
+ * Calculates the current indent level of the selection within a task list in the ProseMirror document.
69
+ *
70
+ * The indent level is determined by finding the depth difference between the selection and the furthest parent
71
+ * node of type `taskList`. If the selection is inside a `blockTaskItem`, the calculation is adjusted to avoid
72
+ * counting nested block items as additional indent levels.
73
+ *
74
+ * @param selection - The current ProseMirror selection.
75
+ * @returns The indent level as a number, or `null` if the selection is not inside a task list.
76
+ * @example
77
+ * ```typescript
78
+ * const indentLevel = getCurrentIndentLevel(editorState.selection);
79
+ * ```
66
80
  */
67
81
  export var getCurrentIndentLevel = function getCurrentIndentLevel(selection) {
68
82
  var $from = selection.$from;
69
- var taskList = $from.doc.type.schema.nodes.taskList;
83
+ var _$from$doc$type$schem = $from.doc.type.schema.nodes,
84
+ taskList = _$from$doc$type$schem.taskList,
85
+ blockTaskItem = _$from$doc$type$schem.blockTaskItem;
70
86
  var furthestParent = findFarthestParentNode(function (node) {
71
87
  return node.type === taskList;
72
88
  })($from);
73
89
  if (!furthestParent) {
74
90
  return null;
75
91
  }
92
+ if (hasParentNodeOfType([blockTaskItem])(selection)) {
93
+ var blockTaskItemNode = findFarthestParentNode(function (node) {
94
+ return node.type === blockTaskItem;
95
+ })($from);
96
+ if (blockTaskItemNode) {
97
+ /**
98
+ * If we are inside a blockTaskItem, calculate the indent level from the
99
+ * blockTaskItemNode instead of the selection, in case the selection is
100
+ * nested inside a blockTaskItem.
101
+ */
102
+ return blockTaskItemNode.depth - furthestParent.depth;
103
+ }
104
+ }
76
105
  return $from.depth - furthestParent.depth;
77
106
  };
78
107
 
@@ -340,12 +340,25 @@ var enter = function enter(editorAnalyticsAPI, getContextIdentifier) {
340
340
  return filter(isInsideTaskOrDecisionItem, chainCommands(filter(isEmptyTaskDecision, chainCommands(getUnindentCommand(editorAnalyticsAPI)(), splitListItem)), function (state, dispatch) {
341
341
  var selection = state.selection,
342
342
  schema = state.schema;
343
- var taskItem = schema.nodes.taskItem;
343
+ var _schema$nodes = schema.nodes,
344
+ decisionItem = _schema$nodes.decisionItem,
345
+ taskItem = _schema$nodes.taskItem,
346
+ blockTaskItem = _schema$nodes.blockTaskItem;
344
347
  var $from = selection.$from,
345
348
  $to = selection.$to;
346
349
  var node = $from.node($from.depth);
347
350
  var nodeType = node && node.type;
348
- var listType = nodeType === taskItem ? 'taskList' : 'decisionList';
351
+
352
+ // Get the parent node type if the current node type is not one of the task or decision items
353
+ // This is required to handle blockTaskItem
354
+ if (![decisionItem, taskItem, blockTaskItem].includes(nodeType)) {
355
+ var _findParentNodeOfType;
356
+ var possibleNodeType = (_findParentNodeOfType = findParentNodeOfType([decisionItem, taskItem, blockTaskItem])(selection)) === null || _findParentNodeOfType === void 0 || (_findParentNodeOfType = _findParentNodeOfType.node) === null || _findParentNodeOfType === void 0 ? void 0 : _findParentNodeOfType.type;
357
+ if (possibleNodeType) {
358
+ nodeType = possibleNodeType;
359
+ }
360
+ }
361
+ var listType = [taskItem, blockTaskItem].includes(nodeType) ? 'taskList' : 'decisionList';
349
362
  var addItem = function addItem(_ref) {
350
363
  var tr = _ref.tr,
351
364
  itemLocalId = _ref.itemLocalId;
@@ -355,10 +368,55 @@ var enter = function enter(editorAnalyticsAPI, getContextIdentifier) {
355
368
  localId: itemLocalId
356
369
  });
357
370
  if (newTask) {
371
+ if (nodeType === blockTaskItem) {
372
+ var blockTaskItemNode = findParentNodeOfType([blockTaskItem])(selection);
373
+
374
+ // New task items for blockTaskItem should be taskItem
375
+ // We want to prevent creating new blockTaskItems as much as possible
376
+ var newTaskItem = taskItem.createAndFill({
377
+ localId: itemLocalId
378
+ });
379
+ if (!blockTaskItemNode || !newTaskItem) {
380
+ return tr;
381
+ }
382
+ return tr.insert(blockTaskItemNode.pos, newTaskItem);
383
+ }
358
384
  // Current position will point to text node, but we want to insert above the taskItem node
359
385
  return tr.insert($from.pos - 1, newTask);
360
386
  }
361
387
  }
388
+ /**
389
+ * For blockTaskItem, must handle it differently because it can have a different depth
390
+ */
391
+ if (nodeType === blockTaskItem) {
392
+ var _blockTaskItemNode = findParentNodeOfType([blockTaskItem])(selection);
393
+ if (!_blockTaskItemNode) {
394
+ return tr;
395
+ }
396
+
397
+ // If the selection is a gap cursor at the end of the blockTaskItem,
398
+ // we should insert a new taskItem.
399
+ if ($from.parentOffset === $from.parent.nodeSize - 2) {
400
+ var _newTaskItem = taskItem.createAndFill({
401
+ localId: itemLocalId
402
+ });
403
+ if (_newTaskItem) {
404
+ tr.insert(_blockTaskItemNode.pos + _blockTaskItemNode.node.nodeSize, _newTaskItem);
405
+
406
+ // Move the cursor to the end of the newly inserted blockTaskItem
407
+ tr.setSelection(TextSelection.create(tr.doc, _blockTaskItemNode.pos + _blockTaskItemNode.node.nodeSize + 1));
408
+ return tr;
409
+ }
410
+ }
411
+
412
+ // Split near the depth of the current selection
413
+ return tr.split($from.pos, $from.depth - 1, [{
414
+ type: blockTaskItem,
415
+ attrs: {
416
+ localId: itemLocalId
417
+ }
418
+ }]);
419
+ }
362
420
  return tr.split($from.pos, 1, [{
363
421
  type: nodeType,
364
422
  attrs: {
@@ -1,6 +1,23 @@
1
1
  import type { Command } from '@atlaskit/editor-common/types';
2
- import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
2
+ import { type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
3
  export declare const liftSelection: Command;
4
+ /**
5
+ * Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
6
+ *
7
+ * - Normalizes the selection to ensure it covers complete task items.
8
+ * - Determines the maximum depth of task list nesting within the selection.
9
+ * - If the selection is already nested at or beyond the maximum depth, the command does nothing.
10
+ * - Calculates the block range to wrap, handling both regular and block task items.
11
+ * - Wraps the block in a task list to increase indentation or create a new task list if necessary.
12
+ *
13
+ * @param state - The current editor state.
14
+ * @param dispatch - The dispatch function to apply the transaction.
15
+ * @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
16
+ * @example
17
+ * ```typescript
18
+ * autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
19
+ * ```
20
+ */
4
21
  export declare const wrapSelectionInTaskList: Command;
5
22
  /**
6
23
  * Tries to move the paragraph content near the given position into the taskItem or decisionItem
@@ -14,7 +14,18 @@ export declare const isTable: (node?: Node | null) => boolean;
14
14
  */
15
15
  export declare const getBlockRange: ($from: ResolvedPos, $to: ResolvedPos) => import("prosemirror-model").NodeRange | null;
16
16
  /**
17
- * Finds the distance between the current $from and the root of the taskList.
17
+ * Calculates the current indent level of the selection within a task list in the ProseMirror document.
18
+ *
19
+ * The indent level is determined by finding the depth difference between the selection and the furthest parent
20
+ * node of type `taskList`. If the selection is inside a `blockTaskItem`, the calculation is adjusted to avoid
21
+ * counting nested block items as additional indent levels.
22
+ *
23
+ * @param selection - The current ProseMirror selection.
24
+ * @returns The indent level as a number, or `null` if the selection is not inside a task list.
25
+ * @example
26
+ * ```typescript
27
+ * const indentLevel = getCurrentIndentLevel(editorState.selection);
28
+ * ```
18
29
  */
19
30
  export declare const getCurrentIndentLevel: (selection: Selection) => number | null;
20
31
  /**
@@ -1,6 +1,23 @@
1
1
  import type { Command } from '@atlaskit/editor-common/types';
2
- import type { ResolvedPos } from '@atlaskit/editor-prosemirror/model';
2
+ import { type ResolvedPos } from '@atlaskit/editor-prosemirror/model';
3
3
  export declare const liftSelection: Command;
4
+ /**
5
+ * Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
6
+ *
7
+ * - Normalizes the selection to ensure it covers complete task items.
8
+ * - Determines the maximum depth of task list nesting within the selection.
9
+ * - If the selection is already nested at or beyond the maximum depth, the command does nothing.
10
+ * - Calculates the block range to wrap, handling both regular and block task items.
11
+ * - Wraps the block in a task list to increase indentation or create a new task list if necessary.
12
+ *
13
+ * @param state - The current editor state.
14
+ * @param dispatch - The dispatch function to apply the transaction.
15
+ * @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
16
+ * @example
17
+ * ```typescript
18
+ * autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
19
+ * ```
20
+ */
4
21
  export declare const wrapSelectionInTaskList: Command;
5
22
  /**
6
23
  * Tries to move the paragraph content near the given position into the taskItem or decisionItem
@@ -14,7 +14,18 @@ export declare const isTable: (node?: Node | null) => boolean;
14
14
  */
15
15
  export declare const getBlockRange: ($from: ResolvedPos, $to: ResolvedPos) => import("prosemirror-model").NodeRange | null;
16
16
  /**
17
- * Finds the distance between the current $from and the root of the taskList.
17
+ * Calculates the current indent level of the selection within a task list in the ProseMirror document.
18
+ *
19
+ * The indent level is determined by finding the depth difference between the selection and the furthest parent
20
+ * node of type `taskList`. If the selection is inside a `blockTaskItem`, the calculation is adjusted to avoid
21
+ * counting nested block items as additional indent levels.
22
+ *
23
+ * @param selection - The current ProseMirror selection.
24
+ * @returns The indent level as a number, or `null` if the selection is not inside a task list.
25
+ * @example
26
+ * ```typescript
27
+ * const indentLevel = getCurrentIndentLevel(editorState.selection);
28
+ * ```
18
29
  */
19
30
  export declare const getCurrentIndentLevel: (selection: Selection) => number | null;
20
31
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-tasks-and-decisions",
3
- "version": "6.2.2",
3
+ "version": "6.2.4",
4
4
  "description": "Tasks and decisions plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -49,7 +49,7 @@
49
49
  "@atlaskit/primitives": "^14.11.0",
50
50
  "@atlaskit/prosemirror-input-rules": "^3.4.0",
51
51
  "@atlaskit/task-decision": "^19.2.0",
52
- "@atlaskit/tmp-editor-statsig": "^9.27.0",
52
+ "@atlaskit/tmp-editor-statsig": "^9.28.0",
53
53
  "@atlaskit/tokens": "^6.0.0",
54
54
  "@babel/runtime": "^7.0.0",
55
55
  "@compiled/react": "^0.18.3",