feat(badges): smooth transitions + polish for badge search UI
- Adds fade/slide transitions throughout the search form: form mount/unmount, filter row, QR scan button, QR scanner panel, Show Hidden, Remote First labels - Min-chars hint switches from class:invisible to opacity-0/opacity-50 + transition-opacity so it fades instead of snapping - Clear button switches from class:hidden to opacity-0 + pointer-events-none + transition-all so it fades without causing layout shifts - "Start Here" button gets transition-opacity for smooth dim on first keystroke - Replaces FileSearch with UserSearch icon in the empty state - Adds w-full to empty state div to prevent subtle page-width shift between no-results and results states Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,14 +27,14 @@ import {
|
||||
Check,
|
||||
Eye,
|
||||
EyeOff,
|
||||
FileSearch,
|
||||
Link,
|
||||
LoaderCircle,
|
||||
Mail,
|
||||
MapPin,
|
||||
Printer,
|
||||
Tags,
|
||||
User
|
||||
User,
|
||||
UserSearch
|
||||
} from '@lucide/svelte';
|
||||
// Track per-badge copy state for the "Review Link" clipboard button
|
||||
let copy_status: Record<string, 'idle' | 'copied'> = $state({});
|
||||
@@ -408,8 +408,8 @@ let visible_badge_obj_li = $derived(
|
||||
</ul>
|
||||
{:else}
|
||||
<div
|
||||
class="flex flex-col items-center justify-center p-20 text-center opacity-50">
|
||||
<FileSearch size="3em" class="mx-auto mb-2 opacity-20" />
|
||||
class="flex w-full flex-col items-center justify-center p-20 text-center opacity-50">
|
||||
<UserSearch size="3em" class="mx-auto mb-2 opacity-20" />
|
||||
{#if !is_trusted && !(badges_loc.current.fulltext_search_qry_str ?? '').trim()}
|
||||
<p>Enter your name above to find your badge.</p>
|
||||
{:else}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
StepForward
|
||||
|
||||
} from '@lucide/svelte';
|
||||
import { fade, slide } from 'svelte/transition';
|
||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||
import { events_sess } from '$lib/stores/ae_events_stores';
|
||||
import { badges_loc } from '$lib/stores/ae_events_stores__badges.svelte';
|
||||
@@ -87,6 +88,7 @@ function handle_qr_scan_result(event: {
|
||||
class="ae_group filters_and_search flex w-full flex-col items-center justify-center gap-2">
|
||||
{#if $events_sess.badges.show_form__search}
|
||||
<form
|
||||
transition:fade={{ duration: 200 }}
|
||||
onsubmit={prevent_default(() => {
|
||||
handle_search_trigger();
|
||||
})}
|
||||
@@ -95,7 +97,7 @@ function handle_qr_scan_result(event: {
|
||||
<div
|
||||
class="flex grow flex-col xl:flex-row flex-wrap items-center justify-center gap-2">
|
||||
{#if ($ae_loc.trusted_access && $ae_loc.edit_mode) || $ae_loc.manager_access}
|
||||
<div class="flex flex-row flex-wrap items-center justify-center gap-1">
|
||||
<div transition:slide={{ duration: 200 }} class="flex flex-row flex-wrap items-center justify-center gap-1">
|
||||
<span class="flex flex-row flex-wrap items-center justify-center gap-1">
|
||||
<select
|
||||
bind:value={badges_loc.current.search_badge_type_code}
|
||||
@@ -159,6 +161,7 @@ function handle_qr_scan_result(event: {
|
||||
|
||||
{#if (badges_loc.current.enable_search_qr && $ae_loc.edit_mode)}
|
||||
<button
|
||||
transition:fade={{ duration: 150 }}
|
||||
type="button"
|
||||
onclick={() => {
|
||||
$events_sess.badges.show_form__search = false;
|
||||
@@ -177,7 +180,7 @@ function handle_qr_scan_result(event: {
|
||||
onclick={() => document.getElementById('badge_fulltext_search_qry_str')?.focus()}
|
||||
aria-label="Start here — focus search field"
|
||||
data-testid="badge-start-btn"
|
||||
class="btn btn-sm preset-filled-secondary-200-800 font-semibold"
|
||||
class="btn btn-sm preset-filled-secondary-200-800 font-semibold transition-opacity duration-300"
|
||||
class:opacity-30={!!badges_loc.current.fulltext_search_qry_str}
|
||||
class:hidden={$ae_loc.trusted_access}
|
||||
>
|
||||
@@ -218,11 +221,11 @@ function handle_qr_scan_result(event: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if (badges_loc.current.fulltext_search_qry_str ?? '').trim().length < effective_min_chars}
|
||||
<p class="w-full text-center text-xs opacity-50">
|
||||
Enter at least {effective_min_chars} character{effective_min_chars === 1 ? '' : 's'} to search
|
||||
</p>
|
||||
{/if}
|
||||
<p class="w-full text-center text-xs transition-opacity duration-200"
|
||||
class:opacity-0={(badges_loc.current.fulltext_search_qry_str ?? '').trim().length >= effective_min_chars}
|
||||
class:opacity-50={(badges_loc.current.fulltext_search_qry_str ?? '').trim().length < effective_min_chars}>
|
||||
Enter at least {effective_min_chars} character{effective_min_chars === 1 ? '' : 's'} to search
|
||||
</p>
|
||||
|
||||
<div class="flex flex-row items-center justify-center gap-1">
|
||||
<button
|
||||
@@ -241,7 +244,10 @@ function handle_qr_scan_result(event: {
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class:hidden={!badges_loc.current.fulltext_search_qry_str &&
|
||||
class:opacity-0={!badges_loc.current.fulltext_search_qry_str &&
|
||||
!badges_loc.current.search_badge_type_code &&
|
||||
badges_loc.current.qry_printed_status === 'all'}
|
||||
class:pointer-events-none={!badges_loc.current.fulltext_search_qry_str &&
|
||||
!badges_loc.current.search_badge_type_code &&
|
||||
badges_loc.current.qry_printed_status === 'all'}
|
||||
onclick={() => {
|
||||
@@ -254,7 +260,7 @@ function handle_qr_scan_result(event: {
|
||||
document.getElementById('badge_fulltext_search_qry_str')?.focus();
|
||||
}}
|
||||
class="
|
||||
hover:text-tertiary-800-200 hover:bg-tertiary-200-800 active:bg-surface-200-700 flex items-center justify-center gap-1 px-3 py-2 text-sm font-bold transition-all duration-1000 hover:duration-300 min-w-0 preset-outlined-tertiary rounded-lg border border-tertiary-200-800
|
||||
hover:text-tertiary-800-200 hover:bg-tertiary-200-800 active:bg-surface-200-700 flex items-center justify-center gap-1 px-3 py-2 text-sm font-bold transition-all duration-200 hover:duration-100 min-w-0 preset-outlined-tertiary rounded-lg border border-tertiary-200-800
|
||||
|
||||
"
|
||||
title="Clear search query">
|
||||
@@ -264,6 +270,7 @@ function handle_qr_scan_result(event: {
|
||||
|
||||
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
|
||||
<label
|
||||
transition:fade={{ duration: 150 }}
|
||||
class="bg-surface-200-800 rounded-token flex cursor-pointer items-center gap-1 px-2 py-1 text-xs font-semibold">
|
||||
<span> Show Hidden </span>
|
||||
<input
|
||||
@@ -275,6 +282,7 @@ function handle_qr_scan_result(event: {
|
||||
{/if}
|
||||
{#if $ae_loc.edit_mode}
|
||||
<label
|
||||
transition:fade={{ duration: 150 }}
|
||||
class="bg-surface-200-800 rounded-token flex cursor-pointer items-center gap-1 px-2 py-1 text-xs font-semibold">
|
||||
<span> Remote First </span>
|
||||
<input
|
||||
@@ -288,6 +296,7 @@ function handle_qr_scan_result(event: {
|
||||
</form>
|
||||
{:else if $events_sess.badges.show_form__scan}
|
||||
<div
|
||||
transition:fade={{ duration: 200 }}
|
||||
class="bg-surface-100-900 mx-auto w-full max-w-2xl rounded-lg p-4 shadow-lg">
|
||||
<Element_qr_scanner
|
||||
bind:start_qr_scanner={$events_sess.badges.qr_scan_start}
|
||||
|
||||
Reference in New Issue
Block a user