scrapbox-suggest-container
これはtakker.iconが使うために自分のprojectに取り込んだやつです
コードのみをコピペしています
2021-03-14 17:12:11 高さを調節した
code:diff
+max-height: 50vh;
-overflow: hidden;
+overflow-x: hidden;
+overflow-y: auto;
code: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;
}
`;
code: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.itemsindex.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 (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.itemsindex ?? this.firstItem).select(); }
pop(...indices) {
indices.forEach(index => this.itemsindex?.remove?.()); if (this.items.length === 0) this.hide();
}
clear() {
this.hide();
}
get items() {
return this._box.childNodes;
}
get selectableItems() {
}
get firstItem() {
return this._box.firstElementChild;
}
get lastItem() {
return this._box.lastElementChild;
}
get firstSelectableItem() {
}
get lastSelectableItem() {
}
get selectedItem() {
}
get selectedItemIndex() {
}
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');
}
});
code: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);
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;
}
`;
code: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() {
}
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;
}