Compare commits

...

3 Commits

Author SHA1 Message Date
f3d633e719 Fix PIS Display 2026-05-05 20:41:27 +01:00
05a04ec922 Add PIS Data formatting to TrainService expander 2026-05-05 19:24:31 +01:00
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 148 additions and 43 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,16 +41,30 @@
<label for="adaptive-input">{label}</label> <label for="adaptive-input">{label}</label>
{/if} {/if}
<input <form onsubmit={handleSubmit} class="input-container">
id="adaptive-input" <input
class:all-caps={uppercase} id="adaptive-input"
{type} class:all-caps={uppercase}
{placeholder} {type}
bind:value {placeholder}
onfocus={() => (isFocussed = true)} bind:value
onblur={() => (isFocussed = false)} onfocus={() => (isFocussed = true)}
{...rest} onblur={() => (isFocussed = false)}
/> {...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>
@@ -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,46 @@
} }
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>
<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>
@@ -147,7 +147,15 @@
{/if} {/if}
{#if details.pis} {#if details.pis}
<div class="pis-detail"> <div class="pis-detail">
{details.pis.code} <span class="pis-code">PIS: {details.pis.code}</span>
{#if details.pis.skip?.skip > 0}
<span class="pis-skip">
(skip {details.pis.skip.position === 'head' ? 'first' : 'last'}
{details.pis.skip.skip}
{details.pis.skip.skip === 1 ? 'stop' : 'stops'}
)
</span>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
@@ -173,8 +181,7 @@
</tr> </tr>
</thead> </thead>
{#each details.locations as loc} {#each details.locations as loc}
<tbody class="location-group"> <tbody class="location-group">
<tr class:pass-loc={loc.r === 'PASS'} class:can-loc={loc.can}> <tr class:pass-loc={loc.r === 'PASS'} class:can-loc={loc.can}>
<td class="tpl-cell" class:tpl-stop={loc.r != 'PASS'}> <td class="tpl-cell" class:tpl-stop={loc.r != 'PASS'}>
{loc.t} {loc.t}
@@ -202,18 +209,17 @@
{/if} {/if}
</tr> </tr>
{#if loc.act && getRelevantActivities(loc.act).length > 0} {#if loc.act && getRelevantActivities(loc.act).length > 0}
<tr class="activity-row"> <tr class="activity-row">
<td colspan="7"> <td colspan="7">
<div class="activity-container"> <div class="activity-container">
{#each getRelevantActivities(loc.act) as note} {#each getRelevantActivities(loc.act) as note}
<span class="activity-tag">{note}</span> <span class="activity-tag">{note}</span>
{/each} {/each}
</div> </div>
</td> </td>
</tr> </tr>
{/if} {/if}
</tbody>{/each}
</tbody>{/each}
</table> </table>
</div> </div>
</div> </div>
@@ -241,10 +247,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 +313,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 {
@@ -310,6 +324,29 @@
color: red; color: red;
} }
/*
PIS Data
*/
.pis-detail {
display: flex;
align-items: center;
flex-direction: column;
gap: 0.2rem;
padding: 8px 0;
width: 100%;
}
.pis-code {
font-weight: 600;
font-size: 1.1rem;
letter-spacing: 0.05em;
}
.pis-skip {
font-size: 0.85rem;
opacity: 0.75;
font-style: oblique;
}
/* /*
Box Extention Box Extention
*/ */
@@ -408,9 +445,9 @@
.activity-tag { .activity-tag {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05);
padding: 2px 6px; padding: 2px 6px;
border-radius: 4px; border-radius: 4px;
} }
.tpl-cell { .tpl-cell {

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,14 @@
</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
<Button type="submit" disabled={!headcode}>Search</Button> placeholder="Enter Headcode"
</form> bind:value={headcode}
maxLength={4}
onsubmit={handleSearch}
/>
</div>
</BaseCard> </BaseCard>
<style> <style>
@@ -29,7 +32,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;