zsbqb-static 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/app.css +16 -11
- package/js/app.js +77 -12
- package/js/catalog-search.js +6 -1
- package/js/query-app.js +41 -7
- package/note-preview/note-preview.min.js +1 -1
- package/package.json +2 -2
package/css/app.css
CHANGED
|
@@ -804,21 +804,26 @@ th { color: var(--muted); font-weight: 600; font-size: 13px; background: var(--b
|
|
|
804
804
|
align-items: start;
|
|
805
805
|
}
|
|
806
806
|
.alpha-rail {
|
|
807
|
-
/* 热区相对 28px
|
|
807
|
+
/* 热区相对 28px 栏宽向外扩一截,便于鼠标/触屏滑动选字母。
|
|
808
|
+
高度由 JS 按「当前位置到视口底」写入 --alpha-rail-fit,避免 100vh
|
|
809
|
+
从标题下方起算把字母拉长,点 B 实际打到 G。 */
|
|
808
810
|
--alpha-rail-pad-start: 14px;
|
|
809
811
|
--alpha-rail-pad-end: 20px;
|
|
810
812
|
--alpha-rail-pad-y: 10px;
|
|
811
813
|
position: sticky;
|
|
812
|
-
top:
|
|
814
|
+
top: 8px;
|
|
813
815
|
z-index: 15;
|
|
814
816
|
display: flex;
|
|
815
817
|
flex-direction: column;
|
|
816
|
-
|
|
818
|
+
justify-content: space-between;
|
|
819
|
+
gap: 0;
|
|
817
820
|
width: 28px;
|
|
818
|
-
box-sizing:
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
821
|
+
box-sizing: border-box;
|
|
822
|
+
height: var(--alpha-rail-fit, auto);
|
|
823
|
+
max-height: calc(100vh - 16px);
|
|
824
|
+
max-height: calc(100svh - 16px);
|
|
825
|
+
max-height: calc(100dvh - 16px);
|
|
826
|
+
overflow: hidden;
|
|
822
827
|
overscroll-behavior: contain;
|
|
823
828
|
padding: var(--alpha-rail-pad-y) var(--alpha-rail-pad-end) var(--alpha-rail-pad-y) var(--alpha-rail-pad-start);
|
|
824
829
|
margin: calc(var(--alpha-rail-pad-y) * -1) calc(var(--alpha-rail-pad-end) * -1) calc(var(--alpha-rail-pad-y) * -1) calc(var(--alpha-rail-pad-start) * -1);
|
|
@@ -846,17 +851,17 @@ th { color: var(--muted); font-weight: 600; font-size: 13px; background: var(--b
|
|
|
846
851
|
display: grid;
|
|
847
852
|
place-items: center;
|
|
848
853
|
width: 28px;
|
|
849
|
-
|
|
850
|
-
|
|
854
|
+
flex: 1 1 auto;
|
|
855
|
+
min-height: 1em;
|
|
856
|
+
padding: 0;
|
|
851
857
|
border-radius: 5px;
|
|
852
858
|
color: var(--muted);
|
|
853
|
-
font-size: 13px;
|
|
859
|
+
font-size: clamp(8px, calc((var(--alpha-rail-fit, 70svh) - 20px) / 36), 13px);
|
|
854
860
|
font-weight: 600;
|
|
855
861
|
font-family: var(--font-sans);
|
|
856
862
|
font-variant-numeric: tabular-nums;
|
|
857
863
|
line-height: 1;
|
|
858
864
|
letter-spacing: 0;
|
|
859
|
-
flex: 0 0 auto;
|
|
860
865
|
}
|
|
861
866
|
.alpha-rail__item:hover,
|
|
862
867
|
.alpha-rail__item.is-scrub {
|
package/js/app.js
CHANGED
|
@@ -1416,11 +1416,11 @@ function bindClientQueryFilters() {
|
|
|
1416
1416
|
|
|
1417
1417
|
window.__setQueryLetterFilter = function (label) {
|
|
1418
1418
|
const next = (label || "").trim();
|
|
1419
|
-
if (
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1419
|
+
if (!next) return;
|
|
1420
|
+
const sameLetter = filterState.letter.toLowerCase() === next.toLowerCase();
|
|
1421
|
+
const qDisplay = (searchInput?.value || filterState.q || "").trim();
|
|
1422
|
+
if (sameLetter && !qDisplay) return;
|
|
1423
|
+
filterState.letter = next;
|
|
1424
1424
|
// 选字母时清空搜索关键词
|
|
1425
1425
|
if (searchInput) {
|
|
1426
1426
|
searchInput.value = "";
|
|
@@ -1561,9 +1561,35 @@ function bindAlphaRail() {
|
|
|
1561
1561
|
|
|
1562
1562
|
let dragging = false;
|
|
1563
1563
|
let current = null;
|
|
1564
|
+
let downItem = null;
|
|
1565
|
+
let downY = 0;
|
|
1564
1566
|
let navigating = false;
|
|
1567
|
+
let pointerHandled = false;
|
|
1568
|
+
let fitFrame = 0;
|
|
1569
|
+
|
|
1570
|
+
function fitRail() {
|
|
1571
|
+
const top = rail.getBoundingClientRect().top;
|
|
1572
|
+
const fit = Math.max(280, Math.round(window.innerHeight - top - 8));
|
|
1573
|
+
const next = `${fit}px`;
|
|
1574
|
+
if (rail.style.getPropertyValue("--alpha-rail-fit") !== next) {
|
|
1575
|
+
rail.style.setProperty("--alpha-rail-fit", next);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function requestFit() {
|
|
1580
|
+
if (fitFrame) return;
|
|
1581
|
+
fitFrame = window.requestAnimationFrame(() => {
|
|
1582
|
+
fitFrame = 0;
|
|
1583
|
+
fitRail();
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
function letterOf(item) {
|
|
1588
|
+
if (!item) return "";
|
|
1589
|
+
return (item.getAttribute("data-letter") || item.textContent || "").trim();
|
|
1590
|
+
}
|
|
1565
1591
|
|
|
1566
|
-
function
|
|
1592
|
+
function pickItemByY(clientY) {
|
|
1567
1593
|
let best = null;
|
|
1568
1594
|
let bestDist = Infinity;
|
|
1569
1595
|
for (const item of items) {
|
|
@@ -1579,6 +1605,29 @@ function bindAlphaRail() {
|
|
|
1579
1605
|
return best;
|
|
1580
1606
|
}
|
|
1581
1607
|
|
|
1608
|
+
function itemFromPoint(clientX, clientY) {
|
|
1609
|
+
const stack =
|
|
1610
|
+
typeof document.elementsFromPoint === "function"
|
|
1611
|
+
? document.elementsFromPoint(clientX, clientY)
|
|
1612
|
+
: [document.elementFromPoint(clientX, clientY)];
|
|
1613
|
+
for (const node of stack) {
|
|
1614
|
+
if (!node || !node.closest) continue;
|
|
1615
|
+
const item = node.closest("[data-alpha-item]");
|
|
1616
|
+
if (item && rail.contains(item)) return item;
|
|
1617
|
+
}
|
|
1618
|
+
return pickItemByY(clientY);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function itemFromEvent(event) {
|
|
1622
|
+
const raw = event.target;
|
|
1623
|
+
const el = raw && raw.nodeType === 1 ? raw : raw && raw.parentElement;
|
|
1624
|
+
if (el && el.closest) {
|
|
1625
|
+
const item = el.closest("[data-alpha-item]");
|
|
1626
|
+
if (item && rail.contains(item)) return item;
|
|
1627
|
+
}
|
|
1628
|
+
return itemFromPoint(event.clientX, event.clientY);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1582
1631
|
function setScrub(item) {
|
|
1583
1632
|
if (current === item) return;
|
|
1584
1633
|
current = item;
|
|
@@ -1592,9 +1641,9 @@ function bindAlphaRail() {
|
|
|
1592
1641
|
|
|
1593
1642
|
function goTo(item) {
|
|
1594
1643
|
if (!item || navigating) return;
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
window.__setQueryLetterFilter
|
|
1644
|
+
const label = letterOf(item);
|
|
1645
|
+
if (typeof window.__setQueryLetterFilter === "function") {
|
|
1646
|
+
window.__setQueryLetterFilter(label);
|
|
1598
1647
|
return;
|
|
1599
1648
|
}
|
|
1600
1649
|
const next = item.href;
|
|
@@ -1606,7 +1655,9 @@ function bindAlphaRail() {
|
|
|
1606
1655
|
function onPointerDown(event) {
|
|
1607
1656
|
if (event.pointerType === "mouse" && event.button !== 0) return;
|
|
1608
1657
|
dragging = true;
|
|
1609
|
-
|
|
1658
|
+
downY = event.clientY;
|
|
1659
|
+
downItem = itemFromEvent(event);
|
|
1660
|
+
setScrub(downItem);
|
|
1610
1661
|
try {
|
|
1611
1662
|
rail.setPointerCapture(event.pointerId);
|
|
1612
1663
|
} catch (err) {
|
|
@@ -1617,20 +1668,27 @@ function bindAlphaRail() {
|
|
|
1617
1668
|
|
|
1618
1669
|
function onPointerMove(event) {
|
|
1619
1670
|
if (!dragging) return;
|
|
1620
|
-
setScrub(
|
|
1671
|
+
setScrub(itemFromPoint(event.clientX, event.clientY));
|
|
1621
1672
|
event.preventDefault();
|
|
1622
1673
|
}
|
|
1623
1674
|
|
|
1624
1675
|
function onPointerUp(event) {
|
|
1625
1676
|
if (!dragging) return;
|
|
1626
1677
|
dragging = false;
|
|
1627
|
-
const
|
|
1678
|
+
const moved = Math.abs(event.clientY - downY) > 8;
|
|
1679
|
+
const target = moved ? itemFromPoint(event.clientX, event.clientY) || current : downItem || itemFromEvent(event);
|
|
1680
|
+
downItem = null;
|
|
1628
1681
|
clearScrub();
|
|
1682
|
+
pointerHandled = true;
|
|
1629
1683
|
goTo(target);
|
|
1684
|
+
window.setTimeout(() => {
|
|
1685
|
+
pointerHandled = false;
|
|
1686
|
+
}, 400);
|
|
1630
1687
|
}
|
|
1631
1688
|
|
|
1632
1689
|
function onPointerCancel() {
|
|
1633
1690
|
dragging = false;
|
|
1691
|
+
downItem = null;
|
|
1634
1692
|
clearScrub();
|
|
1635
1693
|
}
|
|
1636
1694
|
|
|
@@ -1642,8 +1700,15 @@ function bindAlphaRail() {
|
|
|
1642
1700
|
items.forEach((item) => {
|
|
1643
1701
|
item.addEventListener("click", (event) => {
|
|
1644
1702
|
event.preventDefault();
|
|
1703
|
+
if (pointerHandled) return;
|
|
1704
|
+
goTo(item);
|
|
1645
1705
|
});
|
|
1646
1706
|
});
|
|
1707
|
+
|
|
1708
|
+
window.__fitQueryAlphaRail = fitRail;
|
|
1709
|
+
fitRail();
|
|
1710
|
+
window.addEventListener("resize", requestFit);
|
|
1711
|
+
window.addEventListener("scroll", requestFit, { passive: true });
|
|
1647
1712
|
}
|
|
1648
1713
|
|
|
1649
1714
|
function bindSearchClear() {
|
package/js/catalog-search.js
CHANGED
|
@@ -263,10 +263,15 @@
|
|
|
263
263
|
const letter = String((filters && filters.letter) || "").trim();
|
|
264
264
|
const bpmBucket = String((filters && filters.bpmBucket) || "").trim();
|
|
265
265
|
const bpmExact = (filters && filters.bpmExact) || "";
|
|
266
|
+
const browseDefault = !filters || filters.browseDefault !== false;
|
|
267
|
+
// 与 search_index/catalog.search_catalog 一致:无关键词、无字母/星级/BPM 时只展示 # 桶。
|
|
268
|
+
const browseOnly = browseDefault && !needle && !letter && !star && !bpmBucket && !String(bpmExact || "").trim();
|
|
266
269
|
const exact = [];
|
|
267
270
|
list.forEach((entry) => {
|
|
268
271
|
if (star && String(entry.star) !== star) return;
|
|
269
|
-
if (
|
|
272
|
+
if (browseOnly) {
|
|
273
|
+
if (!entry.title_lead_non_zh_en) return;
|
|
274
|
+
} else if (!letterMatches(entry, letter)) return;
|
|
270
275
|
if (!bpmMatches(entry, bpmBucket, bpmExact)) return;
|
|
271
276
|
if (!needle) {
|
|
272
277
|
exact.push({ entry, score: 0, fuzzy: false });
|
package/js/query-app.js
CHANGED
|
@@ -185,6 +185,30 @@
|
|
|
185
185
|
>${inner}</button>`;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
function letterHintLabel(letter) {
|
|
189
|
+
const key = String(letter || "").trim();
|
|
190
|
+
if (!key) return "";
|
|
191
|
+
return key === "#" ? "特殊" : key.toUpperCase();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function syncLetterHint(letter) {
|
|
195
|
+
const el = document.querySelector("[data-query-letter-hint]");
|
|
196
|
+
if (!el) return;
|
|
197
|
+
const label = letterHintLabel(letter);
|
|
198
|
+
el.textContent = label ? `${label}开头` : "";
|
|
199
|
+
el.hidden = !label;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function syncAlphaRail(letter) {
|
|
203
|
+
const key = String(letter || "").trim().toLowerCase();
|
|
204
|
+
document.querySelectorAll("[data-alpha-item]").forEach((el) => {
|
|
205
|
+
const active = Boolean(key) && el.textContent.trim().toLowerCase() === key;
|
|
206
|
+
el.classList.toggle("is-on", active);
|
|
207
|
+
if (active) el.setAttribute("aria-current", "true");
|
|
208
|
+
else el.removeAttribute("aria-current");
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
188
212
|
function syncHint(visibleCount) {
|
|
189
213
|
if (countEl) countEl.textContent = String(visibleCount);
|
|
190
214
|
const qDisplay = (searchInput && searchInput.value ? searchInput.value : filterState.q || "").trim();
|
|
@@ -192,6 +216,7 @@
|
|
|
192
216
|
qHint.textContent = qDisplay ? `[${qDisplay}]` : "";
|
|
193
217
|
qHint.hidden = !qDisplay;
|
|
194
218
|
}
|
|
219
|
+
syncLetterHint(qDisplay ? "" : filterState.letter);
|
|
195
220
|
if (hintTail) {
|
|
196
221
|
if (fuzzySuggest && qDisplay) {
|
|
197
222
|
hintTail.textContent = "。未精确匹配,是否你想找以下结果?";
|
|
@@ -245,11 +270,7 @@
|
|
|
245
270
|
}
|
|
246
271
|
syncHint(ranked.length);
|
|
247
272
|
syncLetterField(filterState.letter);
|
|
248
|
-
|
|
249
|
-
const active =
|
|
250
|
-
filterState.letter && el.textContent.trim().toLowerCase() === filterState.letter.toLowerCase();
|
|
251
|
-
el.classList.toggle("is-on", Boolean(active));
|
|
252
|
-
});
|
|
273
|
+
syncAlphaRail(filterState.letter);
|
|
253
274
|
const params = new URLSearchParams(window.location.search);
|
|
254
275
|
if (family) params.set("family", family);
|
|
255
276
|
if (filterState.q) params.set("q", filterState.q);
|
|
@@ -276,8 +297,11 @@
|
|
|
276
297
|
|
|
277
298
|
window.__setQueryLetterFilter = function (label) {
|
|
278
299
|
const next = (label || "").trim();
|
|
279
|
-
if (
|
|
280
|
-
|
|
300
|
+
if (!next) return;
|
|
301
|
+
const sameLetter = filterState.letter.toLowerCase() === next.toLowerCase();
|
|
302
|
+
const qDisplay = (searchInput && searchInput.value ? searchInput.value : filterState.q || "").trim();
|
|
303
|
+
if (sameLetter && !qDisplay) return;
|
|
304
|
+
filterState.letter = next;
|
|
281
305
|
if (searchInput) {
|
|
282
306
|
searchInput.value = "";
|
|
283
307
|
filterState.q = "";
|
|
@@ -285,6 +309,16 @@
|
|
|
285
309
|
if (clearBtn) clearBtn.classList.remove("is-on");
|
|
286
310
|
}
|
|
287
311
|
applyFilters();
|
|
312
|
+
const toolbar = document.querySelector(".query-toolbar");
|
|
313
|
+
if (toolbar) {
|
|
314
|
+
const nextTop = toolbar.getBoundingClientRect().top + window.scrollY;
|
|
315
|
+
if (Math.abs(window.scrollY - nextTop) > 8) {
|
|
316
|
+
window.scrollTo(0, Math.max(0, nextTop));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
window.requestAnimationFrame(function () {
|
|
320
|
+
if (typeof window.__fitQueryAlphaRail === "function") window.__fitQueryAlphaRail();
|
|
321
|
+
});
|
|
288
322
|
};
|
|
289
323
|
|
|
290
324
|
window.__queryLoadFamily = function (nextFamily, nextStar) {
|