@atlaskit/editor-plugin-tasks-and-decisions
Version:
Tasks and decisions plugin for @atlaskit/editor-core
293 lines (285 loc) • 12.9 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
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; }
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; }
import { uuid } from '@atlaskit/adf-schema';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, USER_CONTEXT } from '@atlaskit/editor-common/analytics';
import { GapCursorSelection } from '@atlaskit/editor-common/selection';
import { autoJoinTr } from '@atlaskit/editor-common/utils';
import { NodeRange } from '@atlaskit/editor-prosemirror/model';
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
import { findParentNodeOfType, hasParentNodeOfType, replaceParentNodeOfType, safeInsert, setTextSelection } from '@atlaskit/editor-prosemirror/utils';
import { fg } from '@atlaskit/platform-feature-flags';
import { stateKey } from './plugin-key';
import { ACTIONS } from './types';
var getContextData = function getContextData() {
var contextProvider = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var objectId = contextProvider.objectId,
containerId = contextProvider.containerId;
var userContext = objectId ? USER_CONTEXT.EDIT : USER_CONTEXT.NEW;
return {
objectId: objectId,
containerId: containerId,
userContext: userContext
};
};
var generateAnalyticsPayload = function generateAnalyticsPayload(listType, contextData, inputMethod, itemLocalId, listLocalId, itemIdx, listSize) {
var containerId;
var objectId;
var userContext;
if (contextData) {
containerId = contextData.containerId;
objectId = contextData.objectId;
userContext = contextData.userContext;
}
var resolvedInputMethod = fg('platform_editor_element_browser_analytic') ? inputMethod : INPUT_METHOD.QUICK_INSERT;
return {
action: ACTION.INSERTED,
actionSubject: ACTION_SUBJECT.DOCUMENT,
actionSubjectId: listType === 'taskList' ? ACTION_SUBJECT_ID.ACTION : ACTION_SUBJECT_ID.DECISION,
eventType: EVENT_TYPE.TRACK,
attributes: {
inputMethod: resolvedInputMethod,
containerAri: containerId,
objectAri: objectId,
userContext: userContext,
localId: itemLocalId,
listLocalId: listLocalId,
position: itemIdx,
listSize: listSize
}
};
};
export var getListTypes = function getListTypes(listType, schema) {
var _schema$nodes = schema.nodes,
decisionList = _schema$nodes.decisionList,
decisionItem = _schema$nodes.decisionItem,
taskList = _schema$nodes.taskList,
taskItem = _schema$nodes.taskItem;
if (listType === 'taskList') {
return {
list: taskList,
item: taskItem
};
}
return {
list: decisionList,
item: decisionItem
};
};
export var insertTaskDecisionAction = function insertTaskDecisionAction(editorAnalyticsAPI, getContextIdentifierProvider) {
return function (state, listType) {
var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INPUT_METHOD.TOOLBAR;
var addItem = arguments.length > 3 ? arguments[3] : undefined;
var listLocalId = arguments.length > 4 ? arguments[4] : undefined;
var itemLocalId = arguments.length > 5 ? arguments[5] : undefined;
var itemAttrs = arguments.length > 6 ? arguments[6] : undefined;
var schema = state.schema;
var addAndCreateList = function addAndCreateList(_ref) {
var tr = _ref.tr,
list = _ref.list,
item = _ref.item,
listLocalId = _ref.listLocalId,
itemLocalId = _ref.itemLocalId;
return createListAtSelection(tr, list, item, schema, state, listLocalId, itemLocalId, itemAttrs);
};
var addToList = function addToList(_ref2) {
var state = _ref2.state,
tr = _ref2.tr,
item = _ref2.item,
itemLocalId = _ref2.itemLocalId;
var $to = state.selection.$to;
var endPos = $to.end($to.depth);
var newItemParagraphPos = endPos + 2;
return tr.split(endPos, 1, [{
type: item,
attrs: {
localId: itemLocalId
}
}]).setSelection(new TextSelection(tr.doc.resolve(newItemParagraphPos)));
};
var addAndCreateListFn = addItem !== null && addItem !== void 0 ? addItem : addAndCreateList;
var tr = insertTaskDecisionWithAnalytics(editorAnalyticsAPI, getContextIdentifierProvider)(state, listType, inputMethod, addAndCreateListFn, addToList, listLocalId, itemLocalId, itemAttrs);
if (!tr) {
return state.tr;
}
autoJoinTr(tr, ['taskList', 'decisionList']);
return tr;
};
};
export var insertTaskDecisionCommand = function insertTaskDecisionCommand(editorAnalyticsAPI, getContextIdentifierProvider) {
return function (listType) {
var inputMethod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : INPUT_METHOD.TOOLBAR;
var addItem = arguments.length > 2 ? arguments[2] : undefined;
var listLocalId = arguments.length > 3 ? arguments[3] : undefined;
var itemLocalId = arguments.length > 4 ? arguments[4] : undefined;
return function (state, dispatch) {
var tr = insertTaskDecisionAction(editorAnalyticsAPI, getContextIdentifierProvider)(state, listType, inputMethod, addItem, listLocalId, itemLocalId);
if (dispatch) {
dispatch(tr);
}
return true;
};
};
};
export var insertTaskDecisionWithAnalytics = function insertTaskDecisionWithAnalytics(editorAnalyticsAPI, getContextIdentifierProvider) {
return function (state, listType, inputMethod, addAndCreateList, addToList, listLocalId, itemLocalId, itemAttrs) {
var schema = state.schema;
var _getListTypes = getListTypes(listType, schema),
list = _getListTypes.list,
item = _getListTypes.item;
var tr = state.tr;
var $to = state.selection.$to;
var listNode = findParentNodeOfType(list)(state.selection);
var contextIdentifierProvider = getContextIdentifierProvider();
var contextData = getContextData(contextIdentifierProvider);
var insertTrCreator;
var itemIdx;
var listSize;
if (!listNode) {
// Not a list - convert to one.
itemIdx = 0;
listSize = 1;
insertTrCreator = addAndCreateList;
} else if ($to.node().textContent.length >= 0) {
listSize = listNode.node.childCount + 1;
listLocalId = listLocalId || listNode.node.attrs.localId;
var listItemNode = findParentNodeOfType(item)(state.selection); // finds current item in list
itemIdx = listItemNode ? state.doc.resolve(listItemNode.pos).index() + 1 : 0;
insertTrCreator = addToList ? addToList : addAndCreateList;
}
listLocalId = listLocalId || uuid.generate();
itemLocalId = itemLocalId || uuid.generate();
if (insertTrCreator) {
var insertTr = insertTrCreator({
state: state,
tr: tr,
list: list,
item: item,
listLocalId: listLocalId,
itemLocalId: itemLocalId,
itemAttrs: itemAttrs
});
if (insertTr) {
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(generateAnalyticsPayload(listType, contextData, inputMethod, itemLocalId, listLocalId, itemIdx || 0, listSize || 0))(insertTr);
}
return insertTr;
}
return null;
};
};
export var isSupportedSourceNode = function isSupportedSourceNode(schema, selection) {
var _schema$nodes2 = schema.nodes,
paragraph = _schema$nodes2.paragraph,
blockquote = _schema$nodes2.blockquote,
decisionList = _schema$nodes2.decisionList,
taskList = _schema$nodes2.taskList,
bulletList = _schema$nodes2.bulletList,
numberedList = _schema$nodes2.numberedList;
return hasParentNodeOfType([blockquote, paragraph, decisionList, taskList])(selection) && !hasParentNodeOfType([bulletList, numberedList])(selection);
};
export var changeInDepth = function changeInDepth(before, after) {
return after.depth - before.depth;
};
export var createListAtSelection = function createListAtSelection(tr, list, item, schema, state) {
var listLocalId = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : uuid.generate();
var itemLocalId = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : uuid.generate();
var itemAttrs = arguments.length > 7 ? arguments[7] : undefined;
var selection = state.selection;
var $from = selection.$from,
$to = selection.$to;
if ($from.parent !== $to.parent) {
// ignore selections across multiple nodes
return null;
}
var _schema$nodes3 = schema.nodes,
paragraph = _schema$nodes3.paragraph,
blockquote = _schema$nodes3.blockquote,
decisionList = _schema$nodes3.decisionList,
taskList = _schema$nodes3.taskList,
decisionItem = _schema$nodes3.decisionItem,
taskItem = _schema$nodes3.taskItem,
mediaGroup = _schema$nodes3.mediaGroup;
if ($from.parent.type === mediaGroup) {
return null;
}
var emptyList = list.create({
localId: listLocalId
}, [item.create(_objectSpread({
localId: itemLocalId
}, itemAttrs))]);
// we don't take the content of a block node next to the gap cursor and always create an empty task
if (selection instanceof GapCursorSelection) {
return safeInsert(emptyList)(tr);
}
// try to replace when selection is in nodes which support it
if (isSupportedSourceNode(schema, selection)) {
var _selection$$from$node = selection.$from.node(),
nodeType = _selection$$from$node.type,
childCount = _selection$$from$node.childCount;
var newListNode = list.create({
localId: uuid.generate()
}, [item.create({
localId: uuid.generate()
}, $from.node($from.depth).content)]);
var hasBlockquoteParent = findParentNodeOfType(blockquote)(selection);
if (hasBlockquoteParent) {
var liftedDepth = $from.depth - 1;
var range = new NodeRange($from, $to, liftedDepth);
tr.lift(range, liftTarget(range));
}
var listParent = findParentNodeOfType(taskList)(selection) || findParentNodeOfType(decisionList)(selection);
var listItem = findParentNodeOfType(taskItem)(selection) || findParentNodeOfType(decisionItem)(selection);
// For a selection inside a task/decision list, we can't just simply replace the
// node type as it will mess up lists with > 1 item
if (listParent && listItem) {
var start;
var end;
var selectionPos = selection.from;
// if selection is in first item in list, we need to delete extra so that
// this list isn't split
if (listParent.node.firstChild === listItem.node) {
start = listParent.start - 1;
end = listItem.start + listItem.node.nodeSize;
if (listParent.node.childCount === 1) {
end = listParent.start + listParent.node.nodeSize - 1;
}
} else {
start = listItem.start - 1;
end = listItem.start + listItem.node.nodeSize;
selectionPos += 2; // as we have added the new list node
}
tr.replaceWith(start, end, newListNode);
tr = setTextSelection(selectionPos)(tr);
return tr;
}
// For a selection inside one of these node types we can just convert the node type
var nodeTypesToReplace = [blockquote];
if (nodeType === paragraph && childCount > 0 || hasBlockquoteParent) {
// Only convert paragraphs containing content.
// Empty paragraphs use the default flow.
// This distinction ensures the text selection remains in the correct location.
// We also want to replace the paragraph type when we are inside a blockQuote
// to avoid inserting an extra taskList whilst keeping the paragraph
nodeTypesToReplace.push(paragraph);
}
var newTr = tr;
newTr = replaceParentNodeOfType(nodeTypesToReplace, newListNode)(tr);
// Adjust depth for new selection, if it has changed (e.g. paragraph to list (ol > li))
var depthAdjustment = changeInDepth($to, newTr.selection.$to);
tr = tr.setSelection(new TextSelection(tr.doc.resolve($to.pos + depthAdjustment)));
// replacing successful
if (newTr !== tr) {
return tr;
}
}
return safeInsert(emptyList)(tr);
};
export var setProvider = function setProvider(provider) {
return function (tr) {
return tr.setMeta(stateKey, {
action: ACTIONS.SET_PROVIDER,
data: provider
});
};
};