UNPKG

@atlaskit/editor-plugin-selection

Version:

Selection plugin for @atlaskit/editor-core

143 lines (139 loc) 6.23 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.toDOM = void 0; var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray")); var _selection = require("@atlaskit/editor-common/selection"); var _utils = require("../utils"); /** * We have a couple of nodes that require us to compute style * on different elements, ideally all nodes should be able to * compute the appropriate styles based on their wrapper. */ var nestedCases = { 'tableView-content-wrap': 'table', 'mediaSingleView-content-wrap': '.rich-media-item', 'bodiedExtensionView-content-wrap': '.extension-container', 'multiBodiedExtensionView-content-wrap': '.multiBodiedExtension--container', 'embedCardView-content-wrap': '.rich-media-item', 'datasourceView-content-wrap': '.datasourceView-content-inner-wrap' }; var computeNestedStyle = function computeNestedStyle(dom) { var foundKey = Object.keys(nestedCases).find(function (className) { return dom.classList.contains(className); }); var nestedSelector = foundKey && nestedCases[foundKey]; if (nestedSelector) { var nestedElement = dom.querySelector(nestedSelector); if (nestedElement) { return window.getComputedStyle(nestedElement); } } }; var measureHeight = function measureHeight(style) { return measureValue(style, ['height', 'padding-top', 'padding-bottom', 'border-top-width', 'border-bottom-width']); }; var measureWidth = function measureWidth(style) { return measureValue(style, ['width', 'padding-left', 'padding-right', 'border-left-width', 'border-right-width']); }; var measureValue = function measureValue(style, measureValues) { var _measureValues = (0, _toArray2.default)(measureValues), base = _measureValues[0], contentBoxValues = _measureValues.slice(1); var measures = [style.getPropertyValue(base)]; var boxSizing = style.getPropertyValue('box-sizing'); if (boxSizing === 'content-box') { contentBoxValues.forEach(function (value) { measures.push(style.getPropertyValue(value)); }); } var result = 0; for (var i = 0; i < measures.length; i++) { result += parseFloat(measures[i]); } return result; }; var mutateElementStyle = function mutateElementStyle(element, style, side) { element.style.transform = style.getPropertyValue('transform'); if ((0, _utils.isLeftCursor)(side)) { element.style.width = style.getPropertyValue('width'); element.style.marginLeft = style.getPropertyValue('margin-left'); } else { var marginRight = parseFloat(style.getPropertyValue('margin-right')); if (marginRight > 0) { element.style.marginLeft = "-".concat(Math.abs(marginRight), "px"); } else { element.style.paddingRight = "".concat(Math.abs(marginRight), "px"); } } }; /** * For nested elements (e.g. .extension-container inside * .extensionView-content-wrap), use getBoundingClientRect to compute * the exact pixel offset between the gap cursor's position in the * flow and the inner element's visual position. */ var positionFromRect = function positionFromRect(gapCursor, cursorParent, nestedElement) { var cursorRect = cursorParent.getBoundingClientRect(); var innerRect = nestedElement.getBoundingClientRect(); gapCursor.style.marginTop = "".concat(innerRect.top - cursorRect.top, "px"); gapCursor.style.left = "".concat(innerRect.left - cursorRect.left, "px"); gapCursor.style.width = "".concat(innerRect.width, "px"); }; var toDOM = exports.toDOM = function toDOM(view, getPos) { var selection = view.state.selection; var $from = selection.$from, side = selection.side; var isRightCursor = side === _selection.Side.RIGHT; var node = isRightCursor ? $from.nodeBefore : $from.nodeAfter; var element = document.createElement('span'); element.className = "ProseMirror-gapcursor ".concat(isRightCursor ? '-right' : '-left'); element.appendChild(document.createElement('span')); if (element.firstChild) { var gapCursor = element.firstChild; // The DOM from view.nodeDOM() might be stale after paste // Use requestAnimationFrame to wait for DOM to update, then fetch and measure requestAnimationFrame(function () { var nodeStart = getPos(); if (nodeStart === undefined) { return; } // if selection has changed, we no longer need to compute the styles for the gapcursor if (!view.state.selection.eq(selection)) { return; } var dom = view.nodeDOM(nodeStart); if (dom instanceof HTMLElement) { // For native embed extensions only, use getBoundingClientRect // to position the gap cursor precisely relative to the inner // .extension-container if (dom.classList.contains('extensionView-content-wrap')) { var nativeEmbed = dom.querySelector('.extension-container:has([data-native-embed-alignment])'); if (nativeEmbed) { var nativeEmbedStyle = window.getComputedStyle(nativeEmbed); gapCursor.style.height = "".concat(measureHeight(nativeEmbedStyle), "px"); positionFromRect(gapCursor, element, nativeEmbed); return; } } var style = computeNestedStyle(dom) || window.getComputedStyle(dom); gapCursor.style.height = "".concat(measureHeight(style), "px"); var layoutMode = node && (0, _utils.getLayoutModeFromTargetNode)(node); if (nodeStart !== 0 || layoutMode || (node === null || node === void 0 ? void 0 : node.type.name) === 'table') { gapCursor.style.marginTop = style.getPropertyValue('margin-top'); } var isNestedTable = (node === null || node === void 0 ? void 0 : node.type.name) === 'table' && selection.$to.depth > 0; if (layoutMode && !isNestedTable) { gapCursor.setAttribute('layout', layoutMode); var breakoutModeStyle = (0, _utils.getComputedStyleForLayoutMode)(dom, node, style); gapCursor.style.width = "".concat(measureWidth(breakoutModeStyle), "px"); } else { mutateElementStyle(gapCursor, style, selection.side); } } }); } return element; };