@luciodale/docs-ui-kit 0.2.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 +607 -0
- package/package.json +46 -0
- package/src/components/Callout.astro +58 -0
- package/src/components/CodeBlock.astro +54 -0
- package/src/components/ContentSection.astro +14 -0
- package/src/components/DocsLayout.astro +28 -0
- package/src/components/Footer.astro +66 -0
- package/src/components/GithubIcon.astro +12 -0
- package/src/components/HeroSection.astro +59 -0
- package/src/components/LinkedInIcon.astro +12 -0
- package/src/components/Navbar.astro +189 -0
- package/src/components/PackageInfo.astro +92 -0
- package/src/components/PageLayout.astro +20 -0
- package/src/components/PropsTable.astro +45 -0
- package/src/components/SEO.astro +120 -0
- package/src/components/SearchModal.astro +278 -0
- package/src/components/SectionHeading.astro +11 -0
- package/src/components/Sidebar.astro +19 -0
- package/src/components/SidebarSection.astro +39 -0
- package/src/components/TableOfContents.astro +60 -0
- package/src/components/icons/DangerIcon.astro +9 -0
- package/src/components/icons/InfoIcon.astro +9 -0
- package/src/components/icons/TipIcon.astro +8 -0
- package/src/components/icons/WarningIcon.astro +9 -0
- package/src/styles/theme.css +78 -0
- package/src/types/config.ts +32 -0
- package/src/types/props.ts +7 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import DangerIcon from "./icons/DangerIcon.astro";
|
|
3
|
+
import InfoIcon from "./icons/InfoIcon.astro";
|
|
4
|
+
import TipIcon from "./icons/TipIcon.astro";
|
|
5
|
+
import WarningIcon from "./icons/WarningIcon.astro";
|
|
6
|
+
|
|
7
|
+
type CalloutType = "info" | "warning" | "tip" | "danger";
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
type?: CalloutType;
|
|
11
|
+
title?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const { type = "info", title } = Astro.props;
|
|
15
|
+
|
|
16
|
+
const styles: Record<CalloutType, { border: string; bg: string; text: string; defaultTitle: string }> = {
|
|
17
|
+
info: {
|
|
18
|
+
border: "border-blue-400/30",
|
|
19
|
+
bg: "bg-blue-400/5",
|
|
20
|
+
text: "text-blue-400",
|
|
21
|
+
defaultTitle: "Note",
|
|
22
|
+
},
|
|
23
|
+
warning: {
|
|
24
|
+
border: "border-yellow-400/30",
|
|
25
|
+
bg: "bg-yellow-400/5",
|
|
26
|
+
text: "text-yellow-400",
|
|
27
|
+
defaultTitle: "Warning",
|
|
28
|
+
},
|
|
29
|
+
tip: {
|
|
30
|
+
border: "border-green-400/30",
|
|
31
|
+
bg: "bg-green-400/5",
|
|
32
|
+
text: "text-green-400",
|
|
33
|
+
defaultTitle: "Tip",
|
|
34
|
+
},
|
|
35
|
+
danger: {
|
|
36
|
+
border: "border-red-400/30",
|
|
37
|
+
bg: "bg-red-400/5",
|
|
38
|
+
text: "text-red-400",
|
|
39
|
+
defaultTitle: "Danger",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const s = styles[type];
|
|
44
|
+
const heading = title ?? s.defaultTitle;
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
<aside class:list={["rounded-lg border-l-4 px-5 py-4 my-6", s.border, s.bg]}>
|
|
48
|
+
<div class:list={["flex items-center gap-1.5 mb-2 text-sm font-semibold", s.text]}>
|
|
49
|
+
{type === "info" && <InfoIcon />}
|
|
50
|
+
{type === "warning" && <WarningIcon />}
|
|
51
|
+
{type === "tip" && <TipIcon />}
|
|
52
|
+
{type === "danger" && <DangerIcon />}
|
|
53
|
+
{heading}
|
|
54
|
+
</div>
|
|
55
|
+
<div class="text-sm text-white/60 leading-relaxed [&_a]:text-accent [&_a]:underline [&_code]:text-white/70 [&_code]:font-mono [&_code]:text-xs">
|
|
56
|
+
<slot />
|
|
57
|
+
</div>
|
|
58
|
+
</aside>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { codeToHtml } from "shiki";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
code: string;
|
|
6
|
+
language?: string;
|
|
7
|
+
filename?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { code, language = "tsx", filename } = Astro.props;
|
|
11
|
+
const trimmed = code.trim();
|
|
12
|
+
const html = await codeToHtml(trimmed, {
|
|
13
|
+
lang: language,
|
|
14
|
+
theme: "vitesse-dark",
|
|
15
|
+
});
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div class="rounded-lg border border-white/10 bg-[#161618] overflow-hidden my-4">
|
|
19
|
+
<div class="flex items-center justify-between px-4 py-2.5 border-b border-white/[0.06] bg-white/[0.02]">
|
|
20
|
+
<span class="text-xs text-white/30 font-mono">{filename ?? language}</span>
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
class="copy-btn text-xs text-white/30 hover:text-white/60 transition-colors cursor-pointer"
|
|
24
|
+
data-code={trimmed}
|
|
25
|
+
>
|
|
26
|
+
<span class="copy-label">Copy</span>
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
class="px-5 py-5 overflow-x-auto text-sm leading-[1.7] [&_pre]:!bg-transparent [&_pre]:!m-0 [&_pre]:!p-0"
|
|
31
|
+
set:html={html}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
function initCopyButtons() {
|
|
37
|
+
document.querySelectorAll(".copy-btn").forEach((btn) => {
|
|
38
|
+
if (btn.dataset.initialized) return;
|
|
39
|
+
btn.dataset.initialized = "true";
|
|
40
|
+
btn.addEventListener("click", async () => {
|
|
41
|
+
const code = btn.dataset.code;
|
|
42
|
+
if (!code) return;
|
|
43
|
+
await navigator.clipboard.writeText(code);
|
|
44
|
+
const label = btn.querySelector(".copy-label");
|
|
45
|
+
if (label) {
|
|
46
|
+
label.textContent = "Copied!";
|
|
47
|
+
setTimeout(() => { label.textContent = "Copy"; }, 2000);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
initCopyButtons();
|
|
53
|
+
document.addEventListener("astro:after-swap", initCopyButtons);
|
|
54
|
+
</script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
type Props = {
|
|
3
|
+
label: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const { label, id } = Astro.props;
|
|
8
|
+
const sectionId = id ?? label.toLowerCase().replace(/\s+/g, "-").replace(/[^\w-]/g, "");
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<section id={sectionId} data-toc-label={label} class="scroll-mt-24 mb-12">
|
|
12
|
+
<h2 class="text-2xl md:text-3xl font-semibold tracking-tight text-white mb-6">{label}</h2>
|
|
13
|
+
<slot />
|
|
14
|
+
</section>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SiteConfig } from "../types/config";
|
|
3
|
+
import Footer from "./Footer.astro";
|
|
4
|
+
import Navbar from "./Navbar.astro";
|
|
5
|
+
import Sidebar from "./Sidebar.astro";
|
|
6
|
+
import TableOfContents from "./TableOfContents.astro";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
config: SiteConfig;
|
|
10
|
+
currentPath: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const { config, currentPath } = Astro.props;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class="flex flex-col min-h-dvh">
|
|
17
|
+
<Navbar config={config} currentPath={currentPath} />
|
|
18
|
+
<div class="flex flex-1">
|
|
19
|
+
<Sidebar sections={config.sidebarSections} currentPath={currentPath} />
|
|
20
|
+
<main class="flex-1 overflow-y-auto">
|
|
21
|
+
<div class="max-w-3xl px-6 md:px-8 xl:px-12 py-10 md:py-16">
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</main>
|
|
25
|
+
<TableOfContents />
|
|
26
|
+
</div>
|
|
27
|
+
<Footer config={config} />
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SiteConfig } from "../types/config";
|
|
3
|
+
import GithubIcon from "./GithubIcon.astro";
|
|
4
|
+
import LinkedInIcon from "./LinkedInIcon.astro";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
config: SiteConfig;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { config } = Astro.props;
|
|
11
|
+
const year = new Date().getFullYear();
|
|
12
|
+
const copyright = config.copyright ?? config.title;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<footer class="mt-auto border-t border-white/10 py-10">
|
|
16
|
+
<div class="max-w-7xl mx-auto px-6 md:px-8">
|
|
17
|
+
<div class="flex flex-col md:flex-row items-center gap-6 md:gap-4">
|
|
18
|
+
<div class="flex items-center gap-3 md:w-1/3">
|
|
19
|
+
{config.logoSrc && (
|
|
20
|
+
<a href="/">
|
|
21
|
+
<img
|
|
22
|
+
src={config.logoSrc}
|
|
23
|
+
alt={config.logoAlt ?? config.title}
|
|
24
|
+
class="w-8 h-auto opacity-60 hover:opacity-100 transition-opacity"
|
|
25
|
+
/>
|
|
26
|
+
</a>
|
|
27
|
+
)}
|
|
28
|
+
<span class="text-white/40 text-sm">
|
|
29
|
+
© {year} {copyright}
|
|
30
|
+
</span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="flex gap-6 md:w-1/3 justify-center">
|
|
34
|
+
{config.navLinks.map((link) => (
|
|
35
|
+
<a
|
|
36
|
+
href={link.href}
|
|
37
|
+
class="text-white/40 text-sm hover:text-white/70 transition-colors"
|
|
38
|
+
>
|
|
39
|
+
{link.label}
|
|
40
|
+
</a>
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="flex gap-4 md:w-1/3 justify-end">
|
|
45
|
+
<a
|
|
46
|
+
href={config.socialLinks.github}
|
|
47
|
+
target="_blank"
|
|
48
|
+
rel="noopener noreferrer"
|
|
49
|
+
class="text-white/30 hover:text-white/60 transition-colors"
|
|
50
|
+
>
|
|
51
|
+
<GithubIcon class="w-5" />
|
|
52
|
+
</a>
|
|
53
|
+
{config.socialLinks.linkedin && (
|
|
54
|
+
<a
|
|
55
|
+
href={config.socialLinks.linkedin}
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noopener noreferrer"
|
|
58
|
+
class="text-white/30 hover:text-white/60 transition-colors"
|
|
59
|
+
>
|
|
60
|
+
<LinkedInIcon class="w-5" />
|
|
61
|
+
</a>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</footer>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
type Props = {
|
|
3
|
+
class?: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const { class: className = "w-5 text-white" } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<svg class={className} fill="currentColor" viewBox="0 0 24 24">
|
|
10
|
+
<title>GitHub</title>
|
|
11
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
type Props = {
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
installCommand: string;
|
|
6
|
+
logoSrc?: string;
|
|
7
|
+
logoAlt?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const { title, description, installCommand, logoSrc, logoAlt } = Astro.props;
|
|
11
|
+
const hasExtra = Astro.slots.has("default");
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<section class="flex flex-col items-center justify-center px-6 py-24 md:py-32 text-center max-w-3xl mx-auto">
|
|
15
|
+
{logoSrc && (
|
|
16
|
+
<img src={logoSrc} alt={logoAlt ?? title} class="w-20 h-20 md:w-24 md:h-24 mb-8" />
|
|
17
|
+
)}
|
|
18
|
+
<h1 class="text-4xl md:text-5xl font-bold tracking-tight text-white leading-[1.1] mb-4">
|
|
19
|
+
{title}
|
|
20
|
+
</h1>
|
|
21
|
+
<p class="text-lg text-white/50 leading-relaxed max-w-xl mb-10">{description}</p>
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
class="copy-btn group rounded-lg border border-white/10 bg-white/5 hover:bg-white/[0.08] px-5 py-3 font-mono text-sm text-white/70 transition-colors cursor-pointer"
|
|
25
|
+
data-code={installCommand}
|
|
26
|
+
>
|
|
27
|
+
<span class="text-accent">$</span>
|
|
28
|
+
<span class="ml-2">{installCommand}</span>
|
|
29
|
+
<span class="ml-3 text-white/30 group-hover:text-white/50 transition-colors text-xs copy-label">
|
|
30
|
+
Copy
|
|
31
|
+
</span>
|
|
32
|
+
</button>
|
|
33
|
+
{hasExtra && (
|
|
34
|
+
<div class="mt-12">
|
|
35
|
+
<slot />
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
</section>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
function initCopyButtons() {
|
|
42
|
+
document.querySelectorAll(".copy-btn").forEach((btn) => {
|
|
43
|
+
if (btn.dataset.initialized) return;
|
|
44
|
+
btn.dataset.initialized = "true";
|
|
45
|
+
btn.addEventListener("click", async () => {
|
|
46
|
+
const code = btn.dataset.code;
|
|
47
|
+
if (!code) return;
|
|
48
|
+
await navigator.clipboard.writeText(code);
|
|
49
|
+
const label = btn.querySelector(".copy-label");
|
|
50
|
+
if (label) {
|
|
51
|
+
label.textContent = "Copied!";
|
|
52
|
+
setTimeout(() => { label.textContent = "Copy"; }, 2000);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
initCopyButtons();
|
|
58
|
+
document.addEventListener("astro:after-swap", initCopyButtons);
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
type Props = {
|
|
3
|
+
class?: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const { class: className = "w-5 text-white" } = Astro.props;
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<svg class={className} fill="currentColor" viewBox="0 0 24 24">
|
|
10
|
+
<title>LinkedIn</title>
|
|
11
|
+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SiteConfig } from "../types/config";
|
|
3
|
+
import GithubIcon from "./GithubIcon.astro";
|
|
4
|
+
import SearchModal from "./SearchModal.astro";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
config: SiteConfig;
|
|
8
|
+
currentPath: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const { config, currentPath } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<header class="sticky top-0 z-50 h-16 border-b border-white/10 bg-black/80 backdrop-blur-md">
|
|
15
|
+
<div class="max-w-7xl h-full mx-auto px-6 md:px-8 flex items-center justify-between">
|
|
16
|
+
<a href="/" class="flex items-center gap-3 text-white hover:opacity-80 transition-opacity">
|
|
17
|
+
{config.logoSrc ? (
|
|
18
|
+
<img src={config.logoSrc} alt={config.logoAlt ?? config.title} class="h-7" />
|
|
19
|
+
) : (
|
|
20
|
+
<span class="text-base font-semibold tracking-tight">{config.title}</span>
|
|
21
|
+
)}
|
|
22
|
+
</a>
|
|
23
|
+
|
|
24
|
+
<nav class="hidden md:flex items-center gap-6">
|
|
25
|
+
{config.navLinks.map((link) => (
|
|
26
|
+
<a
|
|
27
|
+
href={link.href}
|
|
28
|
+
class:list={[
|
|
29
|
+
"text-sm font-medium transition-colors duration-150",
|
|
30
|
+
currentPath.startsWith(link.href)
|
|
31
|
+
? "text-white"
|
|
32
|
+
: "text-white/50 hover:text-white/80",
|
|
33
|
+
]}
|
|
34
|
+
>
|
|
35
|
+
{link.label}
|
|
36
|
+
</a>
|
|
37
|
+
))}
|
|
38
|
+
<button
|
|
39
|
+
id="search-trigger"
|
|
40
|
+
type="button"
|
|
41
|
+
class="flex items-center gap-2 rounded-lg border border-white/10 bg-white/[0.03] hover:bg-white/[0.06] px-3 py-1.5 text-white/40 hover:text-white/60 transition-colors cursor-pointer"
|
|
42
|
+
>
|
|
43
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
|
44
|
+
<circle cx="11" cy="11" r="7" />
|
|
45
|
+
<line x1="16.5" y1="16.5" x2="21" y2="21" />
|
|
46
|
+
</svg>
|
|
47
|
+
<span class="text-xs">Search</span>
|
|
48
|
+
<kbd class="ml-2 rounded border border-white/10 bg-white/[0.04] px-1.5 py-0.5 text-[10px] font-mono text-white/30">
|
|
49
|
+
<span class="text-[10px]">⌘</span>K
|
|
50
|
+
</kbd>
|
|
51
|
+
</button>
|
|
52
|
+
<a
|
|
53
|
+
href={config.githubUrl}
|
|
54
|
+
target="_blank"
|
|
55
|
+
rel="noopener noreferrer"
|
|
56
|
+
class="text-white/40 hover:text-white/70 transition-colors duration-150"
|
|
57
|
+
>
|
|
58
|
+
<GithubIcon class="w-5" />
|
|
59
|
+
</a>
|
|
60
|
+
</nav>
|
|
61
|
+
|
|
62
|
+
<div class="flex items-center gap-3 md:hidden">
|
|
63
|
+
<button
|
|
64
|
+
id="search-trigger-mobile"
|
|
65
|
+
type="button"
|
|
66
|
+
class="text-white/50 hover:text-white transition-colors cursor-pointer"
|
|
67
|
+
aria-label="Search"
|
|
68
|
+
>
|
|
69
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
|
70
|
+
<circle cx="11" cy="11" r="7" />
|
|
71
|
+
<line x1="16.5" y1="16.5" x2="21" y2="21" />
|
|
72
|
+
</svg>
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
id="mobile-nav-toggle"
|
|
76
|
+
type="button"
|
|
77
|
+
class="text-white/60 hover:text-white transition-colors cursor-pointer"
|
|
78
|
+
aria-label="Toggle menu"
|
|
79
|
+
>
|
|
80
|
+
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
81
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 6h16M4 12h16M4 18h16" />
|
|
82
|
+
</svg>
|
|
83
|
+
</button>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</header>
|
|
87
|
+
|
|
88
|
+
<div id="mobile-nav-overlay" class="fixed inset-0 z-40 bg-black/60 backdrop-blur-sm hidden md:hidden"></div>
|
|
89
|
+
|
|
90
|
+
<nav
|
|
91
|
+
id="mobile-nav-menu"
|
|
92
|
+
class="fixed top-0 right-0 z-50 h-dvh w-72 bg-black border-l border-white/10 transform translate-x-full transition-transform duration-200 ease-out md:hidden overflow-y-auto"
|
|
93
|
+
>
|
|
94
|
+
<div class="flex items-center justify-between px-6 py-5 border-b border-white/10">
|
|
95
|
+
<span class="text-sm font-semibold text-white">{config.title}</span>
|
|
96
|
+
<button
|
|
97
|
+
id="mobile-nav-close"
|
|
98
|
+
type="button"
|
|
99
|
+
class="text-white/60 hover:text-white transition-colors cursor-pointer"
|
|
100
|
+
aria-label="Close menu"
|
|
101
|
+
>
|
|
102
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
103
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6 18L18 6M6 6l12 12" />
|
|
104
|
+
</svg>
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="px-6 py-4 flex flex-col gap-3 border-b border-white/10">
|
|
109
|
+
{config.navLinks.map((link) => (
|
|
110
|
+
<a
|
|
111
|
+
href={link.href}
|
|
112
|
+
class:list={[
|
|
113
|
+
"text-sm font-medium transition-colors",
|
|
114
|
+
currentPath.startsWith(link.href)
|
|
115
|
+
? "text-white"
|
|
116
|
+
: "text-white/50 hover:text-white/80",
|
|
117
|
+
]}
|
|
118
|
+
>
|
|
119
|
+
{link.label}
|
|
120
|
+
</a>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
{config.sidebarSections.length > 0 && (
|
|
125
|
+
<div class="px-6 py-4">
|
|
126
|
+
{config.sidebarSections.map((section) => (
|
|
127
|
+
<div class="mb-5">
|
|
128
|
+
<div class="text-xs font-semibold uppercase tracking-widest text-white/30 mb-2">
|
|
129
|
+
{section.title}
|
|
130
|
+
</div>
|
|
131
|
+
<div class="flex flex-col">
|
|
132
|
+
{section.links.map((link) => (
|
|
133
|
+
<a
|
|
134
|
+
href={link.href}
|
|
135
|
+
class:list={[
|
|
136
|
+
"block py-1.5 pl-3 text-sm border-l-2 transition-colors",
|
|
137
|
+
currentPath === link.href
|
|
138
|
+
? "border-accent text-white font-medium"
|
|
139
|
+
: "border-transparent text-white/50 hover:text-white/80 hover:border-white/20",
|
|
140
|
+
]}
|
|
141
|
+
>
|
|
142
|
+
{link.label}
|
|
143
|
+
</a>
|
|
144
|
+
))}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
))}
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
<div class="px-6 py-4 border-t border-white/10 mt-auto flex gap-4">
|
|
152
|
+
<a
|
|
153
|
+
href={config.socialLinks.github}
|
|
154
|
+
target="_blank"
|
|
155
|
+
rel="noopener noreferrer"
|
|
156
|
+
class="text-white/30 hover:text-white/60 transition-colors"
|
|
157
|
+
>
|
|
158
|
+
<GithubIcon class="w-5" />
|
|
159
|
+
</a>
|
|
160
|
+
</div>
|
|
161
|
+
</nav>
|
|
162
|
+
|
|
163
|
+
<SearchModal />
|
|
164
|
+
|
|
165
|
+
<script>
|
|
166
|
+
function initMobileNav() {
|
|
167
|
+
const toggle = document.getElementById("mobile-nav-toggle");
|
|
168
|
+
const close = document.getElementById("mobile-nav-close");
|
|
169
|
+
const overlay = document.getElementById("mobile-nav-overlay");
|
|
170
|
+
const menu = document.getElementById("mobile-nav-menu");
|
|
171
|
+
|
|
172
|
+
function open() {
|
|
173
|
+
overlay?.classList.remove("hidden");
|
|
174
|
+
menu?.classList.remove("translate-x-full");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function shut() {
|
|
178
|
+
overlay?.classList.add("hidden");
|
|
179
|
+
menu?.classList.add("translate-x-full");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
toggle?.addEventListener("click", open);
|
|
183
|
+
close?.addEventListener("click", shut);
|
|
184
|
+
overlay?.addEventListener("click", shut);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
initMobileNav();
|
|
188
|
+
document.addEventListener("astro:after-swap", initMobileNav);
|
|
189
|
+
</script>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
type Props = {
|
|
3
|
+
packageName: string;
|
|
4
|
+
githubRepo?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const { packageName, githubRepo } = Astro.props;
|
|
8
|
+
|
|
9
|
+
type NpmData = { version: string; license: string };
|
|
10
|
+
type GithubData = { stars: number };
|
|
11
|
+
|
|
12
|
+
async function fetchNpm(): Promise<NpmData | null> {
|
|
13
|
+
try {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
16
|
+
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
});
|
|
19
|
+
clearTimeout(timeout);
|
|
20
|
+
if (!res.ok) return null;
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
return { version: data.version, license: data.license ?? "" };
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function fetchGithub(): Promise<GithubData | null> {
|
|
29
|
+
if (!githubRepo) return null;
|
|
30
|
+
try {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
33
|
+
const res = await fetch(`https://api.github.com/repos/${githubRepo}`, {
|
|
34
|
+
signal: controller.signal,
|
|
35
|
+
headers: { Accept: "application/vnd.github.v3+json" },
|
|
36
|
+
});
|
|
37
|
+
clearTimeout(timeout);
|
|
38
|
+
if (!res.ok) return null;
|
|
39
|
+
const data = await res.json();
|
|
40
|
+
return { stars: data.stargazers_count ?? 0 };
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const [npm, github] = await Promise.all([fetchNpm(), fetchGithub()]);
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<div class="flex items-center gap-3 flex-wrap text-xs font-mono my-4">
|
|
50
|
+
{npm?.version && (
|
|
51
|
+
<a
|
|
52
|
+
href={`https://www.npmjs.com/package/${packageName}`}
|
|
53
|
+
target="_blank"
|
|
54
|
+
rel="noopener noreferrer"
|
|
55
|
+
class="inline-flex items-center gap-1.5 rounded-md border border-white/10 bg-white/[0.03] px-2.5 py-1 text-accent hover:bg-white/[0.06] transition-colors"
|
|
56
|
+
>
|
|
57
|
+
<svg class="w-3 h-3" viewBox="0 0 24 24" fill="currentColor">
|
|
58
|
+
<path d="M0 7.334v8h6.666v1.332H12v-1.332h12v-8H0zm6.666 6.664H5.334v-4H3.999v4H1.335V8.667h5.331v5.331zm4 0v1.336H8.001V8.667h5.334v5.332h-2.669v-.001zm12.001 0h-1.33v-4h-1.336v4h-1.335v-4h-1.33v4h-2.671V8.667h8.002v5.331zM10.665 10H12v2.667h-1.335V10z" />
|
|
59
|
+
</svg>
|
|
60
|
+
v{npm.version}
|
|
61
|
+
</a>
|
|
62
|
+
)}
|
|
63
|
+
{npm?.license && (
|
|
64
|
+
<span class="inline-flex items-center rounded-md border border-white/10 bg-white/[0.03] px-2.5 py-1 text-white/50">
|
|
65
|
+
{npm.license}
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
{github && github.stars > 0 && (
|
|
69
|
+
<a
|
|
70
|
+
href={`https://github.com/${githubRepo}`}
|
|
71
|
+
target="_blank"
|
|
72
|
+
rel="noopener noreferrer"
|
|
73
|
+
class="inline-flex items-center gap-1.5 rounded-md border border-white/10 bg-white/[0.03] px-2.5 py-1 text-white/50 hover:bg-white/[0.06] transition-colors"
|
|
74
|
+
>
|
|
75
|
+
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 24 24">
|
|
76
|
+
<path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279L12 19.771l-7.416 3.642 1.48-8.279L0 9.306l8.332-1.151z" />
|
|
77
|
+
</svg>
|
|
78
|
+
{github.stars.toLocaleString()}
|
|
79
|
+
</a>
|
|
80
|
+
)}
|
|
81
|
+
<a
|
|
82
|
+
href={`https://bundlephobia.com/package/${packageName}${npm?.version ? `@${npm.version}` : ""}`}
|
|
83
|
+
target="_blank"
|
|
84
|
+
rel="noopener noreferrer"
|
|
85
|
+
class="inline-flex items-center gap-1.5 rounded-md border border-white/10 bg-white/[0.03] px-2.5 py-1 text-white/50 hover:bg-white/[0.06] transition-colors"
|
|
86
|
+
>
|
|
87
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
88
|
+
<path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 002 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z" />
|
|
89
|
+
</svg>
|
|
90
|
+
bundlephobia
|
|
91
|
+
</a>
|
|
92
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SiteConfig } from "../types/config";
|
|
3
|
+
import Footer from "./Footer.astro";
|
|
4
|
+
import Navbar from "./Navbar.astro";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
config: SiteConfig;
|
|
8
|
+
currentPath: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const { config, currentPath } = Astro.props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<div class="flex flex-col min-h-dvh">
|
|
15
|
+
<Navbar config={config} currentPath={currentPath} />
|
|
16
|
+
<main class="flex-1">
|
|
17
|
+
<slot />
|
|
18
|
+
</main>
|
|
19
|
+
<Footer config={config} />
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { PropsTableRow } from "../types/props";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
title?: string;
|
|
6
|
+
rows: Array<PropsTableRow>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const { title, rows } = Astro.props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div class="my-6">
|
|
13
|
+
{title && <h3 class="text-lg font-semibold text-white mb-4">{title}</h3>}
|
|
14
|
+
<div class="rounded-lg border border-white/10 overflow-hidden">
|
|
15
|
+
<div class="overflow-x-auto">
|
|
16
|
+
<table class="w-full text-left text-sm">
|
|
17
|
+
<thead>
|
|
18
|
+
<tr class="border-b border-white/10 bg-white/[0.02]">
|
|
19
|
+
<th class="py-3 px-4 text-xs font-medium text-white/40 uppercase tracking-wider">Prop</th>
|
|
20
|
+
<th class="py-3 px-4 text-xs font-medium text-white/40 uppercase tracking-wider">Type</th>
|
|
21
|
+
<th class="py-3 px-4 text-xs font-medium text-white/40 uppercase tracking-wider">Default</th>
|
|
22
|
+
<th class="py-3 px-4 text-xs font-medium text-white/40 uppercase tracking-wider">Description</th>
|
|
23
|
+
</tr>
|
|
24
|
+
</thead>
|
|
25
|
+
<tbody>
|
|
26
|
+
{rows.map((row) => (
|
|
27
|
+
<tr class="border-b border-white/[0.04] last:border-b-0">
|
|
28
|
+
<td class="py-3 px-4">
|
|
29
|
+
<code class="text-accent font-mono text-xs">{row.name}</code>
|
|
30
|
+
{row.required && <span class="ml-1 text-red-400 text-xs">*</span>}
|
|
31
|
+
</td>
|
|
32
|
+
<td class="py-3 px-4">
|
|
33
|
+
<code class="text-white/50 font-mono text-xs">{row.type}</code>
|
|
34
|
+
</td>
|
|
35
|
+
<td class="py-3 px-4 text-white/35 text-xs font-mono">
|
|
36
|
+
{row.defaultValue ?? "\u2014"}
|
|
37
|
+
</td>
|
|
38
|
+
<td class="py-3 px-4 text-white/60 text-sm">{row.description}</td>
|
|
39
|
+
</tr>
|
|
40
|
+
))}
|
|
41
|
+
</tbody>
|
|
42
|
+
</table>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|