@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
445 lines (440 loc) • 18 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _regeneratorRuntime from "@babel/runtime/regenerator";
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
/**
* @jsxRuntime classic
* @jsx jsx
*/
import React from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
import { jsx } from '@emotion/react';
import { calculateOffsetLeft } from '@atlaskit/editor-common/media-single';
import { calcColumnsFromPx, calcMediaPxWidth, calcPctFromPx, handleSides, imageAlignmentMap, Resizer, snapTo, wrappedLayouts } from '@atlaskit/editor-common/ui';
import { calculateSnapPoints } from '@atlaskit/editor-common/utils';
import { findParentNodeOfTypeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
import { akEditorWideLayoutWidth } from '@atlaskit/editor-shared-styles';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { checkMediaType } from '../../pm-plugins/utils/check-media-type';
import { wrapperStyle } from './styled';
// eslint-disable-next-line @repo/internal/react/no-class-components
var ResizableMediaSingle = /*#__PURE__*/function (_React$Component) {
function ResizableMediaSingle() {
var _this;
_classCallCheck(this, ResizableMediaSingle);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, ResizableMediaSingle, [].concat(args));
_defineProperty(_this, "hasResized", false);
_defineProperty(_this, "state", {
offsetLeft: calculateOffsetLeft(_this.insideInlineLike, _this.insideLayout, _this.props.view.dom, undefined),
isVideoFile: false
});
_defineProperty(_this, "displayGrid", function (visible, gridType, highlight) {
var _pluginInjectionApi$g;
var _this$props = _this.props,
pluginInjectionApi = _this$props.pluginInjectionApi,
view = _this$props.view;
pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$g = pluginInjectionApi.grid) === null || _pluginInjectionApi$g === void 0 || (_pluginInjectionApi$g = _pluginInjectionApi$g.actions) === null || _pluginInjectionApi$g === void 0 || _pluginInjectionApi$g.displayGrid(view)({
visible: visible,
gridType: gridType,
highlight: highlight
});
});
_defineProperty(_this, "calcNewSize", function (newWidth, stop) {
var _this$props2 = _this.props,
layout = _this$props2.layout,
state = _this$props2.view.state;
if (!_this.hasResized && expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
var _this$wrapper;
var mediaDomEl = (_this$wrapper = _this.wrapper) === null || _this$wrapper === void 0 ? void 0 : _this$wrapper.querySelector('div[data-prosemirror-node-name="media"]');
if (mediaDomEl) {
var event = new CustomEvent('resized');
mediaDomEl === null || mediaDomEl === void 0 || mediaDomEl.dispatchEvent(event);
}
_this.hasResized = true;
}
var newPct = calcPctFromPx(newWidth, _this.props.lineLength) * 100;
_this.setState({
resizedPctWidth: newPct
});
var newLayout = hasParentNodeOfType(state.schema.nodes.table)(state.selection) ? layout : _this.calcUnwrappedLayout(newPct, newWidth);
if (newPct <= 100) {
if (_this.wrappedLayout && (stop ? newPct !== 100 : true)) {
newLayout = layout;
}
return {
width: newPct,
layout: newLayout
};
} else {
return {
width: _this.props.pctWidth || null,
layout: newLayout
};
}
});
_defineProperty(_this, "calcUnwrappedLayout", function (pct, width) {
if (pct <= 100) {
return 'center';
}
if (width <= akEditorWideLayoutWidth) {
return 'wide';
}
return 'full-width';
});
_defineProperty(_this, "calcColumnLeftOffset", function () {
var offsetLeft = _this.state.offsetLeft;
return _this.insideInlineLike ? calcColumnsFromPx(offsetLeft, _this.props.lineLength, _this.props.gridSize) : 0;
});
_defineProperty(_this, "calcPxWidth", function (useLayout) {
var _this$props3 = _this.props,
_this$props3$width = _this$props3.width,
origWidth = _this$props3$width === void 0 ? 0 : _this$props3$width,
origHeight = _this$props3.height,
layout = _this$props3.layout,
pctWidth = _this$props3.pctWidth,
lineLength = _this$props3.lineLength,
containerWidth = _this$props3.containerWidth,
fullWidthMode = _this$props3.fullWidthMode,
getPos = _this$props3.getPos,
state = _this$props3.view.state;
var resizedPctWidth = _this.state.resizedPctWidth;
var pos = typeof getPos === 'function' ? getPos() : undefined;
return calcMediaPxWidth({
origWidth: origWidth,
origHeight: origHeight,
pctWidth: pctWidth,
state: state,
containerWidth: {
width: containerWidth,
lineLength: lineLength
},
isFullWidthModeEnabled: fullWidthMode,
layout: useLayout || layout,
pos: pos,
resizedPctWidth: resizedPctWidth
});
});
_defineProperty(_this, "highlights", function (newWidth, snapPoints) {
var snapWidth = snapTo(newWidth, snapPoints);
var _this$props$view$stat = _this.props.view.state.schema.nodes,
layoutColumn = _this$props$view$stat.layoutColumn,
table = _this$props$view$stat.table,
expand = _this$props$view$stat.expand,
nestedExpand = _this$props$view$stat.nestedExpand,
panel = _this$props$view$stat.panel;
if (_this.$pos && !!findParentNodeOfTypeClosestToPos(_this.$pos, [layoutColumn, table, expand, nestedExpand, panel].filter(Boolean))) {
return [];
}
if (snapWidth > akEditorWideLayoutWidth) {
return ['full-width'];
}
var _this$props4 = _this.props,
layout = _this$props4.layout,
lineLength = _this$props4.lineLength,
gridSize = _this$props4.gridSize;
var columns = calcColumnsFromPx(snapWidth, lineLength, gridSize);
var columnWidth = Math.round(columns);
var highlight = [];
if (layout === 'wrap-left' || layout === 'align-start') {
highlight.push(0, columnWidth);
} else if (layout === 'wrap-right' || layout === 'align-end') {
highlight.push(gridSize, gridSize - columnWidth);
} else if (_this.insideInlineLike) {
highlight.push(Math.round(columns + _this.calcColumnLeftOffset()));
} else {
highlight.push(Math.floor((gridSize - columnWidth) / 2), Math.ceil((gridSize + columnWidth) / 2));
}
return highlight;
});
_defineProperty(_this, "saveWrapper", function (wrapper) {
return _this.wrapper = wrapper;
});
return _this;
}
_inherits(ResizableMediaSingle, _React$Component);
return _createClass(ResizableMediaSingle, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var offsetLeft = calculateOffsetLeft(this.insideInlineLike, this.insideLayout, this.props.view.dom, this.wrapper);
if (offsetLeft !== this.state.offsetLeft && offsetLeft >= 0) {
this.setState({
offsetLeft: offsetLeft
});
}
// Handle undo, when the actual pctWidth changed,
// we sync up with the internal state.
if (prevProps.pctWidth !== this.props.pctWidth) {
this.setState({
resizedPctWidth: this.props.pctWidth
});
}
return true;
}
}, {
key: "wrappedLayout",
get: function get() {
return wrappedLayouts.indexOf(this.props.layout) > -1;
}
// check if is inside of a table
}, {
key: "isNestedInTable",
value: function isNestedInTable() {
var table = this.props.view.state.schema.nodes.table;
if (!this.$pos) {
return false;
}
return !!findParentNodeOfTypeClosestToPos(this.$pos, table);
}
}, {
key: "componentDidMount",
value: function () {
var _componentDidMount = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var viewMediaClientConfig;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
viewMediaClientConfig = this.props.viewMediaClientConfig;
if (!viewMediaClientConfig) {
_context.next = 4;
break;
}
_context.next = 4;
return this.checkVideoFile(viewMediaClientConfig);
case 4:
case "end":
return _context.stop();
}
}, _callee, this);
}));
function componentDidMount() {
return _componentDidMount.apply(this, arguments);
}
return componentDidMount;
}()
}, {
key: "UNSAFE_componentWillReceiveProps",
value: function UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.viewMediaClientConfig !== nextProps.viewMediaClientConfig) {
this.checkVideoFile(nextProps.viewMediaClientConfig);
}
if (this.props.layout !== nextProps.layout) {
this.checkLayout(this.props.layout, nextProps.layout);
}
}
}, {
key: "checkVideoFile",
value: function () {
var _checkVideoFile = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(viewMediaClientConfig) {
var $pos, mediaNode, mediaType, isVideoFile;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
$pos = this.$pos;
if (!(!$pos || !viewMediaClientConfig)) {
_context2.next = 3;
break;
}
return _context2.abrupt("return");
case 3:
mediaNode = this.props.view.state.doc.nodeAt($pos.pos + 1);
if (!mediaNode) {
_context2.next = 10;
break;
}
_context2.next = 7;
return checkMediaType(mediaNode, viewMediaClientConfig);
case 7:
_context2.t0 = _context2.sent;
_context2.next = 11;
break;
case 10:
_context2.t0 = undefined;
case 11:
mediaType = _context2.t0;
isVideoFile = mediaType !== 'external' && mediaType !== 'image';
if (this.state.isVideoFile !== isVideoFile) {
this.setState({
isVideoFile: isVideoFile
});
}
case 14:
case "end":
return _context2.stop();
}
}, _callee2, this);
}));
function checkVideoFile(_x) {
return _checkVideoFile.apply(this, arguments);
}
return checkVideoFile;
}()
/**
* When returning to center layout from a wrapped/aligned layout, it might actually
* be wide or full-width
*/
}, {
key: "checkLayout",
value: function checkLayout(oldLayout, newLayout) {
var resizedPctWidth = this.state.resizedPctWidth;
if (wrappedLayouts.indexOf(oldLayout) > -1 && newLayout === 'center' && resizedPctWidth) {
var layout = this.calcUnwrappedLayout(resizedPctWidth, this.calcPxWidth(newLayout));
this.props.updateSize(resizedPctWidth, layout);
}
}
}, {
key: "$pos",
get: function get() {
if (typeof this.props.getPos !== 'function') {
return null;
}
var pos = this.props.getPos();
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (Number.isNaN(pos) || typeof pos !== 'number') {
return null;
}
// need to pass view because we may not get updated props in time
return this.props.view.state.doc.resolve(pos);
}
/**
* The maxmimum number of grid columns this node can resize to.
*/
}, {
key: "gridWidth",
get: function get() {
var gridSize = this.props.gridSize;
return !(this.wrappedLayout || this.insideInlineLike) ? gridSize / 2 : gridSize;
}
}, {
key: "insideInlineLike",
get: function get() {
var $pos = this.$pos;
if (!$pos) {
return false;
}
var listItem = this.props.view.state.schema.nodes.listItem;
return !!findParentNodeOfTypeClosestToPos($pos, [listItem]);
}
}, {
key: "insideLayout",
get: function get() {
var $pos = this.$pos;
if (!$pos) {
return false;
}
var layoutColumn = this.props.view.state.schema.nodes.layoutColumn;
return !!findParentNodeOfTypeClosestToPos($pos, [layoutColumn]);
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props5 = this.props,
origWidth = _this$props5.width,
origHeight = _this$props5.height,
layout = _this$props5.layout,
pctWidth = _this$props5.pctWidth,
containerWidth = _this$props5.containerWidth,
fullWidthMode = _this$props5.fullWidthMode,
selected = _this$props5.selected,
children = _this$props5.children;
var initialWidth = this.calcPxWidth(); // width with padding
var ratio;
if (origWidth) {
ratio = (origHeight / origWidth * 100).toFixed(3);
}
var enable = {};
handleSides.forEach(function (side) {
var oppositeSide = side === 'left' ? 'right' : 'left';
if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
if (_this2.props.disableHandles) {
enable[side] = false;
return;
}
}
enable[side] = ['full-width', 'wide', 'center'].concat("wrap-".concat(oppositeSide)).concat("align-".concat(imageAlignmentMap[oppositeSide])).indexOf(layout) > -1;
if (side === 'left' && _this2.insideInlineLike) {
enable[side] = false;
}
});
var snapPointsProps = {
$pos: this.$pos,
akEditorWideLayoutWidth: akEditorWideLayoutWidth,
allowBreakoutSnapPoints: this.props.allowBreakoutSnapPoints,
containerWidth: this.props.containerWidth,
gridSize: this.props.gridSize,
gridWidth: this.gridWidth,
insideInlineLike: this.insideInlineLike,
insideLayout: this.insideLayout,
isVideoFile: this.state.isVideoFile,
lineLength: this.props.lineLength,
offsetLeft: this.state.offsetLeft,
wrappedLayout: this.wrappedLayout
};
var nestedInTableHandleStyles = function nestedInTableHandleStyles(isNestedInTable) {
if (!isNestedInTable) {
return;
}
return {
left: {
left: "calc(".concat("var(--ds-space-025, 2px)", " * -0.5)"),
paddingLeft: '0px'
},
right: {
right: "calc(".concat("var(--ds-space-025, 2px)", " * -0.5)"),
paddingRight: '0px'
}
};
};
return jsx("div", {
ref: this.saveWrapper
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766
,
css: wrapperStyle({
layout: layout,
isResized: !!pctWidth,
containerWidth: containerWidth || origWidth,
fullWidthMode: fullWidthMode,
width: origWidth
})
}, jsx(Resizer
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
, _extends({}, this.props, {
displayGrid: this.displayGrid,
ratio: ratio,
width: initialWidth,
selected: selected,
enable: enable,
calcNewSize: this.calcNewSize,
snapPoints: calculateSnapPoints(snapPointsProps),
scaleFactor: !this.wrappedLayout && !this.insideInlineLike ? 2 : 1,
highlights: this.highlights,
nodeType: "media",
dispatchAnalyticsEvent: this.props.dispatchAnalyticsEvent
// when cursor is located below a media with caption,
// press “Up“ key will result cursor focus on an invalid position, (on the resize handler)
// This workaround adds an empty div inside the resize handler to prevent the issue.
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
handleComponentFunc: function handleComponentFunc() {
return jsx("div", {
contentEditable: false
});
},
handleStyles: nestedInTableHandleStyles(this.isNestedInTable())
}), children));
}
}]);
}(React.Component);
export { ResizableMediaSingle as default };