174 lines
5.2 KiB
React
174 lines
5.2 KiB
React
|
const MakaiSideListEmptyElement = function(element) {
|
||
|
if(!(element instanceof Element))
|
||
|
element = <div class="sidelist-empty">
|
||
|
<div class="sidelist-empty-icon">※</div>
|
||
|
<div class="sidelist-empty-label">Nothing yet!</div>
|
||
|
</div>;
|
||
|
|
||
|
const iconElem = element.querySelector('.sidelist-empty-icon');
|
||
|
const labelElem = element.querySelector('.sidelist-empty-label');
|
||
|
|
||
|
return {
|
||
|
get element() { return element; },
|
||
|
|
||
|
get icon() { return iconElem.textContent; },
|
||
|
set icon(value) {
|
||
|
iconElem.textContent = value;
|
||
|
},
|
||
|
|
||
|
get label() { return labelElem.textContent; },
|
||
|
set label(value) {
|
||
|
labelElem.textContent = value;
|
||
|
},
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const MakaiSideListItemElement = function(element, icon, link, click) {
|
||
|
const linkClass = 'sidelist-item-link';
|
||
|
|
||
|
if(!(element instanceof Element)) {
|
||
|
element = <a class="sidelist-item">
|
||
|
<div class="sidelist-item-icon">{icon ?? (Math.random() < 0.5 ? '☆' : '★')}</div>
|
||
|
<div class="sidelist-item-label">{element ?? ''}</div>
|
||
|
</a>;
|
||
|
|
||
|
if(typeof link === 'string') {
|
||
|
element.classList.add(linkClass);
|
||
|
element.href = link;
|
||
|
}
|
||
|
|
||
|
if(typeof click === 'function')
|
||
|
element.onclick = click;
|
||
|
}
|
||
|
|
||
|
const isLink = () => element.classList.contains(linkClass);
|
||
|
|
||
|
const iconElem = element.querySelector('.sidelist-item-icon');
|
||
|
const labelElem = element.querySelector('.sidelist-item-label');
|
||
|
|
||
|
return {
|
||
|
get element() { return element; },
|
||
|
get isLink() { return isLink(); },
|
||
|
|
||
|
get link() { return element.href ?? ''; },
|
||
|
set link(value) {
|
||
|
if(value === undefined)
|
||
|
value = null;
|
||
|
|
||
|
element.href = value;
|
||
|
element.classList.toggle(linkClass, typeof value === 'string' && value !== '');
|
||
|
},
|
||
|
|
||
|
get icon() { return iconElem.textContent; },
|
||
|
set icon(value) {
|
||
|
iconElem.textContent = value;
|
||
|
},
|
||
|
|
||
|
get label() { return labelElem.textContent; },
|
||
|
set label(value) {
|
||
|
labelElem.textContent = value;
|
||
|
},
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const MakaiSideListElement = function(element) {
|
||
|
if(!(element instanceof Element))
|
||
|
element = <div class="sidelist">
|
||
|
<div class="sidelist-title">{element ?? ''}</div>
|
||
|
<div class="sidelist-body"/>
|
||
|
</div>;
|
||
|
|
||
|
const titleElem = element.querySelector('.sidelist-title');
|
||
|
const bodyElem = element.querySelector('.sidelist-body');
|
||
|
const emptyElem = new MakaiSideListEmptyElement(bodyElem.querySelector('.sidelist-empty'));
|
||
|
|
||
|
const getBodyChildren = () => {
|
||
|
const elems = [];
|
||
|
for(const elem of bodyElem.children) {
|
||
|
let item = elem;
|
||
|
if(item.classList.contains('sidelist-empty'))
|
||
|
item = new MakaiSideListEmptyElement(item);
|
||
|
else if(item.classList.contains('sidelist-item'))
|
||
|
item = new MakaiSideListItemElement(item);
|
||
|
elems.push(item);
|
||
|
}
|
||
|
return elems;
|
||
|
};
|
||
|
|
||
|
const insertEmptyElement = () => {
|
||
|
if(!bodyElem.contains(emptyElem.element) && bodyElem.childElementCount < 1)
|
||
|
bodyElem.appendChild(emptyElem.element);
|
||
|
};
|
||
|
const clearEmptyElement = () => {
|
||
|
if(bodyElem.contains(emptyElem.element))
|
||
|
bodyElem.removeChild(emptyElem.element);
|
||
|
};
|
||
|
|
||
|
const clearBody = insertEmpty => {
|
||
|
$rc(bodyElem);
|
||
|
insertEmptyElement();
|
||
|
};
|
||
|
|
||
|
const addItemCommon = (item, ...args) => {
|
||
|
if(typeof item === 'string')
|
||
|
item = new MakaiSideListItemElement(item, ...args);
|
||
|
else if(typeof item !== 'object' || item === null)
|
||
|
throw 'item must be a non-null object';
|
||
|
|
||
|
const elem = 'element' in item ? item.element : item;
|
||
|
if(bodyElem.contains(elem))
|
||
|
return;
|
||
|
|
||
|
clearEmptyElement();
|
||
|
return {
|
||
|
item: item,
|
||
|
elem: elem,
|
||
|
};
|
||
|
};
|
||
|
const appendItem = (...args) => {
|
||
|
const common = addItemCommon(...args);
|
||
|
if(common === undefined)
|
||
|
return;
|
||
|
|
||
|
bodyElem.appendChild(common.elem);
|
||
|
return common.item;
|
||
|
};
|
||
|
const prependItem = (...args) => {
|
||
|
const common = addItemCommon(...args);
|
||
|
if(common === undefined)
|
||
|
return;
|
||
|
|
||
|
bodyElem.insertBefore(common.elem, bodyElem.firstChild);
|
||
|
return common.item;
|
||
|
};
|
||
|
|
||
|
const removeItem = item => {
|
||
|
const elem = 'element' in item ? item.element : item;
|
||
|
if(bodyElem.contains(elem))
|
||
|
bodyElem.removeChild(elem);
|
||
|
|
||
|
insertEmptyElement();
|
||
|
return item;
|
||
|
};
|
||
|
|
||
|
if(bodyElem.childElementCount < 1)
|
||
|
insertEmptyElement();
|
||
|
|
||
|
return {
|
||
|
get element() { return element; },
|
||
|
get titleElem() { return titleElem; },
|
||
|
get bodyElem() { return bodyElem; },
|
||
|
get emptyElem() { return emptyElem; },
|
||
|
get children() { return getBodyChildren(); },
|
||
|
|
||
|
get title() { return titleElem.textContent; },
|
||
|
set title(value) {
|
||
|
titleElem.textContent = value;
|
||
|
},
|
||
|
|
||
|
appendItem: appendItem,
|
||
|
prependItem: prependItem,
|
||
|
removeItem: removeItem,
|
||
|
};
|
||
|
};
|