@atlaskit/editor-plugin-panel
Version:
Panel plugin for @atlaskit/editor-core.
91 lines (87 loc) • 4.23 kB
JavaScript
import { isEmptyNode } from '@atlaskit/editor-common/utils';
import { keymap } from '@atlaskit/editor-prosemirror/keymap';
import { findParentNodeOfType, hasParentNodeOfType, setTextSelection } from '@atlaskit/editor-prosemirror/utils';
function findParentNode(selection, nodeType) {
const parentPosition = findParentNodeOfType(nodeType)(selection);
if (parentPosition) {
return parentPosition.node;
}
return null;
}
function isInsideAnEmptyNode(selection, nodeType, schema) {
const parentNode = findParentNode(selection, nodeType);
return parentNode && isEmptyNode(schema)(parentNode);
}
// Somewhat broken and subverted: https://product-fabric.atlassian.net/browse/ED-6504
export function keymapPlugin() {
const deleteCurrentItem = ($from, tr) => {
return tr.delete($from.before($from.depth) - 1, $from.end($from.depth) + 1);
};
const keymaps = {
Backspace: (state, dispatch) => {
var _$previousPos$nodeAft, _nodeBeforePanel$type, _nodeBeforePanel$type2;
const {
selection,
schema: {
nodes
},
tr
} = state;
const {
panel,
blockquote,
orderedList,
bulletList,
mediaSingle,
mediaGroup,
extension
} = nodes;
const {
$from,
$to
} = selection;
// Don't do anything if selection is a range
if ($from.pos !== $to.pos) {
return false;
}
// If not at the start of a panel, no joining will happen so allow default behaviour (backspacing characters etc..)
if ($from.parentOffset !== 0) {
return false;
}
const $previousPos = tr.doc.resolve(Math.max(0, $from.before($from.depth) - 1));
const previousNodeType = $previousPos.pos > 0 && $previousPos.parent && $previousPos.parent.type;
const parentNodeType = $from.parent.type;
const isPreviousNodeAPanel = previousNodeType === panel;
const isParentNodeAPanel = parentNodeType === panel;
const nodeBeforePanel = $previousPos === null || $previousPos === void 0 ? void 0 : $previousPos.nodeBefore;
const isPreviousNodeMedia = previousNodeType === mediaSingle || previousNodeType === mediaGroup;
const isPreviousNodeAList = previousNodeType === bulletList || previousNodeType === orderedList;
// identifies if new position after backspace is at the start of a non-bodied extension node
const isPreviousPosAtExtension = ((_$previousPos$nodeAft = $previousPos.nodeAfter) === null || _$previousPos$nodeAft === void 0 ? void 0 : _$previousPos$nodeAft.type) === extension;
// Stops merging panels when deleting empty paragraph in between
// Stops merging blockquotes with panels when deleting from start of blockquote
if (isPreviousNodeAPanel && !isParentNodeAPanel && !isPreviousPosAtExtension || isInsideAnEmptyNode(selection, panel, state.schema) || hasParentNodeOfType(blockquote)(selection) && !isPreviousNodeAList && !isPreviousNodeMedia && !isPreviousPosAtExtension ||
// Lift line of panel content up and out of the panel, when backspacing
// at the start of a panel, if the node before the panel is an 'isolating' node
// (for e.g. a table, or an expand), otherwise the default prosemirror backspace
// behaviour will fallback to 'select node backward' logic because the node
// before is an isolating node.
nodeBeforePanel !== null && nodeBeforePanel !== void 0 && (_nodeBeforePanel$type = nodeBeforePanel.type) !== null && _nodeBeforePanel$type !== void 0 && (_nodeBeforePanel$type2 = _nodeBeforePanel$type.spec) !== null && _nodeBeforePanel$type2 !== void 0 && _nodeBeforePanel$type2.isolating && hasParentNodeOfType(panel)(selection)) {
const content = $from.node($from.depth).content;
const insertPos = $previousPos.pos;
deleteCurrentItem($from, tr).insert(insertPos, content);
if (dispatch) {
dispatch(setTextSelection(insertPos)(tr).scrollIntoView());
}
return true;
}
const nodeType = $from.node().type;
if (nodeType !== panel) {
return false;
}
return true;
}
};
return keymap(keymaps);
}
export default keymapPlugin;