Spaces:
Configuration error
Configuration error
let activeDropdown = null; | |
export function removeDropdown() { | |
if (activeDropdown) { | |
activeDropdown.removeEventListeners(); | |
activeDropdown.dropdown.remove(); | |
activeDropdown = null; | |
} | |
} | |
export function createDropdown(inputEl, suggestions, onSelect, isDict = false) { | |
removeDropdown(); | |
new Dropdown(inputEl, suggestions, onSelect, isDict); | |
} | |
class Dropdown { | |
constructor(inputEl, suggestions, onSelect, isDict = false) { | |
this.dropdown = document.createElement('ul'); | |
this.dropdown.setAttribute('role', 'listbox'); | |
this.dropdown.classList.add('easy-dropdown'); | |
this.selectedIndex = -1; | |
this.inputEl = inputEl; | |
this.suggestions = suggestions; | |
this.onSelect = onSelect; | |
this.isDict = isDict; | |
this.focusedDropdown = this.dropdown; | |
this.buildDropdown(); | |
this.onKeyDownBound = this.onKeyDown.bind(this); | |
this.onWheelBound = this.onWheel.bind(this); | |
this.onClickBound = this.onClick.bind(this); | |
this.addEventListeners(); | |
} | |
buildDropdown() { | |
if (this.isDict) { | |
this.buildNestedDropdown(this.suggestions, this.dropdown); | |
} else { | |
this.suggestions.forEach((suggestion, index) => { | |
this.addListItem(suggestion, index, this.dropdown); | |
}); | |
} | |
const inputRect = this.inputEl.getBoundingClientRect(); | |
this.dropdown.style.top = (inputRect.top + inputRect.height - 10) + 'px'; | |
this.dropdown.style.left = inputRect.left + 'px'; | |
document.body.appendChild(this.dropdown); | |
activeDropdown = this; | |
} | |
buildNestedDropdown(dictionary, parentElement) { | |
let index = 0; | |
Object.keys(dictionary).forEach((key) => { | |
const item = dictionary[key]; | |
if (typeof item === "object" && item !== null) { | |
const nestedDropdown = document.createElement('ul'); | |
nestedDropdown.setAttribute('role', 'listbox'); | |
nestedDropdown.classList.add('easy-nested-dropdown'); | |
const parentListItem = document.createElement('li'); | |
parentListItem.classList.add('folder'); | |
parentListItem.textContent = key; | |
parentListItem.appendChild(nestedDropdown); | |
parentListItem.addEventListener('mouseover', this.onMouseOver.bind(this, index, parentElement)); | |
parentElement.appendChild(parentListItem); | |
this.buildNestedDropdown(item, nestedDropdown); | |
index = index + 1; | |
} else { | |
const listItem = document.createElement('li'); | |
listItem.classList.add('item'); | |
listItem.setAttribute('role', 'option'); | |
listItem.textContent = key; | |
listItem.addEventListener('mouseover', this.onMouseOver.bind(this, index, parentElement)); | |
listItem.addEventListener('mousedown', this.onMouseDown.bind(this, key)); | |
parentElement.appendChild(listItem); | |
index = index + 1; | |
} | |
}); | |
} | |
addListItem(item, index, parentElement) { | |
const listItem = document.createElement('li'); | |
listItem.setAttribute('role', 'option'); | |
listItem.textContent = item; | |
listItem.addEventListener('mouseover', this.onMouseOver.bind(this, index)); | |
listItem.addEventListener('mousedown', this.onMouseDown.bind(this, item)); | |
parentElement.appendChild(listItem); | |
} | |
addEventListeners() { | |
document.addEventListener('keydown', this.onKeyDownBound); | |
this.dropdown.addEventListener('wheel', this.onWheelBound); | |
document.addEventListener('click', this.onClickBound); | |
} | |
removeEventListeners() { | |
document.removeEventListener('keydown', this.onKeyDownBound); | |
this.dropdown.removeEventListener('wheel', this.onWheelBound); | |
document.removeEventListener('click', this.onClickBound); | |
} | |
onMouseOver(index, parentElement) { | |
if (parentElement) { | |
this.focusedDropdown = parentElement; | |
} | |
this.selectedIndex = index; | |
this.updateSelection(); | |
} | |
onMouseOut() { | |
this.selectedIndex = -1; | |
this.updateSelection(); | |
} | |
onMouseDown(suggestion, event) { | |
event.preventDefault(); | |
this.onSelect(suggestion); | |
this.dropdown.remove(); | |
this.removeEventListeners(); | |
} | |
onKeyDown(event) { | |
const enterKeyCode = 13; | |
const escKeyCode = 27; | |
const arrowUpKeyCode = 38; | |
const arrowDownKeyCode = 40; | |
const arrowRightKeyCode = 39; | |
const arrowLeftKeyCode = 37; | |
const tabKeyCode = 9; | |
const items = Array.from(this.focusedDropdown.children); | |
const selectedItem = items[this.selectedIndex]; | |
if (activeDropdown) { | |
if (event.keyCode === arrowUpKeyCode) { | |
event.preventDefault(); | |
this.selectedIndex = Math.max(0, this.selectedIndex - 1); | |
this.updateSelection(); | |
} | |
else if (event.keyCode === arrowDownKeyCode) { | |
event.preventDefault(); | |
this.selectedIndex = Math.min(items.length - 1, this.selectedIndex + 1); | |
this.updateSelection(); | |
} | |
else if (event.keyCode === arrowRightKeyCode) { | |
event.preventDefault(); | |
if (selectedItem && selectedItem.classList.contains('folder')) { | |
const nestedDropdown = selectedItem.querySelector('.easy-nested-dropdown'); | |
if (nestedDropdown) { | |
this.focusedDropdown = nestedDropdown; | |
this.selectedIndex = 0; | |
this.updateSelection(); | |
} | |
} | |
} | |
else if (event.keyCode === arrowLeftKeyCode && this.focusedDropdown !== this.dropdown) { | |
const parentDropdown = this.focusedDropdown.closest('.easy-dropdown, .easy-nested-dropdown').parentNode.closest('.easy-dropdown, .easy-nested-dropdown'); | |
if (parentDropdown) { | |
this.focusedDropdown = parentDropdown; | |
this.selectedIndex = Array.from(parentDropdown.children).indexOf(this.focusedDropdown.parentNode); | |
this.updateSelection(); | |
} | |
} | |
else if ((event.keyCode === enterKeyCode || event.keyCode === tabKeyCode) && this.selectedIndex >= 0) { | |
event.preventDefault(); | |
if (selectedItem.classList.contains('item')) { | |
this.onSelect(items[this.selectedIndex].textContent); | |
this.dropdown.remove(); | |
this.removeEventListeners(); | |
} | |
const nestedDropdown = selectedItem.querySelector('.easy-nested-dropdown'); | |
if (nestedDropdown) { | |
this.focusedDropdown = nestedDropdown; | |
this.selectedIndex = 0; | |
this.updateSelection(); | |
} | |
} | |
else if (event.keyCode === escKeyCode) { | |
this.dropdown.remove(); | |
this.removeEventListeners(); | |
} | |
} | |
} | |
onWheel(event) { | |
const top = parseInt(this.dropdown.style.top); | |
if (localStorage.getItem("Comfy.Settings.Comfy.InvertMenuScrolling")) { | |
this.dropdown.style.top = (top + (event.deltaY < 0 ? 10 : -10)) + "px"; | |
} else { | |
this.dropdown.style.top = (top + (event.deltaY < 0 ? -10 : 10)) + "px"; | |
} | |
} | |
onClick(event) { | |
if (!this.dropdown.contains(event.target) && event.target !== this.inputEl) { | |
this.dropdown.remove(); | |
this.removeEventListeners(); | |
} | |
} | |
updateSelection() { | |
Array.from(this.focusedDropdown.children).forEach((li, index) => { | |
if (index === this.selectedIndex) { | |
li.classList.add('selected'); | |
} else { | |
li.classList.remove('selected'); | |
} | |
}); | |
} | |
} |