generated at
suggestWindow
hr
入力補完windowを作成・操作するクラス

2020-09-21 09:08:43
windowの幅を調節した
2020-09-17 06:24:10
<li> にidを付与した
2020-09-16 20:42:25 max-heightが正しく反映されていなかったのを直した

Web Componentsを使って書き換えたいな

script.js
export class suggestWindow { constructor({id, maxHeight = 'calc(50vh - 100px)'} = {}) { this.box = document.createElement('div'); this.box.setAttribute('id', id); this.box.classList.add('form-group'); this.box.style.position = 'absolute'; this.container = document.createElement('div'); this.container.classList.add('dropdown'); this.box.appendChild(this.container); this.items = document.createElement('ul'); this.items.classList.add('dropdown-menu'); this.items.style.whiteSpace = 'nowrap'; // 文字列を折り返さない this.items.style.maxHeight = maxHeight; this.items.style.maxWidth = 'calc(-100px + 50vw)'; this.items.style.overflow = 'auto'; this.container.appendChild(this.items); this.editor = document.getElementById('editor'); this.editor.appendChild(this.box); // 入力候補リストの最初の要素のidに使う番号 this.SEED = new Date().getTime().toString(16); } // 入力補完window を開く open() { // 入力候補がなければ開かない if (this.length == 0) return; this.container.classList.add('open'); } // 入力補完window を閉じる close() { //console.log('close the window.'); if(!this.isOpen()) return; this.container.classList.remove('open'); this.items.textContent =''; document.getElementById('text-input').focus(); } // 入力補完window の開閉判定 isOpen() { return this.container.classList.contains('open'); } // 入力候補の数 get length() { return this.items.children.length; } _makeId(index) { return `item-${this.SEED}-${index}`; } // 入力候補を追加する appendItems(newItems) { newItems.forEach((newItem, index) => { this.items.appendChild(this._makeNewItem({ elements: newItem.elements, onComfirm: newItem.onComfirm, index: index })) }); } // 入力候補を上書きする resetItems(items) { // アイテムを消す this.items.textContent =''; this.appendItems(items); } _makeNewItem({elements, onComfirm, index}) { const listItem = document.createElement('li'); listItem.classList.add('dropdown-item'); const a = document.createElement('a'); a.setAttribute('id', this._makeId(index)); a.dataset.index = index; a.setAttribute('tabindex', '-1'); elements.forEach(element => a.appendChild(element)); a.onclick = onComfirm; listItem.appendChild(a); return listItem; } // indexから要素を取得する getItem(index) { const id = this._makeId(index); const item = document.getElementById(id); if(!item) { //console.log(`item the id of which is ${id} isn't found.`); return undefined; } return item; } // 先頭要素 get firstItem() { //console.log('getting the first item...'); return this.getItem(0); } // 末尾要素 get lastItem() { return this.getItem(this.length - 1); } // 入力補完windowの位置を更新する reDraw(cursor) { this.box.style.top = `${parseInt(cursor.style.top) + parseInt(cursor.style.height) + 3}px`; this.box.style.left = `${parseInt(cursor.style.left)}px`; } // 現在focusがあたっている入力候補を返す get focusedItem() { return this.items.querySelector(':focus'); } hasFocus(){ return this.focusedItem != null; } // 補完候補の先頭にfocusを当てる selectFirstItem() { this.firstItem.focus(); } // 補完候補の末尾にfocusを当てる selectLastItem() { this.lastItem.focus(); } // 最初の補完候補にfocusがあたっているかどうか hasFirstItemFocus() { //console.log(`selectedItem: ${document.activeElement}`); return document.activeElement == this.firstItem; } // 最後の補完候補にfocusがあたっているかどうか hasLastItemFocus() { //console.log(`selectedItem: ${document.activeElement}`); return document.activeElement == this.lastItem; } // 次の補完候補に移動する selectNextItem({wrap = false} = {}) { //console.log('Start selecting the next item...'); // focusが当たっている要素を取得 const selectedItem = this.focusedItem; if(!selectedItem) { //console.log('No item is focused. Instead selecting the first item...'); this.selectFirstItem(); return; } if(selectedItem == this.lastItem){ // 一番下まで行ったら先頭に戻す if(wrap) this.selectFirstItem(); return; } //console.log(`index: ${this.indexOfFocusedItem}`); this.getItem(this.indexOfFocusedItem + 1).focus(); } // 前の補完候補に移動する selectPreviousItem({wrap = false} = {}) { //console.log('Start selecting the previous item...'); // focusが当たっている要素を取得 const selectedItem = this.focusedItem; if(!selectedItem) { //console.log('No item is focused. Instead selecting the last item...'); this.selectLastItem(); return; } if(selectedItem == this.firstItem){ // 一番上まで行ったら末尾に移動する if(wrap) this.selectLastItem(); return; } this.getItem(this.indexOfFocusedItem - 1).focus(); } // focusが当たっている要素の通し番号を取得する get indexOfFocusedItem() { const selectedItem = this.focusedItem; if(!selectedItem) return undefined; return parseInt(selectedItem.dataset.index); } }

#2020-12-26 15:40:24
#2020-10-12 00:28:39