diff --git a/internal/assets/static/js/popover.js b/internal/assets/static/js/popover.js index d431502..7f11ed6 100644 --- a/internal/assets/static/js/popover.js +++ b/internal/assets/static/js/popover.js @@ -36,7 +36,7 @@ function handleMouseEnter(event) { if (activeTarget !== null) { if (activeTarget !== target) { hidePopover(); - setTimeout(showPopover, 5); + requestAnimationFrame(showPopover); } return; @@ -96,28 +96,45 @@ function showPopover() { } function repositionContainer() { - const activeTargetBounds = activeTarget.getBoundingClientRect(); + const targetBounds = activeTarget.getBoundingClientRect(); const containerBounds = containerElement.getBoundingClientRect(); const containerInlinePadding = parseInt(containerComputedStyle.getPropertyValue("padding-inline")); - const activeTargetBoundsWidthOffset = activeTargetBounds.width * (activeTarget.dataset.popoverOffset || 0.5); - const left = activeTargetBounds.left + activeTargetBoundsWidthOffset - (containerBounds.width / 2); + const activeTargetBoundsWidthOffset = targetBounds.width * (activeTarget.dataset.popoverOffset || 0.5); + const position = activeTarget.dataset.popoverPosition || "below"; + const left = targetBounds.left + activeTargetBoundsWidthOffset - (containerBounds.width / 2) + 1; if (left < 0) { containerElement.style.left = 0; containerElement.style.removeProperty("right"); - containerElement.style.setProperty("--triangle-offset", activeTargetBounds.left - containerInlinePadding + activeTargetBoundsWidthOffset + "px"); + containerElement.style.setProperty("--triangle-offset", targetBounds.left - containerInlinePadding + activeTargetBoundsWidthOffset + "px"); } else if (left + containerBounds.width > window.innerWidth) { containerElement.style.removeProperty("left"); containerElement.style.right = 0; - containerElement.style.setProperty("--triangle-offset", containerBounds.width - containerInlinePadding - (window.innerWidth - activeTargetBounds.left - activeTargetBoundsWidthOffset) + "px"); + containerElement.style.setProperty("--triangle-offset", containerBounds.width - containerInlinePadding - (window.innerWidth - targetBounds.left - activeTargetBoundsWidthOffset) + "px"); } else { containerElement.style.removeProperty("right"); containerElement.style.left = left + "px"; containerElement.style.removeProperty("--triangle-offset"); } - frameElement.style.marginTop = activeTarget.dataset.popoverMargin || defaultDistanceFromTarget; - containerElement.style.top = activeTargetBounds.top + window.scrollY + activeTargetBounds.height + "px"; + const distanceFromTarget = activeTarget.dataset.popoverMargin || defaultDistanceFromTarget; + const topWhenAbove = targetBounds.top + window.scrollY - containerBounds.height; + const topWhenBelow = targetBounds.top + window.scrollY + targetBounds.height; + + if ( + position === "above" && topWhenAbove > window.scrollY || + (position === "below" && topWhenBelow + containerBounds.height > window.scrollY + window.innerHeight) + ) { + containerElement.classList.add("position-above"); + frameElement.style.removeProperty("margin-top"); + frameElement.style.marginBottom = distanceFromTarget; + containerElement.style.top = topWhenAbove + "px"; + } else { + containerElement.classList.remove("position-above"); + frameElement.style.removeProperty("margin-bottom"); + frameElement.style.marginTop = distanceFromTarget; + containerElement.style.top = topWhenBelow + "px"; + } } function hidePopover() { diff --git a/internal/assets/static/main.css b/internal/assets/static/main.css index 423a9d6..3fb9c6b 100644 --- a/internal/assets/static/main.css +++ b/internal/assets/static/main.css @@ -34,8 +34,8 @@ --color-separator: hsl(var(--bghs), calc(var(--scheme) ((var(--scheme) var(--bgl)) + 4% * var(--cm)))); --color-widget-content-border: hsl(var(--bghs), calc(var(--scheme) (var(--scheme) var(--bgl) + 4%))); --color-widget-background-highlight: hsl(var(--bghs), calc(var(--scheme) (var(--scheme) var(--bgl) + 4%))); - --color-popover-background: hsl(var(--bgh), calc(var(--bgs) + 3%), calc(var(--bgl) + 2%)); - --color-popover-border: hsl(var(--bghs), calc(var(--scheme) (var(--scheme) var(--bgl) + 7%))); + --color-popover-background: hsl(var(--bgh), calc(var(--bgs) + 3%), calc(var(--bgl) + 3%)); + --color-popover-border: hsl(var(--bghs), calc(var(--scheme) (var(--scheme) var(--bgl) + 10%))); --ths: var(--bgh), calc(var(--bgs) * var(--tsm)); --color-text-base: hsl(var(--ths), calc(var(--scheme) var(--cm) * 58%)); @@ -439,20 +439,32 @@ kbd:active { .popover-container { --triangle-size: 10px; --triangle-offset: 50%; + --triangle-margin: calc(var(--triangle-size) + 3px); + --entrance-y-offset: 8px; + --entrance-direction: calc(var(--entrance-y-offset) * -1); + z-index: 20; position: absolute; - padding-top: calc(var(--triangle-size) + 3px); + padding-top: var(--triangle-margin); padding-inline: var(--content-bounds-padding); } +.popover-container.position-above { + --entrance-direction: var(--entrance-y-offset); + padding-top: 0; + padding-bottom: var(--triangle-margin); +} + .popover-frame { + --shadow-properties: 0 15px 20px -10px; + --shadow-color: hsla(var(--bghs), calc(var(--bgl) * 0.2), 0.5); position: relative; padding: 10px; background: var(--color-popover-background); border: 1px solid var(--color-popover-border); border-radius: 5px; animation: popoverFrameEntrance 0.3s backwards cubic-bezier(0.16, 1, 0.3, 1); - box-shadow: 0 15px 30px -5px hsla(var(--bghs), calc(var(--bgl) * 0.2), 0.5); + box-shadow: var(--shadow-properties) var(--shadow-color); } .popover-frame::before { @@ -462,17 +474,27 @@ kbd:active { height: var(--triangle-size); transform: rotate(45deg); background-color: var(--color-popover-background); - border-top-left-radius: 4px; + border-top-left-radius: 2px; border-left: 1px solid var(--color-popover-border); border-top: 1px solid var(--color-popover-border); left: calc(var(--triangle-offset) - (var(--triangle-size) / 2)); top: calc(var(--triangle-size) / 2 * -1 - 1px); } +.popover-container.position-above .popover-frame::before { + transform: rotate(-135deg); + top: auto; + bottom: calc(var(--triangle-size) / 2 * -1 - 1px); +} + +.popover-container.position-above .popover-frame { + --shadow-properties: 0 10px 20px -10px; +} + @keyframes popoverFrameEntrance { from { opacity: 0; - transform: translateY(-8px); + transform: translateY(var(--entrance-direction)); } }