generated at
scrapbox-suggest-container
拡張Scrapbox入力補完に使う補完windowのComponent
これはtakkerが使うために自分のprojectに取り込んだやつです
コードのみをコピペしています
開発は/programming-notes/scrapbox-suggest-container-2でやっています。

2021-03-14 17:12:11 高さを調節した
diff
+max-height: 50vh; -overflow: hidden; +overflow-x: hidden; +overflow-y: auto;

container-css.js
export const css = ` ul { position: absolute; top: 100%; left: 0; z-index: 1000; flex-direction: column; min-width: 160px; max-width: 80%; max-height: 50vh; padding: 5px 0; margin: 2px 0 0; list-style: none; font-size: 14px; text-align: left; background-color: var(--completion-bg, #fff); border: var(--completion-border, 1px solid rgba(0,0,0,0.15)); border-radius: 4px; box-shadow: var(--completion-shadow, 0 6px 12px rgba(0,0,0,0.175)); background-clip: padding-box; white-space: nowrap; overflow-x: hidden; overflow-y: auto; text-overflow: ellipsis; } `;
script.js
import {css} from './container-css.js'; customElements.define('suggest-container', class extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({mode: 'open'}); shadow.innerHTML = `<style>${css}</style> <ul id="box"></ul>`; } connectedCallback() { this.hide(); } push(...items) { this._box.append(...items); if (this.mode === 'auto') this.show(); } insert(index, ...items) { if (index > this.items.length - 1) throw Error(`An index is out of range.`); //const itemNodes = _createItems(...items); const itemNodes = items; if (index === this.items.length - 1) { this._box.append(...itemNodes); } else { const fragment = new DocumentFragment(); fragment.append(...itemNodes); this.items[index].insertAdjacentElement('afterend', fragment); } if (this.mode === 'auto') this.show(); } pushFirst(...items) { if (this.items.length === 0) { this.push(...items); return; } const index = this.selectedItemIndex; const fragment = new DocumentFragment(); fragment.append(...items); this._box.insertBefore(fragment, this.firstItem); if (index !== -1) this.items[index + items.length].select(); if (this.mode === 'auto') this.show(); } replace(...items) { if (item.length === 0) throw Error('No item to be replaced found.'); const index = this.selecteItemIndex; this.clear(); if (index !== -1) (this.items[index] ?? this.firstItem).select(); } pop(...indices) { indices.forEach(index => this.items[index]?.remove?.()); if (this.items.length === 0) this.hide(); } clear() { [...this.items].forEach(item => item.remove()); this.hide(); } get items() { return this._box.childNodes; } get selectableItems() { return [...this.items].filter(item => !item.disabled); } get firstItem() { return this._box.firstElementChild; } get lastItem() { return this._box.lastElementChild; } get firstSelectableItem() { return [...this.items].find(item => !item.disabled); } get lastSelectableItem() { return [...this.items].reverse().find(item => !item.disabled); } get selectedItem() { return [...this.items].find(item => item.isSelected); } get selectedItemIndex() { return [...this.items].findIndex(item => item.isSelected); } selectNext({wrap}) { if (!this.firstSelectableItem) return; const selectedItem = this.selectedItem; this.selectedItem?.deselect?.(); if (!selectedItem || (wrap && this.lastItem === selectedItem)) { this.firstSelectableItem?.select?.(); } else{ selectedItem.nextSibling?.select?.(); } if (this.selectedItem.disabled) this.selectNext({wrap}); } selectPrevious({wrap}) { if (!this.firstSelectableItem) return; const selectedItem = this.selectedItem; this.selectedItem?.deselect?.(); if (!selectedItem) { this.firstSelectableItem?.select?.(); } else if (wrap && this.firstItem === selectedItem) { this.lastSelectableItem?.select?.(); } else { selectedItem.previousSibling?.select?.(); } if (this.selectedItem.disabled) this.selectPrevious({wrap}); } position({top, left}) { this._box.style.top = `${top}px`; this._box.style.left = `${left}px`; this.show(); } show() { if (this.items.length === 0) { this.hide(); return; } this.hidden = false; } hide() { this.hidden = true; } get _box() { return this.shadowRoot.getElementById('box'); } });
item-css.js
export const css = ` :host(.disabled) { cursor: not-allowed; } a { display: flex; padding: 0px 20px; clear: both; font-weight: normal; line-height: 28px; color: var(--completion-item-text-color, #333); align-items: center; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; text-decoration: none; } a:hover { color: var(--completion-item-hover-text-color, #262626); background-color: var(--completion-item-hover-bg, #f5f5f5); } :host(.selected) a{ color: var(--completion-item-hover-text-color, #262626); background-color: var(--completion-item-hover-bg, #f5f5f5); outline: 0; box-shadow: 0 0px 0px 3px rgba(102,175,233,0.6); border-color: #66afe9; transition: border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s; } img { height: 1.3em; margin-right: 3px; display: inline-block; } `;
item.js
import {css} from './item-css.js'; customElements.define('suggest-container-item', class extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({mode: 'open'}); shadow.innerHTML = `<style>${css}</style> <li id="frame"> <a id="body" tabindex="-1"> <img id="icon" hidden></img> <div id="text"></div> </a> </li>`; this._frame = shadow.getElementById('frame'); this._body = shadow.getElementById('body'); this._icon = shadow.getElementById('icon'); this._text = shadow.getElementById('text'); this._body.addEventListener('click', e =>{ if (!this._onClick) return; e.preventDefault(); e.stopPropagation(); this.click(e); }); } set({text, image, link, onClick}) { if (text) this.setAttribute('text', text); if (image) this.setAttribute('image', image); if (link) this.setAttribute('link', link); if (!onClick) { this.classList.add('disabled'); return; } if (typeof onClick !== 'function') throw Error('onClick is not a function.'); this._onClick = onClick; } get disabled() { return this.classList.contains('disabled'); } get isSelected() { return !this.disabled && this.classList.contains('selected'); } select() { if (!this.disabled) this.classList.add('selected'); } deselect() { this.classList.remove('selected'); } click(eventHandler) { this._onClick?.(eventHandler ?? {}); } static get observedAttributes() { return ['text', 'image', 'link',]; } attributeChangedCallback(name, oldValue, newValue) { switch(name) { case 'text': this._text.textContent = newValue; return; case 'image': const img = this._icon; if (newValue) { img.src = newValue; img.hidden = false; } else { img.src = ''; img.hidden = true; } return; case 'link': this._body.href = newValue ?? ''; return; } } }); export const create = (props) => { const item = document.createElement('suggest-container-item'); item.set(props); return item; }

#2021-03-14 17:23:07
#2021-03-03 10:17:32
#2021-02-20 02:40:41