Skip to content

Commit

Permalink
Prevent flash before JS initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
connor-baer committed Aug 7, 2024
1 parent 77f6112 commit 0cee439
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 32 deletions.
17 changes: 14 additions & 3 deletions src/components/BidsForm.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { actions } from 'astro:actions';
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import { isNumber } from '../utils/type';
Expand All @@ -12,6 +13,12 @@
export let players: { id: string; name: string }[];
export let scores: Score[] | null;
let mounted = false;
onMount(() => {
mounted = true;
});
const store = writable(
players.map((player) => {
const score = scores?.find((score) => score.playerId === player.id) || {
Expand All @@ -22,8 +29,8 @@
}),
);
$: total = $store?.reduce((acc, { score }) => acc + (score?.bid || 0), 0);
$: valid = $store.every(({ score }) => isNumber(score?.bid));
$: total = $store?.reduce((acc, { score }) => acc + (score.bid || 0), 0);
$: valid = !mounted || $store.every(({ score }) => isNumber(score.bid));
</script>

<form method="POST" action={actions.updateBids}>
Expand All @@ -38,7 +45,7 @@
</ol>

<div class="footer">
<div><strong>Total:</strong> {total}/{round}</div>
<div class="total"><strong>Total:</strong> {total}/{round}</div>
<div class="buttons">
<button class="button primary" type="submit" disabled={!valid}>
Save bids
Expand All @@ -60,4 +67,8 @@
gap: 0.5rem;
flex-direction: row-reverse;
}
:global(.no-js) .total {
display: none;
}
</style>
43 changes: 31 additions & 12 deletions src/components/ScoreInput.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script lang="ts">
import { onMount } from 'svelte';
import { isNumber } from '../utils/type';
import {
isArrowDown,
Expand All @@ -24,11 +22,6 @@
let timeoutId: ReturnType<typeof setTimeout>;
let previousKeys = '';
let mounted = false;
onMount(() => {
mounted = true;
});
const min = 0;
const max = round;
Expand Down Expand Up @@ -77,9 +70,9 @@
{player.name}
{#if player.name === 'Mat'}🎯{/if}
</h3>
{#if delta}
{#if name === 'tricks' && delta}
<span
hidden={!delta}
class="delta"
class:positive={delta > 0}
class:negative={delta < 0}
role="status"
Expand All @@ -91,7 +84,7 @@
</div>
<input type="hidden" name="playerIds" value={player.id} />
<input
hidden={mounted}
class="input"
type="number"
{name}
bind:value={player.score[key]}
Expand All @@ -101,8 +94,7 @@
step="1"
/>
<div
hidden={!mounted}
class="input"
class="slider"
role="slider"
aria-labelledby={labelId}
tabindex="0"
Expand Down Expand Up @@ -133,6 +125,10 @@
align-items: baseline;
}
:global(.no-js) .delta {
display: none;
}
.positive {
color: green;
}
Expand All @@ -146,10 +142,32 @@
}
.input {
display: none;
padding: 6px 10px;
border-radius: 4px;
border: 1px solid var(--color-fg-subtle);
width: 100%;
max-width: 10ch;
}
.input:focus,
.input:active {
border: 1px solid var(--color-fg-default);
}
:global(.no-js) .input {
display: block;
}
.slider {
display: inline-block;
border-radius: 1rem;
}
:global(.no-js) .slider {
display: none;
}
.option {
display: inline-grid;
place-content: center;
Expand All @@ -176,5 +194,6 @@
background-color: var(--color-bg-primary);
border: 1px solid var(--color-fg-primary);
color: var(--color-fg-primary);
font-weight: var(--font-weight-ui-bold);
}
</style>
19 changes: 15 additions & 4 deletions src/components/TricksForm.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { actions } from 'astro:actions';
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import { isNumber } from '../utils/type';
Expand All @@ -12,6 +13,12 @@
export let players: { id: string; name: string }[];
export let scores: Score[] | null;
let mounted = false;
onMount(() => {
mounted = true;
});
const store = writable(
players.map((player) => {
const score = scores?.find((score) => score.playerId === player.id) || {
Expand All @@ -22,9 +29,9 @@
}),
);
$: total = $store?.reduce((acc, { score }) => acc + (score?.tricks || 0), 0);
$: allTricks = $store.every(({ score }) => isNumber(score?.tricks));
$: valid = allTricks && total === round;
$: total = $store?.reduce((acc, { score }) => acc + (score.tricks || 0), 0);
$: allTricks = $store.every(({ score }) => isNumber(score.tricks));
$: valid = !mounted || (allTricks && total === round);
</script>

<form method="POST" action={actions.updateTricks}>
Expand All @@ -39,7 +46,7 @@
</ol>

<div class="footer">
<div><strong>Total:</strong> {total}/{round}</div>
<div class="total"><strong>Total:</strong> {total}/{round}</div>
<div class="buttons">
<button class="button primary" type="submit" disabled={!valid}>
Save tricks
Expand All @@ -61,4 +68,8 @@
gap: 0.5rem;
flex-direction: row-reverse;
}
:global(.no-js) .total {
display: none;
}
</style>
5 changes: 4 additions & 1 deletion src/layouts/Base.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const { title = 'Wizard', description } = Astro.props;
---

<!doctype html>
<html lang="en">
<html lang="en" class="no-js">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, viewport-fit=cover" />
Expand Down Expand Up @@ -52,6 +52,9 @@ const { title = 'Wizard', description } = Astro.props;
<meta name="msapplication-config" content="/icons/browserconfig.xml" />
<meta name="msapplication-TileColor" content="#8e4491" />
<meta name="theme-color" content="#8e4491" />
<script is:inline data-astro-rerun>
document.documentElement.classList.remove('no-js');
</script>
<ViewTransitions />
</head>
<body>
Expand Down
8 changes: 7 additions & 1 deletion src/pages/game/[gameId]/round/[round]/bids.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { z } from 'astro/zod';
import Layout from '../../../../../layouts/Standard.astro';
import BidsForm from '../../../../../components/BidsForm.svelte';
import { getGame, getPlayers, getScores } from '../../../../../db';
import { getMaxRounds } from '../../../../../utils/game';
const schema = z.object({
gameId: z.string(),
Expand Down Expand Up @@ -34,12 +35,17 @@ if (result && !result.error) {
const players = await getPlayers(gameId, round);
const scores = await getScores(gameId, round);
const maxRounds = getMaxRounds(players);
const description =
'Each player predicts how many tricks they will take this round.';
---

<Layout title={`Round ${round}`} description={description}>
<Layout
title={`Round ${round} of ${maxRounds}`}
description={description}
link={{ label: 'Scores', href: `/game/${gameId}/scores` }}
>
<h2>Bids</h2>
<p>{description}</p>

Expand Down
8 changes: 7 additions & 1 deletion src/pages/game/[gameId]/round/[round]/dealer.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { z } from 'astro/zod';
import Layout from '../../../../../layouts/Standard.astro';
import { getGame, getPlayers } from '../../../../../db';
import { getMaxRounds } from '../../../../../utils/game';
import { pluralize } from '../../../../../utils/format';
const schema = z.object({
Expand All @@ -27,11 +28,16 @@ if (!game) {
const players = await getPlayers(gameId, round);
const dealer = players[players.length - 1]!;
const maxRounds = getMaxRounds(players);
const description = `${dealer.name} deals ${round} ${pluralize({ singular: 'card', plural: 'cards' }, round)}.`;
---

<Layout title={`Round ${round}`} description={description}>
<Layout
title={`Round ${round} of ${maxRounds}`}
description={description}
link={{ label: 'Scores', href: `/game/${gameId}/scores` }}
>
<h2>Dealer</h2>
<p>{description}</p>
<div class="footer">
Expand Down
8 changes: 7 additions & 1 deletion src/pages/game/[gameId]/round/[round]/tricks.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { z } from 'astro/zod';
import Layout from '../../../../../layouts/Standard.astro';
import TricksForm from '../../../../../components/TricksForm.svelte';
import { getGame, getPlayers, getScores } from '../../../../../db';
import { getMaxRounds } from '../../../../../utils/game';
const schema = z.object({
gameId: z.string(),
Expand Down Expand Up @@ -34,11 +35,16 @@ if (result && !result.error) {
const players = await getPlayers(gameId, round);
const scores = await getScores(gameId, round);
const maxRounds = getMaxRounds(players);
const description = 'Count how many tricks each player managed to take.';
---

<Layout title={`Round ${round}`} description={description}>
<Layout
title={`Round ${round} of ${maxRounds}`}
description={description}
link={{ label: 'Scores', href: `/game/${gameId}/scores` }}
>
<h2>Tricks</h2>
<p>{description}</p>

Expand Down
6 changes: 5 additions & 1 deletion src/pages/game/[gameId]/scores.astro
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ const players = await getPlayers(gameId);
const scoreCard = await getScoreCard(gameId, players);
---

<Layout title="Scores" description="TODO:">
<Layout
title="Scores"
description="TODO:"
link={{ label: 'Back', href: `/game/${gameId}` }}
>
<table style={`--player-count: ${players.length}`}>
<thead>
<tr>
Expand Down
3 changes: 2 additions & 1 deletion src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ const hasActiveGame = games.some(game => !game.endedAt)
margin-bottom: 0.75rem;
}

a {
p a {
font-family: var(--font-family-display);
font-size: 0.9em;
}

.buttons {
Expand Down
6 changes: 3 additions & 3 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
/* type-role(-state) */
--font-family-display: 'Metamorphous', system-ui, sans-serif;
--font-family-ui: system-ui, sans-serif;
--font-weight-ui: 350;
--font-weight-ui-medium: 600;
--font-weight-ui-bold: 800;
--font-weight-ui: regular;
--font-weight-ui-bold: bold;
--font-family-code: Menlo, Monaco, Lucida Console, Liberation Mono,
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
--font-size-prose: 1.125rem;
Expand Down Expand Up @@ -293,6 +292,7 @@ p {
.button.primary {
background-color: var(--color-bg-primary);
color: var(--color-fg-primary);
font-weight: var(--font-weight-ui-bold);
}

.footer {
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ import type { Game, Scores } from 'astro:db';
export type GameId = (typeof Game.$inferSelect)['id'];
export type Round = number;
export type Score = typeof Scores.$inferSelect;
export type Player = {
id: string;
name: string;
position: number;
};
15 changes: 11 additions & 4 deletions src/utils/game.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { POINTS_CORRECT, POINTS_PER_TRICK } from '../constants';
import type { Player, Score } from '../types';
import {
NUMBER_OF_CARDS,
POINTS_CORRECT,
POINTS_PER_TRICK,
} from '../constants';

import { isNumber } from './type';

export function calculateScoreDelta(
score: { bid: number | null; tricks: number | null } | null | undefined,
) {
export function calculateScoreDelta(score: Score | null | undefined) {
if (!score || !isNumber(score.bid) || !isNumber(score.tricks)) {
return null;
}
Expand All @@ -15,3 +18,7 @@ export function calculateScoreDelta(

return -1 * Math.abs(score.tricks - score.bid) * POINTS_PER_TRICK;
}

export function getMaxRounds(players: Player[]) {
return NUMBER_OF_CARDS / players.length;
}

0 comments on commit 0cee439

Please sign in to comment.