🐛 Fixed unreachable toolbar when editing wrapped links (#1511)

closes https://github.com/TryGhost/Ghost/issues/9792

- use `getClientRects()` to get separate rectangles for each line of a link and use the mouse position to find the closest one so that the toolbar can be positioned relative to that link section on that line rather than always in the middle of the editor canvas
- pass the rectangle used for positioning the link toolbar through to the link input component so that there is no jumping of position when clicking the edit button
This commit is contained in:
Kevin Ansfield 2020-03-07 18:22:56 +00:00 committed by GitHub
parent 7678c410f5
commit f021badb9f
4 changed files with 29 additions and 13 deletions

View File

@ -581,15 +581,17 @@ export default Component.extend({
// appropriate <a> markup. If there's a selection when the link edit
// component renders it will re-select when finished which should
// trigger the normal toolbar
editLink(range) {
editLink(range, rect) {
let linkMarkup = getLinkMarkupFromRange(range);
if ((!range.isCollapsed || linkMarkup) && range.headSection.isMarkerable) {
this.set('linkRange', range);
this.set('linkRect', rect);
}
},
cancelEditLink() {
this.set('linkRange', null);
this.set('linkRect', null);
},
deleteCard(card, cursorMovement = CURSOR_AFTER) {

View File

@ -38,6 +38,7 @@ export default Component.extend({
// public attrs
editor: null,
linkRange: null,
linkRect: null,
selectedRange: null,
// internal properties
@ -199,7 +200,7 @@ export default Component.extend({
// TODO: largely shared with {{koenig-toolbar}} code - extract to a shared util?
_positionToolbar() {
let containerRect = this.element.offsetParent.getBoundingClientRect();
let rangeRect = this._windowRange.getBoundingClientRect();
let rangeRect = this.linkRect || this._windowRange.getBoundingClientRect();
let {width, height} = this.element.getBoundingClientRect();
let newPosition = {};

View File

@ -109,7 +109,7 @@ export default Component.extend({
edit() {
// get range that covers link
let linkRange = this._getLinkRange();
this.editLink(linkRange);
this.editLink(linkRange, this._targetRect);
},
remove() {
@ -132,9 +132,8 @@ export default Component.extend({
}
let editor = this.editor;
let rect = this._target.getBoundingClientRect();
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
let x = this._targetRect.x + this._targetRect.width / 2;
let y = this._targetRect.y + this._targetRect.height / 2;
let position = editor.positionAtPoint(x, y);
let linkMarkup = position.marker && position.marker.markups.findBy('tagName', 'a');
if (linkMarkup) {
@ -148,7 +147,7 @@ export default Component.extend({
let target = getEventTargetMatchingTag('a', event.target, this.container);
if (target && target.isContentEditable && target.closest('[data-kg-has-link-toolbar=true]') === this.container) {
this._timeout = run.later(this, function () {
this._showToolbar(target);
this._showToolbar(target, {x: event.clientX, y: event.clientY});
}, DELAY);
}
}
@ -165,7 +164,7 @@ export default Component.extend({
}
},
_showToolbar(target) {
_showToolbar(target, mousePos) {
// extract the href attribute value and convert it to absolute based
// on the configured blog url
this._target = target;
@ -174,7 +173,7 @@ export default Component.extend({
this.set('url', relativeToAbsolute(href, blogUrl));
this.set('showToolbar', true);
run.schedule('afterRender', this, function () {
this._positionToolbar(target);
this._positionToolbar(target, mousePos);
});
},
@ -185,17 +184,30 @@ export default Component.extend({
}
},
_positionToolbar(target) {
_positionToolbar(target, {x, y}) {
let containerRect = this.element.offsetParent.getBoundingClientRect();
let targetRect = target.getBoundingClientRect();
// wrapped links can have multiple rects, find one closest to the pointer
// if we have a pointer position
if (x && y) {
let rects = Array.prototype.slice.call(target.getClientRects());
this._targetRect = rects.find((rect) => {
return rect.x - TOOLBAR_MARGIN <= x && x <= rect.x + rect.width + TOOLBAR_MARGIN &&
rect.y - TOOLBAR_MARGIN <= y && y <= rect.y + rect.height + TOOLBAR_MARGIN;
});
}
if (!this._targetRect) {
this._targetRect = target.getBoundingClientRect();
}
let {width, height} = this.element.getBoundingClientRect();
let newPosition = {};
// targetRect is relative to the viewport so we need to subtract the
// container measurements to get a position relative to the container
newPosition = {
top: targetRect.top - containerRect.top - height - TOOLBAR_MARGIN + TOOLBAR_PADDING,
left: targetRect.left - containerRect.left + targetRect.width / 2 - width / 2,
top: this._targetRect.top - containerRect.top - height - TOOLBAR_MARGIN + TOOLBAR_PADDING,
left: this._targetRect.left - containerRect.left + this._targetRect.width / 2 - width / 2,
right: null
};

View File

@ -28,6 +28,7 @@
<KoenigLinkInput
@editor={{this.editor}}
@linkRange={{this.linkRange}}
@linkRect={{this.linkRect}}
@selectedRange={{this.selectedRange}}
@cancel={{action "cancelEditLink"}}
/>