Move library browsing to main grid
This commit is contained in:
+49
-25
@@ -1,17 +1,18 @@
|
||||
const state = {
|
||||
titles: [],
|
||||
activeId: "",
|
||||
query: ""
|
||||
query: "",
|
||||
view: "collection"
|
||||
};
|
||||
|
||||
const els = {
|
||||
libraryBadge: document.querySelector("#libraryBadge"),
|
||||
libraryPath: document.querySelector("#libraryPath"),
|
||||
backBtn: document.querySelector("#backBtn"),
|
||||
rescanBtn: document.querySelector("#rescanBtn"),
|
||||
searchInput: document.querySelector("#searchInput"),
|
||||
titleList: document.querySelector("#titleList"),
|
||||
details: document.querySelector("#details"),
|
||||
titleButtonTemplate: document.querySelector("#titleButtonTemplate")
|
||||
titleCardTemplate: document.querySelector("#titleCardTemplate")
|
||||
};
|
||||
|
||||
function bytes(value) {
|
||||
@@ -40,9 +41,9 @@ async function loadLibrary() {
|
||||
state.titles = data.titles;
|
||||
els.libraryPath.textContent = data.libraryDir;
|
||||
els.libraryBadge.textContent = `${state.titles.length} title${state.titles.length === 1 ? "" : "s"}`;
|
||||
if (!state.activeId && state.titles.length) state.activeId = state.titles[0].id;
|
||||
if (!state.titles.some((title) => title.id === state.activeId)) {
|
||||
state.activeId = state.titles[0]?.id || "";
|
||||
state.activeId = "";
|
||||
state.view = "collection";
|
||||
}
|
||||
render();
|
||||
}
|
||||
@@ -61,28 +62,18 @@ function filteredTitles() {
|
||||
}
|
||||
|
||||
function render() {
|
||||
renderTitleList();
|
||||
renderDetails();
|
||||
}
|
||||
|
||||
function renderTitleList() {
|
||||
els.titleList.innerHTML = "";
|
||||
for (const title of filteredTitles()) {
|
||||
const node = els.titleButtonTemplate.content.firstElementChild.cloneNode(true);
|
||||
node.classList.toggle("is-active", title.id === state.activeId);
|
||||
node.querySelector(".title-button__name").textContent = title.title;
|
||||
node.querySelector(".title-button__meta").textContent = `${title.count} file${title.count === 1 ? "" : "s"} · ${bytes(title.size)}`;
|
||||
node.addEventListener("click", () => {
|
||||
state.activeId = title.id;
|
||||
render();
|
||||
});
|
||||
els.titleList.append(node);
|
||||
const activeTitle = state.titles.find((item) => item.id === state.activeId);
|
||||
els.backBtn.classList.toggle("hidden", state.view !== "details");
|
||||
if (state.view === "details" && activeTitle) {
|
||||
renderDetails(activeTitle);
|
||||
return;
|
||||
}
|
||||
renderCollection();
|
||||
}
|
||||
|
||||
function renderDetails() {
|
||||
const title = state.titles.find((item) => item.id === state.activeId);
|
||||
if (!title) {
|
||||
function renderCollection() {
|
||||
const titles = filteredTitles();
|
||||
if (!titles.length) {
|
||||
els.details.innerHTML = `
|
||||
<div class="empty">
|
||||
<h2>No titles found</h2>
|
||||
@@ -92,6 +83,33 @@ function renderDetails() {
|
||||
return;
|
||||
}
|
||||
|
||||
els.details.innerHTML = `
|
||||
<div class="collection-grid" id="collectionGrid"></div>
|
||||
`;
|
||||
|
||||
const grid = document.querySelector("#collectionGrid");
|
||||
for (const title of titles) {
|
||||
const node = els.titleCardTemplate.content.firstElementChild.cloneNode(true);
|
||||
node.querySelector(".title-card__name").textContent = title.title;
|
||||
node.querySelector(".title-card__meta").textContent = `${title.count} file${title.count === 1 ? "" : "s"} · ${bytes(title.size)}`;
|
||||
node.querySelector(".title-card__fallback").textContent = initials(title.title);
|
||||
const poster = node.querySelector(".title-card__poster");
|
||||
const image = node.querySelector("img");
|
||||
if (title.thumbnail) {
|
||||
poster.classList.add("has-image");
|
||||
image.src = title.thumbnail;
|
||||
image.alt = "";
|
||||
}
|
||||
node.addEventListener("click", () => {
|
||||
state.activeId = title.id;
|
||||
state.view = "details";
|
||||
render();
|
||||
});
|
||||
grid.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
function renderDetails(title) {
|
||||
els.details.innerHTML = `
|
||||
<div class="hero">
|
||||
<div class="poster">${title.thumbnail ? `<img src="${escapeAttr(title.thumbnail)}" alt="">` : `<span>${initials(title.title)}</span>`}</div>
|
||||
@@ -192,7 +210,13 @@ function escapeAttr(value) {
|
||||
|
||||
els.searchInput.addEventListener("input", (event) => {
|
||||
state.query = event.target.value;
|
||||
renderTitleList();
|
||||
state.view = "collection";
|
||||
render();
|
||||
});
|
||||
|
||||
els.backBtn.addEventListener("click", () => {
|
||||
state.view = "collection";
|
||||
render();
|
||||
});
|
||||
|
||||
els.rescanBtn.addEventListener("click", async () => {
|
||||
|
||||
+86
-11
@@ -75,6 +75,14 @@ button:disabled {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
button.ghost {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
@@ -170,7 +178,7 @@ h3 {
|
||||
|
||||
.shell-note,
|
||||
.muted,
|
||||
.title-button__meta,
|
||||
.title-card__meta,
|
||||
.episode span,
|
||||
.summary p,
|
||||
label span {
|
||||
@@ -238,13 +246,36 @@ label {
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.title-list {
|
||||
.sidebar-note {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.sidebar-note__label {
|
||||
color: #ddd0ff;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.sidebar-note p {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.collection-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title-button {
|
||||
.title-card {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
@@ -258,25 +289,65 @@ label {
|
||||
transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
|
||||
}
|
||||
|
||||
.title-button:hover {
|
||||
.title-card:hover {
|
||||
border-color: var(--line-strong);
|
||||
background: linear-gradient(180deg, rgba(38, 28, 64, 0.94) 0%, rgba(19, 14, 32, 0.98) 100%);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.title-button.is-active {
|
||||
border-color: rgba(157, 123, 255, 0.58);
|
||||
background: linear-gradient(180deg, rgba(48, 36, 80, 0.96), rgba(20, 15, 35, 0.96));
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(157, 123, 255, 0.16),
|
||||
0 18px 38px rgba(5, 2, 15, 0.32);
|
||||
.title-card__poster {
|
||||
aspect-ratio: 3 / 4;
|
||||
position: relative;
|
||||
border: 1px solid rgba(157, 123, 255, 0.16);
|
||||
border-radius: 16px;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(157, 123, 255, 0.28), transparent 44%),
|
||||
linear-gradient(180deg, #221734 0%, #0c0915 100%);
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.title-button__name {
|
||||
.title-card__poster img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 0.16s ease;
|
||||
}
|
||||
|
||||
.title-card__poster.has-image img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.title-card__fallback {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #d8e5ef;
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
background: linear-gradient(180deg, rgba(157, 123, 255, 0.24), rgba(9, 7, 15, 0.18));
|
||||
}
|
||||
|
||||
.title-card__poster.has-image .title-card__fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.title-card__name {
|
||||
font-weight: 800;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.title-card__meta {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.toolbar,
|
||||
.details,
|
||||
.empty,
|
||||
@@ -486,4 +557,8 @@ video {
|
||||
.summary h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.collection-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user