scrapbox-card-container
UI
<card-container>
属性
cards
表示するcardのデータ
↓の形式のobjectをJSON文字列にして入れる
code:ts
type Cards = {
project: string;
title: string;
theme: string;
linked: number;
updated: number;
description: string;
image?: string;
}[];
sort
cardの並び順
linked
updated
title
機能
閉じるボタンをつけたい
主にmobile用
PCではmouseoverしたら消えるようにする
optionでつけないようにもできる
code:css.js
export const css = `
:host {
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
position: absolute;
max-width: 80vw;
box-sizing: content-box;
z-index: 9000;
}
ul {
display: flex;
padding: 0px;
margin: 0px;
list-style: none;
overflow-x: auto;
overflow-y: visible;
}
ul * {
padding: 5px
}
`;
code:script.js
import {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._eventCallbacksevent) this.shadowRoot.removeEventListener(event, this._eventCallbacksevent, {capture: true}); this._eventCallbacksevent = e => { if (!e.target.matches(selecter)) return;
callback(e);
};
this.shadowRoot.addEventListener(event, this._eventCallbacksevent, {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.forEach((card, i) => {
card.project = cardsi.project; card.title = cardsi.title; card.description = cardsi.description; card.thumbnail = cardsi.image; card.dataset.linked = cardsi.linked; card.dataset.updated = cardsi.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':
.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() {
}
});
export const cardContainer = (...params) => h(TAG_NAME, ...params);
test code
code:js
import('/api/code/programming-notes/scrapbox-card-container/test1.js');
code:test1.js
import {cardContainer} from './script.js';
import {scrapboxDOM} from '../scrapbox-dom-accessor/script.js';
(async () => {
const path = '/takker/2021-04-28';
const res = await fetch(/api/pages${path});
const {relatedPages: {links1hop}} = await res.json();
const box = cardContainer({cards: JSON.stringify(
links1hop.map(({title, descriptions, image, linked, updated}) => ({
project: 'takker',
title,
description: descriptions.join('\n'),
image, linked, updated,
})
)
)});
scrapboxDOM.editor.append(box);
const observer = new MutationObserver(async () =>{
box.position({
top: parseInt(scrapboxDOM.cursor.style.top) - box.clientHeight,
left: parseInt(scrapboxDOM.cursor.style.left),
});
box.show();
});
observer.observe(scrapboxDOM.cursor, {attributes: true});
})();