script.jsimport {style, ul, h, fragment} from '../easyDOMgenerator/script.js';
import {css} from './css.js';
import {relatedPageCard} from '../scrapbox-card-bubble-2%2FpageCard/script.js';
const TAG_NAME = 'card-container';
customElements.define(TAG_NAME, class extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(fragment(style(css),ul({id: 'list'})));
this._list = shadowRoot.getElementById('list');
this._eventCallbacks = [];
}
get cards() {
return JSON.parse(this.getAttribute('cards'));
}
get sort() {
return this.getAttribute('sort') ?? 'linked';
}
position({top, left}) {
this.style.top = `${top}px`;
this.style.left = `${left}px`;
}
show() {
// 空っぽの場合は表示しない
// defaultで<style>が含まれるので、nodeが2個以上のときのみ表示する
if (this._list.childElementCount === 0) {
this.hide();
return;
}
this.hidden = false;
}
hide() {
this.hidden = true;
}
on(event, selecter, callback) {
if (this._eventCallbacks[event]) this.shadowRoot.removeEventListener(event, this._eventCallbacks[event], {capture: true});
this._eventCallbacks[event] = e => {
if (!e.target.matches(selecter)) return;
callback(e);
};
this.shadowRoot.addEventListener(event, this._eventCallbacks[event], {capture: true});
}
// page cardを更新する
attributeChangedCallback(name, oldValue, newValue) { // (4)
switch (name) {
case 'cards':
const cards = JSON.parse(newValue).sort((a,b) => {
switch(this.sort) {
case 'updated':
return b.updated - a.updated;
case 'linked':
default:
return b.linked - a.linked;
}
});
if (this._list.children.length > cards.length) {
[...this._list.children].slice(cards.length).forEach(card => card.remove());
}
this._list.children.forEach((card, i) => {
card.project = cards[i].project;
card.title = cards[i].title;
card.description = cards[i].description;
card.thumbnail = cards[i].image;
card.dataset.linked = cards[i].linked;
card.dataset.updated = cards[i].updated;
});
if (this._list.children.length < cards.length) {
this._list.append(...cards
.slice(this._list.children.length)
.map(({project, title, description, image, linked, updated}) =>
relatedPageCard({
project,
title,
description,
thumbnail: image,
'data-linked': linked,
'data-updated': updated,
})
)
);
}
break;
case 'sort':
const sortedCards = [...this._link.children]
.sort((a,b) => {
switch(this.sort) {
case 'updated':
return b.dataset.updated - a.dataset.updated;
case 'linked':
default:
return b.dataset.linked - a.dataset.linked;
}
});
this._link.children.forEach(card => card.remove());
this._link.append(...sortedCards);
break;
}
}
static get observedAttributes() {
return ['cards', 'sort'];
}
});
export const cardContainer = (...params) => h(TAG_NAME, ...params);