@csedl/stimulus-dropdown
Version:
Dropdown and Tooltip with stimulus and floating-ui
99 lines (81 loc) • 4.03 kB
JavaScript
import {Controller} from "@hotwired/stimulus"
import {positionAllPanels, getAllOpenPanels} from "./floating-ui-functions";
import {debugLog} from "./utils.js";
export default class extends Controller {
elements_to_place = null
connect() {
const on = this.element.getAttribute('data-on')
if (on.includes('scroll')) {
this.element.addEventListener('scroll', () => {
this.handle_placement('scroll')
})
}
if (on.includes('resize-observer')) {
var resize_observer = new ResizeObserver(entries => {
for (let entry of entries) {
this.handle_placement('resize-observer')
}
});
resize_observer.observe(this.element)
}
}
handle_placement(trigger_label) {
if (this.elements_to_place === null) {
this.elements_to_place = getAllOpenPanels(this.element)
this.element.setAttribute('csedl-place-all-first-done-time', performance.now())
}
if (this.elements_to_place.length === 0) {
this.cleanup()
} else {
const c = Number(this.element.getAttribute('csedl-place-all-counter'))
this.element.setAttribute('csedl-place-all-counter', c + 1)
const first_run = Number(this.element.getAttribute('csedl-place-all-first-done-time'))
debugLog(`place panels by ${trigger_label} ${c} / ${performance.now() - first_run}ms: x: ${this.element.offsetWidth} y: ${this.element.offsetHeight}`)
positionAllPanels(this.elements_to_place)
this.element.setAttribute('csedl-place-all-done-time', performance.now())
this.setup_run_after_event()
}
}
setup_run_after_event() {
if (this.element.hasAttribute('data-run-after')) {
const run_after_str = this.element.getAttribute('data-run-after')
const run_after = Number(run_after_str)
if (isNaN(run_after)) {
console.error(`the data-run-after attribute must be a valid positive number (integer for milliseconds) but is: «${run_after_str}»`, this.element)
} else if (run_after < 0.0) {
console.error(`the data-run-after attribute must be a positive number (integer for milliseconds) but is: «${run_after_str}»`, this.element)
} else {
if (this.element.getAttribute('csedl-run-after-is-active')) {
debugLog('place-panel run-after is already installed (nothing to do)')
} else {
this.element.setAttribute('csedl-run-after-is-active', true)
this.element.setAttribute('csedl-place-all-counter', 0)
setTimeout(() => this.handle_placement_after(), 50);
debugLog('place-panel run-after INSTALLED')
}
}
}
}
handle_placement_after() {
const run_after = Number(this.element.getAttribute('data-run-after'))
const last_run = Number(this.element.getAttribute('csedl-place-all-done-time'))
const c = Number(this.element.getAttribute('csedl-place-all-counter'))
this.element.setAttribute('csedl-place-all-counter', c + 1)
const running_time = performance.now() - last_run
positionAllPanels(this.elements_to_place)
debugLog(`PLACEMENT FOLLOW-UP ${c}: ${running_time}ms / ${run_after}ms`)
if (running_time < run_after) {
setTimeout(() => this.handle_placement_after(), 50);
} else {
this.cleanup()
}
}
cleanup() {
this.element.removeAttribute('csedl-place-all-done-time')
this.element.removeAttribute('csedl-place-all-first-done-time')
this.element.removeAttribute('csedl-place-all-counter')
this.element.removeAttribute('csedl-run-after-is-active')
this.elements_to_place = null
debugLog('FINISHED placement follow-up')
}
}