lithesome 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/Accordion.svelte +1 -1
- package/dist/components/Accordion/AccordionButton.svelte +1 -1
- package/dist/components/Accordion/AccordionContent.svelte +1 -1
- package/dist/components/Accordion/AccordionHeading.svelte +1 -1
- package/dist/components/Accordion/AccordionItem.svelte +1 -1
- package/dist/components/Checkbox/Checkbox.svelte +2 -2
- package/dist/components/Checkbox/Checkbox.svelte.d.ts +1 -0
- package/dist/components/Combobox/Combobox.svelte +55 -0
- package/dist/components/Combobox/Combobox.svelte.d.ts +50 -0
- package/dist/components/Combobox/ComboboxDropdown.svelte +83 -0
- package/dist/components/Combobox/ComboboxDropdown.svelte.d.ts +33 -0
- package/dist/components/Combobox/ComboboxInput.svelte +82 -0
- package/dist/components/Combobox/ComboboxInput.svelte.d.ts +23 -0
- package/dist/components/Combobox/ComboboxOption.svelte +75 -0
- package/dist/components/Combobox/ComboboxOption.svelte.d.ts +25 -0
- package/dist/components/Combobox/context.svelte.d.ts +40 -0
- package/dist/components/Combobox/context.svelte.js +136 -0
- package/dist/components/Combobox/index.d.ts +4 -0
- package/dist/components/Combobox/index.js +4 -0
- package/dist/components/Menu/Menu.svelte +1 -1
- package/dist/components/Menu/MenuDropdown.svelte +1 -1
- package/dist/components/Menu/MenuItem.svelte +1 -1
- package/dist/components/Menu/MenuTrigger.svelte +1 -1
- package/dist/components/Modal/Modal.svelte +9 -1
- package/dist/components/Modal/ModalContent.svelte +1 -1
- package/dist/components/Modal/ModalDescription.svelte +1 -1
- package/dist/components/Modal/ModalOverlay.svelte +5 -1
- package/dist/components/Modal/ModalOverlay.svelte.d.ts +2 -2
- package/dist/components/Modal/ModalTitle.svelte +1 -1
- package/dist/components/Pin/Pin.svelte +2 -2
- package/dist/components/Pin/PinInput.svelte +11 -1
- package/dist/components/Pin/PinInput.svelte.d.ts +3 -3
- package/dist/components/Pin/PinValue.svelte +1 -1
- package/dist/components/Pin/PinValue.svelte.d.ts +2 -2
- package/dist/components/Popover/Popover.svelte +1 -1
- package/dist/components/Popover/PopoverContent.svelte +1 -1
- package/dist/components/Popover/PopoverTrigger.svelte +1 -1
- package/dist/components/RadioGroup/RadioGroup.svelte +9 -1
- package/dist/components/RadioGroup/RadioGroupItem.svelte +1 -1
- package/dist/components/Select/Select.svelte +5 -6
- package/dist/components/Select/Select.svelte.d.ts +16 -18
- package/dist/components/Select/SelectDropdown.svelte +1 -1
- package/dist/components/Select/SelectOption.svelte +35 -19
- package/dist/components/Select/SelectOption.svelte.d.ts +1 -0
- package/dist/components/Select/SelectTrigger.svelte +1 -1
- package/dist/components/Select/SelectValue.svelte +2 -2
- package/dist/components/Select/SelectValue.svelte.d.ts +3 -3
- package/dist/components/Select/context.svelte.d.ts +9 -11
- package/dist/components/Select/context.svelte.js +23 -22
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/helpers/element.d.ts +5 -0
- package/dist/internal/helpers/element.js +15 -0
- package/dist/internal/helpers/keyboard.js +4 -1
- package/dist/internal/helpers/utils.svelte.d.ts +1 -1
- package/dist/internal/helpers/utils.svelte.js +2 -2
- package/dist/internal/types.d.ts +1 -0
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@ export const context = () => getContext(contextName);
|
|
|
6
6
|
|
|
7
7
|
<script>import { createUID, useActions, classProp } from "../../internal/index.js";
|
|
8
8
|
import { setContext } from "svelte";
|
|
9
|
-
let { children, use = [], class: klass, self, single, ...props } = $props();
|
|
9
|
+
let { children, use = [], class: klass, self = $bindable(), single, ...props } = $props();
|
|
10
10
|
const { uid } = createUID("accordion");
|
|
11
11
|
const API = createContext(uid, single);
|
|
12
12
|
const active = $derived(API.activeItems.length > 0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>import { context } from "./Accordion.svelte";
|
|
2
2
|
import { useActions, classProp } from "../../internal/index.js";
|
|
3
3
|
import { getContext } from "svelte";
|
|
4
|
-
let { children, class: klass, use = [], self, onClick, ...props } = $props();
|
|
4
|
+
let { children, class: klass, use = [], self = $bindable(), onClick, ...props } = $props();
|
|
5
5
|
const API = context();
|
|
6
6
|
const itemId = getContext("accordionitem-id");
|
|
7
7
|
const active = $derived(API.activeItems.includes(itemId));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>import { context } from "./Accordion.svelte";
|
|
2
2
|
import { useActions, getTransition, classProp } from "../../internal/index.js";
|
|
3
3
|
import { getContext } from "svelte";
|
|
4
|
-
let { children, class: klass, use = [], self, transition, ...props } = $props();
|
|
4
|
+
let { children, class: klass, use = [], self = $bindable(), transition, ...props } = $props();
|
|
5
5
|
const API = context();
|
|
6
6
|
const itemId = getContext("accordionitem-id");
|
|
7
7
|
const active = $derived(API.activeItems.includes(itemId));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script>import { useActions, classProp } from "../../internal/index.js";
|
|
2
|
-
let { children, class: klass, use = [], level = 3, self, ...props } = $props();
|
|
2
|
+
let { children, class: klass, use = [], level = 3, self = $bindable(), ...props } = $props();
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<div
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>import { context } from "./Accordion.svelte";
|
|
2
2
|
import { log, useActions, createUID, classProp } from "../../internal/index.js";
|
|
3
3
|
import { onMount, setContext } from "svelte";
|
|
4
|
-
let { children, class: klass, use = [], self, disabled = false, ...props } = $props();
|
|
4
|
+
let { children, class: klass, use = [], self = $bindable(), disabled = false, ...props } = $props();
|
|
5
5
|
const API = context();
|
|
6
6
|
const { uid } = createUID("item");
|
|
7
7
|
onMount(() => {
|
|
@@ -22,6 +22,7 @@ declare const __propDef: {
|
|
|
22
22
|
contextmenu?: string | null | undefined;
|
|
23
23
|
dir?: string | null | undefined;
|
|
24
24
|
draggable?: import("svelte/elements").Booleanish | null | undefined;
|
|
25
|
+
elementtiming?: string | null | undefined;
|
|
25
26
|
enterkeyhint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | null | undefined;
|
|
26
27
|
hidden?: boolean | null | undefined;
|
|
27
28
|
id?: string | null | undefined;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script context="module">import { getContext, onMount, tick } from "svelte";
|
|
2
|
+
import { createContext } from "./context.svelte.js";
|
|
3
|
+
const contextName = "combobox-context";
|
|
4
|
+
export const context = () => getContext(contextName);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script generics="ValueType">import { createUID, useActions, classProp } from "../../internal/index.js";
|
|
8
|
+
import { setContext } from "svelte";
|
|
9
|
+
let {
|
|
10
|
+
children,
|
|
11
|
+
use = [],
|
|
12
|
+
class: klass,
|
|
13
|
+
value = $bindable(),
|
|
14
|
+
label = $bindable(),
|
|
15
|
+
touched = $bindable(),
|
|
16
|
+
self = $bindable(),
|
|
17
|
+
onChange,
|
|
18
|
+
...props
|
|
19
|
+
} = $props();
|
|
20
|
+
const { uid } = createUID("combobox");
|
|
21
|
+
const multiple = Array.isArray(value);
|
|
22
|
+
const API = createContext(uid, multiple, {
|
|
23
|
+
onChange({ newValue, newTouched, newLabel }) {
|
|
24
|
+
if (newValue) {
|
|
25
|
+
value = newValue;
|
|
26
|
+
onChange?.({ value: newValue });
|
|
27
|
+
}
|
|
28
|
+
if (newLabel && !multiple) {
|
|
29
|
+
label = newLabel;
|
|
30
|
+
onChange?.({ label: newLabel });
|
|
31
|
+
}
|
|
32
|
+
if (typeof newTouched === "boolean")
|
|
33
|
+
touched = newTouched;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
setContext(contextName, API);
|
|
37
|
+
onMount(async () => {
|
|
38
|
+
await tick();
|
|
39
|
+
API.setInitialSelected(value);
|
|
40
|
+
API.close();
|
|
41
|
+
API.setMounted(true);
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div
|
|
46
|
+
bind:this={self}
|
|
47
|
+
use:useActions={use}
|
|
48
|
+
id={uid()}
|
|
49
|
+
class={classProp(klass, { visible: API.visible && API.mounted })}
|
|
50
|
+
data-select=""
|
|
51
|
+
data-state={API.visible && API.mounted ? 'opened' : 'closed'}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
{@render children({ visible: API.visible && API.mounted })}
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
export declare const context: () => {
|
|
3
|
+
visible: boolean;
|
|
4
|
+
hoveredIndex: number;
|
|
5
|
+
options: HTMLElement[];
|
|
6
|
+
touched: HTMLElement[];
|
|
7
|
+
dropdown: HTMLElement | null;
|
|
8
|
+
mounted: boolean;
|
|
9
|
+
selectedOptions: HTMLElement[];
|
|
10
|
+
hoveredOption: HTMLElement;
|
|
11
|
+
trigger: HTMLInputElement | null;
|
|
12
|
+
multiple: boolean;
|
|
13
|
+
open(): void;
|
|
14
|
+
close(): void;
|
|
15
|
+
toggle(): void;
|
|
16
|
+
queryElements(): void;
|
|
17
|
+
navigateOptions(action: import("../../internal/index.js").CalcIndexAction): void;
|
|
18
|
+
setHoveredOption(optionId?: string | undefined): void;
|
|
19
|
+
setSelectedOptions(): void;
|
|
20
|
+
setInitialSelected(value: unknown): Promise<void>;
|
|
21
|
+
setTrigger(node: HTMLInputElement): void;
|
|
22
|
+
setDropdown(node: HTMLElement): void;
|
|
23
|
+
setMounted(value: boolean): void;
|
|
24
|
+
setTouched(value: boolean): void;
|
|
25
|
+
uid: import("../../internal/index.js").UID;
|
|
26
|
+
};
|
|
27
|
+
import { type BaseProps } from '../../internal/index.js';
|
|
28
|
+
declare class __sveltets_Render<ValueType> {
|
|
29
|
+
props(): BaseProps<HTMLDivElement, {
|
|
30
|
+
visible: boolean;
|
|
31
|
+
}> & {
|
|
32
|
+
value: ValueType;
|
|
33
|
+
label?: string | undefined;
|
|
34
|
+
touched?: boolean | undefined;
|
|
35
|
+
onChange?: ((payload?: {
|
|
36
|
+
value?: ValueType | undefined;
|
|
37
|
+
label?: string | undefined;
|
|
38
|
+
} | undefined) => void) | undefined;
|
|
39
|
+
};
|
|
40
|
+
events(): {} & {
|
|
41
|
+
[evt: string]: CustomEvent<any>;
|
|
42
|
+
};
|
|
43
|
+
slots(): {};
|
|
44
|
+
}
|
|
45
|
+
export type ComboboxProps<ValueType> = ReturnType<__sveltets_Render<ValueType>['props']>;
|
|
46
|
+
export type ComboboxEvents<ValueType> = ReturnType<__sveltets_Render<ValueType>['events']>;
|
|
47
|
+
export type ComboboxSlots<ValueType> = ReturnType<__sveltets_Render<ValueType>['slots']>;
|
|
48
|
+
export default class Combobox<ValueType> extends SvelteComponent<ComboboxProps<ValueType>, ComboboxEvents<ValueType>, ComboboxSlots<ValueType>> {
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script>import { context } from "./Combobox.svelte";
|
|
2
|
+
import {
|
|
3
|
+
clickOutside,
|
|
4
|
+
anchorElement,
|
|
5
|
+
portal,
|
|
6
|
+
useActions,
|
|
7
|
+
getTransition,
|
|
8
|
+
classProp
|
|
9
|
+
} from "../../internal/index.js";
|
|
10
|
+
import { log } from "../../internal/index.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
let {
|
|
13
|
+
children,
|
|
14
|
+
transition,
|
|
15
|
+
use = [],
|
|
16
|
+
portalTarget = "body",
|
|
17
|
+
sameWidth = false,
|
|
18
|
+
class: klass,
|
|
19
|
+
self = $bindable(),
|
|
20
|
+
placement = "bottom",
|
|
21
|
+
constrainViewport = false,
|
|
22
|
+
...props
|
|
23
|
+
} = $props();
|
|
24
|
+
const API = context();
|
|
25
|
+
let dropdownCleanup = $state(void 0);
|
|
26
|
+
const _transition = getTransition(transition);
|
|
27
|
+
const attrs = $derived({
|
|
28
|
+
id: API.uid("dropdown"),
|
|
29
|
+
"aria-labelledby": API.uid("trigger"),
|
|
30
|
+
role: "listbox",
|
|
31
|
+
class: classProp(klass, { visible: API.visible }),
|
|
32
|
+
"data-comboboxdropdown": "",
|
|
33
|
+
hidden: !API.mounted || void 0
|
|
34
|
+
});
|
|
35
|
+
onMount(() => {
|
|
36
|
+
if (!API)
|
|
37
|
+
log.error("<ComboboxDropdown> Must be a direct child of <Combobox />");
|
|
38
|
+
});
|
|
39
|
+
$effect(() => {
|
|
40
|
+
if (API.visible && self)
|
|
41
|
+
API.setDropdown(self);
|
|
42
|
+
});
|
|
43
|
+
$effect(() => {
|
|
44
|
+
if (API.visible && API.trigger && API.dropdown) {
|
|
45
|
+
dropdownCleanup = anchorElement(API.trigger, API.dropdown, {
|
|
46
|
+
placement,
|
|
47
|
+
constrainViewport,
|
|
48
|
+
sameWidth
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return () => {
|
|
52
|
+
dropdownCleanup?.();
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
{#if _transition}
|
|
58
|
+
{#if API.visible}
|
|
59
|
+
<div
|
|
60
|
+
bind:this={self}
|
|
61
|
+
use:clickOutside={{ exclude: [API.trigger], callback: API.close }}
|
|
62
|
+
use:portal={portalTarget}
|
|
63
|
+
use:useActions={use}
|
|
64
|
+
in:_transition.in.fn={_transition.in.params}
|
|
65
|
+
out:_transition.out.fn={_transition.out.params}
|
|
66
|
+
{...attrs}
|
|
67
|
+
{...props}
|
|
68
|
+
>
|
|
69
|
+
{@render children({ visible: API.visible })}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
{:else if API.visible}
|
|
73
|
+
<div
|
|
74
|
+
bind:this={self}
|
|
75
|
+
use:clickOutside={{ exclude: [API.trigger], callback: API.close }}
|
|
76
|
+
use:portal={portalTarget}
|
|
77
|
+
use:useActions={use}
|
|
78
|
+
{...attrs}
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
{@render children({ visible: API.visible })}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import { type Transition, type BaseProps } from '../../internal/index.js';
|
|
3
|
+
import type { Placement } from '@floating-ui/dom';
|
|
4
|
+
declare const __propDef: {
|
|
5
|
+
props: BaseProps<HTMLDivElement, {
|
|
6
|
+
visible: boolean;
|
|
7
|
+
}> & {
|
|
8
|
+
/**
|
|
9
|
+
* The `svelte/transtion` you wish to use.
|
|
10
|
+
*
|
|
11
|
+
* @see https://lithesome.dev/docs/api#transition-prop
|
|
12
|
+
*/
|
|
13
|
+
transition?: Transition | undefined;
|
|
14
|
+
/** The element to portal the dropdown menu to. */
|
|
15
|
+
portalTarget?: string | HTMLElement | undefined;
|
|
16
|
+
/** The anchor point of the dropdown relative to the trigger. */
|
|
17
|
+
placement?: Placement | undefined;
|
|
18
|
+
/** Keeps the dropdown from ever growing outside of the viewport. */
|
|
19
|
+
constrainViewport?: boolean | undefined;
|
|
20
|
+
/** Makes the dropdown the same width as the trigger. */
|
|
21
|
+
sameWidth?: boolean | undefined;
|
|
22
|
+
};
|
|
23
|
+
events: {
|
|
24
|
+
[evt: string]: CustomEvent<any>;
|
|
25
|
+
};
|
|
26
|
+
slots: {};
|
|
27
|
+
};
|
|
28
|
+
export type ComboboxDropdownProps = typeof __propDef.props;
|
|
29
|
+
export type ComboboxDropdownEvents = typeof __propDef.events;
|
|
30
|
+
export type ComboboxDropdownSlots = typeof __propDef.slots;
|
|
31
|
+
export default class ComboboxDropdown extends SvelteComponent<ComboboxDropdownProps, ComboboxDropdownEvents, ComboboxDropdownSlots> {
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script>import { context } from "./Combobox.svelte";
|
|
2
|
+
import {
|
|
3
|
+
useActions,
|
|
4
|
+
classProp,
|
|
5
|
+
PREVENT_KEYS,
|
|
6
|
+
KEYS
|
|
7
|
+
} from "../../internal/index.js";
|
|
8
|
+
import { onMount } from "svelte";
|
|
9
|
+
let {
|
|
10
|
+
class: klass,
|
|
11
|
+
use = [],
|
|
12
|
+
value = $bindable(),
|
|
13
|
+
self = $bindable(),
|
|
14
|
+
disabled,
|
|
15
|
+
onClick,
|
|
16
|
+
onFocus,
|
|
17
|
+
onKeydown,
|
|
18
|
+
...props
|
|
19
|
+
} = $props();
|
|
20
|
+
const API = context();
|
|
21
|
+
onMount(() => {
|
|
22
|
+
if (!API || !self)
|
|
23
|
+
return;
|
|
24
|
+
API.setTrigger(self);
|
|
25
|
+
});
|
|
26
|
+
const handleClick = (e) => {
|
|
27
|
+
onClick?.(e);
|
|
28
|
+
if (disabled)
|
|
29
|
+
return;
|
|
30
|
+
API.toggle();
|
|
31
|
+
};
|
|
32
|
+
const handleKeydown = (e) => {
|
|
33
|
+
onKeydown?.(e);
|
|
34
|
+
if (disabled)
|
|
35
|
+
return;
|
|
36
|
+
const { key } = e;
|
|
37
|
+
if (!PREVENT_KEYS.includes(key)) {
|
|
38
|
+
API.setTouched(true);
|
|
39
|
+
if (!API.visible)
|
|
40
|
+
API.open();
|
|
41
|
+
}
|
|
42
|
+
if (key === KEYS.arrowUp || key === KEYS.arrowDown || key === KEYS.end || key === KEYS.home) {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
if (!API.visible)
|
|
45
|
+
API.open();
|
|
46
|
+
}
|
|
47
|
+
if (key === KEYS.home)
|
|
48
|
+
API.navigateOptions("first");
|
|
49
|
+
if (key === KEYS.end)
|
|
50
|
+
API.navigateOptions("last");
|
|
51
|
+
if (key === KEYS.arrowUp)
|
|
52
|
+
API.navigateOptions("prev");
|
|
53
|
+
if (key === KEYS.arrowDown)
|
|
54
|
+
API.navigateOptions("next");
|
|
55
|
+
if (key === KEYS.escape)
|
|
56
|
+
API.close();
|
|
57
|
+
if (key === KEYS.enter) {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
if (API.hoveredOption && API.visible) {
|
|
60
|
+
document.querySelector(`#${API.hoveredOption.id}`).click();
|
|
61
|
+
if (!API.multiple)
|
|
62
|
+
API.close();
|
|
63
|
+
} else {
|
|
64
|
+
API.open();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (key === "Tab")
|
|
68
|
+
API.close();
|
|
69
|
+
};
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<input
|
|
73
|
+
type="text"
|
|
74
|
+
bind:this={self}
|
|
75
|
+
use:useActions={use}
|
|
76
|
+
id={API.uid('input')}
|
|
77
|
+
class={classProp(klass, { visible: API.visible })}
|
|
78
|
+
onclick={handleClick}
|
|
79
|
+
onkeydown={handleKeydown}
|
|
80
|
+
bind:value
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import { type BasePropsNoChildren, type Handler } from '../../internal/index.js';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: BasePropsNoChildren<HTMLInputElement, {
|
|
5
|
+
visible: boolean;
|
|
6
|
+
}> & {
|
|
7
|
+
value: string;
|
|
8
|
+
disabled?: boolean | undefined;
|
|
9
|
+
onClick?: Handler<MouseEvent, HTMLInputElement> | undefined;
|
|
10
|
+
onFocus?: Handler<FocusEvent, HTMLInputElement> | undefined;
|
|
11
|
+
onKeydown?: Handler<KeyboardEvent, HTMLInputElement> | undefined;
|
|
12
|
+
};
|
|
13
|
+
events: {
|
|
14
|
+
[evt: string]: CustomEvent<any>;
|
|
15
|
+
};
|
|
16
|
+
slots: {};
|
|
17
|
+
};
|
|
18
|
+
export type ComboboxInputProps = typeof __propDef.props;
|
|
19
|
+
export type ComboboxInputEvents = typeof __propDef.events;
|
|
20
|
+
export type ComboboxInputSlots = typeof __propDef.slots;
|
|
21
|
+
export default class ComboboxInput extends SvelteComponent<ComboboxInputProps, ComboboxInputEvents, ComboboxInputSlots> {
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script>import { context } from "./Combobox.svelte";
|
|
2
|
+
import {
|
|
3
|
+
useActions,
|
|
4
|
+
classProp,
|
|
5
|
+
isBrowser
|
|
6
|
+
} from "../../internal/index.js";
|
|
7
|
+
import { createUID } from "../../internal/index.js";
|
|
8
|
+
import { onMount, tick } from "svelte";
|
|
9
|
+
let {
|
|
10
|
+
children,
|
|
11
|
+
class: klass,
|
|
12
|
+
use = [],
|
|
13
|
+
value,
|
|
14
|
+
label: labelProp,
|
|
15
|
+
self = $bindable(),
|
|
16
|
+
disabled,
|
|
17
|
+
onClick,
|
|
18
|
+
onFocus,
|
|
19
|
+
onMouseenter,
|
|
20
|
+
...props
|
|
21
|
+
} = $props();
|
|
22
|
+
let optionEl;
|
|
23
|
+
const API = context();
|
|
24
|
+
const { uid } = createUID("item");
|
|
25
|
+
const hovered = $derived(API.hoveredOption?.id === uid());
|
|
26
|
+
const selected = $derived(!!API.selectedOptions.find((el) => el.dataset.value === value));
|
|
27
|
+
const label = $derived(labelProp || isBrowser && self ? self?.textContent?.trim() : "");
|
|
28
|
+
const handleClick = (e) => {
|
|
29
|
+
onClick?.(e);
|
|
30
|
+
if (!disabled) {
|
|
31
|
+
API.setSelectedOptions();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleFocus = (e) => {
|
|
35
|
+
onFocus?.(e);
|
|
36
|
+
};
|
|
37
|
+
const handleMouseover = (e) => {
|
|
38
|
+
onMouseenter?.(e);
|
|
39
|
+
if (!disabled)
|
|
40
|
+
API.setHoveredOption(uid());
|
|
41
|
+
};
|
|
42
|
+
onMount(() => {
|
|
43
|
+
API.queryElements();
|
|
44
|
+
return async () => {
|
|
45
|
+
if (!API.visible)
|
|
46
|
+
return;
|
|
47
|
+
await tick();
|
|
48
|
+
API.queryElements();
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<button
|
|
54
|
+
bind:this={self}
|
|
55
|
+
bind:this={optionEl}
|
|
56
|
+
use:useActions={use}
|
|
57
|
+
id={uid()}
|
|
58
|
+
class={classProp(klass, { hovered, selected })}
|
|
59
|
+
type="button"
|
|
60
|
+
{disabled}
|
|
61
|
+
role="option"
|
|
62
|
+
tabindex="0"
|
|
63
|
+
aria-selected={selected}
|
|
64
|
+
data-hovered={hovered ? '' : undefined}
|
|
65
|
+
data-selected={selected ? '' : undefined}
|
|
66
|
+
data-comboboxoption=""
|
|
67
|
+
data-value={value}
|
|
68
|
+
data-label={label}
|
|
69
|
+
onmouseover={handleMouseover}
|
|
70
|
+
onfocus={handleFocus}
|
|
71
|
+
onclick={handleClick}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
{@render children({ hovered, selected })}
|
|
75
|
+
</button>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import { type BaseProps, type JsonValue, type Handler } from '../../internal/index.js';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: BaseProps<HTMLButtonElement | HTMLAnchorElement, {
|
|
5
|
+
hovered: boolean;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
}> & {
|
|
8
|
+
value: JsonValue;
|
|
9
|
+
disabled?: boolean | undefined;
|
|
10
|
+
label?: string | undefined;
|
|
11
|
+
onClick?: Handler<MouseEvent, HTMLButtonElement | HTMLAnchorElement> | undefined;
|
|
12
|
+
onFocus?: Handler<FocusEvent, HTMLButtonElement | HTMLAnchorElement> | undefined;
|
|
13
|
+
onMouseenter?: Handler<MouseEvent, HTMLButtonElement | HTMLAnchorElement> | undefined;
|
|
14
|
+
};
|
|
15
|
+
events: {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
};
|
|
18
|
+
slots: {};
|
|
19
|
+
};
|
|
20
|
+
export type ComboboxOptionProps = typeof __propDef.props;
|
|
21
|
+
export type ComboboxOptionEvents = typeof __propDef.events;
|
|
22
|
+
export type ComboboxOptionSlots = typeof __propDef.slots;
|
|
23
|
+
export default class ComboboxOption extends SvelteComponent<ComboboxOptionProps, ComboboxOptionEvents, ComboboxOptionSlots> {
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type CalcIndexAction, type UID, type JsonValue } from '../../internal/index.js';
|
|
2
|
+
export interface Option {
|
|
3
|
+
value: JsonValue;
|
|
4
|
+
label: string;
|
|
5
|
+
id: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface Hooks<ValueType> {
|
|
9
|
+
onChange: (values: {
|
|
10
|
+
newValue?: ValueType;
|
|
11
|
+
newTouched?: boolean;
|
|
12
|
+
newLabel?: string;
|
|
13
|
+
}) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const createContext: <ValueType>(uid: UID, multiple: boolean | undefined, hooks: Hooks<ValueType>) => {
|
|
16
|
+
visible: boolean;
|
|
17
|
+
hoveredIndex: number;
|
|
18
|
+
options: HTMLElement[];
|
|
19
|
+
touched: HTMLElement[];
|
|
20
|
+
dropdown: HTMLElement | null;
|
|
21
|
+
mounted: boolean;
|
|
22
|
+
selectedOptions: HTMLElement[];
|
|
23
|
+
hoveredOption: HTMLElement;
|
|
24
|
+
trigger: HTMLInputElement | null;
|
|
25
|
+
multiple: boolean;
|
|
26
|
+
open(): void;
|
|
27
|
+
close(): void;
|
|
28
|
+
toggle(): void;
|
|
29
|
+
queryElements(): void;
|
|
30
|
+
navigateOptions(action: CalcIndexAction): void;
|
|
31
|
+
setHoveredOption(optionId?: string): void;
|
|
32
|
+
setSelectedOptions(): void;
|
|
33
|
+
setInitialSelected(value: ValueType): Promise<void>;
|
|
34
|
+
setTrigger(node: HTMLInputElement): void;
|
|
35
|
+
setDropdown(node: HTMLElement): void;
|
|
36
|
+
setMounted(value: boolean): void;
|
|
37
|
+
setTouched(value: boolean): void;
|
|
38
|
+
uid: UID;
|
|
39
|
+
};
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { calculateIndex, disableScroll, removeDisabledElements } from '../../internal/index.js';
|
|
2
|
+
import { tick } from 'svelte';
|
|
3
|
+
export const createContext = (uid, multiple = false, hooks) => {
|
|
4
|
+
let visible = $state(true);
|
|
5
|
+
let hoveredIndex = $state(-1);
|
|
6
|
+
let options = $state([]);
|
|
7
|
+
let trigger = $state(null);
|
|
8
|
+
let dropdown = $state(null);
|
|
9
|
+
let selectedOptions = $state([]);
|
|
10
|
+
let mounted = $state(false);
|
|
11
|
+
let touched = $state(false);
|
|
12
|
+
const hoveredOption = $derived(options[hoveredIndex]);
|
|
13
|
+
$effect(() => {
|
|
14
|
+
disableScroll(visible && !document.body.style.overflow);
|
|
15
|
+
});
|
|
16
|
+
$effect(() => {
|
|
17
|
+
if (!visible || !options || hoveredIndex > options.length - 1) {
|
|
18
|
+
hoveredIndex = -1;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
$effect(() => {
|
|
22
|
+
if (visible) {
|
|
23
|
+
tick().then(() => {
|
|
24
|
+
hoveredIndex = options.findIndex((option) => option.ariaSelected === 'true');
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
options = [];
|
|
29
|
+
touched = false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
$effect(() => {
|
|
33
|
+
if (!visible)
|
|
34
|
+
return;
|
|
35
|
+
hooks.onChange({ newTouched: touched });
|
|
36
|
+
});
|
|
37
|
+
const functions = {
|
|
38
|
+
open() {
|
|
39
|
+
visible = true;
|
|
40
|
+
},
|
|
41
|
+
close() {
|
|
42
|
+
visible = false;
|
|
43
|
+
},
|
|
44
|
+
toggle() {
|
|
45
|
+
visible = !visible;
|
|
46
|
+
},
|
|
47
|
+
queryElements() {
|
|
48
|
+
const elements = removeDisabledElements(`#${uid('dropdown')} [data-comboboxoption]`);
|
|
49
|
+
if (!elements)
|
|
50
|
+
return;
|
|
51
|
+
options = elements;
|
|
52
|
+
},
|
|
53
|
+
navigateOptions(action) {
|
|
54
|
+
hoveredIndex = calculateIndex(action, options, hoveredIndex);
|
|
55
|
+
document.querySelector(`#${hoveredOption?.id}`)?.scrollIntoView({ block: 'nearest' });
|
|
56
|
+
},
|
|
57
|
+
setHoveredOption(optionId) {
|
|
58
|
+
if (!optionId)
|
|
59
|
+
return;
|
|
60
|
+
hoveredIndex = options.findIndex((el) => el.id === optionId);
|
|
61
|
+
},
|
|
62
|
+
setSelectedOptions() {
|
|
63
|
+
if (!hoveredOption)
|
|
64
|
+
return;
|
|
65
|
+
if (multiple) {
|
|
66
|
+
if (selectedOptions.find((el) => el.dataset.value === hoveredOption.dataset.value)) {
|
|
67
|
+
selectedOptions = selectedOptions.filter((el) => el.dataset.value !== hoveredOption.dataset.value);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
selectedOptions = [...selectedOptions, hoveredOption];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
selectedOptions[0] = hoveredOption;
|
|
75
|
+
}
|
|
76
|
+
if (!multiple) {
|
|
77
|
+
functions.close();
|
|
78
|
+
}
|
|
79
|
+
const value = multiple ? selectedOptions.map((el) => el.dataset.value) : selectedOptions[0].dataset.value;
|
|
80
|
+
const label = multiple ? '' : selectedOptions[0].dataset.label || '';
|
|
81
|
+
hooks.onChange({ newValue: value, newLabel: label });
|
|
82
|
+
},
|
|
83
|
+
async setInitialSelected(value) {
|
|
84
|
+
selectedOptions = options.filter((el) => {
|
|
85
|
+
if (!Array.isArray(value) && el.dataset.value === value)
|
|
86
|
+
return el;
|
|
87
|
+
else if (Array.isArray(value) && value.includes(el.dataset.value))
|
|
88
|
+
return el;
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
setTrigger(node) {
|
|
92
|
+
trigger = node;
|
|
93
|
+
},
|
|
94
|
+
setDropdown(node) {
|
|
95
|
+
dropdown = node;
|
|
96
|
+
},
|
|
97
|
+
setMounted(value) {
|
|
98
|
+
mounted = value;
|
|
99
|
+
},
|
|
100
|
+
setTouched(value) {
|
|
101
|
+
touched = value;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
uid,
|
|
106
|
+
...functions,
|
|
107
|
+
get visible() {
|
|
108
|
+
return visible;
|
|
109
|
+
},
|
|
110
|
+
get hoveredIndex() {
|
|
111
|
+
return hoveredIndex;
|
|
112
|
+
},
|
|
113
|
+
get options() {
|
|
114
|
+
return options;
|
|
115
|
+
},
|
|
116
|
+
get touched() {
|
|
117
|
+
return options;
|
|
118
|
+
},
|
|
119
|
+
get dropdown() {
|
|
120
|
+
return dropdown;
|
|
121
|
+
},
|
|
122
|
+
get mounted() {
|
|
123
|
+
return mounted;
|
|
124
|
+
},
|
|
125
|
+
get selectedOptions() {
|
|
126
|
+
return selectedOptions;
|
|
127
|
+
},
|
|
128
|
+
get hoveredOption() {
|
|
129
|
+
return hoveredOption;
|
|
130
|
+
},
|
|
131
|
+
get trigger() {
|
|
132
|
+
return trigger;
|
|
133
|
+
},
|
|
134
|
+
multiple
|
|
135
|
+
};
|
|
136
|
+
};
|