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'); | |
| } | |
| }); | |
| } | |
| } |