Files
phishingclub/frontend/src/lib/components/TextFieldSearchSelect.svelte
T
2025-08-21 16:14:09 +02:00

133 lines
3.3 KiB
Svelte

<script>
import { afterUpdate, onDestroy, onMount } from 'svelte';
import ToolTip from './ToolTip.svelte';
export let id;
// bind a element to this component input field
// use like <TextField bind:bindTo={varYouWantToBindTheInputFieldTo} />
export let bindTo = null;
export let value = ''; // for binding value
export let required = false;
export let options = [];
export let toolTipText = '';
export let placeholder = '';
// (string) => void
export let onKeyUp;
// () => string
export let onSelect;
let showSelection = false;
const closeSelection = () => {
showSelection = false;
// stop listening for a click
document.removeEventListener('click', closeSelection);
};
const onFocus = () => {
document.addEventListener('click', closeSelection);
};
const _onKeyUp = () => {
try {
if (value.length == 0) {
return;
}
onKeyUp(value);
showSelection = true;
} catch (err) {
console.error('failed to search', err);
}
};
// bind to parent form element, if there is one
let parentForm = null;
// listen to parent form reset event, if one exists
let parentFormResetListener = null;
onMount(() => {});
afterUpdate(() => {
if (!parentForm) {
parentForm = bindTo.closest('form');
if (!parentForm) {
return;
}
parentFormResetListener = parentForm.addEventListener('reset', (event) => {
event.preventDefault();
value = '';
});
}
});
onDestroy(() => {
if (parentFormResetListener) {
parentForm.removeEventListener('reset', parentFormResetListener);
}
});
</script>
<div class="flex flex-col justify-start">
<label class="flex flex-col py-2 relative">
<div class="flex items-center">
<p class="font-semibold text-slate-600 py-2">
<slot />
</p>
{#if toolTipText.length > 0}
<ToolTip>
{toolTipText}
</ToolTip>
{/if}
</div>
</label>
<div class="relative">
<div class="flex items-center relative w-60">
<input
type="text"
{placeholder}
bind:this={bindTo}
bind:value
on:focus={onFocus}
on:blur={() => {
if (!options.includes(value)) {
value = '';
}
}}
on:keyup={_onKeyUp}
on:click|stopPropagation={() => {}}
autocomplete="off"
class="w-full relative rounded-md py-2 pl-4 focus:pl-10 text-gray-600 border border-transparent focus:outline-none focus:border-solid focus:border focus:border-slate-400 focus:bg-gray-100 bg-grayblue-light font-normal cursor-pointer focus:cursor-text"
{id}
{required}
/>
{#if showSelection}
<img class="absolute w-4 left-4" src="/search-icon.svg" alt="search" />
{/if}
<img class="absolute pointer-events-none w-4 right-4" src="/arrow.svg" alt="drop down" />
</div>
{#if options.length && showSelection}
<div class="w-96 absolute top-10 z-50">
<ul
class="bg-gray-100 list-none mt-4 rounded-md min-w-fit shadow-md border max-h-40 overflow-y-scroll"
>
{#each options as option}
<li class="break-words">
<button
class="w-full text-left bg-slate-100 rounded-md text-gray-600 hover:bg-grayblue-dark hover:text-white py-2 px-2 cursor-pointer"
on:click|preventDefault={() => {
value = '';
showSelection = false;
onSelect(option);
}}
>
{option}
</button>
</li>
{/each}
</ul>
</div>
{/if}
</div>
</div>