sheer-ui 0.4.0 → 0.4.3
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/package.json +1 -1
- package/src/lib/components/accordion/components/accordion-item.svelte +3 -3
- package/src/lib/components/accordion/components/accordion.svelte +29 -13
- package/src/lib/components/accordion/types.ts +3 -3
- package/src/lib/components/avatar/avatar.svelte.ts +34 -37
- package/src/lib/components/avatar/components/avatar-fallback.svelte +9 -6
- package/src/lib/components/avatar/components/avatar-image.svelte +18 -9
- package/src/lib/components/avatar/components/avatar.svelte +20 -16
- package/src/lib/components/checkbox/checkbox.svelte.ts +45 -55
- package/src/lib/components/checkbox/components/checkbox-group-label.svelte +9 -6
- package/src/lib/components/checkbox/components/checkbox-group.svelte +25 -14
- package/src/lib/components/checkbox/components/checkbox.svelte +39 -21
- package/src/lib/components/combobox/components/combobox.svelte +2 -2
- package/src/lib/components/combobox/select/components/select.svelte +2 -2
- package/src/lib/components/context-menu/components/context-menu-trigger.svelte +1 -1
- package/src/lib/components/date-field/date-field.svelte.ts +2 -3
- package/src/lib/components/dialog/components/dialog-close.svelte +13 -8
- package/src/lib/components/dialog/components/dialog-content-headless.svelte +14 -11
- package/src/lib/components/dialog/components/dialog-description.svelte +9 -6
- package/src/lib/components/dialog/components/dialog-overlay.svelte +10 -7
- package/src/lib/components/dialog/components/dialog-title.svelte +12 -7
- package/src/lib/components/dialog/components/dialog-trigger.svelte +12 -7
- package/src/lib/components/dialog/components/dialog.svelte +10 -4
- package/src/lib/components/dialog/components/modal-surface.svelte +11 -8
- package/src/lib/components/dialog/dialog.svelte.ts +73 -61
- package/src/lib/components/drawer/drawer.svelte +8 -3
- package/src/lib/components/link-preview/components/link-preview-content.svelte +15 -8
- package/src/lib/components/link-preview/components/link-preview-trigger.svelte +9 -6
- package/src/lib/components/link-preview/components/link-preview.svelte +19 -11
- package/src/lib/components/link-preview/link-preview.svelte.ts +42 -43
- package/src/lib/components/menu/components/menu-checkbox-group.svelte +6 -4
- package/src/lib/components/menu/components/menu-checkbox-item.svelte +11 -7
- package/src/lib/components/menu/menu.svelte.ts +29 -19
- package/src/lib/components/menubar/menubar.svelte.ts +7 -6
- package/src/lib/components/meter/components/meter.svelte +18 -9
- package/src/lib/components/meter/meter.svelte.ts +15 -18
- package/src/lib/components/navigation-menu/components/navigation-menu-content.svelte +1 -1
- package/src/lib/components/navigation-menu/components/navigation-menu-indicator.svelte +2 -2
- package/src/lib/components/navigation-menu/components/navigation-menu-trigger.svelte +1 -1
- package/src/lib/components/navigation-menu/navigation-menu.svelte.ts +57 -60
- package/src/lib/components/pagination/components/pagination-next-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination-page.svelte +15 -8
- package/src/lib/components/pagination/components/pagination-prev-button.svelte +12 -7
- package/src/lib/components/pagination/components/pagination.svelte +31 -18
- package/src/lib/components/pagination/pagination.svelte.ts +48 -64
- package/src/lib/components/pin-input/pin-input.svelte.ts +7 -8
- package/src/lib/components/pin-input/usePasswordManager.svelte.ts +2 -2
- package/src/lib/components/popover/components/popover-close.svelte +9 -6
- package/src/lib/components/popover/components/popover-content.svelte +12 -7
- package/src/lib/components/popover/components/popover-trigger.svelte +21 -10
- package/src/lib/components/popover/components/popover.svelte +9 -8
- package/src/lib/components/popover/popover.svelte.ts +68 -74
- package/src/lib/components/progress/components/progress.svelte +18 -9
- package/src/lib/components/progress/progress.svelte.ts +14 -17
- package/src/lib/components/scroll-area/scroll-area.svelte.ts +9 -10
- package/src/lib/components/separator/components/separator.svelte +15 -8
- package/src/lib/components/separator/separator.svelte.ts +12 -15
- package/src/lib/components/sidebar/sidebar-mobile-surface.svelte +8 -4
- package/src/lib/components/slider/components/slider.svelte +2 -1
- package/src/lib/components/tabs/components/tabs-content.svelte +12 -7
- package/src/lib/components/tabs/components/tabs-list.svelte +9 -6
- package/src/lib/components/tabs/components/tabs-trigger.svelte +15 -8
- package/src/lib/components/tabs/components/tabs.svelte +28 -17
- package/src/lib/components/tabs/tabs.svelte.ts +51 -67
- package/src/lib/components/time-field/time-field.svelte.ts +2 -2
- package/src/lib/components/toggle/components/toggle.svelte +19 -14
- package/src/lib/components/toggle/toggle.svelte.ts +15 -20
- package/src/lib/components/toggle-group/components/toggle-group-item.svelte +15 -8
- package/src/lib/components/toggle-group/components/toggle-group.svelte +28 -16
- package/src/lib/components/toggle-group/toggle-group.svelte.ts +33 -35
- package/src/lib/components/toolbar/components/toolbar-group-item.svelte +15 -8
- package/src/lib/components/toolbar/components/toolbar-group.svelte +19 -14
- package/src/lib/components/toolbar/toolbar.svelte.ts +28 -30
- package/src/lib/components/tooltip/components/tooltip-content.svelte +15 -8
- package/src/lib/components/tooltip/components/tooltip-provider.svelte +18 -7
- package/src/lib/components/tooltip/components/tooltip-trigger.svelte +21 -10
- package/src/lib/components/tooltip/components/tooltip.svelte +34 -21
- package/src/lib/components/tooltip/tooltip.svelte.ts +101 -125
- package/src/lib/internal/dismissible-layer/use-dismissable-layer.svelte.ts +3 -4
- package/src/lib/internal/escape-layer/use-escape-layer.svelte.ts +13 -29
- package/src/lib/internal/floating-layer/use-floating-layer.svelte.ts +26 -30
- package/src/lib/internal/group-value.svelte.ts +11 -10
- package/src/lib/internal/native-popover.svelte.ts +6 -6
- package/src/lib/internal/{roving-focus-group.ts → roving-focus-group.svelte.ts} +17 -19
- package/src/lib/internal/roving-focus-item.svelte.ts +12 -17
- package/src/lib/internal/selection.svelte.ts +55 -41
- package/src/lib/internal/{state-machine.ts → state-machine.svelte.ts} +6 -8
- package/src/lib/internal/timeout-fn.svelte.ts +6 -4
- package/src/lib/internal/tools/box.svelte.ts +5 -114
- package/src/lib/internal/tools/index.ts +1 -11
- package/src/lib/internal/tools/types.ts +0 -3
- package/src/lib/internal/typeahead.svelte.ts +9 -13
- package/src/lib/internal/types.ts +6 -0
- package/src/lib/internal/vaul/use-drawer-content.svelte.ts +2 -2
- package/src/lib/internal/vaul/use-drawer-root.svelte.ts +12 -7
- package/src/lib/components/accordion/accordion.svelte.ts +0 -43
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { boxWith } from '../../../internal/tools/index.js';
|
|
3
3
|
import { mergeProps } from '../../../internal/merge-props.js';
|
|
4
4
|
import type { MenuCheckboxGroupProps } from '../types.js';
|
|
5
5
|
import { MenuCheckboxGroupState } from '../menu.svelte.js';
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
}: MenuCheckboxGroupProps = $props();
|
|
19
19
|
|
|
20
20
|
const checkboxGroupState = MenuCheckboxGroupState.create({
|
|
21
|
-
value:
|
|
21
|
+
value: boxWith(
|
|
22
22
|
() => $state.snapshot(value),
|
|
23
|
-
(v) =>
|
|
24
|
-
|
|
23
|
+
(v) => {
|
|
24
|
+
value = $state.snapshot(v);
|
|
25
|
+
onValueChange(v);
|
|
26
|
+
},
|
|
25
27
|
),
|
|
26
28
|
ref: boxWith(
|
|
27
29
|
() => ref,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { join } from 'overrule';
|
|
3
|
-
import {
|
|
3
|
+
import { boxWith, repairBindable } from '../../../internal/tools/index.js';
|
|
4
4
|
import { mergeProps } from '../../../internal/merge-props.js';
|
|
5
5
|
import type { MenuCheckboxItemProps } from '../types.js';
|
|
6
6
|
import { MenuCheckboxItemState } from '../menu.svelte.js';
|
|
@@ -26,10 +26,12 @@
|
|
|
26
26
|
}: MenuCheckboxItemProps = $props();
|
|
27
27
|
|
|
28
28
|
const checkboxItemState = MenuCheckboxItemState.create({
|
|
29
|
-
checked:
|
|
29
|
+
checked: boxWith(
|
|
30
30
|
() => checked,
|
|
31
|
-
(v) =>
|
|
32
|
-
|
|
31
|
+
(v) => {
|
|
32
|
+
checked = v;
|
|
33
|
+
onCheckedChange(v);
|
|
34
|
+
},
|
|
33
35
|
),
|
|
34
36
|
id: boxWith(() => id),
|
|
35
37
|
disabled: boxWith(() => disabled),
|
|
@@ -38,10 +40,12 @@
|
|
|
38
40
|
() => ref,
|
|
39
41
|
(v) => (ref = v),
|
|
40
42
|
),
|
|
41
|
-
indeterminate:
|
|
43
|
+
indeterminate: boxWith(
|
|
42
44
|
() => indeterminate,
|
|
43
|
-
(v) =>
|
|
44
|
-
|
|
45
|
+
(v) => {
|
|
46
|
+
indeterminate = v;
|
|
47
|
+
onIndeterminateChange(v);
|
|
48
|
+
},
|
|
45
49
|
),
|
|
46
50
|
value: boxWith(() => value),
|
|
47
51
|
});
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
getWindow,
|
|
5
5
|
type ReadableBoxedValues,
|
|
6
6
|
type WritableBoxedValues,
|
|
7
|
-
simpleBox,
|
|
8
7
|
boxWith,
|
|
9
8
|
type ReadableBox,
|
|
10
9
|
} from '../../internal/tools/index.js';
|
|
11
10
|
import { mergeProps } from '../../internal/merge-props.js';
|
|
11
|
+
import type { Measurable } from '../../internal/floating-layer/index.js';
|
|
12
12
|
import { createContext, onDestroy, tick, untrack } from 'svelte';
|
|
13
13
|
import { SUB_OPEN_KEYS, getCheckedState, isMouseEvent } from './utils.js';
|
|
14
14
|
import { focusFirst } from '../../internal/focus.js';
|
|
@@ -35,7 +35,7 @@ import type { Direction } from '../../internal/index.js';
|
|
|
35
35
|
import { getTabbableFrom, isTabbable } from '../../internal/tabbable.js';
|
|
36
36
|
import type { KeyboardEventHandler, PointerEventHandler, MouseEventHandler } from 'svelte/elements';
|
|
37
37
|
import { Typeahead, textContentOf } from '../../internal/typeahead.svelte.js';
|
|
38
|
-
import { RovingFocusGroup } from '../../internal/roving-focus-group.js';
|
|
38
|
+
import { RovingFocusGroup } from '../../internal/roving-focus-group.svelte.js';
|
|
39
39
|
import { useOpenChangeComplete } from '../../internal/animations-settled.svelte.js';
|
|
40
40
|
import { joinGroup } from '../../internal/group-value.svelte.js';
|
|
41
41
|
import { createEffectTimeout } from '../../internal/timeout-fn.svelte.js';
|
|
@@ -601,10 +601,10 @@ export class MenuContentState {
|
|
|
601
601
|
getWindow: () => this.domContext.getWindow(),
|
|
602
602
|
});
|
|
603
603
|
this.rovingFocusGroup = new RovingFocusGroup({
|
|
604
|
-
rootNode:
|
|
604
|
+
rootNode: () => this.parentMenu.contentNode,
|
|
605
605
|
candidateSelector: this.#candidateSelector,
|
|
606
|
-
loop: this.opts.loop,
|
|
607
|
-
orientation:
|
|
606
|
+
loop: () => this.opts.loop.current,
|
|
607
|
+
orientation: () => 'vertical',
|
|
608
608
|
});
|
|
609
609
|
|
|
610
610
|
$effect(() => {
|
|
@@ -1139,7 +1139,26 @@ export class MenuCheckboxItemState {
|
|
|
1139
1139
|
constructor(opts: MenuItemCombinedProps & MenuCheckboxItemStateOpts, shared: MenuItemSharedState) {
|
|
1140
1140
|
this.opts = opts;
|
|
1141
1141
|
this.item = new MenuItemState(opts, shared, { closes: false, act: () => this.#toggle() });
|
|
1142
|
-
|
|
1142
|
+
// Accessor views over the boxed options until the menu engine converts.
|
|
1143
|
+
const group = hasMenuCheckboxGroup() ? getMenuCheckboxGroup() : null;
|
|
1144
|
+
this.groupChecked = joinGroup(
|
|
1145
|
+
group && {
|
|
1146
|
+
get value() {
|
|
1147
|
+
return group.opts.value.current;
|
|
1148
|
+
},
|
|
1149
|
+
set value(v) {
|
|
1150
|
+
group.opts.value.current = v;
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
get value() {
|
|
1155
|
+
return opts.value.current;
|
|
1156
|
+
},
|
|
1157
|
+
get checked() {
|
|
1158
|
+
return opts.checked.current;
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
);
|
|
1143
1162
|
}
|
|
1144
1163
|
|
|
1145
1164
|
#toggle() {
|
|
@@ -1461,11 +1480,11 @@ export class ContextMenuTriggerState {
|
|
|
1461
1480
|
readonly opts: ContextMenuTriggerStateOpts;
|
|
1462
1481
|
readonly parentMenu: MenuMenuState;
|
|
1463
1482
|
readonly attachment: RefAttachment;
|
|
1464
|
-
#point = $state({ x: 0, y: 0 });
|
|
1465
|
-
|
|
1466
|
-
virtualElement =
|
|
1483
|
+
#point = $state.raw({ x: 0, y: 0 });
|
|
1484
|
+
// One stable Measurable: readers that measure inside a derived track `#point` through it.
|
|
1485
|
+
readonly virtualElement: Measurable = {
|
|
1467
1486
|
getBoundingClientRect: () => DOMRect.fromRect({ width: 0, height: 0, ...this.#point }),
|
|
1468
|
-
}
|
|
1487
|
+
};
|
|
1469
1488
|
#longPressTimer = createEffectTimeout((e: BitsPointerEvent) => this.#handleOpen(e), () => 700);
|
|
1470
1489
|
|
|
1471
1490
|
constructor(opts: ContextMenuTriggerStateOpts, parentMenu: MenuMenuState) {
|
|
@@ -1478,15 +1497,6 @@ export class ContextMenuTriggerState {
|
|
|
1478
1497
|
this.onpointercancel = this.onpointercancel.bind(this);
|
|
1479
1498
|
this.onpointerup = this.onpointerup.bind(this);
|
|
1480
1499
|
|
|
1481
|
-
$effect(() => {
|
|
1482
|
-
const point = this.#point;
|
|
1483
|
-
untrack(() => {
|
|
1484
|
-
this.virtualElement.current = {
|
|
1485
|
-
getBoundingClientRect: () => DOMRect.fromRect({ width: 0, height: 0, ...point }),
|
|
1486
|
-
};
|
|
1487
|
-
});
|
|
1488
|
-
});
|
|
1489
|
-
|
|
1490
1500
|
$effect(() => {
|
|
1491
1501
|
const isDisabled = this.opts.disabled.current;
|
|
1492
1502
|
untrack(() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReadableBox,
|
|
1
|
+
import { type ReadableBox, attachRef, type ReadableBoxedValues, type WritableBoxedValues } from '../../internal/tools/index.js';
|
|
2
2
|
import type { InteractOutsideBehaviorType } from '../../internal/dismissible-layer/types.js';
|
|
3
3
|
import type { Direction } from '../../internal/index.js';
|
|
4
4
|
import { createBitsAttrs, boolToStr, boolToEmptyStrOrUndef, getDataOpenClosed } from '../../internal/attrs.js';
|
|
@@ -7,7 +7,7 @@ import type { OnChangeFn, RefAttachment, WithRefOpts } from '../../internal/type
|
|
|
7
7
|
import { createContext, onMount, tick, untrack } from 'svelte';
|
|
8
8
|
import type { FocusEventHandler, KeyboardEventHandler, PointerEventHandler } from 'svelte/elements';
|
|
9
9
|
import { getFloatingContentCSSVars } from '../../internal/floating-layer/index.js';
|
|
10
|
-
import { RovingFocusGroup } from '../../internal/roving-focus-group.js';
|
|
10
|
+
import { RovingFocusGroup } from '../../internal/roving-focus-group.svelte.js';
|
|
11
11
|
import { RovingFocusItem } from '../../internal/roving-focus-item.svelte.js';
|
|
12
12
|
|
|
13
13
|
const menubarAttrs = createBitsAttrs({
|
|
@@ -45,10 +45,10 @@ export class MenubarRootState {
|
|
|
45
45
|
this.opts = opts;
|
|
46
46
|
this.attachment = attachRef(this.opts.ref);
|
|
47
47
|
this.rovingFocusGroup = new RovingFocusGroup({
|
|
48
|
-
rootNode: this.opts.ref,
|
|
48
|
+
rootNode: () => this.opts.ref.current,
|
|
49
49
|
candidateAttr: menubarAttrs.trigger,
|
|
50
|
-
loop: this.opts.loop,
|
|
51
|
-
orientation:
|
|
50
|
+
loop: () => this.opts.loop.current,
|
|
51
|
+
orientation: () => 'horizontal',
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -203,7 +203,8 @@ export class MenubarTriggerState {
|
|
|
203
203
|
this.root = menu.root;
|
|
204
204
|
this.rovingItem = new RovingFocusItem({
|
|
205
205
|
group: this.root.rovingFocusGroup,
|
|
206
|
-
ref: this.opts.ref,
|
|
206
|
+
ref: () => this.opts.ref.current,
|
|
207
|
+
setRef: (v) => (this.opts.ref.current = v),
|
|
207
208
|
onRefChange: (v) => (this.menu.triggerNode = v),
|
|
208
209
|
});
|
|
209
210
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { boxWith } from '../../../internal/tools/index.js';
|
|
3
2
|
import { mergeProps } from '../../../internal/merge-props.js';
|
|
4
3
|
import type { MeterRootProps } from '../types.js';
|
|
5
4
|
import { MeterRootState } from '../meter.svelte.js';
|
|
@@ -19,14 +18,24 @@
|
|
|
19
18
|
}: MeterRootProps = $props();
|
|
20
19
|
|
|
21
20
|
const rootState = MeterRootState.create({
|
|
22
|
-
value
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
get value() {
|
|
22
|
+
return value;
|
|
23
|
+
},
|
|
24
|
+
get max() {
|
|
25
|
+
return max;
|
|
26
|
+
},
|
|
27
|
+
get min() {
|
|
28
|
+
return min;
|
|
29
|
+
},
|
|
30
|
+
get id() {
|
|
31
|
+
return id;
|
|
32
|
+
},
|
|
33
|
+
get ref() {
|
|
34
|
+
return ref;
|
|
35
|
+
},
|
|
36
|
+
set ref(v) {
|
|
37
|
+
ref = v;
|
|
38
|
+
},
|
|
30
39
|
});
|
|
31
40
|
|
|
32
41
|
const mergedProps = $derived(mergeProps(restProps, rootState.props));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { attachRef
|
|
2
|
-
import type { RefAttachment,
|
|
1
|
+
import { attachRef } from '../../internal/tools/index.js';
|
|
2
|
+
import type { RefAttachment, RefOpts } from '../../internal/types.js';
|
|
3
3
|
import { createBitsAttrs } from '../../internal/attrs.js';
|
|
4
4
|
|
|
5
5
|
const meterAttrs = createBitsAttrs({
|
|
@@ -7,14 +7,11 @@ const meterAttrs = createBitsAttrs({
|
|
|
7
7
|
parts: ['root'],
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
-
interface MeterRootStateOpts
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
max: number;
|
|
16
|
-
min: number;
|
|
17
|
-
}> {}
|
|
10
|
+
interface MeterRootStateOpts extends RefOpts {
|
|
11
|
+
readonly value: number;
|
|
12
|
+
readonly max: number;
|
|
13
|
+
readonly min: number;
|
|
14
|
+
}
|
|
18
15
|
|
|
19
16
|
export class MeterRootState {
|
|
20
17
|
static create(opts: MeterRootStateOpts) {
|
|
@@ -26,20 +23,20 @@ export class MeterRootState {
|
|
|
26
23
|
|
|
27
24
|
constructor(opts: MeterRootStateOpts) {
|
|
28
25
|
this.opts = opts;
|
|
29
|
-
this.attachment = attachRef(
|
|
26
|
+
this.attachment = attachRef<HTMLElement>((v) => (opts.ref = v));
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
readonly props = $derived.by(
|
|
33
30
|
() =>
|
|
34
31
|
({
|
|
35
32
|
role: 'meter',
|
|
36
|
-
value: this.opts.value
|
|
37
|
-
'aria-valuemin': this.opts.min
|
|
38
|
-
'aria-valuemax': this.opts.max
|
|
39
|
-
'aria-valuenow': this.opts.value
|
|
40
|
-
'data-value': this.opts.value
|
|
41
|
-
'data-max': this.opts.max
|
|
42
|
-
'data-min': this.opts.min
|
|
33
|
+
value: this.opts.value,
|
|
34
|
+
'aria-valuemin': this.opts.min,
|
|
35
|
+
'aria-valuemax': this.opts.max,
|
|
36
|
+
'aria-valuenow': this.opts.value,
|
|
37
|
+
'data-value': this.opts.value,
|
|
38
|
+
'data-max': this.opts.max,
|
|
39
|
+
'data-min': this.opts.min,
|
|
43
40
|
[meterAttrs.root]: '',
|
|
44
41
|
...this.attachment,
|
|
45
42
|
}) as const,
|
|
@@ -53,6 +53,6 @@
|
|
|
53
53
|
);
|
|
54
54
|
</script>
|
|
55
55
|
|
|
56
|
-
<Portal to={contentState.context.viewportRef
|
|
56
|
+
<Portal to={contentState.context.viewportRef || undefined} disabled={!contentState.context.viewportRef}>
|
|
57
57
|
<NavigationMenuContentImpl {...mergedProps} {children} {child} />
|
|
58
58
|
</Portal>
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
);
|
|
42
42
|
</script>
|
|
43
43
|
|
|
44
|
-
{#if indicatorState.context.indicatorTrackRef
|
|
45
|
-
<Portal to={indicatorState.context.indicatorTrackRef
|
|
44
|
+
{#if indicatorState.context.indicatorTrackRef}
|
|
45
|
+
<Portal to={indicatorState.context.indicatorTrackRef}>
|
|
46
46
|
<NavigationMenuIndicatorImpl {...mergedProps} {id} bind:ref {child}>
|
|
47
47
|
{#snippet children()}
|
|
48
48
|
{#if children}
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<!-- focus proxy: visually hidden (the sr-only utility) but focusable, for nav-menu focus management -->
|
|
75
75
|
<span {...mergeProps(triggerState.focusProxyProps, mounted, { class: 'sr-only' })}></span>
|
|
76
76
|
|
|
77
|
-
{#if triggerState.context.viewportRef
|
|
77
|
+
{#if triggerState.context.viewportRef}
|
|
78
78
|
<span aria-owns={triggerState.itemContext.contentId ?? undefined}></span>
|
|
79
79
|
{/if}
|
|
80
80
|
{/if}
|
|
@@ -4,20 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import {
|
|
6
6
|
type AnyFn,
|
|
7
|
-
type ReadableBox,
|
|
8
7
|
type ReadableBoxedValues,
|
|
9
8
|
type WithRefProps,
|
|
10
|
-
type WritableBox,
|
|
11
9
|
type WritableBoxedValues,
|
|
12
10
|
attachRef,
|
|
13
|
-
boxAutoReset,
|
|
14
11
|
DOMContext,
|
|
15
12
|
getWindow,
|
|
16
|
-
simpleBox,
|
|
17
13
|
boxWith,
|
|
18
14
|
} from '../../internal/tools/index.js';
|
|
19
15
|
import { createEffectTimeout } from '../../internal/timeout-fn.svelte.js';
|
|
20
|
-
import { createContext, tick, untrack
|
|
16
|
+
import { createContext, tick, untrack } from 'svelte';
|
|
21
17
|
import { SvelteMap } from 'svelte/reactivity';
|
|
22
18
|
import { type Direction, type Orientation } from '../../internal/index.js';
|
|
23
19
|
import { createId } from '../../internal/create-id.js';
|
|
@@ -29,7 +25,7 @@ import { kbd } from '../../internal/kbd.js';
|
|
|
29
25
|
import { on } from 'svelte/events';
|
|
30
26
|
import { isElement } from '../../internal/tools/utils/dom.js';
|
|
31
27
|
import type { FocusEventHandler, KeyboardEventHandler, MouseEventHandler, PointerEventHandler } from 'svelte/elements';
|
|
32
|
-
import { RovingFocusGroup } from '../../internal/roving-focus-group.js';
|
|
28
|
+
import { RovingFocusGroup } from '../../internal/roving-focus-group.svelte.js';
|
|
33
29
|
import { observeResizeMany } from '../../internal/svelte-resize-observer.svelte.js';
|
|
34
30
|
|
|
35
31
|
const navigationMenuAttrs = createBitsAttrs({
|
|
@@ -79,8 +75,8 @@ class NavigationMenuProviderState {
|
|
|
79
75
|
return setNavigationMenuProvider(new NavigationMenuProviderState(opts));
|
|
80
76
|
}
|
|
81
77
|
readonly opts: NavigationMenuProviderStateOpts;
|
|
82
|
-
indicatorTrackRef =
|
|
83
|
-
viewportRef =
|
|
78
|
+
indicatorTrackRef = $state.raw<HTMLElement | null>(null);
|
|
79
|
+
viewportRef = $state.raw<HTMLElement | null>(null);
|
|
84
80
|
viewportContent = new SvelteMap<string, NavigationMenuItemState>();
|
|
85
81
|
onTriggerEnter: NavigationMenuProviderStateOpts['onTriggerEnter'];
|
|
86
82
|
onTriggerLeave: () => void = () => {};
|
|
@@ -126,29 +122,27 @@ export class NavigationMenuRootState {
|
|
|
126
122
|
readonly opts: NavigationMenuRootStateOpts;
|
|
127
123
|
readonly attachment: RefAttachment;
|
|
128
124
|
provider: NavigationMenuProviderState;
|
|
129
|
-
previousValue =
|
|
130
|
-
|
|
125
|
+
previousValue = $state.raw('');
|
|
126
|
+
// False from an open until skipDelayDuration after the close: a hover in that window skips delayDuration.
|
|
127
|
+
#isOpenDelayed = $state(true);
|
|
128
|
+
readonly #skipDelayTimer: ReturnType<typeof createEffectTimeout<() => void>>;
|
|
131
129
|
readonly #derivedDelay = $derived.by(() => {
|
|
132
130
|
const isOpen = this.opts?.value?.current !== '';
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return 150;
|
|
136
|
-
} else {
|
|
137
|
-
return this.opts.delayDuration.current;
|
|
138
|
-
}
|
|
131
|
+
// 150ms while open or in the skip window: room to switch trigger or move into the content.
|
|
132
|
+
return isOpen || !this.#isOpenDelayed ? 150 : this.opts.delayDuration.current;
|
|
139
133
|
});
|
|
140
134
|
|
|
141
135
|
constructor(opts: NavigationMenuRootStateOpts) {
|
|
142
136
|
this.opts = opts;
|
|
143
137
|
this.attachment = attachRef(this.opts.ref);
|
|
144
|
-
this
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
138
|
+
this.#skipDelayTimer = createEffectTimeout(
|
|
139
|
+
() => (this.#isOpenDelayed = true),
|
|
140
|
+
() => this.opts.skipDelayDuration.current,
|
|
141
|
+
);
|
|
148
142
|
|
|
149
143
|
this.provider = NavigationMenuProviderState.create({
|
|
150
144
|
value: this.opts.value,
|
|
151
|
-
previousValue: this.previousValue,
|
|
145
|
+
previousValue: boxWith(() => this.previousValue, (v) => (this.previousValue = v)),
|
|
152
146
|
dir: this.opts.dir,
|
|
153
147
|
orientation: this.opts.orientation,
|
|
154
148
|
rootNavigationMenuRef: this.opts.ref,
|
|
@@ -179,7 +173,6 @@ export class NavigationMenuRootState {
|
|
|
179
173
|
};
|
|
180
174
|
|
|
181
175
|
#onTriggerLeave = () => {
|
|
182
|
-
this.isDelaySkipped.current = false;
|
|
183
176
|
this.#debouncedFn.start('', null);
|
|
184
177
|
};
|
|
185
178
|
|
|
@@ -203,14 +196,19 @@ export class NavigationMenuRootState {
|
|
|
203
196
|
};
|
|
204
197
|
|
|
205
198
|
setValue = (newValue: string, itemState: NavigationMenuItemState | null) => {
|
|
206
|
-
this.previousValue
|
|
199
|
+
this.previousValue = this.opts.value.current;
|
|
207
200
|
this.opts.value.current = newValue;
|
|
208
201
|
this.provider.setActiveItem(itemState);
|
|
209
202
|
|
|
210
203
|
// When all menus are closed, we want to reset previousValue to prevent
|
|
211
204
|
// weird transitions from old positions when opening fresh
|
|
212
205
|
if (newValue === '') {
|
|
213
|
-
this.previousValue
|
|
206
|
+
this.previousValue = '';
|
|
207
|
+
if (this.opts.skipDelayDuration.current === 0) this.#isOpenDelayed = true;
|
|
208
|
+
else this.#skipDelayTimer.start();
|
|
209
|
+
} else {
|
|
210
|
+
this.#skipDelayTimer.stop();
|
|
211
|
+
this.#isOpenDelayed = false;
|
|
214
212
|
}
|
|
215
213
|
};
|
|
216
214
|
|
|
@@ -243,7 +241,7 @@ export class NavigationMenuSubState {
|
|
|
243
241
|
}
|
|
244
242
|
readonly opts: NavigationMenuSubStateOpts;
|
|
245
243
|
readonly context: NavigationMenuProviderState;
|
|
246
|
-
previousValue =
|
|
244
|
+
previousValue = $state.raw('');
|
|
247
245
|
readonly subProvider: NavigationMenuProviderState;
|
|
248
246
|
readonly attachment: RefAttachment;
|
|
249
247
|
|
|
@@ -261,19 +259,19 @@ export class NavigationMenuSubState {
|
|
|
261
259
|
onTriggerEnter: this.setValue,
|
|
262
260
|
onItemSelect: this.setValue,
|
|
263
261
|
onItemDismiss: () => this.setValue('', null),
|
|
264
|
-
previousValue: this.previousValue,
|
|
262
|
+
previousValue: boxWith(() => this.previousValue, (v) => (this.previousValue = v)),
|
|
265
263
|
});
|
|
266
264
|
}
|
|
267
265
|
|
|
268
266
|
setValue = (newValue: string, itemState: NavigationMenuItemState | null) => {
|
|
269
|
-
this.previousValue
|
|
267
|
+
this.previousValue = this.opts.value.current;
|
|
270
268
|
this.opts.value.current = newValue;
|
|
271
269
|
this.subProvider.setActiveItem(itemState);
|
|
272
270
|
|
|
273
271
|
// When all menus are closed, we want to reset previousValue to prevent
|
|
274
272
|
// weird transitions from old positions when opening fresh
|
|
275
273
|
if (newValue === '') {
|
|
276
|
-
this.previousValue
|
|
274
|
+
this.previousValue = '';
|
|
277
275
|
}
|
|
278
276
|
};
|
|
279
277
|
|
|
@@ -295,12 +293,11 @@ export class NavigationMenuListState {
|
|
|
295
293
|
static create(opts: NavigationMenuListStateOpts) {
|
|
296
294
|
return setNavigationMenuList(new NavigationMenuListState(opts, getNavigationMenuProvider()));
|
|
297
295
|
}
|
|
298
|
-
wrapperId
|
|
299
|
-
wrapperRef = simpleBox<HTMLElement | null>(null);
|
|
296
|
+
readonly wrapperId: string;
|
|
300
297
|
readonly opts: NavigationMenuListStateOpts;
|
|
301
298
|
readonly context: NavigationMenuProviderState;
|
|
302
299
|
readonly attachment: RefAttachment;
|
|
303
|
-
readonly wrapperAttachment: RefAttachment = attachRef(
|
|
300
|
+
readonly wrapperAttachment: RefAttachment = attachRef((v) => (this.context.indicatorTrackRef = v));
|
|
304
301
|
listTriggers = $state.raw<HTMLElement[]>([]);
|
|
305
302
|
readonly rovingFocusGroup: RovingFocusGroup;
|
|
306
303
|
wrapperMounted = $state(false);
|
|
@@ -308,13 +305,13 @@ export class NavigationMenuListState {
|
|
|
308
305
|
constructor(opts: NavigationMenuListStateOpts, context: NavigationMenuProviderState) {
|
|
309
306
|
this.opts = opts;
|
|
310
307
|
this.context = context;
|
|
311
|
-
this.wrapperId
|
|
308
|
+
this.wrapperId = createId('wrapper', opts.id.current);
|
|
312
309
|
this.attachment = attachRef(this.opts.ref);
|
|
313
310
|
this.rovingFocusGroup = new RovingFocusGroup({
|
|
314
|
-
rootNode: opts.ref,
|
|
311
|
+
rootNode: () => opts.ref.current,
|
|
315
312
|
candidateSelector: `${navigationMenuAttrs.selector('trigger')}:not([data-disabled]), ${navigationMenuAttrs.selector('link')}:not([data-disabled])`,
|
|
316
|
-
loop:
|
|
317
|
-
orientation: this.context.opts.orientation,
|
|
313
|
+
loop: () => false,
|
|
314
|
+
orientation: () => this.context.opts.orientation.current,
|
|
318
315
|
});
|
|
319
316
|
}
|
|
320
317
|
|
|
@@ -328,7 +325,7 @@ export class NavigationMenuListState {
|
|
|
328
325
|
readonly wrapperProps = $derived.by(
|
|
329
326
|
() =>
|
|
330
327
|
({
|
|
331
|
-
id: this.wrapperId
|
|
328
|
+
id: this.wrapperId,
|
|
332
329
|
...this.wrapperAttachment,
|
|
333
330
|
}) as const,
|
|
334
331
|
);
|
|
@@ -366,9 +363,6 @@ export class NavigationMenuItemState {
|
|
|
366
363
|
wasEscapeClose = false;
|
|
367
364
|
readonly contentId = $derived.by(() => this.contentNode?.id);
|
|
368
365
|
readonly triggerId = $derived.by(() => this.triggerNode?.id);
|
|
369
|
-
contentChildren: ReadableBox<Snippet | undefined> = simpleBox(undefined);
|
|
370
|
-
contentChild: ReadableBox<Snippet<[{ props: Record<string, unknown> }]> | undefined> = simpleBox(undefined);
|
|
371
|
-
contentProps: ReadableBox<Record<string, unknown>> = simpleBox({});
|
|
372
366
|
domContext: DOMContext;
|
|
373
367
|
constructor(opts: NavigationMenuItemStateOpts, listContext: NavigationMenuListState) {
|
|
374
368
|
this.opts = opts;
|
|
@@ -423,13 +417,13 @@ export class NavigationMenuTriggerState {
|
|
|
423
417
|
}
|
|
424
418
|
readonly opts: NavigationMenuTriggerStateOpts;
|
|
425
419
|
readonly attachment: RefAttachment;
|
|
426
|
-
focusProxyId
|
|
427
|
-
|
|
428
|
-
readonly focusProxyAttachment: RefAttachment = attachRef(this.focusProxyRef, (v) => (this.itemContext.focusProxyNode = v));
|
|
420
|
+
readonly focusProxyId: string;
|
|
421
|
+
readonly focusProxyAttachment: RefAttachment = attachRef((v) => (this.itemContext.focusProxyNode = v));
|
|
429
422
|
context: NavigationMenuProviderState;
|
|
430
423
|
itemContext: NavigationMenuItemState;
|
|
431
424
|
listContext: NavigationMenuListState;
|
|
432
|
-
hasPointerMoveOpened =
|
|
425
|
+
#hasPointerMoveOpened = false;
|
|
426
|
+
readonly #pointerMoveResetTimer: ReturnType<typeof createEffectTimeout<() => void>>;
|
|
433
427
|
wasClickClose = false;
|
|
434
428
|
focusProxyMounted = $state(false);
|
|
435
429
|
readonly open = $derived.by(() => this.itemContext.opts.value.current === this.context.opts.value.current);
|
|
@@ -444,12 +438,13 @@ export class NavigationMenuTriggerState {
|
|
|
444
438
|
},
|
|
445
439
|
) {
|
|
446
440
|
this.opts = opts;
|
|
447
|
-
this.focusProxyId
|
|
441
|
+
this.focusProxyId = createId('focus-proxy', opts.id.current);
|
|
448
442
|
this.attachment = attachRef(this.opts.ref, (v) => (this.itemContext.triggerNode = v));
|
|
449
|
-
this
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
443
|
+
this.#pointerMoveResetTimer = createEffectTimeout(
|
|
444
|
+
() => (this.#hasPointerMoveOpened = false),
|
|
445
|
+
() => 300,
|
|
446
|
+
() => getWindow(opts.ref.current),
|
|
447
|
+
);
|
|
453
448
|
this.context = context.provider;
|
|
454
449
|
this.itemContext = context.item;
|
|
455
450
|
this.listContext = context.list;
|
|
@@ -474,24 +469,26 @@ export class NavigationMenuTriggerState {
|
|
|
474
469
|
this.opts.disabled.current ||
|
|
475
470
|
this.wasClickClose ||
|
|
476
471
|
this.itemContext.wasEscapeClose ||
|
|
477
|
-
this
|
|
472
|
+
this.#hasPointerMoveOpened ||
|
|
478
473
|
!this.itemContext.opts.openOnHover.current
|
|
479
474
|
) {
|
|
480
475
|
return;
|
|
481
476
|
}
|
|
482
477
|
this.context.onTriggerEnter(this.itemContext.opts.value.current, this.itemContext);
|
|
483
|
-
this
|
|
478
|
+
this.#hasPointerMoveOpened = true;
|
|
479
|
+
this.#pointerMoveResetTimer.start();
|
|
484
480
|
});
|
|
485
481
|
|
|
486
482
|
onpointerleave = whenMouse(() => {
|
|
487
483
|
if (this.opts.disabled.current || !this.itemContext.opts.openOnHover.current) return;
|
|
488
484
|
this.context.onTriggerLeave();
|
|
489
|
-
this
|
|
485
|
+
this.#hasPointerMoveOpened = false;
|
|
486
|
+
this.#pointerMoveResetTimer.stop();
|
|
490
487
|
});
|
|
491
488
|
|
|
492
489
|
onclick: MouseEventHandler<HTMLButtonElement> = () => {
|
|
493
490
|
// if opened via pointer move, we prevent the click event
|
|
494
|
-
if (this
|
|
491
|
+
if (this.#hasPointerMoveOpened) return;
|
|
495
492
|
const shouldClose = this.open && (!this.itemContext.opts.openOnHover.current || this.context.opts.isRootMenu);
|
|
496
493
|
if (shouldClose) {
|
|
497
494
|
this.context.onItemSelect('', null);
|
|
@@ -547,7 +544,7 @@ export class NavigationMenuTriggerState {
|
|
|
547
544
|
readonly focusProxyProps = $derived.by(
|
|
548
545
|
() =>
|
|
549
546
|
({
|
|
550
|
-
id: this.focusProxyId
|
|
547
|
+
id: this.focusProxyId,
|
|
551
548
|
tabindex: 0,
|
|
552
549
|
onfocus: this.focusProxyOnFocus,
|
|
553
550
|
...this.focusProxyAttachment,
|
|
@@ -692,7 +689,7 @@ export class NavigationMenuIndicatorImplState {
|
|
|
692
689
|
this.listContext = context.list;
|
|
693
690
|
this.attachment = attachRef(this.opts.ref);
|
|
694
691
|
|
|
695
|
-
observeResizeMany(() => [this.activeTrigger, this.context.indicatorTrackRef
|
|
692
|
+
observeResizeMany(() => [this.activeTrigger, this.context.indicatorTrackRef], this.handlePositionChange);
|
|
696
693
|
}
|
|
697
694
|
|
|
698
695
|
handlePositionChange = () => {
|
|
@@ -846,10 +843,10 @@ export class NavigationMenuContentImplState {
|
|
|
846
843
|
this.context = itemContext.listContext.context;
|
|
847
844
|
this.domContext = new DOMContext(opts.ref);
|
|
848
845
|
this.rovingFocusGroup = new RovingFocusGroup({
|
|
849
|
-
rootNode: this.opts.ref,
|
|
846
|
+
rootNode: () => this.opts.ref.current,
|
|
850
847
|
candidateNodes: getTabbableCandidates,
|
|
851
|
-
loop:
|
|
852
|
-
orientation: this.context.opts.orientation,
|
|
848
|
+
loop: () => false,
|
|
849
|
+
orientation: () => this.context.opts.orientation.current,
|
|
853
850
|
});
|
|
854
851
|
|
|
855
852
|
$effect(() => {
|
|
@@ -886,7 +883,7 @@ export class NavigationMenuContentImplState {
|
|
|
886
883
|
onInteractOutside = (e: PointerEvent) => {
|
|
887
884
|
const target = e.target as HTMLElement;
|
|
888
885
|
const isTrigger = this.listContext.listTriggers.some((trigger) => trigger.contains(target));
|
|
889
|
-
const isRootViewport = this.context.opts.isRootMenu && this.context.viewportRef
|
|
886
|
+
const isRootViewport = this.context.opts.isRootMenu && this.context.viewportRef?.contains(target);
|
|
890
887
|
if (!this.context.opts.isRootMenu && !isTrigger) {
|
|
891
888
|
this.context.onItemDismiss();
|
|
892
889
|
return;
|
|
@@ -988,14 +985,14 @@ export class NavigationMenuViewportState {
|
|
|
988
985
|
constructor(opts: NavigationMenuViewportStateOpts, context: NavigationMenuProviderState) {
|
|
989
986
|
this.opts = opts;
|
|
990
987
|
this.context = context;
|
|
991
|
-
this.attachment = attachRef(this.opts.ref, (v) => (this.context.viewportRef
|
|
988
|
+
this.attachment = attachRef(this.opts.ref, (v) => (this.context.viewportRef = v));
|
|
992
989
|
|
|
993
990
|
$effect(() => {
|
|
994
991
|
const _activeContentValue = this.activeContentValue;
|
|
995
992
|
const _open = this.open;
|
|
996
993
|
untrack(() => {
|
|
997
994
|
tick().then(() => {
|
|
998
|
-
const currNode = this.context.viewportRef
|
|
995
|
+
const currNode = this.context.viewportRef;
|
|
999
996
|
if (!currNode) return;
|
|
1000
997
|
const el = (currNode.querySelector<HTMLElement>('[data-state=open]')?.children?.[0] as HTMLElement | null) ?? null;
|
|
1001
998
|
|