@atlaskit/editor-plugin-card
Version:
Card plugin for @atlaskit/editor-core
419 lines (412 loc) • 18.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _react2 = require("@emotion/react");
var _ui = require("@atlaskit/editor-common/ui");
var _state = require("@atlaskit/editor-prosemirror/state");
var _utils = require("@atlaskit/editor-prosemirror/utils");
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _smartCard = require("@atlaskit/smart-card");
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
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) { (0, _defineProperty2.default)(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; }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(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
*/ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
// eslint-disable-next-line @repo/internal/react/no-class-components
var ResizableEmbedCard = exports.default = /*#__PURE__*/function (_React$Component) {
function ResizableEmbedCard() {
var _this;
(0, _classCallCheck2.default)(this, ResizableEmbedCard);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, ResizableEmbedCard, [].concat(args));
(0, _defineProperty2.default)(_this, "state", {
offsetLeft: _this.calcOffsetLeft()
});
(0, _defineProperty2.default)(_this, "calcNewSize", function (newWidth, stop) {
var _this$props = _this.props,
layout = _this$props.layout,
state = _this$props.view.state;
var newPct = (0, _ui.calcPctFromPx)(newWidth, _this.props.lineLength) * 100;
_this.setState({
resizedPctWidth: newPct
});
var newLayout = (0, _utils.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
};
}
});
(0, _defineProperty2.default)(_this, "calcUnwrappedLayout", function (pct, width) {
if (pct <= 100) {
return 'center';
}
if (width <= _this.wideLayoutWidth) {
return 'wide';
}
return 'full-width';
});
(0, _defineProperty2.default)(_this, "calcColumnLeftOffset", function () {
var offsetLeft = _this.state.offsetLeft;
return _this.insideInlineLike ? (0, _ui.calcColumnsFromPx)(offsetLeft, _this.props.lineLength, _this.props.gridSize) : 0;
});
(0, _defineProperty2.default)(_this, "calcPxWidth", function (useLayout) {
var _this$props2 = _this.props,
layout = _this$props2.layout,
pctWidth = _this$props2.pctWidth,
lineLength = _this$props2.lineLength,
containerWidth = _this$props2.containerWidth,
fullWidthMode = _this$props2.fullWidthMode,
getPos = _this$props2.getPos,
state = _this$props2.view.state;
var resizedPctWidth = _this.state.resizedPctWidth;
var pos = typeof getPos === 'function' ? getPos() : undefined;
return (0, _ui.calcMediaPxWidth)({
origWidth: _editorSharedStyles.DEFAULT_EMBED_CARD_WIDTH,
origHeight: _editorSharedStyles.DEFAULT_EMBED_CARD_HEIGHT,
pctWidth: pctWidth,
state: state,
containerWidth: {
width: containerWidth,
lineLength: lineLength
},
isFullWidthModeEnabled: fullWidthMode,
layout: useLayout || layout,
pos: pos,
resizedPctWidth: resizedPctWidth
});
});
(0, _defineProperty2.default)(_this, "handleResizeStart", function () {
var _this$props3 = _this.props,
view = _this$props3.view,
getPos = _this$props3.getPos;
if (typeof getPos === 'function') {
var pos = getPos();
if (typeof pos === 'number') {
var tr = view.state.tr.setSelection(_state.NodeSelection.create(view.state.doc, pos));
tr.setMeta('scrollIntoView', false);
view.dispatch(tr);
}
}
});
(0, _defineProperty2.default)(_this, "highlights", function (newWidth, snapPoints) {
var snapWidth = (0, _ui.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,
bodiedSyncBlock = _this$props$view$stat.bodiedSyncBlock;
// Hide resizing guideline when embed is nested
if (_this.$pos && !!(0, _utils.findParentNodeOfTypeClosestToPos)(_this.$pos, (0, _experiments.editorExperiment)('platform_synced_block', true) ? [layoutColumn, table, expand, nestedExpand, bodiedSyncBlock] : [layoutColumn, table, expand, nestedExpand])) {
return [];
}
if (snapWidth > _this.wideLayoutWidth) {
return ['full-width'];
}
var _this$props4 = _this.props,
layout = _this$props4.layout,
lineLength = _this$props4.lineLength,
gridSize = _this$props4.gridSize;
var columns = (0, _ui.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;
});
return _this;
}
(0, _inherits2.default)(ResizableEmbedCard, _React$Component);
return (0, _createClass2.default)(ResizableEmbedCard, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var offsetLeft = this.calcOffsetLeft();
if (offsetLeft !== this.state.offsetLeft && offsetLeft >= 0) {
this.setState({
offsetLeft: offsetLeft
});
}
if (this.props.layout !== prevProps.layout) {
this.checkLayout(prevProps.layout, this.props.layout);
}
}
}, {
key: "wrappedLayout",
get: function get() {
return _ui.wrappedLayouts.indexOf(this.props.layout) > -1;
}
/**
* 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 (_ui.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();
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: "calcOffsetLeft",
value: function calcOffsetLeft() {
var offsetLeft = 0;
if (this.wrapper && this.insideInlineLike) {
var currentNode = this.wrapper;
var boundingRect = currentNode.getBoundingClientRect();
var pmRect = this.props.view.dom.getBoundingClientRect();
offsetLeft = boundingRect.left - pmRect.left;
}
return offsetLeft;
}
}, {
key: "wideLayoutWidth",
get: function get() {
var lineLength = this.props.lineLength;
if (lineLength) {
return Math.ceil(lineLength * _editorSharedStyles.breakoutWideScaleRatio);
} else {
return _editorSharedStyles.akEditorWideLayoutWidth;
}
}
// 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 !!(0, _utils.findParentNodeOfTypeClosestToPos)(this.$pos, table);
}
}, {
key: "calcSnapPoints",
value: function calcSnapPoints() {
var offsetLeft = this.state.offsetLeft;
var _this$props5 = this.props,
containerWidth = _this$props5.containerWidth,
lineLength = _this$props5.lineLength;
var snapTargets = [];
for (var i = 0; i < this.gridWidth; i++) {
snapTargets.push((0, _ui.calcPxFromColumns)(i, lineLength, this.gridWidth) - offsetLeft);
}
// full width
snapTargets.push(lineLength - offsetLeft);
var minimumWidth = (0, _ui.calcPxFromColumns)(this.wrappedLayout || this.insideInlineLike ? 1 : 2, lineLength, this.props.gridSize);
var snapPoints = snapTargets.filter(function (width) {
return width >= minimumWidth;
});
var $pos = this.$pos;
if (!$pos) {
return snapPoints;
}
var isTopLevel = $pos.parent.type.name === 'doc';
if (isTopLevel) {
snapPoints.push(this.wideLayoutWidth);
var fullWidthPoint = containerWidth - _editorSharedStyles.akEditorBreakoutPadding;
if (fullWidthPoint > this.wideLayoutWidth) {
snapPoints.push(fullWidthPoint);
}
}
return snapPoints;
}
}, {
key: "insideInlineLike",
get: function get() {
var $pos = this.$pos;
if (!$pos) {
return false;
}
var listItem = this.props.view.state.schema.nodes.listItem;
return !!(0, _utils.findParentNodeOfTypeClosestToPos)($pos, [listItem]);
}
}, {
key: "getHeightDefiningComponent",
value:
/**
* Previously height of the box was controlled with paddingTop/paddingBottom trick inside Wrapper.
* It allowed height to be defined by a given percent ratio and so absolute value was defined by actual width.
* Also, it was part of styled component, which was fine because it was static through out life time of component.
*
* Now, two things changed:
* 1. If `height` is present we take it as actual height of the box, and hence we don't need
* (or even can't have, due to lack of width value) paddingTop trick.
* 2. Since `height` can be changing through out life time of a component, we can't have it as part of styled component,
* and hence we use `style` prop.
*/
function getHeightDefiningComponent() {
var _this$props6 = this.props,
height = _this$props6.height,
aspectRatio = _this$props6.aspectRatio;
var heightDefiningStyles;
if (height) {
heightDefiningStyles = {
height: "".concat(height, "px")
};
} else {
// paddingBottom css trick defines ratio of `iframe height (y) + header (32)` to `width (x)`,
// where is `aspectRatio` defines iframe aspectRatio alone
// So, visually:
//
// x
// ┌──────────┐
// │ header │ 32
// ├──────────┤
// │ │
// │ iframe │ y
// │ │
// └──────────┘
//
// aspectRatio = x / y
// paddingBottom = (y + 32) / x
// which can be achieved with css calc() as (1 / (x/y)) * 100)% + 32px
heightDefiningStyles = {
paddingBottom: "calc(".concat((1 / aspectRatio * 100).toFixed(3), "% + ").concat(_smartCard.embedHeaderHeight, "px)")
};
}
return (0, _react2.jsx)("span", {
"data-testid": 'resizable-embed-card-height-definer',
style: _objectSpread({
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
display: 'block',
/* Fixes extra padding problem in Firefox */
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/design-system/use-tokens-typography -- Ignored via go/DSP-18766
fontSize: 0,
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/design-system/use-tokens-typography -- Ignored via go/DSP-18766
lineHeight: 0
}, heightDefiningStyles)
});
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props7 = this.props,
layout = _this$props7.layout,
pctWidth = _this$props7.pctWidth,
containerWidth = _this$props7.containerWidth,
fullWidthMode = _this$props7.fullWidthMode,
isResizeDisabled = _this$props7.isResizeDisabled,
children = _this$props7.children;
var resizerProps = {
width: this.calcPxWidth(),
innerPadding: _editorSharedStyles.akEditorMediaResizeHandlerPadding
};
var enable = {};
_ui.handleSides.forEach(function (side) {
if (isResizeDisabled) {
enable[side] = false;
return;
}
var oppositeSide = side === 'left' ? 'right' : 'left';
enable[side] = ['full-width', 'wide', 'center'].concat("wrap-".concat(oppositeSide)).concat("align-".concat(_ui.imageAlignmentMap[oppositeSide])).indexOf(layout) > -1;
if (side === 'left' && _this2.insideInlineLike) {
enable[side] = false;
}
});
var nestedInTableHandleStyles = function nestedInTableHandleStyles(isNestedInTable) {
if (!isNestedInTable) {
return;
}
return {
left: {
left: "calc(".concat("var(--ds-space-negative-025, -2px)", " * 0.5)"),
paddingLeft: '0px'
},
right: {
right: "calc(".concat("var(--ds-space-negative-025, -2px)", " * 0.5)"),
paddingRight: '0px'
}
};
};
/* eslint-disable @atlaskit/design-system/consistent-css-prop-usage */
return (0, _react2.jsx)("div", {
"data-testid": "resizable-embed-card-spacing"
}, (0, _react2.jsx)("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
css: (0, _ui.wrapperStyle)({
layout: layout,
isResized: !!pctWidth,
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
containerWidth: containerWidth || _editorSharedStyles.DEFAULT_EMBED_CARD_WIDTH,
fullWidthMode: fullWidthMode
})
}, (0, _react2.jsx)(_ui.Resizer
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
, (0, _extends2.default)({}, this.props, {
enable: enable,
calcNewSize: this.calcNewSize,
snapPoints: this.calcSnapPoints(),
scaleFactor: !this.wrappedLayout && !this.insideInlineLike ? 2 : 1,
highlights: this.highlights,
nodeType: "embed",
onResizeStart: (0, _experiments.editorExperiment)('platform_synced_block', true) && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_9') ? this.handleResizeStart : undefined,
handleStyles: nestedInTableHandleStyles(this.isNestedInTable())
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
}, resizerProps), children, this.getHeightDefiningComponent())));
/* eslint-enable @atlaskit/design-system/consistent-css-prop-usage */
}
}]);
}(_react.default.Component);
(0, _defineProperty2.default)(ResizableEmbedCard, "defaultProps", {
aspectRatio: _editorSharedStyles.DEFAULT_EMBED_CARD_WIDTH / _editorSharedStyles.DEFAULT_EMBED_CARD_HEIGHT
});