Compare commits

...

1 Commits

Author SHA1 Message Date
3b91fad590 Implement 'internal submit button' to the 'Textbox' component. Remove separate submit to the Headcode search card.
Adjust the hover and active styles of the 'TrainService' expandable cards.
2026-05-05 15:55:51 +01:00
3 changed files with 85 additions and 16 deletions

View File

@@ -2,6 +2,8 @@
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import type { HTMLInputAttributes } from 'svelte/elements'; import type { HTMLInputAttributes } from 'svelte/elements';
import { IconChevronRightFilled } from '@tabler/icons-svelte';
interface Props extends HTMLInputAttributes { interface Props extends HTMLInputAttributes {
value?: string; value?: string;
label?: string; label?: string;
@@ -9,6 +11,7 @@
type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url'; type?: 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url';
error?: string; error?: string;
uppercase?: boolean; uppercase?: boolean;
onsubmit?: (val: string) => void | Promise<void>;
[key: string]: any; [key: string]: any;
} }
@@ -19,10 +22,18 @@
type = 'text', type = 'text',
error = '', error = '',
uppercase = false, uppercase = false,
onsubmit,
...rest ...rest
}: Props = $props(); }: Props = $props();
let isFocussed = $state(false); let isFocussed = $state(false);
const handleSubmit = (e: Event) => {
e.preventDefault();
if (onsubmit && value) {
onsubmit(value);
}
}
</script> </script>
<div class="input-wrapper" class:focussed={isFocussed} class:has-error={!!error}> <div class="input-wrapper" class:focussed={isFocussed} class:has-error={!!error}>
@@ -30,6 +41,7 @@
<label for="adaptive-input">{label}</label> <label for="adaptive-input">{label}</label>
{/if} {/if}
<form onsubmit={handleSubmit} class="input-container">
<input <input
id="adaptive-input" id="adaptive-input"
class:all-caps={uppercase} class:all-caps={uppercase}
@@ -41,6 +53,19 @@
{...rest} {...rest}
/> />
{#if onsubmit}
<button
type="submit"
class="submit-icon"
transition:fade
disabled={!value}
aria-label="Submit"
>
<IconChevronRightFilled />
</button>
{/if}
</form>
{#if error} {#if error}
<span class="error-message" transition:fade>{error}</span> <span class="error-message" transition:fade>{error}</span>
{/if} {/if}
@@ -50,11 +75,22 @@
.input-wrapper { .input-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 0.4rem;
width: 100%; width: 100%;
font-family: 'URW Gothic', sans-serif; font-family: 'URW Gothic', sans-serif;
} }
.input-container {
position: relative;
display: flex;
align-items: center;
width: 100%;
background-color: var(--color-title);
border-radius: 5000px;
transition: all 0.2s;
overflow: hidden;
}
label { label {
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 400; font-weight: 400;
@@ -62,17 +98,44 @@
} }
input { input {
width: 100%;
background: transparent;
min-height: 40px; min-height: 40px;
padding: 0 16px; height: 100%;
background-color: var(--color-title); line-height: normal;
border: 2px solid transparent; padding: 0 48px;
border-radius: 20px; border: none;
color: var(--color-bg-dark); color: var(--color-bg-dark);
font-size: 1.2rem; font-size: 1.2rem;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
outline: none; outline: none;
text-align: center; text-align: center;
box-shadow: var(--shadow-std); }
.submit-icon {
position: absolute;
background: transparent;
right: 0;
border: none;
color: var(--color-bg-dark);
cursor: pointer;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.75;
transition: opacity 0.2s, transform 0.2s;
}
.submit-icon:hover:not(:disabled) {
opacity: 1;
transform: translateX(2px);
}
.submit-icon:disabled {
opacity: 0.5;
cursor: not-allowed;
} }
.all-caps { .all-caps {

View File

@@ -93,7 +93,7 @@
}; };
</script> </script>
<div class="train-service"> <div class="train-service" class:isExpanded={isExpanded}>
<button class="summary" onclick={toggleExpand} type="button" aria-expanded={isExpanded}> <button class="summary" onclick={toggleExpand} type="button" aria-expanded={isExpanded}>
{#if loadingDetails} {#if loadingDetails}
<div class="loading-state"><div class="loading-spinner"></div></div> <div class="loading-state"><div class="loading-spinner"></div></div>
@@ -241,10 +241,18 @@
filter: brightness(1.2); filter: brightness(1.2);
} }
.train-service:active .arrow {
filter: brightness(0.2);
}
@media (hover: hover) { @media (hover: hover) {
.train-service:hover { .train-service:not(.isExpanded):hover {
filter: brightness(1.2); filter: brightness(1.2);
} }
.summary:hover .arrow {
filter: brightness(0.2);
}
} }
/* /*
@@ -299,7 +307,7 @@
padding: 0; padding: 0;
margin: 0 0 0 auto; margin: 0 0 0 auto;
height: 25px; height: 25px;
transition: all 0.9s; transition: all 0.3s;
} }
.expanded { .expanded {

View File

@@ -6,8 +6,7 @@
let headcode = $state(''); let headcode = $state('');
function handleSearch(e: SubmitEvent) { function handleSearch(headcode: string) {
e.preventDefault();
if (!headcode.trim()) return; if (!headcode.trim()) return;
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
@@ -18,10 +17,9 @@
</script> </script>
<BaseCard header={'Search Train & PIS'}> <BaseCard header={'Search Train & PIS'}>
<form onsubmit={handleSearch} class="card-content"> <div class="card-content">
<Textbox placeholder="Enter Headcode" bind:value={headcode} maxLength={4} /> <Textbox placeholder="Enter Headcode" bind:value={headcode} maxLength={4} onsubmit={handleSearch} />
<Button type="submit" disabled={!headcode}>Search</Button> </div>
</form>
</BaseCard> </BaseCard>
<style> <style>
@@ -29,7 +27,7 @@
text-align: center; text-align: center;
width: 90%; width: 90%;
margin: auto; margin: auto;
padding: 10px 0 0 0; padding: 10px 0 0.5rem 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;