UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

445 lines (440 loc) 18 kB
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 };