@atlaskit/editor-plugin-tasks-and-decisions
Version:
Tasks and decisions plugin for @atlaskit/editor-core
173 lines (165 loc) • 7.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.wrapSelectionInTaskList = exports.liftSelection = exports.joinAtCut = void 0;
var _commands = require("@atlaskit/editor-common/commands");
var _lists = require("@atlaskit/editor-common/lists");
var _transforms = require("@atlaskit/editor-common/transforms");
var _transform = require("@atlaskit/editor-prosemirror/transform");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _helpers = require("./helpers");
var _utils = require("./utils");
var _paste = require("./utils/paste");
var liftSelection = exports.liftSelection = function liftSelection(state, dispatch) {
var normalizedSelection = (0, _utils.normalizeTaskItemsSelection)(state.selection);
var $from = normalizedSelection.$from,
$to = normalizedSelection.$to;
var tr = (0, _helpers.liftBlock)(state.tr, $from, $to);
if (dispatch && tr) {
dispatch(tr);
}
return !!tr;
};
/**
* Wraps the current selection in a task list, respecting a maximum indentation depth of 6 levels.
*
* - Normalizes the selection to ensure it covers complete task items.
* - Determines the maximum depth of task list nesting within the selection.
* - If the selection is already nested at or beyond the maximum depth, the command does nothing.
* - Calculates the block range to wrap, handling both regular and block task items.
* - Wraps the block in a task list to increase indentation or create a new task list if necessary.
*
* @param state - The current editor state.
* @param dispatch - The dispatch function to apply the transaction.
* @returns `true` if the command was handled (even if no changes were made), otherwise `false`.
* @example
* ```typescript
* autoJoin(wrapSelectionInTaskList, ['taskList']))(state, dispatch);
* ```
*/
var wrapSelectionInTaskList = exports.wrapSelectionInTaskList = function wrapSelectionInTaskList(state, dispatch) {
var _normalizeTaskItemsSe = (0, _utils.normalizeTaskItemsSelection)(state.selection),
$from = _normalizeTaskItemsSe.$from,
$to = _normalizeTaskItemsSe.$to;
// limit ui indentation to 6 levels
var _state$schema$nodes = state.schema.nodes,
taskList = _state$schema$nodes.taskList,
taskItem = _state$schema$nodes.taskItem,
blockTaskItem = _state$schema$nodes.blockTaskItem;
var maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, taskItem]);
if (blockTaskItem) {
var resultOfFindBlockTaskItem = (0, _utils.findBlockTaskItem)($from);
if (resultOfFindBlockTaskItem) {
var hasParagraph = resultOfFindBlockTaskItem.hasParagraph;
// If the selection is inside a nested node inside the blockTaskItem
// Remove the difference in depth between the selection and the blockTaskItemNode
if (hasParagraph) {
maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, blockTaskItem]) - 1;
} else {
maxDepth = (0, _helpers.subtreeHeight)($from, $to, [taskList, blockTaskItem]);
}
}
}
if (maxDepth >= 6) {
return true;
}
var blockRange = (0, _helpers.getBlockRange)({
$from: $from,
$to: $to
});
if (!blockRange) {
return true;
}
var wrapping = (0, _transform.findWrapping)(blockRange, state.schema.nodes.taskList);
if (!wrapping) {
return true;
}
if (dispatch) {
dispatch(state.tr.wrap(blockRange, wrapping).scrollIntoView());
}
return true;
};
/**
* Tries to move the paragraph content near the given position into the taskItem or decisionItem
* before it.
*
* Looks backwards from the given position to find the "cut point" between the last taskItem and the
* following paragraph. Then tries to move the content from that paragraph into the taskItem.
*
* @param $pos Position at the end of, or anywhere in paragraph following, the last taskItem
* @see {joinToPreviousListItem}
*/
var joinAtCut = exports.joinAtCut = function joinAtCut($pos) {
return function (state, dispatch) {
var $cut = (0, _commands.findCutBefore)($pos);
var blockTaskItem = state.schema.nodes.blockTaskItem;
var fontSize = state.schema.marks.fontSize;
if (!$cut) {
return false;
}
var paragraph = $cut.doc.type.schema.nodes.paragraph;
// find the boundary between the taskList and paragraph
if ($cut.nodeBefore && (0, _helpers.isActionOrDecisionList)($cut.nodeBefore) && $cut.nodeAfter && $cut.nodeAfter.type === paragraph) {
// we'll find the boundary of a taskList
// so resolve -1 to find the inside end of the last taskItem
var $lastNode = $cut.doc.resolve($cut.pos - 1);
// might have deeply nested taskList, keep trying to find it
while (!(0, _helpers.isActionOrDecisionItem)($lastNode.parent)) {
$lastNode = state.doc.resolve($lastNode.pos - 1);
}
// grab the structure between the taskItem and the paragraph
// note: structure = true in ReplaceAroundStep
var slice = state.tr.doc.slice($lastNode.pos, $cut.pos);
var from = $lastNode.pos;
var to = $cut.pos + $cut.nodeAfter.nodeSize;
var gapFrom = $cut.pos + 1;
var gapTo = $cut.pos + $cut.nodeAfter.nodeSize - 1;
var insert = 0;
if (blockTaskItem) {
if ($lastNode.parent.type === blockTaskItem) {
var childOfLastNode = state.doc.resolve($lastNode.pos - 1);
if (childOfLastNode.parent.type === paragraph) {
// Recalculate the slice to include the full blockTaskItem structure
slice = state.tr.doc.slice(childOfLastNode.pos, $cut.pos);
// Need to move one pos in to get to the text node of the paragraph
from = $lastNode.pos - 1;
} else {
// If the blockTaskItem last node is not a paragraph
// Expand the gap to include the paragraph being merged
gapFrom = $cut.pos; // To get the actual paragraph node
gapTo = $cut.pos + $cut.nodeAfter.nodeSize - 1;
}
}
}
// collapse the range between end of last taskItem and after the paragraph
// with the gap being the paragraph's content (i.e. take that content)
//
// we pass the structure we found earlier to join the p and taskItem nodes
//
// see https://prosemirror.net/docs/ref/#transform.ReplaceStep.constructor
// see https://prosemirror.net/docs/ref/#transform.ReplaceAroundStep.constructor
var tr = state.tr.step(new _transform.ReplaceAroundStep(from, to, gapFrom, gapTo, slice, insert, true));
if (fontSize && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true)) {
var targetTaskListSmallTextAttrs = (0, _lists.getFirstParagraphBlockMarkAttrs)($cut.nodeBefore, fontSize);
var followingListPos = $cut.pos + $cut.nodeAfter.nodeSize;
var followingListNode = state.doc.resolve(followingListPos).nodeAfter;
if (followingListNode && (0, _transforms.isTaskList)(followingListNode.type)) {
var normalizedListNode = (0, _paste.normalizeNodeForTaskTextSize)(followingListNode, state.schema, targetTaskListSmallTextAttrs)[0];
if (normalizedListNode && normalizedListNode !== followingListNode) {
var mappedFollowingListPos = tr.mapping.map(followingListPos);
var currentFollowingListNode = tr.doc.nodeAt(mappedFollowingListPos);
if (currentFollowingListNode) {
tr = tr.replaceWith(mappedFollowingListPos, mappedFollowingListPos + currentFollowingListNode.nodeSize, normalizedListNode);
}
}
}
}
if (dispatch) {
dispatch(tr);
}
return true;
}
return false;
};
};