feat: basic sketch

This commit is contained in:
Paul Pan 2023-07-16 19:34:31 +08:00
parent a4b0d0308c
commit 0b51183cd7
12 changed files with 346 additions and 151 deletions

View File

@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@ -1,35 +0,0 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App

View File

@ -0,0 +1,58 @@
import ReactMarkdown from "react-markdown";
import { Components } from "react-markdown/lib/ast-to-react";
import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";
import emoji from "remark-emoji";
import rehypeRaw from "rehype-raw";
import rehypeMathJaxSvg from "rehype-mathjax";
import { PrismAsync } from "react-syntax-highlighter";
import { oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
import "github-markdown-css";
interface MarkdownProps {
markdown: string;
}
export default function Markdown(props: MarkdownProps) {
const remarkPlugins = [remarkGfm, emoji, remarkMath];
const rehypePlugins = [rehypeRaw, rehypeMathJaxSvg];
const renderers: Components = {
code: function makeCodeBlock({
inline,
className,
children,
...props
}) {
const match = /language-(\w+)/.exec(className || "");
const codeBlock = (
<PrismAsync
{...props}
language={(match || ["", "text"])[1]}
PreTag="div"
children={String(children).replace(/\n$/, "")}
wrapLines={true}
showLineNumbers={true}
wrapLongLines={true}
style={oneLight}
/>
);
const codeInline = <code {...props}>{children}</code>;
return !inline && match ? codeBlock : codeInline;
},
};
return (
<>
<ReactMarkdown
className={"markdown-body"}
children={props.markdown}
remarkPlugins={remarkPlugins}
rehypePlugins={rehypePlugins}
components={renderers}
/>
</>
);
}

View File

@ -1,69 +1,14 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

View File

@ -1,10 +1,75 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import React from "react";
import ReactDOM from "react-dom/client";
import {
createBrowserRouter,
createRoutesFromChildren,
matchRoutes,
RouterProvider,
useLocation,
useNavigationType,
} from "react-router-dom";
import * as Sentry from "@sentry/react";
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
import { Root, HomePage, ErrorPage } from "./pages/pages.tsx";
import { RouteConfigs } from "./routes.tsx";
import "./index.css";
Sentry.init({
dsn: "https://4f90ea95bb8b462c8d8432ddbabac9b8@o354675.ingest.sentry.io/4505537167491072",
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
),
}),
new Sentry.Replay(),
],
tracesSampleRate: 1.0,
tracePropagationTargets: ["localhost"],
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
const sentryCreateBrowserRouter =
Sentry.wrapCreateBrowserRouter(createBrowserRouter);
const router = sentryCreateBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <HomePage />,
},
...RouteConfigs.map((config) => {
const basic = {
path: config.path,
element: config.element,
};
if (config.loader) {
return { ...basic, loader: config.loader };
} else {
return basic;
}
}),
],
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
);

39
src/pages/error-page.tsx Normal file
View File

@ -0,0 +1,39 @@
import { useRouteError, isRouteErrorResponse } from "react-router-dom";
const ErrorPage = () => {
const style: React.CSSProperties = {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "80vh",
lineHeight: "0.8",
};
const error = useRouteError();
const convertError = (error: unknown): string => {
if (isRouteErrorResponse(error)) {
return error.error?.message || error.statusText;
} else if (error instanceof Error) {
return error.message;
} else if (typeof error === "string") {
return error;
} else {
console.error(error);
return "Unknown error";
}
};
return (
<div style={style}>
<h1>Oops!</h1>
<p>Sorry, an unexpected error has occurred.</p>
<p>
<i style={{ color: "#696969" }}>{convertError(error)}</i>
</p>
</div>
);
};
export { ErrorPage };

5
src/pages/home.tsx Normal file
View File

@ -0,0 +1,5 @@
function HomePage() {
return <div>Home Page</div>;
}
export { HomePage };

4
src/pages/pages.tsx Normal file
View File

@ -0,0 +1,4 @@
export { Root } from "./root";
export { ErrorPage } from "./error-page";
export { HomePage } from "./home";
export { ProblemLoader, ProblemPage } from "./problem";

64
src/pages/problem.tsx Normal file
View File

@ -0,0 +1,64 @@
import { Row, Col, Card, Button, Space } from "antd";
import Markdown from "../components/markdown.tsx";
import { useLoaderData } from "react-router-dom";
interface LoaderProps {
params: {
id: string;
};
}
interface ProblemProps {
text: string;
}
export async function ProblemLoader({ params }: LoaderProps) {
console.log(params.id);
const text = `
# Problem 1
| Time Limit | Memory Limit |
| ---------- | ------------ |
| 1s | 256MB |
\`\`\`cpp
#include<iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
return 0;
}
\`\`\`
I'm a paragraph.
inline \`code\` test
`;
return { text: text };
}
function ProblemPage() {
const { text } = useLoaderData() as ProblemProps;
return (
<>
<Row justify="center" align="top" gutter={[16, 16]}>
<Col span={18}>
<Markdown markdown={text} />
</Col>
<Col span={6}>
<Card title="卡片标题">
<Space>
<Button type="primary">Primary</Button>
<Button type="primary">Primary</Button>
</Space>
</Card>
</Col>
</Row>
</>
);
}
export { ProblemPage };

63
src/pages/root.tsx Normal file
View File

@ -0,0 +1,63 @@
import { Link, Outlet, useLocation, useNavigation } from "react-router-dom";
import { Layout, Menu, Skeleton, theme } from "antd";
import { NavConfigs } from "../routes.tsx";
const { Header, Footer, Content } = Layout;
function Root() {
const {
token: { colorBgContainer },
} = theme.useToken();
const nav = useNavigation();
const pathname = useLocation().pathname;
const pathToKey = () => {
const routes = NavConfigs.filter((c) => pathname.startsWith(c.prefix));
if (routes.length === 0) return ["home"];
return [routes[0].key];
};
return (
<>
<Layout>
<Header style={{ alignItems: "center" }}>
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["home"]}
selectedKeys={pathToKey()}
>
{NavConfigs.map((c) => (
<Menu.Item key={c.key}>
<Link to={c.to}>{c.label}</Link>
</Menu.Item>
))}
</Menu>
</Header>
<Layout>
<Content
style={{
background: colorBgContainer,
display: "flex",
justifyContent: "center",
paddingTop: "30px",
paddingBottom: "30px",
}}
>
<div style={{ width: "90%" }}>
<Skeleton loading={nav.state === "loading"} active>
<Outlet />
</Skeleton>
</div>
</Content>
</Layout>
<Footer style={{ textAlign: "center" }}>
WOJ ©2023 Created by WHUPRJ
</Footer>
</Layout>
</>
);
}
export { Root };

0
src/pages/search.tsx Normal file
View File

29
src/routes.tsx Normal file
View File

@ -0,0 +1,29 @@
import { HomePage } from "./pages/home.tsx";
import { ProblemLoader, ProblemPage } from "./pages/pages.tsx";
const RouteConfigs = [
{
path: "home",
element: <HomePage />,
},
{
path: "problem",
element: <div>problems</div>,
},
{
path: "problem/:id",
element: <ProblemPage />,
loader: ProblemLoader,
},
{
path: "problem/:id/submit",
element: <div>problem submit</div>,
},
];
const NavConfigs = [
{ key: "home", to: "home", label: "Home", prefix: "/home" },
{ key: "problem", to: "problem", label: "Problem", prefix: "/problem" },
];
export { RouteConfigs, NavConfigs };