Skip to content

Commit 55bf1bc

Browse files
committed
just some light refactoring this fine friday
1 parent 4bd2783 commit 55bf1bc

32 files changed

+445
-443
lines changed

app/auth/cookie.ts

Whitespace-only changes.

app/components/button.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { forwardRef } from "react";
2+
3+
export let Button = forwardRef<
4+
HTMLButtonElement,
5+
React.ButtonHTMLAttributes<HTMLButtonElement>
6+
>((props, ref) => {
7+
return (
8+
<button
9+
{...props}
10+
ref={ref}
11+
className="flex w-full justify-center rounded-md bg-brand-blue px-1 py-1 text-sm font-semibold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-blue"
12+
/>
13+
);
14+
});

app/components/input.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { forwardRef, useId } from "react";
2+
3+
export let Input = forwardRef<
4+
HTMLInputElement,
5+
React.InputHTMLAttributes<HTMLInputElement>
6+
>((props, ref) => {
7+
return (
8+
<input
9+
{...props}
10+
ref={ref}
11+
className="form-input block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-brand-blue sm:text-sm sm:leading-6"
12+
/>
13+
);
14+
});
15+
16+
export let Label = forwardRef<
17+
HTMLLabelElement,
18+
React.LabelHTMLAttributes<HTMLLabelElement>
19+
>((props, ref) => {
20+
return (
21+
<label
22+
{...props}
23+
ref={ref}
24+
className="block text-sm font-medium leading-6 text-gray-900"
25+
/>
26+
);
27+
});
28+
29+
export let LabeledInput = forwardRef<
30+
HTMLInputElement,
31+
React.InputHTMLAttributes<HTMLInputElement> & {
32+
label: React.ReactNode;
33+
id?: string;
34+
}
35+
>(({ id, label, ...props }, ref) => {
36+
let uid = useId();
37+
id = id ?? uid;
38+
return (
39+
<>
40+
<Label htmlFor={id}>{label}</Label>
41+
<div className="mt-2">
42+
<Input {...props} ref={ref} id={id} />
43+
</div>
44+
</>
45+
);
46+
});

app/icons/icons.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function Icon({
2626

2727
export function LoginIcon() {
2828
return (
29-
<svg className="inline self-center w-8 h-8 text-white">
29+
<svg className="inline self-center w-8 h-8 text-white transform scale-x-[-1]">
3030
<use href={`${iconsHref}#login`} />
3131
</svg>
3232
);

app/modules/create-form.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

app/root.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import {
99
type ShouldRevalidateFunctionArgs,
1010
Link,
1111
} from "@remix-run/react";
12-
import "./styles.css";
12+
import { redirect, type DataFunctionArgs } from "@remix-run/node";
13+
1314
import { LoginIcon, LogoutIcon } from "./icons/icons";
1415
import { getAuthFromRequest } from "./auth/auth";
15-
import { redirect, type DataFunctionArgs } from "@remix-run/node";
16+
17+
import "./styles.css";
1618

1719
export async function loader({ request }: DataFunctionArgs) {
1820
let auth = await getAuthFromRequest(request);

app/routes/board.$id/CONTENT_TYPES.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

app/routes/board.$id/board.tsx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { useRef } from "react";
2+
import invariant from "tiny-invariant";
3+
import { useFetchers, useLoaderData } from "@remix-run/react";
4+
5+
import { type loader } from "./server";
6+
import { INTENTS, type RenderedItem } from "./types";
7+
import { Column } from "./column";
8+
import { NewColumn } from "./new-column";
9+
10+
export function Board() {
11+
let { board } = useLoaderData<typeof loader>();
12+
13+
let itemsById = new Map(board.items.map((item) => [item.id, item]));
14+
let pendingItems = usePendingItems();
15+
16+
// merge pending items and existing items
17+
for (let pendingItem of pendingItems) {
18+
let item = itemsById.get(pendingItem.id);
19+
let merged = item
20+
? { ...item, ...pendingItem }
21+
: { ...pendingItem, boardId: board.id };
22+
itemsById.set(pendingItem.id, merged);
23+
}
24+
25+
// merge pending and existing columns
26+
let optAddingColumns = useAddingColumns();
27+
type Column = (typeof board.columns)[0] | (typeof optAddingColumns)[0];
28+
type ColumnWithItems = Column & { items: typeof board.items };
29+
let columns = [...board.columns, ...optAddingColumns].reduce(
30+
(map, column) => map.set(String(column.id), { ...column, items: [] }),
31+
new Map<string, ColumnWithItems>(),
32+
);
33+
34+
// add items to their columns
35+
for (let item of itemsById.values()) {
36+
let columnId = item.columnId;
37+
let column = columns.get(columnId);
38+
invariant(column, "missing column");
39+
column.items.push(item);
40+
}
41+
42+
let scrollContainerRef = useRef<HTMLDivElement>(null);
43+
function scrollRight() {
44+
invariant(scrollContainerRef.current);
45+
scrollContainerRef.current.scrollLeft =
46+
scrollContainerRef.current.scrollWidth;
47+
}
48+
49+
return (
50+
<div
51+
className="h-full min-h-0 flex flex-col overflow-x-scroll"
52+
ref={scrollContainerRef}
53+
style={{ backgroundColor: board.color }}
54+
>
55+
<h1 className="px-8 my-4 text-2xl font-medium">{board.name}</h1>
56+
57+
<div className="flex flex-grow min-h-0 h-full items-start gap-4 px-8 pb-4">
58+
{[...columns.values()].map((col) => {
59+
return (
60+
<Column
61+
key={col.id}
62+
name={col.name}
63+
columnId={col.id}
64+
items={col.items}
65+
/>
66+
);
67+
})}
68+
69+
<NewColumn
70+
boardId={board.id}
71+
onAdd={scrollRight}
72+
editInitially={board.columns.length === 0}
73+
/>
74+
75+
<div data-lol-shutup className="w-8 h-1 flex-shrink-0" />
76+
</div>
77+
</div>
78+
);
79+
}
80+
function useAddingColumns() {
81+
type CreateColumnFetcher = ReturnType<typeof useFetchers>[0] & {
82+
formData: FormData;
83+
};
84+
85+
return useFetchers()
86+
.filter((fetcher): fetcher is CreateColumnFetcher => {
87+
return fetcher.formData?.get("intent") === INTENTS.createColumn;
88+
})
89+
.map((fetcher) => {
90+
let name = String(fetcher.formData.get("name"));
91+
let id = String(fetcher.formData.get("id"));
92+
return { name, id };
93+
});
94+
}
95+
function usePendingItems() {
96+
type PendingItem = ReturnType<typeof useFetchers>[0] & {
97+
formData: FormData;
98+
};
99+
return useFetchers()
100+
.filter((fetcher): fetcher is PendingItem => {
101+
if (!fetcher.formData) return false;
102+
let intent = fetcher.formData.get("intent");
103+
return intent === INTENTS.createItem || intent === INTENTS.moveItem;
104+
})
105+
.map((fetcher) => {
106+
let columnId = String(fetcher.formData.get("columnId"));
107+
let title = String(fetcher.formData.get("title"));
108+
let id = String(fetcher.formData.get("id"));
109+
let order = Number(fetcher.formData.get("order"));
110+
let item: RenderedItem = { title, id, order, columnId, content: null };
111+
return item;
112+
});
113+
}

app/routes/board.$id/card.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { flushSync } from "react-dom";
2+
import invariant from "tiny-invariant";
13
import { useSubmit } from "@remix-run/react";
24
import { useState } from "react";
3-
import { INTENTS, ItemMutation } from "./mutations";
4-
import invariant from "tiny-invariant";
5-
import { CONTENT_TYPES } from "./CONTENT_TYPES";
6-
import { flushSync } from "react-dom";
5+
6+
import { ItemMutation, INTENTS, CONTENT_TYPES } from "./types";
77

88
interface CardProps {
99
title: string;

app/routes/board.$id/column.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { useState, useEffect, useRef } from "react";
22
import { useFetcher, useSubmit } from "@remix-run/react";
33

4-
import { Icon } from "../../icons/icons";
5-
6-
import { INTENTS, ItemMutation } from "./mutations";
4+
import { Icon } from "~/icons/icons";
5+
6+
import {
7+
ItemMutation,
8+
INTENTS,
9+
CONTENT_TYPES,
10+
type RenderedItem,
11+
} from "./types";
712
import invariant from "tiny-invariant";
813
import { NewCard } from "./new-card";
914
import { flushSync } from "react-dom";
10-
import { CONTENT_TYPES } from "./CONTENT_TYPES";
1115
import { Card } from "./card";
12-
import type { RenderedItem } from "./types";
1316

1417
interface ColumnProps {
1518
name: string;

0 commit comments

Comments
 (0)