@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
293 lines (284 loc) • 11.3 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
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 { isExternalImageIdentifier, isMediaBlobUrl } from '@atlaskit/media-client';
import { isExternalMedia } from '../../ui/toolbar/utils';
import { getMediaPluginState } from '../main';
import { isVideo } from './media-single';
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;
};
var isSelectionMediaInlineNode = function isSelectionMediaInlineNode(state) {
var _ref3 = state.selection,
node = _ref3.node;
return node && node.type === state.schema.nodes.mediaInline;
};
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;
};
var getMediaInlineNodeFromSelection = function getMediaInlineNodeFromSelection(state) {
if (!isSelectionMediaInlineNode(state)) {
return null;
}
var tr = state.tr;
var pos = tr.selection.from;
var mediaNode = tr.doc.nodeAt(pos);
return mediaNode;
};
export var isMediaSingleOrInlineNodeSelected = function isMediaSingleOrInlineNodeSelected(state) {
var _getMediaPluginState = getMediaPluginState(state),
allowInlineImages = _getMediaPluginState.allowInlineImages;
return isSelectionMediaSingleNode(state) || allowInlineImages && isSelectionMediaInlineNode(state);
};
export var getMediaSingleOrInlineNodeFromSelection = function getMediaSingleOrInlineNodeFromSelection(state) {
var _getMediaPluginState2 = getMediaPluginState(state),
allowInlineImages = _getMediaPluginState2.allowInlineImages;
var mediaNode = getMediaNodeFromSelection(state) || allowInlineImages && getMediaInlineNodeFromSelection(state);
return mediaNode || null;
};
export var getMediaFromSupportedMediaNodesFromSelection = function getMediaFromSupportedMediaNodesFromSelection(state) {
var _ref4 = state.selection,
node = _ref4.node;
// Specifically for media supported nodes, double click could have been initiated on the media caption
if (!node) {
return null;
}
switch (node.type) {
case node.type.schema.nodes.media:
case node.type.schema.nodes.mediaInline:
return node;
case node.type.schema.nodes.mediaSingle:
case node.type.schema.nodes.mediaGroup:
return node.firstChild;
default:
return null;
}
};
export var isNodeDoubleClickSupportedInLivePagesViewMode = function isNodeDoubleClickSupportedInLivePagesViewMode(isViewMode, node) {
if (!node) {
return false;
}
// Double Click is not supported for video nodes on both views
if (isVideo(node.attrs.__fileMimeType)) {
return false;
}
// Double click is supported for all editor media nodes
if (!isViewMode) {
return true;
}
// Double Click is not supported for mediaGroup and mediaInline nodes that are file
if (node.type === node.type.schema.nodes.mediaGroup || node.type === node.type.schema.nodes.mediaInline && node.attrs.type === 'file') {
return false;
}
// Double Click supported for all other media nodes
return true;
};
export var getIdentifier = function getIdentifier(attrs) {
if (isExternalMedia(attrs)) {
return {
mediaItemType: 'external-image',
dataURI: attrs.url
};
} else {
var id = attrs.id,
_attrs$collection = attrs.collection,
collection = _attrs$collection === void 0 ? '' : _attrs$collection;
return {
id: id,
mediaItemType: 'file',
collectionName: collection
};
}
};
export var extractMediaNodes = function extractMediaNodes(doc) {
var mediaNodes = [];
doc.descendants(function (node) {
if (node.type.name === 'media' || node.type.name === 'mediaInline') {
mediaNodes.push(node);
}
});
return mediaNodes;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export var createMediaIdentifierArray = function createMediaIdentifierArray(mediaNodes) {
var mediaIdentifierMap = new Map();
mediaNodes.forEach(function (item) {
var attrs = item.attrs;
if (!attrs) {
return;
}
if (isVideo(attrs.__fileMimeType)) {
return;
}
var identifier = getIdentifier(attrs);
// Add only if not already processed
var idKey = isExternalImageIdentifier(identifier) ? identifier.dataURI : identifier.id;
if (!mediaIdentifierMap.has(idKey)) {
mediaIdentifierMap.set(idKey, identifier);
}
});
return _toConsumableArray(mediaIdentifierMap.values());
};