Lib: Add RacePickCard component

This commit is contained in:
2025-01-27 23:47:10 +01:00
parent 9ad670ab5e
commit aee0da174d
2 changed files with 163 additions and 0 deletions

View File

@ -0,0 +1,161 @@
<script lang="ts">
import { Card, Button, Dropdown, type DropdownOption } from "$lib/components";
import type { Driver, Race, RacePick, User } from "$lib/schema";
import { get_by_value } from "$lib/database";
import type { Action } from "svelte/action";
import { getModalStore, type ModalStore } from "@skeletonlabs/skeleton";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
interface RacePickCardProps {
/** The [RacePick] object used to prefill values. */
racepick?: RacePick | undefined;
/** The [Race] object containing the place to guess */
currentrace: Race | null;
/** The [User] currently logged in */
user?: User;
/** The drivers (to display the headshot) */
drivers: Driver[];
/** Disable all inputs if [true] */
disable_inputs?: boolean;
/** The [src] of the driver headshot template preview */
headshot_template?: string;
/** The value this component's pxx select dropdown will bind to */
pxx_select_value: string;
/** The value this component's dnf select dropdown will bind to */
dnf_select_value: string;
/** The options this component's driver select dropdowns will display */
driver_select_options: DropdownOption[];
}
let {
racepick = undefined,
currentrace = null,
user = undefined,
drivers,
disable_inputs = false,
headshot_template = "",
pxx_select_value,
dnf_select_value,
driver_select_options,
}: RacePickCardProps = $props();
const modalStore: ModalStore = getModalStore();
if ($modalStore[0].meta) {
const meta = $modalStore[0].meta;
// Stuff thats required for the "update" card
racepick = meta.racepick;
currentrace = meta.currentrace;
user = meta.user;
drivers = meta.drivers;
disable_inputs = meta.disable_inputs;
headshot_template = meta.headshot_template;
pxx_select_value = meta.pxx_select_value;
dnf_select_value = meta.dnf_select_value;
driver_select_options = meta.driver_select_options;
}
// This action is used on the <Dropdown> element.
// It will trigger once the Dropdown's <input> elements is mounted.
// This way we'll receive a reference to the object so we can register our event handler.
const register_pxx_preview_handler: Action = (node: HTMLElement) => {
node.addEventListener("DropdownChange", update_pxx_preview);
};
// This event handler is registered to the Dropdown's <input> element through the action above.
const update_pxx_preview = (event: Event) => {
const target: HTMLInputElement = event.target as HTMLInputElement;
// The option "label" gets put into the Dropdown's input value,
// so we need to lookup the driver by "code".
const src: string = get_by_value(drivers, "code", target.value)?.headshot_url || "";
if (src) {
const preview: HTMLImageElement = document.getElementById(
`update_substitution_headshot_preview_${racepick?.id ?? "create"}`,
) as HTMLImageElement;
if (preview) preview.src = src;
}
};
</script>
<Card
imgsrc={get_by_value(drivers, "id", racepick?.pxx ?? "")?.headshot_url ?? headshot_template}
imgid="update_substitution_headshot_preview_{racepick?.id ?? 'create'}"
width="w-full sm:w-auto"
imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()}
>
<form method="POST" enctype="multipart/form-data">
<!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs -->
{#if racepick && !disable_inputs}
<input name="id" type="hidden" value={racepick.id} />
{/if}
<input name="user" type="hidden" value={user?.id} />
<input name="race" type="hidden" value={currentrace?.id} />
<div class="flex flex-col gap-2">
<!-- PXX select -->
<Dropdown
name="pxx"
input_variable={pxx_select_value}
action={register_pxx_preview_handler}
options={driver_select_options}
labelwidth="120px"
disabled={disable_inputs}
>
P{currentrace?.pxx ?? "XX"}
</Dropdown>
<!-- DNF select -->
<Dropdown
name="dnf"
input_variable={dnf_select_value}
options={driver_select_options}
labelwidth="120px"
disabled={disable_inputs}
>
DNF
</Dropdown>
<!-- Save/Delete buttons -->
<div class="flex justify-end gap-2">
{#if racepick}
<Button
formaction="?/update_racepick"
color="secondary"
disabled={disable_inputs}
submit
width="w-1/2"
>
Save Changes
</Button>
<Button
color="primary"
submit
disabled={disable_inputs}
formaction="?/delete_racepick"
width="w-1/2"
>
Delete
</Button>
{:else}
<Button formaction="?/create_racepick" color="tertiary" submit width="w-full">
Make Pick
</Button>
{/if}
</div>
</div>
</form>
</Card>

View File

@ -10,6 +10,7 @@ import Search from "./form/Search.svelte";
import Card from "./cards/Card.svelte";
import DriverCard from "./cards/DriverCard.svelte";
import RaceCard from "./cards/RaceCard.svelte";
import RacePickCard from "./cards/RacePickCard.svelte";
import SubstitutionCard from "./cards/SubstitutionCard.svelte";
import TeamCard from "./cards/TeamCard.svelte";
@ -38,6 +39,7 @@ export {
Card,
DriverCard,
RaceCard,
RacePickCard,
SubstitutionCard,
TeamCard,