From 1f558b9e71f84c18e1cf76693c2b143d30a6047c Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 15:55:37 +0100 Subject: [PATCH 01/12] Introduce `selection`-function and `active`-class --- src/Typeahead.svelte | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 0fbc7d7..3517307 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -15,6 +15,9 @@ /** @type {(item: Item) => Item} */ export let extract = (item) => item; + + /** @type {(item: Item) => Item} */ + export let selection = (item) => false; /** @type {(item: Item) => Item} */ export let disable = (item) => false; @@ -89,7 +92,11 @@ .filter(value, data, options) .filter(({ score }) => score > 0) .filter((result) => !filter(result.original)) - .map((result) => ({ ...result, disabled: disable(result.original) })); + .map((result)=> ({ + ...result, + disabled: disable(result.original), + selected: selection(result.original) + })); $: resultsId = results.map((result) => extract(result.original)).join(""); @@ -171,7 +178,8 @@
  • { @@ -228,18 +236,22 @@ background-color: #cacaca; } + .active { + border: #0f62fe 1px solid !important; + } + .disabled { opacity: 0.4; cursor: not-allowed; } - :global([data-svelte-search] label) { + [data-svelte-typeahead] :global([data-svelte-search] label) { margin-bottom: 0.25rem; display: inline-flex; font-size: 0.875rem; } - :global([data-svelte-search] input) { + [data-svelte-typeahead] :global([data-svelte-search] input) { width: 100%; padding: 0.5rem 0.75rem; background: none; @@ -249,7 +261,7 @@ border: 1px solid #e5e5e5; } - :global([data-svelte-search] input:focus) { + [data-svelte-typeahead] :global([data-svelte-search] input:focus) { outline-color: #0f62fe; outline-offset: 2px; outline-width: 1px; From ad14cc69b59a06542869e2b8393d7c0c6820d859 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 15:56:17 +0100 Subject: [PATCH 02/12] Update Typeahead.d.ts --- types/Typeahead.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/types/Typeahead.d.ts b/types/Typeahead.d.ts index d1a65b7..6e66826 100644 --- a/types/Typeahead.d.ts +++ b/types/Typeahead.d.ts @@ -31,6 +31,11 @@ export interface TypeaheadProps extends SearchProps { * @default (item) => item */ extract?: (item: Item) => Item; + + /** + * @default (item) => item + */ + selection?: (item: Item) => Item; /** * @default (item) => false From 8ae1947057e1748a1259996eaba212e0982a9aa9 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 15:59:14 +0100 Subject: [PATCH 03/12] reove `!important` from css --- src/Typeahead.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 3517307..f48ea41 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -236,8 +236,9 @@ background-color: #cacaca; } - .active { - border: #0f62fe 1px solid !important; + .active, + li:not(:last-of-type).active { + border: #0f62fe 1px solid; } .disabled { From 3ee7b6e06d57379799b4b7041867658a6d5d9d97 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 20:12:14 +0100 Subject: [PATCH 04/12] Prevent disabled items from beeing selected --- src/Typeahead.svelte | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index f48ea41..68cf817 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -51,6 +51,38 @@ let hideDropdown = false; let selectedIndex = -1; let prevResults = ""; + + function setNextSelectedIndex(i = 0) { + i += 1; + + selectedIndex += 1; + if (selectedIndex === results.length) { + selectedIndex = 0; + } + + if(results.lenght == 0 || !(selectedIndex in results) || i > results.lenght) { + selectedIndex = -1; + } else if(results[selectedIndex].disabled) { + setNextSelectedIndex(i); + } + + }; + + function setPreviousSelectedIndex(i = 0) { + i += 1; + + selectedIndex -= 1; + if (selectedIndex < 0) { + selectedIndex = results.length - 1; + } + + if(results.lenght == 0 || i > results.lenght) { + selectedIndex = -1; + } else if(results[selectedIndex].disabled) { + setPreviousSelectedIndex(i); + } + + }; afterUpdate(() => { if (prevResults !== resultsId && autoselect) { @@ -146,17 +178,11 @@ break; case 'ArrowDown': e.preventDefault(); - selectedIndex += 1; - if (selectedIndex === results.length) { - selectedIndex = 0; - } + setNextSelectedIndex(); break; case 'ArrowUp': e.preventDefault(); - selectedIndex -= 1; - if (selectedIndex < 0) { - selectedIndex = results.length - 1; - } + setPreviousSelectedIndex(); break; case 'Escape': e.preventDefault(); From 3b670754dbbfef8018976c9ad04928892144666c Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 20:12:41 +0100 Subject: [PATCH 05/12] Update Typeahead.svelte --- src/Typeahead.svelte | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 68cf817..364e854 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -262,9 +262,8 @@ background-color: #cacaca; } - .active, - li:not(:last-of-type).active { - border: #0f62fe 1px solid; + .active { + background-color: #d8e9f3; } .disabled { From 1e365b8dce5b26654b408e0dddfa822501f02548 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 21:02:33 +0100 Subject: [PATCH 06/12] Update Typeahead.svelte --- src/Typeahead.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 364e854..2e30e7d 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -76,7 +76,7 @@ selectedIndex = results.length - 1; } - if(results.lenght == 0 || i > results.lenght) { + if(results.lenght == 0 || !(selectedIndex in results) || i > results.lenght) { selectedIndex = -1; } else if(results[selectedIndex].disabled) { setPreviousSelectedIndex(i); From 9c619f2044a6fc0d948e134e4e0f0418ca289c12 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 21:04:07 +0100 Subject: [PATCH 07/12] Update Typeahead.svelte --- src/Typeahead.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 2e30e7d..05110d9 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -86,7 +86,7 @@ afterUpdate(() => { if (prevResults !== resultsId && autoselect) { - selectedIndex = 0; + setNextSelectedIndex(); } if (prevResults !== resultsId) { From 0edd3eb6f0e4a8734bc3322880a8ac04562a2589 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 21:05:36 +0100 Subject: [PATCH 08/12] Update Typeahead.svelte --- src/Typeahead.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 05110d9..c6d69e1 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -86,6 +86,7 @@ afterUpdate(() => { if (prevResults !== resultsId && autoselect) { + selectedIndex = -1; setNextSelectedIndex(); } From e573e89b2cdd9db8dc51586b01c2475ff742a378 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 23:24:06 +0100 Subject: [PATCH 09/12] Update Typeahead.svelte --- src/Typeahead.svelte | 46 ++++++++++---------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index c6d69e1..46528db 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -51,43 +51,10 @@ let hideDropdown = false; let selectedIndex = -1; let prevResults = ""; - - function setNextSelectedIndex(i = 0) { - i += 1; - - selectedIndex += 1; - if (selectedIndex === results.length) { - selectedIndex = 0; - } - - if(results.lenght == 0 || !(selectedIndex in results) || i > results.lenght) { - selectedIndex = -1; - } else if(results[selectedIndex].disabled) { - setNextSelectedIndex(i); - } - - }; - - function setPreviousSelectedIndex(i = 0) { - i += 1; - - selectedIndex -= 1; - if (selectedIndex < 0) { - selectedIndex = results.length - 1; - } - - if(results.lenght == 0 || !(selectedIndex in results) || i > results.lenght) { - selectedIndex = -1; - } else if(results[selectedIndex].disabled) { - setPreviousSelectedIndex(i); - } - - }; afterUpdate(() => { if (prevResults !== resultsId && autoselect) { - selectedIndex = -1; - setNextSelectedIndex(); + selectedIndex = results.findIndex(result => !result.disabled); } if (prevResults !== resultsId) { @@ -179,11 +146,18 @@ break; case 'ArrowDown': e.preventDefault(); - setNextSelectedIndex(); + for (selectedIndex++;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex++) { } + if(!(selectedIndex in results) || (selectedIndex in results && results[selectedIndex].disabled)) { + selectedIndex = results.findIndex(result => !result.disabled); + } break; case 'ArrowUp': e.preventDefault(); - setPreviousSelectedIndex(); + for (selectedIndex--;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex--) { } + if(!(selectedIndex in results) || (selectedIndex in results && results[selectedIndex].disabled)) { + let reverseselectedIndex = results.slice().reverse().findIndex(result => !result.disabled) + 1; + selectedIndex = (reverseselectedIndex == -1) ? -1 : (results.length - reverseselectedIndex); + } break; case 'Escape': e.preventDefault(); From f1e522f652c88a1866465276575a18f285e77dd5 Mon Sep 17 00:00:00 2001 From: Amerlander Date: Sun, 21 Feb 2021 23:27:27 +0100 Subject: [PATCH 10/12] Update Typeahead.svelte --- src/Typeahead.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Typeahead.svelte b/src/Typeahead.svelte index 46528db..f6c192e 100644 --- a/src/Typeahead.svelte +++ b/src/Typeahead.svelte @@ -146,14 +146,14 @@ break; case 'ArrowDown': e.preventDefault(); - for (selectedIndex++;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex++) { } + for (selectedIndex++;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex++); if(!(selectedIndex in results) || (selectedIndex in results && results[selectedIndex].disabled)) { selectedIndex = results.findIndex(result => !result.disabled); } break; case 'ArrowUp': e.preventDefault(); - for (selectedIndex--;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex--) { } + for (selectedIndex--;(selectedIndex in results && results[selectedIndex].disabled); selectedIndex--); if(!(selectedIndex in results) || (selectedIndex in results && results[selectedIndex].disabled)) { let reverseselectedIndex = results.slice().reverse().findIndex(result => !result.disabled) + 1; selectedIndex = (reverseselectedIndex == -1) ? -1 : (results.length - reverseselectedIndex); From 4258381b78b08b73ab308ee2bba28f6cfe86b1ad Mon Sep 17 00:00:00 2001 From: JW Date: Tue, 23 Feb 2021 11:44:39 +0100 Subject: [PATCH 11/12] update readme --- README.md | 54 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1103238..1c746e8 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ npm i -D svelte-typeahead Pass an array of objects to the `data` prop. Use the `extractor` to specify the key value to search on. - ```svelte + + item.state} selection={(item) => item.selected} on:select="{handleSelect} /> +``` + + +*Hint: Required items should match `selection` and `disabled` to be shown as selected and prevent them from unselection. Further styling may be needed.* ### Disable and Filter Items Use the `filter` to filter Items out and `disable` to disable them in the result set. +- Filtered items are not part of the results at all. +- Disabled itesm receive the class `disbaled` and will not fire an `on:select` event. Example for disabling and filtering items by their title length: @@ -100,6 +122,7 @@ Example for disabling and filtering items by their title length: ``` + Example for disabling items after selecting them: @@ -107,11 +130,11 @@ Example for disabling items after selecting them: - item.state} disable={(item) => item.selected} on:select="{handleSelect}" /> + item.state} disable={(item) => item.disabled} on:select="{handleSelect}" /> ``` @@ -129,18 +152,19 @@ Set `focusAfterSelect` to `true` to re-focus the search input after selecting a ### Props -| Prop name | Value | Description | -| :--------------- | :-------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | -| value | `string` (default: `""`) | Input search value | -| data | `T[]` (default: `[]`) | Items to search | -| extract | `(T) => T` | Target an item key if `data` is an object array | -| disable | `(T) => T` | Pass in a function to disable items. They will show up in the results list, but wont be selectable. | -| filter | `(T) => T` | Pass in a function to filter items. Thei will be hidden and do not show up at all in the results list. | -| autoselect | `boolean` (default: `true`) | Automatically select the first (top) result | -| inputAfterSelect | `"update" or "clear" or "keep"`(default:`"update"`) | Set to `"clear"` to clear the `value` after selecting a result. Set to `"keep"` keep the search field unchanged after a selection. | -| results | `FuzzyResult[]` (default: `[]`) | Raw fuzzy results from the [fuzzy](https://github.com/mattyork/fuzzy) module | -| focusAfterSelect | `boolean` (default: `false`) | Set to `true` to re-focus the input after selecting a result. | -| `...$$restProps` | (forwarded to `Search` component) | All other props are forwarded to the input element. | +| Prop name | Value | Description | +| :--------------- | :-------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | +| value | `string` (default: `""`) | Input search value | +| data | `T[]` (default: `[]`) | Items to search | +| extract | `(T) => T` | Target an item key if `data` is an object array | +| selection | `(T) => T` | Pass in a function to select items. They will reveice the class `selected`. | +| disable | `(T) => T` | Pass in a function to disable items. They will show up in the results list and receive the class `disabled`, but wont be selectable. | +| filter | `(T) => T` | Pass in a function to filter items. Thei will be hidden and do not show up at all in the results list. | +| autoselect | `boolean` (default: `true`) | Automatically select the first (top) result | +| inputAfterSelect | `"update" or "clear" or "keep"`(default:`"update"`) | Set to `"clear"` to clear the `value` after selecting a result. Set to `"keep"` keep the search field unchanged after a selection. | +| results | `FuzzyResult[]` (default: `[]`) | Raw fuzzy results from the [fuzzy](https://github.com/mattyork/fuzzy) module | +| focusAfterSelect | `boolean` (default: `false`) | Set to `true` to re-focus the input after selecting a result. | +| `...$$restProps` | (forwarded to `Search` component) | All other props are forwarded to the input element. | ### Dispatched events From 9ed60c9f4ce73080726319d916336d2cc4ccc85c Mon Sep 17 00:00:00 2001 From: JW Date: Tue, 23 Feb 2021 11:54:31 +0100 Subject: [PATCH 12/12] Update Readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1c746e8..929681a 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,10 @@ module.exports = { Svelte version 3.31 or greater is required if using TypeScript. +## Internet Explorer + +To make this component compatible with IE11 you'll need to polyfill `findIndex`. + ## Changelog [Changelog](CHANGELOG.md)