Skip to content

Commit 28a505e

Browse files
committed
Add cookie for wordle
1 parent bc95928 commit 28a505e

32 files changed

+32843
-0
lines changed

wordle-remix/.eslintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["@remix-run/eslint-config", "@remix-run/eslint-config/node"]
3+
}

wordle-remix/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
3+
/.cache
4+
/build
5+
/public/build
6+
.env
7+
*.log

wordle-remix/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Welcome to Remix!
2+
3+
- [Remix Docs](https://remix.run/docs)
4+
5+
## Development
6+
7+
From your terminal:
8+
9+
```sh
10+
npm run dev
11+
```
12+
13+
This starts your app in development mode, rebuilding assets on file changes.
14+
15+
## Deployment
16+
17+
First, build your app for production:
18+
19+
```sh
20+
npm run build
21+
```
22+
23+
Then run the app in production mode:
24+
25+
```sh
26+
npm start
27+
```
28+
29+
Now you'll need to pick a host to deploy it to.
30+
31+
### DIY
32+
33+
If you're familiar with deploying node applications, the built-in Remix app server is production-ready.
34+
35+
Make sure to deploy the output of `remix build`
36+
37+
- `build/`
38+
- `public/build/`
39+
40+
### Using a Template
41+
42+
When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server.
43+
44+
```sh
45+
cd ..
46+
# create a new project, and pick a pre-configured host
47+
npx create-remix@latest
48+
cd my-new-remix-app
49+
# remove the new project's app (not the old one!)
50+
rm -rf app
51+
# copy your app over
52+
cp -R ../my-old-remix-app/app app
53+
```
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { HTMLProps } from "react";
2+
import classNames from "classnames";
3+
4+
type Variant = "primary" | "secondary";
5+
type Size = "md" | "lg";
6+
7+
interface ButtonProps extends HTMLProps<HTMLButtonElement> {
8+
variant?: Variant;
9+
size?: Size;
10+
type?: "button" | "submit" | "reset";
11+
}
12+
13+
interface ButtonStyles {
14+
variant?: Variant;
15+
size?: Size;
16+
disabled?: boolean;
17+
}
18+
19+
export const styles = ({
20+
variant = "primary",
21+
size = "md",
22+
disabled = false,
23+
}: ButtonStyles = {}) =>
24+
classNames(
25+
`inline-block rounded-lg transition-colors leading-tight leading-tight
26+
uppercase
27+
rounded
28+
shadow-md transition
29+
duration-150
30+
ease-in-out`,
31+
{
32+
"text-white bg-sky-500 hover:bg-sky-600": variant === "primary",
33+
"hover:bg-sky-600": variant === "primary" && !disabled,
34+
"bg-sky-100": variant === "secondary",
35+
"hover:bg-sky-200": variant === "secondary" && !disabled,
36+
"opacity-50": disabled,
37+
},
38+
{
39+
"px-6 py-3 text-base": size === "md",
40+
"px-8 py-4 text-lg": size === "lg",
41+
}
42+
);
43+
44+
export function Button({
45+
children,
46+
variant = "primary",
47+
size = "md",
48+
onClick,
49+
disabled,
50+
...props
51+
}: ButtonProps) {
52+
return (
53+
<button
54+
className={styles({ variant, disabled, size })}
55+
disabled={disabled}
56+
onClick={onClick}
57+
{...props}
58+
>
59+
{children}
60+
</button>
61+
);
62+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useKeyDown } from "~/hooks/useKeyDown";
2+
import { useOutsideClick } from "~/hooks/useOutsideClick";
3+
import { useRef } from "react";
4+
5+
import type { ReactNode } from "react";
6+
7+
interface DialogProps {
8+
children: ReactNode;
9+
onClose: () => void;
10+
}
11+
12+
export function Dialog({ children, onClose }: DialogProps) {
13+
const ref = useRef<HTMLDivElement>(null);
14+
15+
useOutsideClick(ref, onClose);
16+
useKeyDown("Escape", onClose);
17+
18+
return (
19+
<div className="absolute top-0 left-0 h-screen w-screen bg-black/80 w-100 h-100 flex justify-center items-start">
20+
<div
21+
ref={ref}
22+
role="dialog"
23+
className="mx-auto my-12 p-12 rounded-md bg-white inline-block"
24+
>
25+
{children}
26+
</div>
27+
</div>
28+
);
29+
}

wordle-remix/app/components/Grid.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ReactNode } from "react";
2+
3+
interface GridProps {
4+
children: ReactNode;
5+
}
6+
7+
export function Grid({ children }: GridProps) {
8+
return <div className="grid grid-cols-5 gap-4">{children}</div>;
9+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Grid } from "~/components/Grid";
2+
import { Tile } from "~/components/Tile";
3+
4+
export function Illustration() {
5+
return (
6+
<div className="inline-block">
7+
<Grid>
8+
<Tile status="match">I</Tile>
9+
<Tile status="include">N</Tile>
10+
<Tile status="miss">F</Tile>
11+
<Tile status="miss">I</Tile>
12+
<Tile status="match">N</Tile>
13+
14+
<Tile status="match">I</Tile>
15+
<Tile status="miss">T</Tile>
16+
<Tile status="include">E</Tile>
17+
<Tile status="miss">W</Tile>
18+
<Tile status="miss">O</Tile>
19+
20+
<Tile status="miss">R</Tile>
21+
<Tile status="match">D</Tile>
22+
<Tile status="include">L</Tile>
23+
<Tile status="match">E</Tile>
24+
<Tile status="miss">.</Tile>
25+
</Grid>
26+
</div>
27+
);
28+
}

wordle-remix/app/components/Logo.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import classNames from "classnames";
2+
3+
interface LogoProps {
4+
size?: "sm" | "md" | "lg";
5+
}
6+
7+
export function Logo({ size = "sm" }: LogoProps) {
8+
return (
9+
<div
10+
className={classNames("inline-flex flex-col items-center", {
11+
"text-base": size === "sm",
12+
"text-xl": size === "md",
13+
"text-4xl": size === "lg",
14+
})}
15+
>
16+
<span className="text-sky-400 text-[0.875em] uppercase font-bold leading-none">
17+
Infinite
18+
</span>
19+
<span className="text-[1.125em] uppercase font-bold leading-none">
20+
Wordle
21+
</span>
22+
</div>
23+
);
24+
}

wordle-remix/app/components/Mark.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ReactNode } from "react";
2+
3+
interface MarkProps {
4+
children?: ReactNode;
5+
}
6+
7+
export function Mark({ children }: MarkProps) {
8+
return (
9+
<mark className="bg-sky-100 text-sky-700 rounded-md p-1">
10+
{children}
11+
</mark>
12+
);
13+
}

wordle-remix/app/components/Tile.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ReactNode } from "react";
2+
import classNames from "classnames";
3+
import { LetterStatus } from "~/types";
4+
5+
interface TileProps {
6+
children?: ReactNode;
7+
status?: LetterStatus;
8+
delay?: number;
9+
}
10+
11+
export function Tile({ children, status, delay = 0 }: TileProps) {
12+
return (
13+
<div
14+
style={{ transitionDelay: `${delay}ms` }}
15+
className={classNames(
16+
"w-16 h-16 text-xl font-bold rounded-md flex items-center justify-center transition-colors ease-out",
17+
{
18+
"bg-green-400": status === "match",
19+
"bg-yellow-400": status === "include",
20+
"bg-gray-400": status === "miss",
21+
"bg-gray-100": !status,
22+
}
23+
)}
24+
>
25+
{children}
26+
</div>
27+
);
28+
}

wordle-remix/app/entry.client.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { RemixBrowser } from "@remix-run/react";
2+
import { hydrate } from "react-dom";
3+
4+
hydrate(<RemixBrowser />, document);

wordle-remix/app/entry.server.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { EntryContext } from "@remix-run/node";
2+
import { RemixServer } from "@remix-run/react";
3+
import { renderToString } from "react-dom/server";
4+
5+
export default function handleRequest(
6+
request: Request,
7+
responseStatusCode: number,
8+
responseHeaders: Headers,
9+
remixContext: EntryContext
10+
) {
11+
let markup = renderToString(
12+
<RemixServer context={remixContext} url={request.url} />
13+
);
14+
15+
responseHeaders.set("Content-Type", "text/html");
16+
17+
return new Response("<!DOCTYPE html>" + markup, {
18+
status: responseStatusCode,
19+
headers: responseHeaders,
20+
});
21+
}

wordle-remix/app/hooks/useKeyDown.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useEffect } from "react";
2+
3+
export function useKeyDown(
4+
targetKey: string,
5+
callback: (event: KeyboardEvent) => void
6+
) {
7+
useEffect(() => {
8+
function handleKeyPress(event: KeyboardEvent) {
9+
if (event.key === targetKey) {
10+
callback(event);
11+
}
12+
}
13+
14+
document.addEventListener("keydown", handleKeyPress);
15+
return () => {
16+
document.removeEventListener("keydown", handleKeyPress);
17+
};
18+
}, [targetKey, callback]);
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { useEffect } from "react";
2+
3+
import type { RefObject } from "react";
4+
5+
export function useOutsideClick(
6+
ref: RefObject<HTMLElement>,
7+
callback: (event: Event) => void
8+
) {
9+
useEffect(() => {
10+
function handleClickOutside(event: Event) {
11+
if (ref.current && !ref.current.contains(event.target as Node)) {
12+
callback(event);
13+
}
14+
}
15+
document.addEventListener("mousedown", handleClickOutside);
16+
return () =>
17+
document.removeEventListener("mousedown", handleClickOutside); // Cleanup Function
18+
}, [callback]);
19+
}

wordle-remix/app/root.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
Links,
3+
LiveReload,
4+
Meta,
5+
Outlet,
6+
ScrollRestoration,
7+
Scripts,
8+
Link,
9+
} from "@remix-run/react";
10+
11+
import { Logo } from "~/components/Logo";
12+
13+
import type { LinksFunction, MetaFunction } from "@remix-run/node";
14+
15+
import stylesUrl from "./styles/app.css";
16+
17+
const meta: MetaFunction = () => ({
18+
charset: "utf-8",
19+
title: "New Remix App",
20+
viewport: "width=device-width,initial-scale=1",
21+
});
22+
23+
const links: LinksFunction = () => [{ rel: "stylesheet", href: stylesUrl }];
24+
25+
export default function App() {
26+
return (
27+
<html lang="en">
28+
<head>
29+
<Meta />
30+
<Links />
31+
</head>
32+
<body>
33+
<header className="flex justify-center border-b-2 border-gray-100 p-4">
34+
<Link to="/">
35+
<Logo size="md" />
36+
</Link>
37+
</header>
38+
<Outlet />
39+
<ScrollRestoration />
40+
<Scripts />
41+
<LiveReload />
42+
</body>
43+
</html>
44+
);
45+
}
46+
47+
export { meta, links };

0 commit comments

Comments
 (0)