This commit is contained in:
eewing
2026-02-17 14:10:16 -06:00
parent 2bca5834c5
commit cf73cd3b4c
11246 changed files with 1690552 additions and 0 deletions
@@ -0,0 +1,84 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectContentStaticProps } from "../types.js";
import { SelectContentState } from "../select.svelte.js";
import PopperLayer from "../../utilities/popper-layer/popper-layer.svelte";
import { createId } from "../../../internal/create-id.js";
import { noop } from "../../../internal/noop.js";
import PopperLayerForceMount from "../../utilities/popper-layer/popper-layer-force-mount.svelte";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
forceMount = false,
onInteractOutside = noop,
onEscapeKeydown = noop,
children,
child,
preventScroll = false,
style,
...restProps
}: SelectContentStaticProps = $props();
const contentState = SelectContentState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
onInteractOutside: boxWith(() => onInteractOutside),
onEscapeKeydown: boxWith(() => onEscapeKeydown),
});
const mergedProps = $derived(mergeProps(restProps, contentState.props));
</script>
{#if forceMount}
<PopperLayerForceMount
{...mergedProps}
{...contentState.popperProps}
ref={contentState.opts.ref}
isStatic
enabled={contentState.root.opts.open.current}
{id}
{preventScroll}
forceMount={true}
shouldRender={contentState.shouldRender}
>
{#snippet popper({ props })}
{@const finalProps = mergeProps(props, { style: contentState.props.style }, { style })}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
</div>
{/if}
{/snippet}
</PopperLayerForceMount>
{:else if !forceMount}
<PopperLayer
{...mergedProps}
{...contentState.popperProps}
ref={contentState.opts.ref}
isStatic
open={contentState.root.opts.open.current}
{id}
{preventScroll}
forceMount={false}
shouldRender={contentState.shouldRender}
>
{#snippet popper({ props })}
{@const finalProps = mergeProps(props, { style: contentState.props.style }, { style })}
{#if child}
{@render child({ props: finalProps, ...contentState.snippetProps })}
{:else}
<div {...finalProps}>
{@render children?.()}
</div>
{/if}
{/snippet}
</PopperLayer>
{/if}
@@ -0,0 +1,4 @@
import type { SelectContentStaticProps } from "../types.js";
declare const SelectContentStatic: import("svelte").Component<SelectContentStaticProps, {}, "ref">;
type SelectContentStatic = ReturnType<typeof SelectContentStatic>;
export default SelectContentStatic;
+89
View File
@@ -0,0 +1,89 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectContentProps } from "../types.js";
import { SelectContentState } from "../select.svelte.js";
import PopperLayer from "../../utilities/popper-layer/popper-layer.svelte";
import { noop } from "../../../internal/noop.js";
import PopperLayerForceMount from "../../utilities/popper-layer/popper-layer-force-mount.svelte";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
forceMount = false,
side = "bottom",
onInteractOutside = noop,
onEscapeKeydown = noop,
children,
child,
preventScroll = false,
style,
...restProps
}: SelectContentProps = $props();
const contentState = SelectContentState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
onInteractOutside: boxWith(() => onInteractOutside),
onEscapeKeydown: boxWith(() => onEscapeKeydown),
});
const mergedProps = $derived(mergeProps(restProps, contentState.props));
</script>
{#if forceMount}
<PopperLayerForceMount
{...mergedProps}
{...contentState.popperProps}
ref={contentState.opts.ref}
{side}
enabled={contentState.root.opts.open.current}
{id}
{preventScroll}
forceMount={true}
shouldRender={contentState.shouldRender}
>
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, { style: contentState.props.style }, { style })}
{#if child}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
{/snippet}
</PopperLayerForceMount>
{:else if !forceMount}
<PopperLayer
{...mergedProps}
{...contentState.popperProps}
ref={contentState.opts.ref}
{side}
open={contentState.root.opts.open.current}
{id}
{preventScroll}
forceMount={false}
shouldRender={contentState.shouldRender}
>
{#snippet popper({ props, wrapperProps })}
{@const finalProps = mergeProps(props, { style: contentState.props.style }, { style })}
{#if child}
{@render child({ props: finalProps, wrapperProps, ...contentState.snippetProps })}
{:else}
<div {...wrapperProps}>
<div {...finalProps}>
{@render children?.()}
</div>
</div>
{/if}
{/snippet}
</PopperLayer>
{/if}
@@ -0,0 +1,4 @@
import type { SelectContentProps } from "../types.js";
declare const SelectContent: import("svelte").Component<SelectContentProps, {}, "ref">;
type SelectContent = ReturnType<typeof SelectContent>;
export default SelectContent;
@@ -0,0 +1,33 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectGroupHeadingProps } from "../types.js";
import { SelectGroupHeadingState } from "../select.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
child,
children,
...restProps
}: SelectGroupHeadingProps = $props();
const groupHeadingState = SelectGroupHeadingState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, groupHeadingState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { SelectGroupHeadingProps } from "../types.js";
declare const SelectGroupHeading: import("svelte").Component<SelectGroupHeadingProps, {}, "ref">;
type SelectGroupHeading = ReturnType<typeof SelectGroupHeading>;
export default SelectGroupHeading;
+34
View File
@@ -0,0 +1,34 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { SelectGroupState } from "../select.svelte.js";
import type { SelectGroupProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
children,
child,
...restProps
}: SelectGroupProps = $props();
const groupState = SelectGroupState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, groupState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,4 @@
import type { SelectGroupProps } from "../types.js";
declare const SelectGroup: import("svelte").Component<SelectGroupProps, {}, "ref">;
type SelectGroup = ReturnType<typeof SelectGroup>;
export default SelectGroup;
@@ -0,0 +1,19 @@
<script lang="ts">
import { boxWith } from "svelte-toolbelt";
import { SelectHiddenInputState } from "../select.svelte.js";
import type { HTMLInputAttributes } from "svelte/elements";
import HiddenInput from "../../utilities/hidden-input.svelte";
let {
value = $bindable(),
autocomplete,
}: { value?: string } & Omit<HTMLInputAttributes, "value"> = $props();
const hiddenInputState = SelectHiddenInputState.create({
value: boxWith(() => value),
});
</script>
{#if hiddenInputState.shouldRender}
<HiddenInput {...hiddenInputState.props} bind:value {autocomplete} />
{/if}
@@ -0,0 +1,7 @@
import type { HTMLInputAttributes } from "svelte/elements";
type $$ComponentProps = {
value?: string;
} & Omit<HTMLInputAttributes, "value">;
declare const SelectHiddenInput: import("svelte").Component<$$ComponentProps, {}, "value">;
type SelectHiddenInput = ReturnType<typeof SelectHiddenInput>;
export default SelectHiddenInput;
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { SelectItemState } from "../select.svelte.js";
import type { SelectItemProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
import { noop } from "../../../internal/noop.js";
import Mounted from "../../utilities/mounted.svelte";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
value,
label = value,
disabled = false,
children,
child,
onHighlight = noop,
onUnhighlight = noop,
...restProps
}: SelectItemProps = $props();
const itemState = SelectItemState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
value: boxWith(() => value),
disabled: boxWith(() => disabled),
label: boxWith(() => label),
onHighlight: boxWith(() => onHighlight),
onUnhighlight: boxWith(() => onUnhighlight),
});
const mergedProps = $derived(mergeProps(restProps, itemState.props));
</script>
{#if child}
{@render child({ props: mergedProps, ...itemState.snippetProps })}
{:else}
<div {...mergedProps}>
{@render children?.(itemState.snippetProps)}
</div>
{/if}
<Mounted bind:mounted={itemState.mounted} />
@@ -0,0 +1,4 @@
import type { SelectItemProps } from "../types.js";
declare const SelectItem: import("svelte").Component<SelectItemProps, {}, "ref">;
type SelectItem = ReturnType<typeof SelectItem>;
export default SelectItem;
@@ -0,0 +1,40 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectScrollDownButtonProps } from "../types.js";
import { SelectScrollDownButtonState } from "../select.svelte.js";
import { createId } from "../../../internal/create-id.js";
import { Mounted } from "../../utilities/index.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
delay = () => 50,
child,
children,
...restProps
}: SelectScrollDownButtonProps = $props();
const scrollButtonState = SelectScrollDownButtonState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
delay: boxWith(() => delay),
});
const mergedProps = $derived(mergeProps(restProps, scrollButtonState.props));
</script>
{#if scrollButtonState.canScrollDown}
<Mounted bind:mounted={scrollButtonState.scrollButtonState.mounted} />
{#if child}
{@render child({ props: restProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
{/if}
@@ -0,0 +1,4 @@
import type { SelectScrollDownButtonProps } from "../types.js";
declare const SelectScrollDownButton: import("svelte").Component<SelectScrollDownButtonProps, {}, "ref">;
type SelectScrollDownButton = ReturnType<typeof SelectScrollDownButton>;
export default SelectScrollDownButton;
@@ -0,0 +1,40 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectScrollUpButtonProps } from "../types.js";
import { SelectScrollUpButtonState } from "../select.svelte.js";
import { createId } from "../../../internal/create-id.js";
import { Mounted } from "../../utilities/index.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
delay = () => 50,
child,
children,
...restProps
}: SelectScrollUpButtonProps = $props();
const scrollButtonState = SelectScrollUpButtonState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
delay: boxWith(() => delay),
});
const mergedProps = $derived(mergeProps(restProps, scrollButtonState.props));
</script>
{#if scrollButtonState.canScrollUp}
<Mounted bind:mounted={scrollButtonState.scrollButtonState.mounted} />
{#if child}
{@render child({ props: restProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
{/if}
@@ -0,0 +1,4 @@
import type { SelectScrollUpButtonProps } from "../types.js";
declare const SelectScrollUpButton: import("svelte").Component<SelectScrollUpButtonProps, {}, "ref">;
type SelectScrollUpButton = ReturnType<typeof SelectScrollUpButton>;
export default SelectScrollUpButton;
+38
View File
@@ -0,0 +1,38 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import { SelectTriggerState } from "../select.svelte.js";
import type { SelectTriggerProps } from "../types.js";
import { createId } from "../../../internal/create-id.js";
import { FloatingLayer } from "../../utilities/floating-layer/index.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
child,
children,
type = "button",
...restProps
}: SelectTriggerProps = $props();
const triggerState = SelectTriggerState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, triggerState.props, { type }));
</script>
<FloatingLayer.Anchor {id} ref={triggerState.opts.ref}>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<button {...mergedProps}>
{@render children?.()}
</button>
{/if}
</FloatingLayer.Anchor>
@@ -0,0 +1,4 @@
import type { SelectTriggerProps } from "../types.js";
declare const SelectTrigger: import("svelte").Component<SelectTriggerProps, {}, "ref">;
type SelectTrigger = ReturnType<typeof SelectTrigger>;
export default SelectTrigger;
@@ -0,0 +1,56 @@
<script lang="ts">
import { boxWith, mergeProps } from "svelte-toolbelt";
import type { SelectViewportProps } from "../types.js";
import { SelectViewportState } from "../select.svelte.js";
import { createId } from "../../../internal/create-id.js";
const uid = $props.id();
let {
id = createId(uid),
ref = $bindable(null),
children,
child,
...restProps
}: SelectViewportProps = $props();
const viewportState = SelectViewportState.create({
id: boxWith(() => id),
ref: boxWith(
() => ref,
(v) => (ref = v)
),
});
const mergedProps = $derived(mergeProps(restProps, viewportState.props));
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div {...mergedProps}>
{@render children?.()}
</div>
{/if}
<style>
/* Hide scrollbars cross browser and enable momentum scroll for touch devices */
:global([data-select-viewport]) {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
-webkit-overflow-scrolling: touch !important;
}
:global([data-combobox-viewport]) {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
-webkit-overflow-scrolling: touch !important;
}
:global([data-combobox-viewport])::-webkit-scrollbar {
display: none !important;
}
:global([data-select-viewport])::-webkit-scrollbar {
display: none !important;
}
</style>
@@ -0,0 +1,4 @@
import type { SelectViewportProps } from "../types.js";
declare const SelectViewport: import("svelte").Component<SelectViewportProps, {}, "ref">;
type SelectViewport = ReturnType<typeof SelectViewport>;
export default SelectViewport;
+92
View File
@@ -0,0 +1,92 @@
<script lang="ts">
import FloatingLayer from "../../utilities/floating-layer/components/floating-layer.svelte";
import { noop } from "../../../internal/noop.js";
import { type WritableBox, boxWith } from "svelte-toolbelt";
import { SelectRootState } from "../select.svelte.js";
import type { SelectRootProps } from "../types.js";
import SelectHiddenInput from "./select-hidden-input.svelte";
import { watch } from "runed";
let {
value = $bindable(),
onValueChange = noop,
name = "",
disabled = false,
type,
open = $bindable(false),
onOpenChange = noop,
onOpenChangeComplete = noop,
loop = false,
scrollAlignment = "nearest",
required = false,
items = [],
allowDeselect = false,
autocomplete,
children,
}: SelectRootProps = $props();
function handleDefaultValue() {
if (value !== undefined) return;
value = type === "single" ? "" : [];
}
// SSR
handleDefaultValue();
watch.pre(
() => value,
() => {
handleDefaultValue();
}
);
let inputValue = $state("");
const rootState = SelectRootState.create({
type,
value: boxWith(
() => value!,
(v) => {
value = v;
// oxlint-disable-next-line no-explicit-any
onValueChange(v as any);
}
) as WritableBox<string> | WritableBox<string[]>,
disabled: boxWith(() => disabled),
required: boxWith(() => required),
open: boxWith(
() => open,
(v) => {
open = v;
onOpenChange(v);
}
),
loop: boxWith(() => loop),
scrollAlignment: boxWith(() => scrollAlignment),
name: boxWith(() => name),
isCombobox: false,
items: boxWith(() => items),
allowDeselect: boxWith(() => allowDeselect),
inputValue: boxWith(
() => inputValue,
(v) => (inputValue = v)
),
onOpenChangeComplete: boxWith(() => onOpenChangeComplete),
});
</script>
<FloatingLayer>
{@render children?.()}
</FloatingLayer>
{#if Array.isArray(rootState.opts.value.current)}
{#if rootState.opts.value.current.length === 0}
<SelectHiddenInput {autocomplete} />
{:else}
{#each rootState.opts.value.current as item (item)}
<SelectHiddenInput value={item} {autocomplete} />
{/each}
{/if}
{:else}
<SelectHiddenInput bind:value={rootState.opts.value.current as string} {autocomplete} />
{/if}
+3
View File
@@ -0,0 +1,3 @@
declare const Select: import("svelte").Component<import("../types.js").SelectRootPropsWithoutHTML, {}, "value" | "open">;
type Select = ReturnType<typeof Select>;
export default Select;
+12
View File
@@ -0,0 +1,12 @@
export { default as Root } from "./components/select.svelte";
export { default as Content } from "./components/select-content.svelte";
export { default as ContentStatic } from "./components/select-content-static.svelte";
export { default as Item } from "./components/select-item.svelte";
export { default as Group } from "./components/select-group.svelte";
export { default as GroupHeading } from "./components/select-group-heading.svelte";
export { default as Trigger } from "./components/select-trigger.svelte";
export { default as Portal } from "../utilities/portal/portal.svelte";
export { default as Viewport } from "./components/select-viewport.svelte";
export { default as ScrollUpButton } from "./components/select-scroll-up-button.svelte";
export { default as ScrollDownButton } from "./components/select-scroll-down-button.svelte";
export type { SelectRootProps as RootProps, SelectContentProps as ContentProps, SelectContentStaticProps as ContentStaticProps, SelectItemProps as ItemProps, SelectGroupProps as GroupProps, SelectGroupHeadingProps as GroupHeadingProps, SelectTriggerProps as TriggerProps, SelectViewportProps as ViewportProps, SelectScrollUpButtonProps as ScrollUpButtonProps, SelectScrollDownButtonProps as ScrollDownButtonProps, SelectPortalProps as PortalProps, } from "./types.js";
+11
View File
@@ -0,0 +1,11 @@
export { default as Root } from "./components/select.svelte";
export { default as Content } from "./components/select-content.svelte";
export { default as ContentStatic } from "./components/select-content-static.svelte";
export { default as Item } from "./components/select-item.svelte";
export { default as Group } from "./components/select-group.svelte";
export { default as GroupHeading } from "./components/select-group-heading.svelte";
export { default as Trigger } from "./components/select-trigger.svelte";
export { default as Portal } from "../utilities/portal/portal.svelte";
export { default as Viewport } from "./components/select-viewport.svelte";
export { default as ScrollUpButton } from "./components/select-scroll-up-button.svelte";
export { default as ScrollDownButton } from "./components/select-scroll-down-button.svelte";
+1
View File
@@ -0,0 +1 @@
export * as Select from "./exports.js";
+1
View File
@@ -0,0 +1 @@
export * as Select from "./exports.js";
+428
View File
@@ -0,0 +1,428 @@
import { Previous } from "runed";
import { DOMContext, type ReadableBoxedValues, type WritableBoxedValues, type Box } from "svelte-toolbelt";
import type { BitsEvent, BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, OnChangeFn, WithRefOpts, RefAttachment } from "../../internal/types.js";
import { PresenceManager } from "../../internal/presence-manager.svelte.js";
export declare const INTERACTION_KEYS: string[];
export declare const FIRST_KEYS: string[];
export declare const LAST_KEYS: string[];
export declare const FIRST_LAST_KEYS: string[];
export declare const SELECTION_KEYS: string[];
export declare const CONTENT_MARGIN = 10;
declare const selectAttrs: import("../../internal/attrs.js").CreateBitsAttrsReturn<readonly ["trigger", "content", "item", "viewport", "scroll-up-button", "scroll-down-button", "group", "group-label", "separator", "arrow", "input", "content-wrapper", "item-text", "value"]>;
interface SelectBaseRootStateOpts extends ReadableBoxedValues<{
disabled: boolean;
required: boolean;
name: string;
loop: boolean;
scrollAlignment: "nearest" | "center";
items: {
value: string;
label: string;
disabled?: boolean;
}[];
allowDeselect: boolean;
onOpenChangeComplete: OnChangeFn<boolean>;
}>, WritableBoxedValues<{
open: boolean;
inputValue: string;
}> {
isCombobox: boolean;
}
declare abstract class SelectBaseRootState {
readonly opts: SelectBaseRootStateOpts;
touchedInput: boolean;
inputNode: HTMLElement | null;
contentNode: HTMLElement | null;
contentPresence: PresenceManager;
viewportNode: HTMLElement | null;
triggerNode: HTMLElement | null;
valueId: string;
highlightedNode: HTMLElement | null;
readonly highlightedValue: string | null;
readonly highlightedId: string | undefined;
readonly highlightedLabel: string | null;
isUsingKeyboard: boolean;
isCombobox: boolean;
domContext: DOMContext;
constructor(opts: SelectBaseRootStateOpts);
setHighlightedNode(node: HTMLElement | null, initial?: boolean): void;
getCandidateNodes(): HTMLElement[];
setHighlightedToFirstCandidate(initial?: boolean): void;
getNodeByValue(value: string): HTMLElement | null;
setOpen(open: boolean): void;
toggleOpen(): void;
handleOpen(): void;
handleClose(): void;
toggleMenu(): void;
getBitsAttr: typeof selectAttrs.getAttr;
}
interface SelectSingleRootStateOpts extends SelectBaseRootStateOpts, WritableBoxedValues<{
value: string;
}> {
}
export declare class SelectSingleRootState extends SelectBaseRootState {
readonly opts: SelectSingleRootStateOpts;
readonly isMulti: false;
readonly hasValue: boolean;
readonly currentLabel: string;
readonly candidateLabels: string[];
readonly dataTypeaheadEnabled: boolean;
constructor(opts: SelectSingleRootStateOpts);
includesItem(itemValue: string): boolean;
toggleItem(itemValue: string, itemLabel?: string): void;
setInitialHighlightedNode(): void;
}
interface SelectMultipleRootStateOpts extends SelectBaseRootStateOpts, WritableBoxedValues<{
value: string[];
}> {
}
declare class SelectMultipleRootState extends SelectBaseRootState {
readonly opts: SelectMultipleRootStateOpts;
readonly isMulti: true;
readonly hasValue: boolean;
constructor(opts: SelectMultipleRootStateOpts);
includesItem(itemValue: string): boolean;
toggleItem(itemValue: string, itemLabel?: string): void;
setInitialHighlightedNode(): void;
}
interface SelectRootStateOpts extends ReadableBoxedValues<{
disabled: boolean;
required: boolean;
loop: boolean;
scrollAlignment: "nearest" | "center";
name: string;
items: {
value: string;
label: string;
disabled?: boolean;
}[];
allowDeselect: boolean;
onOpenChangeComplete: OnChangeFn<boolean>;
}>, WritableBoxedValues<{
open: boolean;
inputValue: string;
}> {
isCombobox: boolean;
type: "single" | "multiple";
value: Box<string> | Box<string[]>;
}
export declare class SelectRootState {
static create(props: SelectRootStateOpts): SelectRoot;
}
type SelectRoot = SelectSingleRootState | SelectMultipleRootState;
interface SelectInputStateOpts extends WithRefOpts, ReadableBoxedValues<{
clearOnDeselect: boolean;
}> {
}
export declare class SelectInputState {
static create(opts: SelectInputStateOpts): SelectInputState;
readonly opts: SelectInputStateOpts;
readonly root: SelectRoot;
readonly attachment: RefAttachment;
constructor(opts: SelectInputStateOpts, root: SelectRoot);
onkeydown(e: BitsKeyboardEvent): void;
oninput(e: BitsEvent<Event, HTMLInputElement>): void;
readonly props: {
readonly id: string;
readonly role: "combobox";
readonly disabled: true | undefined;
readonly "aria-activedescendant": string | undefined;
readonly "aria-autocomplete": "list";
readonly "aria-expanded": "true" | "false";
readonly "data-state": "open" | "closed";
readonly "data-disabled": "" | undefined;
readonly onkeydown: (e: BitsKeyboardEvent) => void;
readonly oninput: (e: BitsEvent<Event, HTMLInputElement>) => void;
};
}
interface SelectComboTriggerStateOpts extends WithRefOpts {
}
export declare class SelectComboTriggerState {
static create(opts: SelectComboTriggerStateOpts): SelectComboTriggerState;
readonly opts: SelectComboTriggerStateOpts;
readonly root: SelectBaseRootState;
readonly attachment: RefAttachment;
constructor(opts: SelectComboTriggerStateOpts, root: SelectBaseRootState);
onkeydown(e: BitsKeyboardEvent): void;
/**
* `pointerdown` fires before the `focus` event, so we can prevent the default
* behavior of focusing the button and keep focus on the input.
*/
onpointerdown(e: BitsPointerEvent): void;
readonly props: {
readonly id: string;
readonly disabled: true | undefined;
readonly "aria-haspopup": "listbox";
readonly "data-state": "open" | "closed";
readonly "data-disabled": "" | undefined;
readonly onpointerdown: (e: BitsPointerEvent) => void;
readonly onkeydown: (e: BitsKeyboardEvent) => void;
};
}
interface SelectTriggerStateOpts extends WithRefOpts {
}
export declare class SelectTriggerState {
#private;
static create(opts: SelectTriggerStateOpts): SelectTriggerState;
readonly opts: SelectTriggerStateOpts;
readonly root: SelectRoot;
readonly attachment: RefAttachment;
constructor(opts: SelectTriggerStateOpts, root: SelectRoot);
onkeydown(e: BitsKeyboardEvent): void;
onclick(e: BitsMouseEvent): void;
onpointerdown(e: BitsPointerEvent): void;
onpointerup(e: BitsPointerEvent): void;
readonly props: {
readonly id: string;
readonly disabled: true | undefined;
readonly "aria-haspopup": "listbox";
readonly "aria-expanded": "true" | "false";
readonly "aria-activedescendant": string | undefined;
readonly "data-state": "open" | "closed";
readonly "data-disabled": "" | undefined;
readonly "data-placeholder": "" | undefined;
readonly onpointerdown: (e: BitsPointerEvent) => void;
readonly onkeydown: (e: BitsKeyboardEvent) => void;
readonly onclick: (e: BitsMouseEvent) => void;
readonly onpointerup: (e: BitsPointerEvent) => void;
};
}
interface SelectContentStateOpts extends WithRefOpts, ReadableBoxedValues<{
onInteractOutside: (e: PointerEvent) => void;
onEscapeKeydown: (e: KeyboardEvent) => void;
}> {
}
export declare class SelectContentState {
#private;
static create(opts: SelectContentStateOpts): SelectContentState;
readonly opts: SelectContentStateOpts;
readonly root: SelectRoot;
readonly attachment: RefAttachment;
isPositioned: boolean;
domContext: DOMContext;
constructor(opts: SelectContentStateOpts, root: SelectRoot);
onpointermove(_: BitsPointerEvent): void;
onInteractOutside: (e: PointerEvent) => void;
onEscapeKeydown: (e: KeyboardEvent) => void;
onOpenAutoFocus: (e: Event) => void;
onCloseAutoFocus: (e: Event) => void;
get shouldRender(): boolean;
readonly snippetProps: {
open: boolean;
};
readonly props: {
readonly id: string;
readonly role: "listbox";
readonly "aria-multiselectable": "true" | undefined;
readonly "data-state": "open" | "closed";
readonly style: {
readonly display: "flex";
readonly flexDirection: "column";
readonly outline: "none";
readonly boxSizing: "border-box";
readonly pointerEvents: "auto";
};
readonly onpointermove: (_: BitsPointerEvent) => void;
};
readonly popperProps: {
onInteractOutside: (e: PointerEvent) => void;
onEscapeKeydown: (e: KeyboardEvent) => void;
onOpenAutoFocus: (e: Event) => void;
onCloseAutoFocus: (e: Event) => void;
trapFocus: boolean;
loop: boolean;
onPlaced: () => void;
};
}
interface SelectItemStateOpts extends WithRefOpts, ReadableBoxedValues<{
value: string;
disabled: boolean;
label: string;
onHighlight: () => void;
onUnhighlight: () => void;
}> {
}
export declare class SelectItemState {
static create(opts: SelectItemStateOpts): SelectItemState;
readonly opts: SelectItemStateOpts;
readonly root: SelectRoot;
readonly attachment: RefAttachment;
readonly isSelected: boolean;
readonly isHighlighted: boolean;
readonly prevHighlighted: Previous<boolean>;
mounted: boolean;
constructor(opts: SelectItemStateOpts, root: SelectRoot);
handleSelect(): void;
snippetProps: {
selected: boolean;
highlighted: boolean;
};
onpointerdown(e: BitsPointerEvent): void;
/**
* Using `pointerup` instead of `click` allows power users to pointerdown
* the trigger, then release pointerup on an item to select it vs having to do
* multiple clicks.
*/
onpointerup(e: BitsPointerEvent): void;
onpointermove(e: BitsPointerEvent): void;
readonly props: {
readonly id: string;
readonly role: "option";
readonly "aria-selected": "true" | undefined;
readonly "data-value": string;
readonly "data-disabled": "" | undefined;
readonly "data-highlighted": "" | undefined;
readonly "data-selected": "" | undefined;
readonly "data-label": string;
readonly onpointermove: (e: BitsPointerEvent) => void;
readonly onpointerdown: (e: BitsPointerEvent) => void;
readonly onpointerup: (e: BitsPointerEvent) => void;
};
}
interface SelectGroupStateOpts extends WithRefOpts {
}
export declare class SelectGroupState {
static create(opts: SelectGroupStateOpts): SelectGroupState;
readonly opts: SelectGroupStateOpts;
readonly root: SelectBaseRootState;
labelNode: HTMLElement | null;
readonly attachment: RefAttachment;
constructor(opts: SelectGroupStateOpts, root: SelectBaseRootState);
readonly props: {
readonly id: string;
readonly role: "group";
readonly "aria-labelledby": string | undefined;
};
}
interface SelectGroupHeadingStateOpts extends WithRefOpts {
}
export declare class SelectGroupHeadingState {
static create(opts: SelectGroupHeadingStateOpts): SelectGroupHeadingState;
readonly opts: SelectGroupHeadingStateOpts;
readonly group: SelectGroupState;
readonly attachment: RefAttachment;
constructor(opts: SelectGroupHeadingStateOpts, group: SelectGroupState);
readonly props: {
readonly id: string;
};
}
interface SelectHiddenInputStateOpts extends ReadableBoxedValues<{
value: string | undefined;
}> {
}
export declare class SelectHiddenInputState {
static create(opts: SelectHiddenInputStateOpts): SelectHiddenInputState;
readonly opts: SelectHiddenInputStateOpts;
readonly root: SelectBaseRootState;
readonly shouldRender: boolean;
constructor(opts: SelectHiddenInputStateOpts, root: SelectBaseRootState);
onfocus(e: BitsFocusEvent): void;
readonly props: {
readonly disabled: true | undefined;
readonly required: true | undefined;
readonly name: string;
readonly value: string | undefined;
readonly onfocus: (e: BitsFocusEvent) => void;
};
}
interface SelectViewportStateOpts extends WithRefOpts {
}
export declare class SelectViewportState {
static create(opts: SelectViewportStateOpts): SelectViewportState;
readonly opts: SelectViewportStateOpts;
readonly content: SelectContentState;
readonly root: SelectBaseRootState;
readonly attachment: RefAttachment;
prevScrollTop: number;
constructor(opts: SelectViewportStateOpts, content: SelectContentState);
readonly props: {
readonly id: string;
readonly role: "presentation";
readonly style: {
readonly position: "relative";
readonly flex: 1;
readonly overflow: "auto";
};
};
}
interface SelectScrollButtonImplStateOpts extends WithRefOpts, ReadableBoxedValues<{
delay: (tick: number) => number;
}> {
}
export declare class SelectScrollButtonImplState {
readonly opts: SelectScrollButtonImplStateOpts;
readonly content: SelectContentState;
readonly root: SelectBaseRootState;
readonly attachment: RefAttachment;
autoScrollTimer: number | null;
userScrollTimer: number;
isUserScrolling: boolean;
onAutoScroll: () => void;
mounted: boolean;
constructor(opts: SelectScrollButtonImplStateOpts, content: SelectContentState);
handleUserScroll(): void;
clearAutoScrollInterval(): void;
onpointerdown(_: BitsPointerEvent): void;
onpointermove(e: BitsPointerEvent): void;
onpointerleave(_: BitsPointerEvent): void;
readonly props: {
readonly id: string;
readonly "aria-hidden": "true" | undefined;
readonly style: {
readonly flexShrink: 0;
};
readonly onpointerdown: (_: BitsPointerEvent) => void;
readonly onpointermove: (e: BitsPointerEvent) => void;
readonly onpointerleave: (_: BitsPointerEvent) => void;
};
}
export declare class SelectScrollDownButtonState {
static create(opts: SelectScrollButtonImplStateOpts): SelectScrollDownButtonState;
readonly scrollButtonState: SelectScrollButtonImplState;
readonly content: SelectContentState;
readonly root: SelectBaseRootState;
canScrollDown: boolean;
scrollIntoViewTimer: ReturnType<typeof globalThis.setTimeout> | null;
constructor(scrollButtonState: SelectScrollButtonImplState);
/**
* @param manual - if true, it means the function was invoked manually outside of an event
* listener, so we don't call `handleUserScroll` to prevent the auto scroll from kicking in.
*/
handleScroll: (manual?: boolean) => void;
handleAutoScroll: () => void;
readonly props: {
readonly id: string;
readonly "aria-hidden": "true" | undefined;
readonly style: {
readonly flexShrink: 0;
};
readonly onpointerdown: (_: BitsPointerEvent) => void;
readonly onpointermove: (e: BitsPointerEvent) => void;
readonly onpointerleave: (_: BitsPointerEvent) => void;
};
}
export declare class SelectScrollUpButtonState {
static create(opts: SelectScrollButtonImplStateOpts): SelectScrollUpButtonState;
readonly scrollButtonState: SelectScrollButtonImplState;
readonly content: SelectContentState;
readonly root: SelectBaseRootState;
canScrollUp: boolean;
constructor(scrollButtonState: SelectScrollButtonImplState);
/**
* @param manual - if true, it means the function was invoked manually outside of an event
* listener, so we don't call `handleUserScroll` to prevent the auto scroll from kicking in.
*/
handleScroll: (manual?: boolean) => void;
handleAutoScroll: () => void;
readonly props: {
readonly id: string;
readonly "aria-hidden": "true" | undefined;
readonly style: {
readonly flexShrink: 0;
};
readonly onpointerdown: (_: BitsPointerEvent) => void;
readonly onpointermove: (e: BitsPointerEvent) => void;
readonly onpointerleave: (_: BitsPointerEvent) => void;
};
}
export {};
File diff suppressed because it is too large Load Diff
+201
View File
@@ -0,0 +1,201 @@
import type { Expand } from "svelte-toolbelt";
import type { PortalProps } from "../utilities/portal/types.js";
import type { PopperLayerProps, PopperLayerStaticProps } from "../utilities/popper-layer/types.js";
import type { ArrowProps, ArrowPropsWithoutHTML } from "../utilities/arrow/types.js";
import type { BitsPrimitiveButtonAttributes, BitsPrimitiveDivAttributes } from "../../shared/attributes.js";
import type { OnChangeFn, WithChild, WithChildNoChildrenSnippetProps, WithChildren, Without } from "../../internal/types.js";
import type { FloatingContentSnippetProps, StaticContentSnippetProps } from "../../shared/types.js";
import type { HTMLInputAttributes } from "svelte/elements";
export type SelectBaseRootPropsWithoutHTML = WithChildren<{
/**
* Whether the combobox is disabled.
*
* @defaultValue `false`
*/
disabled?: boolean;
/**
* Whether the combobox is required (for form submission).
*
* @defaultValue `false`
*/
required?: boolean;
/**
* The name to apply to the hidden input element for form submission.
* If not provided, a hidden input will not be rendered and the combobox will not be part of a form.
*/
name?: string;
/**
* Whether the combobox popover is open.
*
* @defaultValue `false`
* @bindable
*/
open?: boolean;
/**
* A callback function called when the open state changes.
*/
onOpenChange?: OnChangeFn<boolean>;
/**
* A callback function called when the open state changes and the animation is complete.
*/
onOpenChangeComplete?: OnChangeFn<boolean>;
/**
* Whether or not the combobox menu should loop through the items when navigating with the keyboard.
*
* @defaultValue `false`
*/
loop?: boolean;
/**
* How to scroll the combobox items into view when navigating with the keyboard.
*
* @defaultValue `"nearest"`
*/
scrollAlignment?: "nearest" | "center";
/**
* Optionally provide an array of `value` and `label` pairs that will be used to match
* and trigger selection when the trigger is focused and a key is pressed while the content
* is closed. It's also used to handle form autofill.
*
* By providing this value, you enable selecting a value when the trigger is focused and a key
* is pressed without the content being open, similar to how a native `<select>` works.
* For this to work, you must
*
* The label is what the user will potentially search for via typeahead, and the value is what
* is set as the selected value when a typeahead match is found.
*
* We can't rely on the individual `Item` components to do this because they may not ever be
* mounted to do the DOM if using a conditional block with a Svelte transition or certain
* animation libraries.
*
* IMPORTANT: This functionality is only available for single-select listboxes.
*/
items?: {
value: string;
label: string;
disabled?: boolean;
}[];
/**
* Whether to allow the user to deselect an item by clicking on an already selected item.
* This is only applicable to `type="single"` selects/comboboxes.
*/
allowDeselect?: boolean;
/**
* The autocomplete attribute to forward to the hidden input element.
*/
autocomplete?: HTMLInputAttributes["autocomplete"];
}>;
export type SelectSingleRootPropsWithoutHTML = {
/**
* The value of the selected combobox item.
*
* @bindable
*/
value?: string;
/**
* A callback function called when the value changes.
*/
onValueChange?: OnChangeFn<string>;
/**
* The type of combobox.
*
* @required
*/
type: "single";
};
export type SelectMultipleRootPropsWithoutHTML = {
/**
* The value of the selected combobox item.
*
* @bindable
*/
value?: string[];
/**
* A callback function called when the value changes.
*/
onValueChange?: OnChangeFn<string[]>;
/**
* The type of combobox.
*
* @required
*/
type: "multiple";
};
export type SelectSingleRootProps = SelectBaseRootPropsWithoutHTML & SelectSingleRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectSingleRootPropsWithoutHTML | SelectBaseRootPropsWithoutHTML>;
export type SelectMultipleRootProps = SelectBaseRootPropsWithoutHTML & SelectMultipleRootPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectMultipleRootPropsWithoutHTML | SelectBaseRootPropsWithoutHTML>;
export type SelectRootPropsWithoutHTML = SelectBaseRootPropsWithoutHTML & (SelectSingleRootPropsWithoutHTML | SelectMultipleRootPropsWithoutHTML);
export type SelectRootProps = SelectRootPropsWithoutHTML;
export type _SharedSelectContentProps = {
/**
* Whether or not to loop through the items when navigating with the keyboard.
*
* @defaultValue `false`
*/
loop?: boolean;
};
export type SelectContentPropsWithoutHTML = Expand<WithChildNoChildrenSnippetProps<Omit<PopperLayerProps, "content" | "onOpenAutoFocus" | "trapFocus" | "onCloseAutoFocus"> & _SharedSelectContentProps, FloatingContentSnippetProps>>;
export type SelectContentProps = SelectContentPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectContentPropsWithoutHTML>;
export type SelectContentStaticPropsWithoutHTML = Expand<WithChildNoChildrenSnippetProps<Omit<PopperLayerStaticProps, "content" | "onOpenAutoFocus" | "onCloseAutoFocus" | "trapFocus"> & _SharedSelectContentProps, StaticContentSnippetProps>>;
export type SelectContentStaticProps = SelectContentStaticPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectContentStaticPropsWithoutHTML>;
export type SelectTriggerPropsWithoutHTML = WithChild;
export type SelectTriggerProps = SelectTriggerPropsWithoutHTML & Without<BitsPrimitiveButtonAttributes, SelectTriggerPropsWithoutHTML>;
export type SelectItemSnippetProps = {
selected: boolean;
highlighted: boolean;
};
export type SelectItemPropsWithoutHTML = WithChild<{
/**
* The value of the item.
*
* @required
*/
value: string;
/**
* The label of the item. If provided, this is the item that users will search for.
* If not provided, the value will be used as the label.
*/
label?: string;
/**
* Whether the item is disabled.
*
* @defaultValue `false`
*/
disabled?: boolean;
/**
* A callback function called when the item is highlighted. This can be used as a
* replacement for `onfocus` since we don't actually focus the item and instead
* rely on the `aria-activedescendant` attribute to indicate the highlighted item.
*/
onHighlight?: () => void;
/**
* A callback function called when the item is unhighlighted. This can be used as a
* replacement for `onblur` since we don't actually focus the item and instead
* rely on the `aria-activedescendant` attribute to indicate the highlighted item.
*/
onUnhighlight?: () => void;
}, SelectItemSnippetProps>;
export type SelectItemProps = SelectItemPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectItemPropsWithoutHTML>;
export type SelectGroupPropsWithoutHTML = WithChild;
export type SelectGroupProps = SelectGroupPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectGroupPropsWithoutHTML>;
export type SelectGroupHeadingPropsWithoutHTML = WithChild;
export type SelectGroupHeadingProps = SelectGroupHeadingPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectGroupHeadingPropsWithoutHTML>;
export type SelectSeparatorPropsWithoutHTML = WithChild;
export type SelectSeparatorProps = SelectSeparatorPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectSeparatorPropsWithoutHTML>;
export type SelectPortalPropsWithoutHTML = PortalProps;
export type SelectPortalProps = SelectPortalPropsWithoutHTML;
export type SelectArrowPropsWithoutHTML = ArrowPropsWithoutHTML;
export type SelectArrowProps = ArrowProps;
export type SelectViewportPropsWithoutHTML = WithChild;
export type SelectViewportProps = SelectViewportPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectViewportPropsWithoutHTML>;
export type SelectScrollButtonPropsWithoutHTML = WithChild<{
/**
* Controls the initial delay (tick 0) and delay between auto-scrolls in milliseconds.
* The default function always returns 50ms.
*
* @param tick current tick number
*/
delay?: (tick: number) => number;
}>;
export type SelectScrollUpButtonPropsWithoutHTML = SelectScrollButtonPropsWithoutHTML;
export type SelectScrollUpButtonProps = SelectScrollUpButtonPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectScrollUpButtonPropsWithoutHTML>;
export type SelectScrollDownButtonPropsWithoutHTML = SelectScrollButtonPropsWithoutHTML;
export type SelectScrollDownButtonProps = SelectScrollDownButtonPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, SelectScrollDownButtonPropsWithoutHTML>;
+1
View File
@@ -0,0 +1 @@
export {};