vitepress-plugin-filelist 0.7.2 → 0.8.0
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/README.md +18 -300
- package/dist/client/browser/index.d.ts +15 -2
- package/dist/client/browser/index.js +379 -128
- package/dist/client/ssr/index.d.ts +15 -2
- package/dist/client/ssr/index.js +208 -19
- package/dist/client/style.css +153 -29
- package/package.json +2 -2
|
@@ -136,12 +136,25 @@ interface FileListLabels {
|
|
|
136
136
|
/** `{limit}` */
|
|
137
137
|
zipBlockText: string;
|
|
138
138
|
gotIt: string;
|
|
139
|
-
/** Last row of a long folder. `{count}` */
|
|
139
|
+
/** Last row of a long folder: how many are not shown yet. `{count}` */
|
|
140
140
|
moreRemaining: string;
|
|
141
|
+
/** Pinned folder row: `{shown}` of `{total}`. */
|
|
142
|
+
moreShown: string;
|
|
143
|
+
/** “Show all”, short form for narrow lists. */
|
|
144
|
+
showAllShort: string;
|
|
145
|
+
/** Show the next page. `{count}` */
|
|
146
|
+
showNext: string;
|
|
147
|
+
/** Show the next page, short form for narrow lists. `{count}` */
|
|
148
|
+
showNextShort: string;
|
|
149
|
+
/** No longer shown (“Show next” replaces it); kept for existing overrides. */
|
|
141
150
|
showMore: string;
|
|
142
151
|
showAll: string;
|
|
143
152
|
/** Fewer than a page left. `{count}` */
|
|
144
153
|
showRest: string;
|
|
154
|
+
/** Pinned folder row: back to the first page. */
|
|
155
|
+
showLess: string;
|
|
156
|
+
/** Pinned folder row once everything is shown. `{count}` */
|
|
157
|
+
allShown: string;
|
|
145
158
|
}
|
|
146
159
|
interface FileListSource {
|
|
147
160
|
/** OpenList path, directory or single file. */
|
|
@@ -174,7 +187,7 @@ interface FileListProps {
|
|
|
174
187
|
*/
|
|
175
188
|
rememberPassword?: boolean;
|
|
176
189
|
/**
|
|
177
|
-
* Rows shown per folder before “Show more”. Defaults to `
|
|
190
|
+
* Rows shown per folder before “Show more”. Defaults to `15`; `0` shows
|
|
178
191
|
* everything. Data is still loaded in full; only rendering is limited.
|
|
179
192
|
*/
|
|
180
193
|
pageSize?: number;
|
package/dist/client/ssr/index.js
CHANGED
|
@@ -353,6 +353,56 @@ function createOpenListApi(base, token) {
|
|
|
353
353
|
}
|
|
354
354
|
};
|
|
355
355
|
}
|
|
356
|
+
// src/client/demoSamples.ts
|
|
357
|
+
const SAMPLE_FOLDER = "/示例文件";
|
|
358
|
+
const SAMPLES = {
|
|
359
|
+
"使用说明.md": {
|
|
360
|
+
type: "text/markdown",
|
|
361
|
+
body: "# 使用说明\n\n这是 **vitepress-plugin-filelist** 演示数据自带的 Markdown 文件。\n\n## 试一试\n\n- 点右上角“渲染”,按 VitePress 正文样式显示\n- 用 ← / → 切换到同一目录里的其它文件\n- 勾选“示例文件”文件夹,点“下载 ZIP”打包\n\n| 文件 | 类型 |\n| --- | --- |\n| 讲义.pdf | PDF |\n| 课程海报.svg | 图片 |\n| 网页示例.html | 网页 |\n\n> 接入 OpenList 后,这里显示的就是网盘里的真实文件。\n\n更多说明见 [文档站](https://nrjycyd.github.io/vitepress-plugin-filelist/)。\n"
|
|
362
|
+
},
|
|
363
|
+
"网页示例.html": {
|
|
364
|
+
type: "text/html",
|
|
365
|
+
body: "<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"utf-8\">\n<title>网页示例</title>\n<style>\n body { font-family: system-ui, sans-serif; margin: 24px; line-height: 1.6; color: #1f2937; }\n h1 { color: #3451b2; }\n .card { padding: 12px 16px; border: 1px solid #d1d5db; border-radius: 8px; }\n</style>\n</head>\n<body>\n<h1>网页示例</h1>\n<p>切换到“渲染”后,这个网页在沙盒中显示:带样式,但脚本不会运行。</p>\n<div class=\"card\">下面的按钮由脚本控制,在沙盒里点了没有反应。</div>\n<p><button onclick=\"alert('脚本运行了')\">点我</button></p>\n<script>document.body.dataset.ran = 'yes'<\/script>\n</body>\n</html>\n"
|
|
366
|
+
},
|
|
367
|
+
"课程海报.svg": {
|
|
368
|
+
type: "image/svg+xml",
|
|
369
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"800\" height=\"450\" viewBox=\"0 0 800 450\">\n<defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"#3451b2\"/><stop offset=\"1\" stop-color=\"#5672cd\"/></linearGradient></defs>\n<rect width=\"800\" height=\"450\" fill=\"url(#g)\"/>\n<circle cx=\"660\" cy=\"90\" r=\"120\" fill=\"#ffffff\" opacity=\"0.08\"/>\n<circle cx=\"120\" cy=\"420\" r=\"160\" fill=\"#ffffff\" opacity=\"0.06\"/>\n<text x=\"64\" y=\"190\" font-family=\"system-ui, sans-serif\" font-size=\"56\" font-weight=\"700\" fill=\"#ffffff\">课程海报</text>\n<text x=\"64\" y=\"250\" font-family=\"system-ui, sans-serif\" font-size=\"26\" fill=\"#dfe5ff\">线性代数 · 2026 秋季学期</text>\n<text x=\"64\" y=\"380\" font-family=\"system-ui, sans-serif\" font-size=\"18\" fill=\"#dfe5ff\">vitepress-plugin-filelist 演示图片</text>\n</svg>\n"
|
|
370
|
+
},
|
|
371
|
+
"讲义.pdf": {
|
|
372
|
+
type: "application/pdf",
|
|
373
|
+
body: "%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>\nendobj\n4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n5 0 obj\n<< /Length 257 >>\nstream\nBT\n/F1 20 Tf\n72 760 Td\n(FileList sample PDF) Tj\n/F1 12 Tf\n0 -22 Td\n() Tj\n0 -22 Td\n(This small PDF ships with the demo data of) Tj\n0 -22 Td\n(vitepress-plugin-filelist, so preview, download) Tj\n0 -22 Td\n(and ZIP can be tried without an OpenList server.) Tj\nET\nendstream\nendobj\nxref\n0 6\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \n0000000241 00000 n \n0000000311 00000 n \ntrailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n619\n%%EOF\n"
|
|
374
|
+
},
|
|
375
|
+
"课程表.txt": {
|
|
376
|
+
type: "text/plain",
|
|
377
|
+
body: "课程表(演示文本)\n\n周一 08:00 线性代数 教 301\n周三 10:00 线性代数习题课 教 205\n周五 14:00 答疑 图书馆 2 楼\n\n纯文本文件只读取前 1 MB,按原样显示。\n"
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const SAMPLE_NAMES = Object.keys(SAMPLES);
|
|
381
|
+
/** Size in bytes of a sample, as UTF-8. */
|
|
382
|
+
function sampleSize(name) {
|
|
383
|
+
return new TextEncoder().encode(SAMPLES[name].body).byteLength;
|
|
384
|
+
}
|
|
385
|
+
function nameOf(path) {
|
|
386
|
+
const name = path.slice(6);
|
|
387
|
+
return path.startsWith(`/示例文件/`) && name in SAMPLES ? name : void 0;
|
|
388
|
+
}
|
|
389
|
+
/** Whether a demo path is one of the sample files (which have a real URL). */
|
|
390
|
+
function isSample(path) {
|
|
391
|
+
return nameOf(path) !== void 0;
|
|
392
|
+
}
|
|
393
|
+
const urls = /* @__PURE__ */ new Map();
|
|
394
|
+
/** A blob: URL for a sample file, `''` for any other path or outside a browser. */
|
|
395
|
+
function sampleUrl(path) {
|
|
396
|
+
const name = nameOf(path);
|
|
397
|
+
if (!name || typeof URL.createObjectURL !== "function") return "";
|
|
398
|
+
let url = urls.get(name);
|
|
399
|
+
if (!url) {
|
|
400
|
+
const { type, body } = SAMPLES[name];
|
|
401
|
+
url = URL.createObjectURL(new Blob([body], { type: `${type};charset=utf-8` }));
|
|
402
|
+
urls.set(name, url);
|
|
403
|
+
}
|
|
404
|
+
return url;
|
|
405
|
+
}
|
|
356
406
|
// src/client/demo.ts
|
|
357
407
|
/** Built-in sample data, used when no `base` is given. */
|
|
358
408
|
const TREE = {
|
|
@@ -392,8 +442,32 @@ const TREE = {
|
|
|
392
442
|
size: 0,
|
|
393
443
|
is_dir: true,
|
|
394
444
|
modified: "2026-10-20T18:00:00+08:00"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "示例文件",
|
|
448
|
+
size: 0,
|
|
449
|
+
is_dir: true,
|
|
450
|
+
modified: "2026-11-11T09:00:00+08:00"
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "照片(260 张)",
|
|
454
|
+
size: 0,
|
|
455
|
+
is_dir: true,
|
|
456
|
+
modified: "2026-10-01T12:00:00+08:00"
|
|
395
457
|
}
|
|
396
458
|
],
|
|
459
|
+
[SAMPLE_FOLDER]: SAMPLE_NAMES.map((name) => ({
|
|
460
|
+
name,
|
|
461
|
+
size: sampleSize(name),
|
|
462
|
+
is_dir: false,
|
|
463
|
+
modified: "2026-11-11T09:00:00+08:00"
|
|
464
|
+
})),
|
|
465
|
+
"/照片(260 张)": Array.from({ length: 260 }, (_, index) => ({
|
|
466
|
+
name: `IMG_${String(index + 1).padStart(4, "0")}.jpg`,
|
|
467
|
+
size: 2e6 + index * 7919 % 15e5,
|
|
468
|
+
is_dir: false,
|
|
469
|
+
modified: "2026-10-01T12:00:00+08:00"
|
|
470
|
+
})),
|
|
397
471
|
"/assignments": [{
|
|
398
472
|
name: "线代作业参考答案",
|
|
399
473
|
size: 0,
|
|
@@ -514,7 +588,7 @@ function guard(path, password) {
|
|
|
514
588
|
function parentOf(path) {
|
|
515
589
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
516
590
|
}
|
|
517
|
-
/** Demo OpenList: same API, no network; files have
|
|
591
|
+
/** Demo OpenList: same API, no network; only the sample files have a URL. */
|
|
518
592
|
function createDemoApi(delay = 220) {
|
|
519
593
|
return {
|
|
520
594
|
async list(path, password) {
|
|
@@ -533,12 +607,14 @@ function createDemoApi(delay = 220) {
|
|
|
533
607
|
title: "",
|
|
534
608
|
iframePreviews: ""
|
|
535
609
|
}),
|
|
536
|
-
fileUrl: async () =>
|
|
610
|
+
fileUrl: async (file) => sampleUrl(file.path),
|
|
537
611
|
directUrl: () => "",
|
|
538
|
-
isAttachmentUrl: () =>
|
|
612
|
+
isAttachmentUrl: (url) => url.startsWith("blob:"),
|
|
539
613
|
clearCache: () => {}
|
|
540
614
|
};
|
|
541
615
|
}
|
|
616
|
+
/** Whether a demo file can really be previewed, downloaded and zipped. */
|
|
617
|
+
const isDemoFile = isSample;
|
|
542
618
|
// src/client/ConfirmDialog.vue
|
|
543
619
|
const _sfc_main$3 = /*@__PURE__*/ defineComponent({
|
|
544
620
|
__name: "ConfirmDialog",
|
|
@@ -754,6 +830,14 @@ const ICONS = {
|
|
|
754
830
|
stroke: 2.2,
|
|
755
831
|
paths: ["m15 6-6 6 6 6"]
|
|
756
832
|
},
|
|
833
|
+
down: {
|
|
834
|
+
stroke: 2.4,
|
|
835
|
+
paths: ["m6 9 6 6 6-6"]
|
|
836
|
+
},
|
|
837
|
+
up: {
|
|
838
|
+
stroke: 2.4,
|
|
839
|
+
paths: ["m18 15-6-6-6 6"]
|
|
840
|
+
},
|
|
757
841
|
zoomIn: {
|
|
758
842
|
stroke: 2,
|
|
759
843
|
paths: [
|
|
@@ -901,7 +985,13 @@ const fileListLocales = {
|
|
|
901
985
|
moreRemaining: "还有 {count} 项",
|
|
902
986
|
showMore: "显示更多",
|
|
903
987
|
showAll: "全部显示",
|
|
904
|
-
showRest: "显示剩余 {count} 项"
|
|
988
|
+
showRest: "显示剩余 {count} 项",
|
|
989
|
+
showLess: "收起",
|
|
990
|
+
allShown: "已显示全部 {count} 项",
|
|
991
|
+
moreShown: "已显示 {shown} / {total} 项",
|
|
992
|
+
showAllShort: "全部",
|
|
993
|
+
showNext: "再显示 {count} 项",
|
|
994
|
+
showNextShort: "+{count}"
|
|
905
995
|
},
|
|
906
996
|
"en-US": {
|
|
907
997
|
allFiles: "All files",
|
|
@@ -997,7 +1087,13 @@ const fileListLocales = {
|
|
|
997
1087
|
moreRemaining: "{count} more",
|
|
998
1088
|
showMore: "Show more",
|
|
999
1089
|
showAll: "Show all",
|
|
1000
|
-
showRest: "Show the remaining {count}"
|
|
1090
|
+
showRest: "Show the remaining {count}",
|
|
1091
|
+
showLess: "Show less",
|
|
1092
|
+
allShown: "All {count} shown",
|
|
1093
|
+
moreShown: "Showing {shown} of {total}",
|
|
1094
|
+
showAllShort: "All",
|
|
1095
|
+
showNext: "Show {count} more",
|
|
1096
|
+
showNextShort: "+{count}"
|
|
1001
1097
|
}
|
|
1002
1098
|
};
|
|
1003
1099
|
function formatLabel(template, values = {}) {
|
|
@@ -1565,7 +1661,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1565
1661
|
type: Boolean,
|
|
1566
1662
|
default: true
|
|
1567
1663
|
},
|
|
1568
|
-
pageSize: { default:
|
|
1664
|
+
pageSize: { default: 15 },
|
|
1569
1665
|
deepSearchLimit: { default: 500 },
|
|
1570
1666
|
deepSearch: { default: "manual" },
|
|
1571
1667
|
zipWarnSize: {},
|
|
@@ -1831,18 +1927,23 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1831
1927
|
applySort(key, direction !== "desc");
|
|
1832
1928
|
}
|
|
1833
1929
|
});
|
|
1834
|
-
/**
|
|
1930
|
+
/** The row closing a long folder while its children are cut at `pageSize`. */
|
|
1835
1931
|
const shown = reactive(/* @__PURE__ */ new Map());
|
|
1932
|
+
function pageSizeOf() {
|
|
1933
|
+
return Math.max(0, Math.floor(props.pageSize ?? 15));
|
|
1934
|
+
}
|
|
1836
1935
|
function limitOf(key) {
|
|
1837
|
-
const size =
|
|
1936
|
+
const size = pageSizeOf();
|
|
1838
1937
|
return size ? shown.get(key) ?? size : Number.POSITIVE_INFINITY;
|
|
1839
1938
|
}
|
|
1840
1939
|
function isMore(row) {
|
|
1841
1940
|
return "more" in row;
|
|
1842
1941
|
}
|
|
1843
|
-
const
|
|
1942
|
+
const layout = computed(() => {
|
|
1844
1943
|
const out = [];
|
|
1944
|
+
const long = [];
|
|
1845
1945
|
const query = keyword.value.trim().toLowerCase();
|
|
1946
|
+
const size = pageSizeOf();
|
|
1846
1947
|
const matches = (node) => !query || node.name.toLowerCase().includes(query) || node.is_dir && (node.children.some(matches) || deepMode.value && !!node.lock);
|
|
1847
1948
|
const walk = (nodes, key, depth) => {
|
|
1848
1949
|
const list = nodes.filter(matches);
|
|
@@ -1857,10 +1958,70 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
1857
1958
|
depth,
|
|
1858
1959
|
remaining: list.length - limit
|
|
1859
1960
|
});
|
|
1961
|
+
let end = out.length - 1;
|
|
1962
|
+
while (end >= 0 && isMore(out[end])) end -= 1;
|
|
1963
|
+
const last = out[end];
|
|
1964
|
+
if (!query && size > 0 && limit > size && last && !isMore(last)) long.push({
|
|
1965
|
+
key,
|
|
1966
|
+
depth: depth - 1,
|
|
1967
|
+
shown: limit,
|
|
1968
|
+
total: list.length,
|
|
1969
|
+
last: last.path
|
|
1970
|
+
});
|
|
1860
1971
|
};
|
|
1861
1972
|
walk(roots.value, props.sources.length ? "" : props.path, 0);
|
|
1862
|
-
return
|
|
1973
|
+
return {
|
|
1974
|
+
rows: out,
|
|
1975
|
+
long
|
|
1976
|
+
};
|
|
1977
|
+
});
|
|
1978
|
+
const rows = computed(() => layout.value.rows);
|
|
1979
|
+
const pinned = ref(null);
|
|
1980
|
+
/** Height of the sticky selection bar above the pinned row (narrow lists only). */
|
|
1981
|
+
const pinOffset = ref(0);
|
|
1982
|
+
function rowElement(path) {
|
|
1983
|
+
return [...rootEl.value?.querySelectorAll("[data-path]") ?? []].find((el) => el.dataset.path === path);
|
|
1984
|
+
}
|
|
1985
|
+
/** The pinned folder's name: its own, or for a single path's top level, the list's title. */
|
|
1986
|
+
const pinnedName = computed(() => {
|
|
1987
|
+
const folder = pinned.value;
|
|
1988
|
+
if (!folder) return "";
|
|
1989
|
+
if (folder.depth < 0) return props.title || folder.key.split("/").filter(Boolean).pop() || "/";
|
|
1990
|
+
const find = (nodes) => {
|
|
1991
|
+
for (const node of nodes) {
|
|
1992
|
+
if (node.path === folder.key) return node;
|
|
1993
|
+
const inner = isWithin(folder.key, node.path) ? find(node.children) : void 0;
|
|
1994
|
+
if (inner) return inner;
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
return find(roots.value)?.name ?? "";
|
|
1863
1998
|
});
|
|
1999
|
+
/** Pin the innermost long folder whose own row has scrolled away while its files are still on screen. */
|
|
2000
|
+
function updatePin() {
|
|
2001
|
+
const root = rootEl.value;
|
|
2002
|
+
if (!root) return;
|
|
2003
|
+
const bar = root.querySelector(".fl-bar");
|
|
2004
|
+
pinOffset.value = bar && getComputedStyle(bar).position === "sticky" ? bar.offsetHeight : 0;
|
|
2005
|
+
const top = stickyTop.value + pinOffset.value;
|
|
2006
|
+
const rowHeight = root.querySelector(".fl-head")?.offsetHeight ?? 40;
|
|
2007
|
+
let found = null;
|
|
2008
|
+
for (const folder of layout.value.long) {
|
|
2009
|
+
const head = folder.depth < 0 ? root.querySelector(".fl-head") : rowElement(folder.key);
|
|
2010
|
+
const last = rowElement(folder.last);
|
|
2011
|
+
if (!head || !last) continue;
|
|
2012
|
+
if (head.getBoundingClientRect().bottom <= top && last.getBoundingClientRect().bottom > top + rowHeight && (!found || folder.depth > found.depth)) found = folder;
|
|
2013
|
+
}
|
|
2014
|
+
pinned.value = found;
|
|
2015
|
+
}
|
|
2016
|
+
let pinFrame = 0;
|
|
2017
|
+
function schedulePin() {
|
|
2018
|
+
if (pinFrame) return;
|
|
2019
|
+
pinFrame = requestAnimationFrame(() => {
|
|
2020
|
+
pinFrame = 0;
|
|
2021
|
+
updatePin();
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
watch(layout, () => void nextTick(schedulePin));
|
|
1864
2025
|
const visible = computed(() => rows.value.filter((row) => !isMore(row)));
|
|
1865
2026
|
/** A name split around the filter keyword, for highlighting. */
|
|
1866
2027
|
function nameParts(name) {
|
|
@@ -2047,7 +2208,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2047
2208
|
}
|
|
2048
2209
|
async function download(node) {
|
|
2049
2210
|
if (node.is_dir) return;
|
|
2050
|
-
if (demo.value) {
|
|
2211
|
+
if (demo.value && !isDemoFile(node.path)) {
|
|
2051
2212
|
showToast(t("demoDownload", { name: node.name }));
|
|
2052
2213
|
return;
|
|
2053
2214
|
}
|
|
@@ -2151,7 +2312,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2151
2312
|
previewViewer.value = "";
|
|
2152
2313
|
previewError.value = "";
|
|
2153
2314
|
previewResolving.value = false;
|
|
2154
|
-
if (demo.value || !canPreview(node)) return;
|
|
2315
|
+
if (demo.value && !isDemoFile(node.path) || !canPreview(node)) return;
|
|
2155
2316
|
previewResolving.value = true;
|
|
2156
2317
|
try {
|
|
2157
2318
|
const url = await fileUrlOf(node);
|
|
@@ -2200,10 +2361,15 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2200
2361
|
loadSettings();
|
|
2201
2362
|
measureStickyTop();
|
|
2202
2363
|
window.addEventListener("resize", measureStickyTop, { passive: true });
|
|
2364
|
+
window.addEventListener("scroll", schedulePin, { passive: true });
|
|
2365
|
+
window.addEventListener("resize", schedulePin, { passive: true });
|
|
2203
2366
|
});
|
|
2204
2367
|
onBeforeUnmount(() => {
|
|
2205
2368
|
zipAbort?.abort();
|
|
2206
2369
|
window.removeEventListener("resize", measureStickyTop);
|
|
2370
|
+
window.removeEventListener("scroll", schedulePin);
|
|
2371
|
+
window.removeEventListener("resize", schedulePin);
|
|
2372
|
+
cancelAnimationFrame(pinFrame);
|
|
2207
2373
|
clearTimeout(toastTimer);
|
|
2208
2374
|
});
|
|
2209
2375
|
return (_ctx, _push, _parent, _attrs) => {
|
|
@@ -2287,7 +2453,30 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2287
2453
|
ssrRenderList(sortOptions.value, (option) => {
|
|
2288
2454
|
_push(`<option${ssrRenderAttr("value", option.value)}${ssrIncludeBooleanAttr(Array.isArray(sortValue.value) ? ssrLooseContain(sortValue.value, option.value) : ssrLooseEqual(sortValue.value, option.value)) ? " selected" : ""}>${ssrInterpolate(option.label)}</option>`);
|
|
2289
2455
|
});
|
|
2290
|
-
_push(`<!--]--></select></label></span></div>`);
|
|
2456
|
+
_push(`<!--]--></select></label></span></div><!-- While scrolling through a long folder, its row stays at the top with “show less”. --><div class="fl-pin-anchor" style="${ssrRenderStyle({ "--fl-pin-offset": `${pinOffset.value}px` })}">`);
|
|
2457
|
+
if (pinned.value) {
|
|
2458
|
+
_push(`<div class="fl-pin" style="${ssrRenderStyle({ "--fl-depth": Math.max(pinned.value.depth, 0) })}"><span class="fl-cell fl-cell-check"></span><span class="fl-cell fl-cell-name">`);
|
|
2459
|
+
if (pinned.value.depth >= 0) _push(`<span class="fl-indent" aria-hidden="true"></span>`);
|
|
2460
|
+
else _push(`<!---->`);
|
|
2461
|
+
if (pinned.value.depth >= 0) {
|
|
2462
|
+
_push(`<span class="fl-pin-arrow" aria-hidden="true">`);
|
|
2463
|
+
_push(ssrRenderComponent(_sfc_main$2, {
|
|
2464
|
+
name: "next",
|
|
2465
|
+
size: 14
|
|
2466
|
+
}, null, _parent));
|
|
2467
|
+
_push(`</span>`);
|
|
2468
|
+
} else _push(`<!---->`);
|
|
2469
|
+
_push(`<svg class="fl-folder-icon" viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path></svg><span class="fl-pin-name">${ssrInterpolate(pinnedName.value)}</span><span class="fl-pin-count">${ssrInterpolate(pinned.value.shown < pinned.value.total ? t("moreShown", {
|
|
2470
|
+
shown: pinned.value.shown,
|
|
2471
|
+
total: pinned.value.total
|
|
2472
|
+
}) : t("allShown", { count: pinned.value.total }))}</span></span><button class="fl-pin-less" type="button">${ssrInterpolate(t("showLess"))}`);
|
|
2473
|
+
_push(ssrRenderComponent(_sfc_main$2, {
|
|
2474
|
+
name: "up",
|
|
2475
|
+
size: 12
|
|
2476
|
+
}, null, _parent));
|
|
2477
|
+
_push(`</button></div>`);
|
|
2478
|
+
} else _push(`<!---->`);
|
|
2479
|
+
_push(`</div>`);
|
|
2291
2480
|
if (loadingRoot.value) _push(`<div class="fl-status">${ssrInterpolate(t("loading"))}</div>`);
|
|
2292
2481
|
else if (rootLock.value) {
|
|
2293
2482
|
_push(`<form class="${ssrRenderClass([{ "fl-unlock-error": rootLock.value.error }, "fl-unlock-root"])}">`);
|
|
@@ -2318,10 +2507,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2318
2507
|
ssrRenderList(rows.value, (node, index) => {
|
|
2319
2508
|
_push(`<!--[-->`);
|
|
2320
2509
|
if (isMore(node)) {
|
|
2321
|
-
_push(`<div class="fl-row fl-more" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><span class="fl-cell fl-cell-check"></span><span class="fl-cell fl-more-cell"
|
|
2322
|
-
if (node.remaining > (__props.pageSize || 0)) _push(`<!--[--><button class="fl-more-
|
|
2323
|
-
else _push(`<button class="fl-more-
|
|
2324
|
-
_push(`</span></div>`);
|
|
2510
|
+
_push(`<div class="fl-row fl-more" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><span class="fl-cell fl-cell-check"></span><span class="fl-cell fl-more-cell"><!-- Reads as one more row of the folder: “⋯” in the type column, the count where names start. --><span class="fl-indent" aria-hidden="true"></span><span class="fl-arrow-placeholder" aria-hidden="true"></span><span class="fl-more-tag" aria-hidden="true">⋯</span><span class="fl-more-count">${ssrInterpolate(t("moreRemaining", { count: node.remaining }))}</span><span class="fl-more-actions">`);
|
|
2511
|
+
if (node.remaining > (__props.pageSize || 0)) _push(`<!--[--><button class="fl-more-link" type="button"${ssrRenderAttr("aria-label", t("showNext", { count: __props.pageSize }))}><span class="fl-more-long">${ssrInterpolate(t("showNext", { count: __props.pageSize }))}</span><span class="fl-more-short" aria-hidden="true">${ssrInterpolate(t("showNextShort", { count: __props.pageSize }))}</span></button><button class="fl-more-link fl-more-all" type="button"${ssrRenderAttr("aria-label", t("showAll"))}><span class="fl-more-long">${ssrInterpolate(t("showAll"))}</span><span class="fl-more-short" aria-hidden="true">${ssrInterpolate(t("showAllShort"))}</span></button><!--]-->`);
|
|
2512
|
+
else _push(`<button class="fl-more-link fl-more-all" type="button">${ssrInterpolate(t("showRest", { count: node.remaining }))}</button>`);
|
|
2513
|
+
_push(`</span></span></div>`);
|
|
2325
2514
|
} else {
|
|
2326
2515
|
_push(`<div class="${ssrRenderClass([{
|
|
2327
2516
|
"fl-row-dir": node.is_dir,
|
|
@@ -2330,7 +2519,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2330
2519
|
"fl-row-locked": !!node.lock,
|
|
2331
2520
|
"fl-row-prompting": node.lock?.prompting,
|
|
2332
2521
|
"fl-row-error": node.lock?.prompting && !!node.lock.error
|
|
2333
|
-
}, "fl-row"])}" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"><label class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(!!node.lock || batchBusy.value) ? " disabled" : ""}${ssrRenderAttr("aria-label", t("selectItem", { name: node.name }))}${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></label><span class="fl-cell fl-cell-name"${ssrRenderAttr("title", node.isRoot ? node.path : void 0)}><span class="fl-indent" aria-hidden="true"></span>`);
|
|
2522
|
+
}, "fl-row"])}" style="${ssrRenderStyle({ "--fl-depth": node.depth })}"${ssrRenderAttr("data-path", node.path)}><label class="fl-cell fl-cell-check"><input type="checkbox"${ssrIncludeBooleanAttr(!!node.lock || batchBusy.value) ? " disabled" : ""}${ssrRenderAttr("aria-label", t("selectItem", { name: node.name }))}${ssrIncludeBooleanAttr(isChecked(node)) ? " checked" : ""}${ssrRenderAttr("indeterminate", isIndeterminate(node))}></label><span class="fl-cell fl-cell-name"${ssrRenderAttr("title", node.isRoot ? node.path : void 0)}><span class="fl-indent" aria-hidden="true"></span>`);
|
|
2334
2523
|
if (node.lock) {
|
|
2335
2524
|
_push(`<button class="fl-arrow fl-lock" type="button"${ssrRenderAttr("aria-label", t("unlockFolder", { name: node.name }))}${ssrRenderAttr("title", t("unlockFolder", { name: node.name }))}>`);
|
|
2336
2525
|
_push(ssrRenderComponent(_sfc_main$2, {
|
|
@@ -2463,7 +2652,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
2463
2652
|
viewer: previewViewer.value,
|
|
2464
2653
|
resolving: previewResolving.value,
|
|
2465
2654
|
error: previewError.value,
|
|
2466
|
-
demo: demo.value,
|
|
2655
|
+
demo: demo.value && !!previewNode.value && !unref(isDemoFile)(previewNode.value.path),
|
|
2467
2656
|
position: previewPosition.value,
|
|
2468
2657
|
total: previewPosition.value ? previewList.value.length : 0,
|
|
2469
2658
|
onClose: closePreview,
|
package/dist/client/style.css
CHANGED
|
@@ -826,47 +826,138 @@
|
|
|
826
826
|
/* ---------------- long folders ---------------- */
|
|
827
827
|
|
|
828
828
|
.fl-more {
|
|
829
|
-
|
|
829
|
+
color: var(--vp-c-text-2);
|
|
830
830
|
}
|
|
831
831
|
|
|
832
832
|
.fl-more-cell {
|
|
833
833
|
grid-column: 2 / -1;
|
|
834
|
-
|
|
834
|
+
/* the same gaps as a file's name cell, so the tag and count line up with its type and name */
|
|
835
|
+
gap: 8px;
|
|
835
836
|
align-self: stretch;
|
|
836
837
|
}
|
|
837
838
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
839
|
+
/* “⋯” in the file-type column: the size of .fl-type */
|
|
840
|
+
.fl-more-tag {
|
|
841
|
+
display: inline-flex;
|
|
842
|
+
flex: none;
|
|
843
|
+
align-items: center;
|
|
844
|
+
justify-content: center;
|
|
845
|
+
width: 34px;
|
|
846
|
+
height: 20px;
|
|
847
|
+
font-size: 13px;
|
|
848
|
+
font-weight: 700;
|
|
849
|
+
line-height: 1;
|
|
850
|
+
color: var(--vp-c-text-3);
|
|
851
|
+
letter-spacing: 1px;
|
|
852
|
+
background: var(--vp-c-default-soft);
|
|
853
|
+
border-radius: 4px;
|
|
841
854
|
}
|
|
842
855
|
|
|
843
856
|
.fl-more-count {
|
|
857
|
+
min-width: 0;
|
|
858
|
+
overflow: hidden;
|
|
844
859
|
font-size: 13px;
|
|
845
|
-
|
|
860
|
+
text-overflow: ellipsis;
|
|
846
861
|
white-space: nowrap;
|
|
847
862
|
}
|
|
848
863
|
|
|
849
|
-
|
|
864
|
+
/* the short forms are for narrow lists */
|
|
865
|
+
.fl-more-short {
|
|
866
|
+
display: none;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
.fl-more-actions {
|
|
870
|
+
display: flex;
|
|
871
|
+
flex: none;
|
|
872
|
+
gap: 2px;
|
|
873
|
+
align-items: center;
|
|
874
|
+
margin-left: auto;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.fl-more-link {
|
|
850
878
|
height: 28px;
|
|
851
|
-
padding: 0
|
|
879
|
+
padding: 0 8px;
|
|
852
880
|
font-size: 13px;
|
|
853
|
-
font-weight: 600;
|
|
854
881
|
color: var(--vp-c-brand-1);
|
|
855
882
|
white-space: nowrap;
|
|
856
883
|
cursor: pointer;
|
|
884
|
+
background: transparent;
|
|
885
|
+
border: 0;
|
|
886
|
+
border-radius: 6px;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.fl-more-all {
|
|
890
|
+
font-weight: 600;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/* hover only where there is a pointer, so a tap does not leave a mark behind */
|
|
894
|
+
@media (hover: hover) {
|
|
895
|
+
.fl-more-link:hover,
|
|
896
|
+
.fl-pin-less:hover {
|
|
897
|
+
background: var(--vp-c-default-soft);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/* the folder row pinned at the top while scrolling through a long folder */
|
|
902
|
+
.fl-pin-anchor {
|
|
903
|
+
position: sticky;
|
|
904
|
+
top: calc(var(--fl-sticky) + var(--fl-pin-offset, 0px));
|
|
905
|
+
z-index: 5;
|
|
906
|
+
height: 0;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.fl-pin {
|
|
910
|
+
position: absolute;
|
|
911
|
+
inset: 0 0 auto;
|
|
912
|
+
display: grid;
|
|
913
|
+
grid-template-columns: 40px minmax(0, 1fr) auto;
|
|
914
|
+
align-items: center;
|
|
915
|
+
min-height: var(--fl-row-h);
|
|
916
|
+
padding: 0 8px;
|
|
857
917
|
background: var(--vp-c-bg);
|
|
858
|
-
border: 1px solid var(--vp-c-
|
|
859
|
-
|
|
918
|
+
border-bottom: 1px solid var(--vp-c-divider);
|
|
919
|
+
box-shadow: 0 6px 12px -8px rgb(0 0 0 / 25%);
|
|
860
920
|
}
|
|
861
921
|
|
|
862
|
-
.fl-
|
|
863
|
-
|
|
922
|
+
.fl-pin-arrow {
|
|
923
|
+
display: inline-flex;
|
|
924
|
+
flex: none;
|
|
925
|
+
align-items: center;
|
|
926
|
+
justify-content: center;
|
|
927
|
+
width: 18px;
|
|
928
|
+
color: var(--vp-c-text-2);
|
|
929
|
+
transform: rotate(90deg);
|
|
864
930
|
}
|
|
865
931
|
|
|
866
|
-
.fl-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
932
|
+
.fl-pin-name {
|
|
933
|
+
min-width: 0;
|
|
934
|
+
overflow: hidden;
|
|
935
|
+
font-weight: 500;
|
|
936
|
+
text-overflow: ellipsis;
|
|
937
|
+
white-space: nowrap;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
.fl-pin-count {
|
|
941
|
+
flex: none;
|
|
942
|
+
font-size: 12px;
|
|
943
|
+
color: var(--vp-c-text-2);
|
|
944
|
+
white-space: nowrap;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.fl-pin-less {
|
|
948
|
+
display: inline-flex;
|
|
949
|
+
gap: 4px;
|
|
950
|
+
align-items: center;
|
|
951
|
+
height: 26px;
|
|
952
|
+
padding: 0 10px;
|
|
953
|
+
font-size: 12px;
|
|
954
|
+
font-weight: 600;
|
|
955
|
+
color: var(--vp-c-brand-1);
|
|
956
|
+
white-space: nowrap;
|
|
957
|
+
cursor: pointer;
|
|
958
|
+
background: var(--vp-c-brand-soft);
|
|
959
|
+
border: 0;
|
|
960
|
+
border-radius: 13px;
|
|
870
961
|
}
|
|
871
962
|
|
|
872
963
|
/* ---------------- filter and deep search ---------------- */
|
|
@@ -1127,31 +1218,63 @@
|
|
|
1127
1218
|
border-radius: 10px;
|
|
1128
1219
|
}
|
|
1129
1220
|
|
|
1130
|
-
.fl-more-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
.fl-more-cell .fl-indent {
|
|
1135
|
-
margin-right: 10px;
|
|
1221
|
+
.fl-more-tag {
|
|
1222
|
+
width: 36px;
|
|
1223
|
+
height: 22px;
|
|
1136
1224
|
}
|
|
1137
1225
|
|
|
1138
1226
|
.fl-more-count {
|
|
1139
1227
|
flex: 1 1 auto;
|
|
1140
1228
|
}
|
|
1141
1229
|
|
|
1142
|
-
.fl-more-
|
|
1143
|
-
|
|
1144
|
-
|
|
1230
|
+
.fl-more-long {
|
|
1231
|
+
display: none;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.fl-more-short {
|
|
1235
|
+
display: inline;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/* smaller to look at, still a 44px target */
|
|
1239
|
+
.fl-more-link {
|
|
1240
|
+
position: relative;
|
|
1241
|
+
height: 32px;
|
|
1242
|
+
padding: 0 10px;
|
|
1145
1243
|
font-size: 14px;
|
|
1146
|
-
border-radius: 10px;
|
|
1147
1244
|
}
|
|
1148
1245
|
|
|
1149
|
-
.fl-more-
|
|
1246
|
+
.fl-more-link::after {
|
|
1247
|
+
position: absolute;
|
|
1248
|
+
inset: -6px 0;
|
|
1249
|
+
content: '';
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
.fl-pin {
|
|
1253
|
+
grid-template-columns: 44px minmax(0, 1fr) auto;
|
|
1254
|
+
padding: 0 8px 0 0;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.fl-pin-count {
|
|
1150
1258
|
display: none;
|
|
1151
1259
|
}
|
|
1152
1260
|
|
|
1261
|
+
.fl-pin-less {
|
|
1262
|
+
position: relative;
|
|
1263
|
+
height: 32px;
|
|
1264
|
+
padding: 0 12px;
|
|
1265
|
+
font-size: 13px;
|
|
1266
|
+
border-radius: 16px;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.fl-pin-less::after {
|
|
1270
|
+
position: absolute;
|
|
1271
|
+
inset: -6px 0;
|
|
1272
|
+
content: '';
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1153
1275
|
/* rows (container queries cannot restyle .fl-root itself) */
|
|
1154
|
-
.fl-row
|
|
1276
|
+
.fl-row,
|
|
1277
|
+
.fl-pin {
|
|
1155
1278
|
--fl-indent: 16px;
|
|
1156
1279
|
--fl-row-h: 56px;
|
|
1157
1280
|
--fl-cols: 44px minmax(0, 1fr) auto;
|
|
@@ -1221,6 +1344,7 @@
|
|
|
1221
1344
|
|
|
1222
1345
|
.fl-arrow,
|
|
1223
1346
|
.fl-arrow-placeholder,
|
|
1347
|
+
.fl-pin-arrow,
|
|
1224
1348
|
.fl-count {
|
|
1225
1349
|
display: none;
|
|
1226
1350
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitepress-plugin-filelist",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"packageManager": "pnpm@11.25.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "OpenList file browser for VitePress: tree view, batch ZIP download, i18n, password and token support.",
|
|
7
7
|
"author": "nrjycyd",
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"homepage": "https://github.
|
|
9
|
+
"homepage": "https://nrjycyd.github.io/vitepress-plugin-filelist/",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "git+https://github.com/nrjycyd/vitepress-plugin-filelist.git"
|