File size: 2,892 Bytes
831819e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | export const esc = (value) =>
String(value ?? "").replace(
/[&<>"']/g,
(c) =>
({
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
})[c],
);
export const number = (value) =>
Number.isFinite(value)
? value.toLocaleString("zh-CN", { maximumFractionDigits: 1 })
: "—";
export function icon(name, label, attributes = "") {
return `<button type="button" class="icon-button synth-icon" aria-label="${esc(label)}" title="${esc(label)}" ${attributes}><i data-lucide="${name}" aria-hidden="true"></i></button>`;
}
export function external(label, url) {
return `<a href="${esc(url)}" target="_blank" rel="noopener noreferrer">${esc(label)}<i data-lucide="arrow-up-right" aria-hidden="true"></i></a>`;
}
export function section(title, body, id = "") {
return `<section class="section" ${id ? `id="${id}"` : ""}><h2>${esc(title)}</h2>${body}</section>`;
}
export function table(headings, rows, className = "") {
return `<div class="table-scroll ${className}" tabindex="0" role="region" aria-label="${esc(headings.join("、"))}"><table><thead><tr>${headings.map((h) => `<th scope="col">${esc(h)}</th>`).join("")}</tr></thead><tbody>${rows.join("")}</tbody></table></div>`;
}
export function empty(message = "没有匹配的数据集", reset = false) {
return `<div class="empty-state"><i data-lucide="search-x" aria-hidden="true"></i><p>${esc(message)}</p>${reset ? '<button type="button" class="text-button" data-clear-filters>重置筛选</button>' : ""}</div>`;
}
export function enhance() {
window.lucide?.createIcons();
}
export function fact(label, value) {
return `<div><dt>${esc(label)}</dt><dd>${value}</dd></div>`;
}
export function tier(id, meta) {
const value = meta.tier[id];
return `<span class="tier tier-${esc(id)}" title="${esc(value?.name + " · " + value?.desc)}">${esc(id === "-" ? "—" : id)}</span>`;
}
export function status(id, meta) {
return `<span class="status status-${esc(id)}" title="${esc(meta.status[id]?.desc)}">${esc(meta.status[id]?.name || id)}</span>`;
}
export function thumbnail(dataset, context) {
const preview = context.previews[dataset.id]?.[0];
const figure = context.figures[dataset.id]?.figures?.[0]?.images?.[0];
return preview || figure
? `<img class="dataset-thumb" src="${esc(preview?.rgb || figure.src)}" alt="${esc(dataset.name)}${preview ? "样例" : "官方图"}" loading="lazy" decoding="async">`
: '<span class="dataset-thumb no-thumb"><i data-lucide="images" aria-hidden="true"></i></span>';
}
export async function readJSON(path, { optional = false, fallback = {} } = {}) {
try {
const response = await fetch(path);
if (!response.ok) throw new Error(`${path}: ${response.status}`);
return await response.json();
} catch (error) {
if (!optional) throw error;
return fallback;
}
}
|