Skip to content

Commit

Permalink
Merge pull request #264 from anderbellstudios/dx/typescript
Browse files Browse the repository at this point in the history
Add TypeScript
  • Loading branch information
12joan authored May 6, 2024
2 parents 8430ee1 + 95b08ae commit 1a2d780
Show file tree
Hide file tree
Showing 69 changed files with 980 additions and 545 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:

- run: docker-compose -f docker-compose.test.yml up -d --build

- run: sleep 10

- run: docker-compose -f docker-compose.test.yml exec -T web curl -f -LI http://localhost:3000/healthcheck

- run: docker-compose -f docker-compose.test.yml down
Expand Down
2 changes: 1 addition & 1 deletion client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@

<body class="p-4 text-slate-800 dark:bg-slate-900 dark:text-white md:p-8">
<div id="app"></div>
<script type="module" src="/src/main.jsx"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
14 changes: 6 additions & 8 deletions client/src/AIControls.jsx → client/src/AIControls.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { h } from 'preact'
import useAppState from './useAppState'
import { useAppState } from './useAppState'
import { setComputerPlayer } from './appState/game/actions'
import useTryingToConnect from './useTryingToConnect'
import capitalise from './capitalise'
import { useTryingToConnect } from './useTryingToConnect'
import { capitalise } from './capitalise'
import { H2 } from './typography'
import { LargeSwitch } from './Switch'

const AIControls = () => {
export const AIControls = () => {
const computerPlayers = useAppState('app.game.computerPlayers')
const [tryingToConnect] = useTryingToConnect()

Expand All @@ -15,12 +15,12 @@ const AIControls = () => {
<H2 class="whitespace-nowrap">Play against computer</H2>

<div class="flex flex-col gap-2">
{['cross', 'circle', 'triangle'].map(player => (
{(['cross', 'circle', 'triangle'] as const).map(player => (
<label class="flex items-center gap-2">
<LargeSwitch
checked={computerPlayers[player]}
onChange={event =>
setComputerPlayer(player, event.target.checked)
setComputerPlayer(player, (event.target as any).checked)
}
disabled={tryingToConnect}
/>
Expand All @@ -32,5 +32,3 @@ const AIControls = () => {
</div>
)
}

export default AIControls
14 changes: 6 additions & 8 deletions client/src/App.jsx → client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { h } from 'preact'
import { H1, H2, H3, Paragraph, LeadParagraph } from './typography'
import GameArea from './GameArea'
import OnlineControls from './OnlineControls'
import AIControls from './AIControls'
import useViewport from './useViewport'
import useTryingToConnect from './useTryingToConnect'
import { GameArea } from './GameArea'
import { OnlineControls } from './OnlineControls'
import { AIControls } from './AIControls'
import { useViewport } from './useViewport'
import { useTryingToConnect } from './useTryingToConnect'

const App = () => {
export const App = () => {
const { viewportWidth } = useViewport()
const twoColumnLayout = viewportWidth >= 768

Expand Down Expand Up @@ -92,5 +92,3 @@ const App = () => {
</main>
)
}

export default App
12 changes: 5 additions & 7 deletions client/src/Button.jsx → client/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,24 @@ const commonClass =
const common = {
tag: 'button',
defaultProps: { type: 'button' },
}
} as const

const Button = elem({
export const Button = elem({
...common,
defaultClass: `${commonClass} py-2 px-4 rounded-lg hover:brightness-95 bg-pink-600 text-white`,
})

const SubtleButton = elem({
export const SubtleButton = elem({
...common,
defaultClass: `${commonClass} py-2 px-4 rounded-lg hover:brightness-95 bg-slate-100 dark:bg-slate-800 dark:hover:brightness-110 text-pink-700 dark:text-pink-500`,
})

const IconButton = elem({
export const IconButton = elem({
...common,
defaultClass: `${commonClass} rounded-lg text-pink-600 -m-1 p-1 hover:bg-slate-50 dark:hover:bg-slate-800 dark:text-pink-500`,
})

const ButtonLink = elem({
export const ButtonLink = elem({
...common,
defaultClass: `${commonClass} text-pink-600 dark:text-pink-500 underline hover:brightness-75 dark:hover:brightness-125`,
})

export { Button, SubtleButton, IconButton, ButtonLink }
27 changes: 14 additions & 13 deletions client/src/Dialog.jsx → client/src/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { h } from 'preact'
import { useState, useEffect, useRef } from 'preact/hooks'
import { ComponentChildren, h } from 'preact'
import { useEffect, useRef } from 'preact/hooks'
import { useA11yDialog } from 'react-a11y-dialog'
// @ts-ignore
import Hint from '@12joan/preact-hint'
import { IconButton } from './Button'

const Dialog = ({ id, title, open, onClose, children }) => {
export interface DialogProps {
id: string
title: string
open: boolean
onClose: () => void
children: ComponentChildren
}

export const Dialog = ({ id, title, open, onClose, children }: DialogProps) => {
const [instance, dialogProps] = useA11yDialog({ id, title, role: 'dialog' })

const actuallyOpen = useRef(false)
Expand Down Expand Up @@ -41,14 +50,10 @@ const Dialog = ({ id, title, open, onClose, children }) => {
)
}

const DialogCloseButton = ({ onClick }) => {
export const DialogCloseButton = ({ onClick }: { onClick: () => void }) => {
return (
<Hint>
<IconButton
onClick={onClick}
data-hint="Close"
aria-label="Close"
>
<IconButton onClick={onClick} data-hint="Close" aria-label="Close">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.5em"
Expand All @@ -64,7 +69,3 @@ const DialogCloseButton = ({ onClick }) => {
</Hint>
)
}

export { DialogCloseButton }

export default Dialog
34 changes: 18 additions & 16 deletions client/src/GameArea.jsx → client/src/GameArea.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { h } from 'preact'
import { useRef, useState } from 'preact/hooks'
// @ts-ignore
import Hint from '@12joan/preact-hint'
import { performNewGame, performUndo } from './appState/game/actions'
import useTryingToConnect from './useTryingToConnect'
import useGameOutcome from './useGameOutcome'
import useEventListener from './useEventListener'
import capitalise from './capitalise'
import Grid from './Grid'
import UpNext from './UpNext'
import { useTryingToConnect } from './useTryingToConnect'
import { useGameOutcome } from './useGameOutcome'
import { useEventListener } from './useEventListener'
import { capitalise } from './capitalise'
import { Grid } from './Grid'
import { UpNext } from './UpNext'
import { Button, SubtleButton, IconButton } from './Button'

const GameArea = ({ twoColumnLayout }) => {
export interface GameAreaProps {
twoColumnLayout: boolean
}

export const GameArea = ({ twoColumnLayout }: GameAreaProps) => {
const gameOutcome = useGameOutcome()
const [tryingToConnect] = useTryingToConnect()

const gridRef = useRef()
const gridRef = useRef<HTMLDivElement>(null)
const [gridDimensions, setGridDimensions] = useState({ padding: 0, width: 4 })

useEventListener(
window,
'resize',
() => {
const gridEl = gridRef.current
const gridEl = gridRef.current!
const padding = parseFloat(getComputedStyle(gridEl).paddingLeft)
const width = gridEl.clientWidth
setGridDimensions({ padding, width })
Expand All @@ -30,7 +35,7 @@ const GameArea = ({ twoColumnLayout }) => {
true
)

const centreOfCell = x =>
const centreOfCell = (x: number) =>
(x + 1) * gridDimensions.padding +
(x + 0.5) * 0.25 * (gridDimensions.width - 5 * gridDimensions.padding)

Expand Down Expand Up @@ -106,10 +111,9 @@ const GameArea = ({ twoColumnLayout }) => {
>
<div class="m-auto text-center">
<div class="mb-4 text-2xl sm:text-3xl">
{{
draw: () => "It's a draw!",
win: () => `${capitalise(gameOutcome.winner)} wins!`,
}[gameOutcome.type]()}
{gameOutcome.type === 'draw' && "It's a draw!"}
{gameOutcome.type === 'win' &&
`${capitalise(gameOutcome.winner)} wins!`}
</div>

<Button onClick={() => performNewGame(true)}>Play again</Button>
Expand All @@ -122,5 +126,3 @@ const GameArea = ({ twoColumnLayout }) => {
</div>
)
}

export default GameArea
31 changes: 0 additions & 31 deletions client/src/Grid.jsx

This file was deleted.

37 changes: 37 additions & 0 deletions client/src/Grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ComponentChildren, Ref, h } from 'preact'
import { forwardRef } from 'preact/compat'
import { performMove } from './appState/game/actions'
import { useAppState } from './useAppState'
import { GridCell } from './GridCell'
import { Move } from '../../common/types'

export interface GridProps {
disabled: boolean
children: ComponentChildren
}

export const Grid = forwardRef(
({ disabled, children }: GridProps, ref: Ref<HTMLDivElement>) => {
const boardState = useAppState('app.game.board')
const currentTurn = useAppState('app.game.currentTurn')

return (
<div
ref={ref}
class="relative grid grid-cols-4 gap-1 rounded-lg bg-slate-100 p-1 dark:bg-slate-800 sm:gap-2 sm:p-2"
>
{Array.from({ length: 16 }).map((_, i) => (
<GridCell
key={i}
shape={boardState[i]}
nextShape={currentTurn}
onClick={() => performMove(i as Move)}
disabled={disabled}
/>
))}

{children}
</div>
)
}
)
19 changes: 15 additions & 4 deletions client/src/GridCell.jsx → client/src/GridCell.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { h } from 'preact'
import { JSX, h } from 'preact'
import { Shape } from '../../common/types'

const GridCell = ({ shape = null, nextShape, disabled, ...otherProps }) => {
export interface GridCellProps
extends Omit<JSX.HTMLAttributes<HTMLButtonElement>, 'shape'> {
shape: Shape | null
nextShape: Shape
disabled?: boolean
}

export const GridCell = ({
shape = null,
nextShape,
disabled,
...otherProps
}: GridCellProps) => {
const emptyClass = `bg-slate-200 dark:bg-slate-700 ${
disabled
? 'cursor-not-allowed'
Expand All @@ -21,5 +34,3 @@ const GridCell = ({ shape = null, nextShape, disabled, ...otherProps }) => {
/>
)
}

export default GridCell
Loading

0 comments on commit 1a2d780

Please sign in to comment.