AutoComplete updated to support REST service

This commit is contained in:
davideraccagni 2022-04-22 01:40:27 +02:00
commit 79828140a7
6 changed files with 313 additions and 281 deletions

View file

@ -5,6 +5,33 @@ components: ["AutoComplete", "AutoCompleteSkeleton"]
<script> <script>
import { AutoComplete, AutoCompleteSkeleton, InlineNotification } from "carbon-components-svelte"; import { AutoComplete, AutoCompleteSkeleton, InlineNotification } from "carbon-components-svelte";
import Preview from "../../components/Preview.svelte"; import Preview from "../../components/Preview.svelte";
let items = [];
fetch('https://restcountries.com/v3.1/all?fields=name,ccn3')
.then(res => {
if (!res.ok) {
throw new Error("Failed!");
}
return res.json();
})
.then(data => {
let _items = [];
Object.values(data).forEach(country => {
_items.push({ id: country.ccn3, text: country.name.common});
});
items = _items;
return items;
})
.catch(err => {
console.log(err);
});
function filteredItems(value) {
return value ? items.filter(country => country.text.startsWith(value)) : [];
}
</script> </script>
`AutoComplete` is keyed for performance reasons. `AutoComplete` is keyed for performance reasons.
@ -15,10 +42,7 @@ components: ["AutoComplete", "AutoCompleteSkeleton"]
### Default ### Default
<AutoComplete titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "11", text: "Email1"},
{id: "12", text: "Email2"},
{id: "2", text: "Fax"}]}" />
### Custom slot ### Custom slot
@ -28,73 +52,41 @@ Override the default slot to customize the display of each item. Access the item
### Hidden label ### Hidden label
<AutoComplete hideLabel titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete hideLabel titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Format item display text
Use the `itemToString` prop to format the display of individual items.
<AutoComplete itemToString={item => {
return item.text + ' (' + item.id +')'
}} titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"},
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Multiple dropdowns
<FileSource src="/framed/AutoComplete/MultipleAutoComplete" />
### Top direction ### Top direction
Set `direction` to `"top"` for the dropdown menu to appear above the input. Set `direction` to `"top"` for the dropdown menu to appear above the input.
<AutoComplete direction="top" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete direction="top" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Light variant ### Light variant
<AutoComplete light titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete light titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Inline variant ### Inline variant
<AutoComplete type="inline" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete type="inline" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Extra-large size ### Extra-large size
<AutoComplete size="xl" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete size="xl" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Small size ### Small size
<AutoComplete size="sm" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete size="sm" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Invalid state ### Invalid state
<AutoComplete invalid invalidText="Secondary contact method must be different from the primary contact" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete invalid invalidText="Secondary contact method must be different from the primary contact" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Warning state ### Warning state
<AutoComplete warn warnText="This contact method is not associated with your account" titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete warn warnText="This contact method is not associated with your account" titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Disabled state ### Disabled state
<AutoComplete disabled titleText="Contact" selectedId="0" items="{[{id: "0", text: "Slack"}, <AutoComplete disabled titleText="Contact" selectedId="0" shouldFilterItem="{filteredItems}" />
{id: "1", text: "Email"},
{id: "2", text: "Fax"}]}" />
### Skeleton ### Skeleton

View file

@ -18,6 +18,15 @@ items={[
{id: "2", text: "Fax"} {id: "2", text: "Fax"}
]} /> ]} />
### `shouldFilterItem`
<ComboBox shouldFilterItem={(item, value) => item.text.startsWith(value)} titleText="Contact" placeholder="Select contact method"
items={[
{id: "0", text: "Slack"},
{id: "1", text: "Email"},
{id: "2", text: "Fax"}
]} />
### Custom slot ### Custom slot
Override the default slot to customize the display of each item. Access the item and index through the `let:` directive. Override the default slot to customize the display of each item. Access the item and index through the `let:` directive.

View file

@ -1,15 +1,40 @@
<script> <script>
import { AutoComplete } from "carbon-components-svelte"; import { AutoComplete } from "carbon-components-svelte";
let items = [];
fetch("https://restcountries.com/v3.1/all?fields=name,ccn3")
.then((res) => {
if (!res.ok) {
throw new Error("Failed!");
}
return res.json();
})
.then((data) => {
let _items = [];
Object.values(data).forEach((country) => {
_items.push({ id: country.ccn3, text: country.name.common });
});
items = _items;
return items;
})
.catch((err) => {
console.log(err);
});
function filteredItems(value) {
return value
? items.filter((country) => country.text.startsWith(value))
: [];
}
</script> </script>
<AutoComplete <AutoComplete
titleText="Contact" titleText="Contact"
selectedId="0" selectedId="0"
items="{[ shouldFilterItem="{filteredItems}"
{ id: '0', text: 'Slack' },
{ id: '1', text: 'Email' },
{ id: '2', text: 'Fax' },
]}"
let:item let:item
let:index let:index
> >

View file

@ -1,42 +0,0 @@
<script>
import { AutoComplete } from "carbon-components-svelte";
const items = [
{ id: "0", text: "Slack" },
{ id: "1", text: "Email" },
{ id: "2", text: "Fax" },
];
let auto_complete1_selectedId = "0";
let auto_complete2_selectedId = "1";
const formatSelected = (id) =>
items.find((item) => item.id === id)?.text ?? "N/A";
$: primary = formatSelected(auto_complete1_selectedId);
$: secondary = formatSelected(auto_complete2_selectedId);
</script>
<AutoComplete
titleText="Primary contact"
bind:selectedId="{auto_complete1_selectedId}"
items="{items}"
/>
<div>Primary: {primary}</div>
<AutoComplete
invalid="{auto_complete1_selectedId === auto_complete2_selectedId}"
invalidText="Secondary contact method must be different from the primary contact"
titleText="Secondary contact"
bind:selectedId="{auto_complete2_selectedId}"
items="{items}"
/>
<div>Secondary: {secondary}</div>
<style>
div {
margin: var(--cds-layout-01) 0 var(--cds-layout-03);
}
</style>

View file

@ -7,12 +7,6 @@
* @slot {{ item: AutoCompleteItem; index: number; }} * @slot {{ item: AutoCompleteItem; index: number; }}
*/ */
/**
* Set the full list of items
* @type {AutoCompleteItem[]}
*/
export let items = [];
/** /**
* Override the display of a dropdown item * Override the display of a dropdown item
* @type {(item: AutoCompleteItem) => string} * @type {(item: AutoCompleteItem) => string}
@ -32,10 +26,10 @@
export let selectedItem = undefined; export let selectedItem = undefined;
/** /**
* Specify the type of dropdown * Determine if an item should be filtered given the current combobox value
* @type {"default" | "inline"} * @type {(value: string) => AutoCompleteItem[]}
*/ */
export let type = "default"; export let shouldFilterItem = () => [];
/** /**
* Specify the direction of the dropdown menu * Specify the direction of the dropdown menu
@ -52,9 +46,6 @@
/** Set to `true` to open the dropdown */ /** Set to `true` to open the dropdown */
export let open = false; export let open = false;
/** Set to `true` to use the inline variant */
export let inline = false;
/** Set to `true` to enable the light variant */ /** Set to `true` to enable the light variant */
export let light = false; export let light = false;
@ -103,80 +94,100 @@
/** Specify the placeholder text */ /** Specify the placeholder text */
export let placeholder = null; export let placeholder = null;
import { createEventDispatcher } from "svelte"; /**
* Obtain a reference to the list HTML element
* @type {null | HTMLDivElement}
*/
export let listRef = null;
import { createEventDispatcher, afterUpdate, tick } from "svelte";
import Checkmark from "../icons/Checkmark.svelte";
import WarningFilled from "../icons/WarningFilled.svelte"; import WarningFilled from "../icons/WarningFilled.svelte";
import WarningAltFilled from "../icons/WarningAltFilled.svelte"; import WarningAltFilled from "../icons/WarningAltFilled.svelte";
import { ListBox, ListBoxMenu, ListBoxMenuItem } from "../ListBox"; import ListBox from "../ListBox/ListBox.svelte";
import ListBoxField from "../ListBox/ListBoxField.svelte";
import ListBoxMenu from "../ListBox/ListBoxMenu.svelte";
import ListBoxMenuIcon from "../ListBox/ListBoxMenuIcon.svelte";
import ListBoxMenuItem from "../ListBox/ListBoxMenuItem.svelte";
import ListBoxSelection from "../ListBox/ListBoxSelection.svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let filteredItems = []; let filteredItems = [];
let inputValue = value;
let prevSelectedId = null;
let highlightedIndex = -1; let highlightedIndex = -1;
let innerValue = undefined;
function change(dir) { function change(dir) {
let index = highlightedIndex + dir; let index = highlightedIndex + dir;
if (index < 0) { if (index < 0) {
index = filteredItems.length - 1; index = filteredItems.length - 1;
} else if (index >= filteredItems.length) { } else if (index >= filteredItems.length) {
index = 0; index = 0;
} }
highlightedIndex = index; highlightedIndex = index;
} }
function onKeydown(event) { /**
let key = event.key; * Clear the combo box programmatically
* @type {(options?: { focus?: boolean; }) => void}
if (["Enter", "ArrowDown", "ArrowUp"].includes(key)) { */
event.preventDefault(); export function clear(options = {}) {
} prevSelectedId = null;
highlightedIndex = -1;
if (key === "Enter") { highlightedId = undefined;
open = !open; selectedId = undefined;
if ( selectedItem = undefined;
highlightedIndex > -1 && open = false;
filteredItems[highlightedIndex].id !== selectedId inputValue = "";
) { if (options?.focus !== false) ref?.focus();
selectedItem = filteredItems[highlightedIndex];
selectedId = selectedItem.id;
innerValue = selectedItem.text;
open = false;
}
} else if (key === "Backspace") {
selectedItem = undefined;
selectedId = undefined;
open = innerValue.length > 0 && filteredItems.length > 0;
} else if (key === "Tab") {
open = false;
ref.blur();
} else if (key === "ArrowDown") {
change(1);
} else if (key === "ArrowUp") {
change(-1);
} else if (key === "Escape") {
innerValue = "";
dispatch("clear");
open = false;
} else {
if (!open) open = filteredItems.length > 0;
}
} }
afterUpdate(() => {
if (open) {
ref.focus();
filteredItems = shouldFilterItem(value);
} else {
highlightedIndex = -1;
filteredItems = [];
if (!selectedItem) {
selectedId = undefined;
inputValue = "";
highlightedIndex = -1;
highlightedId = undefined;
} else {
// programmatically set inputValue
inputValue = selectedItem.text;
}
}
});
$: if (selectedId !== undefined) { $: if (selectedId !== undefined) {
dispatch("select", { selectedId, selectedItem }); if (prevSelectedId !== selectedId) {
prevSelectedId = selectedId;
if (filteredItems?.length === 1 && open) {
selectedId = filteredItems[0].id;
selectedItem = filteredItems[0];
highlightedIndex = -1;
highlightedId = undefined;
} else {
selectedItem = filteredItems.find((item) => item.id === selectedId);
}
dispatch("select", { selectedId, selectedItem });
}
} else {
prevSelectedId = selectedId;
selectedItem = undefined;
} }
$: filteredItems = items.filter( $: ariaLabel = $$props["aria-label"] || "Choose an item";
(item) => innerValue?.length > 0 && item.text.startsWith(innerValue) $: menuId = `menu-${id}`;
); $: comboId = `combo-${id}`;
$: inline = type === "inline"; $: highlightedId = filteredItems[highlightedIndex]
$: if (!open) { ? filteredItems[highlightedIndex].id
highlightedIndex = -1; : 0;
} $: filteredItems = shouldFilterItem(value);
$: value = inputValue;
</script> </script>
<svelte:window <svelte:window
@ -187,88 +198,169 @@
}}" }}"
/> />
<div {...$$restProps}> <div class:bx--list-box__wrapper="{true}">
{#if titleText} {#if titleText && !hideLabel}
<label <label
for="{id}" for="{id}"
class:bx--label="{true}" class:bx--label="{true}"
class:bx--label--disabled="{disabled}" class:bx--label--disabled="{disabled}"
class:bx--visually-hidden="{hideLabel}"
> >
{titleText} {titleText}
</label> </label>
{/if} {/if}
<ListBox <ListBox
type="{type}" class="bx--combo-box {direction === 'top' &&
size="{size}" 'bx--list-box--up'} {!invalid && warn && 'bx--combo-box--warning'}"
id="{id}" id="{comboId}"
name="{name}" aria-label="{ariaLabel}"
aria-label="{$$props['aria-label']}"
class="bx--dropdown {direction === 'top' && 'bx--list-box--up'} {invalid &&
'bx--dropdown--invalid'} {!invalid &&
warn &&
'bx--dropdown--warning'} {open && 'bx--dropdown--open'}
{size === 'sm' && 'bx--dropdown--sm'}
{size === 'xl' && 'bx--dropdown--xl'}
{inline && 'bx--dropdown--inline'}
{disabled && 'bx--dropdown--disabled'}
{light && 'bx--dropdown--light'}"
on:click="{({ target }) => {
if (disabled) return;
open = ref.contains(target) ? !open : false;
}}"
disabled="{disabled}" disabled="{disabled}"
open="{open}"
invalid="{invalid}" invalid="{invalid}"
invalidText="{invalidText}" invalidText="{invalidText}"
open="{open}"
light="{light}" light="{light}"
size="{size}"
warn="{warn}" warn="{warn}"
warnText="{warnText}" warnText="{warnText}"
> >
{#if invalid} <ListBoxField
<WarningFilled class="bx--text-input__invalid-icon" /> role="button"
{/if} aria-expanded="{open}"
{#if !invalid && warn} on:click="{async () => {
<WarningAltFilled if (disabled) return;
class="bx--text-input__invalid-icon open = true;
bx--text-input__invalid-icon--warning" await tick();
/> ref.focus();
{/if} }}"
<input
bind:this="{ref}"
bind:value="{innerValue}"
type="text"
role="searchbox"
class="
auto-complete__input
{size === 'sm' && 'auto-complete__input--sm'}
{size === 'xl' && 'auto-complete__input--xl'}
"
autocomplete="false"
disabled="{disabled}"
id="{id}" id="{id}"
name="{name}" disabled="{disabled}"
placeholder="{placeholder}"
translateWithId="{translateWithId}" translateWithId="{translateWithId}"
{...$$restProps} >
on:change <input
on:focus bind:this="{ref}"
on:blur tabindex="0"
on:input autocomplete="off"
on:keydown="{onKeydown}" aria-autocomplete="list"
/> aria-expanded="{open}"
aria-activedescendant="{highlightedId}"
aria-labelledby="{comboId}"
aria-disabled="{disabled}"
aria-controls="{open ? menuId : undefined}"
aria-owns="{open ? menuId : undefined}"
disabled="{disabled}"
placeholder="{placeholder}"
id="{id}"
value="{inputValue}"
name="{name}"
{...$$restProps}
class:bx--text-input="{true}"
class:bx--text-input--light="{light}"
class:bx--text-input--empty="{inputValue === ''}"
on:input="{async ({ target }) => {
if (!open && target.value.length > 0) {
open = true;
}
inputValue = target.value;
if (!inputValue.length) {
clear();
open = true;
}
}}"
on:keydown
on:keydown|stopPropagation="{({ key }) => {
if (key === 'Enter') {
open = !open;
if (
highlightedIndex > -1 &&
filteredItems[highlightedIndex]?.id !== selectedId
) {
open = false;
if (filteredItems[highlightedIndex]) {
inputValue = filteredItems[highlightedIndex].text;
selectedItem = filteredItems[highlightedIndex];
selectedId = filteredItems[highlightedIndex].id;
}
} else {
open = false;
if (filteredItems[0]) {
inputValue = filteredItems[0].text;
selectedItem = filteredItems[0];
selectedId = filteredItems[0].id;
}
}
highlightedIndex = -1;
} else if (key === 'Tab') {
open = false;
} else if (key === 'ArrowDown') {
change(1);
} else if (key === 'ArrowUp') {
change(-1);
} else if (key === 'Escape') {
open = false;
}
}}"
on:keyup
on:focus
on:blur
on:blur="{({ relatedTarget }) => {
if (!open || !relatedTarget) return;
if (
relatedTarget &&
!['INPUT', 'SELECT', 'TEXTAREA'].includes(relatedTarget.tagName) &&
relatedTarget.getAttribute('role') !== 'button' &&
relatedTarget.getAttribute('role') !== 'searchbox'
) {
ref.focus();
}
}}"
/>
{#if invalid}
<WarningFilled class="bx--list-box__invalid-icon" />
{/if}
{#if !invalid && warn}
<WarningAltFilled
class="bx--list-box__invalid-icon bx--list-box__invalid-icon--warning"
/>
{/if}
{#if inputValue}
<ListBoxSelection
on:clear
on:clear="{clear}"
translateWithId="{translateWithId}"
disabled="{disabled}"
open="{open}"
/>
{/if}
<ListBoxMenuIcon
on:click="{(e) => {
if (disabled) return;
e.stopPropagation();
open = !open;
}}"
translateWithId="{translateWithId}"
open="{open}"
/>
</ListBoxField>
{#if open} {#if open}
<ListBoxMenu aria-labelledby="{id}" id="{id}"> <ListBoxMenu
aria-label="{ariaLabel}"
id="{id}"
on:scroll
bind:ref="{listRef}"
>
{#each filteredItems as item, i (item.id)} {#each filteredItems as item, i (item.id)}
<ListBoxMenuItem <ListBoxMenuItem
id="{item.id}" id="{item.id}"
active="{selectedId === item.id}" active="{selectedId === item.id}"
highlighted="{highlightedIndex === i || selectedId === item.id}" highlighted="{highlightedIndex === i}"
on:click="{() => { on:click="{() => {
selectedItem = item;
selectedId = item.id; selectedId = item.id;
innerValue = item.text; open = false;
ref.focus();
if (filteredItems[i]) {
inputValue = filteredItems[i].text;
}
}}" }}"
on:mouseenter="{() => { on:mouseenter="{() => {
highlightedIndex = i; highlightedIndex = i;
@ -277,12 +369,15 @@
<slot item="{item}" index="{i}"> <slot item="{item}" index="{i}">
{itemToString(item)} {itemToString(item)}
</slot> </slot>
{#if selectedItem && selectedItem.id === item.id}
<Checkmark class="bx--list-box__menu-item__selected-icon" />
{/if}
</ListBoxMenuItem> </ListBoxMenuItem>
{/each} {/each}
</ListBoxMenu> </ListBoxMenu>
{/if} {/if}
</ListBox> </ListBox>
{#if !inline && !invalid && !warn && helperText} {#if !invalid && helperText && !warn}
<div <div
class:bx--form__helper-text="{true}" class:bx--form__helper-text="{true}"
class:bx--form__helper-text--disabled="{disabled}" class:bx--form__helper-text--disabled="{disabled}"
@ -291,50 +386,3 @@
</div> </div>
{/if} {/if}
</div> </div>
<style>
.auto-complete__input {
font-size: var(--cds-body-short-01-font-size, 0.875rem);
font-weight: var(--cds-body-short-01-font-weight, 400);
line-height: var(--cds-body-short-01-line-height, 1.28572);
letter-spacing: var(--cds-body-short-01-letter-spacing, 0.16px);
outline: 2px solid transparent;
outline-offset: -2px;
width: 100%;
height: 2.5rem;
padding: 0 1rem;
border: none;
border-bottom-color: currentcolor;
border-bottom-style: none;
border-bottom-width: medium;
border-bottom: 1px solid var(--cds-ui-04, #8d8d8d);
background-color: var(--cds-field-01, #f4f4f4);
color: var(--cds-text-01, #161616);
transition: background-color 70ms cubic-bezier(0.2, 0, 0.38, 0.9),
outline 70ms cubic-bezier(0.2, 0, 0.38, 0.9);
}
.auto-complete__input:focus {
outline: 2px solid var(--cds-focus, #0f62fe);
outline-offset: -2px;
}
.auto-complete__input--sm {
height: 2rem;
}
.auto-complete__input--xl,
.auto-complete__input--lg {
height: 3rem;
}
.auto-complete__input:disabled {
outline: 2px solid transparent;
outline-offset: -2px;
border-bottom: 1px solid transparent;
background-color: var(--cds-field, #f4f4f4);
color: var(--cds-text-disabled, #c6c6c6);
cursor: not-allowed;
-webkit-text-fill-color: var(--cds-disabled-02, #c6c6c6);
}
</style>

View file

@ -11,13 +11,7 @@ export interface AutoCompleteItem {
} }
export interface AutoCompleteProps export interface AutoCompleteProps
extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap["div"]> { extends svelte.JSX.HTMLAttributes<HTMLElementTagNameMap["input"]> {
/**
* Set the full list of items
* @default []
*/
items?: AutoCompleteItem[];
/** /**
* Override the display of a dropdown item * Override the display of a dropdown item
* @default (item) => item.text || item.id * @default (item) => item.text || item.id
@ -37,10 +31,10 @@ export interface AutoCompleteProps
selectedItem?: AutoCompleteItem; selectedItem?: AutoCompleteItem;
/** /**
* Specify the type of dropdown * Determine if an item should be filtered given the current combobox value
* @default "default" * @default () => []
*/ */
type?: "default" | "inline"; shouldFilterItem?: (value: string) => AutoCompleteItem[];
/** /**
* Specify the direction of the dropdown menu * Specify the direction of the dropdown menu
@ -60,12 +54,6 @@ export interface AutoCompleteProps
*/ */
open?: boolean; open?: boolean;
/**
* Set to `true` to use the inline variant
* @default false
*/
inline?: boolean;
/** /**
* Set to `true` to enable the light variant * Set to `true` to enable the light variant
* @default false * @default false
@ -149,6 +137,12 @@ export interface AutoCompleteProps
* @default null * @default null
*/ */
placeholder?: undefined; placeholder?: undefined;
/**
* Obtain a reference to the list HTML element
* @default null
*/
listRef?: null | HTMLDivElement;
} }
export default class AutoComplete extends SvelteComponentTyped< export default class AutoComplete extends SvelteComponentTyped<
@ -158,11 +152,17 @@ export default class AutoComplete extends SvelteComponentTyped<
selectedId: AutoCompleteItemId; selectedId: AutoCompleteItemId;
selectedItem: AutoCompleteItem; selectedItem: AutoCompleteItem;
}>; }>;
change: WindowEventMap["change"]; keydown: WindowEventMap["keydown"];
keyup: WindowEventMap["keyup"];
focus: WindowEventMap["focus"]; focus: WindowEventMap["focus"];
blur: WindowEventMap["blur"]; blur: WindowEventMap["blur"];
input: WindowEventMap["input"]; clear: WindowEventMap["clear"];
clear: CustomEvent<any>; scroll: WindowEventMap["scroll"];
}, },
{ default: { item: AutoCompleteItem; index: number } } { default: { item: AutoCompleteItem; index: number } }
> {} > {
/**
* Clear the combo box programmatically
*/
clear: (options?: { focus?: boolean }) => void;
}