@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
149 lines (147 loc) • 6.8 kB
JavaScript
import { fg } from '@atlaskit/platform-feature-flags';
/*
Calculates the position of the floating toolbar relative to the selection.
This is a re-implementation which closely matches the behaviour on Confluence renderer.
The main difference is the popup is always above the selection.
Things to consider:
- popup is always above the selection
- coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
- popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
- we use the toolbarRect to center align toolbar
- use wrapperBounds to clamp values
- editorView.dom bounds differ to wrapperBounds, convert at the end
*/
export var calculateToolbarPositionAboveSelection = function calculateToolbarPositionAboveSelection(toolbarTitle) {
return function (editorView, nextPos) {
var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
if (!toolbar) {
return nextPos;
}
// scroll wrapper for full page, fall back to document body
// TODO: look into using getScrollGutterOptions()
var scrollWrapper = editorView.dom.closest('.fabric-editor-popup-scroll-parent') || document.body;
var wrapperBounds = scrollWrapper.getBoundingClientRect();
var selection = window && window.getSelection();
var range = selection && !selection.isCollapsed && selection.getRangeAt(0);
if (!range) {
return nextPos;
}
var toolbarRect = toolbar.getBoundingClientRect();
var _editorView$state$sel = editorView.state.selection,
head = _editorView$state$sel.head,
anchor = _editorView$state$sel.anchor;
var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
var top = (topCoords.top || 0) - toolbarRect.height * 1.5;
var left = 0;
// If not on the same line
if (topCoords.top !== bottomCoords.top) {
// selecting downwards
if (head > anchor) {
left = Math.max(topCoords.right, bottomCoords.right);
} else {
left = Math.min(topCoords.left, bottomCoords.left);
}
/*
short selection above a long paragraph
eg. short {<}heading
The purpose of this text is to show the selection range{>}.
The horizontal positioning should center around "heading",
not where it ends at "range".
Note: if it was "head<b>ing</b>" then it would only center
around "head". Undesireable but matches the current renderer.
*/
var cliffPosition = range.getClientRects()[0];
if (cliffPosition.right < left) {
left = cliffPosition.left + cliffPosition.width / 2;
}
} else {
// Otherwise center on the single line selection
left = topCoords.left + (bottomCoords.right - topCoords.left) / 2;
}
left -= toolbarRect.width / 2;
// Place toolbar below selection if not sufficient space above
if (top < wrapperBounds.top) {
var _getCoordsBelowSelect = getCoordsBelowSelection(bottomCoords, toolbarRect);
top = _getCoordsBelowSelect.top;
left = _getCoordsBelowSelect.left;
}
// remap positions from browser document to wrapperBounds
return {
top: top - wrapperBounds.top + scrollWrapper.scrollTop,
left: Math.max(0, left - wrapperBounds.left)
};
};
};
/*
Calculates the position of the floating toolbar relative to the selection.
This is a re-implementation which closely matches the behaviour on Confluence renderer.
The main difference is the popup is always above the selection.
Things to consider:
- stick as close to the head X release coordinates as possible
- coordinates of head X and getBoundingClientRect() are absolute in client viewport (not including scroll offsets)
- popup may appear in '.fabric-editor-popup-scroll-parent' (or body)
- we use the toolbarRect to center align toolbar
- use wrapperBounds to clamp values
- editorView.dom bounds differ to wrapperBounds, convert at the end
*/
export var calculateToolbarPositionTrackHead = function calculateToolbarPositionTrackHead(toolbarTitle) {
return function (editorView, nextPos) {
var toolbar = document.querySelector("div[aria-label=\"".concat(toolbarTitle, "\"]"));
if (!toolbar) {
return nextPos;
}
// scroll wrapper for full page, fall back to document body
// TODO: look into using getScrollGutterOptions()
var scrollWrapper = editorView.dom.closest('.fabric-editor-popup-scroll-parent') || document.body;
var wrapperBounds = scrollWrapper.getBoundingClientRect();
var selection = window && window.getSelection();
var range = selection && !selection.isCollapsed && selection.getRangeAt(0);
if (!range) {
return nextPos;
}
var toolbarRect = toolbar.getBoundingClientRect();
var _editorView$state$sel2 = editorView.state.selection,
head = _editorView$state$sel2.head,
anchor = _editorView$state$sel2.anchor;
var topCoords = editorView.coordsAtPos(Math.min(head, anchor));
var bottomCoords = editorView.coordsAtPos(Math.max(head, anchor) - Math.min(range.endOffset, 1));
var top;
// If not the same line, display toolbar below.
if (head > anchor && topCoords.top !== bottomCoords.top) {
// We are taking the previous pos to the maxium, so avoid end of line positions
// returning the next line's rect.
top = (bottomCoords.top || 0) + toolbarRect.height / 1.15;
} else {
top = (topCoords.top || 0) - toolbarRect.height * 1.5;
}
var left = (head > anchor ? bottomCoords.right : topCoords.left) - toolbarRect.width / 2;
// Place toolbar below selection if not sufficient space above
if (top < wrapperBounds.top) {
var _getCoordsBelowSelect2 = getCoordsBelowSelection(bottomCoords, toolbarRect);
top = _getCoordsBelowSelect2.top;
left = _getCoordsBelowSelect2.left;
}
var leftCoord = Math.max(0, left - wrapperBounds.left);
if (fg('platform_editor_selection_toolbar_scroll_fix')) {
if (leftCoord + toolbarRect.width > wrapperBounds.width) {
var scrollbarWidth = 20;
leftCoord = Math.max(0, wrapperBounds.width - (toolbarRect.width + scrollbarWidth));
}
}
// remap positions from browser document to wrapperBounds
return {
top: top - wrapperBounds.top + scrollWrapper.scrollTop,
left: leftCoord
};
};
};
/**
* Returns the coordintes at the bottom the selection.
*/
var getCoordsBelowSelection = function getCoordsBelowSelection(bottomCoords, toolbarRect) {
return {
top: (bottomCoords.top || 0) + toolbarRect.height / 1.15,
left: bottomCoords.right - toolbarRect.width / 2
};
};