Skip to content

Commit

Permalink
Position hover card above or below anchor if right and left don't work (
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-white authored Dec 13, 2024
1 parent cd5c4f9 commit e82c391
Showing 1 changed file with 29 additions and 10 deletions.
39 changes: 29 additions & 10 deletions src/components/hover-cards.vue
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,26 @@ const component = shallowRef(null);
const placement = ref(undefined);

const popoverRef = ref(null);
const positionPopover = () => {
const rightOfAnchor = hoverCard.anchor.getBoundingClientRect().right;
const componentElement = popoverRef.value.$el.children[0];
const componentWidth = componentElement.getBoundingClientRect().width;
// Subtracting 50 in order to provide some buffer.
const overflowsRight = rightOfAnchor + componentWidth >
document.documentElement.clientWidth - 50;
placement.value = overflowsRight ? 'left' : 'right';
// The minimum distance in px between the edge of the hover card and the edge of
// the viewport. This provides space for the popover arrow, as well some extra
// buffer.
const buffer = 20;
const computePlacement = () => {
const anchorRect = hoverCard.anchor.getBoundingClientRect();
const popoverRect = popoverRef.value.$el.getBoundingClientRect();

// Try 'right' and 'left' first unless the anchor is so close to the bottom of
// the page that the popover won't fit. Dividing by 2 because the popover
// would be vertically centered relative to the anchor element.
if (window.scrollY + anchorRect.top + (anchorRect.height / 2) + (popoverRect.height / 2) <
document.body.scrollHeight) {
if (anchorRect.right + popoverRect.width <=
document.documentElement.clientWidth - buffer)
return 'right';
if (anchorRect.left - popoverRect.width >= buffer) return 'left';
}

return anchorRect.top - popoverRect.height >= buffer ? 'top' : 'bottom';
};
const show = () => {
const typeConfig = types[hoverCard.type];
Expand All @@ -108,8 +120,15 @@ const show = () => {
.then(() => {
component.value = typeConfig.component;
// Wait a tick in order to render the component. We need to measure the
// component's width before deciding where to place it.
return nextTick().then(positionPopover);
// size of the component before deciding where to place it.
return nextTick()
.then(computePlacement)
.then(result => { placement.value = result; })
.catch(error => {
// Log if computePlacement() throws an error, as that's unexpected.
console.error(error); // eslint-disable-line no-console
throw error;
});
})
// If some but not all requests succeeded, we want to avoid holding onto the
// resources that were successful.
Expand Down

0 comments on commit e82c391

Please sign in to comment.