Files
once-campfire/app/javascript/lib/autocomplete/helpers.js
Kevin McConnell df76a227dc Hello world
First open source release of Campfire 🎉
2025-08-21 09:31:59 +01:00

104 lines
2.7 KiB
JavaScript

export function generateUUID() {
const template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
return template.replace(/[xy]/g, function(char) {
const rand = (Math.random() * 16) | 0
const value = char === "x" ? rand : ((rand & 0x3)|0x8)
return value.toString(16)
})
}
export function synchronize(fn) {
const monitorCallbacks = new WeakMap
return function(callback) {
let callbacks = monitorCallbacks.get(this)
if (!callbacks) {
monitorCallbacks.set(this, (callbacks = []))
}
callbacks.push(callback)
if (callbacks.length === 1) {
return fn.call(this, () => {
Array.from(callbacks).forEach((callback) => { callback?.() })
return monitorCallbacks.delete(this)
})
}
}
}
export function transitionElementWithClass(element, className, callback) {
return applyClassAwaitingEvent(element, className, "transitionend", callback)
}
const applyClassAwaitingEvent = function(element, className, eventName, callback) {
let timeout
let uninstalled = false
const uninstall = function() {
if (!uninstalled) {
uninstalled = true
element.removeEventListener(eventName, uninstall)
return requestAnimationFrame(function() {
element.classList.remove(className)
return callback?.()
})
}
}
element.addEventListener(eventName, uninstall)
element.classList.add(className)
// Failsafe: If we don't receive a {transition,animation}end event
// for some reason, ensure that uninstall is still called.
const duration = getDuration(element, eventName)
if (duration) {
timeout = duration + 50
} else {
timeout = 50
}
return setTimeout(uninstall, timeout)
}
const getDuration = function(element, eventName) {
const type = eventName === "animationend" ? "animation" : "transition"
const duration = getComputedStyle(element)[`${type}Duration`]
if (duration) {
if (/ms/.test(duration)) {
return parseInt(duration, 10)
} else {
return parseFloat(duration) * 1000
}
}
}
export function getElementMargin(element) {
const result = {}
const style = window.getComputedStyle(element);
["Top", "Right", "Bottom", "Left"].forEach((side) => {
result[side.toLowerCase()] = parseInt(style[`margin${side}`], 10)
})
return result
}
export function getAbsolutePositionForOffsets({ top, right, bottom, left }) {
return {
top: top + window.scrollY,
right: right + window.scrollX,
bottom: bottom + window.scrollY,
left: left + window.scrollX
}
}
export function getViewportRect() {
return {
top: window.scrollY,
right: window.scrollX + window.innerWidth,
bottom: window.scrollY + window.innerHeight,
left: window.scrollX
}
}