@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
276 lines (275 loc) • 11.9 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import { GapCursorSelection } from '@atlaskit/editor-common/selection/gap-cursor/selection';
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
import { EditorState, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { dropPoint } from '@atlaskit/editor-prosemirror/transform';
import { getMediaNodeInsertionType } from './utils/media-inline';
/** Keeps a media insertion point collapsed so a drop never replaces the current selection. */
export var mediaDropSelection = function mediaDropSelection(doc, pos) {
var $pos = doc.resolve(pos);
return $pos.parent.inlineContent ? TextSelection.create(doc, pos) : new GapCursorSelection($pos);
};
export var findMediaDropPosition = function findMediaDropPosition(view, coords, fileTypes, mediaOptions) {
if (!view.editable || view.dragging || fileTypes.length === 0) {
return;
}
var rect = view.dom.getBoundingClientRect();
var hit = view.posAtCoords({
left: Math.max(rect.left + 1, Math.min(coords.left, rect.right - 1)),
top: Math.max(rect.top + 1, Math.min(coords.top, rect.bottom - 1))
});
if (!hit) {
return;
}
var _view$state = view.state,
doc = _view$state.doc,
schema = _view$state.schema;
var targetState = EditorState.create({
doc: doc,
schema: schema,
selection: mediaDropSelection(doc, hit.pos)
});
var supportedOptions = schema.nodes.mediaInline ? mediaOptions : _objectSpread(_objectSpread({}, mediaOptions), {}, {
featureFlags: _objectSpread(_objectSpread({}, mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.featureFlags), {}, {
mediaInline: false
})
});
var insertionTypes = new Set(Array.from(new Set(fileTypes), function (fileType) {
return getMediaNodeInsertionType(targetState, supportedOptions, fileType);
}));
var slices = Array.from(insertionTypes).flatMap(function (insertionType) {
var node;
if (insertionType === 'inline') {
node = schema.nodes.mediaInline.create();
} else {
var container = insertionType === 'block' ? schema.nodes.mediaSingle : schema.nodes.mediaGroup;
if (!container || !schema.nodes.media) {
return [];
}
node = container.create(null, schema.nodes.media.create({
type: 'file'
}));
}
return new Slice(Fragment.from(node), 0, 0);
});
if (slices.length !== insertionTypes.size) {
return;
}
var _iterator = _createForOfIteratorHelper(slices),
_step;
try {
var _loop = function _loop() {
var slice = _step.value;
var pos = dropPoint(doc, hit.pos, slice);
if (pos !== null && slices.every(function (content) {
var alreadyValidated = content === slice && pos === hit.pos;
return alreadyValidated || dropPoint(doc, pos, content) === pos;
})) {
return {
v: pos
};
}
},
_ret;
for (_iterator.s(); !(_step = _iterator.n()).done;) {
_ret = _loop();
if (_ret) return _ret.v;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return;
};
/** Only a single image over a media card can replace it; captions and surrounding space insert. */
export var findMediaDropReplacement = function findMediaDropReplacement(view, event, fileTypes) {
var _mediaDom$querySelect;
if (!view.editable || view.dragging || fileTypes.length !== 1 || !fileTypes[0].startsWith('image/')) {
return;
}
if (!(event.target instanceof Element)) {
return;
}
var mediaDom = event.target.closest('[data-prosemirror-node-name="media"], [data-node-type="media"]');
if (!mediaDom || !view.dom.contains(mediaDom)) {
return;
}
var card = (_mediaDom$querySelect = mediaDom.querySelector('.media-card-wrapper')) !== null && _mediaDom$querySelect !== void 0 ? _mediaDom$querySelect : mediaDom;
var rect = card.getBoundingClientRect();
if (event.clientX < rect.left || event.clientX >= rect.right || event.clientY < rect.top || event.clientY >= rect.bottom) {
return;
}
var $pos = view.state.doc.resolve(view.posAtDOM(mediaDom, 0));
if ($pos.parent.type !== view.state.schema.nodes.mediaSingle) {
return;
}
var media = $pos.parent.firstChild;
if (!media || media.type !== view.state.schema.nodes.media) {
return;
}
var mimeType = media.attrs.__fileMimeType;
var hasImagePreview = !!card.querySelector('img[data-fileid], img[data-testid="media-image"], img[data-testid="media-card-svg"]') && !card.querySelector('video, [data-testid="media-card-play-button-wrapper"]');
var isImage = mimeType ? mimeType.startsWith('image/') : media.attrs.type === 'external' || hasImagePreview;
return isImage ? {
pos: $pos.before(),
media: media
} : undefined;
};
var mapReplacement = function mapReplacement(replacement, mapping) {
if (!replacement) {
return;
}
var mapped = mapping.mapResult(replacement.pos, 1);
return mapped.deleted ? undefined : _objectSpread(_objectSpread({}, replacement), {}, {
pos: mapped.pos
});
};
var currentReplacement = function currentReplacement(replacement, doc) {
if (!replacement) {
return;
}
var node = doc.nodeAt(replacement.pos);
var media = node === null || node === void 0 ? void 0 : node.firstChild;
var original = replacement.media;
if ((node === null || node === void 0 ? void 0 : node.type.name) === 'mediaSingle' && (media === null || media === void 0 ? void 0 : media.type) === original.type && media.attrs.id === original.attrs.id && media.attrs.collection === original.attrs.collection && media.attrs.type === original.attrs.type && media.attrs.url === original.attrs.url) {
return replacement;
}
return;
};
/** Retains drop locations while uploads prepare and other edits change document positions. */
export var MediaDropTargets = /*#__PURE__*/function () {
function MediaDropTargets() {
_classCallCheck(this, MediaDropTargets);
_defineProperty(this, "batches", new Map());
_defineProperty(this, "files", new Map());
}
return _createClass(MediaDropTargets, [{
key: "start",
value: function start(traceId, pos, replacement) {
var _this = this;
var batch = {
pos: pos,
replacement: replacement,
files: new Set(),
prepared: false
};
this.batches.set(traceId, batch);
return function () {
batch.prepared = true;
_this.prune();
};
}
}, {
key: "register",
value: function register(traceId, fileIds) {
var batch = this.batches.get(traceId);
if (!batch) {
return;
}
var _iterator2 = _createForOfIteratorHelper(fileIds),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var id = _step2.value;
batch.files.add(id);
this.files.set(id, batch);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
}
}, {
key: "positionFor",
value: function positionFor(fileId) {
var _this$files$get;
return (_this$files$get = this.files.get(fileId)) === null || _this$files$get === void 0 ? void 0 : _this$files$get.pos;
}
}, {
key: "replacementFor",
value: function replacementFor(fileId, doc) {
var _this$files$get2;
return currentReplacement((_this$files$get2 = this.files.get(fileId)) === null || _this$files$get2 === void 0 ? void 0 : _this$files$get2.replacement, doc);
}
}, {
key: "hoverReplacementFor",
value: function hoverReplacementFor(doc) {
return currentReplacement(this.hoverReplacement, doc);
}
}, {
key: "clearHover",
value: function clearHover() {
this.hover = undefined;
this.hoverReplacement = undefined;
}
}, {
key: "inserted",
value: function inserted(fileId) {
var _this$files$get3;
(_this$files$get3 = this.files.get(fileId)) === null || _this$files$get3 === void 0 || _this$files$get3.files.delete(fileId);
this.files.delete(fileId);
this.prune();
}
}, {
key: "map",
value: function map(mapping) {
if (mapping.maps.length === 0) {
return;
}
if (this.hover !== undefined) {
this.hover = mapping.map(this.hover, 1);
}
this.hoverReplacement = mapReplacement(this.hoverReplacement, mapping);
var _iterator3 = _createForOfIteratorHelper(this.batches.values()),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var batch = _step3.value;
batch.pos = mapping.map(batch.pos, 1);
batch.replacement = mapReplacement(batch.replacement, mapping);
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
}
}, {
key: "clear",
value: function clear() {
this.clearHover();
this.batches.clear();
this.files.clear();
}
}, {
key: "prune",
value: function prune() {
var _iterator4 = _createForOfIteratorHelper(this.batches),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var _step4$value = _slicedToArray(_step4.value, 2),
id = _step4$value[0],
batch = _step4$value[1];
if (batch.prepared && batch.files.size === 0) {
this.batches.delete(id);
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
}
}]);
}();