@atlaskit/editor-plugin-selection
Version:
Selection plugin for @atlaskit/editor-core
137 lines (133 loc) • 6.01 kB
JavaScript
import _toArray from "@babel/runtime/helpers/toArray";
import { Side } from '@atlaskit/editor-common/selection';
import { getComputedStyleForLayoutMode, getLayoutModeFromTargetNode, isLeftCursor } from '../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 = _toArray(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 (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");
};
export var toDOM = function toDOM(view, getPos) {
var selection = view.state.selection;
var $from = selection.$from,
side = selection.side;
var isRightCursor = side === 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 && 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 = getComputedStyleForLayoutMode(dom, node, style);
gapCursor.style.width = "".concat(measureWidth(breakoutModeStyle), "px");
} else {
mutateElementStyle(gapCursor, style, selection.side);
}
}
});
}
return element;
};