mirror of
https://github.com/quartz-community/explorer.git
synced 2026-07-22 02:50:24 +00:00
feat: use @quartz-community/utils for shared utilities
- Add @quartz-community/utils dependency - Update tsup config to bundle inline scripts with esbuild - Import simplifySlug and joinSegments from utils - Remove unused template dependencies - Update repository URL in package.json
This commit is contained in:
parent
bb78ce4baf
commit
56f6b7b8f0
5 changed files with 119 additions and 1192 deletions
1206
package-lock.json
generated
1206
package-lock.json
generated
File diff suppressed because it is too large
Load diff
19
package.json
19
package.json
|
|
@ -8,16 +8,14 @@
|
|||
"homepage": "https://quartz.jzhao.xyz",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/quartz-community/plugin-template"
|
||||
"url": "https://github.com/quartz-community/explorer"
|
||||
},
|
||||
"keywords": [
|
||||
"quartz",
|
||||
"quartz-plugin",
|
||||
"plugin-template",
|
||||
"remark",
|
||||
"rehype",
|
||||
"mdast",
|
||||
"hast"
|
||||
"explorer",
|
||||
"file-tree",
|
||||
"navigation"
|
||||
],
|
||||
"files": [
|
||||
"dist",
|
||||
|
|
@ -67,14 +65,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@quartz-community/types": "github:quartz-community/types",
|
||||
"mdast-util-find-and-replace": "^3.0.1",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.3"
|
||||
"@quartz-community/utils": "github:quartz-community/utils"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/hast": "^3.0.4",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
// @ts-nocheck
|
||||
import { simplifySlug, joinSegments } from "@quartz-community/utils";
|
||||
|
||||
// Simple trie node implementation for client-side
|
||||
class FileTrieNode {
|
||||
constructor(segments, data) {
|
||||
|
|
@ -77,11 +79,6 @@ class FileTrieNode {
|
|||
}
|
||||
}
|
||||
|
||||
// Helper to join segments safely
|
||||
function joinSegments(...segments) {
|
||||
return segments.filter(Boolean).join("/");
|
||||
}
|
||||
|
||||
// Process and sort nodes
|
||||
const defaultSortFn = (a, b) => {
|
||||
if ((!a.isFolder && !b.isFolder) || (a.isFolder && b.isFolder)) {
|
||||
|
|
@ -168,11 +165,12 @@ function renderTree(node, container, currentSlug, folderBehavior, savedState, pa
|
|||
if (!folderTemplate || !fileTemplate) return;
|
||||
|
||||
const currentPath = pathPrefix ? pathPrefix + "/" + node.slugSegment : node.slugSegment;
|
||||
const simplifiedCurrentSlug = simplifySlug(currentSlug);
|
||||
|
||||
if (node.isFolder) {
|
||||
const clone = folderTemplate.content.cloneNode(true);
|
||||
const folderContainer = clone.querySelector(".folder-container");
|
||||
const folderButton = clone.querySelector(".folder-button");
|
||||
let folderButton = clone.querySelector(".folder-button");
|
||||
const folderTitle = clone.querySelector(".folder-title");
|
||||
const folderOuter = clone.querySelector(".folder-outer");
|
||||
const contentUl = clone.querySelector(".content");
|
||||
|
|
@ -180,10 +178,30 @@ function renderTree(node, container, currentSlug, folderBehavior, savedState, pa
|
|||
if (folderTitle) folderTitle.textContent = node.displayName || node.slugSegment;
|
||||
if (folderContainer) folderContainer.dataset.folderpath = node.slug;
|
||||
|
||||
if (folderBehavior === "link" && folderButton) {
|
||||
const folderLink = document.createElement("a");
|
||||
folderLink.className = folderButton.className;
|
||||
const folderHref = simplifySlug(node.slug);
|
||||
folderLink.href = folderHref || ".";
|
||||
if (folderTitle) {
|
||||
folderLink.appendChild(folderTitle);
|
||||
} else {
|
||||
folderLink.textContent = node.displayName || node.slugSegment;
|
||||
}
|
||||
folderButton.replaceWith(folderLink);
|
||||
folderButton = folderLink;
|
||||
}
|
||||
|
||||
// Check saved state for collapsed status
|
||||
const isCollapsed = savedState[node.slug] !== undefined ? savedState[node.slug] : true; // Default collapsed
|
||||
|
||||
if (!isCollapsed && folderOuter) {
|
||||
// if this folder is a prefix of the current path we want to open it anyways
|
||||
const simpleFolderPath = simplifySlug(node.slug);
|
||||
const folderIsPrefixOfCurrentSlug =
|
||||
simpleFolderPath &&
|
||||
simpleFolderPath === simplifiedCurrentSlug.slice(0, simpleFolderPath.length);
|
||||
|
||||
if ((!isCollapsed || folderIsPrefixOfCurrentSlug) && folderOuter) {
|
||||
folderOuter.classList.add("open");
|
||||
}
|
||||
|
||||
|
|
@ -199,8 +217,8 @@ function renderTree(node, container, currentSlug, folderBehavior, savedState, pa
|
|||
const clone = fileTemplate.content.cloneNode(true);
|
||||
const link = clone.querySelector("a");
|
||||
if (link) {
|
||||
// Use absolute path for consistent navigation from any page
|
||||
link.href = "/" + node.data.slug;
|
||||
// Use relative path for consistent navigation from any page
|
||||
link.href = node.data.slug;
|
||||
link.textContent = node.displayName || node.slugSegment;
|
||||
if (node.data.slug === currentSlug) {
|
||||
link.classList.add("active");
|
||||
|
|
@ -213,7 +231,7 @@ function renderTree(node, container, currentSlug, folderBehavior, savedState, pa
|
|||
document.addEventListener("nav", async (e) => {
|
||||
try {
|
||||
console.log("[Explorer] Nav event received");
|
||||
const currentSlug = e.detail?.url || "";
|
||||
const currentSlug = (e.detail?.url || "").replace(/^\/+/, "");
|
||||
const allExplorers = document.querySelectorAll("div.explorer");
|
||||
console.log("[Explorer] Found", allExplorers.length, "explorers");
|
||||
|
||||
|
|
@ -256,6 +274,17 @@ document.addEventListener("nav", async (e) => {
|
|||
console.warn("[Explorer] No trie or empty children");
|
||||
}
|
||||
|
||||
// restore scrollTop position or scroll to active element
|
||||
const scrollTop = sessionStorage.getItem("explorerScrollTop");
|
||||
if (scrollTop) {
|
||||
explorerUl.scrollTop = parseInt(scrollTop, 10);
|
||||
} else {
|
||||
const activeElement = explorerUl.querySelector(".active");
|
||||
if (activeElement) {
|
||||
activeElement.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}
|
||||
|
||||
const cleanupHandlers = [];
|
||||
|
||||
const explorerButtons = explorer.getElementsByClassName("explorer-toggle");
|
||||
|
|
@ -315,8 +344,10 @@ document.addEventListener("nav", async (e) => {
|
|||
|
||||
if (folderBehavior === "link") {
|
||||
if (folderPath) {
|
||||
window.location.href = "/" + folderPath;
|
||||
const folderHref = simplifySlug(folderPath);
|
||||
window.location.href = folderHref || ".";
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
evt.stopPropagation();
|
||||
if (!childFolderContainer) return;
|
||||
|
|
@ -356,15 +387,6 @@ document.addEventListener("nav", async (e) => {
|
|||
document.documentElement.classList.remove("mobile-no-scroll");
|
||||
}
|
||||
}
|
||||
|
||||
// Restore scroll position
|
||||
const savedScrollTop = sessionStorage.getItem("explorerScrollTop");
|
||||
if (savedScrollTop) {
|
||||
const explorerUl = document.querySelector(".explorer-ul");
|
||||
if (explorerUl) {
|
||||
explorerUl.scrollTop = parseInt(savedScrollTop, 10);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("[Explorer] Fatal error in nav handler:", err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { Explorer } from "../src/components/Explorer";
|
||||
import Explorer from "../src/components/Explorer";
|
||||
|
||||
describe("Explorer Component", () => {
|
||||
it("should create an Explorer component with default options", () => {
|
||||
|
|
@ -47,7 +47,8 @@ describe("Explorer Manifest", () => {
|
|||
expect(manifest.version).toBeDefined();
|
||||
expect(manifest.components).toBeDefined();
|
||||
expect(typeof manifest.components).toBe("object");
|
||||
expect(manifest.components.Explorer).toBeDefined();
|
||||
expect(manifest.components.Explorer.name).toBe("Explorer");
|
||||
const explorerComponent = manifest.components?.Explorer;
|
||||
expect(explorerComponent).toBeDefined();
|
||||
expect(explorerComponent?.name).toBe("Explorer");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { defineConfig } from "tsup";
|
||||
import * as esbuild from "esbuild";
|
||||
|
||||
export default defineConfig({
|
||||
entry: {
|
||||
|
|
@ -32,11 +33,19 @@ export default defineConfig({
|
|||
});
|
||||
|
||||
build.onLoad({ filter: /\.inline\.ts$/ }, async (args) => {
|
||||
const fs = await import("fs");
|
||||
const text = await fs.promises.readFile(args.path, "utf8");
|
||||
const result = await esbuild.build({
|
||||
entryPoints: [args.path],
|
||||
bundle: true,
|
||||
write: false,
|
||||
format: "iife",
|
||||
target: "es2022",
|
||||
minify: false,
|
||||
platform: "browser",
|
||||
});
|
||||
const code = result.outputFiles?.[0]?.text ?? "";
|
||||
return {
|
||||
contents: text,
|
||||
loader: "text",
|
||||
contents: `export default ${JSON.stringify(code)};`,
|
||||
loader: "ts",
|
||||
};
|
||||
});
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue