bits-ui 1.6.1 → 1.8.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/bits/context-menu/exports.d.ts +2 -1
- package/dist/bits/context-menu/exports.js +1 -0
- package/dist/bits/context-menu/types.d.ts +2 -2
- package/dist/bits/dropdown-menu/exports.d.ts +2 -1
- package/dist/bits/dropdown-menu/exports.js +1 -0
- package/dist/bits/dropdown-menu/types.d.ts +2 -2
- package/dist/bits/menu/components/menu-checkbox-group.svelte +43 -0
- package/dist/bits/menu/components/menu-checkbox-group.svelte.d.ts +4 -0
- package/dist/bits/menu/components/menu-checkbox-item.svelte +53 -24
- package/dist/bits/menu/exports.d.ts +2 -1
- package/dist/bits/menu/exports.js +1 -0
- package/dist/bits/menu/menu.svelte.d.ts +30 -4
- package/dist/bits/menu/menu.svelte.js +68 -3
- package/dist/bits/menu/types.d.ts +17 -0
- package/dist/bits/menubar/exports.d.ts +2 -1
- package/dist/bits/menubar/exports.js +1 -0
- package/dist/bits/menubar/types.d.ts +1 -1
- package/dist/bits/navigation-menu/components/navigation-menu-item.svelte +2 -0
- package/dist/bits/navigation-menu/components/navigation-menu-viewport.svelte +2 -0
- package/dist/bits/navigation-menu/navigation-menu.svelte.d.ts +29 -17
- package/dist/bits/navigation-menu/navigation-menu.svelte.js +94 -24
- package/dist/bits/navigation-menu/types.d.ts +14 -7
- package/dist/bits/select/components/select-scroll-down-button.svelte +1 -1
- package/dist/bits/select/components/select-scroll-up-button.svelte +1 -1
- package/dist/bits/select/select.svelte.d.ts +4 -4
- package/dist/bits/select/select.svelte.js +30 -21
- package/dist/bits/slider/components/slider.svelte +2 -0
- package/dist/bits/slider/slider.svelte.d.ts +2 -1
- package/dist/bits/slider/slider.svelte.js +4 -0
- package/dist/bits/slider/types.d.ts +7 -1
- package/dist/shared/index.d.ts +7 -0
- package/package.json +1 -1
|
@@ -15,4 +15,5 @@ export { default as SubContentStatic } from "../menu/components/menu-sub-content
|
|
|
15
15
|
export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelte";
|
|
16
16
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
17
17
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
18
|
-
export
|
|
18
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
19
|
+
export type { ContextMenuArrowProps as ArrowProps, ContextMenuCheckboxItemProps as CheckboxItemProps, ContextMenuGroupProps as GroupProps, ContextMenuItemProps as ItemProps, ContextMenuGroupHeadingProps as GroupHeadingProps, ContextMenuRootProps as RootProps, ContextMenuRadioGroupProps as RadioGroupProps, ContextMenuRadioItemProps as RadioItemProps, ContextMenuSeparatorProps as SeparatorProps, ContextMenuSubContentProps as SubContentProps, ContextMenuSubContentStaticProps as SubContentStaticProps, ContextMenuSubProps as SubProps, ContextMenuSubTriggerProps as SubTriggerProps, ContextMenuContentProps as ContentProps, ContextMenuContentStaticProps as ContentStaticProps, ContextMenuTriggerProps as TriggerProps, ContextMenuPortalProps as PortalProps, ContextMenuCheckboxGroupProps as CheckboxGroupProps, } from "./types.js";
|
|
@@ -15,3 +15,4 @@ export { default as SubContentStatic } from "../menu/components/menu-sub-content
|
|
|
15
15
|
export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelte";
|
|
16
16
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
17
17
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
18
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
@@ -11,5 +11,5 @@ export type ContextMenuTriggerPropsWithoutHTML = WithChild<{
|
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
}>;
|
|
13
13
|
export type ContextMenuTriggerProps = ContextMenuTriggerPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, ContextMenuTriggerPropsWithoutHTML>;
|
|
14
|
-
export type { MenuArrowProps as ContextMenuArrowProps, MenuContentStaticProps as ContextMenuContentStaticProps, MenuCheckboxItemProps as ContextMenuCheckboxItemProps, MenuGroupProps as ContextMenuGroupProps, MenuItemProps as ContextMenuItemProps, MenuGroupHeadingProps as ContextMenuGroupHeadingProps, MenuRootProps as ContextMenuRootProps, MenuRadioGroupProps as ContextMenuRadioGroupProps, MenuRadioItemProps as ContextMenuRadioItemProps, MenuSeparatorProps as ContextMenuSeparatorProps, MenuSubContentProps as ContextMenuSubContentProps, MenuSubContentStaticProps as ContextMenuSubContentStaticProps, MenuSubProps as ContextMenuSubProps, MenuSubTriggerProps as ContextMenuSubTriggerProps, MenuPortalProps as ContextMenuPortalProps, } from "../menu/types.js";
|
|
15
|
-
export type { MenuRootPropsWithoutHTML as ContextMenuRootPropsWithoutHTML, MenuContentStaticPropsWithoutHTML as ContextMenuContentStaticPropsWithoutHTML, MenuArrowPropsWithoutHTML as ContextMenuArrowPropsWithoutHTML, MenuCheckboxItemPropsWithoutHTML as ContextMenuCheckboxItemPropsWithoutHTML, MenuGroupPropsWithoutHTML as ContextMenuGroupPropsWithoutHTML, MenuItemPropsWithoutHTML as ContextMenuItemPropsWithoutHTML, MenuGroupHeadingPropsWithoutHTML as ContextMenuGroupHeadingPropsWithoutHTML, MenuRadioGroupPropsWithoutHTML as ContextMenuRadioGroupPropsWithoutHTML, MenuRadioItemPropsWithoutHTML as ContextMenuRadioItemPropsWithoutHTML, MenuSeparatorPropsWithoutHTML as ContextMenuSeparatorPropsWithoutHTML, MenuSubPropsWithoutHTML as ContextMenuSubPropsWithoutHTML, MenuSubTriggerPropsWithoutHTML as ContextMenuSubTriggerPropsWithoutHTML, MenuSubContentPropsWithoutHTML as ContextMenuSubContentPropsWithoutHTML, MenuSubContentStaticPropsWithoutHTML as ContextMenuSubContentStaticPropsWithoutHTML, MenuPortalPropsWithoutHTML as ContextMenuPortalPropsWithoutHTML, } from "../menu/types.js";
|
|
14
|
+
export type { MenuArrowProps as ContextMenuArrowProps, MenuContentStaticProps as ContextMenuContentStaticProps, MenuCheckboxItemProps as ContextMenuCheckboxItemProps, MenuGroupProps as ContextMenuGroupProps, MenuItemProps as ContextMenuItemProps, MenuGroupHeadingProps as ContextMenuGroupHeadingProps, MenuRootProps as ContextMenuRootProps, MenuRadioGroupProps as ContextMenuRadioGroupProps, MenuRadioItemProps as ContextMenuRadioItemProps, MenuSeparatorProps as ContextMenuSeparatorProps, MenuSubContentProps as ContextMenuSubContentProps, MenuSubContentStaticProps as ContextMenuSubContentStaticProps, MenuSubProps as ContextMenuSubProps, MenuSubTriggerProps as ContextMenuSubTriggerProps, MenuPortalProps as ContextMenuPortalProps, MenuCheckboxGroupProps as ContextMenuCheckboxGroupProps, } from "../menu/types.js";
|
|
15
|
+
export type { MenuRootPropsWithoutHTML as ContextMenuRootPropsWithoutHTML, MenuContentStaticPropsWithoutHTML as ContextMenuContentStaticPropsWithoutHTML, MenuArrowPropsWithoutHTML as ContextMenuArrowPropsWithoutHTML, MenuCheckboxItemPropsWithoutHTML as ContextMenuCheckboxItemPropsWithoutHTML, MenuGroupPropsWithoutHTML as ContextMenuGroupPropsWithoutHTML, MenuItemPropsWithoutHTML as ContextMenuItemPropsWithoutHTML, MenuGroupHeadingPropsWithoutHTML as ContextMenuGroupHeadingPropsWithoutHTML, MenuRadioGroupPropsWithoutHTML as ContextMenuRadioGroupPropsWithoutHTML, MenuRadioItemPropsWithoutHTML as ContextMenuRadioItemPropsWithoutHTML, MenuSeparatorPropsWithoutHTML as ContextMenuSeparatorPropsWithoutHTML, MenuSubPropsWithoutHTML as ContextMenuSubPropsWithoutHTML, MenuSubTriggerPropsWithoutHTML as ContextMenuSubTriggerPropsWithoutHTML, MenuSubContentPropsWithoutHTML as ContextMenuSubContentPropsWithoutHTML, MenuSubContentStaticPropsWithoutHTML as ContextMenuSubContentStaticPropsWithoutHTML, MenuPortalPropsWithoutHTML as ContextMenuPortalPropsWithoutHTML, MenuCheckboxGroupPropsWithoutHTML as ContextMenuCheckboxGroupPropsWithoutHTML, } from "../menu/types.js";
|
|
@@ -15,4 +15,5 @@ export { default as SubContentStatic } from "../menu/components/menu-sub-content
|
|
|
15
15
|
export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelte";
|
|
16
16
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
17
17
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
18
|
-
export
|
|
18
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
19
|
+
export type { DropdownMenuArrowProps as ArrowProps, DropdownMenuCheckboxItemProps as CheckboxItemProps, DropdownMenuContentProps as ContentProps, DropdownMenuContentStaticProps as ContentStaticProps, DropdownMenuGroupProps as GroupProps, DropdownMenuItemProps as ItemProps, DropdownMenuGroupHeadingProps as GroupHeadingProps, DropdownMenuRootProps as RootProps, DropdownMenuRadioGroupProps as RadioGroupProps, DropdownMenuRadioItemProps as RadioItemProps, DropdownMenuSeparatorProps as SeparatorProps, DropdownMenuSubContentProps as SubContentProps, DropdownMenuSubContentStaticProps as SubContentStaticProps, DropdownMenuSubProps as SubProps, DropdownMenuSubTriggerProps as SubTriggerProps, DropdownMenuTriggerProps as TriggerProps, DropdownMenuPortalProps as PortalProps, DropdownMenuCheckboxGroupProps as CheckboxGroupProps, } from "./types.js";
|
|
@@ -15,3 +15,4 @@ export { default as SubContentStatic } from "../menu/components/menu-sub-content
|
|
|
15
15
|
export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelte";
|
|
16
16
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
17
17
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
18
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { MenuArrowProps as DropdownMenuArrowProps, MenuCheckboxItemProps as DropdownMenuCheckboxItemProps, MenuContentProps as DropdownMenuContentProps, MenuContentStaticProps as DropdownMenuContentStaticProps, MenuGroupProps as DropdownMenuGroupProps, MenuItemProps as DropdownMenuItemProps, MenuGroupHeadingProps as DropdownMenuGroupHeadingProps, MenuRootProps as DropdownMenuRootProps, MenuRadioGroupProps as DropdownMenuRadioGroupProps, MenuRadioItemProps as DropdownMenuRadioItemProps, MenuSeparatorProps as DropdownMenuSeparatorProps, MenuSubContentProps as DropdownMenuSubContentProps, MenuSubContentStaticProps as DropdownMenuSubContentStaticProps, MenuSubProps as DropdownMenuSubProps, MenuSubTriggerProps as DropdownMenuSubTriggerProps, MenuTriggerProps as DropdownMenuTriggerProps, MenuPortalProps as DropdownMenuPortalProps, } from "../menu/types.js";
|
|
2
|
-
export type { MenuRootPropsWithoutHTML as DropdownMenuRootPropsWithoutHTML, MenuArrowPropsWithoutHTML as DropdownMenuArrowPropsWithoutHTML, MenuCheckboxItemPropsWithoutHTML as DropdownMenuCheckboxItemPropsWithoutHTML, MenuContentPropsWithoutHTML as DropdownMenuContentPropsWithoutHTML, MenuContentStaticPropsWithoutHTML as DropdownMenuContentStaticPropsWithoutHTML, MenuGroupPropsWithoutHTML as DropdownMenuGroupPropsWithoutHTML, MenuItemPropsWithoutHTML as DropdownMenuItemPropsWithoutHTML, MenuGroupHeadingPropsWithoutHTML as DropdownMenuGroupHeadingPropsWithoutHTML, MenuRadioGroupPropsWithoutHTML as DropdownMenuRadioGroupPropsWithoutHTML, MenuRadioItemPropsWithoutHTML as DropdownMenuRadioItemPropsWithoutHTML, MenuSeparatorPropsWithoutHTML as DropdownMenuSeparatorPropsWithoutHTML, MenuSubPropsWithoutHTML as DropdownMenuSubPropsWithoutHTML, MenuSubTriggerPropsWithoutHTML as DropdownMenuSubTriggerPropsWithoutHTML, MenuSubContentPropsWithoutHTML as DropdownMenuSubContentPropsWithoutHTML, MenuSubContentStaticPropsWithoutHTML as DropdownMenuSubContentStaticPropsWithoutHTML, MenuTriggerPropsWithoutHTML as DropdownMenuTriggerPropsWithoutHTML, MenuPortalPropsWithoutHTML as DropdownMenuPortalPropsWithoutHTML, } from "../menu/types.js";
|
|
1
|
+
export type { MenuArrowProps as DropdownMenuArrowProps, MenuCheckboxItemProps as DropdownMenuCheckboxItemProps, MenuContentProps as DropdownMenuContentProps, MenuContentStaticProps as DropdownMenuContentStaticProps, MenuGroupProps as DropdownMenuGroupProps, MenuItemProps as DropdownMenuItemProps, MenuGroupHeadingProps as DropdownMenuGroupHeadingProps, MenuRootProps as DropdownMenuRootProps, MenuRadioGroupProps as DropdownMenuRadioGroupProps, MenuRadioItemProps as DropdownMenuRadioItemProps, MenuSeparatorProps as DropdownMenuSeparatorProps, MenuSubContentProps as DropdownMenuSubContentProps, MenuSubContentStaticProps as DropdownMenuSubContentStaticProps, MenuSubProps as DropdownMenuSubProps, MenuSubTriggerProps as DropdownMenuSubTriggerProps, MenuTriggerProps as DropdownMenuTriggerProps, MenuPortalProps as DropdownMenuPortalProps, MenuCheckboxGroupProps as DropdownMenuCheckboxGroupProps, } from "../menu/types.js";
|
|
2
|
+
export type { MenuRootPropsWithoutHTML as DropdownMenuRootPropsWithoutHTML, MenuArrowPropsWithoutHTML as DropdownMenuArrowPropsWithoutHTML, MenuCheckboxItemPropsWithoutHTML as DropdownMenuCheckboxItemPropsWithoutHTML, MenuContentPropsWithoutHTML as DropdownMenuContentPropsWithoutHTML, MenuContentStaticPropsWithoutHTML as DropdownMenuContentStaticPropsWithoutHTML, MenuGroupPropsWithoutHTML as DropdownMenuGroupPropsWithoutHTML, MenuItemPropsWithoutHTML as DropdownMenuItemPropsWithoutHTML, MenuGroupHeadingPropsWithoutHTML as DropdownMenuGroupHeadingPropsWithoutHTML, MenuRadioGroupPropsWithoutHTML as DropdownMenuRadioGroupPropsWithoutHTML, MenuRadioItemPropsWithoutHTML as DropdownMenuRadioItemPropsWithoutHTML, MenuSeparatorPropsWithoutHTML as DropdownMenuSeparatorPropsWithoutHTML, MenuSubPropsWithoutHTML as DropdownMenuSubPropsWithoutHTML, MenuSubTriggerPropsWithoutHTML as DropdownMenuSubTriggerPropsWithoutHTML, MenuSubContentPropsWithoutHTML as DropdownMenuSubContentPropsWithoutHTML, MenuSubContentStaticPropsWithoutHTML as DropdownMenuSubContentStaticPropsWithoutHTML, MenuTriggerPropsWithoutHTML as DropdownMenuTriggerPropsWithoutHTML, MenuPortalPropsWithoutHTML as DropdownMenuPortalPropsWithoutHTML, MenuCheckboxGroupPropsWithoutHTML as DropdownMenuCheckboxGroupPropsWithoutHTML, } from "../menu/types.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { box, mergeProps } from "svelte-toolbelt";
|
|
3
|
+
import type { MenuCheckboxGroupProps } from "../types.js";
|
|
4
|
+
import { useMenuCheckboxGroup } from "../menu.svelte.js";
|
|
5
|
+
import { noop } from "../../../internal/noop.js";
|
|
6
|
+
import { useId } from "../../../internal/use-id.js";
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
id = useId(),
|
|
10
|
+
children,
|
|
11
|
+
child,
|
|
12
|
+
ref = $bindable(null),
|
|
13
|
+
value = $bindable([]),
|
|
14
|
+
onValueChange = noop,
|
|
15
|
+
...restProps
|
|
16
|
+
}: MenuCheckboxGroupProps = $props();
|
|
17
|
+
|
|
18
|
+
const checkboxGroupState = useMenuCheckboxGroup({
|
|
19
|
+
value: box.with(
|
|
20
|
+
() => $state.snapshot(value),
|
|
21
|
+
(v) => {
|
|
22
|
+
value = $state.snapshot(v);
|
|
23
|
+
onValueChange(v);
|
|
24
|
+
}
|
|
25
|
+
),
|
|
26
|
+
onValueChange: box.with(() => onValueChange),
|
|
27
|
+
ref: box.with(
|
|
28
|
+
() => ref,
|
|
29
|
+
(v) => (ref = v)
|
|
30
|
+
),
|
|
31
|
+
id: box.with(() => id),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const mergedProps = $derived(mergeProps(restProps, checkboxGroupState.props));
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
{#if child}
|
|
38
|
+
{@render child({ props: mergedProps })}
|
|
39
|
+
{:else}
|
|
40
|
+
<div {...mergedProps}>
|
|
41
|
+
{@render children?.()}
|
|
42
|
+
</div>
|
|
43
|
+
{/if}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { box, mergeProps } from "svelte-toolbelt";
|
|
3
3
|
import type { MenuCheckboxItemProps } from "../types.js";
|
|
4
|
-
import { useMenuCheckboxItem } from "../menu.svelte.js";
|
|
4
|
+
import { MenuCheckboxGroupContext, useMenuCheckboxItem } from "../menu.svelte.js";
|
|
5
5
|
import { useId } from "../../../internal/use-id.js";
|
|
6
6
|
import { noop } from "../../../internal/noop.js";
|
|
7
|
+
import { watch } from "runed";
|
|
7
8
|
|
|
8
9
|
let {
|
|
9
10
|
child,
|
|
@@ -17,33 +18,61 @@
|
|
|
17
18
|
closeOnSelect = true,
|
|
18
19
|
indeterminate = $bindable(false),
|
|
19
20
|
onIndeterminateChange = noop,
|
|
21
|
+
value = "",
|
|
20
22
|
...restProps
|
|
21
23
|
}: MenuCheckboxItemProps = $props();
|
|
22
24
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
indeterminate = v;
|
|
43
|
-
onIndeterminateChange(v);
|
|
25
|
+
const group = MenuCheckboxGroupContext.getOr(null);
|
|
26
|
+
|
|
27
|
+
if (group && value) {
|
|
28
|
+
if (group.opts.value.current.includes(value)) {
|
|
29
|
+
checked = true;
|
|
30
|
+
} else {
|
|
31
|
+
checked = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
watch.pre(
|
|
36
|
+
() => value,
|
|
37
|
+
() => {
|
|
38
|
+
if (group && value) {
|
|
39
|
+
if (group.opts.value.current.includes(value)) {
|
|
40
|
+
checked = true;
|
|
41
|
+
} else {
|
|
42
|
+
checked = false;
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const checkboxItemState = useMenuCheckboxItem(
|
|
49
|
+
{
|
|
50
|
+
checked: box.with(
|
|
51
|
+
() => checked,
|
|
52
|
+
(v) => {
|
|
53
|
+
checked = v;
|
|
54
|
+
onCheckedChange(v);
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
id: box.with(() => id),
|
|
58
|
+
disabled: box.with(() => disabled),
|
|
59
|
+
onSelect: box.with(() => handleSelect),
|
|
60
|
+
ref: box.with(
|
|
61
|
+
() => ref,
|
|
62
|
+
(v) => (ref = v)
|
|
63
|
+
),
|
|
64
|
+
closeOnSelect: box.with(() => closeOnSelect),
|
|
65
|
+
indeterminate: box.with(
|
|
66
|
+
() => indeterminate,
|
|
67
|
+
(v) => {
|
|
68
|
+
indeterminate = v;
|
|
69
|
+
onIndeterminateChange(v);
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
value: box.with(() => value),
|
|
73
|
+
},
|
|
74
|
+
group
|
|
75
|
+
);
|
|
47
76
|
|
|
48
77
|
function handleSelect(e: Event) {
|
|
49
78
|
onSelect(e);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Root } from "./components/menu.svelte";
|
|
2
2
|
export { default as Arrow } from "./components/menu-arrow.svelte";
|
|
3
|
+
export { default as CheckboxGroup } from "./components/menu-checkbox-group.svelte";
|
|
3
4
|
export { default as CheckboxItem } from "./components/menu-checkbox-item.svelte";
|
|
4
5
|
export { default as Content } from "./components/menu-content.svelte";
|
|
5
6
|
export { default as ContentStatic } from "./components/menu-content-static.svelte";
|
|
@@ -15,4 +16,4 @@ export { default as SubContent } from "./components/menu-sub-content.svelte";
|
|
|
15
16
|
export { default as SubTrigger } from "./components/menu-sub-trigger.svelte";
|
|
16
17
|
export { default as Trigger } from "./components/menu-trigger.svelte";
|
|
17
18
|
export { default as SubContentStatic } from "./components/menu-sub-content-static.svelte";
|
|
18
|
-
export type { MenuRootPropsWithoutHTML as RootProps, MenuContentProps as ContentProps, MenuContentStaticProps as ContentStaticProps, MenuItemProps as ItemProps, MenuTriggerProps as TriggerProps, MenuSubPropsWithoutHTML as SubProps, MenuSubContentProps as SubContentProps, MenuSubContentStaticProps as SubContentStaticProps, MenuSeparatorProps as SeparatorProps, MenuArrowProps as ArrowProps, MenuCheckboxItemProps as CheckboxItemProps, MenuGroupHeadingProps as GroupHeadingProps, MenuGroupProps as GroupProps, MenuRadioGroupProps as RadioGroupProps, MenuRadioItemProps as RadioItemProps, MenuSubTriggerProps as SubTriggerProps, MenuPortalProps as PortalProps, } from "./types.js";
|
|
19
|
+
export type { MenuRootPropsWithoutHTML as RootProps, MenuContentProps as ContentProps, MenuContentStaticProps as ContentStaticProps, MenuItemProps as ItemProps, MenuTriggerProps as TriggerProps, MenuSubPropsWithoutHTML as SubProps, MenuSubContentProps as SubContentProps, MenuSubContentStaticProps as SubContentStaticProps, MenuSeparatorProps as SeparatorProps, MenuArrowProps as ArrowProps, MenuCheckboxGroupProps as CheckboxGroupProps, MenuCheckboxItemProps as CheckboxItemProps, MenuGroupHeadingProps as GroupHeadingProps, MenuGroupProps as GroupProps, MenuRadioGroupProps as RadioGroupProps, MenuRadioItemProps as RadioItemProps, MenuSubTriggerProps as SubTriggerProps, MenuPortalProps as PortalProps, } from "./types.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Root } from "./components/menu.svelte";
|
|
2
2
|
export { default as Arrow } from "./components/menu-arrow.svelte";
|
|
3
|
+
export { default as CheckboxGroup } from "./components/menu-checkbox-group.svelte";
|
|
3
4
|
export { default as CheckboxItem } from "./components/menu-checkbox-item.svelte";
|
|
4
5
|
export { default as Content } from "./components/menu-content.svelte";
|
|
5
6
|
export { default as ContentStatic } from "./components/menu-content-static.svelte";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Context } from "runed";
|
|
1
2
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
2
3
|
import { CustomEventDispatcher } from "../../internal/events.js";
|
|
3
4
|
import type { AnyFn, BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent, WithRefProps } from "../../internal/types.js";
|
|
@@ -5,6 +6,7 @@ import { useRovingFocus } from "../../internal/use-roving-focus.svelte.js";
|
|
|
5
6
|
import type { Direction } from "../../shared/index.js";
|
|
6
7
|
import { IsUsingKeyboard } from "../../index.js";
|
|
7
8
|
export declare const CONTEXT_MENU_TRIGGER_ATTR = "data-context-menu-trigger";
|
|
9
|
+
export declare const MenuCheckboxGroupContext: Context<MenuCheckboxGroupState>;
|
|
8
10
|
type MenuVariant = "context-menu" | "dropdown-menu" | "menubar";
|
|
9
11
|
export type MenuRootStateProps = ReadableBoxedValues<{
|
|
10
12
|
dir: Direction;
|
|
@@ -184,11 +186,14 @@ declare class MenuSubTriggerState {
|
|
|
184
186
|
type MenuCheckboxItemStateProps = WritableBoxedValues<{
|
|
185
187
|
checked: boolean;
|
|
186
188
|
indeterminate: boolean;
|
|
189
|
+
}> & ReadableBoxedValues<{
|
|
190
|
+
value: string;
|
|
187
191
|
}>;
|
|
188
192
|
declare class MenuCheckboxItemState {
|
|
189
193
|
readonly opts: MenuCheckboxItemStateProps;
|
|
190
194
|
readonly item: MenuItemState;
|
|
191
|
-
|
|
195
|
+
readonly group: MenuCheckboxGroupState | null;
|
|
196
|
+
constructor(opts: MenuCheckboxItemStateProps, item: MenuItemState, group?: MenuCheckboxGroupState | null);
|
|
192
197
|
toggleChecked(): void;
|
|
193
198
|
snippetProps: {
|
|
194
199
|
checked: boolean;
|
|
@@ -231,8 +236,8 @@ declare class MenuGroupState {
|
|
|
231
236
|
type MenuGroupHeadingStateProps = WithRefProps;
|
|
232
237
|
declare class MenuGroupHeadingState {
|
|
233
238
|
readonly opts: MenuGroupHeadingStateProps;
|
|
234
|
-
readonly group: MenuGroupState | MenuRadioGroupState;
|
|
235
|
-
constructor(opts: MenuGroupHeadingStateProps, group: MenuGroupState | MenuRadioGroupState);
|
|
239
|
+
readonly group: MenuGroupState | MenuRadioGroupState | MenuCheckboxGroupState;
|
|
240
|
+
constructor(opts: MenuGroupHeadingStateProps, group: MenuGroupState | MenuRadioGroupState | MenuCheckboxGroupState);
|
|
236
241
|
props: {
|
|
237
242
|
readonly [x: string]: string;
|
|
238
243
|
readonly id: string;
|
|
@@ -360,6 +365,26 @@ declare class ContextMenuTriggerState {
|
|
|
360
365
|
readonly oncontextmenu: (e: BitsMouseEvent) => void;
|
|
361
366
|
};
|
|
362
367
|
}
|
|
368
|
+
type MenuCheckboxGroupStateProps = WithRefProps & ReadableBoxedValues<{
|
|
369
|
+
onValueChange: (value: string[]) => void;
|
|
370
|
+
}> & WritableBoxedValues<{
|
|
371
|
+
value: string[];
|
|
372
|
+
}>;
|
|
373
|
+
declare class MenuCheckboxGroupState {
|
|
374
|
+
readonly opts: MenuCheckboxGroupStateProps;
|
|
375
|
+
readonly content: MenuContentState;
|
|
376
|
+
groupHeadingId: string | null;
|
|
377
|
+
root: MenuRootState;
|
|
378
|
+
constructor(opts: MenuCheckboxGroupStateProps, content: MenuContentState);
|
|
379
|
+
addValue(checkboxValue: string | undefined): void;
|
|
380
|
+
removeValue(checkboxValue: string | undefined): void;
|
|
381
|
+
props: {
|
|
382
|
+
readonly [x: string]: string | null;
|
|
383
|
+
readonly id: string;
|
|
384
|
+
readonly role: "group";
|
|
385
|
+
readonly "aria-labelledby": string | null;
|
|
386
|
+
};
|
|
387
|
+
}
|
|
363
388
|
type MenuItemCombinedProps = MenuItemSharedStateProps & MenuItemStateProps;
|
|
364
389
|
export declare function useMenuRoot(props: MenuRootStateProps): MenuRootState;
|
|
365
390
|
export declare function useMenuMenu(root: MenuRootState, props: MenuMenuStateProps): MenuMenuState;
|
|
@@ -369,11 +394,12 @@ export declare function useMenuDropdownTrigger(props: DropdownMenuTriggerStatePr
|
|
|
369
394
|
export declare function useMenuContextTrigger(props: ContextMenuTriggerStateProps): ContextMenuTriggerState;
|
|
370
395
|
export declare function useMenuContent(props: MenuContentStateProps): MenuContentState;
|
|
371
396
|
export declare function useMenuItem(props: MenuItemCombinedProps): MenuItemState;
|
|
372
|
-
export declare function useMenuCheckboxItem(props: MenuItemCombinedProps & MenuCheckboxItemStateProps): MenuCheckboxItemState;
|
|
397
|
+
export declare function useMenuCheckboxItem(props: MenuItemCombinedProps & MenuCheckboxItemStateProps, checkboxGroup: MenuCheckboxGroupState | null): MenuCheckboxItemState;
|
|
373
398
|
export declare function useMenuRadioGroup(props: MenuRadioGroupStateProps): MenuGroupState | MenuRadioGroupState;
|
|
374
399
|
export declare function useMenuRadioItem(props: MenuRadioItemStateProps & MenuItemCombinedProps): MenuRadioItemState;
|
|
375
400
|
export declare function useMenuGroup(props: MenuGroupStateProps): MenuGroupState | MenuRadioGroupState;
|
|
376
401
|
export declare function useMenuGroupHeading(props: MenuGroupHeadingStateProps): MenuGroupHeadingState;
|
|
377
402
|
export declare function useMenuSeparator(props: MenuSeparatorStateProps): MenuSeparatorState;
|
|
378
403
|
export declare function useMenuArrow(): MenuArrowState;
|
|
404
|
+
export declare function useMenuCheckboxGroup(props: MenuCheckboxGroupStateProps): MenuCheckboxGroupState;
|
|
379
405
|
export {};
|
|
@@ -19,6 +19,7 @@ const MenuMenuContext = new Context("Menu.Root | Menu.Sub");
|
|
|
19
19
|
const MenuContentContext = new Context("Menu.Content");
|
|
20
20
|
const MenuGroupContext = new Context("Menu.Group | Menu.RadioGroup");
|
|
21
21
|
const MenuRadioGroupContext = new Context("Menu.RadioGroup");
|
|
22
|
+
export const MenuCheckboxGroupContext = new Context("Menu.CheckboxGroup");
|
|
22
23
|
export const MenuOpenEvent = new CustomEventDispatcher("bitsmenuopen", {
|
|
23
24
|
bubbles: false,
|
|
24
25
|
cancelable: true,
|
|
@@ -534,9 +535,26 @@ class MenuSubTriggerState {
|
|
|
534
535
|
class MenuCheckboxItemState {
|
|
535
536
|
opts;
|
|
536
537
|
item;
|
|
537
|
-
|
|
538
|
+
group;
|
|
539
|
+
constructor(opts, item, group = null) {
|
|
538
540
|
this.opts = opts;
|
|
539
541
|
this.item = item;
|
|
542
|
+
this.group = group;
|
|
543
|
+
// Watch for value changes in the group if we're part of one
|
|
544
|
+
if (this.group) {
|
|
545
|
+
watch(() => this.group.opts.value.current, (groupValues) => {
|
|
546
|
+
this.opts.checked.current = groupValues.includes(this.opts.value.current);
|
|
547
|
+
});
|
|
548
|
+
// Watch for checked state changes and sync with group
|
|
549
|
+
watch(() => this.opts.checked.current, (checked) => {
|
|
550
|
+
if (checked) {
|
|
551
|
+
this.group.addValue(this.opts.value.current);
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
this.group.removeValue(this.opts.value.current);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
540
558
|
}
|
|
541
559
|
toggleChecked() {
|
|
542
560
|
if (this.opts.indeterminate.current) {
|
|
@@ -818,6 +836,43 @@ class ContextMenuTriggerState {
|
|
|
818
836
|
oncontextmenu: this.oncontextmenu,
|
|
819
837
|
}));
|
|
820
838
|
}
|
|
839
|
+
class MenuCheckboxGroupState {
|
|
840
|
+
opts;
|
|
841
|
+
content;
|
|
842
|
+
groupHeadingId = $state(null);
|
|
843
|
+
root;
|
|
844
|
+
constructor(opts, content) {
|
|
845
|
+
this.opts = opts;
|
|
846
|
+
this.content = content;
|
|
847
|
+
this.root = content.parentMenu.root;
|
|
848
|
+
useRefById(opts);
|
|
849
|
+
}
|
|
850
|
+
addValue(checkboxValue) {
|
|
851
|
+
if (!checkboxValue)
|
|
852
|
+
return;
|
|
853
|
+
if (!this.opts.value.current.includes(checkboxValue)) {
|
|
854
|
+
const newValue = [...$state.snapshot(this.opts.value.current), checkboxValue];
|
|
855
|
+
this.opts.value.current = newValue;
|
|
856
|
+
this.opts.onValueChange.current(newValue);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
removeValue(checkboxValue) {
|
|
860
|
+
if (!checkboxValue)
|
|
861
|
+
return;
|
|
862
|
+
const index = this.opts.value.current.indexOf(checkboxValue);
|
|
863
|
+
if (index === -1)
|
|
864
|
+
return;
|
|
865
|
+
const newValue = this.opts.value.current.filter((v) => v !== checkboxValue);
|
|
866
|
+
this.opts.value.current = newValue;
|
|
867
|
+
this.opts.onValueChange.current(newValue);
|
|
868
|
+
}
|
|
869
|
+
props = $derived.by(() => ({
|
|
870
|
+
id: this.opts.id.current,
|
|
871
|
+
[this.root.getAttr("checkbox-group")]: "",
|
|
872
|
+
role: "group",
|
|
873
|
+
"aria-labelledby": this.groupHeadingId,
|
|
874
|
+
}));
|
|
875
|
+
}
|
|
821
876
|
export function useMenuRoot(props) {
|
|
822
877
|
const root = new MenuRootState(props);
|
|
823
878
|
FocusScopeContext.set({
|
|
@@ -853,9 +908,9 @@ export function useMenuItem(props) {
|
|
|
853
908
|
const item = new MenuItemSharedState(props, MenuContentContext.get());
|
|
854
909
|
return new MenuItemState(props, item);
|
|
855
910
|
}
|
|
856
|
-
export function useMenuCheckboxItem(props) {
|
|
911
|
+
export function useMenuCheckboxItem(props, checkboxGroup) {
|
|
857
912
|
const item = new MenuItemState(props, new MenuItemSharedState(props, MenuContentContext.get()));
|
|
858
|
-
return new MenuCheckboxItemState(props, item);
|
|
913
|
+
return new MenuCheckboxItemState(props, item, checkboxGroup);
|
|
859
914
|
}
|
|
860
915
|
export function useMenuRadioGroup(props) {
|
|
861
916
|
return MenuGroupContext.set(MenuRadioGroupContext.set(new MenuRadioGroupState(props, MenuContentContext.get())));
|
|
@@ -870,6 +925,13 @@ export function useMenuGroup(props) {
|
|
|
870
925
|
return MenuGroupContext.set(new MenuGroupState(props, MenuRootContext.get()));
|
|
871
926
|
}
|
|
872
927
|
export function useMenuGroupHeading(props) {
|
|
928
|
+
// Try to get checkbox group first, then radio group, then regular group
|
|
929
|
+
const checkboxGroup = MenuCheckboxGroupContext.getOr(null);
|
|
930
|
+
if (checkboxGroup)
|
|
931
|
+
return new MenuGroupHeadingState(props, checkboxGroup);
|
|
932
|
+
const radioGroup = MenuRadioGroupContext.getOr(null);
|
|
933
|
+
if (radioGroup)
|
|
934
|
+
return new MenuGroupHeadingState(props, radioGroup);
|
|
873
935
|
return new MenuGroupHeadingState(props, MenuGroupContext.get());
|
|
874
936
|
}
|
|
875
937
|
export function useMenuSeparator(props) {
|
|
@@ -878,3 +940,6 @@ export function useMenuSeparator(props) {
|
|
|
878
940
|
export function useMenuArrow() {
|
|
879
941
|
return new MenuArrowState(MenuRootContext.get());
|
|
880
942
|
}
|
|
943
|
+
export function useMenuCheckboxGroup(props) {
|
|
944
|
+
return MenuCheckboxGroupContext.set(new MenuCheckboxGroupState(props, MenuContentContext.get()));
|
|
945
|
+
}
|
|
@@ -98,8 +98,25 @@ export type MenuCheckboxItemPropsWithoutHTML = MenuItemPropsWithoutHTML<MenuChec
|
|
|
98
98
|
* @defaultValue true
|
|
99
99
|
*/
|
|
100
100
|
closeOnSelect?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* The value of the checkbox item when used in a checkbox group.
|
|
103
|
+
*/
|
|
104
|
+
value?: string;
|
|
101
105
|
};
|
|
102
106
|
export type MenuCheckboxItemProps = MenuCheckboxItemPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, MenuCheckboxItemPropsWithoutHTML>;
|
|
107
|
+
export type MenuCheckboxGroupPropsWithoutHTML = WithChild<{
|
|
108
|
+
/**
|
|
109
|
+
* The values of the selected checkbox items.
|
|
110
|
+
*
|
|
111
|
+
* Supports two-way binding with `bind:value`.
|
|
112
|
+
*/
|
|
113
|
+
value?: string[];
|
|
114
|
+
/**
|
|
115
|
+
* A callback that is fired when the selected checkbox items change.
|
|
116
|
+
*/
|
|
117
|
+
onValueChange?: OnChangeFn<string[]>;
|
|
118
|
+
}>;
|
|
119
|
+
export type MenuCheckboxGroupProps = MenuCheckboxGroupPropsWithoutHTML & Without<BitsPrimitiveDivAttributes, MenuCheckboxGroupPropsWithoutHTML>;
|
|
103
120
|
export type MenuTriggerPropsWithoutHTML = WithChild<{
|
|
104
121
|
/**
|
|
105
122
|
* Whether the trigger is disabled.
|
|
@@ -16,5 +16,6 @@ export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelt
|
|
|
16
16
|
export { default as RadioGroup } from "../menu/components/menu-radio-group.svelte";
|
|
17
17
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
18
18
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
19
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
19
20
|
export type { MenubarRootProps as RootProps, MenubarMenuProps as MenuProps, MenubarTriggerProps as TriggerProps, MenubarContentProps as ContentProps, MenubarContentStaticProps as ContentStaticProps, MenubarPortalProps as PortalProps, } from "./types.js";
|
|
20
|
-
export type { MenuSubPropsWithoutHTML as SubProps, MenuItemProps as ItemProps, MenuGroupProps as GroupProps, MenuGroupHeadingProps as GroupHeadingProps, MenuArrowProps as ArrowProps, MenuRadioItemProps as RadioItemProps, MenuSeparatorProps as SeparatorProps, MenuSubContentProps as SubContentProps, MenuSubTriggerProps as SubTriggerProps, MenuRadioGroupProps as RadioGroupProps, MenuCheckboxItemProps as CheckboxItemProps, MenuSubContentStaticProps as SubContentStaticProps, } from "../menu/types.js";
|
|
21
|
+
export type { MenuSubPropsWithoutHTML as SubProps, MenuItemProps as ItemProps, MenuGroupProps as GroupProps, MenuGroupHeadingProps as GroupHeadingProps, MenuArrowProps as ArrowProps, MenuRadioItemProps as RadioItemProps, MenuSeparatorProps as SeparatorProps, MenuSubContentProps as SubContentProps, MenuSubTriggerProps as SubTriggerProps, MenuRadioGroupProps as RadioGroupProps, MenuCheckboxItemProps as CheckboxItemProps, MenuSubContentStaticProps as SubContentStaticProps, MenuCheckboxGroupProps as CheckboxGroupProps, } from "../menu/types.js";
|
|
@@ -16,3 +16,4 @@ export { default as SubTrigger } from "../menu/components/menu-sub-trigger.svelt
|
|
|
16
16
|
export { default as RadioGroup } from "../menu/components/menu-radio-group.svelte";
|
|
17
17
|
export { default as CheckboxItem } from "../menu/components/menu-checkbox-item.svelte";
|
|
18
18
|
export { default as Portal } from "../utilities/portal/portal.svelte";
|
|
19
|
+
export { default as CheckboxGroup } from "../menu/components/menu-checkbox-group.svelte";
|
|
@@ -44,6 +44,6 @@ export type MenubarTriggerPropsWithoutHTML = WithChild<{
|
|
|
44
44
|
disabled?: boolean | null | undefined;
|
|
45
45
|
}>;
|
|
46
46
|
export type MenubarTriggerProps = MenubarTriggerPropsWithoutHTML & Without<BitsPrimitiveButtonAttributes, MenubarTriggerPropsWithoutHTML>;
|
|
47
|
-
export type { MenuContentPropsWithoutHTML as MenubarContentPropsWithoutHTML, MenuContentProps as MenubarContentProps, MenuContentStaticPropsWithoutHTML as MenubarContentStaticPropsWithoutHTML, MenuContentStaticProps as MenubarContentStaticProps, MenuItemPropsWithoutHTML as MenubarItemPropsWithoutHTML, MenuItemProps as MenubarItemProps, MenuGroupPropsWithoutHTML as MenubarGroupPropsWithoutHTML, MenuGroupProps as MenubarGroupProps, MenuGroupHeadingPropsWithoutHTML as MenubarGroupHeadingPropsWithoutHTML, MenuGroupHeadingProps as MenubarGroupHeadingProps, MenuCheckboxItemPropsWithoutHTML as MenubarCheckboxItemPropsWithoutHTML, MenuCheckboxItemProps as MenubarCheckboxItemProps, MenuRadioGroupPropsWithoutHTML as MenubarRadioGroupPropsWithoutHTML, MenuRadioGroupProps as MenubarRadioGroupProps, MenuRadioItemPropsWithoutHTML as MenubarRadioItemPropsWithoutHTML, MenuRadioItemProps as MenubarRadioItemProps, MenuSeparatorPropsWithoutHTML as MenubarSeparatorPropsWithoutHTML, MenuSeparatorProps as MenubarSeparatorProps, MenuSubContentPropsWithoutHTML as MenubarSubContentPropsWithoutHTML, MenuSubContentProps as MenubarSubContentProps, MenuSubContentStaticPropsWithoutHTML as MenubarSubContentStaticPropsWithoutHTML, MenuSubContentStaticProps as MenubarSubContentStaticProps, MenuSubTriggerPropsWithoutHTML as MenubarSubTriggerPropsWithoutHTML, MenuSubTriggerProps as MenubarSubTriggerProps, MenuSubPropsWithoutHTML as MenubarSubPropsWithoutHTML, MenuPortalPropsWithoutHTML as MenubarPortalPropsWithoutHTML, MenuPortalProps as MenubarPortalProps, } from "../menu/types.js";
|
|
47
|
+
export type { MenuContentPropsWithoutHTML as MenubarContentPropsWithoutHTML, MenuContentProps as MenubarContentProps, MenuContentStaticPropsWithoutHTML as MenubarContentStaticPropsWithoutHTML, MenuContentStaticProps as MenubarContentStaticProps, MenuItemPropsWithoutHTML as MenubarItemPropsWithoutHTML, MenuItemProps as MenubarItemProps, MenuGroupPropsWithoutHTML as MenubarGroupPropsWithoutHTML, MenuGroupProps as MenubarGroupProps, MenuGroupHeadingPropsWithoutHTML as MenubarGroupHeadingPropsWithoutHTML, MenuGroupHeadingProps as MenubarGroupHeadingProps, MenuCheckboxItemPropsWithoutHTML as MenubarCheckboxItemPropsWithoutHTML, MenuCheckboxItemProps as MenubarCheckboxItemProps, MenuRadioGroupPropsWithoutHTML as MenubarRadioGroupPropsWithoutHTML, MenuRadioGroupProps as MenubarRadioGroupProps, MenuRadioItemPropsWithoutHTML as MenubarRadioItemPropsWithoutHTML, MenuRadioItemProps as MenubarRadioItemProps, MenuSeparatorPropsWithoutHTML as MenubarSeparatorPropsWithoutHTML, MenuSeparatorProps as MenubarSeparatorProps, MenuSubContentPropsWithoutHTML as MenubarSubContentPropsWithoutHTML, MenuSubContentProps as MenubarSubContentProps, MenuSubContentStaticPropsWithoutHTML as MenubarSubContentStaticPropsWithoutHTML, MenuSubContentStaticProps as MenubarSubContentStaticProps, MenuSubTriggerPropsWithoutHTML as MenubarSubTriggerPropsWithoutHTML, MenuSubTriggerProps as MenubarSubTriggerProps, MenuSubPropsWithoutHTML as MenubarSubPropsWithoutHTML, MenuPortalPropsWithoutHTML as MenubarPortalPropsWithoutHTML, MenuPortalProps as MenubarPortalProps, MenuCheckboxGroupPropsWithoutHTML as MenubarCheckboxGroupPropsWithoutHTML, MenuCheckboxGroupProps as MenubarCheckboxGroupProps, } from "../menu/types.js";
|
|
48
48
|
export type MenubarArrowPropsWithoutHTML = ArrowPropsWithoutHTML;
|
|
49
49
|
export type MenubarArrowProps = MenuArrowProps;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
ref = $bindable(null),
|
|
11
11
|
child,
|
|
12
12
|
children,
|
|
13
|
+
openOnHover = true,
|
|
13
14
|
...restProps
|
|
14
15
|
}: NavigationMenuItemProps = $props();
|
|
15
16
|
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
(v) => (ref = v)
|
|
21
22
|
),
|
|
22
23
|
value: box.with(() => value),
|
|
24
|
+
openOnHover: box.with(() => openOnHover),
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
const mergedProps = $derived(mergeProps(restProps, itemState.props));
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { useId } from "../../../internal/use-id.js";
|
|
5
5
|
import PresenceLayer from "../../utilities/presence-layer/presence-layer.svelte";
|
|
6
6
|
import { box, mergeProps } from "svelte-toolbelt";
|
|
7
|
+
import { Mounted } from "../../utilities/index.js";
|
|
7
8
|
|
|
8
9
|
let {
|
|
9
10
|
id = useId(),
|
|
@@ -34,5 +35,6 @@
|
|
|
34
35
|
{@render children?.()}
|
|
35
36
|
</div>
|
|
36
37
|
{/if}
|
|
38
|
+
<Mounted bind:mounted={viewportState.mounted} />
|
|
37
39
|
{/snippet}
|
|
38
40
|
</PresenceLayer>
|
|
@@ -9,6 +9,7 @@ import { SvelteMap } from "svelte/reactivity";
|
|
|
9
9
|
import { type Direction, type Orientation } from "../../shared/index.js";
|
|
10
10
|
import type { BitsFocusEvent, BitsKeyboardEvent, BitsMouseEvent, BitsPointerEvent } from "../../internal/types.js";
|
|
11
11
|
import { useRovingFocus } from "../../internal/use-roving-focus.svelte.js";
|
|
12
|
+
import type { FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler } from "svelte/elements";
|
|
12
13
|
type NavigationMenuProviderStateProps = ReadableBoxedValues<{
|
|
13
14
|
dir: Direction;
|
|
14
15
|
orientation: Orientation;
|
|
@@ -18,11 +19,11 @@ type NavigationMenuProviderStateProps = ReadableBoxedValues<{
|
|
|
18
19
|
previousValue: string;
|
|
19
20
|
}> & {
|
|
20
21
|
isRootMenu: boolean;
|
|
21
|
-
onTriggerEnter: (itemValue: string) => void;
|
|
22
|
+
onTriggerEnter: (itemValue: string, itemState: NavigationMenuItemState | null) => void;
|
|
22
23
|
onTriggerLeave?: () => void;
|
|
23
24
|
onContentEnter?: () => void;
|
|
24
25
|
onContentLeave?: () => void;
|
|
25
|
-
onItemSelect: (itemValue: string) => void;
|
|
26
|
+
onItemSelect: (itemValue: string, itemState: NavigationMenuItemState | null) => void;
|
|
26
27
|
onItemDismiss: () => void;
|
|
27
28
|
};
|
|
28
29
|
declare class NavigationMenuProviderState {
|
|
@@ -36,7 +37,10 @@ declare class NavigationMenuProviderState {
|
|
|
36
37
|
onContentLeave: () => void;
|
|
37
38
|
onItemSelect: NavigationMenuProviderStateProps["onItemSelect"];
|
|
38
39
|
onItemDismiss: NavigationMenuProviderStateProps["onItemDismiss"];
|
|
40
|
+
activeItem: NavigationMenuItemState | null;
|
|
41
|
+
prevActiveItem: NavigationMenuItemState | null;
|
|
39
42
|
constructor(opts: NavigationMenuProviderStateProps);
|
|
43
|
+
setActiveItem: (item: NavigationMenuItemState | null) => void;
|
|
40
44
|
}
|
|
41
45
|
type NavigationMenuRootStateProps = WithRefProps<WritableBoxedValues<{
|
|
42
46
|
value: string;
|
|
@@ -53,7 +57,7 @@ declare class NavigationMenuRootState {
|
|
|
53
57
|
previousValue: WritableBox<string>;
|
|
54
58
|
isDelaySkipped: WritableBox<boolean>;
|
|
55
59
|
constructor(opts: NavigationMenuRootStateProps);
|
|
56
|
-
setValue: (newValue: string) => void;
|
|
60
|
+
setValue: (newValue: string, itemState: NavigationMenuItemState | null) => void;
|
|
57
61
|
props: {
|
|
58
62
|
readonly id: string;
|
|
59
63
|
readonly "data-orientation": "horizontal" | "vertical";
|
|
@@ -71,8 +75,9 @@ declare class NavigationMenuSubState {
|
|
|
71
75
|
readonly opts: NavigationMenuSubStateProps;
|
|
72
76
|
readonly context: NavigationMenuProviderState;
|
|
73
77
|
previousValue: WritableBox<string>;
|
|
78
|
+
subProvider: NavigationMenuProviderState;
|
|
74
79
|
constructor(opts: NavigationMenuSubStateProps, context: NavigationMenuProviderState);
|
|
75
|
-
setValue: (newValue: string) => void;
|
|
80
|
+
setValue: (newValue: string, itemState: NavigationMenuItemState | null) => void;
|
|
76
81
|
props: {
|
|
77
82
|
readonly id: string;
|
|
78
83
|
readonly "data-orientation": "horizontal" | "vertical";
|
|
@@ -102,6 +107,7 @@ declare class NavigationMenuListState {
|
|
|
102
107
|
}
|
|
103
108
|
type NavigationMenuItemStateProps = WithRefProps<ReadableBoxedValues<{
|
|
104
109
|
value: string;
|
|
110
|
+
openOnHover: boolean;
|
|
105
111
|
}>>;
|
|
106
112
|
export declare class NavigationMenuItemState {
|
|
107
113
|
#private;
|
|
@@ -147,13 +153,14 @@ declare class NavigationMenuTriggerState {
|
|
|
147
153
|
provider: NavigationMenuProviderState;
|
|
148
154
|
item: NavigationMenuItemState;
|
|
149
155
|
list: NavigationMenuListState;
|
|
156
|
+
sub: NavigationMenuSubState | null;
|
|
150
157
|
});
|
|
151
158
|
onpointerenter: (_: BitsPointerEvent<HTMLButtonElement>) => void;
|
|
152
|
-
onpointermove:
|
|
153
|
-
onpointerleave:
|
|
154
|
-
onclick:
|
|
155
|
-
onkeydown:
|
|
156
|
-
focusProxyOnFocus:
|
|
159
|
+
onpointermove: PointerEventHandler<HTMLElement>;
|
|
160
|
+
onpointerleave: PointerEventHandler<HTMLElement>;
|
|
161
|
+
onclick: MouseEventHandler<HTMLButtonElement>;
|
|
162
|
+
onkeydown: KeyboardEventHandler<HTMLButtonElement>;
|
|
163
|
+
focusProxyOnFocus: FocusEventHandler<HTMLElement>;
|
|
157
164
|
props: {
|
|
158
165
|
readonly id: string;
|
|
159
166
|
readonly disabled: boolean | null | undefined;
|
|
@@ -163,16 +170,16 @@ declare class NavigationMenuTriggerState {
|
|
|
163
170
|
readonly "aria-expanded": "true" | "false";
|
|
164
171
|
readonly "aria-controls": string | undefined;
|
|
165
172
|
readonly "data-navigation-menu-trigger": "";
|
|
166
|
-
readonly onpointermove:
|
|
167
|
-
readonly onpointerleave:
|
|
173
|
+
readonly onpointermove: PointerEventHandler<HTMLElement>;
|
|
174
|
+
readonly onpointerleave: PointerEventHandler<HTMLElement>;
|
|
168
175
|
readonly onpointerenter: (_: BitsPointerEvent<HTMLButtonElement>) => void;
|
|
169
|
-
readonly onclick:
|
|
170
|
-
readonly onkeydown:
|
|
176
|
+
readonly onclick: MouseEventHandler<HTMLButtonElement>;
|
|
177
|
+
readonly onkeydown: KeyboardEventHandler<HTMLButtonElement>;
|
|
171
178
|
};
|
|
172
179
|
focusProxyProps: {
|
|
173
180
|
readonly id: string;
|
|
174
181
|
readonly tabindex: 0;
|
|
175
|
-
readonly onfocus:
|
|
182
|
+
readonly onfocus: FocusEventHandler<HTMLElement>;
|
|
176
183
|
};
|
|
177
184
|
restructureSpanProps: {
|
|
178
185
|
readonly "aria-owns": string | undefined;
|
|
@@ -183,6 +190,7 @@ type NavigationMenuLinkStateProps = WithRefProps & ReadableBoxedValues<{
|
|
|
183
190
|
onSelect: (e: Event) => void;
|
|
184
191
|
}>;
|
|
185
192
|
declare class NavigationMenuLinkState {
|
|
193
|
+
#private;
|
|
186
194
|
readonly opts: NavigationMenuLinkStateProps;
|
|
187
195
|
readonly context: {
|
|
188
196
|
provider: NavigationMenuProviderState;
|
|
@@ -197,6 +205,8 @@ declare class NavigationMenuLinkState {
|
|
|
197
205
|
onkeydown: (e: BitsKeyboardEvent) => void;
|
|
198
206
|
onfocus: (_: BitsFocusEvent) => void;
|
|
199
207
|
onblur: (_: BitsFocusEvent) => void;
|
|
208
|
+
onpointerenter: PointerEventHandler<HTMLAnchorElement>;
|
|
209
|
+
onpointermove: PointerEventHandler<HTMLElement>;
|
|
200
210
|
props: {
|
|
201
211
|
readonly id: string;
|
|
202
212
|
readonly "data-active": "" | undefined;
|
|
@@ -206,6 +216,8 @@ declare class NavigationMenuLinkState {
|
|
|
206
216
|
readonly onkeydown: (e: BitsKeyboardEvent) => void;
|
|
207
217
|
readonly onfocus: (_: BitsFocusEvent) => void;
|
|
208
218
|
readonly onblur: (_: BitsFocusEvent) => void;
|
|
219
|
+
readonly onpointerenter: PointerEventHandler<HTMLAnchorElement>;
|
|
220
|
+
readonly onpointermove: PointerEventHandler<HTMLElement>;
|
|
209
221
|
readonly "data-navigation-menu-link": "";
|
|
210
222
|
};
|
|
211
223
|
}
|
|
@@ -266,11 +278,11 @@ declare class NavigationMenuContentState {
|
|
|
266
278
|
list: NavigationMenuListState;
|
|
267
279
|
});
|
|
268
280
|
onpointerenter: (_: BitsPointerEvent) => void;
|
|
269
|
-
onpointerleave:
|
|
281
|
+
onpointerleave: PointerEventHandler<HTMLElement>;
|
|
270
282
|
props: {
|
|
271
283
|
readonly id: string;
|
|
272
284
|
readonly onpointerenter: (_: BitsPointerEvent) => void;
|
|
273
|
-
readonly onpointerleave:
|
|
285
|
+
readonly onpointerleave: PointerEventHandler<HTMLElement>;
|
|
274
286
|
};
|
|
275
287
|
}
|
|
276
288
|
type MotionAttribute = "to-start" | "to-end" | "from-start" | "from-end";
|
|
@@ -309,6 +321,7 @@ declare class NavigationMenuViewportState {
|
|
|
309
321
|
viewportWidth: string | undefined;
|
|
310
322
|
viewportHeight: string | undefined;
|
|
311
323
|
activeContentValue: string;
|
|
324
|
+
mounted: boolean;
|
|
312
325
|
constructor(opts: NavigationMenuViewportImplStateProps, context: NavigationMenuProviderState);
|
|
313
326
|
props: {
|
|
314
327
|
readonly id: string;
|
|
@@ -338,5 +351,4 @@ export declare function useNavigationMenuLink(props: NavigationMenuLinkStateProp
|
|
|
338
351
|
export declare function useNavigationMenuContentImpl(props: NavigationMenuContentImplStateProps, itemState?: NavigationMenuItemState): NavigationMenuContentImplState;
|
|
339
352
|
export declare function useNavigationMenuViewport(props: NavigationMenuViewportImplStateProps): NavigationMenuViewportState;
|
|
340
353
|
export declare function useNavigationMenuIndicator(): NavigationMenuIndicatorState;
|
|
341
|
-
type BitsPointerEventHandler<T extends HTMLElement = HTMLElement> = (e: BitsPointerEvent<T>) => void;
|
|
342
354
|
export {};
|
|
@@ -38,6 +38,8 @@ class NavigationMenuProviderState {
|
|
|
38
38
|
onContentLeave = noop;
|
|
39
39
|
onItemSelect;
|
|
40
40
|
onItemDismiss;
|
|
41
|
+
activeItem = null;
|
|
42
|
+
prevActiveItem = null;
|
|
41
43
|
constructor(opts) {
|
|
42
44
|
this.opts = opts;
|
|
43
45
|
this.onTriggerEnter = opts.onTriggerEnter;
|
|
@@ -47,6 +49,10 @@ class NavigationMenuProviderState {
|
|
|
47
49
|
this.onItemDismiss = opts.onItemDismiss;
|
|
48
50
|
this.onItemSelect = opts.onItemSelect;
|
|
49
51
|
}
|
|
52
|
+
setActiveItem = (item) => {
|
|
53
|
+
this.prevActiveItem = this.activeItem;
|
|
54
|
+
this.activeItem = item;
|
|
55
|
+
};
|
|
50
56
|
}
|
|
51
57
|
class NavigationMenuRootState {
|
|
52
58
|
opts;
|
|
@@ -74,8 +80,8 @@ class NavigationMenuRootState {
|
|
|
74
80
|
orientation: this.opts.orientation,
|
|
75
81
|
rootNavigationMenuRef: this.opts.ref,
|
|
76
82
|
isRootMenu: true,
|
|
77
|
-
onTriggerEnter: (itemValue) => {
|
|
78
|
-
this.#onTriggerEnter(itemValue);
|
|
83
|
+
onTriggerEnter: (itemValue, itemState) => {
|
|
84
|
+
this.#onTriggerEnter(itemValue, itemState);
|
|
79
85
|
},
|
|
80
86
|
onTriggerLeave: this.#onTriggerLeave,
|
|
81
87
|
onContentEnter: this.#onContentEnter,
|
|
@@ -84,34 +90,44 @@ class NavigationMenuRootState {
|
|
|
84
90
|
onItemDismiss: this.#onItemDismiss,
|
|
85
91
|
});
|
|
86
92
|
}
|
|
87
|
-
#debouncedFn = useDebounce((val) => {
|
|
93
|
+
#debouncedFn = useDebounce((val, itemState) => {
|
|
88
94
|
// passing `undefined` meant to reset the debounce timer
|
|
89
95
|
if (typeof val === "string") {
|
|
90
|
-
this.setValue(val);
|
|
96
|
+
this.setValue(val, itemState);
|
|
91
97
|
}
|
|
92
98
|
}, () => this.#derivedDelay);
|
|
93
|
-
#onTriggerEnter = (itemValue) => {
|
|
94
|
-
this.#debouncedFn(itemValue);
|
|
99
|
+
#onTriggerEnter = (itemValue, itemState) => {
|
|
100
|
+
this.#debouncedFn(itemValue, itemState);
|
|
95
101
|
};
|
|
96
102
|
#onTriggerLeave = () => {
|
|
97
103
|
this.isDelaySkipped.current = false;
|
|
98
|
-
this.#debouncedFn("");
|
|
104
|
+
this.#debouncedFn("", null);
|
|
99
105
|
};
|
|
100
106
|
#onContentEnter = () => {
|
|
101
|
-
this.#debouncedFn();
|
|
107
|
+
this.#debouncedFn(undefined, null);
|
|
102
108
|
};
|
|
103
109
|
#onContentLeave = () => {
|
|
104
|
-
this
|
|
110
|
+
if (this.provider.activeItem &&
|
|
111
|
+
this.provider.activeItem.opts.openOnHover.current === false) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.#debouncedFn("", null);
|
|
105
115
|
};
|
|
106
|
-
#onItemSelect = (itemValue) => {
|
|
107
|
-
this.setValue(itemValue);
|
|
116
|
+
#onItemSelect = (itemValue, itemState) => {
|
|
117
|
+
this.setValue(itemValue, itemState);
|
|
108
118
|
};
|
|
109
119
|
#onItemDismiss = () => {
|
|
110
|
-
this.setValue("");
|
|
120
|
+
this.setValue("", null);
|
|
111
121
|
};
|
|
112
|
-
setValue = (newValue) => {
|
|
122
|
+
setValue = (newValue, itemState) => {
|
|
113
123
|
this.previousValue.current = this.opts.value.current;
|
|
114
124
|
this.opts.value.current = newValue;
|
|
125
|
+
this.provider.setActiveItem(itemState);
|
|
126
|
+
// When all menus are closed, we want to reset previousValue to prevent
|
|
127
|
+
// weird transitions from old positions when opening fresh
|
|
128
|
+
if (newValue === "") {
|
|
129
|
+
this.previousValue.current = "";
|
|
130
|
+
}
|
|
115
131
|
};
|
|
116
132
|
props = $derived.by(() => ({
|
|
117
133
|
id: this.opts.id.current,
|
|
@@ -125,11 +141,12 @@ class NavigationMenuSubState {
|
|
|
125
141
|
opts;
|
|
126
142
|
context;
|
|
127
143
|
previousValue = box("");
|
|
144
|
+
subProvider;
|
|
128
145
|
constructor(opts, context) {
|
|
129
146
|
this.opts = opts;
|
|
130
147
|
this.context = context;
|
|
131
148
|
useRefById(opts);
|
|
132
|
-
useNavigationMenuProvider({
|
|
149
|
+
this.subProvider = useNavigationMenuProvider({
|
|
133
150
|
isRootMenu: false,
|
|
134
151
|
value: this.opts.value,
|
|
135
152
|
dir: this.context.opts.dir,
|
|
@@ -137,12 +154,19 @@ class NavigationMenuSubState {
|
|
|
137
154
|
rootNavigationMenuRef: this.opts.ref,
|
|
138
155
|
onTriggerEnter: this.setValue,
|
|
139
156
|
onItemSelect: this.setValue,
|
|
140
|
-
onItemDismiss: () => this.setValue(""),
|
|
157
|
+
onItemDismiss: () => this.setValue("", null),
|
|
141
158
|
previousValue: this.previousValue,
|
|
142
159
|
});
|
|
143
160
|
}
|
|
144
|
-
setValue = (newValue) => {
|
|
161
|
+
setValue = (newValue, itemState) => {
|
|
162
|
+
this.previousValue.current = this.opts.value.current;
|
|
145
163
|
this.opts.value.current = newValue;
|
|
164
|
+
this.subProvider.setActiveItem(itemState);
|
|
165
|
+
// When all menus are closed, we want to reset previousValue to prevent
|
|
166
|
+
// weird transitions from old positions when opening fresh
|
|
167
|
+
if (newValue === "") {
|
|
168
|
+
this.previousValue.current = "";
|
|
169
|
+
}
|
|
146
170
|
};
|
|
147
171
|
props = $derived.by(() => ({
|
|
148
172
|
id: this.opts.id.current,
|
|
@@ -281,28 +305,30 @@ class NavigationMenuTriggerState {
|
|
|
281
305
|
if (this.opts.disabled.current ||
|
|
282
306
|
this.wasClickClose ||
|
|
283
307
|
this.itemContext.wasEscapeClose ||
|
|
284
|
-
this.hasPointerMoveOpened.current
|
|
308
|
+
this.hasPointerMoveOpened.current ||
|
|
309
|
+
!this.itemContext.opts.openOnHover.current) {
|
|
285
310
|
return;
|
|
286
311
|
}
|
|
287
|
-
this.context.onTriggerEnter(this.itemContext.opts.value.current);
|
|
312
|
+
this.context.onTriggerEnter(this.itemContext.opts.value.current, this.itemContext);
|
|
288
313
|
this.hasPointerMoveOpened.current = true;
|
|
289
314
|
});
|
|
290
315
|
onpointerleave = whenMouse(() => {
|
|
291
|
-
if (this.opts.disabled.current)
|
|
316
|
+
if (this.opts.disabled.current || !this.itemContext.opts.openOnHover.current)
|
|
292
317
|
return;
|
|
293
318
|
this.context.onTriggerLeave();
|
|
294
319
|
this.hasPointerMoveOpened.current = false;
|
|
295
320
|
});
|
|
296
|
-
onclick = (
|
|
321
|
+
onclick = () => {
|
|
297
322
|
// if opened via pointer move, we prevent the click event
|
|
298
323
|
if (this.hasPointerMoveOpened.current)
|
|
299
324
|
return;
|
|
300
|
-
const shouldClose = this.open &&
|
|
325
|
+
const shouldClose = this.open &&
|
|
326
|
+
(!this.itemContext.opts.openOnHover.current || this.context.opts.isRootMenu);
|
|
301
327
|
if (shouldClose) {
|
|
302
|
-
this.context.onItemSelect("");
|
|
328
|
+
this.context.onItemSelect("", null);
|
|
303
329
|
}
|
|
304
330
|
else if (!this.open) {
|
|
305
|
-
this.context.onItemSelect(this.itemContext.opts.value.current);
|
|
331
|
+
this.context.onItemSelect(this.itemContext.opts.value.current, this.itemContext);
|
|
306
332
|
}
|
|
307
333
|
this.wasClickClose = shouldClose;
|
|
308
334
|
};
|
|
@@ -386,6 +412,23 @@ class NavigationMenuLinkState {
|
|
|
386
412
|
onblur = (_) => {
|
|
387
413
|
this.isFocused = false;
|
|
388
414
|
};
|
|
415
|
+
#handlePointerDismiss = () => {
|
|
416
|
+
// only close submenu if this link is not inside the currently open submenu content
|
|
417
|
+
const currentlyOpenValue = this.context.provider.opts.value.current;
|
|
418
|
+
const isInsideOpenSubmenu = this.context.item.opts.value.current === currentlyOpenValue;
|
|
419
|
+
const activeItem = this.context.item.listContext.context.activeItem;
|
|
420
|
+
if (activeItem && !activeItem.opts.openOnHover.current)
|
|
421
|
+
return;
|
|
422
|
+
if (currentlyOpenValue && !isInsideOpenSubmenu) {
|
|
423
|
+
this.context.provider.onItemDismiss();
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
onpointerenter = () => {
|
|
427
|
+
this.#handlePointerDismiss();
|
|
428
|
+
};
|
|
429
|
+
onpointermove = whenMouse(() => {
|
|
430
|
+
this.#handlePointerDismiss();
|
|
431
|
+
});
|
|
389
432
|
props = $derived.by(() => ({
|
|
390
433
|
id: this.opts.id.current,
|
|
391
434
|
"data-active": this.opts.active.current ? "" : undefined,
|
|
@@ -395,6 +438,8 @@ class NavigationMenuLinkState {
|
|
|
395
438
|
onkeydown: this.onkeydown,
|
|
396
439
|
onfocus: this.onfocus,
|
|
397
440
|
onblur: this.onblur,
|
|
441
|
+
onpointerenter: this.onpointerenter,
|
|
442
|
+
onpointermove: this.onpointermove,
|
|
398
443
|
[NAVIGATION_MENU_LINK_ATTR]: "",
|
|
399
444
|
}));
|
|
400
445
|
}
|
|
@@ -497,6 +542,8 @@ class NavigationMenuContentState {
|
|
|
497
542
|
this.context.onContentEnter();
|
|
498
543
|
};
|
|
499
544
|
onpointerleave = whenMouse(() => {
|
|
545
|
+
if (!this.itemContext.opts.openOnHover.current)
|
|
546
|
+
return;
|
|
500
547
|
this.context.onContentLeave();
|
|
501
548
|
});
|
|
502
549
|
props = $derived.by(() => ({
|
|
@@ -520,6 +567,11 @@ class NavigationMenuContentImplState {
|
|
|
520
567
|
const prevIndex = values.indexOf(this.context.opts.previousValue.current);
|
|
521
568
|
const isSelected = this.itemContext.opts.value.current === this.context.opts.value.current;
|
|
522
569
|
const wasSelected = prevIndex === values.indexOf(this.itemContext.opts.value.current);
|
|
570
|
+
// When all menus are closed, we want to reset motion state to prevent residual animations
|
|
571
|
+
if (!this.context.opts.value.current && !this.context.opts.previousValue.current) {
|
|
572
|
+
untrack(() => (this.prevMotionAttribute = null));
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
523
575
|
// We only want to update selected and the last selected content
|
|
524
576
|
// this avoids animations being interrupted outside of that range
|
|
525
577
|
if (!isSelected && !wasSelected)
|
|
@@ -585,8 +637,17 @@ class NavigationMenuContentImplState {
|
|
|
585
637
|
const target = e.target;
|
|
586
638
|
const isTrigger = this.listContext.listTriggers.some((trigger) => trigger.contains(target));
|
|
587
639
|
const isRootViewport = this.context.opts.isRootMenu && this.context.viewportRef.current?.contains(target);
|
|
588
|
-
if (
|
|
640
|
+
if (!this.context.opts.isRootMenu && !isTrigger) {
|
|
641
|
+
this.context.onItemDismiss();
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (isTrigger || isRootViewport) {
|
|
589
645
|
e.preventDefault();
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (!this.itemContext.opts.openOnHover.current) {
|
|
649
|
+
this.context.onItemSelect("", null);
|
|
650
|
+
}
|
|
590
651
|
};
|
|
591
652
|
onkeydown = (e) => {
|
|
592
653
|
// prevent parent menus handling sub-menu keydown events
|
|
@@ -661,6 +722,7 @@ class NavigationMenuViewportState {
|
|
|
661
722
|
viewportWidth = $derived.by(() => (this.size ? `${this.size.width}px` : undefined));
|
|
662
723
|
viewportHeight = $derived.by(() => (this.size ? `${this.size.height}px` : undefined));
|
|
663
724
|
activeContentValue = $derived.by(() => this.context.opts.value.current);
|
|
725
|
+
mounted = $state(false);
|
|
664
726
|
constructor(opts, context) {
|
|
665
727
|
this.opts = opts;
|
|
666
728
|
this.context = context;
|
|
@@ -695,6 +757,12 @@ class NavigationMenuViewportState {
|
|
|
695
757
|
};
|
|
696
758
|
}
|
|
697
759
|
});
|
|
760
|
+
// reset size when viewport closes to prevent residual size animations
|
|
761
|
+
watch(() => this.mounted, () => {
|
|
762
|
+
if (!this.mounted && this.size) {
|
|
763
|
+
this.size = null;
|
|
764
|
+
}
|
|
765
|
+
});
|
|
698
766
|
}
|
|
699
767
|
props = $derived.by(() => ({
|
|
700
768
|
id: this.opts.id.current,
|
|
@@ -714,6 +782,7 @@ const NavigationMenuProviderContext = new Context("NavigationMenu.Root");
|
|
|
714
782
|
export const NavigationMenuItemContext = new Context("NavigationMenu.Item");
|
|
715
783
|
const NavigationMenuListContext = new Context("NavigationMenu.List");
|
|
716
784
|
const NavigationMenuContentContext = new Context("NavigationMenu.Content");
|
|
785
|
+
const NavigationMenuSubContext = new Context("NavigationMenu.Sub");
|
|
717
786
|
export function useNavigationMenuRoot(props) {
|
|
718
787
|
return new NavigationMenuRootState(props);
|
|
719
788
|
}
|
|
@@ -740,6 +809,7 @@ export function useNavigationMenuTrigger(props) {
|
|
|
740
809
|
provider: NavigationMenuProviderContext.get(),
|
|
741
810
|
item: NavigationMenuItemContext.get(),
|
|
742
811
|
list: NavigationMenuListContext.get(),
|
|
812
|
+
sub: NavigationMenuSubContext.getOr(null),
|
|
743
813
|
});
|
|
744
814
|
}
|
|
745
815
|
export function useNavigationMenuContent(props) {
|
|
@@ -15,21 +15,22 @@ export type NavigationMenuRootPropsWithoutHTML = WithChild<{
|
|
|
15
15
|
*/
|
|
16
16
|
onValueChange?: OnChangeFn<string>;
|
|
17
17
|
/**
|
|
18
|
-
* The
|
|
18
|
+
* The amount of time in ms from when the mouse enters a trigger until the content opens.
|
|
19
19
|
*
|
|
20
|
-
* @
|
|
20
|
+
* @default 200
|
|
21
21
|
*/
|
|
22
22
|
delayDuration?: number;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* The amount of time in ms that a user has to enter another trigger without
|
|
25
|
+
* incurring a delay again.
|
|
25
26
|
*
|
|
26
|
-
* @
|
|
27
|
+
* @default 300
|
|
27
28
|
*/
|
|
28
29
|
skipDelayDuration?: number;
|
|
29
30
|
/**
|
|
30
31
|
* The reading direction of the content.
|
|
31
32
|
*
|
|
32
|
-
* @
|
|
33
|
+
* @default "ltr"
|
|
33
34
|
*/
|
|
34
35
|
dir?: Direction;
|
|
35
36
|
/**
|
|
@@ -68,6 +69,13 @@ export type NavigationMenuItemPropsWithoutHTML = WithChild<{
|
|
|
68
69
|
* The value of the menu item.
|
|
69
70
|
*/
|
|
70
71
|
value?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Whether to open the menu associated with the item when the item's trigger
|
|
74
|
+
* is hovered.
|
|
75
|
+
*
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
openOnHover?: boolean;
|
|
71
79
|
}>;
|
|
72
80
|
export type NavigationMenuItemProps = NavigationMenuItemPropsWithoutHTML & Without<BitsPrimitiveLiAttributes, NavigationMenuItemPropsWithoutHTML>;
|
|
73
81
|
export type NavigationMenuTriggerPropsWithoutHTML = WithChild<{
|
|
@@ -82,7 +90,6 @@ export type NavigationMenuContentPropsWithoutHTML = WithChild<{
|
|
|
82
90
|
/**
|
|
83
91
|
* Callback fired when an interaction occurs outside the content.
|
|
84
92
|
* Default behavior can be prevented with `event.preventDefault()`
|
|
85
|
-
*
|
|
86
93
|
*/
|
|
87
94
|
onInteractOutside?: (event: PointerEvent) => void;
|
|
88
95
|
/**
|
|
@@ -108,7 +115,7 @@ export type NavigationMenuContentPropsWithoutHTML = WithChild<{
|
|
|
108
115
|
* This is useful when wanting to use more custom transition and animation
|
|
109
116
|
* libraries.
|
|
110
117
|
*
|
|
111
|
-
* @
|
|
118
|
+
* @default false
|
|
112
119
|
*/
|
|
113
120
|
forceMount?: boolean;
|
|
114
121
|
}>;
|
|
@@ -334,12 +334,12 @@ declare class SelectScrollButtonImplState {
|
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
336
|
declare class SelectScrollDownButtonState {
|
|
337
|
-
readonly
|
|
337
|
+
readonly scrollButtonState: SelectScrollButtonImplState;
|
|
338
338
|
content: SelectContentState;
|
|
339
339
|
root: SelectBaseRootState;
|
|
340
340
|
canScrollDown: boolean;
|
|
341
341
|
scrollIntoViewTimer: ReturnType<typeof globalThis.setTimeout> | null;
|
|
342
|
-
constructor(
|
|
342
|
+
constructor(scrollButtonState: SelectScrollButtonImplState);
|
|
343
343
|
/**
|
|
344
344
|
* @param manual - if true, it means the function was invoked manually outside of an event
|
|
345
345
|
* listener, so we don't call `handleUserScroll` to prevent the auto scroll from kicking in.
|
|
@@ -358,11 +358,11 @@ declare class SelectScrollDownButtonState {
|
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
360
|
declare class SelectScrollUpButtonState {
|
|
361
|
-
readonly
|
|
361
|
+
readonly scrollButtonState: SelectScrollButtonImplState;
|
|
362
362
|
content: SelectContentState;
|
|
363
363
|
root: SelectBaseRootState;
|
|
364
364
|
canScrollUp: boolean;
|
|
365
|
-
constructor(
|
|
365
|
+
constructor(scrollButtonState: SelectScrollButtonImplState);
|
|
366
366
|
/**
|
|
367
367
|
* @param manual - if true, it means the function was invoked manually outside of an event
|
|
368
368
|
* listener, so we don't call `handleUserScroll` to prevent the auto scroll from kicking in.
|
|
@@ -705,7 +705,11 @@ class SelectContentState {
|
|
|
705
705
|
trapFocus: false,
|
|
706
706
|
loop: false,
|
|
707
707
|
onPlaced: () => {
|
|
708
|
-
|
|
708
|
+
// onPlaced is also called when the menu is closed, so we need to check if the menu
|
|
709
|
+
// is actually open to avoid setting positioning to true when the menu is closed
|
|
710
|
+
if (this.root.opts.open.current) {
|
|
711
|
+
this.isPositioned = true;
|
|
712
|
+
}
|
|
709
713
|
},
|
|
710
714
|
};
|
|
711
715
|
}
|
|
@@ -993,25 +997,24 @@ class SelectScrollButtonImplState {
|
|
|
993
997
|
}));
|
|
994
998
|
}
|
|
995
999
|
class SelectScrollDownButtonState {
|
|
996
|
-
|
|
1000
|
+
scrollButtonState;
|
|
997
1001
|
content;
|
|
998
1002
|
root;
|
|
999
1003
|
canScrollDown = $state(false);
|
|
1000
1004
|
scrollIntoViewTimer = null;
|
|
1001
|
-
constructor(
|
|
1002
|
-
this.
|
|
1003
|
-
this.content =
|
|
1004
|
-
this.root =
|
|
1005
|
-
this.
|
|
1005
|
+
constructor(scrollButtonState) {
|
|
1006
|
+
this.scrollButtonState = scrollButtonState;
|
|
1007
|
+
this.content = scrollButtonState.content;
|
|
1008
|
+
this.root = scrollButtonState.root;
|
|
1009
|
+
this.scrollButtonState.onAutoScroll = this.handleAutoScroll;
|
|
1006
1010
|
watch([() => this.content.viewportNode, () => this.content.isPositioned], () => {
|
|
1007
|
-
if (!this.content.viewportNode || !this.content.isPositioned)
|
|
1011
|
+
if (!this.content.viewportNode || !this.content.isPositioned)
|
|
1008
1012
|
return;
|
|
1009
|
-
}
|
|
1010
1013
|
this.handleScroll(true);
|
|
1011
1014
|
return on(this.content.viewportNode, "scroll", () => this.handleScroll());
|
|
1012
1015
|
});
|
|
1013
|
-
watch(() => this.
|
|
1014
|
-
if (!this.
|
|
1016
|
+
watch(() => this.scrollButtonState.mounted, () => {
|
|
1017
|
+
if (!this.scrollButtonState.mounted)
|
|
1015
1018
|
return;
|
|
1016
1019
|
if (this.scrollIntoViewTimer) {
|
|
1017
1020
|
clearTimeout(this.scrollIntoViewTimer);
|
|
@@ -1028,7 +1031,7 @@ class SelectScrollDownButtonState {
|
|
|
1028
1031
|
*/
|
|
1029
1032
|
handleScroll = (manual = false) => {
|
|
1030
1033
|
if (!manual) {
|
|
1031
|
-
this.
|
|
1034
|
+
this.scrollButtonState.handleUserScroll();
|
|
1032
1035
|
}
|
|
1033
1036
|
if (!this.content.viewportNode)
|
|
1034
1037
|
return;
|
|
@@ -1044,18 +1047,21 @@ class SelectScrollDownButtonState {
|
|
|
1044
1047
|
return;
|
|
1045
1048
|
viewport.scrollTop = viewport.scrollTop + selectedItem.offsetHeight;
|
|
1046
1049
|
};
|
|
1047
|
-
props = $derived.by(() => ({
|
|
1050
|
+
props = $derived.by(() => ({
|
|
1051
|
+
...this.scrollButtonState.props,
|
|
1052
|
+
[this.root.bitsAttrs["scroll-down-button"]]: "",
|
|
1053
|
+
}));
|
|
1048
1054
|
}
|
|
1049
1055
|
class SelectScrollUpButtonState {
|
|
1050
|
-
|
|
1056
|
+
scrollButtonState;
|
|
1051
1057
|
content;
|
|
1052
1058
|
root;
|
|
1053
1059
|
canScrollUp = $state(false);
|
|
1054
|
-
constructor(
|
|
1055
|
-
this.
|
|
1056
|
-
this.content =
|
|
1057
|
-
this.root =
|
|
1058
|
-
this.
|
|
1060
|
+
constructor(scrollButtonState) {
|
|
1061
|
+
this.scrollButtonState = scrollButtonState;
|
|
1062
|
+
this.content = scrollButtonState.content;
|
|
1063
|
+
this.root = scrollButtonState.root;
|
|
1064
|
+
this.scrollButtonState.onAutoScroll = this.handleAutoScroll;
|
|
1059
1065
|
watch([() => this.content.viewportNode, () => this.content.isPositioned], () => {
|
|
1060
1066
|
if (!this.content.viewportNode || !this.content.isPositioned)
|
|
1061
1067
|
return;
|
|
@@ -1069,7 +1075,7 @@ class SelectScrollUpButtonState {
|
|
|
1069
1075
|
*/
|
|
1070
1076
|
handleScroll = (manual = false) => {
|
|
1071
1077
|
if (!manual) {
|
|
1072
|
-
this.
|
|
1078
|
+
this.scrollButtonState.handleUserScroll();
|
|
1073
1079
|
}
|
|
1074
1080
|
if (!this.content.viewportNode)
|
|
1075
1081
|
return;
|
|
@@ -1082,7 +1088,10 @@ class SelectScrollUpButtonState {
|
|
|
1082
1088
|
this.content.viewportNode.scrollTop =
|
|
1083
1089
|
this.content.viewportNode.scrollTop - this.root.highlightedNode.offsetHeight;
|
|
1084
1090
|
};
|
|
1085
|
-
props = $derived.by(() => ({
|
|
1091
|
+
props = $derived.by(() => ({
|
|
1092
|
+
...this.scrollButtonState.props,
|
|
1093
|
+
[this.root.bitsAttrs["scroll-up-button"]]: "",
|
|
1094
|
+
}));
|
|
1086
1095
|
}
|
|
1087
1096
|
const SelectRootContext = new Context("Select.Root | Combobox.Root");
|
|
1088
1097
|
const SelectGroupContext = new Context("Select.Group | Combobox.Group");
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
dir = "ltr",
|
|
23
23
|
autoSort = true,
|
|
24
24
|
orientation = "horizontal",
|
|
25
|
+
thumbPositioning = "contain",
|
|
25
26
|
...restProps
|
|
26
27
|
}: SliderRootProps = $props();
|
|
27
28
|
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
dir: box.with(() => dir),
|
|
64
65
|
autoSort: box.with(() => autoSort),
|
|
65
66
|
orientation: box.with(() => orientation),
|
|
67
|
+
thumbPositioning: box.with(() => thumbPositioning),
|
|
66
68
|
type,
|
|
67
69
|
});
|
|
68
70
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Box, type ReadableBox } from "svelte-toolbelt";
|
|
2
2
|
import type { ReadableBoxedValues, WritableBoxedValues } from "../../internal/box.svelte.js";
|
|
3
3
|
import type { BitsKeyboardEvent, OnChangeFn, WithRefProps } from "../../internal/types.js";
|
|
4
|
-
import type { Direction, Orientation } from "../../shared/index.js";
|
|
4
|
+
import type { Direction, Orientation, SliderThumbPositioning } from "../../shared/index.js";
|
|
5
5
|
type SliderBaseRootStateProps = WithRefProps<ReadableBoxedValues<{
|
|
6
6
|
disabled: boolean;
|
|
7
7
|
orientation: Orientation;
|
|
@@ -10,6 +10,7 @@ type SliderBaseRootStateProps = WithRefProps<ReadableBoxedValues<{
|
|
|
10
10
|
step: number;
|
|
11
11
|
dir: Direction;
|
|
12
12
|
autoSort: boolean;
|
|
13
|
+
thumbPositioning: SliderThumbPositioning;
|
|
13
14
|
}>>;
|
|
14
15
|
declare class SliderBaseRootState {
|
|
15
16
|
#private;
|
|
@@ -46,6 +46,10 @@ class SliderBaseRootState {
|
|
|
46
46
|
return Array.from(node.querySelectorAll(`[${SLIDER_THUMB_ATTR}]`));
|
|
47
47
|
};
|
|
48
48
|
getThumbScale = () => {
|
|
49
|
+
if (this.opts.thumbPositioning.current === "exact") {
|
|
50
|
+
// User opted out of containment
|
|
51
|
+
return [0, 100];
|
|
52
|
+
}
|
|
49
53
|
const isVertical = this.opts.orientation.current === "vertical";
|
|
50
54
|
// this assumes all thumbs are the same width
|
|
51
55
|
const activeThumb = this.getAllThumbs()[0];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OnChangeFn, WithChild, Without } from "../../internal/types.js";
|
|
2
2
|
import type { BitsPrimitiveSpanAttributes } from "../../shared/attributes.js";
|
|
3
|
-
import type { Direction, Orientation } from "../../shared/index.js";
|
|
3
|
+
import type { Direction, Orientation, SliderThumbPositioning } from "../../shared/index.js";
|
|
4
4
|
export type SliderRootSnippetProps = {
|
|
5
5
|
ticks: number[];
|
|
6
6
|
thumbs: number[];
|
|
@@ -53,6 +53,12 @@ export type BaseSliderRootPropsWithoutHTML = {
|
|
|
53
53
|
* @defaultValue false
|
|
54
54
|
*/
|
|
55
55
|
disabled?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The positioning of the slider thumb.
|
|
58
|
+
*
|
|
59
|
+
* @defaultValue "contain"
|
|
60
|
+
*/
|
|
61
|
+
thumbPositioning?: SliderThumbPositioning;
|
|
56
62
|
};
|
|
57
63
|
export type SliderSingleRootPropsWithoutHTML = BaseSliderRootPropsWithoutHTML & {
|
|
58
64
|
/**
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ export type StyleProperties = CSS.Properties & {
|
|
|
12
12
|
};
|
|
13
13
|
export type Orientation = "horizontal" | "vertical";
|
|
14
14
|
export type Direction = "ltr" | "rtl";
|
|
15
|
+
/**
|
|
16
|
+
* Controls positioning of the slider thumb.
|
|
17
|
+
*
|
|
18
|
+
* - `exact`: The thumb is centered exactly at the value of the slider.
|
|
19
|
+
* - `contain`: The thumb is centered exactly at the value of the slider, but will be contained within the slider track at the ends.
|
|
20
|
+
*/
|
|
21
|
+
export type SliderThumbPositioning = "exact" | "contain";
|
|
15
22
|
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
|
16
23
|
export type WithoutChildren<T> = T extends {
|
|
17
24
|
children?: any;
|