zudoku 0.17.0 → 0.18.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/app/demo.js +0 -2
- package/dist/app/demo.js.map +1 -1
- package/dist/app/entry.client.js +14 -0
- package/dist/app/entry.client.js.map +1 -1
- package/dist/app/entry.server.js +5 -4
- package/dist/app/entry.server.js.map +1 -1
- package/dist/app/standalone.js +0 -2
- package/dist/app/standalone.js.map +1 -1
- package/dist/codegen.d.ts +3 -0
- package/dist/codegen.js +45 -0
- package/dist/codegen.js.map +1 -0
- package/dist/config/validators/InputSidebarSchema.d.ts +10 -10
- package/dist/config/validators/validate.d.ts +74 -74
- package/dist/lib/authentication/hook.d.ts +5 -4
- package/dist/lib/authentication/hook.js +1 -3
- package/dist/lib/authentication/hook.js.map +1 -1
- package/dist/lib/authentication/providers/auth0.js +11 -11
- package/dist/lib/authentication/providers/auth0.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +0 -1
- package/dist/lib/authentication/providers/openid.js +11 -26
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/authentication/state.d.ts +25 -4
- package/dist/lib/authentication/state.js +28 -5
- package/dist/lib/authentication/state.js.map +1 -1
- package/dist/lib/components/Bootstrap.d.ts +3 -1
- package/dist/lib/components/Bootstrap.js +11 -3
- package/dist/lib/components/Bootstrap.js.map +1 -1
- package/dist/lib/components/DeveloperHint.js +2 -1
- package/dist/lib/components/DeveloperHint.js.map +1 -1
- package/dist/lib/components/Header.js +3 -7
- package/dist/lib/components/Header.js.map +1 -1
- package/dist/lib/components/Heading.d.ts +1 -1
- package/dist/lib/components/Layout.js +11 -3
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/components/MobileTopNavigation.js +6 -7
- package/dist/lib/components/MobileTopNavigation.js.map +1 -1
- package/dist/lib/components/SyntaxHighlight.js +16 -12
- package/dist/lib/components/SyntaxHighlight.js.map +1 -1
- package/dist/lib/components/ThemeSwitch.d.ts +1 -0
- package/dist/lib/components/ThemeSwitch.js +13 -0
- package/dist/lib/components/ThemeSwitch.js.map +1 -0
- package/dist/lib/components/TopNavigation.d.ts +2 -0
- package/dist/lib/components/TopNavigation.js +13 -7
- package/dist/lib/components/TopNavigation.js.map +1 -1
- package/dist/lib/components/Zudoku.js +4 -5
- package/dist/lib/components/Zudoku.js.map +1 -1
- package/dist/lib/components/context/ZudokuContext.d.ts +3 -3
- package/dist/lib/components/context/ZudokuContext.js +7 -12
- package/dist/lib/components/context/ZudokuContext.js.map +1 -1
- package/dist/lib/components/index.d.ts +14 -3
- package/dist/lib/components/navigation/Sidebar.js +1 -1
- package/dist/lib/components/navigation/Sidebar.js.map +1 -1
- package/dist/lib/components/navigation/utils.js +2 -2
- package/dist/lib/components/navigation/utils.js.map +1 -1
- package/dist/lib/core/ZudokuContext.d.ts +0 -4
- package/dist/lib/core/ZudokuContext.js +0 -5
- package/dist/lib/core/ZudokuContext.js.map +1 -1
- package/dist/lib/errors/ErrorAlert.js +1 -1
- package/dist/lib/errors/ErrorAlert.js.map +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.js +13 -9
- package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
- package/dist/lib/plugins/openapi/Endpoint.d.ts +1 -1
- package/dist/lib/plugins/openapi/Endpoint.js +5 -9
- package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
- package/dist/lib/plugins/openapi/OperationList.d.ts +2 -2
- package/dist/lib/plugins/openapi/OperationList.js +6 -21
- package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
- package/dist/lib/plugins/openapi/Route.d.ts +4 -4
- package/dist/lib/plugins/openapi/Route.js +2 -4
- package/dist/lib/plugins/openapi/Route.js.map +1 -1
- package/dist/lib/plugins/openapi/Sidecar.d.ts +1 -1
- package/dist/lib/plugins/openapi/Sidecar.js +8 -11
- package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
- package/dist/lib/plugins/openapi/client/GraphQLClient.d.ts +8 -0
- package/dist/lib/plugins/openapi/client/GraphQLClient.js +102 -0
- package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -0
- package/dist/lib/plugins/openapi/client/GraphQLContext.d.ts +7 -0
- package/dist/lib/plugins/openapi/client/GraphQLContext.js +5 -0
- package/dist/lib/plugins/openapi/client/GraphQLContext.js.map +1 -0
- package/dist/lib/plugins/openapi/client/createServer.d.ts +1 -0
- package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +5 -0
- package/dist/lib/plugins/openapi/client/useCreateQuery.js +13 -0
- package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -0
- package/dist/lib/plugins/openapi/client/worker.d.ts +4 -1
- package/dist/lib/plugins/openapi/client/worker.js +23 -14
- package/dist/lib/plugins/openapi/client/worker.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/fragment-masking.d.ts +3 -3
- package/dist/lib/plugins/openapi/graphql/fragment-masking.js +3 -4
- package/dist/lib/plugins/openapi/graphql/fragment-masking.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/gql.d.ts +5 -52
- package/dist/lib/plugins/openapi/graphql/gql.js +2 -1
- package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
- package/dist/lib/plugins/openapi/graphql/graphql.d.ts +134 -9
- package/dist/lib/plugins/openapi/graphql/graphql.js +194 -778
- package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
- package/dist/lib/plugins/openapi/index.js +40 -53
- package/dist/lib/plugins/openapi/index.js.map +1 -1
- package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
- package/dist/lib/plugins/openapi-worker.d.ts +1 -1
- package/dist/lib/plugins/openapi-worker.js +7 -1
- package/dist/lib/plugins/openapi-worker.js.map +1 -1
- package/dist/lib/util/MdxComponents.d.ts +1 -1
- package/dist/vite/config.js +0 -1
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/html.js +0 -2
- package/dist/vite/html.js.map +1 -1
- package/dist/vite/plugin-component.js +1 -1
- package/dist/vite/plugin-component.js.map +1 -1
- package/dist/vite/plugin-mdx.js +3 -2
- package/dist/vite/plugin-mdx.js.map +1 -1
- package/dist/vite/plugin.js +0 -2
- package/dist/vite/plugin.js.map +1 -1
- package/dist/vite/remarkStaticGeneration.d.ts +3 -0
- package/dist/vite/remarkStaticGeneration.js +125 -0
- package/dist/vite/remarkStaticGeneration.js.map +1 -0
- package/lib/{AnchorLink-DYbUOP9U.js → AnchorLink-CDlhr8gL.js} +11 -10
- package/lib/{AnchorLink-DYbUOP9U.js.map → AnchorLink-CDlhr8gL.js.map} +1 -1
- package/lib/{AuthenticationPlugin-bqGAKfot.js → AuthenticationPlugin-DeGDVa1r.js} +6 -5
- package/lib/{AuthenticationPlugin-bqGAKfot.js.map → AuthenticationPlugin-DeGDVa1r.js.map} +1 -1
- package/lib/{Spinner-ChOGyPls.js → Button-jK0EsymC.js} +12 -15
- package/lib/Button-jK0EsymC.js.map +1 -0
- package/lib/Markdown-ievDDhFT.js +15192 -0
- package/lib/Markdown-ievDDhFT.js.map +1 -0
- package/lib/{MdxPage-DRKqyn2b.js → MdxPage-Bwn-VSsH.js} +5 -5
- package/lib/{MdxPage-DRKqyn2b.js.map → MdxPage-Bwn-VSsH.js.map} +1 -1
- package/lib/OperationList-BwBl1xrD.js +4691 -0
- package/lib/OperationList-BwBl1xrD.js.map +1 -0
- package/lib/Route-DlG_HTMu.js +11 -0
- package/lib/Route-DlG_HTMu.js.map +1 -0
- package/lib/{Select-DYKDahHt.js → Select-O9ZM3ZgX.js} +7 -7
- package/lib/Select-O9ZM3ZgX.js.map +1 -0
- package/lib/SidebarBadge-DxFJcJ6V.js +51 -0
- package/lib/SidebarBadge-DxFJcJ6V.js.map +1 -0
- package/lib/SlotletProvider-DyomlzGx.js +252 -0
- package/lib/SlotletProvider-DyomlzGx.js.map +1 -0
- package/lib/Spinner-3cQDBVGr.js +7 -0
- package/lib/Spinner-3cQDBVGr.js.map +1 -0
- package/lib/SyntaxHighlight-DkLOsjHS.js +2983 -0
- package/lib/SyntaxHighlight-DkLOsjHS.js.map +1 -0
- package/lib/assets/{worker-YA-aCP3P.js → worker-CPsGZsve.js} +24 -22
- package/lib/assets/{worker-YA-aCP3P.js.map → worker-CPsGZsve.js.map} +1 -1
- package/lib/context-D1nXWxm7.js +22 -0
- package/lib/context-D1nXWxm7.js.map +1 -0
- package/lib/createServer-DK-g7kbB.js +16089 -0
- package/lib/createServer-DK-g7kbB.js.map +1 -0
- package/lib/{hook-CjQERPa7.js → hook-hEqe7fPB.js} +12 -14
- package/lib/hook-hEqe7fPB.js.map +1 -0
- package/lib/index-Czzd9rjU.js +899 -0
- package/lib/index-Czzd9rjU.js.map +1 -0
- package/lib/index-DNxQ_rCt.js +1273 -0
- package/lib/index-DNxQ_rCt.js.map +1 -0
- package/lib/index-Yn8c3UWE.js +921 -0
- package/lib/index-Yn8c3UWE.js.map +1 -0
- package/lib/{router-BsfSoK2j.js → router-lfyopgBI.js} +23 -23
- package/lib/{router-BsfSoK2j.js.map → router-lfyopgBI.js.map} +1 -1
- package/lib/state-tsXBLONe.js +203 -0
- package/lib/state-tsXBLONe.js.map +1 -0
- package/lib/ui/ActionButton.js +11 -10
- package/lib/ui/ActionButton.js.map +1 -1
- package/lib/useExposedProps-CTPtylCV.js +10 -0
- package/lib/{useExposedProps-BxyHjPNN.js.map → useExposedProps-CTPtylCV.js.map} +1 -1
- package/lib/{utils-DNAltzXc.js → utils-DcpDOncX.js} +197 -202
- package/lib/utils-DcpDOncX.js.map +1 -0
- package/lib/zudoku.auth-auth0.js +24 -18
- package/lib/zudoku.auth-auth0.js.map +1 -1
- package/lib/zudoku.auth-clerk.js +2 -2
- package/lib/zudoku.auth-openid.js +124 -138
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +1133 -992
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.openapi-worker.js +10 -16346
- package/lib/zudoku.openapi-worker.js.map +1 -1
- package/lib/zudoku.plugin-api-keys.js +18 -18
- package/lib/zudoku.plugin-custom-pages.js +2 -2
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +5 -9
- package/lib/zudoku.plugin-openapi.js.map +1 -1
- package/lib/zudoku.plugin-redirect.js +1 -1
- package/package.json +14 -4
- package/src/app/demo.tsx +0 -3
- package/src/app/entry.client.tsx +14 -0
- package/src/app/entry.server.tsx +59 -57
- package/src/app/standalone.tsx +0 -3
- package/src/lib/authentication/hook.ts +1 -3
- package/src/lib/authentication/providers/auth0.tsx +16 -11
- package/src/lib/authentication/providers/openid.tsx +12 -30
- package/src/lib/authentication/state.ts +44 -10
- package/src/lib/components/Bootstrap.tsx +36 -9
- package/src/lib/components/DeveloperHint.tsx +6 -1
- package/src/lib/components/Header.tsx +31 -42
- package/src/lib/components/Layout.tsx +48 -36
- package/src/lib/components/MobileTopNavigation.tsx +15 -18
- package/src/lib/components/SyntaxHighlight.tsx +81 -46
- package/src/lib/components/ThemeSwitch.tsx +26 -0
- package/src/lib/components/TopNavigation.tsx +27 -19
- package/src/lib/components/Zudoku.tsx +5 -10
- package/src/lib/components/context/ZudokuContext.ts +8 -13
- package/src/lib/components/navigation/Sidebar.tsx +3 -3
- package/src/lib/components/navigation/utils.ts +2 -2
- package/src/lib/core/ZudokuContext.ts +0 -8
- package/src/lib/errors/ErrorAlert.tsx +2 -1
- package/src/lib/plugins/openapi/ColorizedParam.tsx +23 -14
- package/src/lib/plugins/openapi/Endpoint.tsx +5 -10
- package/src/lib/plugins/openapi/OperationList.tsx +5 -40
- package/src/lib/plugins/openapi/Route.tsx +11 -12
- package/src/lib/plugins/openapi/Sidecar.tsx +10 -13
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +140 -0
- package/src/lib/plugins/openapi/client/GraphQLContext.tsx +16 -0
- package/src/lib/plugins/openapi/client/createServer.ts +2 -0
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +18 -0
- package/src/lib/plugins/openapi/client/worker.ts +38 -24
- package/src/lib/plugins/openapi/graphql/fragment-masking.ts +11 -18
- package/src/lib/plugins/openapi/graphql/gql.ts +7 -25
- package/src/lib/plugins/openapi/graphql/graphql.ts +351 -782
- package/src/lib/plugins/openapi/index.tsx +40 -63
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +1 -1
- package/src/lib/plugins/openapi-worker.ts +11 -1
- package/dist/lib/components/context/ThemeContext.d.ts +0 -2
- package/dist/lib/components/context/ThemeContext.js +0 -7
- package/dist/lib/components/context/ThemeContext.js.map +0 -1
- package/dist/lib/components/context/ThemeProvider.d.ts +0 -4
- package/dist/lib/components/context/ThemeProvider.js +0 -23
- package/dist/lib/components/context/ThemeProvider.js.map +0 -1
- package/dist/lib/plugins/openapi/client/createMemoryClient.d.ts +0 -9
- package/dist/lib/plugins/openapi/client/createMemoryClient.js +0 -54
- package/dist/lib/plugins/openapi/client/createMemoryClient.js.map +0 -1
- package/dist/lib/plugins/openapi/client/createWorkerClient.d.ts +0 -10
- package/dist/lib/plugins/openapi/client/createWorkerClient.js +0 -62
- package/dist/lib/plugins/openapi/client/createWorkerClient.js.map +0 -1
- package/dist/lib/plugins/openapi/client/interfaces.d.ts +0 -4
- package/dist/lib/plugins/openapi/client/interfaces.js +0 -2
- package/dist/lib/plugins/openapi/client/interfaces.js.map +0 -1
- package/dist/lib/themeToggle.d.ts +0 -1
- package/dist/lib/themeToggle.js +0 -7
- package/dist/lib/themeToggle.js.map +0 -1
- package/dist/lib/util/createWaitForNotify.d.ts +0 -1
- package/dist/lib/util/createWaitForNotify.js +0 -15
- package/dist/lib/util/createWaitForNotify.js.map +0 -1
- package/dist/vite/plugin-html-transform.d.ts +0 -2
- package/dist/vite/plugin-html-transform.js +0 -15
- package/dist/vite/plugin-html-transform.js.map +0 -1
- package/lib/DeveloperHint-DHdLXGHA.js +0 -16
- package/lib/DeveloperHint-DHdLXGHA.js.map +0 -1
- package/lib/Markdown-D6UxMbZm.js +0 -18059
- package/lib/Markdown-D6UxMbZm.js.map +0 -1
- package/lib/OperationList-BHUBGM0c.js +0 -621
- package/lib/OperationList-BHUBGM0c.js.map +0 -1
- package/lib/Route-B0XuN1oC.js +0 -13
- package/lib/Route-B0XuN1oC.js.map +0 -1
- package/lib/Select-DYKDahHt.js.map +0 -1
- package/lib/SidebarBadge-Bbt92M5K.js +0 -38
- package/lib/SidebarBadge-Bbt92M5K.js.map +0 -1
- package/lib/SlotletProvider-mhjLPG44.js +0 -241
- package/lib/SlotletProvider-mhjLPG44.js.map +0 -1
- package/lib/Spinner-ChOGyPls.js.map +0 -1
- package/lib/StaggeredRender-DDHSzQKE.js +0 -17
- package/lib/StaggeredRender-DDHSzQKE.js.map +0 -1
- package/lib/hook-CjQERPa7.js.map +0 -1
- package/lib/index-BRg5pi5D.js +0 -5902
- package/lib/index-BRg5pi5D.js.map +0 -1
- package/lib/index-DM9hrcCG.js +0 -1783
- package/lib/index-DM9hrcCG.js.map +0 -1
- package/lib/state-BsPrOUAh.js +0 -252
- package/lib/state-BsPrOUAh.js.map +0 -1
- package/lib/urql-core-35Qt_U4i.js +0 -1511
- package/lib/urql-core-35Qt_U4i.js.map +0 -1
- package/lib/useExposedProps-BxyHjPNN.js +0 -9
- package/lib/utils-DNAltzXc.js.map +0 -1
- package/src/lib/components/context/ThemeContext.tsx +0 -8
- package/src/lib/components/context/ThemeProvider.tsx +0 -27
- package/src/lib/plugins/openapi/client/createMemoryClient.ts +0 -65
- package/src/lib/plugins/openapi/client/createWorkerClient.ts +0 -79
- package/src/lib/plugins/openapi/client/interfaces.ts +0 -5
- package/src/lib/themeToggle.ts +0 -7
- package/src/lib/util/createWaitForNotify.ts +0 -18
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HydrationBoundary,
|
|
3
|
+
QueryClient,
|
|
4
|
+
QueryClientProvider,
|
|
5
|
+
} from "@tanstack/react-query";
|
|
1
6
|
import { type HelmetData, HelmetProvider } from "@zudoku/react-helmet-async";
|
|
2
|
-
import { StrictMode } from "react";
|
|
7
|
+
import { StrictMode, useState } from "react";
|
|
3
8
|
import { type createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
4
9
|
import {
|
|
5
10
|
type createStaticRouter,
|
|
@@ -15,13 +20,31 @@ const Bootstrap = ({
|
|
|
15
20
|
hydrate?: boolean;
|
|
16
21
|
router: ReturnType<typeof createBrowserRouter>;
|
|
17
22
|
}) => {
|
|
23
|
+
const [queryClient] = useState(
|
|
24
|
+
() =>
|
|
25
|
+
new QueryClient({
|
|
26
|
+
defaultOptions: {
|
|
27
|
+
queries: {
|
|
28
|
+
staleTime: 1000 * 60 * 5,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
18
34
|
return (
|
|
19
35
|
<StrictMode>
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
<QueryClientProvider client={queryClient}>
|
|
37
|
+
<HydrationBoundary state={hydrate ? (window as any).DATA : undefined}>
|
|
38
|
+
<HelmetProvider>
|
|
39
|
+
<StaggeredRenderContext.Provider value={{ stagger: !hydrate }}>
|
|
40
|
+
<RouterProvider
|
|
41
|
+
router={router}
|
|
42
|
+
future={{ v7_startTransition: true }}
|
|
43
|
+
/>
|
|
44
|
+
</StaggeredRenderContext.Provider>
|
|
45
|
+
</HelmetProvider>
|
|
46
|
+
</HydrationBoundary>
|
|
47
|
+
</QueryClientProvider>
|
|
25
48
|
</StrictMode>
|
|
26
49
|
);
|
|
27
50
|
};
|
|
@@ -29,16 +52,20 @@ const Bootstrap = ({
|
|
|
29
52
|
const BootstrapStatic = ({
|
|
30
53
|
router,
|
|
31
54
|
context,
|
|
55
|
+
queryClient,
|
|
32
56
|
helmetContext,
|
|
33
57
|
}: {
|
|
34
58
|
helmetContext: HelmetData["context"];
|
|
35
59
|
context: StaticHandlerContext;
|
|
60
|
+
queryClient: QueryClient;
|
|
36
61
|
router: ReturnType<typeof createStaticRouter>;
|
|
37
62
|
}) => (
|
|
38
63
|
<StrictMode>
|
|
39
|
-
<
|
|
40
|
-
<
|
|
41
|
-
|
|
64
|
+
<QueryClientProvider client={queryClient}>
|
|
65
|
+
<HelmetProvider context={helmetContext}>
|
|
66
|
+
<StaticRouterProvider router={router} context={context} />
|
|
67
|
+
</HelmetProvider>
|
|
68
|
+
</QueryClientProvider>
|
|
42
69
|
</StrictMode>
|
|
43
70
|
);
|
|
44
71
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { Callout } from "../ui/Callout.js";
|
|
3
|
+
import { Markdown } from "./Markdown.js";
|
|
3
4
|
|
|
4
5
|
export const DeveloperHint = ({
|
|
5
6
|
children,
|
|
@@ -13,7 +14,11 @@ export const DeveloperHint = ({
|
|
|
13
14
|
return (
|
|
14
15
|
<Callout type="caution" title="Developer hint" className={className}>
|
|
15
16
|
<div className="flex flex-col gap-2">
|
|
16
|
-
|
|
17
|
+
{typeof children === "string" ? (
|
|
18
|
+
<Markdown content={children} />
|
|
19
|
+
) : (
|
|
20
|
+
<div>{children}</div>
|
|
21
|
+
)}
|
|
17
22
|
<small className="italic">
|
|
18
23
|
Note: This hint is only shown in development mode.
|
|
19
24
|
</small>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MoonStarIcon, SunIcon } from "lucide-react";
|
|
2
1
|
import { memo } from "react";
|
|
3
2
|
import { Link } from "react-router-dom";
|
|
4
3
|
import { Button } from "zudoku/ui/Button.js";
|
|
@@ -17,15 +16,14 @@ import {
|
|
|
17
16
|
DropdownMenuSubTrigger,
|
|
18
17
|
DropdownMenuTrigger,
|
|
19
18
|
} from "../ui/DropdownMenu.js";
|
|
20
|
-
import { cn } from "../util/cn.js";
|
|
21
19
|
import { joinPath } from "../util/joinPath.js";
|
|
22
20
|
import { Banner } from "./Banner.js";
|
|
23
21
|
import { ClientOnly } from "./ClientOnly.js";
|
|
24
|
-
import { useTheme } from "./context/ThemeContext.js";
|
|
25
22
|
import { useZudoku } from "./context/ZudokuContext.js";
|
|
26
23
|
import { MobileTopNavigation } from "./MobileTopNavigation.js";
|
|
27
24
|
import { Search } from "./Search.js";
|
|
28
25
|
import { Slotlet } from "./SlotletProvider.js";
|
|
26
|
+
import { ThemeSwitch } from "./ThemeSwitch.js";
|
|
29
27
|
import { TopNavigation } from "./TopNavigation.js";
|
|
30
28
|
|
|
31
29
|
const RecursiveMenu = ({ item }: { item: ProfileNavigationItem }) => {
|
|
@@ -50,7 +48,6 @@ const RecursiveMenu = ({ item }: { item: ProfileNavigationItem }) => {
|
|
|
50
48
|
|
|
51
49
|
export const Header = memo(function HeaderInner() {
|
|
52
50
|
const auth = useAuth();
|
|
53
|
-
const [isDark, toggleTheme] = useTheme();
|
|
54
51
|
const { isAuthenticated, profile, isAuthEnabled } = useAuth();
|
|
55
52
|
const context = useZudoku();
|
|
56
53
|
const { page, plugins } = context;
|
|
@@ -60,8 +57,6 @@ export const Header = memo(function HeaderInner() {
|
|
|
60
57
|
.flatMap((p) => p.getProfileMenuItems(context))
|
|
61
58
|
.map((i) => <RecursiveMenu key={i.label} item={i} />);
|
|
62
59
|
|
|
63
|
-
const ThemeIcon = isDark ? MoonStarIcon : SunIcon;
|
|
64
|
-
|
|
65
60
|
return (
|
|
66
61
|
<header className="sticky lg:top-0 z-10 bg-background/80 backdrop-blur w-full">
|
|
67
62
|
<Banner />
|
|
@@ -83,10 +78,11 @@ export const Header = memo(function HeaderInner() {
|
|
|
83
78
|
}
|
|
84
79
|
alt={page.logo.alt ?? page.pageTitle}
|
|
85
80
|
style={{ width: page.logo.width }}
|
|
86
|
-
className=
|
|
81
|
+
className="h-10 dark:hidden"
|
|
87
82
|
loading="lazy"
|
|
88
83
|
/>
|
|
89
84
|
<img
|
|
85
|
+
data-hide-on-theme="light"
|
|
90
86
|
src={
|
|
91
87
|
/https?:\/\//.test(page.logo.src.dark)
|
|
92
88
|
? page.logo.src.dark
|
|
@@ -97,7 +93,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
97
93
|
}
|
|
98
94
|
alt={page.logo.alt ?? page.pageTitle}
|
|
99
95
|
style={{ width: page.logo.width }}
|
|
100
|
-
className=
|
|
96
|
+
className="h-10"
|
|
101
97
|
loading="lazy"
|
|
102
98
|
/>
|
|
103
99
|
</>
|
|
@@ -116,40 +112,33 @@ export const Header = memo(function HeaderInner() {
|
|
|
116
112
|
<MobileTopNavigation />
|
|
117
113
|
<div className="hidden lg:flex items-center justify-self-end text-sm gap-2">
|
|
118
114
|
<Slotlet name="head-navigation-start" />
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
isDark ? "Switch to light mode" : "Switch to dark mode"
|
|
147
|
-
}
|
|
148
|
-
className="p-2.5 -m-2.5 rounded-full"
|
|
149
|
-
onClick={toggleTheme}
|
|
150
|
-
>
|
|
151
|
-
<ThemeIcon size={18} />
|
|
152
|
-
</Button>
|
|
115
|
+
{isAuthEnabled && (
|
|
116
|
+
<ClientOnly
|
|
117
|
+
fallback={<Skeleton className="rounded h-5 w-24 mr-4" />}
|
|
118
|
+
>
|
|
119
|
+
{!isAuthenticated ? (
|
|
120
|
+
<Button variant="ghost" onClick={() => auth.login()}>
|
|
121
|
+
Login
|
|
122
|
+
</Button>
|
|
123
|
+
) : (
|
|
124
|
+
accountItems.length > 0 && (
|
|
125
|
+
<DropdownMenu modal={false}>
|
|
126
|
+
<DropdownMenuTrigger asChild>
|
|
127
|
+
<Button variant="ghost">
|
|
128
|
+
{profile?.email ? `${profile.email}` : "My Account"}
|
|
129
|
+
</Button>
|
|
130
|
+
</DropdownMenuTrigger>
|
|
131
|
+
<DropdownMenuContent className="w-56">
|
|
132
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
133
|
+
<DropdownMenuSeparator />
|
|
134
|
+
{accountItems}
|
|
135
|
+
</DropdownMenuContent>
|
|
136
|
+
</DropdownMenu>
|
|
137
|
+
)
|
|
138
|
+
)}
|
|
139
|
+
</ClientOnly>
|
|
140
|
+
)}
|
|
141
|
+
<ThemeSwitch />
|
|
153
142
|
<Slotlet name="head-navigation-end" />
|
|
154
143
|
</div>
|
|
155
144
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Helmet } from "@zudoku/react-helmet-async";
|
|
2
2
|
import { PanelLeftIcon } from "lucide-react";
|
|
3
3
|
import { Suspense, useEffect, useRef, type ReactNode } from "react";
|
|
4
|
-
import { Outlet, useLocation } from "react-router-dom";
|
|
4
|
+
import { Outlet, useLocation, useNavigation } from "react-router-dom";
|
|
5
|
+
import { useSpinDelay } from "spin-delay";
|
|
5
6
|
import { Drawer, DrawerTrigger } from "../ui/Drawer.js";
|
|
6
7
|
import { cn } from "../util/cn.js";
|
|
7
8
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
@@ -13,6 +14,12 @@ import { Sidebar } from "./navigation/Sidebar.js";
|
|
|
13
14
|
import { Slotlet } from "./SlotletProvider.js";
|
|
14
15
|
import { Spinner } from "./Spinner.js";
|
|
15
16
|
|
|
17
|
+
const LoadingFallback = () => (
|
|
18
|
+
<main className="grid h-[calc(100vh-var(--header-height))] place-items-center">
|
|
19
|
+
<Spinner />
|
|
20
|
+
</main>
|
|
21
|
+
);
|
|
22
|
+
|
|
16
23
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
17
24
|
const location = useLocation();
|
|
18
25
|
const { setActiveAnchor } = useViewportAnchor();
|
|
@@ -36,6 +43,13 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
36
43
|
previousLocationPath.current = location.pathname;
|
|
37
44
|
}, [location.pathname, setActiveAnchor]);
|
|
38
45
|
|
|
46
|
+
// Page transition is happening: https://reactrouter.com/start/framework/pending-ui#global-pending-navigation
|
|
47
|
+
const isNavigating = Boolean(useNavigation().location);
|
|
48
|
+
const showSpinner = useSpinDelay(isNavigating, {
|
|
49
|
+
delay: 300,
|
|
50
|
+
minDuration: 500,
|
|
51
|
+
});
|
|
52
|
+
|
|
39
53
|
return (
|
|
40
54
|
<>
|
|
41
55
|
{import.meta.env.MODE === "standalone" && (
|
|
@@ -52,41 +66,39 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
52
66
|
<Slotlet name="layout-after-head" />
|
|
53
67
|
|
|
54
68
|
<div className="w-full max-w-screen-2xl mx-auto px-10 lg:px-12">
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</Drawer>
|
|
89
|
-
</Suspense>
|
|
69
|
+
{showSpinner ? (
|
|
70
|
+
<LoadingFallback />
|
|
71
|
+
) : (
|
|
72
|
+
<Suspense fallback={<LoadingFallback />}>
|
|
73
|
+
<Drawer direction="left">
|
|
74
|
+
<Sidebar />
|
|
75
|
+
<div
|
|
76
|
+
className={cn(
|
|
77
|
+
"lg:hidden -mx-10 px-10 py-2 sticky bg-background/80 backdrop-blur z-10 top-0 left-0 right-0 border-b",
|
|
78
|
+
"peer-data-[navigation=false]:hidden",
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<DrawerTrigger className="flex items-center gap-2">
|
|
82
|
+
<PanelLeftIcon size={16} strokeWidth={1.5} />
|
|
83
|
+
<span className="text-sm">Menu</span>
|
|
84
|
+
</DrawerTrigger>
|
|
85
|
+
</div>
|
|
86
|
+
<main
|
|
87
|
+
className={cn(
|
|
88
|
+
"h-full dark:border-white/10 translate-x-0",
|
|
89
|
+
"lg:overflow-visible",
|
|
90
|
+
// This works in tandem with the `SidebarWrapper` component
|
|
91
|
+
"lg:peer-data-[navigation=true]:w-[calc(100%-var(--side-nav-width))]",
|
|
92
|
+
"lg:peer-data-[navigation=true]:translate-x-[--side-nav-width] lg:peer-data-[navigation=true]:pl-12",
|
|
93
|
+
)}
|
|
94
|
+
>
|
|
95
|
+
<Slotlet name="zudoku-before-content" />
|
|
96
|
+
{children ?? <Outlet />}
|
|
97
|
+
<Slotlet name="zudoku-after-content" />
|
|
98
|
+
</main>
|
|
99
|
+
</Drawer>
|
|
100
|
+
</Suspense>
|
|
101
|
+
)}
|
|
90
102
|
</div>
|
|
91
103
|
</>
|
|
92
104
|
);
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
2
|
-
import { cx } from "class-variance-authority";
|
|
3
2
|
import { MenuIcon } from "lucide-react";
|
|
4
|
-
import {
|
|
3
|
+
import { useState } from "react";
|
|
5
4
|
import { useAuth } from "../authentication/hook.js";
|
|
6
5
|
import {
|
|
7
6
|
Drawer,
|
|
8
|
-
DrawerClose,
|
|
9
7
|
DrawerContent,
|
|
10
8
|
DrawerTitle,
|
|
11
9
|
DrawerTrigger,
|
|
12
10
|
} from "../ui/Drawer.js";
|
|
13
11
|
import { useZudoku } from "./context/ZudokuContext.js";
|
|
14
12
|
import { Search } from "./Search.js";
|
|
15
|
-
import {
|
|
13
|
+
import { ThemeSwitch } from "./ThemeSwitch.js";
|
|
14
|
+
import { isHiddenItem, TopNavItem } from "./TopNavigation.js";
|
|
16
15
|
|
|
17
16
|
export const MobileTopNavigation = () => {
|
|
18
17
|
const { topNavigation } = useZudoku();
|
|
19
18
|
const { isAuthenticated } = useAuth();
|
|
19
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
|
-
<Drawer
|
|
22
|
+
<Drawer
|
|
23
|
+
direction="right"
|
|
24
|
+
open={drawerOpen}
|
|
25
|
+
onOpenChange={(open) => setDrawerOpen(open)}
|
|
26
|
+
>
|
|
23
27
|
<div className="flex lg:hidden justify-self-end">
|
|
24
28
|
<DrawerTrigger className="lg:hidden">
|
|
25
29
|
<MenuIcon size={22} />
|
|
@@ -36,21 +40,14 @@ export const MobileTopNavigation = () => {
|
|
|
36
40
|
<Search />
|
|
37
41
|
</div>
|
|
38
42
|
<ul className="flex flex-col items-center gap-4 p-4">
|
|
43
|
+
<li>
|
|
44
|
+
<ThemeSwitch />
|
|
45
|
+
</li>
|
|
39
46
|
{topNavigation.filter(isHiddenItem(isAuthenticated)).map((item) => (
|
|
40
47
|
<li key={item.label}>
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"block font-medium border-b-2",
|
|
45
|
-
isActive
|
|
46
|
-
? "border-primary text-foreground"
|
|
47
|
-
: "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
to={item.id}
|
|
51
|
-
>
|
|
52
|
-
<DrawerClose>{item.label}</DrawerClose>
|
|
53
|
-
</NavLink>
|
|
48
|
+
<button onClick={() => setDrawerOpen(false)}>
|
|
49
|
+
<TopNavItem {...item} />
|
|
50
|
+
</button>
|
|
54
51
|
</li>
|
|
55
52
|
))}
|
|
56
53
|
</ul>
|
|
@@ -33,9 +33,10 @@ void import("prismjs/components/prism-javascript.min.js");
|
|
|
33
33
|
// @ts-expect-error This is untyped
|
|
34
34
|
void import("prismjs/components/prism-typescript.min.js");
|
|
35
35
|
|
|
36
|
+
import { useTheme } from "next-themes";
|
|
36
37
|
import { useState } from "react";
|
|
37
38
|
import { cn } from "../util/cn.js";
|
|
38
|
-
import {
|
|
39
|
+
import { ClientOnly } from "./ClientOnly.js";
|
|
39
40
|
|
|
40
41
|
type SyntaxHighlightProps = {
|
|
41
42
|
className?: string;
|
|
@@ -55,72 +56,106 @@ export const SyntaxHighlight = ({
|
|
|
55
56
|
language = "plain",
|
|
56
57
|
...props
|
|
57
58
|
}: SyntaxHighlightProps) => {
|
|
58
|
-
const
|
|
59
|
+
const { resolvedTheme } = useTheme();
|
|
59
60
|
const [isCopied, setIsCopied] = useState(false);
|
|
60
61
|
|
|
61
62
|
if (!props.code) {
|
|
62
63
|
return null;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
const highlightTheme =
|
|
67
|
+
resolvedTheme === "dark" ? themes.vsDark : themes.github;
|
|
68
|
+
|
|
69
|
+
// hardcoded values from the themes to avoid color flash in SSR
|
|
70
|
+
const themeColorClasses =
|
|
71
|
+
"bg-[#f6f8fa] text-[#393a34] dark:bg-[#1e1e1e] dark:text-[#9cdcfe]";
|
|
72
|
+
|
|
65
73
|
return (
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
language={remapLang[language] ?? language}
|
|
69
|
-
{...props}
|
|
70
|
-
>
|
|
71
|
-
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
|
74
|
+
<ClientOnly
|
|
75
|
+
fallback={
|
|
72
76
|
<div className="relative group">
|
|
73
77
|
<pre
|
|
74
78
|
className={cn(
|
|
75
79
|
"relative scrollbar overflow-x-auto",
|
|
76
|
-
className,
|
|
77
80
|
props.className,
|
|
78
|
-
props.noBackground
|
|
81
|
+
props.noBackground ? "!bg-transparent" : themeColorClasses,
|
|
79
82
|
props.wrapLines && "whitespace-pre-wrap break-words",
|
|
80
83
|
)}
|
|
81
|
-
style={style}
|
|
82
84
|
>
|
|
83
|
-
{
|
|
84
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
85
|
-
<div key={i} {...getLineProps({ line })}>
|
|
86
|
-
{line.map((token, key) => (
|
|
87
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
88
|
-
<span key={key} {...getTokenProps({ token })} />
|
|
89
|
-
))}
|
|
90
|
-
</div>
|
|
91
|
-
))}
|
|
85
|
+
{props.code}
|
|
92
86
|
</pre>
|
|
93
87
|
{props.showLanguageIndicator && (
|
|
94
88
|
<span className="absolute top-1.5 right-3 text-[11px] font-mono text-muted-foreground transition group-hover:opacity-0">
|
|
95
89
|
{language}
|
|
96
90
|
</span>
|
|
97
91
|
)}
|
|
98
|
-
{copyable && (
|
|
99
|
-
<button
|
|
100
|
-
type="button"
|
|
101
|
-
aria-label="Copy code"
|
|
102
|
-
title="Copy code"
|
|
103
|
-
className="absolute top-2 right-2 p-2 opacity-0 group-hover:opacity-100 group-hover:bg-zinc-100 group-hover:dark:bg-zinc-700 hover:outline hover:outline-border/75 dark:hover:outline-border rounded-md text-sm text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-400 transition"
|
|
104
|
-
disabled={isCopied}
|
|
105
|
-
onClick={() => {
|
|
106
|
-
setIsCopied(true);
|
|
107
|
-
void navigator.clipboard.writeText(
|
|
108
|
-
tokens
|
|
109
|
-
.map((line) => line.map(({ content }) => content).join(""))
|
|
110
|
-
.join("\n"),
|
|
111
|
-
);
|
|
112
|
-
setTimeout(() => setIsCopied(false), 2000);
|
|
113
|
-
}}
|
|
114
|
-
>
|
|
115
|
-
{isCopied ? (
|
|
116
|
-
<CheckIcon className="text-emerald-600" size={16} />
|
|
117
|
-
) : (
|
|
118
|
-
<CopyIcon size={16} />
|
|
119
|
-
)}
|
|
120
|
-
</button>
|
|
121
|
-
)}
|
|
122
92
|
</div>
|
|
123
|
-
|
|
124
|
-
|
|
93
|
+
}
|
|
94
|
+
>
|
|
95
|
+
<Highlight
|
|
96
|
+
theme={highlightTheme}
|
|
97
|
+
language={remapLang[language] ?? language}
|
|
98
|
+
{...props}
|
|
99
|
+
>
|
|
100
|
+
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
|
101
|
+
<div className="relative group">
|
|
102
|
+
<pre
|
|
103
|
+
className={cn(
|
|
104
|
+
"relative scrollbar overflow-x-auto",
|
|
105
|
+
className,
|
|
106
|
+
props.className,
|
|
107
|
+
props.noBackground && "!bg-transparent",
|
|
108
|
+
props.wrapLines && "whitespace-pre-wrap break-words",
|
|
109
|
+
)}
|
|
110
|
+
style={style}
|
|
111
|
+
>
|
|
112
|
+
{tokens.map((line, i) => (
|
|
113
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
114
|
+
<div key={i} {...getLineProps({ line })}>
|
|
115
|
+
{line.map((token, key) => (
|
|
116
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
117
|
+
<span key={key} {...getTokenProps({ token })} />
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
))}
|
|
121
|
+
</pre>
|
|
122
|
+
{props.showLanguageIndicator && (
|
|
123
|
+
<span className="absolute top-1.5 right-3 text-[11px] font-mono text-muted-foreground transition group-hover:opacity-0">
|
|
124
|
+
{language}
|
|
125
|
+
</span>
|
|
126
|
+
)}
|
|
127
|
+
{copyable && (
|
|
128
|
+
<button
|
|
129
|
+
type="button"
|
|
130
|
+
aria-label="Copy code"
|
|
131
|
+
title="Copy code"
|
|
132
|
+
className="absolute top-2 right-2 p-2 opacity-0 group-hover:opacity-100 group-hover:bg-zinc-100 group-hover:dark:bg-zinc-700 hover:outline hover:outline-border/75 dark:hover:outline-border rounded-md text-sm text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-400 transition"
|
|
133
|
+
disabled={isCopied}
|
|
134
|
+
onClick={() => {
|
|
135
|
+
setIsCopied(true);
|
|
136
|
+
void navigator.clipboard.writeText(
|
|
137
|
+
tokens
|
|
138
|
+
.map((l) => l.map(({ content }) => content).join(""))
|
|
139
|
+
.join("\n"),
|
|
140
|
+
);
|
|
141
|
+
setTimeout(() => setIsCopied(false), 2000);
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
{isCopied ? (
|
|
145
|
+
<CheckIcon
|
|
146
|
+
className="text-emerald-600"
|
|
147
|
+
size={16}
|
|
148
|
+
strokeWidth={2.5}
|
|
149
|
+
absoluteStrokeWidth
|
|
150
|
+
/>
|
|
151
|
+
) : (
|
|
152
|
+
<CopyIcon size={16} />
|
|
153
|
+
)}
|
|
154
|
+
</button>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
</Highlight>
|
|
159
|
+
</ClientOnly>
|
|
125
160
|
);
|
|
126
161
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MoonStarIcon, SunIcon } from "lucide-react";
|
|
2
|
+
import { useTheme } from "next-themes";
|
|
3
|
+
import { Button } from "zudoku/ui/Button.js";
|
|
4
|
+
import { ClientOnly } from "./ClientOnly.js";
|
|
5
|
+
|
|
6
|
+
export const ThemeSwitch = () => {
|
|
7
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
8
|
+
const ThemeIcon = resolvedTheme === "dark" ? MoonStarIcon : SunIcon;
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<ClientOnly>
|
|
12
|
+
<Button
|
|
13
|
+
variant="ghost"
|
|
14
|
+
aria-label={
|
|
15
|
+
resolvedTheme === "dark"
|
|
16
|
+
? "Switch to light mode"
|
|
17
|
+
: "Switch to dark mode"
|
|
18
|
+
}
|
|
19
|
+
className="p-2.5 -m-2.5 rounded-full"
|
|
20
|
+
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
|
21
|
+
>
|
|
22
|
+
<ThemeIcon size={18} />
|
|
23
|
+
</Button>
|
|
24
|
+
</ClientOnly>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { cx } from "class-variance-authority";
|
|
2
2
|
import { Suspense } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { useAuth } from "../authentication/hook.js";
|
|
3
|
+
import { NavLink, useNavigation } from "react-router-dom";
|
|
5
4
|
import { TopNavigationItem } from "../../config/validators/validate.js";
|
|
5
|
+
import { useAuth } from "../authentication/hook.js";
|
|
6
|
+
import { ZudokuError } from "../util/invariant.js";
|
|
6
7
|
import { joinPath } from "../util/joinPath.js";
|
|
7
8
|
import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
|
|
8
9
|
import { traverseSidebar } from "./navigation/utils.js";
|
|
@@ -32,7 +33,7 @@ export const TopNavigation = () => {
|
|
|
32
33
|
<nav className="hidden lg:block border-b text-sm px-12 h-[--top-nav-height]">
|
|
33
34
|
<ul className="flex flex-row items-center gap-8">
|
|
34
35
|
{topNavigation.filter(isHiddenItem(isAuthenticated)).map((item) => (
|
|
35
|
-
|
|
36
|
+
<li key={item.id}>
|
|
36
37
|
<TopNavItem {...item} />
|
|
37
38
|
</li>
|
|
38
39
|
))}
|
|
@@ -42,10 +43,16 @@ export const TopNavigation = () => {
|
|
|
42
43
|
);
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
const TopNavItem = ({
|
|
46
|
+
export const TopNavItem = ({
|
|
47
|
+
id,
|
|
48
|
+
label,
|
|
49
|
+
default: defaultLink,
|
|
50
|
+
}: TopNavigationItem) => {
|
|
46
51
|
const { sidebars } = useZudoku();
|
|
47
|
-
const nav = useCurrentNavigation();
|
|
48
52
|
const currentSidebar = sidebars[id];
|
|
53
|
+
const currentNav = useCurrentNavigation();
|
|
54
|
+
const isNavigating = Boolean(useNavigation().location);
|
|
55
|
+
const isActive = currentNav.topNavItem?.id === id && !isNavigating;
|
|
49
56
|
|
|
50
57
|
// TODO: This is a bit of a hack to get the first link in the sidebar
|
|
51
58
|
// We should really process this when we load the config so we can validate
|
|
@@ -60,25 +67,26 @@ const TopNavItem = ({ id, label, default: defaultLink }: TopNavigationItem) => {
|
|
|
60
67
|
: joinPath(id));
|
|
61
68
|
|
|
62
69
|
if (!first) {
|
|
63
|
-
throw new
|
|
64
|
-
`No links found in top navigation for
|
|
65
|
-
);
|
|
70
|
+
throw new ZudokuError("Page not found.", {
|
|
71
|
+
developerHint: `No links found in top navigation for '${id}'. Check that the sidebar isn't empty or that a default link is set.`,
|
|
72
|
+
});
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
// Manually set the active sidebar based on our logic of what is active
|
|
69
|
-
const isActive = nav.data.topNavItem?.id === id;
|
|
70
|
-
|
|
71
75
|
return (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
:
|
|
78
|
-
|
|
76
|
+
// We don't use isActive here because it has to be inside the sidebar,
|
|
77
|
+
// the top nav id doesn't necessarily start with the sidebar id
|
|
78
|
+
<NavLink
|
|
79
|
+
className={({ isPending }) =>
|
|
80
|
+
cx(
|
|
81
|
+
"block lg:py-3.5 font-medium -mb-px border-b-2",
|
|
82
|
+
isActive || isPending
|
|
83
|
+
? "border-primary text-foreground"
|
|
84
|
+
: "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
|
|
85
|
+
)
|
|
86
|
+
}
|
|
79
87
|
to={first}
|
|
80
88
|
>
|
|
81
89
|
{label}
|
|
82
|
-
</
|
|
90
|
+
</NavLink>
|
|
83
91
|
);
|
|
84
92
|
};
|