bilibili-bangumi-component
Version:
展示 bilibili 与 Bangumi 追番列表的 WebComponent 组件
328 lines (313 loc) • 19.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-b78452e6.js');
function serializeSearchParams(searchParams) {
return Object.entries(searchParams)
.filter(([, value]) => !!value)
.map(([key, value]) => `${key}=${value}`)
.join('&');
}
function formatUrl(baseUrl) {
if (!baseUrl.startsWith('http'))
return baseUrl;
const url = new URL(baseUrl);
const pathname = url.pathname === '/' ? '' : url.pathname;
return `${url.origin}${pathname}`;
}
function thorttle(fn, wait) {
let timer;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, args);
}, wait);
}
};
}
const animeCollectionMap = {
全部: '0',
想看: '1',
在看: '2',
看过: '3',
搁置: '4',
抛弃: '5',
};
const gameCollectionMap = {
全部: '0',
想玩: '1',
在玩: '2',
玩过: '3',
搁置: '4',
抛弃: '5',
};
const bookCollectionMap = {
全部: '0',
想读: '1',
在读: '2',
读过: '3',
搁置: '4',
抛弃: '5',
};
const collectionMap = {
动画: animeCollectionMap,
游戏: gameCollectionMap,
书籍: bookCollectionMap,
音乐: bookCollectionMap,
三次元: animeCollectionMap,
};
const subjectMap = {
动画: '1',
游戏: '2',
书籍: '3',
音乐: '4',
三次元: '5',
};
const collectionLabelMap = {
动画: ['全部', '想看', '在看', '看过', '搁置', '抛弃'],
游戏: ['全部', '想玩', '在玩', '玩过', '搁置', '抛弃'],
书籍: ['全部', '想读', '在读', '读过', '搁置', '抛弃'],
音乐: ['全部', '想听', '在听', '听过', '搁置', '抛弃'],
三次元: ['全部', '想看', '在看', '看过', '搁置', '抛弃'],
};
async function getBilibili(baseUrl, params) {
const query = Object.assign(Object.assign({}, params), { collectionType: animeCollectionMap[params.collectionType] });
const res = await fetch(`${formatUrl(baseUrl)}/bilibili?${serializeSearchParams(query)}`);
return await res.json();
}
async function getBangumi(baseUrl, params) {
const { subjectType } = params;
const query = Object.assign(Object.assign({}, params), { collectionType: collectionMap[subjectType][params.collectionType], subjectType: subjectMap[params.subjectType] });
const res = await fetch(`${formatUrl(baseUrl)}/bgm?${serializeSearchParams(query)}`);
return await res.json();
}
async function getCustom(baseUrl, params) {
const { subjectType } = params;
const query = Object.assign(Object.assign({}, params), { collectionType: collectionMap[subjectType][params.collectionType], subjectType: subjectMap[params.subjectType] });
const res = await fetch(`${formatUrl(baseUrl)}/custom?${serializeSearchParams(query)}`);
return await res.json();
}
function Tabs({ activeLabel, labels, containerState, onChange }) {
const handleClick = (label) => {
if (activeLabel !== label)
onChange(label);
};
return (index.h("div", { class: "bbc-tabs" }, labels.map(label => (index.h("div", { class: {
'bbc-tab-item': true,
'bbc-tab-item-active': label === activeLabel,
'bbc-md-tab-item': containerState !== 'large',
}, key: label, onClick: () => handleClick(label) }, label)))));
}
function Skeleton() {
return (index.h("div", { class: "bbc-skeleton-container bbc-bangumi-item" },
index.h("div", { class: "bbc-skeleton-avatar" }),
index.h("div", { class: "bbc-skeleton-content bbc-bangumi-content" },
index.h("div", { class: "bbc-skeleton-row", style: { width: '30%' } }),
index.h("div", { class: "bbc-skeleton-row", style: { width: '60%', height: '40px' } }),
index.h("div", { class: "bbc-skeleton-row", style: { width: '90%', height: '32px' } }))));
}
const Label = ({ containerState, labels }) => {
let renderLabels = [...labels];
if (containerState === 'middle')
renderLabels = renderLabels.slice(0, 4);
if (containerState === 'small')
renderLabels = renderLabels.slice(0, 3);
return (index.h("div", { class: "bbc-bangumi-labels" }, renderLabels.map(label => (index.h("div", { class: {
'bbc-bangumi-label': true,
'bbc-md-label-text': containerState !== 'large',
} },
index.h("p", { class: "bbc-bangumi-label-title" }, label.label),
label.value && index.h("p", null, label.value))))));
};
const List = ({ list, loading, containerState }) => {
return (index.h("div", { class: "bbc-bangumi" }, list.map(item => (index.h("div", null, loading
? index.h(Skeleton, null)
: (index.h("div", { class: "bbc-bangumi-item" },
index.h("a", { href: item.url, target: "_blank", rel: "noreferrer" },
index.h("img", { src: item.cover, alt: "cover", loading: "lazy", referrerpolicy: "no-referrer" })),
index.h("div", { class: "bbc-bangumi-content" },
index.h("h3", { class: "bbc-bangumi-title" },
index.h("a", { href: item.url, target: "_blank", rel: "noreferrer", innerHTML: item.name ? item.name : item.nameCN }),
item.name && index.h("small", { innerHTML: item.nameCN })),
index.h(Label, { containerState: containerState, labels: item.labels }),
index.h("p", { class: "bbc-bangumi-summary", innerHTML: item.summary })))))))));
};
function Pagination({ pageNumber, totalPages, onChange, onInputChange }) {
return (index.h("div", { class: "bbc-pagination" },
index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('head') }, "\u9996\u9875"),
index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('prev') }, "\u4E0A\u4E00\u9875"),
index.h("span", { class: "bbc-pagination-pagenum" }, `${pageNumber} / ${totalPages}`),
index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('next') }, "\u4E0B\u4E00\u9875"),
index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('tail') }, "\u5C3E\u9875"),
index.h("div", { class: "bbc-pagination-input" },
index.h("span", null, "\u8DF3\u81F3"),
index.h("input", { type: "text", maxLength: 4, onChange: onInputChange }),
index.h("span", null, "\u9875"))));
}
function Empty() {
return (index.h("div", { class: "bbc-empty" },
index.h("img", { src: "https://s1.hdslb.com/bfs/static/webssr/article/empty.png", alt: "empty", referrerpolicy: "no-referrer" })));
}
function Error({ error }) {
return (index.h("div", { class: "bbc-error" },
index.h("img", { src: "https://s1.hdslb.com/bfs/static/jinkela/long/bitmap/error_01.png", alt: "parse failed",
// @ts-expect-error
referrerpolicy: "no-referrer" }),
index.h("p", null, "\u03A3(o\uFF9F\u0434\uFF9Fo\uFF89) \u53D1\u751F\u4E86\u4E00\u4E9B\u9519\u8BEF"),
index.h("p", null, `message: ${error.message}`)));
}
const indexCss = "@layer bilibili-bangumi-component {\n :host {\n display: block;\n /* 基础文本颜色 */\n --bbc-text-base-color: #4c4948;\n /* 标签颜色 */\n --bbc-label-color: #FF9843;\n /* 下划线、背景之类的颜色 */\n --bbc-primary-color: #425aef;\n }\n\n * {\n box-sizing: border-box;\n }\n\n a {\n text-decoration: none;\n }\n\n .bbc-tabs {\n display: flex;\n gap: 8px;\n }\n\n .bbc-tab-item {\n padding: 4px 16px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: bold;\n color: var(--bbc-text-base-color);\n cursor: pointer;\n }\n\n .bbc-md-tab-item {\n padding: 4px 12px;\n }\n\n .bbc-tab-item:hover {\n background-color: var(--bbc-primary-color);\n color: white;\n }\n\n .bbc-tab-item-active {\n color: white;\n background-color: var(--bbc-primary-color);\n }\n\n .bbc-header-platform {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .bbc-header-platform .divider {\n width: 3px;\n border-radius: 2px;\n height: 32px;\n background-color: var(--bbc-primary-color);\n }\n\n .bbc-bangumi:last-child {\n border-bottom: none;\n }\n\n .bbc-bangumi-item {\n display: flex;\n margin: 12px 0;\n gap: 16px;\n padding: 16px;\n border-bottom: 1px solid #ddd;\n }\n\n .bbc-bangumi-item img {\n border-radius: 8px;\n cursor: pointer;\n height: 140px;\n object-fit: cover;\n aspect-ratio: 3 / 4;\n }\n\n .bbc-bangumi-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n }\n\n .bbc-bangumi-title {\n margin: 0;\n margin-bottom: 8px;\n }\n\n .bbc-bangumi-title a {\n color: var(--bbc-text-base-color);\n }\n\n .bbc-bangumi-title a:hover {\n border-bottom: 2px solid var(--bbc-primary-color);\n }\n\n .bbc-bangumi-title small {\n margin-left: 8px;\n font-weight: normal;\n font-size: 14px;\n color: var(--bbc-text-base-color);\n height: 24px;\n }\n\n .bbc-bangumi-labels {\n color: var(--bbc-label-color);\n display: flex;\n }\n\n .bbc-bangumi-label {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n padding: 0 12px;\n height: 36px;\n font-size: 14px;\n border-right: 1px solid var(--bbc-label-color);\n }\n\n .bbc-bangumi-label:last-child {\n border-right: none;\n }\n\n .bbc-bangumi-label p {\n margin: 0;\n padding: 0 4px;\n }\n\n .bbc-bangumi-label:not(.bbc-bangumi-label > .bbc-bangumi-label-title) {\n justify-content: center;\n }\n\n .bbc-md-label-text {\n font-size: 12px;\n }\n\n .bbc-bangumi-summary {\n margin-bottom: 0;\n font-size: 12px;\n color: var(--bbc-text-base-color);\n\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n }\n\n .bbc-pagination {\n height: 40px;\n text-align: center;\n padding-right: 20px;\n }\n\n .bbc-pagination-button {\n border-radius: 4px;\n font-size: 14px;\n padding: 4px 8px;\n margin: 0 4px;\n color: #bbb;\n cursor: pointer;\n }\n\n .bbc-pagination-button:hover {\n background-color: var(--bbc-primary-color);\n color: white;\n }\n\n .bbc-pagination-pagenum {\n color: #bbb;\n font-size: 14px;\n }\n \n .bbc-pagination-input {\n display: inline-block;\n color: #bbb;\n font-size: 12px;\n }\n\n .bbc-pagination-input input {\n display: inline-block;\n margin: 0 8px;\n font-size: 12px;\n color: #bbb;\n width: 30px;\n height: 24px;\n line-height: 24px;\n border-radius: 4px;\n border: 1px solid #bbb;\n text-align: center;\n outline: none;\n transition: border .2s ease;\n }\n\n .bbc-skeleton-container {\n animation: skeleton-blink 1.2s ease-in-out infinite;\n }\n\n @keyframes skeleton-blink {\n 50% {\n opacity: 0.6;\n }\n }\n\n .bbc-skeleton-avatar {\n height: 140px;\n border-radius: 12px;\n background-color: #f2f3f5;\n aspect-ratio: 3 / 4;\n }\n\n .bbc-skeleton-row {\n height: 24px;\n border-radius: 8px;\n background-color: #f2f3f5;\n }\n\n .bbc-empty {\n display: flex;\n padding: 40px 0;\n justify-content: center;\n align-items: center;\n }\n\n .bbc-empty img {\n height: 140px;\n object-fit: cover;\n }\n\n .bbc-error {\n margin: 20px 0;\n display: flex;\n gap: 12px;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n\n .bbc-error img {\n display: block;\n width: 260px;\n }\n\n .bbc-error p {\n margin: 0;\n color: var(--bbc-text-base-color);\n font-size: 18px;\n }\n}";
const BilibiliBangumiStyle0 = indexCss;
const BilibiliBangumi = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.platformLabels = ['Bilibili', 'Bangumi'];
this.subjectLabels = ['动画', '游戏', '书籍', '音乐', '三次元'];
this.fetchData = async () => {
try {
this.loading = true;
this.error = null;
let response;
const bilibiliParams = {
uid: this.bilibiliUid,
collectionType: this.activeCollection,
pageSize: this.pageSize,
pageNumber: this.pageNumber,
};
if (this.activePlatform === 'Bilibili') {
response = await getBilibili(this.api, bilibiliParams);
}
else if (this.activePlatform === 'Bangumi') {
response = await getBangumi(this.api, Object.assign(Object.assign({}, bilibiliParams), { uid: this.bgmUid, subjectType: this.activeSubject }));
}
else {
response = await getCustom(this.api, Object.assign(Object.assign({}, bilibiliParams), { subjectType: this.activeSubject }));
}
if (response.code === 200) {
this.responseData = response.data;
}
else {
this.error = response;
this.responseData = null;
}
}
catch (error) {
this.error = error;
this.responseData = null;
}
this.loading = false;
};
this.handlePlatformChange = (label) => {
this.collectionLabels = label === 'Bilibili' ? ['全部', '想看', '在看', '看过'] : collectionLabelMap['动画'];
this.activePlatform = label;
this.pageNumber = 1;
this.activeSubject = '动画';
this.activeCollection = '全部';
this.fetchData();
};
this.handleSubjectChange = (label) => {
this.collectionLabels = this.activePlatform === 'Bilibili' ? ['全部', '想看', '在看', '看过'] : collectionLabelMap[label];
this.activeSubject = label;
this.pageNumber = 1;
this.activeCollection = '全部';
this.fetchData();
};
this.handleCollectionChange = (label) => {
this.activeCollection = label;
this.pageNumber = 1;
this.fetchData();
};
this.scrollToTop = () => {
document.documentElement.scrollTo({
top: 0,
behavior: 'smooth',
});
};
this.handlePageChange = (changeType) => {
const { totalPages } = this.responseData;
switch (changeType) {
case 'head':
this.pageNumber = 1;
break;
case 'prev':
if (this.pageNumber === 1)
return;
this.pageNumber--;
break;
case 'next':
if (this.pageNumber === totalPages)
return;
this.pageNumber++;
break;
case 'tail':
this.pageNumber = totalPages;
break;
}
this.scrollToTop();
this.fetchData();
};
this.handleInputChange = (event) => {
const inputValue = Number.parseInt(event.target.value);
if (Object.is(inputValue, Number.NaN))
return;
const { totalPages } = this.responseData;
if (inputValue < 1)
this.pageNumber = 1;
else if (inputValue > totalPages)
this.pageNumber = totalPages;
else
this.pageNumber = inputValue;
this.scrollToTop();
this.fetchData();
};
this.api = undefined;
this.bilibiliUid = undefined;
this.bgmUid = undefined;
this.bilibiliEnabled = true;
this.bgmEnabled = true;
this.pageSize = 15;
this.customEnabled = false;
this.customLabel = '自定义';
this.loading = false;
this.error = undefined;
this.pageNumber = 1;
this.responseData = undefined;
this.activePlatform = 'Bilibili';
this.activeSubject = '动画';
this.collectionLabels = ['全部', '想看', '在看', '看过'];
this.activeCollection = '全部';
this.containerRef = null;
this.containerState = 'large';
}
componentWillLoad() {
const platformLabels = [...this.platformLabels];
if (this.customEnabled)
platformLabels.push(this.customLabel);
const filterArr = [this.bilibiliEnabled, this.bgmEnabled, this.customEnabled];
this.platformLabels = platformLabels.filter((_, index) => filterArr[index]);
this.activePlatform = this.platformLabels[0];
this.fetchData();
}
componentDidLoad() {
// 监听容器尺寸变化
const update = thorttle((width) => {
let containerState = 'large';
if (width <= 640)
containerState = 'middle';
if (width <= 465)
containerState = 'small';
this.containerState = containerState;
}, 100).bind(this);
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries)
update(entry.contentRect.width);
});
resizeObserver.observe(this.containerRef);
}
render() {
return (index.h("div", { ref: ele => this.containerRef = ele }, index.h("div", { class: "bbc-header-platform" }, index.h(Tabs, { containerState: this.containerState, activeLabel: this.activePlatform, labels: this.platformLabels, onChange: this.handlePlatformChange }), this.activePlatform !== 'Bilibili' && index.h("div", { class: "divider" }), this.activePlatform !== 'Bilibili'
&& index.h(Tabs, { containerState: this.containerState, activeLabel: this.activeSubject, labels: this.subjectLabels, onChange: this.handleSubjectChange })), index.h("div", null, index.h(Tabs, { containerState: this.containerState, activeLabel: this.activeCollection, labels: this.collectionLabels, onChange: this.handleCollectionChange })), this.loading && index.h(Skeleton, null), this.error && index.h(Error, { error: this.error }), this.responseData && index.h(List, { containerState: this.containerState, loading: this.loading, list: this.responseData.list }), this.responseData && this.responseData.total === 0 && index.h(Empty, null), this.responseData && (index.h(Pagination, { pageNumber: this.pageNumber, totalPages: this.responseData.totalPages, onChange: this.handlePageChange, onInputChange: this.handleInputChange }))));
}
};
BilibiliBangumi.style = BilibiliBangumiStyle0;
exports.bilibili_bangumi = BilibiliBangumi;