Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@melt-ui/svelte

Package Overview
Dependencies
Maintainers
1
Versions
195
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@melt-ui/svelte - npm Package Compare versions

Comparing version 0.38.1 to 0.39.0

39

dist/builders/combobox/create.d.ts
/// <reference types="svelte" />
import type { MeltActionReturn } from '../../internal/types.js';
import { type Writable } from 'svelte/store';
import type { ComboboxItemProps, CreateComboboxProps } from './types.js';
import type { MeltActionReturn } from '../../internal/types.js';
import type { ComboboxEvents } from './events.js';
import type { ComboboxItemProps, ComboboxOption, CreateComboboxProps } from './types.js';
export declare const INTERACTION_KEYS: string[];

@@ -14,3 +14,3 @@ /**

*/
export declare function createCombobox<ItemValue>(props?: CreateComboboxProps<ItemValue>): {
export declare function createCombobox<Value>(props?: CreateComboboxProps<Value>): {
elements: {

@@ -47,7 +47,7 @@ input: import("../../internal/helpers/index.js").ExplicitBuilderReturn<[{

}, string>;
item: import("../../internal/helpers/index.js").ExplicitBuilderReturn<[{
update: (updater: import("svelte/store").Updater<ItemValue | undefined>, sideEffect?: ((newValue: ItemValue | undefined) => void) | undefined) => void;
set: (this: void, value: ItemValue | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<ItemValue | undefined>, invalidate?: import("svelte/store").Invalidator<ItemValue | undefined> | undefined): import("svelte/store").Unsubscriber;
}, Writable<import("./types.js").ComboboxFilterFunction<ItemValue>>, {
option: import("../../internal/helpers/index.js").ExplicitBuilderReturn<[{
update: (updater: import("svelte/store").Updater<ComboboxOption<Value> | undefined>, sideEffect?: ((newValue: ComboboxOption<Value> | undefined) => void) | undefined) => void;
set: (this: void, value: ComboboxOption<Value> | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<ComboboxOption<Value> | undefined>, invalidate?: import("svelte/store").Invalidator<ComboboxOption<Value> | undefined> | undefined): import("svelte/store").Unsubscriber;
}, Writable<import("./types.js").ComboboxFilterFunction<Value>>, {
set: (value: string) => void;

@@ -76,3 +76,3 @@ update: (fn: (value: string) => string) => void;

}> | undefined): import("svelte/store").Unsubscriber;
}], (node: HTMLElement) => MeltActionReturn<ComboboxEvents['item']>, ([$value, $filterFunction, $inputValue, $touchedInput]: [ItemValue | undefined, import("./types.js").ComboboxFilterFunction<ItemValue>, {
}], (node: HTMLElement) => MeltActionReturn<ComboboxEvents['item']>, ([$value, $filterFunction, $inputValue, $touchedInput]: [ComboboxOption<Value> | undefined, import("./types.js").ComboboxFilterFunction<Value>, {
value: string;

@@ -83,3 +83,3 @@ debounced: string;

debounced: boolean;
}]) => (props: ComboboxItemProps<ItemValue>) => {
}]) => (props: ComboboxItemProps<Value>) => {
readonly 'data-value': string;

@@ -114,6 +114,6 @@ readonly 'data-label': string | undefined;

};
value: {
update: (updater: import("svelte/store").Updater<ItemValue | undefined>, sideEffect?: ((newValue: ItemValue | undefined) => void) | undefined) => void;
set: (this: void, value: ItemValue | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<ItemValue | undefined>, invalidate?: import("svelte/store").Invalidator<ItemValue | undefined> | undefined): import("svelte/store").Unsubscriber;
selected: {
update: (updater: import("svelte/store").Updater<ComboboxOption<Value> | undefined>, sideEffect?: ((newValue: ComboboxOption<Value> | undefined) => void) | undefined) => void;
set: (this: void, value: ComboboxOption<Value> | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<ComboboxOption<Value> | undefined>, invalidate?: import("svelte/store").Invalidator<ComboboxOption<Value> | undefined> | undefined): import("svelte/store").Unsubscriber;
};

@@ -127,9 +127,6 @@ inputValue: import("svelte/store").Readable<{

helpers: {
isSelected: import("svelte/store").Readable<(item: ItemValue) => boolean>;
isSelected: import("svelte/store").Readable<(item: Value) => boolean>;
};
options: {
forceVisible: Writable<boolean>;
value?: Writable<Writable<ItemValue | undefined> | undefined> | undefined;
onValueChange?: Writable<import("../../internal/helpers/index.js").ChangeFn<ItemValue | undefined> | undefined> | undefined;
defaultValue?: Writable<ItemValue | undefined> | undefined;
onOpenChange?: Writable<import("../../internal/helpers/index.js").ChangeFn<boolean> | undefined> | undefined;

@@ -143,5 +140,7 @@ preventScroll: Writable<boolean>;

scrollAlignment: Writable<"center" | "nearest">;
filterFunction: Writable<import("./types.js").ComboboxFilterFunction<ItemValue>>;
filterFunction: Writable<import("./types.js").ComboboxFilterFunction<Value>>;
loop: Writable<boolean>;
defaultInputValue?: Writable<string | undefined> | undefined;
defaultSelected?: Writable<ComboboxOption<Value> | undefined> | undefined;
selected?: Writable<Writable<ComboboxOption<Value> | undefined> | undefined> | undefined;
onSelectedChange?: Writable<import("../../internal/helpers/index.js").ChangeFn<ComboboxOption<Value> | undefined> | undefined> | undefined;
closeOnOutsideClick: Writable<boolean>;

@@ -148,0 +147,0 @@ closeOnEscape: Writable<boolean>;

import { useEscapeKeydown, usePopper } from '../../internal/actions/index.js';
import { back, builder, createElHelpers, effect, executeCallbacks, FIRST_LAST_KEYS, forward, generateId, isBrowser, isElementDisabled, isHTMLElement, isHTMLInputElement, kbd, last, next, noop, overridable, prev, removeScroll, sleep, styleToString, toWritableStores, addHighlight, removeHighlight, omit, getOptions, derivedVisible, addMeltEventListener, getPortalDestination, } from '../../internal/helpers/index.js';
import { FIRST_LAST_KEYS, addHighlight, addMeltEventListener, back, builder, createElHelpers, derivedVisible, effect, executeCallbacks, forward, generateId, getOptions, getPortalDestination, isBrowser, isElementDisabled, isHTMLElement, isHTMLInputElement, kbd, last, next, noop, omit, overridable, prev, removeHighlight, removeScroll, sleep, styleToString, toWritableStores, } from '../../internal/helpers/index.js';
import { debounceable } from '../../internal/helpers/store/debounceable.js';
import deepEqual from 'deep-equal';
import { onMount, tick } from 'svelte';
import { derived, get, readonly, writable } from 'svelte/store';
import { createLabel } from '../label/create.js';
import { debounceable } from '../../internal/helpers/store/debounceable.js';
import deepEqual from 'deep-equal';
// prettier-ignore

@@ -40,12 +40,11 @@ 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 highlightedItem = writable(null);
const valueWritable = withDefaults.value ?? writable(withDefaults.defaultValue);
const value = overridable(valueWritable, withDefaults?.onValueChange);
const selectedWritable = withDefaults.selected ??
writable(withDefaults.defaultSelected);
const selected = overridable(selectedWritable, withDefaults?.onSelectedChange);
// The current value of the input element.
const inputValue = debounceable(withDefaults.defaultInputValue ?? '', withDefaults.debounce);
const inputValue = debounceable(withDefaults.defaultSelected?.label ?? '', withDefaults.debounce);
// Either the provided open store or a store with the default open value
const openWritable = withDefaults.open ?? writable(true);
const openWritable = withDefaults.open ?? writable(false);
// The overridable open store which is the source of truth for the open state.
const open = overridable(openWritable, withDefaults?.onOpenChange);
// Open so we can register the optionsList items before mounted = true
open.set(true);
const isEmpty = writable(false);

@@ -63,3 +62,3 @@ const options = toWritableStores(omit(withDefaults, 'open', 'defaultOpen', 'debounce'));

/** ------- */
function getItemProps(el) {
function getOptionProps(el) {
const value = el.getAttribute('data-value');

@@ -74,21 +73,5 @@ const label = el.getAttribute('data-label');

}
const cachedItemPropsArr = [];
function getItemPropsArr() {
if (!isBrowser)
return cachedItemPropsArr;
const menuElement = document.getElementById(ids.menu);
if (!isHTMLElement(menuElement))
return cachedItemPropsArr;
const items = getOptions(menuElement);
return items.map(getItemProps);
}
function getSelectedLabel() {
const $value = get(value);
const itemPropsArr = getItemPropsArr();
const selectedItem = itemPropsArr.find((item) => deepEqual(item.value, $value));
return selectedItem?.label;
}
/** Resets the combobox inputValue and filteredItems back to the selectedItem */
function reset() {
const $selectedItem = get(value);
const $selectedItem = get(selected);
// If no item is selected the input should be cleared and the filter reset.

@@ -99,3 +82,3 @@ if (!$selectedItem) {

else {
inputValue.forceSet(getSelectedLabel() ?? '');
inputValue.forceSet(get(selected)?.label ?? '');
}

@@ -109,5 +92,4 @@ touchedInput.forceSet(false);

function selectItem(item) {
if (!item.dataset.value)
return;
value.set(JSON.parse(item.dataset.value));
const props = getOptionProps(item);
selected.set(props);
const activeTrigger = document.getElementById(ids.input);

@@ -182,3 +164,3 @@ if (activeTrigger) {

*/
const isSelected = derived([value], ([$value]) => {
const isSelected = derived([selected], ([$value]) => {
return (item) => $value === item;

@@ -235,3 +217,3 @@ });

tick().then(() => {
const $selectedItem = get(value);
const $selectedItem = get(selected);
if ($selectedItem)

@@ -454,6 +436,5 @@ return;

});
const item = builder(name('item'), {
stores: [value, filterFunction, inputValue, touchedInput],
const option = builder(name('option'), {
stores: [selected, filterFunction, inputValue, touchedInput],
returned: ([$value, $filterFunction, $inputValue, $touchedInput]) => (props) => {
cachedItemPropsArr.push(props);
let hidden = false;

@@ -523,10 +504,4 @@ if ($touchedInput.debounced &&

});
effect(value, function setInputValue($value) {
if (!$value) {
inputValue.set('');
return;
}
tick().then(() => {
inputValue.set(getSelectedLabel() ?? '');
});
effect(selected, function setInputValue($selected) {
inputValue.set($selected?.label ?? '');
});

@@ -561,3 +536,3 @@ /**

input,
item,
option,
menu,

@@ -568,3 +543,3 @@ label,

open,
value,
selected,
inputValue: readonly(inputValue),

@@ -571,0 +546,0 @@ isEmpty: readonly(isEmpty),

@@ -7,3 +7,7 @@ import type { BuilderReturn } from '../../internal/types.js';

export type { ComboboxComponentEvents } from './events.js';
export type CreateComboboxProps<ItemValue> = {
export type ComboboxOption<Value> = {
value: Value;
label?: string;
};
export type CreateComboboxProps<Value> = {
/**

@@ -29,3 +33,3 @@ * Options for positioning the popover menu.

*/
filterFunction?: ComboboxFilterFunction<ItemValue>;
filterFunction?: ComboboxFilterFunction<Value>;
/**

@@ -58,26 +62,20 @@ * Whether or not the combobox should loop through the list when

/**
* The default value set on the select input.
* The default selected option.
*
* This will be overridden if you also pass a `value` store prop.
* This will be overridden if you also pass a `selected` store prop.
*
* @default undefined
*/
defaultValue?: ItemValue;
defaultSelected?: ComboboxOption<Value>;
/**
* An optional controlled store that manages the value state of the combobox.
* An optional controlled store that manages the selected option of the combobox.
*/
value?: Writable<ItemValue | undefined>;
selected?: Writable<ComboboxOption<Value> | undefined>;
/**
* A change handler for the value store called when the value would normally change.
* A change handler for the selected store called when the selected would normally change.
*
* @see https://melt-ui.com/docs/controlled#change-functions
*/
onValueChange?: ChangeFn<ItemValue | undefined>;
onSelectedChange?: ChangeFn<ComboboxOption<Value> | undefined>;
/**
* The default value for inputValue.
*
* @default undefined
*/
defaultInputValue?: string;
/**
* Whether or not to close the combobox menu when the user clicks

@@ -128,10 +126,4 @@ * outside of the combobox.

export type ComboboxFilterFunction<T> = (args: ComboboxFilterFunctionArgs<T>) => boolean;
export type ComboboxItemProps<T> = {
value: T;
export type ComboboxItemProps<Value> = ComboboxOption<Value> & {
/**
* By default, the textContent of the item will be used as the label.
* Use the `label` prop to override this behavior.
*/
label?: string;
/**
* Is the item disabled?

@@ -138,0 +130,0 @@ */

/// <reference types="svelte" />
import type { MeltActionReturn } from '../../internal/types.js';
import type { SelectEvents } from './events.js';
import type { CreateSelectProps, SelectOptionProps } from './types.js';
export declare function createSelect<Value extends Multiple extends true ? Array<unknown> : unknown = any, Multiple extends boolean = false>(props?: CreateSelectProps<Value, Multiple>): {
import type { CreateSelectProps, SelectOption, SelectOptionProps } from './types.js';
export declare function createSelect<Value = unknown, Multiple extends boolean = false, Selected extends Multiple extends true ? Array<SelectOption<Value>> : SelectOption<Value> = Multiple extends true ? Array<SelectOption<Value>> : SelectOption<Value>>(props?: CreateSelectProps<Value, Multiple, Selected>): {
elements: {

@@ -35,6 +35,6 @@ menu: import("../../internal/helpers/index.js").ExplicitBuilderReturn<[import("svelte/store").Readable<boolean>, import("svelte/store").Writable<string | HTMLElement | null | undefined>], (node: HTMLElement) => MeltActionReturn<SelectEvents['menu']>, ([$isVisible, $portal]: [boolean, string | HTMLElement | null | undefined]) => {

option: import("../../internal/helpers/index.js").ExplicitBuilderReturn<{
update: (updater: import("svelte/store").Updater<Value>, sideEffect?: ((newValue: Value) => void) | undefined) => void;
set: (this: void, value: Value) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Value>, invalidate?: import("svelte/store").Invalidator<Value> | undefined): import("svelte/store").Unsubscriber;
}, (node: HTMLElement) => MeltActionReturn<SelectEvents['option']>, ($value: Value) => (props: SelectOptionProps<Value>) => {
update: (updater: import("svelte/store").Updater<Selected | undefined>, sideEffect?: ((newValue: Selected | undefined) => void) | undefined) => void;
set: (this: void, value: Selected | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Selected | undefined>, invalidate?: import("svelte/store").Invalidator<Selected | undefined> | undefined): import("svelte/store").Unsubscriber;
}, (node: HTMLElement) => MeltActionReturn<SelectEvents['option']>, ($selected: Selected | undefined) => (props: SelectOptionProps<Value>) => {
readonly role: "option";

@@ -49,9 +49,9 @@ readonly 'aria-selected': boolean;

input: import("../../internal/helpers/index.js").ExplicitBuilderReturn<[{
update: (updater: import("svelte/store").Updater<Value>, sideEffect?: ((newValue: Value) => void) | undefined) => void;
set: (this: void, value: Value) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Value>, invalidate?: import("svelte/store").Invalidator<Value> | undefined): import("svelte/store").Unsubscriber;
}, import("svelte/store").Writable<boolean>, import("svelte/store").Writable<boolean>, import("svelte/store").Writable<string | undefined>], import("svelte/action").Action<any, any, Record<never, any>>, ([$value, $required, $disabled, $nameStore]: [Value, boolean, boolean, string | undefined]) => {
update: (updater: import("svelte/store").Updater<Selected | undefined>, sideEffect?: ((newValue: Selected | undefined) => void) | undefined) => void;
set: (this: void, value: Selected | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Selected | undefined>, invalidate?: import("svelte/store").Invalidator<Selected | undefined> | undefined): import("svelte/store").Unsubscriber;
}, import("svelte/store").Writable<boolean>, import("svelte/store").Writable<boolean>, import("svelte/store").Writable<string | undefined>], import("svelte/action").Action<any, any, Record<never, any>>, ([$value, $required, $disabled, $nameStore]: [Selected | undefined, boolean, boolean, string | undefined]) => {
type: string;
name: string | undefined;
value: Value;
value: Selected | undefined;
'aria-hidden': boolean;

@@ -92,11 +92,11 @@ hidden: boolean;

};
value: {
update: (updater: import("svelte/store").Updater<Value>, sideEffect?: ((newValue: Value) => void) | undefined) => void;
set: (this: void, value: Value) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Value>, invalidate?: import("svelte/store").Invalidator<Value> | undefined): import("svelte/store").Unsubscriber;
selected: {
update: (updater: import("svelte/store").Updater<Selected | undefined>, sideEffect?: ((newValue: Selected | undefined) => void) | undefined) => void;
set: (this: void, value: Selected | undefined) => void;
subscribe(this: void, run: import("svelte/store").Subscriber<Selected | undefined>, invalidate?: import("svelte/store").Invalidator<Selected | undefined> | undefined): import("svelte/store").Unsubscriber;
};
valueLabel: import("svelte/store").Readable<string | null>;
selectedLabel: import("svelte/store").Readable<string>;
};
helpers: {
isSelected: import("svelte/store").Readable<(value: unknown) => boolean>;
isSelected: import("svelte/store").Readable<(value: Value) => boolean>;
};

@@ -108,3 +108,2 @@ options: {

name: import("svelte/store").Writable<string | undefined>;
defaultValue?: import("svelte/store").Writable<Value | undefined> | undefined;
required: import("svelte/store").Writable<boolean>;

@@ -111,0 +110,0 @@ preventScroll: import("svelte/store").Writable<boolean>;

@@ -5,3 +5,3 @@ import { createLabel, createSeparator } from '../index.js';

import { onMount, tick } from 'svelte';
import { derived, get, readonly, writable } from 'svelte/store';
import { derived, get, writable } from 'svelte/store';
const defaults = {

@@ -28,14 +28,10 @@ arrowSize: 8,

const options = toWritableStores({
...omit(withDefaults, 'value', 'defaultValueLabel', 'onValueChange', 'onOpenChange', 'open', 'defaultOpen'),
...omit(withDefaults, 'selected', 'defaultSelected', 'onSelectedChange', 'onOpenChange', 'open', 'defaultOpen'),
multiple: withDefaults.multiple ?? false,
});
const { positioning, arrowSize, required, disabled, loop, preventScroll, name: nameStore, portal, forceVisible, closeOnEscape, closeOnOutsideClick, multiple, } = options;
let mounted = false;
const openWritable = withDefaults.open ?? writable(true);
const openWritable = withDefaults.open ?? writable(false);
const open = overridable(openWritable, withDefaults?.onOpenChange);
// Open so we can register the optionsList items before mounted = true
open.set(true);
const valueWritable = withDefaults.value ?? writable(withDefaults.defaultValue);
const value = overridable(valueWritable, withDefaults?.onValueChange);
const valueLabel = writable(withDefaults.defaultValueLabel ?? null);
const selectedWritable = withDefaults.selected ?? writable(withDefaults.defaultSelected);
const selected = overridable(selectedWritable, withDefaults?.onSelectedChange);
const activeTrigger = writable(null);

@@ -64,24 +60,109 @@ /**

const { typed, handleTypeaheadSearch } = createTypeaheadSearch();
onMount(() => {
// Run after all initial effects
tick().then(() => {
mounted = true;
});
open.set(withDefaults.defaultOpen);
if (!isBrowser)
/* ------- */
/* Helpers */
/* ------- */
const isSelected = derived([selected], ([$selected]) => {
return (value) => {
if (Array.isArray($selected)) {
return $selected.map((o) => o.value).includes(value);
}
return $selected === value;
};
});
function isMouse(e) {
return e.pointerType === 'mouse';
}
function getSelectedOption(menuElement) {
const selectedOption = menuElement.querySelector('[data-selected]');
return isHTMLElement(selectedOption) ? selectedOption : null;
}
function onOptionPointerMove(e) {
if (!isMouse(e))
return;
const menuEl = document.getElementById(ids.menu);
if (!menuEl)
const currentTarget = e.currentTarget;
if (!isHTMLElement(currentTarget))
return;
const triggerEl = document.getElementById(ids.trigger);
if (triggerEl) {
activeTrigger.set(triggerEl);
handleRovingFocus(currentTarget);
}
function onOptionLeave() {
const menuElement = document.getElementById(ids.menu);
if (!isHTMLElement(menuElement))
return;
handleRovingFocus(menuElement);
}
/**
* Keyboard event handler for menu navigation
* @param e The keyboard event
*/
function handleMenuNavigation(e) {
e.preventDefault();
// currently focused menu item
const currentFocusedItem = document.activeElement;
// menu element being navigated
const currentTarget = e.currentTarget;
if (!isHTMLElement(currentFocusedItem) || !isHTMLElement(currentTarget))
return;
// menu items of the current menu
const items = getOptions(currentTarget);
if (!items.length)
return;
// Disabled items can't be highlighted. Skip them.
const candidateNodes = items.filter((opt) => !isElementDisabled(opt));
// Get the index of the currently highlighted item.
const currentIndex = candidateNodes.indexOf(currentFocusedItem);
// Find the next menu item to highlight.
let nextItem;
const $loop = get(loop);
switch (e.key) {
case kbd.ARROW_DOWN:
nextItem = next(candidateNodes, currentIndex, $loop);
break;
case kbd.PAGE_DOWN:
nextItem = forward(candidateNodes, currentIndex, 10, $loop);
break;
case kbd.ARROW_UP:
nextItem = prev(candidateNodes, currentIndex, $loop);
break;
case kbd.PAGE_UP:
nextItem = back(candidateNodes, currentIndex, 10, $loop);
break;
case kbd.HOME:
nextItem = candidateNodes[0];
break;
case kbd.END:
nextItem = last(candidateNodes);
break;
default:
return;
}
const selectedEl = menuEl.querySelector('[data-selected]');
if (!isHTMLElement(selectedEl))
return;
const dataLabel = selectedEl.getAttribute('data-label');
valueLabel.set(dataLabel ?? selectedEl.textContent ?? null);
handleRovingFocus(nextItem);
}
function handleTabNavigation(e) {
if (e.shiftKey) {
const $prevFocusable = get(prevFocusable);
if ($prevFocusable) {
e.preventDefault();
$prevFocusable.focus();
prevFocusable.set(null);
}
}
else {
const $nextFocusable = get(nextFocusable);
if ($nextFocusable) {
e.preventDefault();
$nextFocusable.focus();
nextFocusable.set(null);
}
}
}
const isVisible = derivedVisible({ open, forceVisible, activeTrigger });
const selectedLabel = derived(selected, ($selected) => {
if (Array.isArray($selected)) {
return $selected.map((o) => o.label).join(', ');
}
return $selected?.label ?? '';
});
const isVisible = derivedVisible({ open, forceVisible, activeTrigger });
/* -------- */
/* Builders */
/* -------- */
const menu = builder(name('menu'), {

@@ -320,32 +401,19 @@ stores: [isVisible, portal],

};
const setValue = (newValue) => {
value.update(($value) => {
const setOption = (newOption) => {
selected.update(($option) => {
const $multiple = get(multiple);
if (Array.isArray($value) || ($value === undefined && $multiple)) {
return toggle(newValue, ($value ?? []));
if ($multiple) {
const optionArr = Array.isArray($option) ? $option : [];
return toggle(newOption, optionArr);
}
return newValue;
return newOption;
});
};
/**
* List of options fetched from SSR.
*/
const cachedOptionPropsArr = [];
const getOptionPropsArr = () => {
if (!isBrowser)
return cachedOptionPropsArr;
const menuEl = document.getElementById(ids.menu);
if (!menuEl)
return cachedOptionPropsArr;
const options = getOptions(menuEl);
return options.map(getOptionProps);
};
const option = builder(name('option'), {
stores: value,
returned: ($value) => {
stores: selected,
returned: ($selected) => {
return (props) => {
cachedOptionPropsArr.push(props);
const isSelected = Array.isArray($value)
? $value.includes(props?.value)
: $value === props?.value;
const isSelected = Array.isArray($selected)
? $selected.map((o) => o.value).includes(props.value)
: $selected?.value === props?.value;
return {

@@ -373,3 +441,3 @@ role: 'option',

handleRovingFocus(itemElement);
setValue(props.value);
setOption(props);
const $multiple = get(multiple);

@@ -389,3 +457,3 @@ if (!$multiple)

node.setAttribute('data-selected', '');
setValue(props.value);
setOption(props);
const $multiple = get(multiple);

@@ -431,16 +499,37 @@ if (!$multiple)

});
effect(value, function updateValueLabel($value) {
const optionPropsArr = getOptionPropsArr();
if (Array.isArray($value)) {
const labels = optionPropsArr.reduce((result, current) => {
if ($value.includes(current.value) && current.label) {
result.add(current.label);
}
return result;
}, new Set());
valueLabel.set(Array.from(labels).join(', '));
const input = builder(name('input'), {
stores: [selected, required, disabled, nameStore],
returned: ([$value, $required, $disabled, $nameStore]) => {
return {
type: 'hidden',
name: $nameStore,
value: $value,
'aria-hidden': true,
hidden: true,
tabIndex: -1,
required: $required,
disabled: $disabled,
style: styleToString({
position: 'absolute',
opacity: 0,
'pointer-events': 'none',
margin: 0,
transform: 'translateX(-100%)',
}),
};
},
});
/* ------------------- */
/* Lifecycle & Effects */
/* ------------------- */
onMount(() => {
const triggerEl = document.getElementById(ids.trigger);
if (triggerEl) {
activeTrigger.set(triggerEl);
}
else {
const newLabel = optionPropsArr.find((opt) => opt.value === $value)?.label;
valueLabel.set(newLabel ?? null);
});
let hasOpened = false;
effect(open, ($open) => {
if ($open) {
hasOpened = true;
}

@@ -455,3 +544,2 @@ });

}
const constantMounted = mounted;
sleep(1).then(() => {

@@ -476,3 +564,3 @@ const menuEl = document.getElementById(ids.menu);

}
else if ($activeTrigger && constantMounted && isUsingKeyboard) {
else if ($activeTrigger && hasOpened) {
// Hacky way to prevent the keydown event from triggering on the trigger

@@ -486,10 +574,2 @@ handleRovingFocus($activeTrigger);

});
const isSelected = derived([value], ([$value]) => {
return (value) => {
if (Array.isArray($value)) {
return $value.includes(value);
}
return $value === value;
};
});
effect([open, activeTrigger], ([$open, $activeTrigger]) => {

@@ -510,110 +590,2 @@ if (!isBrowser)

});
const input = builder(name('input'), {
stores: [value, required, disabled, nameStore],
returned: ([$value, $required, $disabled, $nameStore]) => {
return {
type: 'hidden',
name: $nameStore,
value: $value,
'aria-hidden': true,
hidden: true,
tabIndex: -1,
required: $required,
disabled: $disabled,
style: styleToString({
position: 'absolute',
opacity: 0,
'pointer-events': 'none',
margin: 0,
transform: 'translateX(-100%)',
}),
};
},
});
function isMouse(e) {
return e.pointerType === 'mouse';
}
function getSelectedOption(menuElement) {
const selectedOption = menuElement.querySelector('[data-selected]');
return isHTMLElement(selectedOption) ? selectedOption : null;
}
function onOptionPointerMove(e) {
if (!isMouse(e))
return;
const currentTarget = e.currentTarget;
if (!isHTMLElement(currentTarget))
return;
handleRovingFocus(currentTarget);
}
function onOptionLeave() {
const menuElement = document.getElementById(ids.menu);
if (!isHTMLElement(menuElement))
return;
handleRovingFocus(menuElement);
}
/**
* Keyboard event handler for menu navigation
* @param e The keyboard event
*/
function handleMenuNavigation(e) {
e.preventDefault();
// currently focused menu item
const currentFocusedItem = document.activeElement;
// menu element being navigated
const currentTarget = e.currentTarget;
if (!isHTMLElement(currentFocusedItem) || !isHTMLElement(currentTarget))
return;
// menu items of the current menu
const items = getOptions(currentTarget);
if (!items.length)
return;
// Disabled items can't be highlighted. Skip them.
const candidateNodes = items.filter((opt) => !isElementDisabled(opt));
// Get the index of the currently highlighted item.
const currentIndex = candidateNodes.indexOf(currentFocusedItem);
// Find the next menu item to highlight.
let nextItem;
const $loop = get(loop);
switch (e.key) {
case kbd.ARROW_DOWN:
nextItem = next(candidateNodes, currentIndex, $loop);
break;
case kbd.PAGE_DOWN:
nextItem = forward(candidateNodes, currentIndex, 10, $loop);
break;
case kbd.ARROW_UP:
nextItem = prev(candidateNodes, currentIndex, $loop);
break;
case kbd.PAGE_UP:
nextItem = back(candidateNodes, currentIndex, 10, $loop);
break;
case kbd.HOME:
nextItem = candidateNodes[0];
break;
case kbd.END:
nextItem = last(candidateNodes);
break;
default:
return;
}
handleRovingFocus(nextItem);
}
function handleTabNavigation(e) {
if (e.shiftKey) {
const $prevFocusable = get(prevFocusable);
if ($prevFocusable) {
e.preventDefault();
$prevFocusable.focus();
prevFocusable.set(null);
}
}
else {
const $nextFocusable = get(nextFocusable);
if ($nextFocusable) {
e.preventDefault();
$nextFocusable.focus();
nextFocusable.set(null);
}
}
}
return {

@@ -633,4 +605,4 @@ elements: {

open,
value,
valueLabel: readonly(valueLabel),
selected,
selectedLabel,
},

@@ -637,0 +609,0 @@ helpers: {

@@ -7,3 +7,7 @@ import type { FloatingConfig } from '../../internal/actions/index.js';

export type { SelectComponentEvents } from './events.js';
export type CreateSelectProps<Item extends Multiple extends true ? Array<unknown> : unknown = any, Multiple extends boolean = false> = {
export type SelectOption<Value> = {
value: Value;
label?: string;
};
export type CreateSelectProps<Value = unknown, Multiple extends boolean = false, Selected extends Multiple extends true ? Array<SelectOption<Value>> : SelectOption<Value> = Multiple extends true ? Array<SelectOption<Value>> : SelectOption<Value>> = {
/**

@@ -50,8 +54,2 @@ * Options for positioning the popover menu.

/**
* The label for the select input.
*
* @default undefined
*/
defaultValueLabel?: string;
/**
* The name for the select input.

@@ -108,7 +106,7 @@ *

*/
defaultValue?: Item;
defaultSelected?: Selected;
/**
* An optional controlled store that manages the value state of the combobox.
*/
value?: Writable<Item>;
selected?: Writable<Selected>;
/**

@@ -119,8 +117,6 @@ * A change handler for the value store called when the value would normally change.

*/
onValueChange?: ChangeFn<Item>;
onSelectedChange?: ChangeFn<Selected | undefined>;
multiple?: Multiple;
};
export type SelectOptionProps<Item = unknown> = {
value: Item;
label?: string;
export type SelectOptionProps<Value = unknown> = SelectOption<Value> & {
disabled?: boolean;

@@ -127,0 +123,0 @@ };

@@ -70,6 +70,6 @@ /// <reference types="svelte" />

update: import("svelte/store").Writable<import("./types.js").UpdateTag | undefined>;
selected?: import("svelte/store").Writable<Tag | undefined> | undefined;
placeholder: import("svelte/store").Writable<string>;
add: import("svelte/store").Writable<import("./types.js").AddTag | undefined>;
editable: import("svelte/store").Writable<boolean>;
selected?: import("svelte/store").Writable<Tag | undefined> | undefined;
defaultTags: import("svelte/store").Writable<string[] | Tag[]>;

@@ -76,0 +76,0 @@ onTagsChange?: import("svelte/store").Writable<import("../../internal/helpers/index.js").ChangeFn<Tag[]> | undefined> | undefined;

@@ -30,3 +30,2 @@ import { addEventListener, addMeltEventListener, builder, createElHelpers, derivedVisible, effect, executeCallbacks, generateId, getPortalDestination, isBrowser, isTouch, kbd, makeHullFromElements, noop, omit, overridable, pointInPolygon, styleToString, toWritableStores, } from '../../internal/helpers/index.js';

};
let timeout = null;
let clickedTrigger = false;

@@ -38,23 +37,31 @@ onMount(() => {

});
let openTimeout = null;
let closeTimeout = null;
function openTooltip() {
if (timeout) {
window.clearTimeout(timeout);
timeout = null;
if (closeTimeout) {
window.clearTimeout(closeTimeout);
closeTimeout = null;
}
timeout = window.setTimeout(() => {
open.set(true);
}, get(openDelay));
if (!openTimeout) {
openTimeout = window.setTimeout(() => {
open.set(true);
openTimeout = null;
}, get(openDelay));
}
}
function closeTooltip(isBlur) {
if (timeout) {
window.clearTimeout(timeout);
timeout = null;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
if (isBlur && isMouseInTooltipArea)
return;
timeout = window.setTimeout(() => {
open.set(false);
if (isBlur)
clickedTrigger = false;
}, get(closeDelay));
if (!closeTimeout) {
closeTimeout = window.setTimeout(() => {
open.set(false);
if (isBlur)
clickedTrigger = false;
closeTimeout = null;
}, get(closeDelay));
}
}

@@ -74,5 +81,5 @@ const trigger = builder(name('trigger'), {

clickedTrigger = true;
if (timeout) {
window.clearTimeout(timeout);
timeout = null;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}

@@ -86,5 +93,5 @@ }), addMeltEventListener(node, 'pointerenter', (e) => {

return;
if (timeout) {
window.clearTimeout(timeout);
timeout = null;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}

@@ -97,5 +104,5 @@ }), addMeltEventListener(node, 'focus', () => {

if (get(closeOnEscape) && e.key === kbd.ESCAPE) {
if (timeout) {
window.clearTimeout(timeout);
timeout = null;
if (openTimeout) {
window.clearTimeout(openTimeout);
openTimeout = null;
}

@@ -102,0 +109,0 @@ open.set(false);

@@ -0,1 +1,2 @@

import deepEqual from 'deep-equal';
/**

@@ -76,7 +77,10 @@ * Returns the element some number before the given index. If the target index is out of bounds:

export function toggle(item, array) {
if (array.includes(item)) {
return array.filter((i) => i !== item);
const itemIdx = array.findIndex((i) => deepEqual(i, item));
if (itemIdx !== -1) {
array.splice(itemIdx, 1);
}
array.push(item);
else {
array.push(item);
}
return array;
}
{
"name": "@melt-ui/svelte",
"version": "0.38.1",
"version": "0.39.0",
"license": "MIT",

@@ -5,0 +5,0 @@ "exports": {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc