Skip to content

Commit 27adbc2

Browse files
fix preview popover positioning for links with line breaks
1 parent 8d2be11 commit 27adbc2

1 file changed

Lines changed: 68 additions & 6 deletions

File tree

resources/js/init.js

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -485,20 +485,82 @@ $.fn.preview_popover = function() {
485485
* Create initial popover for notes and previews
486486
* with template from page.html#carousel-popover for the content
487487
*/
488-
$('.preview, .noteMarker').on('click', function() {
489-
$(this).popover({
488+
function getClickedClientRect(element, event) {
489+
const originalEvent = event.originalEvent || event,
490+
pointer = originalEvent.changedTouches ? originalEvent.changedTouches[0] : originalEvent,
491+
clientX = pointer.clientX,
492+
clientY = pointer.clientY,
493+
rects = Array.from(element.getClientRects());
494+
let closestRect,
495+
closestDistance = Number.POSITIVE_INFINITY;
496+
497+
if(undefined === clientX || undefined === clientY || rects.length < 2) { return }
498+
499+
for(const rect of rects) {
500+
if(clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom) {
501+
return rect;
502+
}
503+
504+
const distanceX = Math.max(rect.left - clientX, 0, clientX - rect.right),
505+
distanceY = Math.max(rect.top - clientY, 0, clientY - rect.bottom),
506+
distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
507+
if(distance < closestDistance) {
508+
closestRect = rect;
509+
closestDistance = distance;
510+
}
511+
}
512+
513+
return closestRect;
514+
}
515+
516+
function createPopoverAnchor(source, event) {
517+
const rect = source.classList.contains('preview') ? getClickedClientRect(source, event) : undefined;
518+
519+
if(undefined === rect) { return source }
520+
521+
const anchor = document.createElement('span');
522+
523+
Array.from(source.attributes).forEach(function(attribute) {
524+
if(attribute.name !== 'id') {
525+
anchor.setAttribute(attribute.name, attribute.value);
526+
}
527+
});
528+
anchor.classList.add('preview-popover-anchor');
529+
anchor.style.position = 'absolute';
530+
anchor.style.left = `${rect.left + window.pageXOffset}px`;
531+
anchor.style.top = `${rect.top + window.pageYOffset}px`;
532+
anchor.style.width = `${Math.max(rect.width, 1)}px`;
533+
anchor.style.height = `${Math.max(rect.height, 1)}px`;
534+
anchor.style.pointerEvents = 'none';
535+
anchor.style.opacity = '0';
536+
537+
document.body.appendChild(anchor);
538+
539+
return anchor;
540+
}
541+
542+
$('.preview, .noteMarker').on('click', function(event) {
543+
const popoverTrigger = createPopoverAnchor(this, event);
544+
545+
$(popoverTrigger).one('hidden.bs.popover', function() {
546+
if(popoverTrigger !== event.currentTarget) {
547+
popoverTrigger.remove();
548+
}
549+
});
550+
551+
$(popoverTrigger).popover({
490552
"html": true,
491553
"trigger": "manual",
492554
"container": 'body',
493555
'placement': 'auto',
494556
"title": "Loading …", // This is just a dummy title otherwise the content function will be called twice, see https://github.com/twbs/bootstrap/issues/12563
495557
"content": popover_template
496558
});
497-
$(this).popover('show');
498-
559+
$(popoverTrigger).popover('show');
560+
499561
/* Need to call this after popover('show') to get access to the popover options in a later step (in preview_popover) */
500-
popover_callBack.call($(this));
501-
562+
popover_callBack.call($(popoverTrigger));
563+
502564
/* Return false to suppress the default link mechanism on html:a */
503565
return false;
504566
});

0 commit comments

Comments
 (0)