File size: 6,059 Bytes
b8874fd f86a242 b8874fd 9d9dd89 f86a242 8480986 9d9dd89 8480986 9d9dd89 d6b7277 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd f86a242 9d9dd89 f86a242 9d9dd89 b8874fd f86a242 9d9dd89 b8874fd f86a242 9d9dd89 f86a242 9d9dd89 f86a242 8480986 f86a242 68ab4bd f86a242 68ab4bd f86a242 68ab4bd f86a242 68ab4bd f86a242 68ab4bd f86a242 68ab4bd f86a242 68ab4bd 8e19ba1 f86a242 8e19ba1 f86a242 8e19ba1 f86a242 353f356 f86a242 8e19ba1 f86a242 283e17d f86a242 283e17d f86a242 353f356 f86a242 353f356 68ab4bd 9d9dd89 b8874fd d6b7277 b8874fd 9d9dd89 b8874fd 9d9dd89 f86a242 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd d6b7277 9d9dd89 b8874fd 9d9dd89 d6b7277 9d9dd89 b8874fd 9d9dd89 b8874fd 9d9dd89 b8874fd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
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");
} else 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!.realWidth - popoverArrowDimensions.width
),
popoverArrowDimensions.width
);
} else if (requiredAlignment === "end") {
leftToSet = Math.max(
Math.min(
elementDimensions.left - popoverDimensions?.realWidth + elementDimensions.width + popoverPadding,
window.innerWidth - popoverDimensions?.realWidth - popoverArrowDimensions.width
),
popoverArrowDimensions.width
);
}
popover.wrapper.style.top = `${topToSet}px`;
popover.wrapper.style.left = `${leftToSet}px`;
popover.wrapper.style.bottom = `auto`;
popover.wrapper.style.right = "auto";
} else if (isBottomOptimal) {
// @todo - handle bottom rendering
}
// We need to check which position we end up rendering the popover at
// to be able to assign the proper arrow class
renderPopoverArrow();
}
function renderPopoverArrow() {
// @todo
}
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;
}
|