zudoku 0.3.0-dev.83 → 0.3.0-dev.85
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/cli.js +5 -1
- package/dist/app/demo.js +5 -4
- package/dist/app/demo.js.map +1 -1
- package/dist/app/main.js +3 -1
- package/dist/app/main.js.map +1 -1
- package/dist/app/standalone.js +5 -4
- package/dist/app/standalone.js.map +1 -1
- package/dist/config/validators/ResolvedSidebarSchema.d.ts +18 -0
- package/dist/config/validators/ResolvedSidebarSchema.js +76 -0
- package/dist/config/validators/ResolvedSidebarSchema.js.map +1 -0
- package/dist/config/validators/SidebarSchema.d.ts +177 -0
- package/dist/config/validators/SidebarSchema.js +71 -0
- package/dist/config/validators/SidebarSchema.js.map +1 -0
- package/dist/config/validators/validate.d.ts +411 -59
- package/dist/config/validators/validate.js +22 -4
- package/dist/config/validators/validate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/lib/components/DevPortal.js +1 -1
- package/dist/lib/components/DevPortal.js.map +1 -1
- package/dist/lib/components/Header.js +5 -4
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/Heading.d.ts +1 -1
- package/dist/lib/components/Layout.js +2 -2
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/TopNavigation.js +5 -5
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/context/DevPortalProvider.d.ts +9 -3
- package/dist/lib/components/context/DevPortalProvider.js +11 -23
- package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
- package/dist/lib/components/context/ThemeContext.d.ts +1 -4
- package/dist/lib/components/context/ThemeContext.js +3 -29
- package/dist/lib/components/context/ThemeContext.js.map +1 -1
- package/dist/lib/components/context/ThemeProvider.d.ts +4 -0
- package/dist/lib/components/context/ThemeProvider.js +23 -0
- package/dist/lib/components/context/ThemeProvider.js.map +1 -0
- package/dist/lib/components/navigation/Sidebar.d.ts +1 -0
- package/dist/lib/components/navigation/Sidebar.js +12 -0
- package/dist/lib/components/navigation/Sidebar.js.map +1 -0
- package/dist/lib/components/navigation/SidebarBadge.d.ts +22 -0
- package/dist/lib/components/navigation/SidebarBadge.js +24 -0
- package/dist/lib/components/navigation/SidebarBadge.js.map +1 -0
- package/dist/lib/components/navigation/SidebarCategory.d.ts +5 -0
- package/dist/lib/components/navigation/SidebarCategory.js +33 -0
- package/dist/lib/components/navigation/SidebarCategory.js.map +1 -0
- package/dist/lib/components/navigation/SidebarItem.d.ts +12 -0
- package/dist/lib/components/navigation/SidebarItem.js +42 -0
- package/dist/lib/components/navigation/SidebarItem.js.map +1 -0
- package/dist/lib/components/navigation/{SideNavigationWrapper.d.ts → SidebarWrapper.d.ts} +1 -1
- package/dist/lib/components/navigation/{SideNavigationWrapper.js → SidebarWrapper.js} +2 -2
- package/dist/lib/components/navigation/SidebarWrapper.js.map +1 -0
- package/dist/lib/components/navigation/utils.d.ts +16 -0
- package/dist/lib/components/navigation/utils.js +85 -0
- package/dist/lib/components/navigation/utils.js.map +1 -0
- package/dist/lib/core/DevPortalContext.d.ts +9 -32
- package/dist/lib/core/DevPortalContext.js +8 -5
- package/dist/lib/core/DevPortalContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +6 -8
- package/dist/lib/core/plugins.js.map +1 -1
- package/dist/lib/plugins/markdown/MdxPage.js +5 -36
- package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
- package/dist/lib/plugins/markdown/generateRoutes.js +20 -43
- package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationListItem.js +4 -3
- package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterList.js +2 -1
- package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
- package/dist/lib/plugins/openapi/SchemaListView.js +6 -1
- package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -1
- package/dist/lib/plugins/openapi/SchemaListViewItem.d.ts +3 -2
- package/dist/lib/plugins/openapi/SchemaListViewItem.js +6 -3
- package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +12 -2
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/index.js +14 -11
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/interfaces.d.ts +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.js +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.js +1 -1
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/util/useScrollToAnchor.js +31 -17
- package/dist/lib/util/useScrollToAnchor.js.map +1 -1
- package/dist/vite/plugin-sidebar.d.ts +3 -0
- package/dist/vite/plugin-sidebar.js +23 -0
- package/dist/vite/plugin-sidebar.js.map +1 -0
- package/dist/vite/plugin.js +2 -0
- package/dist/vite/plugin.js.map +1 -1
- package/lib/{AuthenticationPlugin-XS0DoAhE.js → AuthenticationPlugin-DgwV0hVu.js} +7 -7
- package/lib/{AuthenticationPlugin-XS0DoAhE.js.map → AuthenticationPlugin-DgwV0hVu.js.map} +1 -1
- package/lib/{CategoryHeading-DCmchnA1.js → CategoryHeading-BWq12Bfa.js} +3 -3
- package/lib/{CategoryHeading-DCmchnA1.js.map → CategoryHeading-BWq12Bfa.js.map} +1 -1
- package/lib/{Combination-C442XfGG.js → Combination-DkycFHkm.js} +4 -4
- package/lib/{Combination-C442XfGG.js.map → Combination-DkycFHkm.js.map} +1 -1
- package/lib/{DevPortalProvider-BWeAysxF.js → DevPortalProvider-CTxoCHIT.js} +375 -417
- package/lib/DevPortalProvider-CTxoCHIT.js.map +1 -0
- package/lib/DeveloperHint-BQSFXH01.js +10 -0
- package/lib/{DeveloperHint-DQVwIery.js.map → DeveloperHint-BQSFXH01.js.map} +1 -1
- package/lib/{Input-3IEt27jb.js → Input-BclXSY0g.js} +5 -5
- package/lib/{Input-3IEt27jb.js.map → Input-BclXSY0g.js.map} +1 -1
- package/lib/{Markdown-QsZ-PHET.js → Markdown-B_Gax7at.js} +1136 -1152
- package/lib/{Markdown-QsZ-PHET.js.map → Markdown-B_Gax7at.js.map} +1 -1
- package/lib/{MdxPage-CA1WmW14.js → MdxPage-Crlr0GmN.js} +67 -81
- package/lib/MdxPage-Crlr0GmN.js.map +1 -0
- package/lib/{OperationList-CHK_erYP.js → OperationList-CMH3DPpj.js} +145 -129
- package/lib/OperationList-CMH3DPpj.js.map +1 -0
- package/lib/Route-CwXfyIUw.js +14 -0
- package/lib/{Route-D70pGn9n.js.map → Route-CwXfyIUw.js.map} +1 -1
- package/lib/{SlotletProvider-B71hNEUL.js → SlotletProvider-CzMAO73_.js} +12 -12
- package/lib/{SlotletProvider-B71hNEUL.js.map → SlotletProvider-CzMAO73_.js.map} +1 -1
- package/lib/Spinner-fF-Xv-gw.js +274 -0
- package/lib/Spinner-fF-Xv-gw.js.map +1 -0
- package/lib/index-7kcHaXD6.js +1771 -0
- package/lib/index-7kcHaXD6.js.map +1 -0
- package/lib/{index-Bl6YeerK.js → index-DUrF63A6.js} +1028 -1044
- package/lib/index-DUrF63A6.js.map +1 -0
- package/lib/{index-BH-Ub36F.js → index-DkuZvRNP.js} +4 -4
- package/lib/{index-BH-Ub36F.js.map → index-DkuZvRNP.js.map} +1 -1
- package/lib/joinPath-VeNuJa7y.js +8 -0
- package/lib/joinPath-VeNuJa7y.js.map +1 -0
- package/lib/jsx-runtime-B6kdoens.js +635 -0
- package/lib/jsx-runtime-B6kdoens.js.map +1 -0
- package/lib/{AnchorLink-BZcpTwOs.js → utils-CzT_9Tsn.js} +258 -214
- package/lib/utils-CzT_9Tsn.js.map +1 -0
- package/lib/zudoku.auth-clerk.js +1 -1
- package/lib/zudoku.auth-openid.js +2 -2
- package/lib/zudoku.components.js +1229 -1227
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +19 -19
- package/lib/zudoku.plugin-custom-page.js +2 -2
- package/lib/zudoku.plugin-markdown.js +21 -38
- package/lib/zudoku.plugin-markdown.js.map +1 -1
- package/lib/zudoku.plugin-openapi.js +8 -6
- package/lib/zudoku.plugin-openapi.js.map +1 -1
- package/package.json +4 -1
- package/src/app/demo.tsx +5 -4
- package/src/app/main.css +2 -2
- package/src/app/main.tsx +3 -1
- package/src/app/standalone.tsx +5 -4
- package/src/lib/components/DevPortal.tsx +1 -1
- package/src/lib/components/Header.tsx +21 -19
- package/src/lib/components/Layout.tsx +2 -2
- package/src/lib/components/TopNavigation.tsx +5 -5
- package/src/lib/components/context/DevPortalProvider.ts +11 -28
- package/src/lib/components/context/ThemeContext.tsx +3 -41
- package/src/lib/components/context/ThemeProvider.tsx +27 -0
- package/src/lib/components/navigation/{SideNavigation.tsx → Sidebar.tsx} +7 -7
- package/src/lib/components/navigation/SidebarBadge.tsx +40 -0
- package/src/lib/components/navigation/SidebarCategory.tsx +105 -0
- package/src/lib/components/navigation/SidebarItem.tsx +96 -0
- package/src/lib/components/navigation/{SideNavigationWrapper.tsx → SidebarWrapper.tsx} +1 -1
- package/src/lib/components/navigation/utils.ts +120 -0
- package/src/lib/core/DevPortalContext.ts +12 -44
- package/src/lib/core/plugins.ts +6 -13
- package/src/lib/plugins/markdown/MdxPage.tsx +14 -50
- package/src/lib/plugins/markdown/generateRoutes.tsx +29 -57
- package/src/lib/plugins/openapi/OperationListItem.tsx +5 -6
- package/src/lib/plugins/openapi/ParameterList.tsx +13 -10
- package/src/lib/plugins/openapi/SchemaListView.tsx +18 -2
- package/src/lib/plugins/openapi/SchemaListViewItem.tsx +16 -7
- package/src/lib/plugins/openapi/Sidecar.tsx +15 -2
- package/src/lib/plugins/openapi/index.tsx +17 -23
- package/src/lib/plugins/openapi/interfaces.ts +1 -1
- package/src/lib/plugins/openapi/playground/Headers.tsx +1 -1
- package/src/lib/plugins/openapi/playground/Playground.tsx +1 -1
- package/src/lib/util/useScrollToAnchor.ts +39 -18
- package/dist/lib/components/navigation/SideNavigation.d.ts +0 -1
- package/dist/lib/components/navigation/SideNavigation.js +0 -12
- package/dist/lib/components/navigation/SideNavigation.js.map +0 -1
- package/dist/lib/components/navigation/SideNavigationCategory.d.ts +0 -4
- package/dist/lib/components/navigation/SideNavigationCategory.js +0 -26
- package/dist/lib/components/navigation/SideNavigationCategory.js.map +0 -1
- package/dist/lib/components/navigation/SideNavigationItem.d.ts +0 -9
- package/dist/lib/components/navigation/SideNavigationItem.js +0 -44
- package/dist/lib/components/navigation/SideNavigationItem.js.map +0 -1
- package/dist/lib/components/navigation/SideNavigationWrapper.js.map +0 -1
- package/dist/lib/components/navigation/useNavigationCollapsibleState.d.ts +0 -9
- package/dist/lib/components/navigation/useNavigationCollapsibleState.js +0 -28
- package/dist/lib/components/navigation/useNavigationCollapsibleState.js.map +0 -1
- package/dist/lib/components/navigation/util.d.ts +0 -8
- package/dist/lib/components/navigation/util.js +0 -15
- package/dist/lib/components/navigation/util.js.map +0 -1
- package/dist/lib/plugins/openapi/MethodBadge.d.ts +0 -13
- package/dist/lib/plugins/openapi/MethodBadge.js +0 -26
- package/dist/lib/plugins/openapi/MethodBadge.js.map +0 -1
- package/dist/lib/util/traverseNavigation.d.ts +0 -6
- package/dist/lib/util/traverseNavigation.js +0 -30
- package/dist/lib/util/traverseNavigation.js.map +0 -1
- package/lib/AnchorLink-BZcpTwOs.js.map +0 -1
- package/lib/DevPortalProvider-BWeAysxF.js.map +0 -1
- package/lib/DeveloperHint-DQVwIery.js +0 -10
- package/lib/MdxPage-CA1WmW14.js.map +0 -1
- package/lib/OperationList-CHK_erYP.js.map +0 -1
- package/lib/Route-D70pGn9n.js +0 -13
- package/lib/Spinner-Coi7ORUV.js +0 -244
- package/lib/Spinner-Coi7ORUV.js.map +0 -1
- package/lib/index-Bl6YeerK.js.map +0 -1
- package/lib/index-Dt-pU7Vu.js +0 -916
- package/lib/index-Dt-pU7Vu.js.map +0 -1
- package/lib/jsx-runtime-CJBdjYYx.js +0 -1526
- package/lib/jsx-runtime-CJBdjYYx.js.map +0 -1
- package/src/lib/components/navigation/SideNavigationCategory.tsx +0 -72
- package/src/lib/components/navigation/SideNavigationItem.tsx +0 -148
- package/src/lib/components/navigation/useNavigationCollapsibleState.ts +0 -42
- package/src/lib/components/navigation/util.ts +0 -38
- package/src/lib/plugins/openapi/MethodBadge.tsx +0 -36
- package/src/lib/util/traverseNavigation.ts +0 -55
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cn } from "../../util/cn.js";
|
|
2
|
+
|
|
3
|
+
export const TextColorMap = {
|
|
4
|
+
green: "text-green-600",
|
|
5
|
+
blue: "text-sky-600",
|
|
6
|
+
yellow: "text-yellow-600",
|
|
7
|
+
red: "text-red-600",
|
|
8
|
+
purple: "text-purple-600",
|
|
9
|
+
indigo: "text-indigo-600",
|
|
10
|
+
gray: "text-gray-600",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const ColorMap = {
|
|
14
|
+
green: "bg-green-400 dark:bg-green-800",
|
|
15
|
+
blue: "bg-sky-400 dark:bg-sky-800",
|
|
16
|
+
yellow: "bg-yellow-400 dark:bg-yellow-800",
|
|
17
|
+
red: "bg-red-400 dark:bg-red-800",
|
|
18
|
+
purple: "bg-purple-400 dark:bg-purple-600",
|
|
19
|
+
indigo: "bg-indigo-400 dark:bg-indigo-600",
|
|
20
|
+
gray: "bg-gray-400 dark:bg-gray-600",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const SidebarBadge = ({
|
|
24
|
+
color,
|
|
25
|
+
label,
|
|
26
|
+
}: {
|
|
27
|
+
color: keyof typeof ColorMap;
|
|
28
|
+
label: string;
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<span
|
|
32
|
+
className={cn(
|
|
33
|
+
"mt-0.5 flex items-center duration-200 transition-opacity text-center uppercase font-mono text-[0.65rem] font-bold rounded text-background dark:text-zinc-50 h-4 px-1",
|
|
34
|
+
ColorMap[color],
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
{label}
|
|
38
|
+
</span>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as Collapsible from "@radix-ui/react-collapsible";
|
|
2
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { NavLink } from "react-router-dom";
|
|
5
|
+
import type { ResolvedSidebarItemCategory } from "../../../config/validators/ResolvedSidebarSchema.js";
|
|
6
|
+
import { cn } from "../../util/cn.js";
|
|
7
|
+
import { joinPath } from "../../util/joinPath.js";
|
|
8
|
+
import { useTopNavigationItem } from "../context/DevPortalProvider.js";
|
|
9
|
+
import { navigationListItem, SidebarItem } from "./SidebarItem.js";
|
|
10
|
+
import { useIsCategoryOpen } from "./utils.js";
|
|
11
|
+
|
|
12
|
+
export const SidebarCategory = ({
|
|
13
|
+
category,
|
|
14
|
+
level,
|
|
15
|
+
}: {
|
|
16
|
+
category: ResolvedSidebarItemCategory;
|
|
17
|
+
level: number;
|
|
18
|
+
}) => {
|
|
19
|
+
const topNavItem = useTopNavigationItem();
|
|
20
|
+
const isCategoryOpen = useIsCategoryOpen(category);
|
|
21
|
+
|
|
22
|
+
const isCollapsible = category.collapsible ?? true;
|
|
23
|
+
const isCollapsed = category.collapsed ?? true;
|
|
24
|
+
const isDefaultOpen = Boolean(
|
|
25
|
+
!isCollapsible || !isCollapsed || isCategoryOpen,
|
|
26
|
+
);
|
|
27
|
+
const [open, setOpen] = useState(isDefaultOpen);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
// this is triggered when an item from the sidebar is clicked
|
|
31
|
+
// and the sidebar, enclosing this item, is not opened
|
|
32
|
+
if (isCategoryOpen) {
|
|
33
|
+
setOpen(true);
|
|
34
|
+
}
|
|
35
|
+
}, [isCategoryOpen]);
|
|
36
|
+
|
|
37
|
+
const ToggleButton = isCollapsible && (
|
|
38
|
+
<button
|
|
39
|
+
type="button"
|
|
40
|
+
onClick={(e) => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
setOpen((prev) => !prev);
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
<ChevronRightIcon
|
|
46
|
+
size={16}
|
|
47
|
+
className="transition shrink-0 group-data-[state=open]:rotate-90"
|
|
48
|
+
/>
|
|
49
|
+
</button>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Collapsible.Root
|
|
54
|
+
className={cn("flex flex-col", level === 0 && "-mx-[--padding-nav-item]")}
|
|
55
|
+
defaultOpen={isDefaultOpen}
|
|
56
|
+
open={open}
|
|
57
|
+
onOpenChange={() => setOpen(true)}
|
|
58
|
+
>
|
|
59
|
+
<Collapsible.Trigger
|
|
60
|
+
className={cn(
|
|
61
|
+
"group text-start",
|
|
62
|
+
navigationListItem({ isActive: false, isTopLevel: level === 0 }),
|
|
63
|
+
isCollapsible
|
|
64
|
+
? "cursor-pointer"
|
|
65
|
+
: "cursor-default hover:bg-transparent",
|
|
66
|
+
)}
|
|
67
|
+
asChild
|
|
68
|
+
disabled={!isCollapsible}
|
|
69
|
+
>
|
|
70
|
+
{category.link?.type === "doc" ? (
|
|
71
|
+
<NavLink to={joinPath(topNavItem?.id, category.link.id)}>
|
|
72
|
+
{({ isActive }) => (
|
|
73
|
+
<div
|
|
74
|
+
className={cn(
|
|
75
|
+
"flex items-center gap-2 justify-between w-full",
|
|
76
|
+
isActive ? "text-primary font-medium" : "text-foreground/80",
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
<div className="truncate">{category.label}</div>
|
|
80
|
+
{ToggleButton}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</NavLink>
|
|
84
|
+
) : (
|
|
85
|
+
<div className="flex items-center justify-between w-full">
|
|
86
|
+
<div className="flex gap-2 truncate w-full">{category.label}</div>
|
|
87
|
+
{ToggleButton}
|
|
88
|
+
</div>
|
|
89
|
+
)}
|
|
90
|
+
</Collapsible.Trigger>
|
|
91
|
+
<Collapsible.Content className="CollapsibleContent ms-[calc(var(--padding-nav-item)*1.125)]">
|
|
92
|
+
<ul className="mt-1 border-l ps-2">
|
|
93
|
+
{category.items.map((item) => (
|
|
94
|
+
<SidebarItem
|
|
95
|
+
key={item.label}
|
|
96
|
+
level={level + 1}
|
|
97
|
+
item={item}
|
|
98
|
+
// activeAnchor={activeAnchor}
|
|
99
|
+
/>
|
|
100
|
+
))}
|
|
101
|
+
</ul>
|
|
102
|
+
</Collapsible.Content>
|
|
103
|
+
</Collapsible.Root>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
import { ExternalLinkIcon } from "lucide-react";
|
|
3
|
+
import { NavLink } from "react-router-dom";
|
|
4
|
+
|
|
5
|
+
import type { ResolvedSidebarItem } from "../../../config/validators/ResolvedSidebarSchema.js";
|
|
6
|
+
import { cn } from "../../util/cn.js";
|
|
7
|
+
import { joinPath } from "../../util/joinPath.js";
|
|
8
|
+
import { AnchorLink } from "../AnchorLink.js";
|
|
9
|
+
import { useTopNavigationItem } from "../context/DevPortalProvider.js";
|
|
10
|
+
import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
|
|
11
|
+
import { SidebarBadge } from "./SidebarBadge.js";
|
|
12
|
+
import { SidebarCategory } from "./SidebarCategory.js";
|
|
13
|
+
|
|
14
|
+
export const navigationListItem = cva(
|
|
15
|
+
"flex px-[--padding-nav-item] py-1.5 rounded-lg hover:bg-accent transition-colors duration-300",
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
isTopLevel: {
|
|
19
|
+
true: "font-semibold",
|
|
20
|
+
},
|
|
21
|
+
isActive: {
|
|
22
|
+
true: "text-primary font-medium",
|
|
23
|
+
false: "text-foreground/80",
|
|
24
|
+
},
|
|
25
|
+
isMuted: {
|
|
26
|
+
true: "text-foreground/30",
|
|
27
|
+
false: "",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const DATA_ANCHOR_ATTR = "data-anchor";
|
|
34
|
+
|
|
35
|
+
export const SidebarItem = ({
|
|
36
|
+
item,
|
|
37
|
+
level = 0,
|
|
38
|
+
}: {
|
|
39
|
+
item: ResolvedSidebarItem;
|
|
40
|
+
basePath?: string;
|
|
41
|
+
level?: number;
|
|
42
|
+
}) => {
|
|
43
|
+
const topNavItem = useTopNavigationItem();
|
|
44
|
+
const { activeAnchor } = useViewportAnchor();
|
|
45
|
+
|
|
46
|
+
switch (item.type) {
|
|
47
|
+
case "category":
|
|
48
|
+
return <SidebarCategory category={item} level={level} />;
|
|
49
|
+
case "doc":
|
|
50
|
+
return (
|
|
51
|
+
<NavLink
|
|
52
|
+
className={({ isActive }) =>
|
|
53
|
+
navigationListItem({ isActive, isTopLevel: level === 0 })
|
|
54
|
+
}
|
|
55
|
+
to={joinPath(topNavItem?.id, item.id)}
|
|
56
|
+
>
|
|
57
|
+
{item.label}
|
|
58
|
+
{item.badge && <SidebarBadge {...item.badge} />}
|
|
59
|
+
</NavLink>
|
|
60
|
+
);
|
|
61
|
+
case "link":
|
|
62
|
+
return item.href.startsWith("#") ? (
|
|
63
|
+
<AnchorLink
|
|
64
|
+
to={item.href}
|
|
65
|
+
{...{ [DATA_ANCHOR_ATTR]: item.href.slice(1) }}
|
|
66
|
+
className={cn(
|
|
67
|
+
"flex gap-2.5 justify-between",
|
|
68
|
+
level === 0 && "-mx-[--padding-nav-item]",
|
|
69
|
+
navigationListItem({
|
|
70
|
+
isActive: item.href.slice(1) === activeAnchor,
|
|
71
|
+
}),
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
{item.label}
|
|
75
|
+
{item.badge && <SidebarBadge {...item.badge} />}
|
|
76
|
+
</AnchorLink>
|
|
77
|
+
) : (
|
|
78
|
+
<a
|
|
79
|
+
className={cn(
|
|
80
|
+
navigationListItem({ isTopLevel: level === 0 }),
|
|
81
|
+
"block",
|
|
82
|
+
)}
|
|
83
|
+
href={item.href}
|
|
84
|
+
target="_blank"
|
|
85
|
+
rel="noopener noreferrer"
|
|
86
|
+
>
|
|
87
|
+
<span className="whitespace-normal">{item.label}</span>
|
|
88
|
+
{/* This prevents that the icon would be positioned in its own line if the text fills an line entirely */}
|
|
89
|
+
<span className="whitespace-nowrap">
|
|
90
|
+
|
|
91
|
+
<ExternalLinkIcon className="inline ml-1" size={12} />
|
|
92
|
+
</span>
|
|
93
|
+
</a>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { forwardRef, type PropsWithChildren } from "react";
|
|
2
2
|
import { cn } from "../../util/cn.js";
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const SidebarWrapper = forwardRef<
|
|
5
5
|
HTMLDivElement,
|
|
6
6
|
PropsWithChildren<{ pushMainContent?: boolean; className?: string }>
|
|
7
7
|
>(function SideNavigation({ children, className, pushMainContent }, ref) {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useLocation } from "react-router-dom";
|
|
2
|
+
import type {
|
|
3
|
+
ResolvedSidebarItem,
|
|
4
|
+
ResolvedSidebarItemCategory,
|
|
5
|
+
} from "../../../config/validators/ResolvedSidebarSchema.js";
|
|
6
|
+
import { joinPath } from "../../util/joinPath.js";
|
|
7
|
+
import {
|
|
8
|
+
useDevPortal,
|
|
9
|
+
useTopNavigationItem,
|
|
10
|
+
} from "../context/DevPortalProvider.js";
|
|
11
|
+
|
|
12
|
+
export type TraverseCallback<T> = (
|
|
13
|
+
item: ResolvedSidebarItem,
|
|
14
|
+
parentCategories: ResolvedSidebarItem[],
|
|
15
|
+
) => T | void;
|
|
16
|
+
|
|
17
|
+
export const traverseSidebar = <T>(
|
|
18
|
+
sidebar: ResolvedSidebarItem[],
|
|
19
|
+
callback: TraverseCallback<T>,
|
|
20
|
+
): T | undefined => {
|
|
21
|
+
for (const item of sidebar) {
|
|
22
|
+
const result = traverseSidebarItem(item, callback);
|
|
23
|
+
if (result !== undefined) return result;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const traverseSidebarItem = <T>(
|
|
28
|
+
item: ResolvedSidebarItem,
|
|
29
|
+
callback: TraverseCallback<T>,
|
|
30
|
+
parentCategories: ResolvedSidebarItem[] = [],
|
|
31
|
+
): T | undefined => {
|
|
32
|
+
const result = callback(item, parentCategories);
|
|
33
|
+
if (result !== undefined) return result;
|
|
34
|
+
|
|
35
|
+
if (item.type === "category") {
|
|
36
|
+
for (const child of item.items) {
|
|
37
|
+
const childResult = traverseSidebarItem(child, callback, [
|
|
38
|
+
...parentCategories,
|
|
39
|
+
item,
|
|
40
|
+
]);
|
|
41
|
+
if (childResult !== undefined) return childResult;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const useCurrentItem = () => {
|
|
47
|
+
const location = useLocation();
|
|
48
|
+
const topNavItem = useTopNavigationItem();
|
|
49
|
+
const { sidebars } = useDevPortal();
|
|
50
|
+
const currentSidebar = topNavItem?.id ? sidebars[topNavItem.id] : [];
|
|
51
|
+
|
|
52
|
+
return traverseSidebar(currentSidebar, (item) => {
|
|
53
|
+
if (
|
|
54
|
+
item.type === "doc" &&
|
|
55
|
+
joinPath(topNavItem?.id, item.id) === location.pathname
|
|
56
|
+
) {
|
|
57
|
+
return item;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const useIsCategoryOpen = (category: ResolvedSidebarItemCategory) => {
|
|
63
|
+
const location = useLocation();
|
|
64
|
+
const topNavItem = useTopNavigationItem();
|
|
65
|
+
|
|
66
|
+
return traverseSidebarItem(category, (item) => {
|
|
67
|
+
if (item.type === "category" && item.link) {
|
|
68
|
+
const categoryLinkPath = joinPath(topNavItem?.id, item.link.id);
|
|
69
|
+
if (categoryLinkPath === location.pathname) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (item.type === "doc") {
|
|
75
|
+
const docPath = joinPath(topNavItem?.id, item.id);
|
|
76
|
+
if (docPath === location.pathname) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const usePrevNext = (): {
|
|
84
|
+
prev?: { label: string; id: string };
|
|
85
|
+
next?: { label: string; id: string };
|
|
86
|
+
} => {
|
|
87
|
+
const currentId = useLocation().pathname;
|
|
88
|
+
const { sidebars } = useDevPortal();
|
|
89
|
+
const topNavItem = useTopNavigationItem();
|
|
90
|
+
const currentSidebar = topNavItem?.id ? sidebars[topNavItem.id] : [];
|
|
91
|
+
|
|
92
|
+
let prev;
|
|
93
|
+
let next;
|
|
94
|
+
|
|
95
|
+
let foundCurrent = false;
|
|
96
|
+
|
|
97
|
+
traverseSidebar(currentSidebar, (item) => {
|
|
98
|
+
const itemId =
|
|
99
|
+
item.type === "doc"
|
|
100
|
+
? joinPath(topNavItem?.id, item.id)
|
|
101
|
+
: item.type === "category" && item.link
|
|
102
|
+
? joinPath(topNavItem?.id, item.link.id)
|
|
103
|
+
: undefined;
|
|
104
|
+
|
|
105
|
+
if (!itemId) return;
|
|
106
|
+
|
|
107
|
+
if (foundCurrent) {
|
|
108
|
+
next = { label: item.label, id: itemId };
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (currentId === itemId) {
|
|
113
|
+
foundCurrent = true;
|
|
114
|
+
} else {
|
|
115
|
+
prev = { label: item.label, id: itemId };
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return { prev, next };
|
|
120
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
-
import { type ReactNode } from "react";
|
|
3
2
|
import { NavigateFunction } from "react-router-dom";
|
|
3
|
+
import type { ResolvedSidebarConfig } from "../../config/validators/ResolvedSidebarSchema.js";
|
|
4
4
|
import { type AuthenticationProvider } from "../authentication/authentication.js";
|
|
5
5
|
import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
|
|
6
|
-
import { type DevPortalPath } from "../components/DevPortal.js";
|
|
7
6
|
import { Slotlets } from "../components/SlotletProvider.js";
|
|
7
|
+
import { joinPath } from "../util/joinPath.js";
|
|
8
8
|
import type { MdxComponentsType } from "../util/MdxComponents.js";
|
|
9
9
|
import {
|
|
10
10
|
type DevPortalPlugin,
|
|
@@ -20,41 +20,6 @@ export interface ApiIdentity {
|
|
|
20
20
|
id: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
type BaseNavigationCategoryItem = {
|
|
24
|
-
label: ReactNode;
|
|
25
|
-
title?: string;
|
|
26
|
-
muted?: boolean;
|
|
27
|
-
icon?: ReactNode;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type PathNavigationCategoryItem = BaseNavigationCategoryItem & {
|
|
31
|
-
path: string;
|
|
32
|
-
children?: NavigationCategoryItem[];
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type HrefNavigationCategoryItem = BaseNavigationCategoryItem & {
|
|
36
|
-
href: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export type NavigationCategoryItem =
|
|
40
|
-
| PathNavigationCategoryItem
|
|
41
|
-
| HrefNavigationCategoryItem;
|
|
42
|
-
|
|
43
|
-
export type NavigationCategory = {
|
|
44
|
-
label: string;
|
|
45
|
-
path?: string;
|
|
46
|
-
expanded?: boolean;
|
|
47
|
-
collapsible?: boolean;
|
|
48
|
-
icon?: ReactNode;
|
|
49
|
-
children: NavigationCategoryItem[];
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type NavigationItem = {
|
|
53
|
-
label: string;
|
|
54
|
-
path: DevPortalPath;
|
|
55
|
-
categories?: NavigationCategory[];
|
|
56
|
-
};
|
|
57
|
-
|
|
58
23
|
export const queryClient = new QueryClient();
|
|
59
24
|
|
|
60
25
|
export type ApiKeyCache = "api-keys";
|
|
@@ -90,7 +55,8 @@ export type ZudokuContextOptions = {
|
|
|
90
55
|
metadata?: Metadata;
|
|
91
56
|
page?: Page;
|
|
92
57
|
authentication?: AuthenticationProvider;
|
|
93
|
-
|
|
58
|
+
topNavigation?: Array<{ id: string; label: string }>;
|
|
59
|
+
sidebars?: ResolvedSidebarConfig;
|
|
94
60
|
plugins?: DevPortalPlugin[];
|
|
95
61
|
slotlets?: Slotlets;
|
|
96
62
|
mdx?: {
|
|
@@ -100,8 +66,9 @@ export type ZudokuContextOptions = {
|
|
|
100
66
|
};
|
|
101
67
|
|
|
102
68
|
export class DevPortalContext {
|
|
103
|
-
public plugins: NonNullable<ZudokuContextOptions["plugins"]
|
|
104
|
-
public
|
|
69
|
+
public plugins: NonNullable<ZudokuContextOptions["plugins"]>;
|
|
70
|
+
public sidebars: NonNullable<ZudokuContextOptions["sidebars"]>;
|
|
71
|
+
public topNavigation: NonNullable<ZudokuContextOptions["topNavigation"]>;
|
|
105
72
|
public meta: ZudokuContextOptions["metadata"];
|
|
106
73
|
public page: ZudokuContextOptions["page"];
|
|
107
74
|
public authentication?: ZudokuContextOptions["authentication"];
|
|
@@ -109,7 +76,8 @@ export class DevPortalContext {
|
|
|
109
76
|
|
|
110
77
|
constructor(config: ZudokuContextOptions) {
|
|
111
78
|
this.plugins = config.plugins ?? [];
|
|
112
|
-
this.
|
|
79
|
+
this.topNavigation = config.topNavigation ?? [];
|
|
80
|
+
this.sidebars = config.sidebars ?? {};
|
|
113
81
|
this.navigationPlugins = this.plugins.filter(isNavigationPlugin);
|
|
114
82
|
this.authentication = config.authentication;
|
|
115
83
|
this.meta = config.metadata;
|
|
@@ -142,10 +110,10 @@ export class DevPortalContext {
|
|
|
142
110
|
return keys.flat();
|
|
143
111
|
};
|
|
144
112
|
|
|
145
|
-
|
|
113
|
+
getPluginNavigation = async (path: string) => {
|
|
146
114
|
const navigations = await Promise.all(
|
|
147
|
-
this.navigationPlugins.map(
|
|
148
|
-
plugin.getNavigation?.(path),
|
|
115
|
+
this.navigationPlugins.map((plugin) =>
|
|
116
|
+
plugin.getNavigation?.(joinPath(path)),
|
|
149
117
|
),
|
|
150
118
|
);
|
|
151
119
|
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import { NavigateFunction, type RouteObject } from "react-router-dom";
|
|
3
|
+
import type { ResolvedSidebar } from "../../config/validators/ResolvedSidebarSchema.js";
|
|
3
4
|
import { MdxComponentsType } from "../util/MdxComponents.js";
|
|
4
|
-
import {
|
|
5
|
-
DevPortalContext,
|
|
6
|
-
type ApiIdentity,
|
|
7
|
-
type NavigationCategory,
|
|
8
|
-
} from "./DevPortalContext.js";
|
|
9
|
-
|
|
10
|
-
export type PluginNavigationCategory = {
|
|
11
|
-
path: string;
|
|
12
|
-
} & NavigationCategory;
|
|
5
|
+
import { DevPortalContext, type ApiIdentity } from "./DevPortalContext.js";
|
|
13
6
|
|
|
14
7
|
export type DevPortalPlugin =
|
|
15
8
|
| CommonPlugin
|
|
@@ -19,7 +12,7 @@ export type DevPortalPlugin =
|
|
|
19
12
|
|
|
20
13
|
export interface NavigationPlugin {
|
|
21
14
|
getRoutes: () => RouteObject[];
|
|
22
|
-
getNavigation?: (path: string) => Promise<
|
|
15
|
+
getNavigation?: (path: string) => Promise<ResolvedSidebar>;
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
export interface ApiIdentityPlugin {
|
|
@@ -27,13 +20,13 @@ export interface ApiIdentityPlugin {
|
|
|
27
20
|
}
|
|
28
21
|
|
|
29
22
|
export interface ProfileMenuPlugin {
|
|
30
|
-
getProfileMenuItems: (context: DevPortalContext) =>
|
|
23
|
+
getProfileMenuItems: (context: DevPortalContext) => ProfileNavigationItem[];
|
|
31
24
|
}
|
|
32
25
|
|
|
33
|
-
export type
|
|
26
|
+
export type ProfileNavigationItem = {
|
|
34
27
|
label: string;
|
|
35
28
|
path?: string;
|
|
36
|
-
children?:
|
|
29
|
+
children?: ProfileNavigationItem[];
|
|
37
30
|
};
|
|
38
31
|
|
|
39
32
|
export interface CommonPlugin {
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { useMDXComponents } from "@mdx-js/react";
|
|
2
2
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
3
|
-
import {
|
|
4
|
-
import { Link
|
|
3
|
+
import { type PropsWithChildren } from "react";
|
|
4
|
+
import { Link } from "react-router-dom";
|
|
5
5
|
import { CategoryHeading } from "../../components/CategoryHeading.js";
|
|
6
6
|
import { Heading } from "../../components/Heading.js";
|
|
7
7
|
import { ProseClasses } from "../../components/Markdown.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
useCurrentItem,
|
|
10
|
+
usePrevNext,
|
|
11
|
+
} from "../../components/navigation/utils.js";
|
|
10
12
|
import type { MdxComponentsType } from "../../util/MdxComponents.js";
|
|
11
13
|
import { cn } from "../../util/cn.js";
|
|
12
14
|
import slugify from "../../util/slugify.js";
|
|
13
|
-
import { traverseNavigation } from "../../util/traverseNavigation.js";
|
|
14
15
|
import { Toc } from "./Toc.js";
|
|
15
16
|
import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
|
|
16
17
|
|
|
@@ -39,17 +40,7 @@ export const MdxPage = ({
|
|
|
39
40
|
defaultOptions?: MarkdownPluginDefaultOptions;
|
|
40
41
|
}
|
|
41
42
|
>) => {
|
|
42
|
-
const
|
|
43
|
-
const location = useLocation();
|
|
44
|
-
|
|
45
|
-
const categoryTitle = navItem
|
|
46
|
-
? traverseNavigation(navItem, (_node, fullPath, parentNodes) => {
|
|
47
|
-
if (fullPath === location.pathname) {
|
|
48
|
-
return parentNodes.at(0)?.label;
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
: undefined;
|
|
52
|
-
|
|
43
|
+
const categoryTitle = useCurrentItem()?.categoryLabel;
|
|
53
44
|
const title = frontmatter.title;
|
|
54
45
|
const category = frontmatter.category ?? categoryTitle;
|
|
55
46
|
const hideToc = frontmatter.toc === false || defaultOptions?.toc === false;
|
|
@@ -65,30 +56,7 @@ export const MdxPage = ({
|
|
|
65
56
|
|
|
66
57
|
const showToc = !hideToc && tocEntries.length > 0;
|
|
67
58
|
|
|
68
|
-
const { prev, next } =
|
|
69
|
-
let prev = { path: "", label: "" as ReactNode };
|
|
70
|
-
let next = { path: "", label: "" as ReactNode };
|
|
71
|
-
let shouldStop = false;
|
|
72
|
-
|
|
73
|
-
if (!navItem) return { prev, next };
|
|
74
|
-
|
|
75
|
-
traverseNavigation(navItem, (node, fullPath) => {
|
|
76
|
-
const item = { path: fullPath, label: node.label };
|
|
77
|
-
|
|
78
|
-
if (shouldStop && isPathItem(node) && !node.children?.length) {
|
|
79
|
-
next = item;
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
if (fullPath === location.pathname) {
|
|
83
|
-
shouldStop = true;
|
|
84
|
-
}
|
|
85
|
-
if (!shouldStop && isPathItem(node) && !node.children?.length) {
|
|
86
|
-
prev = item;
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return { prev, next } as const;
|
|
91
|
-
}, [navItem, location.pathname]);
|
|
59
|
+
const { prev, next } = usePrevNext();
|
|
92
60
|
|
|
93
61
|
return (
|
|
94
62
|
<div className="xl:grid grid-cols-[--sidecar-grid-cols] gap-8 justify-between">
|
|
@@ -119,13 +87,11 @@ export const MdxPage = ({
|
|
|
119
87
|
<>
|
|
120
88
|
<hr />
|
|
121
89
|
<div className="not-prose flex items-center justify-between gap-8">
|
|
122
|
-
{prev
|
|
90
|
+
{prev ? (
|
|
123
91
|
<Link
|
|
124
|
-
to={prev.
|
|
92
|
+
to={prev.id}
|
|
125
93
|
className="flex flex-col items-stretch gap-2 flex-1 truncate border rounded px-6 py-4 text-start hover:border-primary/85 transition shadow-sm hover:shadow-md"
|
|
126
|
-
title={
|
|
127
|
-
typeof prev.label === "string" ? prev.label : undefined
|
|
128
|
-
}
|
|
94
|
+
title={prev.label}
|
|
129
95
|
>
|
|
130
96
|
<div className="text-sm text-muted-foreground">
|
|
131
97
|
← Previous page
|
|
@@ -137,13 +103,11 @@ export const MdxPage = ({
|
|
|
137
103
|
) : (
|
|
138
104
|
<div className="flex-1" />
|
|
139
105
|
)}
|
|
140
|
-
{next
|
|
106
|
+
{next ? (
|
|
141
107
|
<Link
|
|
142
|
-
to={next.
|
|
108
|
+
to={next.id}
|
|
143
109
|
className="flex flex-col items-stretch gap-2 flex-1 truncate border rounded px-6 py-4 text-end hover:border-primary/85 transition shadow-sm hover:shadow-md"
|
|
144
|
-
title={
|
|
145
|
-
typeof next.label === "string" ? next.label : undefined
|
|
146
|
-
}
|
|
110
|
+
title={next.label}
|
|
147
111
|
>
|
|
148
112
|
<div className="text-sm text-muted-foreground">
|
|
149
113
|
Next page →
|