radiant-docs 0.1.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/dist/index.js +312 -0
- package/package.json +38 -0
- package/template/.vscode/extensions.json +4 -0
- package/template/.vscode/launch.json +11 -0
- package/template/astro.config.mjs +216 -0
- package/template/ec.config.mjs +51 -0
- package/template/package-lock.json +12546 -0
- package/template/package.json +51 -0
- package/template/public/favicon.svg +9 -0
- package/template/src/assets/icons/check.svg +33 -0
- package/template/src/assets/icons/danger.svg +37 -0
- package/template/src/assets/icons/info.svg +36 -0
- package/template/src/assets/icons/lightbulb.svg +74 -0
- package/template/src/assets/icons/warning.svg +37 -0
- package/template/src/components/Header.astro +176 -0
- package/template/src/components/MdxPage.astro +49 -0
- package/template/src/components/OpenApiPage.astro +270 -0
- package/template/src/components/Search.astro +362 -0
- package/template/src/components/Sidebar.astro +19 -0
- package/template/src/components/SidebarDropdown.astro +149 -0
- package/template/src/components/SidebarGroup.astro +51 -0
- package/template/src/components/SidebarLink.astro +56 -0
- package/template/src/components/SidebarMenu.astro +46 -0
- package/template/src/components/SidebarSubgroup.astro +136 -0
- package/template/src/components/TableOfContents.astro +480 -0
- package/template/src/components/ThemeSwitcher.astro +84 -0
- package/template/src/components/endpoint/PlaygroundBar.astro +68 -0
- package/template/src/components/endpoint/PlaygroundButton.astro +44 -0
- package/template/src/components/endpoint/PlaygroundField.astro +54 -0
- package/template/src/components/endpoint/PlaygroundForm.astro +203 -0
- package/template/src/components/endpoint/RequestSnippets.astro +308 -0
- package/template/src/components/endpoint/ResponseDisplay.astro +177 -0
- package/template/src/components/endpoint/ResponseFields.astro +224 -0
- package/template/src/components/endpoint/ResponseSnippets.astro +247 -0
- package/template/src/components/sidebar/SidebarEndpointLink.astro +51 -0
- package/template/src/components/sidebar/SidebarOpenApi.astro +207 -0
- package/template/src/components/ui/Field.astro +69 -0
- package/template/src/components/ui/Tag.astro +5 -0
- package/template/src/components/ui/demo/CodeDemo.astro +15 -0
- package/template/src/components/ui/demo/Demo.astro +3 -0
- package/template/src/components/ui/demo/UiDisplay.astro +13 -0
- package/template/src/components/user/Accordian.astro +69 -0
- package/template/src/components/user/AccordianGroup.astro +13 -0
- package/template/src/components/user/Callout.astro +101 -0
- package/template/src/components/user/Step.astro +51 -0
- package/template/src/components/user/Steps.astro +9 -0
- package/template/src/components/user/Tab.astro +25 -0
- package/template/src/components/user/Tabs.astro +122 -0
- package/template/src/content.config.ts +11 -0
- package/template/src/entrypoint.ts +9 -0
- package/template/src/layouts/Layout.astro +92 -0
- package/template/src/lib/component-error.ts +163 -0
- package/template/src/lib/frontmatter-schema.ts +9 -0
- package/template/src/lib/oas.ts +24 -0
- package/template/src/lib/pagefind.ts +88 -0
- package/template/src/lib/routes.ts +316 -0
- package/template/src/lib/utils.ts +59 -0
- package/template/src/lib/validation.ts +1097 -0
- package/template/src/pages/[...slug].astro +77 -0
- package/template/src/styles/global.css +209 -0
- package/template/tsconfig.json +5 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from "astro-icon/components";
|
|
3
|
+
import type { NavMenu } from "../lib/validation";
|
|
4
|
+
import SidebarMenu from "./SidebarMenu.astro";
|
|
5
|
+
import { slugify } from "../lib/utils";
|
|
6
|
+
import { getAllRoutes } from "../lib/routes";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
menu: NavMenu;
|
|
10
|
+
parentSlug?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { menu, parentSlug = "" } = Astro.props;
|
|
14
|
+
|
|
15
|
+
const pathname = Astro.url.pathname;
|
|
16
|
+
|
|
17
|
+
let currentMenuIndex = menu.items.findIndex(
|
|
18
|
+
(i) => slugify(i.label) === pathname.split("/")[1]
|
|
19
|
+
);
|
|
20
|
+
currentMenuIndex = currentMenuIndex === -1 ? 0 : currentMenuIndex;
|
|
21
|
+
|
|
22
|
+
const routes = await getAllRoutes();
|
|
23
|
+
|
|
24
|
+
let firstHrefOfMenuItems: string[] = [];
|
|
25
|
+
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
|
|
28
|
+
for (const route of routes) {
|
|
29
|
+
const slug = route.slug;
|
|
30
|
+
|
|
31
|
+
const parts = slug.split("/");
|
|
32
|
+
const topLevel = parts[0];
|
|
33
|
+
|
|
34
|
+
if (!seen.has(topLevel)) {
|
|
35
|
+
seen.add(topLevel);
|
|
36
|
+
firstHrefOfMenuItems.push(slug);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Calculate the parentSlug for the currently selected menu item
|
|
41
|
+
const selectedMenuItem = menu.items[currentMenuIndex];
|
|
42
|
+
const menuItemSlug = slugify(selectedMenuItem.label);
|
|
43
|
+
const currentPrefix = parentSlug
|
|
44
|
+
? `${parentSlug}/${menuItemSlug}`
|
|
45
|
+
: menuItemSlug;
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
<div x-data=`{
|
|
49
|
+
open: false,
|
|
50
|
+
}`>
|
|
51
|
+
<div
|
|
52
|
+
class:list={[
|
|
53
|
+
"mt-3 mx-2",
|
|
54
|
+
menu.label &&
|
|
55
|
+
"rounded-lg bg-neutral-100/80 px-0.5 pb-0.5 inset-shadow-xs inset-shadow-neutral-700/5 border border-neutral-200/60",
|
|
56
|
+
]}
|
|
57
|
+
>
|
|
58
|
+
{
|
|
59
|
+
menu.label && (
|
|
60
|
+
<label class="font-semibold text-xs px-2 py-1.5 block">
|
|
61
|
+
{menu.label}
|
|
62
|
+
</label>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
<div class="relative">
|
|
66
|
+
<button
|
|
67
|
+
class="flex items-center w-full text-sm bg-white border border-neutral-200 rounded-lg shadow-xs px-3 py-2 cursor-pointer"
|
|
68
|
+
x-on:click="open = true"
|
|
69
|
+
aria-haspopup="menu"
|
|
70
|
+
aria-expanded
|
|
71
|
+
>
|
|
72
|
+
{
|
|
73
|
+
menu.items[currentMenuIndex].icon && (
|
|
74
|
+
<Icon
|
|
75
|
+
class="mr-2 size-4"
|
|
76
|
+
name={`lucide:${menu.items[currentMenuIndex].icon}`}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
<span class="font-medium">{menu.items[currentMenuIndex].label}</span>
|
|
81
|
+
<Icon class="ml-auto" name="lucide:chevrons-up-down" />
|
|
82
|
+
</button>
|
|
83
|
+
<div class="fixed inset-0 z-50" x-show="open" x-on:click="open = false">
|
|
84
|
+
</div>
|
|
85
|
+
<ul
|
|
86
|
+
class:list={[
|
|
87
|
+
"z-50 absolute bg-white border border-neutral-200 rounded-lg inset-x-0 top-full py-[3px] shadow-xl overflow-hidden",
|
|
88
|
+
menu.label ? "mt-1.5" : "mt-1",
|
|
89
|
+
]}
|
|
90
|
+
x-init
|
|
91
|
+
role="menu"
|
|
92
|
+
x-show="open"
|
|
93
|
+
x-transition.origin.top
|
|
94
|
+
x-cloak
|
|
95
|
+
>
|
|
96
|
+
{
|
|
97
|
+
menu.items.map(({ label }, index) => {
|
|
98
|
+
return (
|
|
99
|
+
<li
|
|
100
|
+
x-data={`{
|
|
101
|
+
index: ${index}
|
|
102
|
+
}`}
|
|
103
|
+
role="menuitem"
|
|
104
|
+
>
|
|
105
|
+
<a
|
|
106
|
+
class="flex items-center px-3 py-2 cursor-pointer text-sm text-neutral-700 relative z-0 before:-z-10 before:absolute before:inset-x-1 before:inset-y-px before:rounded-md before:duration-150"
|
|
107
|
+
class:list={[
|
|
108
|
+
index === currentMenuIndex
|
|
109
|
+
? "before:bg-neutral-200/50 text-neutral-900"
|
|
110
|
+
: "hover:before:bg-neutral-100/70",
|
|
111
|
+
]}
|
|
112
|
+
href={`/${firstHrefOfMenuItems[index]}`}
|
|
113
|
+
>
|
|
114
|
+
{menu.items[index].icon && (
|
|
115
|
+
<Icon
|
|
116
|
+
class="mr-2 size-4"
|
|
117
|
+
name={`lucide:${menu.items[index].icon}`}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
{label}
|
|
121
|
+
{index === currentMenuIndex && (
|
|
122
|
+
<Icon
|
|
123
|
+
class="ml-auto text-neutral-900 [&_path]:stroke-3"
|
|
124
|
+
name="lucide:check"
|
|
125
|
+
stroke-width={10}
|
|
126
|
+
/>
|
|
127
|
+
)}
|
|
128
|
+
</a>
|
|
129
|
+
</li>
|
|
130
|
+
);
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
</ul>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<div
|
|
137
|
+
class:list={[
|
|
138
|
+
"relative overflow-y-auto",
|
|
139
|
+
menu.label
|
|
140
|
+
? "h-[calc(100vh-4px-64px-12px-70px-52px)]"
|
|
141
|
+
: "h-[calc(100vh-4px-64px-12px-38px-52px)]",
|
|
142
|
+
]}
|
|
143
|
+
>
|
|
144
|
+
<SidebarMenu
|
|
145
|
+
navigation={menu.items[currentMenuIndex].submenu}
|
|
146
|
+
parentSlug={currentPrefix}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { NavGroup } from "../lib/validation";
|
|
3
|
+
import SidebarLink from "./SidebarLink.astro";
|
|
4
|
+
import SidebarSubgroup from "./SidebarSubgroup.astro";
|
|
5
|
+
import { slugify } from "../lib/utils";
|
|
6
|
+
import Tag from "./ui/Tag.astro";
|
|
7
|
+
import { Icon } from "astro-icon/components";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
item: NavGroup;
|
|
11
|
+
parentSlug?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { item, parentSlug = "" } = Astro.props;
|
|
15
|
+
|
|
16
|
+
const groupSlug = slugify(item.group);
|
|
17
|
+
const currentPrefix = parentSlug ? `${parentSlug}/${groupSlug}` : groupSlug;
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<li>
|
|
21
|
+
<div class:list={["text-sm font-semibold mb-2 flex items-center gap-2 px-2"]}>
|
|
22
|
+
{
|
|
23
|
+
item.icon && (
|
|
24
|
+
<Icon name={`lucide:${item.icon}`} class="size-4 text-neutral-500" />
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
{item.group}
|
|
28
|
+
{item.tag && <Tag>{item.tag}</Tag>}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<ul>
|
|
32
|
+
{
|
|
33
|
+
item.pages.map((child) => (
|
|
34
|
+
<li>
|
|
35
|
+
{typeof child === "string" ? (
|
|
36
|
+
<SidebarLink path={child} groupSlug={currentPrefix} />
|
|
37
|
+
) : "page" in child ? (
|
|
38
|
+
<SidebarLink
|
|
39
|
+
path={child.page}
|
|
40
|
+
icon={child.icon}
|
|
41
|
+
tag={child.tag}
|
|
42
|
+
groupSlug={currentPrefix}
|
|
43
|
+
/>
|
|
44
|
+
) : (
|
|
45
|
+
<SidebarSubgroup item={child} parentSlug={currentPrefix} />
|
|
46
|
+
)}
|
|
47
|
+
</li>
|
|
48
|
+
))
|
|
49
|
+
}
|
|
50
|
+
</ul>
|
|
51
|
+
</li>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { deriveTitleFromEntryId, slugify } from "../lib/utils";
|
|
3
|
+
import { Icon } from "astro-icon/components";
|
|
4
|
+
import Tag from "./ui/Tag.astro";
|
|
5
|
+
import { getCollection } from "astro:content";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
path: string;
|
|
9
|
+
groupSlug?: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
tag?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { path: filePath, groupSlug = "", icon, tag } = Astro.props;
|
|
15
|
+
|
|
16
|
+
const docs = await getCollection("docs");
|
|
17
|
+
|
|
18
|
+
// Find the entry matching this path
|
|
19
|
+
const entry = docs.find((doc: any) => {
|
|
20
|
+
const docPath = doc.id.replace(/\.mdx$/, "");
|
|
21
|
+
return docPath === filePath;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!entry) {
|
|
25
|
+
throw new Error("Entry not found");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Generate a nice label from the filename (e.g. "writing-content/page" -> "Page")
|
|
29
|
+
const filename = filePath ? filePath.split("/").pop() || filePath : "";
|
|
30
|
+
const text = entry.data.title || deriveTitleFromEntryId(entry.id);
|
|
31
|
+
|
|
32
|
+
const pageSlug = slugify(filename);
|
|
33
|
+
// If groupSlug exists, join it. Otherwise just use pageSlug (though practically groupSlug should exist from hierarchy)
|
|
34
|
+
const href = groupSlug ? `/${groupSlug}/${pageSlug}` : `/${pageSlug}`;
|
|
35
|
+
|
|
36
|
+
// Normalize paths for comparison (remove trailing slashes)
|
|
37
|
+
const currentPath = Astro.url.pathname.replace(/\/$/, "");
|
|
38
|
+
const targetPath = href.replace(/\/$/, "");
|
|
39
|
+
const isActive = currentPath === targetPath;
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<a
|
|
43
|
+
href={href}
|
|
44
|
+
class:list={[
|
|
45
|
+
"block px-2 py-[7px] text-sm relative z-0 font-[450] before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:duration-150",
|
|
46
|
+
isActive
|
|
47
|
+
? "before:bg-neutral-200/50 dark:before:bg-neutral-800 text-neutral-900 dark:text-neutral-200"
|
|
48
|
+
: "text-neutral-600 dark:text-neutral-400 hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50 hover:text-neutral-900 dark:hover:text-neutral-300",
|
|
49
|
+
]}
|
|
50
|
+
>
|
|
51
|
+
<div class="flex items-center gap-2">
|
|
52
|
+
{icon && <Icon name={`lucide:${icon}`} class="size-4 opacity-75" />}
|
|
53
|
+
{text}
|
|
54
|
+
{tag && <Tag>{tag}</Tag>}
|
|
55
|
+
</div>
|
|
56
|
+
</a>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { NavGroup, NavigationItem, NavPage } from "../lib/validation";
|
|
3
|
+
import SidebarOpenApi from "./sidebar/SidebarOpenApi.astro";
|
|
4
|
+
import SidebarDropdown from "./SidebarDropdown.astro";
|
|
5
|
+
import SidebarGroup from "./SidebarGroup.astro";
|
|
6
|
+
import SidebarLink from "./SidebarLink.astro";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
navigation: NavigationItem;
|
|
10
|
+
parentSlug?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { navigation, parentSlug = "" } = Astro.props;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
navigation.pages ? (
|
|
18
|
+
<ul class="px-2 pt-4 pb-3">
|
|
19
|
+
{navigation.pages.map((item) => (
|
|
20
|
+
<li>
|
|
21
|
+
{typeof item === "string" ? (
|
|
22
|
+
<SidebarLink path={item} />
|
|
23
|
+
) : (
|
|
24
|
+
<SidebarLink
|
|
25
|
+
path={(item as NavPage).page}
|
|
26
|
+
icon={(item as NavPage).icon}
|
|
27
|
+
tag={(item as NavPage).tag}
|
|
28
|
+
/>
|
|
29
|
+
)}
|
|
30
|
+
</li>
|
|
31
|
+
))}
|
|
32
|
+
</ul>
|
|
33
|
+
) : navigation.groups ? (
|
|
34
|
+
<ul class="px-2 pt-4 pb-3 [&>:nth-child(n+2)]:mt-5 [&>:nth-child(n+2)]:pt-[25px] [&>:nth-child(n+2)]:relative [&>:nth-child(n+2)]:before:absolute [&>:nth-child(n+2)]:before:top-0 [&>:nth-child(n+2)]:before:inset-x-0 [&>:nth-child(n+2)]:before:h-px [&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-200)_20%,var(--color-neutral-200)_80%,transparent] dark:[&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-700)_20%,var(--color-neutral-700)_80%,transparent]">
|
|
35
|
+
{navigation.groups.map((item) => (
|
|
36
|
+
<SidebarGroup item={item as NavGroup} parentSlug={parentSlug} />
|
|
37
|
+
))}
|
|
38
|
+
</ul>
|
|
39
|
+
) : navigation.menu ? (
|
|
40
|
+
!navigation.menu.type || navigation.menu.type === "dropdown" ? (
|
|
41
|
+
<SidebarDropdown menu={navigation.menu} parentSlug={parentSlug} />
|
|
42
|
+
) : navigation.menu.type === "collapsible" ? null : null
|
|
43
|
+
) : navigation.openapi ? (
|
|
44
|
+
<SidebarOpenApi openapi={navigation.openapi} parentSlug={parentSlug} />
|
|
45
|
+
) : null
|
|
46
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { NavGroup } from "../lib/validation";
|
|
3
|
+
import SidebarLink from "./SidebarLink.astro";
|
|
4
|
+
import { slugify } from "../lib/utils";
|
|
5
|
+
import Tag from "./ui/Tag.astro";
|
|
6
|
+
import { Icon } from "astro-icon/components";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
item: NavGroup;
|
|
10
|
+
parentSlug: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { item, parentSlug } = Astro.props;
|
|
14
|
+
|
|
15
|
+
const groupSlug = slugify(item.group);
|
|
16
|
+
const currentPrefix = `${parentSlug}/${groupSlug}`;
|
|
17
|
+
|
|
18
|
+
const groupId = `group-${currentPrefix}`;
|
|
19
|
+
const listId = `list-${groupId}`;
|
|
20
|
+
|
|
21
|
+
// Check if any child page is active (shallow check, no recursion needed)
|
|
22
|
+
const containsActivePage = item.pages.some((child) => {
|
|
23
|
+
const pagePath =
|
|
24
|
+
typeof child === "string" ? child : "pages" in child ? null : child.page;
|
|
25
|
+
|
|
26
|
+
if (pagePath) {
|
|
27
|
+
const filename = pagePath.split("/").pop() || pagePath;
|
|
28
|
+
const pageSlug = slugify(filename);
|
|
29
|
+
const href = `/${currentPrefix}/${pageSlug}`;
|
|
30
|
+
|
|
31
|
+
// Normalize paths for comparison (remove trailing slashes)
|
|
32
|
+
const normalizedCurrent = Astro.url.pathname.replace(/\/$/, "");
|
|
33
|
+
const normalizedTarget = href.replace(/\/$/, "");
|
|
34
|
+
return normalizedCurrent === normalizedTarget;
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
});
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<div
|
|
41
|
+
class="group"
|
|
42
|
+
x-data={`{
|
|
43
|
+
expanded: $persist(${item.expanded ?? false}).as('sidebar-state-${groupId}'),
|
|
44
|
+
init() {
|
|
45
|
+
if (${containsActivePage}) {
|
|
46
|
+
this.expanded = true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}`}
|
|
50
|
+
>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
@click="expanded = !expanded"
|
|
54
|
+
class:list={[
|
|
55
|
+
"flex items-center justify-between gap-2 w-full px-2 py-[7px] text-sm font-[450] text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-neutral-300 cursor-pointer relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:transition-colors hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50",
|
|
56
|
+
]}
|
|
57
|
+
>
|
|
58
|
+
<div class="flex items-center gap-2">
|
|
59
|
+
{
|
|
60
|
+
item.icon && (
|
|
61
|
+
<Icon name={`lucide:${item.icon}`} class="size-4 opacity-75" />
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
{item.group}
|
|
65
|
+
{item.tag && <Tag>{item.tag}</Tag>}
|
|
66
|
+
</div>
|
|
67
|
+
<svg
|
|
68
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
69
|
+
width="14"
|
|
70
|
+
height="14"
|
|
71
|
+
viewBox="0 0 24 24"
|
|
72
|
+
fill="none"
|
|
73
|
+
stroke="currentColor"
|
|
74
|
+
stroke-width="2"
|
|
75
|
+
stroke-linecap="round"
|
|
76
|
+
stroke-linejoin="round"
|
|
77
|
+
class="transition-transform duration-200 text-inherit opacity-80"
|
|
78
|
+
:class="expanded ? 'rotate-90' : ''"
|
|
79
|
+
>
|
|
80
|
+
<path d="m9 18 6-6-6-6"></path>
|
|
81
|
+
</svg>
|
|
82
|
+
</button>
|
|
83
|
+
|
|
84
|
+
<ul
|
|
85
|
+
id={listId}
|
|
86
|
+
x-show="expanded"
|
|
87
|
+
x-collapse
|
|
88
|
+
x-cloak
|
|
89
|
+
class="border-l border-neutral-300/80 ml-[15.5px] pl-2"
|
|
90
|
+
>
|
|
91
|
+
{
|
|
92
|
+
item.pages.map((child) => (
|
|
93
|
+
<li>
|
|
94
|
+
{typeof child === "string" ? (
|
|
95
|
+
<SidebarLink path={child} groupSlug={currentPrefix} />
|
|
96
|
+
) : "page" in child ? (
|
|
97
|
+
<SidebarLink
|
|
98
|
+
path={child.page}
|
|
99
|
+
icon={child.icon}
|
|
100
|
+
tag={child.tag}
|
|
101
|
+
groupSlug={currentPrefix}
|
|
102
|
+
/>
|
|
103
|
+
) : null}
|
|
104
|
+
</li>
|
|
105
|
+
))
|
|
106
|
+
}
|
|
107
|
+
</ul>
|
|
108
|
+
|
|
109
|
+
{/* Anti-flash script to prevent content jumping */}
|
|
110
|
+
<script define:vars={{ listId, groupId, containsActivePage }}>
|
|
111
|
+
(function () {
|
|
112
|
+
try {
|
|
113
|
+
const storageKey = "sidebar-state-" + groupId;
|
|
114
|
+
|
|
115
|
+
// 1. Force state in localStorage if this is the active group
|
|
116
|
+
if (containsActivePage) {
|
|
117
|
+
localStorage.setItem(storageKey, "true");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 2. Read state
|
|
121
|
+
const storedState = localStorage.getItem(storageKey);
|
|
122
|
+
const shouldBeVisible = storedState === "true";
|
|
123
|
+
|
|
124
|
+
// 3. Remove x-cloak immediately if it should be visible
|
|
125
|
+
if (shouldBeVisible) {
|
|
126
|
+
const list = document.getElementById(listId);
|
|
127
|
+
if (list) {
|
|
128
|
+
list.removeAttribute("x-cloak");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error(e);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
</script>
|
|
136
|
+
</div>
|