@melt-ui/svelte
Version:

72 lines (71 loc) • 2.68 kB
JavaScript
import { addEventListener, addMeltEventListener, makeElement, createElHelpers, effect, executeCallbacks, isContentEditable, isHTMLInputElement, kbd, omit, } from '../../internal/helpers/index.js';
import { writable } from 'svelte/store';
import { createListbox } from '../listbox/create.js';
// prettier-ignore
export const INTERACTION_KEYS = [kbd.ARROW_LEFT, kbd.ESCAPE, kbd.ARROW_RIGHT, kbd.SHIFT, kbd.CAPS_LOCK, kbd.CONTROL, kbd.ALT, kbd.META, kbd.ENTER, kbd.F1, kbd.F2, kbd.F3, kbd.F4, kbd.F5, kbd.F6, kbd.F7, kbd.F8, kbd.F9, kbd.F10, kbd.F11, kbd.F12];
const { name } = createElHelpers('combobox');
/**
* Creates an ARIA-1.2-compliant combobox.
*
* @TODO multi-select using `tags-input` builder?
*/
export function createCombobox(props) {
const listbox = createListbox({ ...props, builder: 'combobox', typeahead: false });
const inputValue = writable('');
const touchedInput = writable(false);
/* -------- */
/* ELEMENTS */
/* -------- */
/** Action and attributes for the text input. */
const input = makeElement(name('input'), {
stores: [listbox.elements.trigger, inputValue],
returned: ([$trigger, $inputValue]) => {
return {
...omit($trigger, 'action'),
role: 'combobox',
value: $inputValue,
autocomplete: 'off',
};
},
action: (node) => {
const unsubscribe = executeCallbacks(addMeltEventListener(node, 'input', (e) => {
if (!isHTMLInputElement(e.target) && !isContentEditable(e.target))
return;
touchedInput.set(true);
}),
// This shouldn't be cancelled ever, so we don't use addMeltEventListener.
addEventListener(node, 'input', (e) => {
if (isHTMLInputElement(e.target)) {
inputValue.set(e.target.value);
}
if (isContentEditable(e.target)) {
inputValue.set(e.target.innerText);
}
}));
const { destroy } = listbox.elements.trigger(node);
return {
destroy() {
destroy?.();
unsubscribe();
},
};
},
});
effect(listbox.states.open, ($open) => {
if (!$open) {
touchedInput.set(false);
}
});
return {
...listbox,
elements: {
...omit(listbox.elements, 'trigger'),
input,
},
states: {
...listbox.states,
touchedInput,
inputValue,
},
};
}