feat: rebuild app using vike

This commit is contained in:
2024-02-22 12:15:04 +00:00
parent 87ef4ca1e3
commit 9777ae1676
104 changed files with 2214 additions and 2976 deletions
+15
View File
@@ -0,0 +1,15 @@
import type { Config } from "vike/types";
export default {
clientRouting: true,
passToClient: ["routeParams"],
meta: {
title: {
env: { server: true, client: true },
},
description: {
env: { server: true },
},
},
hydrationCanBeAborted: true,
} satisfies Config;
+5
View File
@@ -0,0 +1,5 @@
import NProgress from "nprogress";
export const onPageTransitionEnd = async () => {
NProgress.done();
};
+5
View File
@@ -0,0 +1,5 @@
import NProgress from "nprogress";
export const onPageTransitionStart = async () => {
NProgress.start();
};
+38
View File
@@ -0,0 +1,38 @@
import ReactDOM from "react-dom/client";
import { getPageMetadata } from "./utils";
import type { OnRenderClientAsync } from "vike/types";
import Layout from "./layout";
let root: ReactDOM.Root;
export const onRenderClient: OnRenderClientAsync = async (
pageContext
): ReturnType<OnRenderClientAsync> => {
const { Page } = pageContext;
if (!Page)
throw new Error(
"My onRenderClient() hook expects pageContext.Page to be defined"
);
const container = document.getElementById("react-root");
if (!container) throw new Error("DOM element #react-root not found");
const page = (
<Layout pageContext={pageContext}>
<Page />
</Layout>
);
if (pageContext.isHydration) {
root = ReactDOM.hydrateRoot(container, page);
} else {
if (!root) {
root = ReactDOM.createRoot(container);
}
root.render(page);
}
const meta = getPageMetadata(pageContext);
document.title = meta.title;
};
+44
View File
@@ -0,0 +1,44 @@
import ReactDOMServer from "react-dom/server";
import { escapeInject, dangerouslySkipEscape } from "vike/server";
import type { OnRenderHtmlAsync } from "vike/types";
import { getPageMetadata } from "./utils";
import Layout from "./layout";
export const onRenderHtml: OnRenderHtmlAsync = async (
pageContext
): ReturnType<OnRenderHtmlAsync> => {
const { Page } = pageContext;
if (!Page)
throw new Error(
"My onRenderHtml() hook expects pageContext.Page to be defined"
);
const page = ReactDOMServer.renderToString(
<Layout pageContext={pageContext}>
<Page />
</Layout>
);
// See https://vike.dev/head
const meta = getPageMetadata(pageContext);
const documentHtml = escapeInject`<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="${meta.description}" />
<title>${meta.title}</title>
</head>
<body>
<div id="react-root">${dangerouslySkipEscape(page)}</div>
</body>
</html>`;
return {
documentHtml,
pageContext: {},
};
};
+34
View File
@@ -0,0 +1,34 @@
import React, { useEffect, useState } from "react";
import type { ReactNode } from "react";
type ClientOnlyProps = {
children: ReactNode;
fallback?: ReactNode | null;
};
const ClientOnly = ({ children, fallback }: ClientOnlyProps) => {
const [isMounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (typeof window === "undefined") {
return fallback;
}
return isMounted ? children : fallback;
};
export const withClientOnly = <T extends unknown>(
Component: React.ComponentType<T>,
fallback?: ReactNode | null
): React.ComponentType<T> => {
return (props: any) => (
<ClientOnly fallback={fallback}>
<Component {...props} />
</ClientOnly>
);
};
export default ClientOnly;
+26
View File
@@ -0,0 +1,26 @@
// https://vike.dev/usePageContext
// eslint-disable-next-line react-refresh/only-export-components
export { usePageContext };
export { PageContextProvider };
import React, { useContext } from "react";
import type { PageContext } from "vike/types";
const Context = React.createContext<PageContext>(
undefined as unknown as PageContext
);
type Props = {
pageContext: PageContext;
children: React.ReactNode;
};
const PageContextProvider = ({ pageContext, children }: Props) => {
return <Context.Provider value={pageContext}>{children}</Context.Provider>;
};
/** https://vike.dev/usePageContext */
function usePageContext() {
const pageContext = useContext(Context);
return pageContext;
}
+26
View File
@@ -0,0 +1,26 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply bg-slate-900 text-white;
}
.cm-theme {
@apply h-full md:text-[16px];
}
.cm-editor {
@apply py-2;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+16
View File
@@ -0,0 +1,16 @@
import { usePageContext } from "./context";
export const useData = <T = any>() => {
const { data } = usePageContext();
return data as T;
};
export const useParams = <T = any>() => {
const { routeParams } = usePageContext();
return (routeParams || {}) as T;
};
export const useSearchParams = () => {
const { urlParsed } = usePageContext();
return new URLSearchParams(urlParsed.searchOriginal || "");
};
+23
View File
@@ -0,0 +1,23 @@
import React from "react";
import { PageContextProvider } from "./context";
import type { PageContext } from "vike/types";
import Providers from "./providers";
import "./globals.css";
import "nprogress/nprogress.css";
type LayoutProps = {
children: React.ReactNode;
pageContext: PageContext;
};
const Layout = ({ children, pageContext }: LayoutProps) => {
return (
<React.StrictMode>
<PageContextProvider pageContext={pageContext}>
<Providers>{children}</Providers>
</PageContextProvider>
</React.StrictMode>
);
};
export default Layout;
+19
View File
@@ -0,0 +1,19 @@
import { ComponentProps } from "react";
import { usePageContext } from "./context";
const Link = (props: ComponentProps<"a">) => {
const pageContext = usePageContext();
const { urlPathname } = pageContext;
const { href } = props;
const isActive =
href === "/" ? urlPathname === href : urlPathname.startsWith(href!);
const className = [props.className, isActive && "is-active"]
.filter(Boolean)
.join(" ");
return <a {...props} className={className} />;
};
export default Link;
+32
View File
@@ -0,0 +1,32 @@
import React, { useState } from "react";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import trpc, { getBaseUrl } from "~/lib/trpc";
import { httpBatchLink } from "@trpc/react-query";
type Props = {
children: React.ReactNode;
};
const Providers = ({ children }: Props) => {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: getBaseUrl() + "/api/trpc",
headers() {
return {};
},
}),
],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
};
export default Providers;
+19
View File
@@ -0,0 +1,19 @@
//
declare global {
namespace Vike {
interface PageContext {
Page: () => React.ReactElement;
data?: {
title?: string;
description?: string;
};
config: {
title?: string;
description?: string;
};
abortReason?: string;
}
}
}
export {};
+11
View File
@@ -0,0 +1,11 @@
import type { PageContext } from "vike/types";
export function getPageMetadata(pageContext: PageContext) {
let title = pageContext.data?.title || pageContext.config.title;
title = title ? `${title} - Vike` : "Welcome to Vike";
const description =
pageContext.data?.description || pageContext.config.description || "";
return { title, description };
}