geist-ui-svelte
Version:
<img src="./src/lib/assets/demo-page.png">
186 lines (185 loc) • 8.68 kB
JavaScript
export const place = (anchor, element, { placement = "bottom-start", flip = true, offset = { x: 0, y: 0 } }) => {
const options = { placement, flip, offset };
switch (placement) {
case "bottom":
return placeBottom(anchor, element, options);
case "bottom-end":
return placeBottomEnd(anchor, element, options);
case "bottom-start":
return placeBottomStart(anchor, element, options);
case "top":
return placeTop(anchor, element, options);
case "top-end":
return placeTopEnd(anchor, element, options);
case "top-start":
return placeTopStart(anchor, element, options);
case "right":
return placeRight(anchor, element, options);
case "right-end":
return placeRightEnd(anchor, element, options);
case "right-start":
return placeRightStart(anchor, element, options);
case "left":
return placeLeft(anchor, element, options);
case "left-end":
return placeLeftEnd(anchor, element, options);
case "left-start":
return placeLeftStart(anchor, element, options);
}
};
const getMaxes = (element) => {
const maxLeft = window.innerWidth + window.scrollX - (element.offsetWidth + 5);
const maxTop = window.innerHeight + window.scrollY - (element.offsetHeight + 80);
return { left: maxLeft, top: maxTop };
};
const placeBottom = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + anchor.offsetHeight + options.offset.y;
if (top > maxes.top && options.flip && guard == 0)
return placeTop(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth / 2 - element.offsetWidth / 2 + options.offset.x;
if (left > maxes.left)
return placeBottomEnd(anchor, element, options);
if (left < 0)
return placeBottomStart(anchor, element, options);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "bottom";
};
const placeBottomEnd = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + anchor.offsetHeight + options.offset.y;
if (top > maxes.top && options.flip && guard == 0)
return placeTopEnd(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth - element.offsetWidth + options.offset.x;
if (left < 0 && options.flip && guard <= 1)
return placeBottomStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "bottom-end";
};
const placeBottomStart = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + anchor.offsetHeight + options.offset.y;
if (top > maxes.top && options.flip && guard == 0)
return placeTopStart(anchor, element, options, 1);
const left = anchor.offsetLeft + options.offset.x;
if (left > maxes.left && options.flip && guard <= 1)
return placeBottomEnd(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "bottom-start";
};
const placeTop = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop - element.offsetHeight - options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeBottom(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth / 2 - element.offsetWidth / 2 + options.offset.x;
if (left > maxes.left)
return placeTopEnd(anchor, element, options);
if (left < 0)
return placeTopStart(anchor, element, options);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "top";
};
const placeTopEnd = (anchor, element, options, guard = 0) => {
const top = anchor.offsetTop - element.offsetHeight - options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeBottomEnd(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth - element.offsetWidth + options.offset.x;
if (left < 0 && options.flip && guard <= 1)
return placeTopStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "top-end";
};
const placeTopStart = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop - element.offsetHeight - options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeBottomStart(anchor, element, options, 1);
const left = anchor.offsetLeft + options.offset.x;
if (left > maxes.left && options.flip && guard <= 1)
return placeTopEnd(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "top-start";
};
const placeRight = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + (anchor.offsetHeight - element.offsetHeight) / 2 + options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeRightStart(anchor, element, options, 1);
if (top > maxes.top && options.flip && guard == 0)
return placeRightEnd(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth + options.offset.x;
if (left > maxes.left && guard <= 1)
return placeLeft(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "right";
};
const placeRightEnd = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + anchor.offsetHeight - element.offsetHeight + options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeRightStart(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth + options.offset.x;
if (left > maxes.left && guard <= 1)
return placeLeftStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "right-end";
};
const placeRightStart = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + options.offset.y;
if (top > maxes.top && options.flip && guard == 0)
return placeRightEnd(anchor, element, options, 1);
const left = anchor.offsetLeft + anchor.offsetWidth + options.offset.x;
if (left > maxes.left && options.flip && guard <= 1)
return placeLeftStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "right-start";
};
const placeLeft = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + (anchor.offsetHeight - element.offsetHeight) / 2 + options.offset.y;
if (top < 0 && options.flip && guard == 0)
return placeLeftStart(anchor, element, options, 1);
if (top > maxes.top && options.flip && guard == 0)
return placeLeftEnd(anchor, element, options, 1);
const left = anchor.offsetLeft - element.offsetWidth - options.offset.x;
if (left > maxes.left && guard <= 1)
return placeRight(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "left";
};
const placeLeftEnd = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + anchor.offsetHeight - element.offsetHeight;
if (top < 0 && options.flip && guard == 0)
return placeLeftStart(anchor, element, options, 1);
const left = anchor.offsetLeft - element.offsetWidth - options.offset.x;
if (left > maxes.left && guard <= 1)
return placeRightStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "left-end";
};
const placeLeftStart = (anchor, element, options, guard = 0) => {
const maxes = getMaxes(element);
const top = anchor.offsetTop + options.offset.y;
if (top > maxes.top && options.flip && guard == 0)
return placeLeftEnd(anchor, element, options, 1);
const left = anchor.offsetLeft - element.offsetWidth - options.offset.x;
if (left > maxes.left && options.flip && guard <= 1)
return placeRightStart(anchor, element, options, 2);
element.style.left = `${left}px`;
element.style.top = `${top}px`;
return "left-start";
};