1
0
Fork 0
mirror of synced 2024-09-08 21:51:58 +12:00

Improve dropdown logic

This commit is contained in:
Andrew Kingston 2023-01-12 14:42:05 +00:00
parent 1ce4c8a569
commit 020cddc76e

View file

@ -2,86 +2,68 @@ export default function positionDropdown(
element,
{ anchor, align, maxWidth, useAnchorWidth }
) {
let positionSide = "top"
let maxHeight = 0
let minWidth = 0
let dimensions = getDimensions(anchor)
function getDimensions() {
const {
bottom,
top: spaceAbove,
left,
width,
} = anchor.getBoundingClientRect()
const spaceBelow = window.innerHeight - bottom
const containerRect = element.getBoundingClientRect()
let y
if (window.innerHeight - bottom < 100) {
positionSide = "bottom"
maxHeight = spaceAbove - 20
y = window.innerHeight - spaceAbove + 5
} else {
positionSide = "top"
y = bottom + 5
maxHeight = spaceBelow - 20
const update = () => {
console.log("update")
const anchorBounds = anchor.getBoundingClientRect()
const elementBounds = element.getBoundingClientRect()
let styles = {
maxHeight: null,
minWidth: null,
maxWidth,
left: null,
top: null,
}
// Determine vertical styles
if (window.innerHeight - anchorBounds.bottom < 100) {
styles.top = anchorBounds.top - elementBounds.height - 5
} else {
styles.top = anchorBounds.bottom + 5
styles.maxHeight = window.innerHeight - anchorBounds.bottom - 20
}
// Determine horizontal styles
if (!maxWidth && useAnchorWidth) {
maxWidth = width
styles.maxWidth = anchorBounds.width
}
if (useAnchorWidth) {
minWidth = width
styles.minWidth = anchorBounds.width
}
return {
[positionSide]: y,
left,
width,
containerWidth: containerRect.width,
}
}
function calcLeftPosition() {
let left
if (align === "right") {
left = dimensions.left + dimensions.width - dimensions.containerWidth
styles.left = anchorBounds.left + anchorBounds.width - elementBounds.width
} else if (align === "right-side") {
left = dimensions.left + dimensions.width
styles.left = anchorBounds.left + anchorBounds.width
} else {
left = dimensions.left
styles.left = anchorBounds.left
}
return left
// Apply styles
Object.entries(styles).forEach(([style, value]) => {
if (value) {
element.style[style] = `${value.toFixed(0)}px`
} else {
element.style[style] = null
}
})
}
// Apply initial styles which don't need to change
element.style.position = "absolute"
element.style.zIndex = "9999"
if (maxWidth) {
element.style.maxWidth = `${maxWidth}px`
}
if (minWidth) {
element.style.minWidth = `${minWidth}px`
}
element.style.maxHeight = `${maxHeight.toFixed(0)}px`
element.style.transformOrigin = `center ${positionSide}`
element.style[positionSide] = `${dimensions[positionSide]}px`
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
// Observe both anchor and element and resize the popover as appropriate
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(() => {
dimensions = getDimensions()
element.style[positionSide] = `${dimensions[positionSide]}px`
element.style.left = `${calcLeftPosition(dimensions).toFixed(0)}px`
})
entries.forEach(update)
})
resizeObserver.observe(anchor)
resizeObserver.observe(element)
document.addEventListener("scroll", update, true)
return {
destroy() {
resizeObserver.disconnect()
document.removeEventListener("scroll", update, true)
},
}
}