Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
fix: component prop types
Browse files Browse the repository at this point in the history
  • Loading branch information
jwcub committed Apr 21, 2024
1 parent a99d311 commit c9449cf
Show file tree
Hide file tree
Showing 10 changed files with 977 additions and 606 deletions.
4 changes: 3 additions & 1 deletion app/components/game/roomList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Tooltip,
Tr
} from "@chakra-ui/react";
import { useNavigate } from "@remix-run/react";
import { useTranslation } from "react-i18next";
import type { IconType } from "react-icons";
import { FaTachometerAlt } from "react-icons/fa";
Expand Down Expand Up @@ -96,14 +97,15 @@ function VotedRoomProperty<T extends VoteItem>({

function Room({ id, ongoing, players, rated, votes }: RoomProps) {
const { t } = useTranslation();
const navigate = useNavigate();

return (
<Button
as={Tr}
display="table-row"
fontWeight="normal"
cursor="pointer"
onClick={() => open(`/game/${encodeURIComponent(id)}`)}
onClick={() => navigate(id)}
variant="ghost"
>
<Td>{id}</Td>
Expand Down
5 changes: 3 additions & 2 deletions app/components/layout/hamburgerIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { motion } from "framer-motion";
import type { ComponentPropsWithRef } from "react";

function Path(props: typeof motion.path.defaultProps) {
function Path(props: ComponentPropsWithRef<typeof motion.path>) {
return (
<motion.path
fill="transparent"
Expand All @@ -12,7 +13,7 @@ function Path(props: typeof motion.path.defaultProps) {
);
}

type HamburgerButtonProps = typeof motion.svg.defaultProps & {
type HamburgerButtonProps = ComponentPropsWithRef<typeof motion.svg> & {
isOpen: boolean;
};

Expand Down
25 changes: 25 additions & 0 deletions app/game/gm/land.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { expect, it } from "vitest";

import { Land } from "./land";

it("creates a land", () => {
const land = new Land(1, 2, 3);

expect(land.color).toBe(1);
expect(land.type).toBe(2);
expect(land.amount).toBe(3);
});

it("serializes a land", () => {
const land = new Land(1, 2, 3);
const exported = [1, 2, 3];

expect(JSON.stringify(land)).toBe(JSON.stringify(exported));
});

it("deserializes a land", () => {
const exported = [1, 2, 3] as const;
const land = new Land(1, 2, 3);

expect(Land.from(exported)).toStrictEqual(land);
});
46 changes: 46 additions & 0 deletions app/game/gm/land.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Map land.
*/
export class Land extends Array<number> {
/**
* Land types.
*/
static Type = {
Land: 0,
General: 1,
City: 2,
Mountain: 3,
UnknownCity: 4,
Unknown: 5,
UnknownMountain: 6,
Swamp: 7
} as const;

/**
* Land color.
*/
get color() {
return this[0];
}

/**
* Land type.
*/
get type() {
return this[1];
}

/**
* Land amount.
*/
get amount() {
return this[2];
}

/**
* Creates a Land with given color, type and amount.
*/
constructor(color = 0, type = 0, amount = 0) {
super(color, type, amount);
}
}
80 changes: 80 additions & 0 deletions app/game/gm/matrix.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { expect, it } from "vitest";

import { Matrix } from "./matrix";

it("creates a matrix", () => {
const height = 2,
width = 1;
const items = [
[0, 0],
[0, 0],
[0, 0]
];
const matrix = Matrix.default(height, width, 0);

expect(matrix.width).toBe(width);
expect(matrix.height).toBe(height);
expect(matrix).toEqual(items);
});

it("serializes a matrix", () => {
const matrix = Matrix.default(2, 1, 0);
const items = [
[0, 0],
[0, 0],
[0, 0]
];

expect(JSON.stringify(matrix)).toBe(JSON.stringify(items));
});

it("deserializes a matrix", () => {
const items = [
[0, 0],
[0, 0],
[0, 0]
];
const matrix = Matrix.default(2, 1, 0);

expect(Matrix.from(items)).toStrictEqual(matrix);
});

it("gets an item", () => {
const matrix = Matrix.default(2, 1, 0);

expect(matrix.get([1, 1])).toStrictEqual(0);
});

it("sets an item", () => {
const matrix = Matrix.default(2, 2, 0);
matrix.set([1, 1], 1);
const items = [
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
];

expect(matrix).toEqual(items);
});

it("checks a pos", () => {
const matrix = Matrix.default(1, 2, 0);

expect(matrix.check([1, 1])).toBe(true);
expect(matrix.check([1, 2])).toBe(true);

expect(matrix.check([1, 3])).toBe(false);
expect(matrix.check([0, 1])).toBe(false);
expect(matrix.check([2, 1])).toBe(false);
expect(matrix.check([1, 0])).toBe(false);
});

it("returns all positions", () => {
const matrix = Matrix.default(1, 2, 0);
const positions = [
[1, 1],
[1, 2]
];

expect(matrix.positions()).toStrictEqual(positions);
});
122 changes: 122 additions & 0 deletions app/game/gm/matrix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { isEqual } from "lodash";

import { Patch } from "./patch";

/**
* Positions in a Matrix.
*/
export type Pos = [number, number];

/**
* Basic matrix extended from two-dimensional Array.
*
* A Matrix supports indexing, updating, diffing and patching.
*/
export class Matrix<Item> extends Array<Item[]> {
/**
* Matrix height.
*/
get height() {
return this.length - 1;
}

/**
* Matrix width.
*/
get width() {
return this[0].length - 1;
}

/**
* Not meant to be called directly.
*/
private constructor(...args: ConstructorParameters<typeof Array<Item[]>>) {
super(...args);
}

/**
* Creates a Matrix from iterables.
*/
static from<Item>(iterables: Item[][]) {
return new this(...iterables);
}

/**
* Creates a Matrix with the given width and height,
* filling in the default value resolved by a function provided by the caller.
*
* The function will be called each time to produce an isolated value.
*/
static defaultWith<Item>(
height: number,
width: number,
defaultFunction: () => Item
) {
const matrix = new this<Item>();

for (let i = 0; i <= height; i++) {
matrix.push([]);

for (let j = 0; j <= width; j++) {
matrix[i].push(defaultFunction());
}
}

return matrix;
}

/**
* Creates a Matrix with the given width and height,
* filling in the default value provided by the caller.
*
* This function uses Matrix.defaultWith internally.
*/
static default<Item>(height: number, width: number, defaultValue: Item) {
return this.defaultWith(height, width, () => defaultValue);
}

/**
* Gets a particular item.
*/
get([x, y]: Pos) {
return this[x][y];
}

/**
* Updates a particular item.
*/
set([x, y]: Pos, item: Item) {
this[x][y] = item;
}

/**
* Checks whether the position is in the Matrix.
*/
check([x, y]: Pos) {
return x >= 1 && x <= this.height && y >= 1 && y <= this.width;
}

/**
* Returns an array of all positions in the Matrix for iterating.
*/
positions() {
const ans = [];

for (let i = 1; i <= this.height; i++) {
for (let j = 1; j <= this.width; j++) {
ans.push([i, j] as Pos);
}
}

return ans;
}

/**
* Diffs two matrices and returns the Patch array.
*/
diff(other: Matrix<Item>) {
return this.positions()
.filter(pos => !isEqual(this.get(pos), other.get(pos)))
.map(pos => new Patch(pos, other.get(pos)));
}
}
41 changes: 41 additions & 0 deletions app/game/gm/patch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, it } from "vitest";

import type { Pos } from "./matrix";
import { Matrix } from "./matrix";
import { Patch } from "./patch";

it("creates a patch", () => {
const pos: Pos = [1, 2],
item = 0;
const patch = new Patch(pos, item);

expect(patch.pos).toBe(pos);
expect(patch.item).toBe(item);
});

it("diffs two matrices", () => {
const a = Matrix.from([
[0, 0, 0],
[0, 1, 0],
[0, 1, 0]
]);

const b = Matrix.from([
[0, 0, 0],
[0, 1, 0],
[0, 2, 3]
]);

const aDiffB = [
[[2, 1], 2],
[[2, 2], 3]
];

const bDiffA = [
[[2, 1], 1],
[[2, 2], 0]
];

expect(a.diff(b)).toEqual(aDiffB);
expect(b.diff(a)).toEqual(bDiffA);
});
27 changes: 27 additions & 0 deletions app/game/gm/patch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Pos } from "./matrix";

/**
* A single patch object consisting of a position and an item.
*/
export class Patch<Item> extends Array<Pos | Item> {
/**
* Patch position.
*/
get pos() {
return this[0] as Pos;
}

/**
* Patch item.
*/
get item() {
return this[1] as Item;
}

/**
* Creates a new Patch with the given position and item.
*/
constructor(pos: Pos, item: Item) {
super(pos, item);
}
}
Loading

0 comments on commit c9449cf

Please sign in to comment.