@atlaskit/editor-plugin-expand
Version:
Expand plugin for @atlaskit/editor-core
131 lines (127 loc) • 6.31 kB
JavaScript
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
import { expandClassNames } from '@atlaskit/editor-common/styles';
import { transformSliceExpandToNestedExpand, transformSliceNestedExpandToExpand } from '@atlaskit/editor-common/transforms';
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
// Ignored via go/ees005
// eslint-disable-next-line import/no-named-as-default
import ExpandNodeView from '../node-views';
export var pluginKey = new PluginKey('expandPlugin');
export function containsClass(element, className) {
var _element$classList;
return Boolean(element === null || element === void 0 || (_element$classList = element.classList) === null || _element$classList === void 0 ? void 0 : _element$classList.contains(className));
}
export var createPlugin = function createPlugin(dispatch, getIntl) {
var appearance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'full-page';
var useLongPressSelection = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var api = arguments.length > 4 ? arguments[4] : undefined;
var nodeViewPortalProviderAPI = arguments.length > 5 ? arguments[5] : undefined;
var allowInteractiveExpand = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : true;
var __livePage = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : false;
var isMobile = false;
return new SafePlugin({
key: pluginKey,
props: {
nodeViews: {
expand: ExpandNodeView({
getIntl: getIntl,
isMobile: isMobile,
api: api,
nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
allowInteractiveExpand: allowInteractiveExpand,
__livePage: __livePage
}),
nestedExpand: ExpandNodeView({
getIntl: getIntl,
isMobile: isMobile,
api: api,
nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
allowInteractiveExpand: allowInteractiveExpand,
__livePage: __livePage
})
},
handleKeyDown: function handleKeyDown(_view, event) {
return containsClass(event.target, expandClassNames.titleContainer);
},
handleKeyPress: function handleKeyPress(_view, event) {
return containsClass(event.target, expandClassNames.titleContainer);
},
handleScrollToSelection: function handleScrollToSelection() {
return containsClass(document.activeElement, expandClassNames.titleInput);
},
handleClickOn: createSelectionClickHandler(['expand', 'nestedExpand'], function (target) {
return target.classList.contains(expandClassNames.prefix);
}, {
useLongPressSelection: useLongPressSelection
}),
handleDrop: function handleDrop(view, event, slice, _moved) {
return handleExpandDrag(view, event, slice);
}
},
// @see ED-8027 to follow up on this work-around
filterTransaction: function filterTransaction(tr) {
if (containsClass(document.activeElement, expandClassNames.titleInput) && tr.selectionSet && (!tr.steps.length || tr.isGeneric)) {
return false;
}
return true;
}
});
};
/**
* Convert a nested expand to an expand when dropped outside an expand or table. Convert an expand to a nested expand when dropped inside an expand or table.
*/
export function handleExpandDrag(view, event, slice) {
var state = view.state,
dispatch = view.dispatch;
var tr = state.tr;
var selection = state.selection;
var from = selection.from,
to = selection.to;
var sliceContainsExpand = false;
var sliceContainsNestedExpand = false;
slice.content.forEach(function (node) {
if (node.type === state.schema.nodes.expand) {
sliceContainsExpand = true;
} else if (node.type === state.schema.nodes.nestedExpand) {
sliceContainsNestedExpand = true;
}
});
// Check if the contents of the dragged slice contain a nested expand node or expand node.
// Also not handling expands with nested expands for now.
if (!sliceContainsExpand && !sliceContainsNestedExpand || sliceContainsExpand && sliceContainsNestedExpand) {
return false;
}
var dropPos = view.posAtCoords({
left: event.clientX,
top: event.clientY
});
if (!dropPos) {
return false;
}
var resolvedPos = state.doc.resolve(dropPos.pos);
var dropLocationNodeType = resolvedPos.node().type;
var dropLocationParentNodeType = resolvedPos.depth > 0 ? resolvedPos.node(resolvedPos.depth - 1).type : dropLocationNodeType;
var nodesWithNestedExpandSupport = [state.schema.nodes.expand, state.schema.nodes.tableHeader, state.schema.nodes.tableCell];
var isNodeAtDropPosInsideNodesWithNestedExpandSupport = nodesWithNestedExpandSupport.includes(dropLocationNodeType) || nodesWithNestedExpandSupport.includes(dropLocationParentNodeType);
var isNodeBeingDroppedInsideNestedExpand = dropLocationNodeType === state.schema.nodes.nestedExpand || dropLocationParentNodeType === state.schema.nodes.nestedExpand;
var updatedSlice = slice;
if (sliceContainsExpand && isNodeAtDropPosInsideNodesWithNestedExpandSupport) {
updatedSlice = transformSliceExpandToNestedExpand(slice);
} else if (sliceContainsNestedExpand && !isNodeAtDropPosInsideNodesWithNestedExpandSupport && !isNodeBeingDroppedInsideNestedExpand) {
updatedSlice = transformSliceNestedExpandToExpand(slice, state.schema);
}
if (!updatedSlice || updatedSlice.eq(slice)) {
return false;
}
// The drop position will be affected when the original slice is deleted from the document.
var updatedDropPos = dropPos.pos > from ? dropPos.pos - updatedSlice.content.size : dropPos.pos;
// Adjust the drop position to place the slice before the node at the position the cursor is pointing at, except when the drop location is the document node.
// Otherwise causes weird behaviour with tables & quotes, splits them apart. Only do this for nested expand slice transformed to expand.
if (dropLocationNodeType !== state.schema.nodes.doc && !sliceContainsExpand) {
updatedDropPos = updatedDropPos - 1;
}
tr.delete(from, to);
tr.insert(updatedDropPos, updatedSlice.content);
dispatch(tr);
return true;
}