adds icon picker
This commit is contained in:
93
web/src/lib/components/base/combobox.svelte
Normal file
93
web/src/lib/components/base/combobox.svelte
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<script context="module" lang="ts">
|
||||||
|
export type ComboboxItem = {
|
||||||
|
text: string;
|
||||||
|
value: any;
|
||||||
|
icon: string;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher, tick } from "svelte";
|
||||||
|
import TextField from "./text_field.svelte";
|
||||||
|
|
||||||
|
export let name: string = "";
|
||||||
|
export let icon: string = "";
|
||||||
|
export let label: string = "";
|
||||||
|
export let value: string = "";
|
||||||
|
export let items: ComboboxItem[] = [];
|
||||||
|
export let placeholder: string = "";
|
||||||
|
export let extraClasses: string = "";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let searching: boolean = false;
|
||||||
|
|
||||||
|
$: dropDownOpen = value.length > 0 && items.length > 0 && searching;
|
||||||
|
|
||||||
|
async function onSearchType() {
|
||||||
|
await tick();
|
||||||
|
const dropdownMenu = document.querySelector(".menu");
|
||||||
|
if (dropdownMenu) {
|
||||||
|
let dropdownHTML = dropdownMenu.innerHTML;
|
||||||
|
|
||||||
|
for (const li of dropdownMenu.children) {
|
||||||
|
const textNode = li.getElementsByTagName("p")[0];
|
||||||
|
const text = textNode.innerText.replace(
|
||||||
|
new RegExp(value, "gi"),
|
||||||
|
(match) => `<strong>${match}</strong>`,
|
||||||
|
);
|
||||||
|
textNode.innerHTML = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(q: string) {
|
||||||
|
dispatch("update", q);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleItemClick(item: ComboboxItem) {
|
||||||
|
value = item.value;
|
||||||
|
dispatch("click", item);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative {extraClasses}">
|
||||||
|
<TextField
|
||||||
|
type="search"
|
||||||
|
{name}
|
||||||
|
autocomplete="off"
|
||||||
|
{icon}
|
||||||
|
{label}
|
||||||
|
{placeholder}
|
||||||
|
bind:value
|
||||||
|
on:change
|
||||||
|
on:input={onSearchType}
|
||||||
|
on:focusin={() => (searching = true)}
|
||||||
|
on:focusout={() => (searching = false)}
|
||||||
|
></TextField>
|
||||||
|
|
||||||
|
{#if dropDownOpen}
|
||||||
|
<ul
|
||||||
|
class="menu absolute bg-menu-background border border-input-border rounded-xl shadow-md w-full max-h-64 overflow-x-hidden overflow-y-scroll"
|
||||||
|
class:none={!dropDownOpen}
|
||||||
|
style="z-index: 1001"
|
||||||
|
>
|
||||||
|
{#each items as item}
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
|
<li
|
||||||
|
class="menu-item flex items-center px-4 py-3 cursor-pointer hover:bg-menu-item-background-hover focus:bg-menu-item-background-focus transition-colors"
|
||||||
|
tabindex="0"
|
||||||
|
on:mousedown|stopPropagation={() => handleItemClick(item)}
|
||||||
|
on:keydown|stopPropagation={() => handleItemClick(item)}
|
||||||
|
>
|
||||||
|
<i class="fa fa-{item.icon} mr-6"></i>
|
||||||
|
|
||||||
|
<p class="text-ellipsis">{item.text}</p>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -72,9 +72,9 @@
|
|||||||
type="search"
|
type="search"
|
||||||
name="q"
|
name="q"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
extraClasses="{large
|
extraClasses={large
|
||||||
? 'px-12 py-4 text-xl min-w-80 w-[33vw] max-w-[532px] rounded-xl'
|
? "px-12 py-4 text-xl min-w-80 w-[33vw] max-w-[532px] rounded-xl"
|
||||||
: 'px-10'}"
|
: "px-10"}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
bind:value
|
bind:value
|
||||||
on:input={onSearchType}
|
on:input={onSearchType}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
import { waypoint } from "$lib/stores/waypoint_store";
|
import { waypoint } from "$lib/stores/waypoint_store";
|
||||||
|
import { icons } from "$lib/util/icon_util";
|
||||||
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
import { createForm } from "$lib/vendor/svelte-form-lib/index";
|
||||||
import { util } from "$lib/vendor/svelte-form-lib/util";
|
import { util } from "$lib/vendor/svelte-form-lib/util";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
|
import Combobox from "../base/combobox.svelte";
|
||||||
import Modal from "../base/modal.svelte";
|
import Modal from "../base/modal.svelte";
|
||||||
import TextField from "../base/text_field.svelte";
|
import TextField from "../base/text_field.svelte";
|
||||||
import Textarea from "../base/textarea.svelte";
|
import Textarea from "../base/textarea.svelte";
|
||||||
@@ -23,6 +25,13 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
$: form.set(util.cloneDeep($waypoint));
|
$: form.set(util.cloneDeep($waypoint));
|
||||||
|
|
||||||
|
$: filteredIcons =
|
||||||
|
($form.icon?.length ?? 0) > 2
|
||||||
|
? icons
|
||||||
|
.filter((i) => i.includes($form.icon ?? ""))
|
||||||
|
.map((i) => ({ text: i, value: i, icon: i }))
|
||||||
|
: [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
@@ -40,7 +49,7 @@
|
|||||||
on:submit={handleSubmit}
|
on:submit={handleSubmit}
|
||||||
>
|
>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div class="basis-full">
|
<div class="basis-2/3">
|
||||||
<TextField
|
<TextField
|
||||||
name="name"
|
name="name"
|
||||||
label={$_("name")}
|
label={$_("name")}
|
||||||
@@ -50,14 +59,14 @@
|
|||||||
></TextField>
|
></TextField>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TextField
|
<Combobox
|
||||||
name="icon"
|
name="icon"
|
||||||
label={$_("icon")}
|
|
||||||
bind:value={$form.icon}
|
bind:value={$form.icon}
|
||||||
icon={$form.icon}
|
icon={$form.icon}
|
||||||
error={$errors.icon}
|
items={filteredIcons}
|
||||||
|
label={$_("icon")}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
></TextField>
|
></Combobox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
1391
web/src/lib/util/icon_util.ts
Normal file
1391
web/src/lib/util/icon_util.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user