UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

255 lines (242 loc) • 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calcMinWidth = exports.calcMediaSinglePixelWidth = exports.calcMediaSingleMaxWidth = void 0; exports.calculateOffsetLeft = calculateOffsetLeft; exports.getMediaSingleInitialWidth = exports.getMaxWidthForNestedNodeNext = exports.getMaxWidthForNestedNode = exports.currentMediaNodeWithPos = void 0; exports.getMediaSinglePixelWidth = getMediaSinglePixelWidth; exports.roundToNearest = exports.getParentWidthForNestedMediaSingleNodeForInsertion = exports.getParentWidthForNestedMediaSingleNode = void 0; var _state = require("@atlaskit/editor-prosemirror/state"); var _editorSharedStyles = require("@atlaskit/editor-shared-styles"); var _richMediaUtils = require("../utils/rich-media-utils"); var _constants = require("./constants"); /** * Convert media node width to pixel * * for legacy experience, image is aligned inside resize handle bar with a gap. So gutterOffset is used to for this use case. * for new experience, image is aligned with resize handle bar, so gutterOffset is 0 * * @param width - media single node width * @param editorWidth - width of editor * @param widthType - width type is defined in the adf document for mediaSingle node, and it is associated with the `width` * @param gutterOffset - resize handle bar offset, determines whether align with resize handle bar * @returns pixel number for media single node */ function getMediaSinglePixelWidth(width, editorWidth) { var widthType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'percentage'; var gutterOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; if (widthType === 'pixel') { return width; } return Math.ceil((editorWidth + gutterOffset) * (width / 100) - gutterOffset); } /** * Convert width attribute to pixel value for legacy (resized or not resisized) and new media single node for new experience * @param width node width attribute * @param widthType node widthType attribute * @param origWidth original media width * @param layout node layout attribute * @param contentWidth editor content width * @param containerWidth editor container width * @param gutterOffset gap between resizer handle and media * @returns pixel width of the node */ var calcMediaSinglePixelWidth = exports.calcMediaSinglePixelWidth = function calcMediaSinglePixelWidth(_ref) { var width = _ref.width, _ref$widthType = _ref.widthType, widthType = _ref$widthType === void 0 ? 'percentage' : _ref$widthType, origWidth = _ref.origWidth, layout = _ref.layout, contentWidth = _ref.contentWidth, containerWidth = _ref.containerWidth, _ref$gutterOffset = _ref.gutterOffset, gutterOffset = _ref$gutterOffset === void 0 ? 0 : _ref$gutterOffset; if (widthType === 'pixel' && width) { return width; } switch (layout) { case 'wide': return calcLegacyWideWidth(containerWidth, origWidth, contentWidth); case 'full-width': // legacy and new experience have different definitions of full-width, // since it's for new experience, we convert to the new definition return calcMediaSingleMaxWidth(containerWidth); default: if (width) { return Math.ceil(((contentWidth || containerWidth) + gutterOffset) * (width / 100) - gutterOffset); } } // Handle the case of not resized node with wrapped layout // It's possible that the node is first inserted with align layout (e.g. jira) // in which the legacy image would render the width as min(origWidth, halfContentWidth). // However, new experience won't be able to distinguish the two. Thus, we render halfContentWidth // to make sure confluence legacy node is renderered correctly if (_constants.wrappedLayouts.includes(layout)) { return Math.ceil((contentWidth || containerWidth) / 2); } // set initial width for not resized legacy image return getMediaSingleInitialWidth(origWidth, // in case containerWidth is 0, we fallback to undefined to use akEditorDefaultLayoutWidth contentWidth || containerWidth || undefined); }; /** * Calculate pixel width for legacy media single * @param contentWidth editor content width * @param containerWidth editor container width */ var calcLegacyWideWidth = function calcLegacyWideWidth(containerWidth, origWidth, contentWidth) { if (contentWidth) { var wideWidth = Math.ceil(contentWidth * _editorSharedStyles.breakoutWideScaleRatio); return wideWidth > containerWidth ? contentWidth : wideWidth; } return origWidth; }; /** * Calculate maximum width allowed for media single node in fix-width editor in new experience * @param containerWidth width of editor container */ var calcMediaSingleMaxWidth = exports.calcMediaSingleMaxWidth = function calcMediaSingleMaxWidth(containerWidth) { var fullWidthPadding = _editorSharedStyles.akEditorGutterPadding * 2; return Math.min(containerWidth - fullWidthPadding, _editorSharedStyles.akEditorFullWidthLayoutWidth); }; /** * Calculate initial media single pixel width. * Make it fall between max width and min width * @param origWidth original width of image (media node width) * @param maxWidth default to akEditorDefaultLayoutWidth (760) * @param minWidth default to MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH (24) */ var getMediaSingleInitialWidth = exports.getMediaSingleInitialWidth = function getMediaSingleInitialWidth() { var origWidth = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _constants.DEFAULT_IMAGE_WIDTH; var maxWidth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _editorSharedStyles.akEditorDefaultLayoutWidth; var minWidth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _constants.MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH; return Math.max(Math.min(origWidth, maxWidth), minWidth); }; function calculateOffsetLeft(insideInlineLike, insideLayout, pmViewDom, wrapper) { var offsetLeft = 0; if (wrapper && insideInlineLike && !insideLayout) { var currentNode = wrapper; var boundingRect = currentNode.getBoundingClientRect(); offsetLeft = boundingRect.left - pmViewDom.getBoundingClientRect().left; } return offsetLeft; } /** * Returns the number rounded to the nearest interval. * @param {number} value The number to round * @param {number} interval The numeric interval to round to, default to 0.5 * @return {number} the rounded number */ var roundToNearest = exports.roundToNearest = function roundToNearest(value) { var interval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _constants.DEFAULT_ROUNDING_INTERVAL; return Math.round(value / interval) * interval; }; /** * Retuns minimum value for media single node * @param isVideoFile is child media of video type * @param contentWidth parent content width */ var calcMinWidth = exports.calcMinWidth = function calcMinWidth(isVideoFile, contentWidth) { return Math.min(contentWidth, isVideoFile ? _constants.MEDIA_SINGLE_VIDEO_MIN_PIXEL_WIDTH : _constants.MEDIA_SINGLE_DEFAULT_MIN_PIXEL_WIDTH); }; /** * Get parent width for a nested media single node * @param view Editor view * @param pos node position */ var getMaxWidthForNestedNode = exports.getMaxWidthForNestedNode = function getMaxWidthForNestedNode(view, pos) { if (typeof pos !== 'number') { return null; } if ((0, _richMediaUtils.isRichMediaInsideOfBlockNode)(view, pos)) { var $pos = view.state.doc.resolve(pos); var domNode = view.nodeDOM($pos.pos); if ($pos.nodeAfter && _richMediaUtils.floatingLayouts.indexOf($pos.nodeAfter.attrs.layout) > -1 && domNode && domNode.parentElement) { return domNode.parentElement.offsetWidth; } if (domNode instanceof HTMLElement) { return domNode.offsetWidth; } } return null; }; var calcParentPadding = function calcParentPadding(view, resolvedPos) { // since table has constant padding, use hardcoded constant instead of query the dom var tablePadding = 8; var _view$state$schema$no = view.state.schema.nodes, tableCell = _view$state$schema$no.tableCell, tableHeader = _view$state$schema$no.tableHeader; return [tableCell, tableHeader].includes(resolvedPos.parent.type) ? tablePadding * 2 : 0; }; /** * Get parent width for a nested media single node for new experience * We don't check for mediaSingle selection in this function. * @param view Editor view * @param pos node position * @param forInsertion for insertion */ var getMaxWidthForNestedNodeNext = exports.getMaxWidthForNestedNodeNext = function getMaxWidthForNestedNodeNext(view, pos, forInsertion) { if (typeof pos !== 'number') { return null; } var $pos = view.state.doc.resolve(pos); if ($pos && $pos.parent.type.name !== 'doc') { return forInsertion ? getParentWidthForNestedMediaSingleNodeForInsertion($pos, view) : getParentWidthForNestedMediaSingleNode($pos, view); } return null; }; /** * Get parent content width for nested media single node. * @param resolvedPos resolved Position of the node * @param view editor view * @returns parent content width for nested node */ var getParentWidthForNestedMediaSingleNode = exports.getParentWidthForNestedMediaSingleNode = function getParentWidthForNestedMediaSingleNode(resolvedPos, view) { var domNode = view.nodeDOM(resolvedPos.pos); if (resolvedPos.nodeAfter && _richMediaUtils.floatingLayouts.includes(resolvedPos.nodeAfter.attrs.layout) && domNode && domNode.parentElement) { var parentPadding = calcParentPadding(view, resolvedPos); return domNode.parentElement.offsetWidth - parentPadding; } if (domNode instanceof HTMLElement) { return domNode.offsetWidth; } return null; }; /** * Get parent width for nested media single nodes * @param resolvedPos resolved Position of the node * @param view editor view * @returns parent width used for media single initial width on insertion */ var getParentWidthForNestedMediaSingleNodeForInsertion = exports.getParentWidthForNestedMediaSingleNodeForInsertion = function getParentWidthForNestedMediaSingleNodeForInsertion(resolvedPos, view) { var parentPos = resolvedPos.before(resolvedPos.depth); var parentDomNode = view.nodeDOM(parentPos); var parentPadding = calcParentPadding(view, resolvedPos); if (parentDomNode instanceof HTMLElement) { return parentDomNode.offsetWidth - parentPadding; } return null; }; /** * * @param editorState current editor state * @returns selected media node (child of mediaSingle only) with position */ var currentMediaNodeWithPos = exports.currentMediaNodeWithPos = function currentMediaNodeWithPos(editorState) { var doc = editorState.doc, selection = editorState.selection, schema = editorState.schema; if (!doc || !selection || !(selection instanceof _state.NodeSelection) || selection.node.type !== schema.nodes.mediaSingle) { return; } var pos = selection.$anchor.pos + 1; var node = doc.nodeAt(pos); if (!node || node.type !== schema.nodes.media) { return; } return { node: node, pos: pos }; };