@barodoc/theme-docs 0.0.1

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +68 -0
  3. package/src/components/CodeCopy.astro +173 -0
  4. package/src/components/DocHeader.tsx +166 -0
  5. package/src/components/DocsSidebar.tsx +84 -0
  6. package/src/components/Header.astro +32 -0
  7. package/src/components/LanguageSwitcher.astro +77 -0
  8. package/src/components/MobileNav.astro +61 -0
  9. package/src/components/MobileNavSheet.tsx +73 -0
  10. package/src/components/Search.tsx +210 -0
  11. package/src/components/SearchDialog.tsx +83 -0
  12. package/src/components/Sidebar.astro +56 -0
  13. package/src/components/SidebarWrapper.tsx +24 -0
  14. package/src/components/TableOfContents.astro +98 -0
  15. package/src/components/ThemeScript.astro +11 -0
  16. package/src/components/ThemeToggle.tsx +57 -0
  17. package/src/components/api/ApiEndpoint.astro +36 -0
  18. package/src/components/api/ApiParam.astro +26 -0
  19. package/src/components/api/ApiParams.astro +16 -0
  20. package/src/components/api/ApiResponse.astro +35 -0
  21. package/src/components/index.ts +30 -0
  22. package/src/components/mdx/Accordion.tsx +61 -0
  23. package/src/components/mdx/Badge.tsx +33 -0
  24. package/src/components/mdx/Callout.astro +79 -0
  25. package/src/components/mdx/Card.astro +66 -0
  26. package/src/components/mdx/CardGroup.astro +18 -0
  27. package/src/components/mdx/CodeGroup.astro +63 -0
  28. package/src/components/mdx/CodeGroup.tsx +51 -0
  29. package/src/components/mdx/Columns.tsx +31 -0
  30. package/src/components/mdx/DocAccordion.tsx +87 -0
  31. package/src/components/mdx/DocCallout.tsx +65 -0
  32. package/src/components/mdx/DocCard.tsx +70 -0
  33. package/src/components/mdx/DocTabs.tsx +48 -0
  34. package/src/components/mdx/Expandable.tsx +107 -0
  35. package/src/components/mdx/FileTree.tsx +72 -0
  36. package/src/components/mdx/Frame.tsx +23 -0
  37. package/src/components/mdx/Icon.tsx +59 -0
  38. package/src/components/mdx/Mermaid.tsx +94 -0
  39. package/src/components/mdx/ParamField.tsx +76 -0
  40. package/src/components/mdx/ResponseField.tsx +62 -0
  41. package/src/components/mdx/Step.astro +14 -0
  42. package/src/components/mdx/Steps.astro +37 -0
  43. package/src/components/mdx/Steps.tsx +49 -0
  44. package/src/components/mdx/Tabs.tsx +67 -0
  45. package/src/components/mdx/Tooltip.tsx +36 -0
  46. package/src/components/ui/accordion.tsx +54 -0
  47. package/src/components/ui/alert.tsx +60 -0
  48. package/src/components/ui/button.tsx +57 -0
  49. package/src/components/ui/card.tsx +75 -0
  50. package/src/components/ui/collapsible.tsx +9 -0
  51. package/src/components/ui/dialog.tsx +119 -0
  52. package/src/components/ui/index.ts +11 -0
  53. package/src/components/ui/scroll-area.tsx +45 -0
  54. package/src/components/ui/separator.tsx +28 -0
  55. package/src/components/ui/sheet.tsx +137 -0
  56. package/src/components/ui/tabs.tsx +52 -0
  57. package/src/components/ui/tooltip.tsx +29 -0
  58. package/src/index.ts +74 -0
  59. package/src/layouts/BaseLayout.astro +28 -0
  60. package/src/layouts/DocsLayout.astro +121 -0
  61. package/src/lib/utils.ts +6 -0
  62. package/src/pages/docs/[...slug].astro +116 -0
  63. package/src/pages/index.astro +217 -0
  64. package/src/styles/global.css +342 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Barocss
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@barodoc/theme-docs",
3
+ "version": "0.0.1",
4
+ "description": "Documentation theme for Barodoc",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts",
8
+ "./components": "./src/components/index.ts",
9
+ "./components/*": "./src/components/*",
10
+ "./layouts/*": "./src/layouts/*",
11
+ "./pages/*": "./src/pages/*",
12
+ "./styles/*": "./src/styles/*",
13
+ "./lib/*": "./src/lib/*"
14
+ },
15
+ "files": [
16
+ "src"
17
+ ],
18
+ "dependencies": {
19
+ "@radix-ui/react-accordion": "^1.2.12",
20
+ "@radix-ui/react-collapsible": "^1.1.12",
21
+ "@radix-ui/react-dialog": "^1.1.15",
22
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
23
+ "@radix-ui/react-scroll-area": "^1.2.10",
24
+ "@radix-ui/react-separator": "^1.1.8",
25
+ "@radix-ui/react-slot": "^1.2.4",
26
+ "@radix-ui/react-tabs": "^1.1.13",
27
+ "@radix-ui/react-tooltip": "^1.2.8",
28
+ "class-variance-authority": "^0.7.1",
29
+ "clsx": "^2.1.1",
30
+ "lucide-react": "^0.563.0",
31
+ "mermaid": "^11.12.2",
32
+ "tailwind-merge": "^3.4.0",
33
+ "@barodoc/core": "0.0.1"
34
+ },
35
+ "peerDependencies": {
36
+ "astro": "^5.0.0",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@astrojs/mdx": "^4.0.0",
42
+ "@astrojs/react": "^4.0.0",
43
+ "@tailwindcss/typography": "^0.5.19",
44
+ "@tailwindcss/vite": "^4.0.0",
45
+ "@types/node": "^22.0.0",
46
+ "@types/react": "^19.0.0",
47
+ "@types/react-dom": "^19.0.0",
48
+ "astro": "^5.0.0",
49
+ "tailwindcss": "^4.0.0",
50
+ "typescript": "^5.7.0"
51
+ },
52
+ "keywords": [
53
+ "astro",
54
+ "astro-integration",
55
+ "documentation",
56
+ "barodoc",
57
+ "theme"
58
+ ],
59
+ "license": "MIT",
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "https://github.com/barocss/barodoc.git",
63
+ "directory": "packages/theme-docs"
64
+ },
65
+ "scripts": {
66
+ "typecheck": "tsc --noEmit"
67
+ }
68
+ }
@@ -0,0 +1,173 @@
1
+ ---
2
+ // This component adds copy functionality and filename display to code blocks
3
+ ---
4
+
5
+ <script>
6
+ function initCodeCopy() {
7
+ document.querySelectorAll('pre').forEach((pre) => {
8
+ // Skip if already processed
9
+ if (pre.closest('.code-block-wrapper')) return;
10
+
11
+ const code = pre.querySelector('code');
12
+ if (!code) return;
13
+
14
+ // Get filename from data attribute or class (e.g., language-js:filename.js)
15
+ let filename = pre.getAttribute('data-filename') || '';
16
+ const langClass = code.className.match(/language-(\S+)/);
17
+ const lang = langClass ? langClass[1].split(':')[0] : '';
18
+
19
+ // Check if filename is in the language class (e.g., language-js:app.js)
20
+ if (!filename && langClass && langClass[1].includes(':')) {
21
+ filename = langClass[1].split(':')[1];
22
+ }
23
+
24
+ // Create wrapper
25
+ const wrapper = document.createElement('div');
26
+ wrapper.className = 'code-block-wrapper';
27
+
28
+ // Create header if filename exists
29
+ if (filename) {
30
+ const header = document.createElement('div');
31
+ header.className = 'code-block-header';
32
+ header.innerHTML = `
33
+ <span class="code-block-filename">${filename}</span>
34
+ `;
35
+ wrapper.appendChild(header);
36
+ }
37
+
38
+ // Create copy button
39
+ const button = document.createElement('button');
40
+ button.className = 'copy-button';
41
+ button.innerHTML = `
42
+ <span class="copy-text">Copy</span>
43
+ <svg class="copy-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
44
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
45
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
46
+ </svg>
47
+ <svg class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
48
+ <polyline points="20 6 9 17 4 12"></polyline>
49
+ </svg>
50
+ `;
51
+ button.setAttribute('aria-label', 'Copy code');
52
+
53
+ // Add click handler
54
+ button.addEventListener('click', async () => {
55
+ try {
56
+ await navigator.clipboard.writeText(code.textContent || '');
57
+
58
+ // Show check state
59
+ button.classList.add('copied');
60
+ const copyText = button.querySelector('.copy-text');
61
+ if (copyText) copyText.textContent = 'Copied!';
62
+
63
+ setTimeout(() => {
64
+ button.classList.remove('copied');
65
+ if (copyText) copyText.textContent = 'Copy';
66
+ }, 2000);
67
+ } catch (err) {
68
+ console.error('Failed to copy:', err);
69
+ }
70
+ });
71
+
72
+ // Insert wrapper
73
+ pre.parentNode?.insertBefore(wrapper, pre);
74
+ wrapper.appendChild(pre);
75
+ wrapper.appendChild(button);
76
+ });
77
+ }
78
+
79
+ // Run on page load
80
+ initCodeCopy();
81
+
82
+ // Re-run on Astro page transitions
83
+ document.addEventListener('astro:page-load', initCodeCopy);
84
+ </script>
85
+
86
+ <style is:global>
87
+ .code-block-wrapper {
88
+ position: relative;
89
+ margin: 1rem 0;
90
+ border-radius: 0.5rem;
91
+ overflow: hidden;
92
+ background: var(--color-bg-secondary);
93
+ border: 1px solid var(--color-border);
94
+ }
95
+
96
+ .code-block-wrapper pre {
97
+ margin: 0 !important;
98
+ border: none !important;
99
+ border-radius: 0 !important;
100
+ background: transparent !important;
101
+ }
102
+
103
+ .code-block-header {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: space-between;
107
+ padding: 0.5rem 1rem;
108
+ background: var(--color-bg-tertiary);
109
+ border-bottom: 1px solid var(--color-border);
110
+ }
111
+
112
+ .code-block-filename {
113
+ font-size: 0.8125rem;
114
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
115
+ color: var(--color-text-secondary);
116
+ }
117
+
118
+ .copy-button {
119
+ position: absolute;
120
+ top: 0.5rem;
121
+ right: 0.5rem;
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 0.375rem;
125
+ padding: 0.375rem 0.625rem;
126
+ font-size: 0.75rem;
127
+ font-weight: 500;
128
+ background: var(--color-bg);
129
+ border: 1px solid var(--color-border);
130
+ border-radius: 0.375rem;
131
+ cursor: pointer;
132
+ opacity: 0;
133
+ transition: opacity 150ms ease, background 150ms ease;
134
+ color: var(--color-text-muted);
135
+ z-index: 10;
136
+ }
137
+
138
+ .code-block-wrapper:hover .copy-button {
139
+ opacity: 1;
140
+ }
141
+
142
+ .copy-button:hover {
143
+ background: var(--color-bg-secondary);
144
+ color: var(--color-text);
145
+ }
146
+
147
+ .copy-button .copy-icon,
148
+ .copy-button .check-icon {
149
+ flex-shrink: 0;
150
+ }
151
+
152
+ .copy-button .check-icon {
153
+ display: none;
154
+ color: #22c55e;
155
+ }
156
+
157
+ .copy-button.copied .copy-icon {
158
+ display: none;
159
+ }
160
+
161
+ .copy-button.copied .check-icon {
162
+ display: block;
163
+ }
164
+
165
+ .copy-button.copied {
166
+ color: #22c55e;
167
+ }
168
+
169
+ /* Adjust button position when header exists */
170
+ .code-block-wrapper:has(.code-block-header) .copy-button {
171
+ top: 0.375rem;
172
+ }
173
+ </style>
@@ -0,0 +1,166 @@
1
+ import * as React from "react";
2
+ import { Search, Menu, Moon, Sun, Github } from "lucide-react";
3
+ import { Button } from "./ui/button";
4
+ import { Separator } from "./ui/separator";
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipProvider,
9
+ TooltipTrigger,
10
+ } from "./ui/tooltip";
11
+ import { cn } from "../lib/utils";
12
+
13
+ interface DocHeaderProps {
14
+ siteName: string;
15
+ logo?: string;
16
+ githubUrl?: string;
17
+ hasMultipleLocales?: boolean;
18
+ currentLocale?: string;
19
+ localeLabels?: Record<string, string>;
20
+ }
21
+
22
+ export function DocHeader({
23
+ siteName,
24
+ logo,
25
+ githubUrl,
26
+ hasMultipleLocales,
27
+ currentLocale,
28
+ localeLabels,
29
+ }: DocHeaderProps) {
30
+ const [theme, setTheme] = React.useState<"light" | "dark">("light");
31
+
32
+ React.useEffect(() => {
33
+ const isDark = document.documentElement.classList.contains("dark");
34
+ setTheme(isDark ? "dark" : "light");
35
+ }, []);
36
+
37
+ const toggleTheme = () => {
38
+ const newTheme = theme === "light" ? "dark" : "light";
39
+ setTheme(newTheme);
40
+ document.documentElement.classList.toggle("dark", newTheme === "dark");
41
+ localStorage.setItem("theme", newTheme);
42
+ };
43
+
44
+ const openSearch = () => {
45
+ document.dispatchEvent(new CustomEvent("toggle-search"));
46
+ };
47
+
48
+ const openMobileNav = () => {
49
+ document.dispatchEvent(new CustomEvent("toggle-mobile-nav"));
50
+ };
51
+
52
+ return (
53
+ <TooltipProvider>
54
+ <header className="sticky top-0 z-50 w-full border-b border-[var(--color-border)] bg-[var(--color-bg)]/95 backdrop-blur-md supports-[backdrop-filter]:bg-[var(--color-bg)]/80">
55
+ <div className="flex h-14 items-center justify-between px-4 max-w-[1120px] mx-auto">
56
+ {/* Logo */}
57
+ <div className="flex items-center gap-6">
58
+ <a
59
+ href="/"
60
+ className="flex items-center gap-2.5 font-semibold text-[var(--color-text)] hover:opacity-80 transition-opacity"
61
+ >
62
+ {logo && <img src={logo} alt={siteName} className="h-7 w-7" />}
63
+ <span className="text-lg">{siteName}</span>
64
+ </a>
65
+ </div>
66
+
67
+ {/* Right side actions */}
68
+ <div className="flex items-center gap-1">
69
+ {/* Search button */}
70
+ <Button
71
+ variant="outline"
72
+ className="hidden md:flex items-center gap-3 px-4 py-2 h-10 min-w-[200px] justify-start rounded-xl"
73
+ onClick={openSearch}
74
+ >
75
+ <Search className="h-4 w-4 shrink-0 text-[var(--color-text-muted)]" />
76
+ <span className="flex-1 text-left text-[var(--color-text-muted)]">
77
+ Search...
78
+ </span>
79
+ <kbd className="hidden lg:inline-flex items-center gap-0.5 px-2 py-1 text-xs font-medium bg-[var(--color-bg)] border border-[var(--color-border)] rounded-md text-[var(--color-text-muted)]">
80
+ <span>⌘</span>K
81
+ </kbd>
82
+ </Button>
83
+
84
+ {/* Mobile search button */}
85
+ <Tooltip>
86
+ <TooltipTrigger asChild>
87
+ <Button
88
+ variant="ghost"
89
+ size="icon"
90
+ className="md:hidden rounded-xl"
91
+ onClick={openSearch}
92
+ >
93
+ <Search className="h-5 w-5" />
94
+ <span className="sr-only">Search</span>
95
+ </Button>
96
+ </TooltipTrigger>
97
+ <TooltipContent>Search</TooltipContent>
98
+ </Tooltip>
99
+
100
+ {/* Divider */}
101
+ <Separator orientation="vertical" className="hidden md:block h-6 mx-2" />
102
+
103
+ {/* GitHub link */}
104
+ {githubUrl && (
105
+ <Tooltip>
106
+ <TooltipTrigger asChild>
107
+ <Button
108
+ variant="ghost"
109
+ size="icon"
110
+ className="rounded-xl"
111
+ asChild
112
+ >
113
+ <a
114
+ href={githubUrl}
115
+ target="_blank"
116
+ rel="noopener noreferrer"
117
+ >
118
+ <Github className="h-5 w-5" />
119
+ <span className="sr-only">GitHub</span>
120
+ </a>
121
+ </Button>
122
+ </TooltipTrigger>
123
+ <TooltipContent>GitHub</TooltipContent>
124
+ </Tooltip>
125
+ )}
126
+
127
+ {/* Theme toggle */}
128
+ <Tooltip>
129
+ <TooltipTrigger asChild>
130
+ <Button
131
+ variant="ghost"
132
+ size="icon"
133
+ className="rounded-xl"
134
+ onClick={toggleTheme}
135
+ >
136
+ {theme === "light" ? (
137
+ <Moon className="h-5 w-5" />
138
+ ) : (
139
+ <Sun className="h-5 w-5" />
140
+ )}
141
+ <span className="sr-only">Toggle theme</span>
142
+ </Button>
143
+ </TooltipTrigger>
144
+ <TooltipContent>
145
+ {theme === "light" ? "Dark mode" : "Light mode"}
146
+ </TooltipContent>
147
+ </Tooltip>
148
+
149
+ {/* Mobile menu button */}
150
+ <Button
151
+ variant="ghost"
152
+ size="icon"
153
+ className="lg:hidden rounded-xl"
154
+ onClick={openMobileNav}
155
+ >
156
+ <Menu className="h-5 w-5" />
157
+ <span className="sr-only">Menu</span>
158
+ </Button>
159
+ </div>
160
+ </div>
161
+ </header>
162
+ </TooltipProvider>
163
+ );
164
+ }
165
+
166
+ export default DocHeader;
@@ -0,0 +1,84 @@
1
+ import * as React from "react";
2
+ import { ChevronRight } from "lucide-react";
3
+ import { cn } from "../lib/utils";
4
+ import { ScrollArea } from "./ui/scroll-area";
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "./ui/collapsible";
10
+
11
+ interface NavItem {
12
+ title: string;
13
+ href: string;
14
+ isActive?: boolean;
15
+ }
16
+
17
+ interface NavGroup {
18
+ title: string;
19
+ items: NavItem[];
20
+ defaultOpen?: boolean;
21
+ }
22
+
23
+ interface DocsSidebarProps {
24
+ groups: NavGroup[];
25
+ className?: string;
26
+ }
27
+
28
+ export function DocsSidebar({ groups, className }: DocsSidebarProps) {
29
+ return (
30
+ <ScrollArea className={cn("h-full", className)}>
31
+ <nav className="space-y-4">
32
+ {groups.map((group, index) => (
33
+ <SidebarGroup key={index} group={group} />
34
+ ))}
35
+ </nav>
36
+ </ScrollArea>
37
+ );
38
+ }
39
+
40
+ function SidebarGroup({ group }: { group: NavGroup }) {
41
+ const [isOpen, setIsOpen] = React.useState(group.defaultOpen ?? true);
42
+ const hasActiveItem = group.items.some((item) => item.isActive);
43
+
44
+ return (
45
+ <Collapsible open={isOpen} onOpenChange={setIsOpen}>
46
+ <CollapsibleTrigger className="group flex w-full items-center gap-1.5 py-1 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)] hover:text-[var(--color-text)] transition-colors">
47
+ <ChevronRight
48
+ className={cn(
49
+ "h-3 w-3 transition-transform duration-200",
50
+ isOpen && "rotate-90"
51
+ )}
52
+ />
53
+ {group.title}
54
+ </CollapsibleTrigger>
55
+ <CollapsibleContent className="overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
56
+ <ul className="mt-1 space-y-0.5 border-l border-[var(--color-border)] ml-1.5">
57
+ {group.items.map((item, index) => (
58
+ <SidebarItem key={index} item={item} />
59
+ ))}
60
+ </ul>
61
+ </CollapsibleContent>
62
+ </Collapsible>
63
+ );
64
+ }
65
+
66
+ function SidebarItem({ item }: { item: NavItem }) {
67
+ return (
68
+ <li className="relative">
69
+ <a
70
+ href={item.href}
71
+ className={cn(
72
+ "block pl-4 pr-2 py-1.5 text-sm transition-all duration-150 -ml-px border-l-2",
73
+ item.isActive
74
+ ? "border-primary-500 text-primary-600 dark:text-primary-400 font-medium bg-primary-50/50 dark:bg-primary-950/30"
75
+ : "border-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:border-[var(--color-border)]"
76
+ )}
77
+ >
78
+ <span className="truncate">{item.title}</span>
79
+ </a>
80
+ </li>
81
+ );
82
+ }
83
+
84
+ export default DocsSidebar;
@@ -0,0 +1,32 @@
1
+ ---
2
+ import { DocHeader } from "./DocHeader";
3
+ import LanguageSwitcher from "./LanguageSwitcher.astro";
4
+ import config from "virtual:barodoc/config";
5
+ import { locales, defaultLocale } from "virtual:barodoc/i18n";
6
+
7
+ interface Props {
8
+ currentLocale?: string;
9
+ }
10
+
11
+ const { currentLocale = defaultLocale } = Astro.props;
12
+ const hasMultipleLocales = locales.length > 1;
13
+
14
+ // Build locale labels from config
15
+ const localeLabels: Record<string, string> = config.i18n?.labels || {};
16
+ ---
17
+
18
+ <DocHeader
19
+ client:load
20
+ siteName={config.name}
21
+ logo={config.logo}
22
+ githubUrl={config.topbar?.github}
23
+ hasMultipleLocales={hasMultipleLocales}
24
+ currentLocale={currentLocale}
25
+ localeLabels={localeLabels}
26
+ />
27
+
28
+ {hasMultipleLocales && (
29
+ <div class="hidden">
30
+ <LanguageSwitcher currentLocale={currentLocale} />
31
+ </div>
32
+ )}
@@ -0,0 +1,77 @@
1
+ ---
2
+ import { i18n, locales, defaultLocale } from "virtual:barodoc/i18n";
3
+ import { getLocaleLabel, getLocalizedPath } from "@barodoc/core";
4
+
5
+ interface Props {
6
+ currentLocale: string;
7
+ }
8
+
9
+ const { currentLocale } = Astro.props;
10
+ const currentPath = Astro.url.pathname;
11
+
12
+ function getLocalizedUrl(locale: string): string {
13
+ // Remove current locale from path if present
14
+ let path = currentPath;
15
+ for (const loc of locales) {
16
+ if (path.startsWith(`/${loc}/`) || path === `/${loc}`) {
17
+ path = path.slice(loc.length + 1) || "/";
18
+ break;
19
+ }
20
+ }
21
+
22
+ // Add new locale
23
+ if (locale === defaultLocale) {
24
+ return path;
25
+ }
26
+ return `/${locale}${path}`;
27
+ }
28
+ ---
29
+
30
+ <div class="relative">
31
+ <button
32
+ id="lang-switcher-btn"
33
+ class="flex items-center gap-1 px-2 py-1.5 text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] rounded-lg hover:bg-[var(--color-bg-secondary)] transition-colors"
34
+ aria-label="Select language"
35
+ >
36
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
37
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129"></path>
38
+ </svg>
39
+ <span class="hidden sm:inline">{getLocaleLabel(currentLocale, i18n?.labels)}</span>
40
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
41
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
42
+ </svg>
43
+ </button>
44
+
45
+ <div
46
+ id="lang-switcher-menu"
47
+ class="hidden absolute right-0 mt-1 py-1 w-32 bg-[var(--color-bg)] border border-[var(--color-border)] rounded-lg shadow-lg z-50"
48
+ >
49
+ {locales.map((locale) => (
50
+ <a
51
+ href={getLocalizedUrl(locale)}
52
+ class:list={[
53
+ "block px-3 py-1.5 text-sm transition-colors",
54
+ locale === currentLocale
55
+ ? "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300"
56
+ : "text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)]"
57
+ ]}
58
+ >
59
+ {getLocaleLabel(locale, i18n?.labels)}
60
+ </a>
61
+ ))}
62
+ </div>
63
+ </div>
64
+
65
+ <script>
66
+ const btn = document.getElementById('lang-switcher-btn');
67
+ const menu = document.getElementById('lang-switcher-menu');
68
+
69
+ btn?.addEventListener('click', (e) => {
70
+ e.stopPropagation();
71
+ menu?.classList.toggle('hidden');
72
+ });
73
+
74
+ document.addEventListener('click', () => {
75
+ menu?.classList.add('hidden');
76
+ });
77
+ </script>
@@ -0,0 +1,61 @@
1
+ ---
2
+ import { MobileNavSheet } from "./MobileNavSheet";
3
+ import config from "virtual:barodoc/config";
4
+ import { defaultLocale } from "virtual:barodoc/i18n";
5
+ import { getLocalizedNavGroup } from "@barodoc/core";
6
+
7
+ interface Props {
8
+ currentPath: string;
9
+ currentLocale?: string;
10
+ }
11
+
12
+ const { currentPath, currentLocale = defaultLocale } = Astro.props;
13
+
14
+ // Normalize path for comparison
15
+ function normalizePath(path: string): string {
16
+ return path.replace(/\/$/, '').replace(/^\//, '');
17
+ }
18
+
19
+ function isActive(page: string): boolean {
20
+ const normalized = normalizePath(currentPath);
21
+ const pagePath = normalizePath(page);
22
+ return normalized === pagePath ||
23
+ normalized === `docs/${pagePath}` ||
24
+ normalized === `${currentLocale}/docs/${pagePath}`;
25
+ }
26
+
27
+ function getPageHref(page: string): string {
28
+ if (currentLocale === defaultLocale) {
29
+ return `/docs/${page}`;
30
+ }
31
+ return `/${currentLocale}/docs/${page}`;
32
+ }
33
+
34
+ // Get page title from the page slug
35
+ function getPageTitle(page: string): string {
36
+ const parts = page.split('/');
37
+ const name = parts[parts.length - 1];
38
+ return name
39
+ .split('-')
40
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
41
+ .join(' ');
42
+ }
43
+
44
+ // Transform config.navigation to the format expected by MobileNavSheet
45
+ const groups = config.navigation.map((group) => ({
46
+ title: getLocalizedNavGroup(group, currentLocale, defaultLocale),
47
+ defaultOpen: true,
48
+ items: group.pages.map((page) => ({
49
+ title: getPageTitle(page),
50
+ href: getPageHref(page),
51
+ isActive: isActive(page),
52
+ })),
53
+ }));
54
+ ---
55
+
56
+ <MobileNavSheet
57
+ client:load
58
+ groups={groups}
59
+ siteName={config.name}
60
+ logo={config.logo}
61
+ />