mirror of
https://github.com/khairul169/code-share.git
synced 2026-10-24 11:53:58 +07:00
feat: rebuild app using vike
This commit is contained in:
@@ -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;
|
||||
@@ -0,0 +1,5 @@
|
||||
import NProgress from "nprogress";
|
||||
|
||||
export const onPageTransitionEnd = async () => {
|
||||
NProgress.done();
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import NProgress from "nprogress";
|
||||
|
||||
export const onPageTransitionStart = async () => {
|
||||
NProgress.start();
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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: {},
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -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 || "");
|
||||
};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {};
|
||||
@@ -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 };
|
||||
}
|
||||
Reference in New Issue
Block a user