Dodanie raportów, dnd, listy gości i innych.
This commit is contained in:
157
src/lib/components/GuestField.svelte
Normal file
157
src/lib/components/GuestField.svelte
Normal file
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import type { Guest } from "$lib/types";
|
||||
import * as db from "$lib/db";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
/** Wybranie gościa z podpowiedzi — rodzic uzupełnia pozostałe pola. */
|
||||
onselect: (guest: Guest) => void;
|
||||
}
|
||||
|
||||
let { value = $bindable(), onselect }: Props = $props();
|
||||
|
||||
let suggestions = $state<Guest[]>([]);
|
||||
let open = $state(false);
|
||||
let highlighted = $state(0);
|
||||
let searchToken = 0;
|
||||
|
||||
async function oninput() {
|
||||
const q = value.trim();
|
||||
if (q.length < 2) {
|
||||
suggestions = [];
|
||||
open = false;
|
||||
return;
|
||||
}
|
||||
const token = ++searchToken;
|
||||
const found = await db.searchGuests(q);
|
||||
if (token !== searchToken) return;
|
||||
suggestions = found;
|
||||
highlighted = 0;
|
||||
open = found.length > 0;
|
||||
}
|
||||
|
||||
function choose(guest: Guest) {
|
||||
value = guest.name;
|
||||
open = false;
|
||||
onselect(guest);
|
||||
}
|
||||
|
||||
function onkeydown(event: KeyboardEvent) {
|
||||
if (!open) return;
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
highlighted = (highlighted + 1) % suggestions.length;
|
||||
} else if (event.key === "ArrowUp") {
|
||||
event.preventDefault();
|
||||
highlighted = (highlighted - 1 + suggestions.length) % suggestions.length;
|
||||
} else if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
choose(suggestions[highlighted]);
|
||||
} else if (event.key === "Escape") {
|
||||
// zamknij tylko podpowiedzi, nie cały dialog
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onblur() {
|
||||
open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="wrap">
|
||||
<input
|
||||
type="text"
|
||||
bind:value
|
||||
{oninput}
|
||||
{onkeydown}
|
||||
{onblur}
|
||||
required
|
||||
placeholder="Jan Kowalski"
|
||||
autocomplete="off"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
aria-autocomplete="list"
|
||||
aria-controls="guest-suggestions"
|
||||
/>
|
||||
{#if open}
|
||||
<ul class="suggestions" id="guest-suggestions" role="listbox">
|
||||
{#each suggestions as guest, i (guest.id)}
|
||||
<li role="option" aria-selected={i === highlighted}>
|
||||
<button
|
||||
type="button"
|
||||
class:hl={i === highlighted}
|
||||
onclick={() => choose(guest)}
|
||||
onpointerdown={(e) => e.preventDefault()}
|
||||
>
|
||||
<span class="n">{guest.name}</span>
|
||||
{#if guest.phone}<span class="d">{guest.phone}</span>{/if}
|
||||
{#if guest.address}<span class="d">{guest.address}</span>{/if}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrap {
|
||||
position: relative;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
font: inherit;
|
||||
font-size: 14px;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
.suggestions {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
list-style: none;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 14px rgb(0 0 0 / 0.15);
|
||||
max-height: 220px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.suggestions button {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
width: 100%;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
padding: 6px 8px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
.suggestions button:hover,
|
||||
.suggestions button.hl {
|
||||
background: var(--today);
|
||||
}
|
||||
.n {
|
||||
font-weight: 600;
|
||||
}
|
||||
.d {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user