@melt-ui/svelte
Advanced tools
Comparing version 0.26.3 to 0.27.0
@@ -61,3 +61,4 @@ /// <reference types="svelte" /> | ||
id: string; | ||
for: string; | ||
}, string>; | ||
}; |
@@ -338,2 +338,3 @@ import { usePopper } from '../../internal/actions'; | ||
id: ids.label, | ||
for: ids.input, | ||
}; | ||
@@ -340,0 +341,0 @@ }, |
/// <reference types="svelte" /> | ||
import type { CreateSelectProps, SelectOptionProps } from './types'; | ||
export declare function createSelect(props?: CreateSelectProps): { | ||
options: import("svelte/store").Writable<Omit<CreateSelectProps, "label" | "value">>; | ||
options: import("svelte/store").Writable<Omit<CreateSelectProps, "value" | "valueLabel">>; | ||
open: import("svelte/store").Writable<boolean>; | ||
isSelected: import("svelte/store").Readable<(value: unknown) => boolean>; | ||
value: import("svelte/store").Writable<unknown>; | ||
trigger: import("../../internal/helpers").ExplicitBuilderReturn<[import("svelte/store").Writable<boolean>, import("svelte/store").Writable<Omit<CreateSelectProps, "label" | "value">>], (node: HTMLElement) => { | ||
trigger: import("../../internal/helpers").ExplicitBuilderReturn<[import("svelte/store").Writable<boolean>, import("svelte/store").Writable<Omit<CreateSelectProps, "value" | "valueLabel">>], (node: HTMLElement) => { | ||
destroy: () => void; | ||
}, ([$open, $options]: [boolean, Omit<CreateSelectProps, "label" | "value">]) => { | ||
}, ([$open, $options]: [boolean, Omit<CreateSelectProps, "value" | "valueLabel">]) => { | ||
readonly role: "combobox"; | ||
@@ -18,2 +18,3 @@ readonly 'aria-autocomplete': "none"; | ||
readonly 'data-disabled': true | undefined; | ||
readonly 'aria-labelledby': string; | ||
readonly disabled: boolean | undefined; | ||
@@ -43,3 +44,3 @@ readonly id: string; | ||
}, string>; | ||
input: import("../../internal/helpers").ExplicitBuilderReturn<[import("svelte/store").Writable<unknown>, import("svelte/store").Writable<Omit<CreateSelectProps, "label" | "value">>], import("svelte/action").Action<any, any, Record<never, any>>, ([$value, $options]: [unknown, Omit<CreateSelectProps, "label" | "value">]) => { | ||
input: import("../../internal/helpers").ExplicitBuilderReturn<[import("svelte/store").Writable<unknown>, import("svelte/store").Writable<Omit<CreateSelectProps, "value" | "valueLabel">>], import("svelte/action").Action<any, any, Record<never, any>>, ([$value, $options]: [unknown, Omit<CreateSelectProps, "value" | "valueLabel">]) => { | ||
type: string; | ||
@@ -55,3 +56,3 @@ name: string | undefined; | ||
}, string>; | ||
label: import("svelte/store").Writable<string | number | null>; | ||
valueLabel: import("svelte/store").Writable<string | number | null>; | ||
separator: import("../../internal/helpers").ExplicitBuilderReturn<import("svelte/store").Writable<{ | ||
@@ -76,6 +77,12 @@ orientation: import("../../internal/types").Orientation; | ||
}, string>; | ||
arrow: import("../../internal/helpers").ExplicitBuilderReturn<import("svelte/store").Writable<Omit<CreateSelectProps, "label" | "value">>, import("svelte/action").Action<any, any, Record<never, any>>, ($options: Omit<CreateSelectProps, "label" | "value">) => { | ||
arrow: import("../../internal/helpers").ExplicitBuilderReturn<import("svelte/store").Writable<Omit<CreateSelectProps, "value" | "valueLabel">>, import("svelte/action").Action<any, any, Record<never, any>>, ($options: Omit<CreateSelectProps, "value" | "valueLabel">) => { | ||
'data-arrow': boolean; | ||
style: string; | ||
}, string>; | ||
label: import("../../internal/helpers").ExplicitBuilderReturn<(import("svelte/store").Readable<any> | import("svelte/store").Readable<any>[] | [import("svelte/store").Readable<any>, ...import("svelte/store").Readable<any>[]]) | undefined, <Node_2 extends any>(node: Node_2) => { | ||
destroy: () => void; | ||
}, () => { | ||
id: string; | ||
for: string; | ||
}, string>; | ||
}; |
@@ -8,2 +8,4 @@ import { usePopper } from '../../internal/actions/popper'; | ||
import { createSeparator } from '../separator'; | ||
import { usePortal } from '../../internal/actions'; | ||
import { createLabel } from '../label'; | ||
const defaults = { | ||
@@ -20,9 +22,9 @@ arrowSize: 8, | ||
}; | ||
const { name } = createElHelpers('select'); | ||
const { name, selector } = createElHelpers('select'); | ||
export function createSelect(props) { | ||
const withDefaults = { ...defaults, ...props }; | ||
const options = writable(omit(withDefaults, 'value', 'label')); | ||
const options = writable(omit(withDefaults, 'value', 'valueLabel')); | ||
const open = writable(false); | ||
const value = writable(withDefaults.value ?? null); | ||
const label = writable(withDefaults.label ?? null); | ||
const valueLabel = writable(withDefaults.valueLabel ?? null); | ||
const activeTrigger = writable(null); | ||
@@ -48,2 +50,3 @@ /** | ||
trigger: generateId(), | ||
label: generateId(), | ||
}; | ||
@@ -60,3 +63,3 @@ onMount(() => { | ||
const dataLabel = selectedEl.getAttribute('data-label'); | ||
label.set(dataLabel ?? selectedEl.textContent ?? null); | ||
valueLabel.set(dataLabel ?? selectedEl.textContent ?? null); | ||
}); | ||
@@ -87,2 +90,5 @@ const menu = builder(name('menu'), { | ||
floating: $options.positioning, | ||
// We want portal to always be true for the menu. | ||
// So we do it outside popper instead. | ||
portal: null, | ||
}, | ||
@@ -124,2 +130,3 @@ }); | ||
})); | ||
const unsubPortal = usePortal(node, 'body')?.destroy; | ||
return { | ||
@@ -129,2 +136,3 @@ destroy() { | ||
unsubPopper(); | ||
unsubPortal?.(); | ||
unsubEventListeners(); | ||
@@ -146,2 +154,3 @@ }, | ||
'data-disabled': $options.disabled ? true : undefined, | ||
'aria-labelledby': ids.label, | ||
disabled: $options.disabled, | ||
@@ -224,2 +233,25 @@ id: ids.trigger, | ||
}); | ||
// Use our existing label builder to create a label for the select trigger. | ||
const labelBuilder = createLabel(); | ||
const { action: labelAction } = get(labelBuilder); | ||
const label = builder(name('label'), { | ||
returned: () => { | ||
return { | ||
id: ids.label, | ||
for: ids.trigger, | ||
}; | ||
}, | ||
action: (node) => { | ||
const destroy = executeCallbacks(labelAction(node)?.destroy, addEventListener(node, 'click', (e) => { | ||
e.preventDefault(); | ||
const triggerEl = document.getElementById(ids.trigger); | ||
if (!isHTMLElement(triggerEl)) | ||
return; | ||
triggerEl.focus(); | ||
})); | ||
return { | ||
destroy, | ||
}; | ||
}, | ||
}); | ||
const { root: separator } = createSeparator({ | ||
@@ -254,2 +286,12 @@ decorative: true, | ||
}); | ||
const getOptionProps = (el) => { | ||
const value = el.getAttribute('data-value'); | ||
const label = el.getAttribute('data-label'); | ||
const disabled = el.hasAttribute('data-disabled'); | ||
return { | ||
value, | ||
label: label ?? el.textContent ?? null, | ||
disabled: disabled ? true : false, | ||
}; | ||
}; | ||
const option = builder(name('option'), { | ||
@@ -271,23 +313,7 @@ stores: value, | ||
action: (node) => { | ||
const getElprops = () => { | ||
const value = node.getAttribute('data-value'); | ||
const label = node.getAttribute('data-label'); | ||
const disabled = node.hasAttribute('data-disabled'); | ||
return { | ||
value, | ||
label: label ?? node.textContent ?? null, | ||
disabled: disabled ? true : false, | ||
}; | ||
}; | ||
const unsub = executeCallbacks(addEventListener(node, 'pointerdown', (e) => { | ||
const props = getElprops(); | ||
if (props.disabled) { | ||
e.preventDefault(); | ||
return; | ||
} | ||
}), addEventListener(node, 'click', (e) => { | ||
const unsub = executeCallbacks(addEventListener(node, 'click', (e) => { | ||
const itemElement = e.currentTarget; | ||
if (!isHTMLElement(itemElement)) | ||
return; | ||
const props = getElprops(); | ||
const props = getOptionProps(node); | ||
if (props.disabled) { | ||
@@ -299,3 +325,2 @@ e.preventDefault(); | ||
value.set(props.value); | ||
label.set(props.label); | ||
open.set(false); | ||
@@ -311,10 +336,9 @@ }), addEventListener(node, 'keydown', (e) => { | ||
e.preventDefault(); | ||
const props = getElprops(); | ||
const props = getOptionProps(node); | ||
node.setAttribute('data-selected', ''); | ||
value.set(props.value); | ||
label.set(props.label); | ||
open.set(false); | ||
} | ||
}), addEventListener(node, 'pointermove', (e) => { | ||
const props = getElprops(); | ||
const props = getOptionProps(node); | ||
if (props.disabled) { | ||
@@ -354,2 +378,14 @@ e.preventDefault(); | ||
}); | ||
effect(value, ($value) => { | ||
if (!isBrowser) | ||
return; | ||
const menuEl = document.getElementById(ids.menu); | ||
if (!menuEl) | ||
return; | ||
const optionEl = menuEl.querySelector(`${selector('option')}[data-value="${$value}"]`); | ||
if (!isHTMLElement(optionEl)) | ||
return; | ||
const props = getOptionProps(optionEl); | ||
valueLabel.set(props.label ?? null); | ||
}); | ||
const { typed, handleTypeaheadSearch } = createTypeaheadSearch(); | ||
@@ -540,3 +576,3 @@ effect([open, activeTrigger], ([$open, $activeTrigger]) => { | ||
input, | ||
label, | ||
valueLabel, | ||
separator, | ||
@@ -546,3 +582,4 @@ group, | ||
arrow, | ||
label, | ||
}; | ||
} |
@@ -9,3 +9,3 @@ import type { FloatingConfig } from '../../internal/actions'; | ||
value?: unknown; | ||
label?: string; | ||
valueLabel?: string; | ||
name?: string; | ||
@@ -12,0 +12,0 @@ preventScroll?: boolean; |
{ | ||
"name": "@melt-ui/svelte", | ||
"version": "0.26.3", | ||
"version": "0.27.0", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "exports": { |
421003
10343