UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

175 lines (172 loc) 7.21 kB
import { atTheBeginningOfBlock, atTheBeginningOfDoc, atTheEndOfBlock, endPositionOfParent, GapCursorSelection, startPositionOfParent } from '@atlaskit/editor-common/selection'; import { createNewParagraphBelow, createParagraphNear } from '@atlaskit/editor-common/utils'; import { deleteSelection, splitBlock } from '@atlaskit/editor-prosemirror/commands'; import { NodeSelection } from '@atlaskit/editor-prosemirror/state'; import { findPositionOfNodeBefore } from '@atlaskit/editor-prosemirror/utils'; import { isMediaBlobUrl } from '@atlaskit/media-client'; var isTemporary = function isTemporary(id) { return id.indexOf('temporary:') === 0; }; export var isMediaBlobUrlFromAttrs = function isMediaBlobUrlFromAttrs(attrs) { return !!(attrs && attrs.type === 'external' && isMediaBlobUrl(attrs.url)); }; export var posOfMediaGroupNearby = function posOfMediaGroupNearby(state) { return posOfParentMediaGroup(state) || posOfFollowingMediaGroup(state) || posOfPrecedingMediaGroup(state) || posOfMediaGroupNextToGapCursor(state); }; export var isSelectionNonMediaBlockNode = function isSelectionNonMediaBlockNode(state) { var _ref = state.selection, node = _ref.node; return node && node.type !== state.schema.nodes.media && node.isBlock; }; export var isSelectionMediaSingleNode = function isSelectionMediaSingleNode(state) { var _ref2 = state.selection, node = _ref2.node; return node && node.type === state.schema.nodes.mediaSingle; }; export var posOfPrecedingMediaGroup = function posOfPrecedingMediaGroup(state) { if (!atTheBeginningOfBlock(state)) { return; } return posOfMediaGroupAbove(state, state.selection.$from); }; var posOfMediaGroupNextToGapCursor = function posOfMediaGroupNextToGapCursor(state) { var selection = state.selection; if (selection instanceof GapCursorSelection) { var $pos = state.selection.$from; var mediaGroupType = state.schema.nodes.mediaGroup; return posOfImmediatePrecedingMediaGroup($pos, mediaGroupType) || posOfImmediateFollowingMediaGroup($pos, mediaGroupType); } }; var posOfImmediatePrecedingMediaGroup = function posOfImmediatePrecedingMediaGroup($pos, mediaGroupType) { if ($pos.nodeBefore && $pos.nodeBefore.type === mediaGroupType) { return $pos.pos - $pos.nodeBefore.nodeSize + 1; } }; var posOfImmediateFollowingMediaGroup = function posOfImmediateFollowingMediaGroup($pos, mediaGroupType) { if ($pos.nodeAfter && $pos.nodeAfter.type === mediaGroupType) { return $pos.pos + 1; } }; var posOfFollowingMediaGroup = function posOfFollowingMediaGroup(state) { if (!atTheEndOfBlock(state)) { return; } return posOfMediaGroupBelow(state, state.selection.$to); }; var posOfMediaGroupAbove = function posOfMediaGroupAbove(state, $pos) { var adjacentPos; var adjacentNode; if (isSelectionNonMediaBlockNode(state)) { adjacentPos = $pos.pos; adjacentNode = $pos.nodeBefore; } else { adjacentPos = startPositionOfParent($pos) - 1; adjacentNode = state.doc.resolve(adjacentPos).nodeBefore; } if (adjacentNode && adjacentNode.type === state.schema.nodes.mediaGroup) { return adjacentPos - adjacentNode.nodeSize + 1; } return; }; /** * Determine whether the cursor is inside empty paragraph * or the selection is the entire paragraph */ export var isInsidePotentialEmptyParagraph = function isInsidePotentialEmptyParagraph(state) { var $from = state.selection.$from; return $from.parent.type === state.schema.nodes.paragraph && atTheBeginningOfBlock(state) && atTheEndOfBlock(state); }; var posOfMediaGroupBelow = function posOfMediaGroupBelow(state, $pos) { var prepend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; var adjacentPos; var adjacentNode; if (isSelectionNonMediaBlockNode(state)) { adjacentPos = $pos.pos; adjacentNode = $pos.nodeAfter; } else { adjacentPos = endPositionOfParent($pos); adjacentNode = state.doc.nodeAt(adjacentPos); } if (adjacentNode && adjacentNode.type === state.schema.nodes.mediaGroup) { return prepend ? adjacentPos + 1 : adjacentPos + adjacentNode.nodeSize - 1; } return; }; export var posOfParentMediaGroup = function posOfParentMediaGroup(state, $pos) { var prepend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var $from = state.selection.$from; $pos = $pos || $from; if ($pos.parent.type === state.schema.nodes.mediaGroup) { return prepend ? startPositionOfParent($pos) : endPositionOfParent($pos) - 1; } return; }; export var removeMediaNode = function removeMediaNode(view, node, getPos) { var id = node.attrs.id; var state = view.state; var tr = state.tr, selection = state.selection, doc = state.doc; var currentMediaNodePos = getPos(); if (typeof currentMediaNodePos !== 'number') { return; } tr.deleteRange(currentMediaNodePos, currentMediaNodePos + node.nodeSize); if (isTemporary(id)) { tr.setMeta('addToHistory', false); } var $currentMediaNodePos = doc.resolve(currentMediaNodePos); var nodeBefore = $currentMediaNodePos.nodeBefore, parent = $currentMediaNodePos.parent; var isLastMediaNode = $currentMediaNodePos.index() === parent.childCount - 1; // If deleting a selected media node, we need to tell where the cursor to go next. // Prosemirror didn't gave us the behaviour of moving left if the media node is not the last one. // So we handle it ourselves. if (selection.from === currentMediaNodePos && !isLastMediaNode && !atTheBeginningOfDoc(state) && nodeBefore && nodeBefore.type.name === 'media') { var _nodeBefore = findPositionOfNodeBefore(tr.selection); if (_nodeBefore) { tr.setSelection(NodeSelection.create(tr.doc, _nodeBefore)); } } view.dispatch(tr); }; export var splitMediaGroup = function splitMediaGroup(view) { var selection = view.state.selection; // if selection is not a media node, do nothing. if (!(selection instanceof NodeSelection) || selection.node.type !== view.state.schema.nodes.media) { return false; } deleteSelection(view.state, view.dispatch); if (selection.$to.nodeAfter) { splitBlock(view.state, view.dispatch); createParagraphNear(false)(view.state, view.dispatch); } else { createNewParagraphBelow(view.state, view.dispatch); } return true; }; var isOptionalAttr = function isOptionalAttr(attr) { return attr.length > 1 && attr[0] === '_' && attr[1] === '_'; }; export var copyOptionalAttrsFromMediaState = function copyOptionalAttrsFromMediaState(mediaState, node) { Object.keys(node.attrs).filter(isOptionalAttr).forEach(function (key) { var mediaStateKey = key.substring(2); var attrValue = mediaState[mediaStateKey]; if (attrValue !== undefined) { // @ts-ignore - [unblock prosemirror bump] assigning to readonly prop node.attrs[key] = attrValue; } }); }; export var getMediaNodeFromSelection = function getMediaNodeFromSelection(state) { if (!isSelectionMediaSingleNode(state)) { return null; } var tr = state.tr; var pos = tr.selection.from + 1; var mediaNode = tr.doc.nodeAt(pos); if (mediaNode && mediaNode.type === state.schema.nodes.media) { return mediaNode; } return null; };