From 2ef993ec04e89b58831c0066a160d5be307bf24d Mon Sep 17 00:00:00 2001 From: Junior Garcia Date: Tue, 16 Apr 2024 17:48:08 -0300 Subject: [PATCH 1/3] chore(dropdown): missing events added --- packages/components/dropdown/src/use-dropdown.ts | 6 +++++- packages/components/dropdown/stories/dropdown.stories.tsx | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/dropdown/src/use-dropdown.ts b/packages/components/dropdown/src/use-dropdown.ts index d71afed1d1..9465dccffa 100644 --- a/packages/components/dropdown/src/use-dropdown.ts +++ b/packages/components/dropdown/src/use-dropdown.ts @@ -139,7 +139,11 @@ export function useDropdown(props: UseDropdownProps) { return { ref: mergeRefs(_ref, menuRef), menuProps, - ...mergeProps(props, {onAction: () => onMenuAction(props?.closeOnSelect)}), + closeOnSelect, + ...mergeProps(props, { + onAction: () => onMenuAction(props?.closeOnSelect), + onClose: state.close, + }), } as MenuProps; }; diff --git a/packages/components/dropdown/stories/dropdown.stories.tsx b/packages/components/dropdown/stories/dropdown.stories.tsx index 95ab23cc83..e9b071cd7f 100644 --- a/packages/components/dropdown/stories/dropdown.stories.tsx +++ b/packages/components/dropdown/stories/dropdown.stories.tsx @@ -137,7 +137,7 @@ const Template = ({color, variant, ...args}: DropdownProps & DropdownMenuProps) - + New file Copy link Edit file From a7249b9b8abb706d910bc3618546e0951e145731 Mon Sep 17 00:00:00 2001 From: Junior Garcia Date: Wed, 17 Apr 2024 23:15:58 -0300 Subject: [PATCH 2/3] fix(menu): item selection events --- .../components/menu/__tests__/menu.test.tsx | 78 ++++ packages/components/menu/package.json | 2 + packages/components/menu/src/use-menu-item.ts | 39 +- packages/components/menu/src/use-menu.ts | 5 +- packages/hooks/use-aria-menu/README.md | 24 ++ packages/hooks/use-aria-menu/package.json | 63 ++++ packages/hooks/use-aria-menu/src/index.ts | 5 + .../hooks/use-aria-menu/src/use-menu-item.ts | 341 ++++++++++++++++++ packages/hooks/use-aria-menu/src/use-menu.ts | 84 +++++ packages/hooks/use-aria-menu/tsconfig.json | 4 + pnpm-lock.yaml | 43 +++ 11 files changed, 666 insertions(+), 22 deletions(-) create mode 100644 packages/hooks/use-aria-menu/README.md create mode 100644 packages/hooks/use-aria-menu/package.json create mode 100644 packages/hooks/use-aria-menu/src/index.ts create mode 100644 packages/hooks/use-aria-menu/src/use-menu-item.ts create mode 100644 packages/hooks/use-aria-menu/src/use-menu.ts create mode 100644 packages/hooks/use-aria-menu/tsconfig.json diff --git a/packages/components/menu/__tests__/menu.test.tsx b/packages/components/menu/__tests__/menu.test.tsx index 7002224632..aeb7f14a5a 100644 --- a/packages/components/menu/__tests__/menu.test.tsx +++ b/packages/components/menu/__tests__/menu.test.tsx @@ -109,6 +109,7 @@ describe("Menu", () => { {(section: any) => ( + {/* @ts-ignore */} {(item: any) => {item.name}} )} @@ -273,4 +274,81 @@ describe("Menu", () => { expect(checkmark1).toBeFalsy(); }); + + it("should dispatch onAction events correctly", async () => { + let onAction = jest.fn(); + + const wrapper = render( + + New file + Copy link + Edit file + + Delete file + + , + ); + + let menuItems = wrapper.getAllByRole("menuitem"); + + await act(async () => { + await userEvent.click(menuItems[1]); + + expect(onAction).toBeCalledTimes(1); + }); + }); + + it("should not dispatch onAction events if item is disabled", async () => { + let onAction = jest.fn(); + + const wrapper = render( + + New file + + Copy link + + Edit file + + Delete file + + , + ); + + let menuItems = wrapper.getAllByRole("menuitem"); + + await act(async () => { + await userEvent.click(menuItems[1]); + + expect(onAction).toBeCalledTimes(0); + }); + }); + + it("should dispatch onPress, onAction and onClick events", async () => { + let onPress = jest.fn(); + let onClick = jest.fn(); + let onAction = jest.fn(); + + const wrapper = render( + + + New file + + Copy link + Edit file + + Delete file + + , + ); + + let menuItems = wrapper.getAllByRole("menuitem"); + + await act(async () => { + await userEvent.click(menuItems[0]); + + expect(onAction).toBeCalledTimes(1); + expect(onPress).toBeCalledTimes(1); + expect(onClick).toBeCalledTimes(1); + }); + }); }); diff --git a/packages/components/menu/package.json b/packages/components/menu/package.json index 5e67cfc983..b7ba67b4e0 100644 --- a/packages/components/menu/package.json +++ b/packages/components/menu/package.json @@ -45,6 +45,7 @@ "@nextui-org/use-is-mobile": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", + "@nextui-org/use-aria-menu": "workspace:*", "@react-aria/focus": "^3.16.2", "@react-aria/interactions": "^3.21.1", "@react-aria/menu": "^3.13.1", @@ -57,6 +58,7 @@ "devDependencies": { "@nextui-org/theme": "workspace:*", "@nextui-org/system": "workspace:*", + "@nextui-org/test-utils": "workspace:*", "clean-package": "2.2.0", "@nextui-org/shared-icons": "workspace:*", "react": "^18.0.0", diff --git a/packages/components/menu/src/use-menu-item.ts b/packages/components/menu/src/use-menu-item.ts index cdbc056469..922ca2daad 100644 --- a/packages/components/menu/src/use-menu-item.ts +++ b/packages/components/menu/src/use-menu-item.ts @@ -1,17 +1,16 @@ import type {MenuItemBaseProps} from "./base/menu-item-base"; +import type {Node} from "@react-types/shared"; import {useMemo, useRef, useCallback} from "react"; import {menuItem} from "@nextui-org/theme"; import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; import {useFocusRing} from "@react-aria/focus"; -import {Node} from "@react-types/shared"; -import {filterDOMProps} from "@nextui-org/react-utils"; import {TreeState} from "@react-stately/tree"; import {clsx, dataAttr, objectToDeps, removeEvents} from "@nextui-org/shared-utils"; -import {useMenuItem as useAriaMenuItem} from "@react-aria/menu"; -import {chain, mergeProps} from "@react-aria/utils"; -import {useHover, usePress} from "@react-aria/interactions"; +import {useAriaMenuItem} from "@nextui-org/use-aria-menu"; +import {mergeProps} from "@react-aria/utils"; import {useIsMobile} from "@nextui-org/use-is-mobile"; +import {filterDOMProps} from "@nextui-org/react-utils"; interface Props extends MenuItemBaseProps { item: Node; @@ -38,8 +37,12 @@ export function useMenuItem(originalProps: UseMenuItemProps classNames, onAction, autoFocus, - onPress, onClick, + onPress, + onPressStart, + onPressUp, + onPressEnd, + onPressChange, hideSelectedIcon = false, isReadOnly = false, closeOnSelect, @@ -61,21 +64,13 @@ export function useMenuItem(originalProps: UseMenuItemProps const isMobile = useIsMobile(); - const {pressProps, isPressed} = usePress({ - ref: domRef, - isDisabled: isDisabled, - onPress, - }); - - const {isHovered, hoverProps} = useHover({ - isDisabled, - }); - const {isFocusVisible, focusProps} = useFocusRing({ autoFocus, }); const { + isHovered, + isPressed, isFocused, isSelected, menuItemProps, @@ -87,6 +82,12 @@ export function useMenuItem(originalProps: UseMenuItemProps key, onClose, isDisabled, + onPress, + onClick, + onPressStart, + onPressUp, + onPressEnd, + onPressChange, "aria-label": props["aria-label"], closeOnSelect, isVirtualized, @@ -117,12 +118,11 @@ export function useMenuItem(originalProps: UseMenuItemProps const getItemProps: PropGetter = (props = {}) => ({ ref: domRef, ...mergeProps( - itemProps, - isReadOnly ? {} : mergeProps(focusProps, pressProps), - hoverProps, + isReadOnly ? {} : focusProps, filterDOMProps(otherProps, { enabled: shouldFilterDOMProps, }), + itemProps, props, ), "data-focus": dataAttr(isFocused), @@ -133,7 +133,6 @@ export function useMenuItem(originalProps: UseMenuItemProps "data-pressed": dataAttr(isPressed), "data-focus-visible": dataAttr(isFocusVisible), className: slots.base({class: clsx(baseStyles, props.className)}), - onClick: chain(pressProps.onClick, onClick), }); const getLabelProps: PropGetter = (props = {}) => ({ diff --git a/packages/components/menu/src/use-menu.ts b/packages/components/menu/src/use-menu.ts index 48191e29a2..fa1dd7e847 100644 --- a/packages/components/menu/src/use-menu.ts +++ b/packages/components/menu/src/use-menu.ts @@ -1,7 +1,8 @@ import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; -import type {AriaMenuProps} from "@react-types/menu"; -import {AriaMenuOptions, useMenu as useAriaMenu} from "@react-aria/menu"; +import {AriaMenuProps} from "@react-types/menu"; +import {AriaMenuOptions} from "@react-aria/menu"; +import {useAriaMenu} from "@nextui-org/use-aria-menu"; import {menu, MenuVariantProps, SlotsToClasses, MenuSlots} from "@nextui-org/theme"; import {TreeState, useTreeState} from "@react-stately/tree"; import {ReactRef, filterDOMProps, useDOMRef} from "@nextui-org/react-utils"; diff --git a/packages/hooks/use-aria-menu/README.md b/packages/hooks/use-aria-menu/README.md new file mode 100644 index 0000000000..b0e31c23d8 --- /dev/null +++ b/packages/hooks/use-aria-menu/README.md @@ -0,0 +1,24 @@ +# @nextui-org/use-aria-menu-item + +A Quick description of the component + +> This is an internal utility, not intended for public usage. + +## Installation + +```sh +yarn add @nextui-org/use-aria-menu-item +# or +npm i @nextui-org/use-aria-menu-item +``` + +## Contribution + +Yes please! See the +[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md) +for details. + +## License + +This project is licensed under the terms of the +[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE). diff --git a/packages/hooks/use-aria-menu/package.json b/packages/hooks/use-aria-menu/package.json new file mode 100644 index 0000000000..013099fcd4 --- /dev/null +++ b/packages/hooks/use-aria-menu/package.json @@ -0,0 +1,63 @@ +{ + "name": "@nextui-org/use-aria-menu", + "version": "2.0.0", + "description": "React-aria useMenu hooks with custom implementations", + "keywords": [ + "use-aria-menu" + ], + "author": "Junior Garcia ", + "homepage": "https://nextui.org", + "license": "MIT", + "main": "src/index.ts", + "sideEffects": false, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/nextui-org/nextui.git", + "directory": "packages/hooks/use-aria-menu" + }, + "bugs": { + "url": "https://github.com/nextui-org/nextui/issues" + }, + "scripts": { + "build": "tsup src --dts", + "build:fast": "tsup src", + "dev": "pnpm build:fast --watch", + "clean": "rimraf dist .turbo", + "typecheck": "tsc --noEmit", + "prepack": "clean-package", + "postpack": "clean-package restore" + }, + "peerDependencies": { + "react": ">=18" + }, + "dependencies": { + "@react-aria/utils": "^3.23.2", + "@react-types/shared": "^3.22.1", + "@react-aria/menu": "^3.13.1", + "@react-aria/interactions": "^3.21.1", + "@react-stately/tree": "^3.7.6", + "@react-aria/i18n": "^3.10.2", + "@react-aria/selection": "^3.17.5", + "@react-stately/collections": "^3.10.5", + "@react-types/menu": "^3.9.7" + }, + "devDependencies": { + "clean-package": "2.2.0", + "react": "^18.0.0" + }, + "clean-package": "../../../clean-package.config.json", + "tsup": { + "clean": true, + "target": "es2019", + "format": [ + "cjs", + "esm" + ] + } +} diff --git a/packages/hooks/use-aria-menu/src/index.ts b/packages/hooks/use-aria-menu/src/index.ts new file mode 100644 index 0000000000..448ede3174 --- /dev/null +++ b/packages/hooks/use-aria-menu/src/index.ts @@ -0,0 +1,5 @@ +export {menuData, useMenu as useAriaMenu} from "./use-menu"; +export {useMenuItem as useAriaMenuItem} from "./use-menu-item"; + +export type {AriaMenuOptions, MenuAria} from "./use-menu"; +export type {AriaMenuItemProps, MenuItemAria} from "./use-menu-item"; diff --git a/packages/hooks/use-aria-menu/src/use-menu-item.ts b/packages/hooks/use-aria-menu/src/use-menu-item.ts new file mode 100644 index 0000000000..5a2357a500 --- /dev/null +++ b/packages/hooks/use-aria-menu/src/use-menu-item.ts @@ -0,0 +1,341 @@ +import { + DOMAttributes, + DOMProps, + FocusableElement, + FocusEvents, + HoverEvents, + Key, + KeyboardEvents, + PressEvent, + PressEvents, +} from "@react-types/shared"; +import {chain, filterDOMProps, mergeProps, useRouter, useSlotId} from "@react-aria/utils"; +import {getItemCount} from "@react-stately/collections"; +import {isFocusVisible, useFocus, useHover, useKeyboard, usePress} from "@react-aria/interactions"; +import {RefObject} from "react"; +import {TreeState} from "@react-stately/tree"; +import {useSelectableItem} from "@react-aria/selection"; + +import {menuData} from "./use-menu"; + +export interface MenuItemAria { + /** Props for the menu item element. */ + menuItemProps: DOMAttributes; + + /** Props for the main text element inside the menu item. */ + labelProps: DOMAttributes; + + /** Props for the description text element inside the menu item, if any. */ + descriptionProps: DOMAttributes; + + /** Props for the keyboard shortcut text element inside the item, if any. */ + keyboardShortcutProps: DOMAttributes; + + /** Whether the item is currently hovered. */ + isHovered: boolean; + /** Whether the item is currently focused. */ + isFocused: boolean; + /** Whether the item is currently selected. */ + isSelected: boolean; + /** Whether the item is currently in a pressed state. */ + isPressed: boolean; + /** Whether the item is disabled. */ + isDisabled: boolean; +} + +export interface AriaMenuItemProps + extends DOMProps, + PressEvents, + HoverEvents, + KeyboardEvents, + FocusEvents { + /** + * Whether the menu item is disabled. + * @deprecated - pass disabledKeys to useTreeState instead. + */ + isDisabled?: boolean; + + /** + * Whether the menu item is selected. + * @deprecated - pass selectedKeys to useTreeState instead. + */ + isSelected?: boolean; + + /** A screen reader only label for the menu item. */ + "aria-label"?: string; + + /** The unique key for the menu item. */ + key?: Key; + + /** + * Handler that is called when the menu should close after selecting an item. + * @deprecated - pass to the menu instead. + */ + onClose?: () => void; + + /** + * Whether the menu should close when the menu item is selected. + * @default true + */ + closeOnSelect?: boolean; + + /** Whether the menu item is contained in a virtual scrolling menu. */ + isVirtualized?: boolean; + + /** + * Handler that is called when the user activates the item. + * @deprecated - pass to the menu instead. + */ + onAction?: (key: Key) => void; + + onClick?: DOMAttributes["onClick"]; + + /** What kind of popup the item opens. */ + "aria-haspopup"?: "menu" | "dialog"; + + /** Indicates whether the menu item's popup element is expanded or collapsed. */ + "aria-expanded"?: boolean | "true" | "false"; + + /** Identifies the menu item's popup element whose contents or presence is controlled by the menu item. */ + "aria-controls"?: string; +} + +/** + * Provides the behavior and accessibility implementation for an item in a menu. + * See `useMenu` for more details about menus. + * @param props - Props for the item. + * @param state - State for the menu, as returned by `useTreeState`. + */ +export function useMenuItem( + props: AriaMenuItemProps, + state: TreeState, + ref: RefObject, +): MenuItemAria { + let { + key, + closeOnSelect, + isVirtualized, + "aria-haspopup": hasPopup, + onPressStart: pressStartProp, + onPressUp: pressUpProp, + onPress, + onPressChange, + onPressEnd, + onHoverStart: hoverStartProp, + onHoverChange, + onHoverEnd, + onKeyDown, + onKeyUp, + onFocus, + onFocusChange, + onBlur, + onClick, + } = props; + + let isTrigger = !!hasPopup; + // @ts-ignore + let isDisabled = props.isDisabled ?? state.disabledKeys.has(key); + // @ts-ignore + let isSelected = props.isSelected ?? state.selectionManager.isSelected(key); + let data = menuData.get(state); + // @ts-ignore + let onClose = props.onClose || data.onClose; + // @ts-ignore + let onAction = isTrigger ? () => {} : props.onAction || data.onAction; + let router = useRouter(); + let performAction = (e: PressEvent) => { + if (onAction) { + // @ts-ignore + onAction(key); + } + + if (e.target instanceof HTMLAnchorElement) { + router.open(e.target, e); + } + }; + + let role = "menuitem"; + + if (!isTrigger) { + if (state.selectionManager.selectionMode === "single") { + role = "menuitemradio"; + } else if (state.selectionManager.selectionMode === "multiple") { + role = "menuitemcheckbox"; + } + } + + let labelId = useSlotId(); + let descriptionId = useSlotId(); + let keyboardId = useSlotId(); + + let ariaProps = { + "aria-disabled": isDisabled || undefined, + role, + "aria-label": props["aria-label"], + "aria-labelledby": labelId, + "aria-describedby": [descriptionId, keyboardId].filter(Boolean).join(" ") || undefined, + "aria-controls": props["aria-controls"], + "aria-haspopup": hasPopup, + "aria-expanded": props["aria-expanded"], + }; + + if (state.selectionManager.selectionMode !== "none" && !isTrigger) { + // @ts-ignore + ariaProps["aria-checked"] = isSelected; + } + + // @ts-ignore + let item = state.collection.getItem(key); + + if (isVirtualized) { + // @ts-ignore + ariaProps["aria-posinset"] = item?.index; + // @ts-ignore + ariaProps["aria-setsize"] = getItemCount(state.collection); + } + + let onPressStart = (e: PressEvent) => { + if (e.pointerType === "keyboard") { + performAction(e); + } + + pressStartProp?.(e); + }; + + let onPressUp = (e: PressEvent) => { + if (e.pointerType !== "keyboard") { + setTimeout(() => { + performAction(e); + }); + // // Pressing a menu item should close by default in single selection mode but not multiple + // // selection mode, except if overridden by the closeOnSelect prop. + if ( + !isTrigger && + onClose && + (closeOnSelect ?? + (state.selectionManager.selectionMode !== "multiple" || + // @ts-ignore + state.selectionManager.isLink(key))) + ) { + setTimeout(() => { + onClose?.(); + }); + } + } + + pressUpProp?.(e); + }; + + let {itemProps, isFocused} = useSelectableItem({ + selectionManager: state.selectionManager, + // @ts-ignore + key, + ref, + shouldSelectOnPressUp: true, + allowsDifferentPressOrigin: true, + // Disable all handling of links in useSelectable item + // because we handle it ourselves. The behavior of menus + // is slightly different from other collections because + // actions are performed on key down rather than key up. + linkBehavior: "none", + }); + + let {pressProps, isPressed} = usePress({ + onPressStart, + onPress, + onPressUp, + onPressChange, + onPressEnd, + isDisabled, + }); + + let {isHovered, hoverProps} = useHover({ + isDisabled, + onHoverStart(e) { + if (!isFocusVisible()) { + state.selectionManager.setFocused(true); + // @ts-ignore + state.selectionManager.setFocusedKey(key); + } + hoverStartProp?.(e); + }, + onHoverChange, + onHoverEnd, + }); + + let {keyboardProps} = useKeyboard({ + onKeyDown: (e) => { + // Ignore repeating events, which may have started on the menu trigger before moving + // focus to the menu item. We want to wait for a second complete key press sequence. + if (e.repeat) { + e.continuePropagation(); + + return; + } + + switch (e.key) { + case " ": + if ( + !isDisabled && + state.selectionManager.selectionMode === "none" && + !isTrigger && + closeOnSelect !== false && + onClose + ) { + onClose(); + } + break; + case "Enter": + // The Enter key should always close on select, except if overridden. + if (!isDisabled && closeOnSelect !== false && !isTrigger && onClose) { + onClose(); + } + break; + default: + if (!isTrigger) { + e.continuePropagation(); + } + + onKeyDown?.(e); + break; + } + }, + onKeyUp, + }); + + let {focusProps} = useFocus({onBlur, onFocus, onFocusChange}); + // @ts-ignore + let domProps = filterDOMProps(item.props, {isLink: !!item?.props?.href}); + + delete domProps.id; + + return { + menuItemProps: { + ...ariaProps, + ...mergeProps( + domProps, + isTrigger ? {onFocus: itemProps.onFocus} : itemProps, + pressProps, + hoverProps, + keyboardProps, + focusProps, + ), + onClick: chain(onClick, pressProps.onClick), + tabIndex: itemProps.tabIndex != null ? -1 : undefined, + }, + labelProps: { + id: labelId, + }, + descriptionProps: { + id: descriptionId, + }, + keyboardShortcutProps: { + id: keyboardId, + }, + isHovered, + isFocused, + isSelected, + isPressed, + isDisabled, + }; +} diff --git a/packages/hooks/use-aria-menu/src/use-menu.ts b/packages/hooks/use-aria-menu/src/use-menu.ts new file mode 100644 index 0000000000..2dad54f1aa --- /dev/null +++ b/packages/hooks/use-aria-menu/src/use-menu.ts @@ -0,0 +1,84 @@ +/* eslint-disable no-console */ +import {AriaMenuProps} from "@react-types/menu"; +import {DOMAttributes, Key, KeyboardDelegate, KeyboardEvents} from "@react-types/shared"; +import {filterDOMProps, mergeProps} from "@react-aria/utils"; +import {RefObject} from "react"; +import {TreeState} from "@react-stately/tree"; +import {useSelectableList} from "@react-aria/selection"; + +export interface MenuAria { + /** Props for the menu element. */ + menuProps: DOMAttributes; +} + +export interface AriaMenuOptions extends Omit, "children">, KeyboardEvents { + /** Whether the menu uses virtual scrolling. */ + isVirtualized?: boolean; + + /** + * An optional keyboard delegate implementation for type to select, + * to override the default. + */ + keyboardDelegate?: KeyboardDelegate; +} + +interface MenuData { + onClose?: () => void; + onAction?: (key: Key) => void; +} + +export const menuData = new WeakMap, MenuData>(); + +/** + * Provides the behavior and accessibility implementation for a menu component. + * A menu displays a list of actions or options that a user can choose. + * @param props - Props for the menu. + * @param state - State for the menu, as returned by `useListState`. + */ +export function useMenu( + props: AriaMenuOptions, + state: TreeState, + ref: RefObject, +): MenuAria { + let {shouldFocusWrap = true, onKeyDown, onKeyUp, ...otherProps} = props; + + if (!props["aria-label"] && !props["aria-labelledby"]) { + console.warn("An aria-label or aria-labelledby prop is required for accessibility."); + } + + // @ts-ignore + let domProps = filterDOMProps(props, {labelable: true}); + let {listProps} = useSelectableList({ + ...otherProps, + ref, + selectionManager: state.selectionManager, + collection: state.collection, + disabledKeys: state.disabledKeys, + shouldFocusWrap, + linkBehavior: "override", + }); + + menuData.set(state, { + onClose: props.onClose, + onAction: props.onAction, + }); + + return { + menuProps: mergeProps( + domProps, + {onKeyDown, onKeyUp}, + { + role: "menu", + ...listProps, + // @ts-ignore + onKeyDown: (e) => { + // don't clear the menu selected keys if the user is presses escape since escape closes the menu + if (e.key !== "Escape") { + // @ts-ignore + listProps.onKeyDown(e); + } + }, + }, + ), + }; +} diff --git a/packages/hooks/use-aria-menu/tsconfig.json b/packages/hooks/use-aria-menu/tsconfig.json new file mode 100644 index 0000000000..46e3b466c2 --- /dev/null +++ b/packages/hooks/use-aria-menu/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.json", + "include": ["src", "index.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aeae2c2cfc..39cf5547e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1710,6 +1710,9 @@ importers: '@nextui-org/shared-utils': specifier: workspace:* version: link:../../utilities/shared-utils + '@nextui-org/use-aria-menu': + specifier: workspace:* + version: link:../../hooks/use-aria-menu '@nextui-org/use-is-mobile': specifier: workspace:* version: link:../../hooks/use-is-mobile @@ -1744,6 +1747,9 @@ importers: '@nextui-org/system': specifier: workspace:* version: link:../../core/system + '@nextui-org/test-utils': + specifier: workspace:* + version: link:../../utilities/test-utils '@nextui-org/theme': specifier: workspace:* version: link:../../core/theme @@ -3161,6 +3167,43 @@ importers: specifier: ^18.2.0 version: 18.2.0 + packages/hooks/use-aria-menu: + dependencies: + '@react-aria/i18n': + specifier: ^3.10.2 + version: 3.10.2(react@18.2.0) + '@react-aria/interactions': + specifier: ^3.21.1 + version: 3.21.1(react@18.2.0) + '@react-aria/menu': + specifier: ^3.13.1 + version: 3.13.1(react-dom@18.2.0)(react@18.2.0) + '@react-aria/selection': + specifier: ^3.17.5 + version: 3.17.5(react-dom@18.2.0)(react@18.2.0) + '@react-aria/utils': + specifier: ^3.23.2 + version: 3.23.2(react@18.2.0) + '@react-stately/collections': + specifier: ^3.10.5 + version: 3.10.5(react@18.2.0) + '@react-stately/tree': + specifier: ^3.7.6 + version: 3.7.6(react@18.2.0) + '@react-types/menu': + specifier: ^3.9.7 + version: 3.9.7(react@18.2.0) + '@react-types/shared': + specifier: ^3.22.1 + version: 3.22.1(react@18.2.0) + devDependencies: + clean-package: + specifier: 2.2.0 + version: 2.2.0 + react: + specifier: ^18.2.0 + version: 18.2.0 + packages/hooks/use-aria-modal-overlay: dependencies: '@react-aria/overlays': From 70bd1b92a9981075ed96de59df452e318da97696 Mon Sep 17 00:00:00 2001 From: Junior Garcia Date: Wed, 17 Apr 2024 23:17:59 -0300 Subject: [PATCH 3/3] chore(menu): changeset --- .changeset/healthy-parents-brake.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/healthy-parents-brake.md diff --git a/.changeset/healthy-parents-brake.md b/.changeset/healthy-parents-brake.md new file mode 100644 index 0000000000..f50af281c5 --- /dev/null +++ b/.changeset/healthy-parents-brake.md @@ -0,0 +1,7 @@ +--- +"@nextui-org/dropdown": patch +"@nextui-org/menu": patch +"@nextui-org/use-aria-menu": patch +--- + +Fix #2743 #2751 internal react-aria use-menu hooks implemented to pass down the press events and control the pressUp one