Compare commits

...

3 Commits

Author SHA1 Message Date
65f60ec6b1 Lib: Remove LazyDropdown
It was identical to "Dropdown" after removing the non-lazy "Image"
component
2024-12-27 19:44:05 +01:00
bcd8f3dfb5 Lib: Close Team/Driver/Race/Substitution cards when clicking on the image 2024-12-27 19:43:36 +01:00
00a577520c Lib: Allow to pass an onclick handler for the card image 2024-12-27 19:43:16 +01:00
9 changed files with 13 additions and 176 deletions

View File

@ -23,6 +23,9 @@
/** The width class for the card, defaults to [w-auto] */ /** The width class for the card, defaults to [w-auto] */
width?: string; width?: string;
/** An optional event handler for clicking the image */
imgonclick?: (event: Event) => void;
} }
let { let {
@ -33,6 +36,7 @@
imgid = undefined, imgid = undefined,
imghidden = false, imghidden = false,
width = "w-auto", width = "w-auto",
imgonclick = undefined,
...restProps ...restProps
}: CardProps = $props(); }: CardProps = $props();
@ -53,6 +57,7 @@
hidden={imghidden} hidden={imghidden}
imgwidth={imgwidth ?? 0} imgwidth={imgwidth ?? 0}
imgheight={imgheight ?? 0} imgheight={imgheight ?? 0}
onclick={imgonclick}
/> />
{/if} {/if}

View File

@ -66,6 +66,7 @@
width="w-full sm:w-auto" width="w-full sm:w-auto"
imgwidth={DRIVER_HEADSHOT_WIDTH} imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->

View File

@ -74,6 +74,7 @@
width="w-full sm:w-auto" width="w-full sm:w-auto"
imgwidth={RACE_PICTOGRAM_WIDTH} imgwidth={RACE_PICTOGRAM_WIDTH}
imgheight={RACE_PICTOGRAM_HEIGHT} imgheight={RACE_PICTOGRAM_HEIGHT}
imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->

View File

@ -101,6 +101,7 @@
width="w-full sm:w-auto" width="w-full sm:w-auto"
imgwidth={DRIVER_HEADSHOT_WIDTH} imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->

View File

@ -55,6 +55,7 @@
width="w-full sm:w-auto" width="w-full sm:w-auto"
imgwidth={TEAM_BANNER_WIDTH} imgwidth={TEAM_BANNER_WIDTH}
imgheight={TEAM_BANNER_HEIGHT} imgheight={TEAM_BANNER_HEIGHT}
imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->

View File

@ -1,148 +0,0 @@
<script lang="ts">
import { ListBox, ListBoxItem, popup, type PopupSettings } from "@skeletonlabs/skeleton";
import type { Snippet } from "svelte";
import type { Action } from "svelte/action";
import type { HTMLInputAttributes } from "svelte/elements";
import { v4 as uuid } from "uuid";
import { type LazyDropdownOption, LazyImage } from "$lib/components";
interface LazyDropdownProps extends HTMLInputAttributes {
children: Snippet;
/** Placeholder for the empty input element */
placeholder?: string;
/** Form name of the input element, to reference input data after form submission */
name?: string;
/** Manually set the label width, to align multiple inputs vertically. Supply value in CSS units. */
labelwidth?: string;
/** The variable to bind to the input element. Has to be a [$state] so its value can be updated with the input element's contents. */
input_variable: string;
/** Any action to bind to the input element */
action?: Action;
/** The ID of the popup to trigger. UUID by default. */
popup_id?: string;
/** The [PopupSettings] object for the popup to trigger. */
popup_settings?: PopupSettings;
/** The options this autocomplete component allows to choose from.
* Example: [[{ label: "Aston", value: "0" }, { label: "VCARB", value: "1" }]].
*/
options: LazyDropdownOption[];
}
let {
children,
placeholder = "",
name = "",
labelwidth = "auto",
input_variable,
action = undefined,
popup_id = uuid(),
popup_settings = {
event: "click",
target: popup_id,
placement: "bottom",
closeQuery: ".listbox-item",
},
options,
...restProps
}: LazyDropdownProps = $props();
/** Find the "label" of an option by its "value" */
const get_label = (value: string): string | undefined => {
return options.find((o) => o.value === value)?.label;
};
// Use an action to fill the "input" variable
// required to dispatch the custom event using $effect
let input: HTMLInputElement | undefined = undefined;
const obtain_input: Action = (node: HTMLElement) => {
input = node as HTMLInputElement;
};
// This will run everyting "input_variable" changes.
// The event is fired when the input's value is updated via JavaScript.
$effect(() => {
// Just list this so SvelteKit picks it up as dependency
input_variable;
if (input) input.dispatchEvent(new CustomEvent("DropdownChange"));
});
let load: boolean = $state(false);
const lazy_click_handler = () => {
load = true;
};
</script>
<div class="input-group input-group-divider grid-cols-[auto_1fr_auto]">
<div
class="input-group-shim select-none text-nowrap text-neutral-900"
style="width: {labelwidth};"
>
{@render children()}
</div>
<!-- TODO: How to assign use: conditionally? I don't wan't to repeat the entire input... -->
{#if action}
<input
use:popup={popup_settings}
type="button"
autocomplete="off"
style="height: 42px; text-align: start; text-indent: 12px; border-top-left-radius: 0; border-bottom-left-radius: 0;"
use:obtain_input
use:action
onmousedown={lazy_click_handler}
onkeypress={(event: Event) => event.preventDefault()}
value={get_label(input_variable) ?? placeholder}
{...restProps}
/>
{:else}
<input
use:popup={popup_settings}
type="button"
autocomplete="off"
style="height: 42px; text-align: start; text-indent: 12px; border-top-left-radius: 0; border-bottom-left-radius: 0;"
use:obtain_input
onmousedown={lazy_click_handler}
onkeypress={(event: Event) => event.preventDefault()}
value={get_label(input_variable) ?? placeholder}
{...restProps}
/>
{/if}
</div>
<div
data-popup={popup_id}
class="card z-10 w-auto overflow-y-scroll p-2 shadow"
style="max-height: 350px;"
>
{#if load}
<ListBox>
{#each options as option}
<ListBoxItem bind:group={input_variable} {name} value={option.value}>
<div class="flex flex-nowrap">
{#if option.icon_url}
<LazyImage
src={option.icon_url}
alt=""
imgwidth={option.icon_width ?? 1}
imgheight={option.icon_height ?? 1}
class="rounded"
imgstyle="height: 24px;"
containerstyle="height: 24px;"
/>
{/if}
<span class="ml-2">{option.label}</span>
</div>
</ListBoxItem>
{/each}
</ListBox>
{/if}
</div>

View File

@ -1,18 +0,0 @@
import { type DropdownOption } from "$lib/components";
export interface LazyDropdownOption extends DropdownOption {
/** The label displayed in the list of options. */
label: string;
/** The value assigned to the dropdown value variable */
value: string;
/** An optional icon displayed left to the label */
icon_url?: string;
/** The aspect ratio width of the optional icon */
icon_width?: number;
/** The aspect ratio height of the optional icon */
icon_height?: number;
}

View File

@ -5,7 +5,6 @@ import Table from "./Table.svelte";
import Button from "./form/Button.svelte"; import Button from "./form/Button.svelte";
import Dropdown from "./form/Dropdown.svelte"; import Dropdown from "./form/Dropdown.svelte";
import Input from "./form/Input.svelte"; import Input from "./form/Input.svelte";
import LazyDropdown from "./form/LazyDropdown.svelte";
import Search from "./form/Search.svelte"; import Search from "./form/Search.svelte";
import Card from "./cards/Card.svelte"; import Card from "./cards/Card.svelte";
@ -16,7 +15,6 @@ import SubstitutionCard from "./cards/SubstitutionCard.svelte";
import TeamCard from "./cards/TeamCard.svelte"; import TeamCard from "./cards/TeamCard.svelte";
import type { DropdownOption } from "./form/Dropdown"; import type { DropdownOption } from "./form/Dropdown";
import type { LazyDropdownOption } from "./form/LazyDropdown";
import type { TableColumn } from "./Table"; import type { TableColumn } from "./Table";
import MenuDrawerIcon from "./svg/MenuDrawerIcon.svelte"; import MenuDrawerIcon from "./svg/MenuDrawerIcon.svelte";
@ -33,7 +31,6 @@ export {
Button, Button,
Dropdown, Dropdown,
Input, Input,
LazyDropdown,
Search, Search,
// Cards // Cards
@ -46,7 +43,6 @@ export {
// Types // Types
type DropdownOption, type DropdownOption,
type LazyDropdownOption,
type TableColumn, type TableColumn,
// SVG // SVG

View File

@ -9,15 +9,13 @@
type ModalStore, type ModalStore,
} from "@skeletonlabs/skeleton"; } from "@skeletonlabs/skeleton";
import { Button, Table, type LazyDropdownOption, type TableColumn } from "$lib/components"; import { Button, Table, type DropdownOption, type TableColumn } from "$lib/components";
import { get_by_value } from "$lib/database"; import { get_by_value } from "$lib/database";
import { import {
DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_HEIGHT,
DRIVER_HEADSHOT_WIDTH, DRIVER_HEADSHOT_WIDTH,
RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_HEIGHT,
RACE_PICTOGRAM_WIDTH, RACE_PICTOGRAM_WIDTH,
TEAM_BANNER_HEIGHT,
TEAM_BANNER_WIDTH,
TEAM_LOGO_HEIGHT, TEAM_LOGO_HEIGHT,
TEAM_LOGO_WIDTH, TEAM_LOGO_WIDTH,
} from "$lib/config"; } from "$lib/config";
@ -57,7 +55,7 @@
update_substitution_race_select_values["create"] = ""; update_substitution_race_select_values["create"] = "";
// All options to create a <Dropdown> component for the teams // All options to create a <Dropdown> component for the teams
const team_dropdown_options: LazyDropdownOption[] = []; const team_dropdown_options: DropdownOption[] = [];
data.teams.forEach((team: Team) => { data.teams.forEach((team: Team) => {
team_dropdown_options.push({ team_dropdown_options.push({
label: team.name, label: team.name,
@ -69,7 +67,7 @@
}); });
// All options to create a <Dropdown> component for the drivers // All options to create a <Dropdown> component for the drivers
const driver_dropdown_options: LazyDropdownOption[] = []; const driver_dropdown_options: DropdownOption[] = [];
data.drivers.then((drivers: Driver[]) => data.drivers.then((drivers: Driver[]) =>
drivers.forEach((driver: Driver) => { drivers.forEach((driver: Driver) => {
driver_dropdown_options.push({ driver_dropdown_options.push({
@ -83,7 +81,7 @@
); );
// All options to create a <Dropdown> component for the races // All options to create a <Dropdown> component for the races
const race_dropdown_options: LazyDropdownOption[] = []; const race_dropdown_options: DropdownOption[] = [];
data.races.then((races: Race[]) => data.races.then((races: Race[]) =>
races.forEach((race: Race) => { races.forEach((race: Race) => {
race_dropdown_options.push({ race_dropdown_options.push({