Improve result placement display with custom medal icons

This commit is contained in:
Remmy Cat Stock
2022-10-15 13:16:43 +02:00
parent 9bb7de7dba
commit 9f7faa2054
7 changed files with 72 additions and 9 deletions

View File

@@ -0,0 +1,48 @@
import { getEnglishOrdinalSuffix } from "~/utils/strings"
export type PlacementProps = {
placement: number,
iconClassName?: string,
textClassName?: string
}
const getSpecialPlacementIconPath = (placement: number): string | null => {
switch (placement) {
case 3:
return'/svg/placements/third.svg';
case 2:
return '/svg/placements/second.svg';
case 1:
return '/svg/placements/first.svg';
default:
return null;
}
}
export function Placement({ placement, iconClassName, textClassName }: PlacementProps) {
/*
Placements are using english ordinal syntax only.
If wished for, we could look into properly adding translations here, but
english-style ordinals are commonly used internationally as well.
*/
const ordinalSuffix = getEnglishOrdinalSuffix(placement);
const iconPath = getSpecialPlacementIconPath(placement);
if (!iconPath) {
return <span className={textClassName} lang="en-us">
{placement}<sup>{ordinalSuffix}</sup>
</span>
}
const placementString = `${placement}${ordinalSuffix}`;
return <img
lang="en-us"
alt={placementString}
title={placementString}
src={iconPath}
className={iconClassName}
height={20}
width={20}
/>
}

View File

@@ -15,6 +15,7 @@ import { Avatar } from "~/components/Avatar";
import { LinkButton } from "~/components/Button";
import { Image } from "~/components/Image";
import { Main } from "~/components/Main";
import { Placement } from "~/components/Placement";
import { Section } from "~/components/Section";
import { db } from "~/db";
import { useIsMounted } from "~/hooks/useIsMounted";
@@ -28,7 +29,7 @@ import calendarStyles from "~/styles/calendar-event.css";
import mapsStyles from "~/styles/maps.css";
import { databaseTimestampToDate } from "~/utils/dates";
import { notFoundIfFalsy } from "~/utils/remix";
import { discordFullName, makeTitle, placementString } from "~/utils/strings";
import { discordFullName, makeTitle } from "~/utils/strings";
import {
calendarEditPage,
calendarReportWinnersPage,
@@ -200,7 +201,9 @@ function Results() {
<tbody>
{data.results.map((result, i) => (
<tr key={i}>
<td className="pl-4">{placementString(result.placement)}</td>
<td className="pl-4">
<Placement placement={result.placement} />
</td>
<td>{result.teamName}</td>
<td>
<ul className="event__results-players">

View File

@@ -2,9 +2,10 @@ import { Link, useMatches } from "@remix-run/react";
import { useTranslation } from "react-i18next";
import invariant from "tiny-invariant";
import { Avatar } from "~/components/Avatar";
import { Placement } from "~/components/Placement";
import { Section } from "~/components/Section";
import { databaseTimestampToDate } from "~/utils/dates";
import { discordFullName, placementString } from "~/utils/strings";
import { discordFullName } from "~/utils/strings";
import { calendarEventPage, userPage } from "~/utils/urls";
import type { UserPageLoaderData } from "../u.$identifier";
@@ -31,7 +32,9 @@ export default function UserResultsPage() {
<tbody>
{data.results.map((result) => (
<tr key={result.eventId}>
<td className="pl-4">{placementString(result.placement)}</td>
<td className="pl-4">
<Placement placement={result.placement} />
</td>
<td>{result.teamName}</td>
<td>
<Link to={calendarEventPage(result.eventId)}>

View File

@@ -10,12 +10,18 @@ export function makeTitle(title: string | string[]) {
return `${Array.isArray(title) ? title.join(" | ") : title} | sendou.ink`;
}
export function placementString(placement: number) {
if (placement === 1) return "🥇";
if (placement === 2) return "🥈";
if (placement === 3) return "🥉";
export function getEnglishOrdinalSuffix(num: number) {
const lastDigit = num % 10;
const last2Digits = num % 100;
return `${placement}th`;
if (lastDigit === 1 && last2Digits !== 11) {
return 'st';
} else if (lastDigit === 2 && last2Digits !== 12) {
return 'nd';
} else if (lastDigit === 3 && last2Digits !== 13) {
return 'rd';
}
return 'th';
}
export function semiRandomId() {

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#f3e64a" offset="0"/><stop stop-color="#e2b405" offset="1"/></linearGradient><linearGradient id="e" x1="70.984" x2="88.639" y1="-23.39" y2="-97.925" gradientUnits="userSpaceOnUse"><stop stop-color="#e16a16" offset="0"/><stop stop-color="#eb8014" stop-opacity=".415" offset="1"/></linearGradient><linearGradient id="b" x1="52.493" x2="80.947" y1="33.175" y2="99.585" gradientTransform="translate(-1.513)" gradientUnits="userSpaceOnUse"><stop stop-color="#c98413" offset="0"/><stop stop-color="#b74e00" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0682 0 0 1.0634 -4.367 4.058)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#d4d4d4" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".954"/><stop stop-color="#fff" stop-opacity=".432" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M44.995 28.584s-1.022 16.073-.372 28.336c0 0 3.81.465 6.41.279 0 0 .094 24.713.558 41.807 0 0 14.68.65 28.894.464 0 0 .186-34.189 0-70.7 0 0-16.816-.465-35.49-.187z" fill="url(#b)" style="shape-inside:url(#rect77399);white-space:pre"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.6" ry="51.366" fill="url(#c)" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".434"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#9d3801"/></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#a2b5ca" offset="0"/><stop stop-color="#7c94b3" offset="1"/></linearGradient><linearGradient id="e" x1="79.345" x2="83.332" y1="-18.74" y2="-106.86" gradientUnits="userSpaceOnUse"><stop stop-color="#1f3e64" offset="0"/><stop stop-color="#14407b" stop-opacity=".56" offset="1"/></linearGradient><linearGradient id="b" x1="44.567" x2="97.969" y1="39.61" y2="111.55" gradientTransform="translate(2.082 -.139)" gradientUnits="userSpaceOnUse"><stop stop-color="#4e6284" offset="0"/><stop stop-color="#35435d" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0684 0 0 1.0635 -4.383 4.06)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#1985aa" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".939"/><stop stop-color="#fff" stop-opacity=".71" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M69.628 81.133c25.401-5.268 25.495-26.06 25.495-26.06 0-22.485-20.697-25.966-30.199-26.53-.188 0-19.662-1.129-27.659 2.164-.376 8.655-.188 23.896-.094 28.223 19.286-.47 20.603 8.09 20.697 8.843.658 7.432-10.443 13.077-20.697 15.335-.094 7.526-.094 13.735 0 15.993 22.579.47 48.168.188 53.436.094.376-6.962.188-13.453 0-15.993-7.056-.188-20.697.094-20.697.094z" fill="url(#b)"/><ellipse transform="rotate(90)" cx="63.993" cy="-64.007" rx="51.607" ry="51.373" fill="url(#c)" opacity=".346" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".291"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#4b536e" /></g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="13.101" x2="114.9" y1="-64" y2="-64" gradientUnits="userSpaceOnUse"><stop stop-color="#eb7258" offset="0"/><stop stop-color="#ac5341" offset="1"/></linearGradient><linearGradient id="e" x1="73.629" x2="93.549" y1="-25.502" y2="-103.49" gradientUnits="userSpaceOnUse"><stop stop-color="#3f1e18" offset="0"/><stop stop-color="#51271f" stop-opacity=".421" offset="1"/></linearGradient><linearGradient id="b" x1="43.134" x2="49.212" y1="36.009" y2="45.558" gradientTransform="matrix(8.5638 0 0 7.8638 -325.88 -248.07)" gradientUnits="userSpaceOnUse"><stop stop-color="#6d342a" offset="0"/><stop stop-color="#4b231c" offset="1"/></linearGradient><radialGradient id="c" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0682 0 0 1.0634 -4.367 4.058)" gradientUnits="userSpaceOnUse"><stop stop-color="#ccc" stop-opacity="0" offset=".892"/><stop stop-color="#d4d4d4" offset="1"/></radialGradient><radialGradient id="d" cx="64" cy="-64" r="48.304" gradientTransform="matrix(1.0749 0 0 1.0568 -4.795 3.638)" gradientUnits="userSpaceOnUse"><stop stop-color="#fff" stop-opacity="0" offset=".954"/><stop stop-color="#fff" stop-opacity=".432" offset="1"/></radialGradient><filter id="f" x="-.059" y="-.025" width="1.119" height="1.051" color-interpolation-filters="sRGB"><feGaussianBlur result="blur" stdDeviation="1.004"/></filter></defs><g transform="translate(-3.288 -3.288) scale(1.0514)"><circle transform="rotate(90)" cx="64" cy="-64" r="50.899" fill="url(#a)"/><path d="M87.978 66.718c9.763-6.04 8.221-17.835 8.118-18.213-1.85-12.268-11.613-17.08-20.759-18.779-11.407-2.076-29.699-1.038-38.126-.377-.308 10.003 0 22.553.103 26.894 15.826-5.38 17.573 4.246 17.676 4.529 1.336 6.7-8.015 9.53-16.956 8.021l.103 6.511c7.296-1.604 16.442-.188 16.751 5.568.411 7.55-7.913 8.304-16.751 6.417-.103 4.152 0 8.21 0 9.908 8.735 1.321 27.13 2.076 27.13 2.076 14.49 1.227 24.561-2.359 29.494-11.7 5.036-9.343 1.233-17.742-6.782-20.856z" fill="url(#b)" style="shape-inside:url(#rect244);white-space:pre"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.6" ry="51.366" fill="url(#c)" style="mix-blend-mode:overlay"/><ellipse transform="rotate(90)" cx="64" cy="-64" rx="51.923" ry="51.05" fill="url(#d)" style="mix-blend-mode:luminosity"/><path transform="rotate(89.024 -1.49 -.09)" d="M71.494-16.465A48.126 48.126 0 0 0 112.127-64a48.126 48.126 0 0 0-40.633-47.535z" fill="url(#e)" filter="url(#f)" opacity=".326"/><path d="M64 3.127C30.428 3.127 3.127 30.428 3.127 64S30.428 124.873 64 124.873 124.873 97.572 124.873 64 97.572 3.127 64 3.127zm0 8c29.248 0 52.873 23.625 52.873 52.873S93.248 116.873 64 116.873 11.127 93.248 11.127 64 34.752 11.127 64 11.127z" color="#000" fill="#fff" style="-inkscape-stroke:none;paint-order:stroke fill markers"/><path d="M64 7.127A56.873 56.873 0 0 0 7.127 64 56.873 56.873 0 0 0 64 120.873 56.873 56.873 0 0 0 120.873 64 56.873 56.873 0 0 0 64 7.127zm0 5.975A50.899 50.899 0 0 1 114.898 64 50.899 50.899 0 0 1 64 114.898 50.899 50.899 0 0 1 13.102 64 50.899 50.899 0 0 1 64 13.102z" color="#000" fill="#692913" /></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB