From 0005595703b2f3f7083ce4ba19bf5770057c75bd Mon Sep 17 00:00:00 2001
From: ivarlovlie <git@ivarlovlie.no>
Date: Tue, 25 Oct 2022 17:51:37 +0800
Subject: .

---
 code/app/src/lib/components/combobox.svelte | 57 ++++++++++++++++-------------
 1 file changed, 32 insertions(+), 25 deletions(-)

(limited to 'code/app/src/lib/components/combobox.svelte')

diff --git a/code/app/src/lib/components/combobox.svelte b/code/app/src/lib/components/combobox.svelte
index ee69917..4e7b1cd 100644
--- a/code/app/src/lib/components/combobox.svelte
+++ b/code/app/src/lib/components/combobox.svelte
@@ -7,7 +7,7 @@
 </script>
 
 <script lang="ts">
-    import { CheckCircleIcon, ChevronUpDownIcon, XIcon } from "$lib/components/icons";
+    import { CheckCircleIcon, ChevronUpDownIcon, XIcon } from "./icons";
     import { element_has_focus, random_string } from "$lib/helpers";
     import { go, highlight } from "fuzzysort";
     import Badge from "./badge.svelte";
@@ -20,19 +20,19 @@
     export let disabled: boolean | undefined = undefined;
     export let required: boolean | undefined = undefined;
     export let maxlength: number | undefined = undefined;
-    export let placeholder = $LL.combobox.search();
+    export let placeholder: string = $LL.combobox.search();
     export let options: Array<ComboboxOption> | undefined = [];
     export let createable = false;
     export let loading = false;
     export let multiple = false;
-    export let noResultsText = $LL.combobox.noRecordsFound();
+    export let noResultsText: string = $LL.combobox.noRecordsFound();
     export let on_create_async = async ({ name: string }) => {};
 
     export const reset = () => methods.reset();
     export const select = (id: string) => methods.select_entry(id);
     export const deselect = (id: string) => methods.deselect_entry(id);
 
-    const INTERNAL_ID = "INTERNAL__combobox-" + random_string(3);
+    const INTERNAL_ID = "INTERNAL__" + id;
 
     let optionsListId = id + "--options";
     let searchInputNode;
@@ -75,7 +75,7 @@
             if (!value) {
                 return "";
             }
-            return value.toString().trim().toLowerCase();
+            return value.trim().toLowerCase();
         },
         do() {
             const query = search.normalise_value(searchValue);
@@ -85,9 +85,9 @@
                 return;
             }
 
-            //@ts-ignore
+            // @ts-ignore
             searchResults = go(query, options, {
-                limit: 10,
+                limit: 15,
                 allowTypo: true,
                 threshold: -10000,
                 key: "name",
@@ -206,11 +206,11 @@
             if (searchValue) {
                 return options;
             }
-            return (options as any).sort((a, b) => {
-                search.normalise_value(a.name).localeCompare(search.normalise_value(b.name));
-            });
+
+            return options.sort((a, b) => search.normalise_value(a.name).localeCompare(search.normalise_value(b.name)));
         },
     };
+
     const windowEvents = {
         on_mousemove(event: any) {
             if (!event.target) return;
@@ -230,7 +230,7 @@
             const spacePressed = event.code === "Space";
             const arrowDownPressed = event.code === "ArrowDown";
             const searchInputHasFocus = element_has_focus(searchInputNode);
-            const focusedEntry = document.querySelector("#" + INTERNAL_ID + " ul .focus");
+            const focusedEntry = document.querySelector("#" + INTERNAL_ID + " ul li.focus") as HTMLLIElement;
 
             if (showDropdown && (enterPressed || arrowDownPressed || arrowUpPressed)) {
                 event.preventDefault();
@@ -262,16 +262,18 @@
                         focusedEntry.nextElementSibling.classList.add("focus");
                         focusedEntry.nextElementSibling.scrollIntoView(false);
                     } else {
-                        document.querySelector("#" + INTERNAL_ID + " ul li:first-of-type").classList.add("focus");
-                        document.querySelector("#" + INTERNAL_ID + " ul li:first-of-type").scrollIntoView(false);
+                        const firstLIEl = document.querySelector("#" + INTERNAL_ID + " ul li:first-of-type");
+                        firstLIEl.classList.add("focus");
+                        firstLIEl.scrollIntoView(false);
                     }
                 } else if (arrowUpPressed) {
                     if (focusedEntry.previousElementSibling) {
                         focusedEntry.previousElementSibling.classList.add("focus");
                         focusedEntry.previousElementSibling.scrollIntoView(false);
                     } else {
-                        document.querySelector("#" + INTERNAL_ID + " ul li:last-of-type").classList.add("focus");
-                        document.querySelector("#" + INTERNAL_ID + " ul li:last-of-type").scrollIntoView(false);
+                        const lastLIEl = document.querySelector("#" + INTERNAL_ID + " ul li:last-of-type");
+                        lastLIEl.classList.add("focus");
+                        lastLIEl.scrollIntoView(false);
                     }
                 }
                 focusedEntry.classList.remove("focus");
@@ -279,7 +281,6 @@
             }
 
             if (focusedEntry && (spacePressed || enterPressed)) {
-                //@ts-ignore
                 methods.select_entry(focusedEntry.dataset.id);
                 return;
             }
@@ -303,14 +304,17 @@
 
 <div id={INTERNAL_ID} class:cursor-wait={loading}>
     {#if label}
-        <label for={id} class="block text-sm font-medium text-gray-700">{label}</label>
+        <label for={id} class="block text-sm font-medium text-gray-700">
+            {label}
+            {@html required ? "<span class='text-red-500'>*</span>" : ""}
+        </label>
     {/if}
     <div class="relative {label ? 'mt-1' : ''}">
         <div
             on:click={search.on_input_wrapper_focus}
             on:keypress={search.on_input_wrapper_focus}
-            class="cursor-text w-full rounded-md border bg-white py-2 pl-3 pr-12 sm:text-sm
-            {inputHasFocus ? `border-${colorName}-500 outline-none ring-1 ring-{colorName}-500` : 'shadow-sm border-gray-300'}"
+            class="cursor-text w-full flex rounded-md border bg-white py-2 pl-3 pr-12 sm:text-sm
+            {inputHasFocus ? `border-${colorName}-500 outline-none ring-1 ring-${colorName}-500` : 'shadow-sm border-gray-300'}"
         >
             {#if multiple === true && hasSelection}
                 <div class="flex gap-1 flex-wrap">
@@ -325,7 +329,7 @@
                     {/each}
                 </div>
             {/if}
-            <div class={multiple === true && hasSelection ? "mt-2" : ""}>
+            <div>
                 <input
                     {...attributes}
                     type="text"
@@ -346,12 +350,13 @@
                         type="button"
                         on:click={() => reset()}
                         title={$LL.reset()}
+                        tabindex="-1"
                         class="text-gray-400 absolute cursor-pointer inset-y-0 right-0 flex items-center rounded-r-md px-2"
                     >
                         <XIcon />
                     </button>
                 {:else}
-                    <span class="text-gray-400 absolute inset-y-0 right-0 flex items-center rounded-r-md px-2">
+                    <span tabindex="-1" class="text-gray-400 absolute inset-y-0 right-0 flex items-center rounded-r-md px-2">
                         <ChevronUpDownIcon />
                     </span>
                 {/if}
@@ -406,10 +411,12 @@
                         </li>
                     {/each}
                 {:else}
-                    <p class="px-2">{noResultsText}</p>
-                    {#if createable && !searchValue}
-                        <p class="px-2 text-gray-500">{$LL.combobox.createRecordHelpText()}</p>
-                    {/if}
+                    <slot name="no-records">
+                        <p class="px-2">{noResultsText}</p>
+                        {#if createable && !searchValue}
+                            <p class="px-2 text-gray-500">{$LL.combobox.createRecordHelpText()}</p>
+                        {/if}
+                    </slot>
                 {/if}
             </ul>
             {#if showCreationHint}
-- 
cgit v1.3

