import { bringInView } from "./utils"; import { STAGE_PADDING } from "./stage"; export type Side = "top" | "right" | "bottom" | "left"; export type Alignment = "start" | "center" | "end"; const POPOVER_OFFSET = 10; export type Popover = { title?: string; description: string; side?: Side; align?: Alignment; }; type PopoverDOM = { wrapper: HTMLElement; arrow: HTMLElement; title: HTMLElement; description: HTMLElement; footer: HTMLElement; previousButton: HTMLElement; nextButton: HTMLElement; closeButton: HTMLElement; footerButtons: HTMLElement; }; let popover: PopoverDOM | undefined; export function renderPopover(element: Element) { if (!popover) { popover = createPopover(); document.body.appendChild(popover.wrapper); } // Reset the popover position const popoverWrapper = popover.wrapper; popoverWrapper.style.display = "block"; popoverWrapper.style.left = ""; popoverWrapper.style.top = ""; popoverWrapper.style.bottom = ""; popoverWrapper.style.right = ""; // Reset the classes responsible for the arrow position const popoverArrow = popover.arrow; popoverArrow.className = "driver-popover-arrow"; repositionPopover(element); bringInView(popoverWrapper); } function getPopoverDimensions() { if (!popover?.wrapper) { return; } const boundingClientRect = popover.wrapper.getBoundingClientRect(); return { width: boundingClientRect.width + STAGE_PADDING + POPOVER_OFFSET, height: boundingClientRect.height + STAGE_PADDING + POPOVER_OFFSET, realWidth: boundingClientRect.width, realHeight: boundingClientRect.height, }; } export function repositionPopover(element: Element) { if (!popover) { return; } const requiredAlignment: Alignment = "start"; const popoverPadding = STAGE_PADDING; const popoverDimensions = getPopoverDimensions(); const popoverArrowDimensions = popover.arrow.getBoundingClientRect(); const elementDimensions = element.getBoundingClientRect(); const topValue = elementDimensions.top - popoverDimensions!.height; const isTopOptimal = topValue >= 0; const bottomValue = window.innerHeight - (elementDimensions.bottom + popoverDimensions!.height); const isBottomOptimal = bottomValue >= 0; const leftValue = elementDimensions.left - popoverDimensions!.width; const isLeftOptimal = leftValue >= 0; const rightValue = window.innerWidth - (elementDimensions.right + popoverDimensions!.width); const isRightOptimal = rightValue >= 0; const noneOptimal = !isTopOptimal && !isBottomOptimal && !isLeftOptimal && !isRightOptimal; if (noneOptimal) { const leftValue = window.innerWidth / 2 - popoverDimensions?.realWidth! / 2; const bottomValue = 10; popover.wrapper.style.left = `${leftValue}px`; popover.wrapper.style.right = `auto`; popover.wrapper.style.bottom = `${bottomValue}px`; popover.wrapper.style.top = `auto`; popover.arrow.classList.add("driver-popover-arrow-none"); return; } if (isTopOptimal) { const topToSet = Math.min(topValue, window.innerHeight - popoverDimensions.height - popoverArrowDimensions.width); let leftToSet = 0; if (requiredAlignment === "start") { leftToSet = Math.max( Math.min( elementDimensions.left - popoverPadding, window.innerWidth - popoverDimensions.width - popoverArrowDimensions.width ), popoverArrowDimensions.width ); } // popover.arrow.classList.add("driver-popover-arrow-bottom"); popover.wrapper.style.top = `${topToSet}px`; popover.wrapper.style.left = `${leftToSet}px`; popover.wrapper.style.bottom = `auto`; popover.wrapper.style.right = "auto"; } } function createPopover(): PopoverDOM { const wrapper = document.createElement("div"); wrapper.classList.add("driver-popover"); const arrow = document.createElement("div"); arrow.classList.add("driver-popover-arrow"); const title = document.createElement("div"); title.classList.add("driver-popover-title"); title.innerText = "Popover Title"; const description = document.createElement("div"); description.classList.add("driver-popover-description"); description.innerText = "Popover description is here"; const footer = document.createElement("div"); footer.classList.add("driver-popover-footer"); const closeButton = document.createElement("button"); closeButton.classList.add("driver-popover-close-btn"); closeButton.innerText = "Close"; const footerButtons = document.createElement("span"); footerButtons.classList.add("driver-popover-footer-btns"); const previousButton = document.createElement("button"); previousButton.classList.add("driver-popover-prev-btn"); previousButton.innerHTML = "← Previous"; const nextButton = document.createElement("button"); nextButton.classList.add("driver-popover-next-btn"); nextButton.innerHTML = "Next →"; footerButtons.appendChild(previousButton); footerButtons.appendChild(nextButton); footer.appendChild(closeButton); footer.appendChild(footerButtons); wrapper.appendChild(arrow); wrapper.appendChild(title); wrapper.appendChild(description); wrapper.appendChild(footer); return { wrapper, arrow, title, description, footer, previousButton, nextButton, closeButton, footerButtons, }; } export function destroyPopover() { if (!popover) { return; } popover.wrapper.parentElement?.removeChild(popover.wrapper); popover = undefined; }