Skip to content

Commit c86bd31

Browse files
committed
fix
1 parent b1ce76b commit c86bd31

File tree

3 files changed

+68
-33
lines changed

3 files changed

+68
-33
lines changed

packages/floating-ui-svelte/src/hooks/use-list-navigation.svelte.ts

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class ListNavigationState {
221221
#parentId: string | null = null;
222222
#tree: FloatingTreeType | null = null;
223223
#index = $state(this.#selectedIndex ?? -1);
224+
224225
#typeableComboboxReference = $derived.by(() =>
225226
isTypeableCombobox(this.context.domReference),
226227
);
@@ -232,9 +233,8 @@ class ListNavigationState {
232233

233234
#activeId = $state<string | undefined>();
234235
#virtualId = $state<string | undefined>();
235-
#previousMounted: boolean;
236-
#previousOpen: boolean;
237-
#previousOnNavigate: () => void;
236+
#mounted = false;
237+
#previousOpen = false;
238238
#hasActiveIndex = $derived.by(() => this.#activeIndex != null);
239239

240240
#ariaActiveDescendantProp = $derived.by(() => {
@@ -273,9 +273,7 @@ class ListNavigationState {
273273
this.#virtualItemRef = this.opts.virtualItemRef;
274274
this.#parentId = useFloatingParentNodeId();
275275
this.#tree = useFloatingTree();
276-
this.#previousMounted = !!this.context.floating;
277-
this.#previousOpen = this.context.open;
278-
this.#previousOnNavigate = this.#onNavigate;
276+
this.#mounted = !!this.context.floating;
279277

280278
// Sync `selectedIndex` to be the `activeIndex` upon opening the floating
281279
// element. Also, reset `activeIndex` upon closing the floating element.
@@ -296,7 +294,7 @@ class ListNavigationState {
296294
this.#index = this.#selectedIndex;
297295
this.#onNavigate();
298296
}
299-
} else if (this.#previousMounted) {
297+
} else if (this.#mounted) {
300298
this.#index = -1;
301299
this.#onNavigate();
302300
}
@@ -331,14 +329,14 @@ class ListNavigationState {
331329
}
332330

333331
// Reset while the floating element was open (e.g. the list changed).
334-
if (this.#previousMounted) {
332+
if (this.#mounted) {
335333
this.#index = -1;
336334
this.#focusItem();
337335
}
338336

339337
// Initial sync.
340338
if (
341-
(!this.#previousOpen || !this.#previousMounted) &&
339+
(!this.#previousOpen || !this.#mounted) &&
342340
this.#focusItemOnOpen &&
343341
(this.#key != null ||
344342
(this.#focusItemOnOpen === true && this.#key == null))
@@ -397,7 +395,7 @@ class ListNavigationState {
397395
this.context.floating ||
398396
!this.#tree ||
399397
this.#virtual ||
400-
!this.#previousMounted
398+
!this.#mounted
401399
) {
402400
return;
403401
}
@@ -446,17 +444,13 @@ class ListNavigationState {
446444
);
447445

448446
$effect.pre(() => {
449-
this.#previousMounted = !!this.context.floating;
447+
this.#mounted = !!this.context.floating;
450448
});
451449

452450
$effect.pre(() => {
453451
this.#previousOpen = this.context.open;
454452
});
455453

456-
$effect.pre(() => {
457-
this.#previousOnNavigate = this.#onNavigate;
458-
});
459-
460454
$effect.pre(() => {
461455
this.#focusItemOnOpen = this.#focusItemOnOpenProp;
462456
});
@@ -472,7 +466,6 @@ class ListNavigationState {
472466
}
473467

474468
#onNavigate = () => {
475-
console.log("calling on navigate");
476469
this.opts.onNavigate?.(this.#index === -1 ? null : this.#index);
477470
};
478471

@@ -811,6 +804,7 @@ class ListNavigationState {
811804

812805
#referenceOnKeyDown = (event: KeyboardEvent) => {
813806
this.#isPointerModality = false;
807+
const isOpen = this.context.open;
814808

815809
const isArrowKey = event.key.startsWith("Arrow");
816810
const isHomeOrEndKey = ["Home", "End"].includes(event.key);
@@ -831,7 +825,7 @@ class ListNavigationState {
831825
event.key === "Enter" ||
832826
event.key.trim() === "";
833827

834-
if (this.#virtual && this.context.open) {
828+
if (this.#virtual && isOpen) {
835829
const rootNode = this.#tree?.nodes.find((node) => node.parentId == null);
836830
const deepestNode =
837831
this.#tree && rootNode
@@ -879,9 +873,7 @@ class ListNavigationState {
879873

880874
// If a floating element should not open on arrow key down, avoid
881875
// setting `activeIndex` while it's closed.
882-
if (!this.context.open && !this.#openOnArrowKeyDown && isArrowKey) {
883-
return;
884-
}
876+
if (!isOpen && !this.#openOnArrowKeyDown && isArrowKey) return;
885877

886878
if (isNavigationKey) {
887879
this.#key = this.#nested && isMainKey ? null : event.key;
@@ -891,7 +883,7 @@ class ListNavigationState {
891883
if (isCrossOpenKey) {
892884
stopEvent(event);
893885

894-
if (this.context.open) {
886+
if (isOpen) {
895887
this.#index = getMinIndex(this.#listRef, this.#disabledIndices);
896888
this.#onNavigate();
897889
} else {
@@ -909,13 +901,13 @@ class ListNavigationState {
909901

910902
stopEvent(event);
911903

912-
if (!this.context.open && this.#openOnArrowKeyDown) {
904+
if (!isOpen && this.#openOnArrowKeyDown) {
913905
this.context.onOpenChange(true, event, "list-navigation");
914906
} else {
915907
this.#commonOnKeyDown(event);
916908
}
917909

918-
if (this.context.open) {
910+
if (isOpen) {
919911
this.#onNavigate();
920912
}
921913
}

packages/floating-ui-svelte/test/unit/hooks/use-list-navigation.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,7 @@ describe("allowEscape + virtual", () => {
273273
);
274274
});
275275

276-
// TODO: Fix this test.
277-
it.skip("true - calls `onNavigate` with `null` when escaped", async () => {
276+
it("true - calls `onNavigate` with `null` when escaped", async () => {
278277
const spy = vi.fn();
279278
render(Main, {
280279
allowEscape: true,
Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,56 @@
11
<script lang="ts">
2-
import { useFloating } from "../../../../src/index.js";
2+
import {
3+
useClick,
4+
useFloating,
5+
useInteractions,
6+
useListNavigation,
7+
type UseListNavigationOptions,
8+
} from "../../../../src/index.js";
39
4-
const f = useFloating();
10+
let props: Omit<Partial<UseListNavigationOptions>, "listRef"> = $props();
11+
12+
let open = $state(false);
13+
let listRef = $state<Array<HTMLLIElement | null>>([]);
14+
let activeIndex = $state<number | null>(null);
15+
16+
const f = useFloating({
17+
open: () => open,
18+
onOpenChange: (v) => (open = v),
19+
});
20+
21+
const ints = useInteractions([
22+
useClick(f.context),
23+
useListNavigation(f.context, {
24+
...props,
25+
allowEscape: true,
26+
virtual: true,
27+
loop: true,
28+
listRef: () => listRef,
29+
activeIndex: () => activeIndex,
30+
onNavigate: (index) => {
31+
activeIndex = index;
32+
console.log("onNavigateCalled");
33+
props.onNavigate?.(index);
34+
},
35+
}),
36+
]);
537
</script>
638

7-
<h1 class="text-5xl font-bold mb-8">New</h1>
8-
<div
9-
class="grid place-items-center border border-slate-400 rounded lg:w-[40rem] h-[20rem] mb-4">
10-
<div bind:this={f.reference}>Reference</div>
11-
<div bind:this={f.floating} style={f.floatingStyles}>Floating</div>
12-
</div>
39+
<button {...ints.getReferenceProps()} bind:this={f.reference}> Open </button>
40+
{#if open}
41+
<div role="menu" {...ints.getFloatingProps()} bind:this={f.floating}>
42+
<ul>
43+
{#each ["one", "two", "three"] as str, index (str)}
44+
<li
45+
role="option"
46+
data-testid={`item-${index}`}
47+
aria-selected={activeIndex === index}
48+
tabIndex={-1}
49+
{...ints.getItemProps()}
50+
bind:this={listRef[index]}>
51+
{str}
52+
</li>
53+
{/each}
54+
</ul>
55+
</div>
56+
{/if}

0 commit comments

Comments
 (0)