flotilla/src/app/components/IconPicker.svelte
2026-08-27 17:20:46 -07:00

58 lines
1.7 KiB
Svelte

<script lang="ts">
import {createSearch} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
const iconModules = import.meta.glob<{default: string}>("@assets/icons/*.svg", {
query: "?dataurl",
eager: true,
})
const icons = Object.entries(iconModules)
.map(([path, module]) => {
const name = path.split("/").pop()?.replace(".svg", "") || ""
return {
name,
url: module.default,
searchText: name.replace(/[-_]/g, " ").toLowerCase(),
}
})
.filter(icon => icon.name && !icon.name.startsWith("icon-") && icon.name !== "index")
.sort((a, b) => a.name.localeCompare(b.name))
const iconSearch = createSearch(icons, {
getValue: icon => icon.name,
fuseOptions: {
keys: ["name", "searchText"],
threshold: 0.4,
},
})
type Props = {
onSelect: (iconUrl: string) => void
}
const {onSelect}: Props = $props()
let searchTerm = $state("")
const filteredIcons = $derived(searchTerm ? iconSearch.searchOptions(searchTerm) : icons)
</script>
<label class="input flex w-full items-center gap-2">
<Icon icon={Magnifier} />
<input bind:value={searchTerm} class="grow" type="text" placeholder="Search icons..." />
</label>
<div class="mt-2 max-h-80 overflow-y-auto">
<div class="grid grid-cols-8 gap-2 p-2">
{#each filteredIcons as icon (icon.name)}
<button
type="button"
title={icon.name}
class="flex aspect-square items-center justify-center rounded-2xl transition-colors hover:bg-primary hover:text-primary-content"
onclick={() => onSelect(icon.url)}>
<Icon icon={icon.url} class="h-6 w-6" />
</button>
{/each}
</div>
</div>