style upgrades

This commit is contained in:
yohlo
2026-07-12 21:26:39 -07:00
parent ec334bbed4
commit 4f81f3c0ca
72 changed files with 1541 additions and 471 deletions
@@ -4,6 +4,7 @@ import { BracketData } from "../types";
import { Bracket } from "./bracket";
import useAppShellHeight from "@/hooks/use-appshell-height";
import { Match } from "@/features/matches/types";
import styles from "./styles.module.css";
interface BracketViewProps {
bracket: BracketData;
@@ -25,7 +26,7 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, showControls, groupC
return <ScrollArea
h={`calc(${height})`}
className="bracket-container"
className={styles["bracket-container"]}
style={{
backgroundImage: `radial-gradient(circle, var(--mantine-color-default-border) 1px, transparent 1px)`,
backgroundSize: "16px 16px",
@@ -134,6 +134,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
queryClient.invalidateQueries({
queryKey: tournamentKeys.details(match.tournament.id),
});
editSheet.close();
},
});
@@ -149,9 +150,8 @@ export const MatchCard: React.FC<MatchCardProps> = ({
matchId: match.id,
},
});
editSheet.close();
},
[match.id, editSheet]
[end, match.id]
);
const speak = useCallback((text: string): Promise<void> => {
@@ -311,6 +311,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
variant="subtle"
color="gray"
onClick={handleSpeakerClick}
aria-label="Announce matchup"
>
<SpeakerHighIcon size={12} />
</ActionIcon>
@@ -323,10 +324,12 @@ export const MatchCard: React.FC<MatchCardProps> = ({
<ActionIcon
color="green"
onClick={handleStart}
loading={start.isPending}
size="sm"
h="100%"
radius="sm"
ml={-4}
aria-label="Start match"
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
@@ -343,10 +346,12 @@ export const MatchCard: React.FC<MatchCardProps> = ({
<ActionIcon
color="blue"
onClick={editSheet.open}
loading={end.isPending}
size="sm"
h="100%"
radius="sm"
ml={-4}
aria-label="Edit match score"
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
@@ -363,6 +368,7 @@ export const MatchCard: React.FC<MatchCardProps> = ({
match={match}
onSubmit={handleFormSubmit}
onCancel={editSheet.close}
loading={end.isPending}
/>
</Sheet>
</Flex>
+11 -3
View File
@@ -10,12 +10,14 @@ interface MatchFormProps {
ot_count: number;
}) => void;
onCancel: () => void;
loading?: boolean;
}
export const MatchForm: React.FC<MatchFormProps> = ({
match,
onSubmit,
onCancel,
loading = false,
}) => {
const form = useForm({
initialValues: {
@@ -35,7 +37,6 @@ export const MatchForm: React.FC<MatchFormProps> = ({
return "At least one team must have 10 cups";
}
// Both teams can't have 10 cups
if (homeCups === 10 && awayCups === 10) {
return "Both teams cannot have 10 cups";
}
@@ -145,8 +146,15 @@ export const MatchForm: React.FC<MatchFormProps> = ({
</Flex>
<Stack mt="md">
<Button type="submit">Update Match</Button>
<Button variant="subtle" color="red" onClick={onCancel}>
<Button type="submit" loading={loading}>
Update Match
</Button>
<Button
variant="subtle"
color="red"
onClick={onCancel}
disabled={loading}
>
Cancel
</Button>
</Stack>
@@ -0,0 +1,16 @@
.slotEnter {
animation: slot-enter 300ms ease-out;
}
@keyframes slot-enter {
from {
opacity: 0;
transform: translateY(4px);
}
}
@media (prefers-reduced-motion: reduce) {
.slotEnter {
animation: none;
}
}
+77 -55
View File
@@ -1,8 +1,10 @@
import { Flex, Text } from "@mantine/core";
import React from "react";
import React, { useEffect, useRef, useState } from "react";
import { CrownIcon } from "@phosphor-icons/react";
import { SeedBadge } from "./seed-badge";
import { TeamInfo } from "@/features/teams/types";
import AnimatedScore from "@/features/matches/components/animated-score";
import classes from "./match-slot.module.css";
interface MatchSlotProps {
from?: number;
@@ -22,60 +24,80 @@ export const MatchSlot: React.FC<MatchSlotProps> = ({
cups,
isWinner,
groupLabel
}) => (
<Flex
align="stretch"
style={{
backgroundColor: isWinner ? 'var(--mantine-color-green-light)' : 'transparent',
borderRadius: 'var(--mantine-radius-sm)',
transition: 'background-color 200ms ease',
}}
>
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
<Flex p="6px 10px" w='100%' align="center">
<Flex align="center" gap={4} flex={1}>
{team ? (
<>
<Text
size={team.name.length > 12 ? (team.name.length > 18 ? '10px' : '11px') : 'xs'}
truncate
style={{ minWidth: 0, flex: 1, lineHeight: "12px" }}
>
{team.name}
}) => {
const teamId = team?.id;
const previousTeamIdRef = useRef<string | undefined>(teamId);
const [entranceKey, setEntranceKey] = useState(0);
useEffect(() => {
const previousTeamId = previousTeamIdRef.current;
previousTeamIdRef.current = teamId;
if (teamId && previousTeamId !== teamId) {
setEntranceKey((key) => key + 1);
}
}, [teamId]);
return (
<Flex
align="stretch"
style={{
backgroundColor: isWinner ? 'var(--mantine-color-green-light)' : 'transparent',
borderRadius: 'var(--mantine-radius-sm)',
transition: 'background-color 200ms ease',
}}
>
{(seed && seed > 0) ? <SeedBadge seed={seed} /> : undefined}
<Flex p="6px 10px" w='100%' align="center">
<Flex
key={entranceKey}
className={entranceKey > 0 ? classes.slotEnter : undefined}
align="center"
gap={4}
flex={1}
>
{team ? (
<>
<Text
size={team.name.length > 12 ? (team.name.length > 18 ? '10px' : '11px') : 'xs'}
truncate
style={{ minWidth: 0, flex: 1, lineHeight: "12px" }}
>
{team.name}
</Text>
{isWinner && (
<CrownIcon
size={14}
weight="fill"
style={{
color: 'gold',
marginLeft: '2px',
marginTop: '-1px',
filter: 'drop-shadow(0 1px 1px rgba(0,0,0,0.3))',
flexShrink: 0
}}
/>
)}
</>
) : groupLabel ? (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
{groupLabel}
</Text>
{isWinner && (
<CrownIcon
size={14}
weight="fill"
style={{
color: 'gold',
marginLeft: '2px',
marginTop: '-1px',
filter: 'drop-shadow(0 1px 1px rgba(0,0,0,0.3))',
flexShrink: 0
}}
/>
)}
</>
) : groupLabel ? (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
{groupLabel}
</Text>
) : from ? (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
{from_loser ? "Loser" : "Winner"} of Match {from}
</Text>
) : (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
TBD
</Text>
)}
) : from ? (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
{from_loser ? "Loser" : "Winner"} of Match {from}
</Text>
) : (
<Text c="dimmed" size="xs" truncate style={{ minWidth: 0, flex: 1 }}>
TBD
</Text>
)}
</Flex>
{
cups !== undefined ? (
<AnimatedScore ta='center' w={15} fw={800} ml={4} size="xs" value={cups} />
) : undefined
}
</Flex>
{
cups !== undefined ? (
<Text ta='center' w={15} fw="800" ml={4} size="xs">{cups}</Text>
) : undefined
}
</Flex>
</Flex>
);
);
};