UNPKG

@atlaskit/editor-plugin-tasks-and-decisions

Version:

Tasks and decisions plugin for @atlaskit/editor-core

118 lines 4.75 kB
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;