@atlaskit/editor-plugin-tasks-and-decisions
Version:
Tasks and decisions plugin for @atlaskit/editor-core
118 lines • 4.75 kB
JavaScript
import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { createRule } from '@atlaskit/editor-common/utils';
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { canInsert } from '@atlaskit/editor-prosemirror/utils';
import { createPlugin, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules';
import { changeInDepth, getListTypes, insertTaskDecisionAction } from './insert-commands';
const createListRule = (editorAnalyticsAPI, getContextIdentifierProvider) => (regex, listType, itemAttrs) => {
return createRule(regex, (state, _match, start, end) => {
const {
paragraph
} = state.schema.nodes;
const {
list
} = getListTypes(listType, state.schema);
const $end = state.doc.resolve(end);
const $endOfParent = state.doc.resolve($end.after());
// Only allow creating list in nodes that support them.
// Parent must be a paragraph as we don't want this applying to headings
if ($end.parent.type !== paragraph || !canInsert($endOfParent, list.createAndFill())) {
return null;
}
const insertTr = insertTaskDecisionAction(editorAnalyticsAPI, getContextIdentifierProvider)(state, listType, INPUT_METHOD.FORMATTING, addItem(start, end), undefined, undefined, itemAttrs);
return insertTr;
});
};
const isCursorInsideList = $pos => {
var _$pos$node;
return ((_$pos$node = $pos.node($pos.depth - 1)) === null || _$pos$node === void 0 ? void 0 : _$pos$node.type.name) === 'listItem';
};
const processShortcutForNestedTask = (content, $from, tr, list, item, listLocalId, itemLocalId, itemAttrs) => {
//Extracting the content into the 'contentWithoutShortcut' from 'content' after removing the keyboard shortcut text, i.e., '[] '.
const contentWithoutShortcut = content.cut($from.pos - $from.start(), content.size);
tr.insert($from.after(), list.create({
localId: listLocalId
}, [item.create({
localId: itemLocalId,
...itemAttrs
}, contentWithoutShortcut)])).setSelection(new TextSelection(tr.doc.resolve($from.after()))).delete($from.start(), $from.end());
};
const addItem = (start, end) => ({
tr,
state,
list,
item,
listLocalId,
itemLocalId,
itemAttrs
}) => {
const {
selection: {
$from
},
schema
} = state;
const {
hardBreak
} = schema.nodes;
const content = $from.node($from.depth).content;
let shouldBreakNode = false;
content.forEach((node, offset) => {
if (node.type === hardBreak && offset < start) {
shouldBreakNode = true;
}
});
if (!shouldBreakNode) {
if (isCursorInsideList($from)) {
processShortcutForNestedTask(content, $from, tr, list, item, listLocalId, itemLocalId, itemAttrs);
return tr;
}
tr.replaceRangeWith($from.before(), $from.after(), list.create({
localId: listLocalId
}, [item.create({
localId: itemLocalId,
...itemAttrs
}, content)])).delete(start + 1, end + 1).setSelection(new TextSelection(tr.doc.resolve(start + 1)));
return tr;
}
const depthAdjustment = changeInDepth($from, tr.selection.$from);
tr.split($from.pos).setSelection(new NodeSelection(tr.doc.resolve($from.pos + 1))).replaceSelectionWith(list.create({
localId: listLocalId
}, [item.create({
localId: itemLocalId,
...itemAttrs
}, tr.doc.nodeAt($from.pos + 1).content)])).setSelection(new TextSelection(tr.doc.resolve($from.pos + depthAdjustment))).delete(start, end + 1);
return tr;
};
export const inputRulePlugin = (editorAnalyticsAPI, getContextIdentifierProvider) => (schema, featureFlags) => {
const rules = [];
const {
decisionList,
decisionItem,
taskList,
taskItem
} = schema.nodes;
if (decisionList && decisionItem) {
rules.push(createListRule(editorAnalyticsAPI, getContextIdentifierProvider)(
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
new RegExp(`(^|${leafNodeReplacementCharacter})\\<\\>\\s$`), 'decisionList'));
}
if (taskList && taskItem) {
rules.push(createListRule(editorAnalyticsAPI, getContextIdentifierProvider)(
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
new RegExp(`(^|${leafNodeReplacementCharacter})\\[\\]\\s$`), 'taskList'));
rules.push(createListRule(editorAnalyticsAPI, getContextIdentifierProvider)(
// Ignored via go/ees005
// eslint-disable-next-line require-unicode-regexp
new RegExp(`(^|${leafNodeReplacementCharacter})\\[x\\]\\s$`), 'taskList', {
state: 'DONE'
}));
}
return new SafePlugin(createPlugin('tasks-and-decisions', rules, {
isBlockNodeRule: true
}));
};
export default inputRulePlugin;