91 lines
2.0 KiB
Svelte
91 lines
2.0 KiB
Svelte
<script lang="ts">
|
|
import { clickoutside } from '@svelte-put/clickoutside'
|
|
import { onMount, tick } from 'svelte'
|
|
|
|
type Item = { text: string }
|
|
export let value: Item | null = null
|
|
export let items: Item[]
|
|
let filteredItems: Item[] = []
|
|
|
|
let inputFilter: HTMLInputElement | null = null
|
|
let filter = ''
|
|
|
|
let divContainer: HTMLDivElement | null = null
|
|
|
|
let areOptionsVisible = false
|
|
|
|
$: {
|
|
if (filter) {
|
|
filteredItems = items.filter((item) => item.text.toLowerCase().includes(filter.toLowerCase()))
|
|
} else {
|
|
filteredItems = [...items]
|
|
}
|
|
}
|
|
|
|
$: {
|
|
// Watch filter value
|
|
if (filter) {
|
|
tick().then(() => {
|
|
if (divContainer) {
|
|
if (divContainer.scrollHeight > divContainer.clientHeight) {
|
|
divContainer.classList.add('h-96')
|
|
} else {
|
|
divContainer.classList.remove('h-96')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function showOptions() {
|
|
// Add a delay so that the click outside event doesn't trigger too soon
|
|
setTimeout(() => {
|
|
areOptionsVisible = true
|
|
tick().then(() => inputFilter?.select())
|
|
}, 0)
|
|
setTimeout(() => {}, 100)
|
|
}
|
|
|
|
function hideOptions() {
|
|
areOptionsVisible = false
|
|
}
|
|
|
|
function selectItem(item: Item) {
|
|
value = item
|
|
hideOptions()
|
|
}
|
|
</script>
|
|
|
|
<div class="relative" use:clickoutside on:clickoutside={hideOptions}>
|
|
{#if areOptionsVisible}
|
|
<input
|
|
type="text"
|
|
bind:value={filter}
|
|
bind:this={inputFilter}
|
|
class={$$restProps.class ?? ''}
|
|
/>
|
|
<div
|
|
class="absolute top-full left-0 w-full bg-gray-700 max-h-[90vh] overflow-y-auto"
|
|
bind:this={divContainer}
|
|
>
|
|
{#each filteredItems as item}
|
|
<div class="p-1 hover:bg-gray-600" on:click={() => selectItem(item)}>{item.text}</div>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<input
|
|
type="text"
|
|
readonly
|
|
value={value?.text ?? ''}
|
|
class={$$restProps.class ?? ''}
|
|
on:click={showOptions}
|
|
/>
|
|
{/if}
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
select {
|
|
appearance: none;
|
|
}
|
|
</style>
|