From dc30c09ab0e5c6c8ee75cfe1e770051b621a30d8 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 05:27:24 -0400 Subject: [PATCH] Only register an option once in strict mode (#3692) I'm not 100% sure this is the right fix but it _does_ fix keyboard navigation in listbox and menu when using strict mode. Basically the same items/options are being registered more than once and that causes the arrow up/down logic to only advance on every other keypress. --- .../src/components/listbox/listbox-machine.ts | 11 +++++++++-- .../src/components/menu/menu-machine.ts | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/@headlessui-react/src/components/listbox/listbox-machine.ts b/packages/@headlessui-react/src/components/listbox/listbox-machine.ts index 9004cdc..26b7fb3 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox-machine.ts +++ b/packages/@headlessui-react/src/components/listbox/listbox-machine.ts @@ -426,10 +426,17 @@ export class ListboxMachine extends Machine, Actions> { }, registerOption: batch(() => { let options: { id: string; dataRef: ListboxOptionDataRef }[] = [] + let seen = new Set>() + return [ - (id: string, dataRef: ListboxOptionDataRef) => options.push({ id, dataRef }), + (id: string, dataRef: ListboxOptionDataRef) => { + if (seen.has(dataRef)) return + seen.add(dataRef) + options.push({ id, dataRef }) + }, () => { - this.send({ type: ActionTypes.RegisterOptions, options: options.splice(0) }) + seen.clear() + return this.send({ type: ActionTypes.RegisterOptions, options: options.splice(0) }) }, ] }), diff --git a/packages/@headlessui-react/src/components/menu/menu-machine.ts b/packages/@headlessui-react/src/components/menu/menu-machine.ts index 0ab812e..b888690 100644 --- a/packages/@headlessui-react/src/components/menu/menu-machine.ts +++ b/packages/@headlessui-react/src/components/menu/menu-machine.ts @@ -366,10 +366,18 @@ export class MenuMachine extends Machine { // Batched version to register multiple items at the same time registerItem: batch(() => { let items: { id: string; dataRef: MenuItemDataRef }[] = [] + let seen = new Set() return [ - (id: string, dataRef: MenuItemDataRef) => items.push({ id, dataRef }), - () => this.send({ type: ActionTypes.RegisterItems, items: items.splice(0) }), + (id: string, dataRef: MenuItemDataRef) => { + if (seen.has(dataRef)) return + seen.add(dataRef) + items.push({ id, dataRef }) + }, + () => { + seen.clear() + return this.send({ type: ActionTypes.RegisterItems, items: items.splice(0) }) + }, ] }), unregisterItem: batch(() => {