Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Right click context menu #2085

Open
AdrianFahrbach opened this issue Feb 26, 2024 · 4 comments
Open

Right click context menu #2085

AdrianFahrbach opened this issue Feb 26, 2024 · 4 comments
Labels
enhancement New feature or request Needs: Triage 🔍

Comments

@AdrianFahrbach
Copy link

Summary

The deprecated react-native-macos package from ptmt has the option to add a custom context menu to a view:
ptmt/react-native-macos#237

It basically works like this:

const contextMenu = [
  { key: 'foo', title: 'Foo' },
  { isSeparator: true },
  { key: 'bar', title: 'Bar' },
]

<View
  contextMenu={contextMenu}
  onContextMenuItemClick={event => {
    console.log(event.nativeEvent)
  }} />

Maybe porting that feature to this package is a viable option.

Motivation

The context menu would give an easy way to hide more complex functions.

In my use case I got a list of workload entries from Jira. All of those entries can be edited and delete through a separate edit screen but that requires a bit of navigation.
"Pro" users could access shortcuts to specific functions (like "delete entry") through that right click menu. Other useful actions that aren't important enough for a separate button could also be hidden there. In my example that could be something like "Copy issue id" or "Open issue in browser".

Basic Example

No response

Open Questions

Porting the code from the other packages seems like a nice shortcut to a useful feature for me. I have no idea if this is possible though, so this should probably be checked first.
If porting the code is not an option, then other features may be more important for now.

@Saadnajmi
Copy link
Collaborator

I've been meaning to add support for macOS to https://github.com/react-native-menu/menu but haven't gotten around to it.
Meanwhile, I ended up writing a fully custom replacement for NSMenu with FluentUI React Native Menu. That one had to be fully custom (A custom NSWindow holding RN Views instead of just rendering a native NSMenu) because we wanted more customizable menu items (I.E, you can put any view in there instead of just text). That might be worth checking out. The spec page I linked doesn't have macOS screenshots but I encourage you to try the test app out

@Saadnajmi
Copy link
Collaborator

I think when I've thought about this before, I've resisted the approach of just adding props to <View> because menus can get secretly complex when you add in images, checkboxes, multi-select, submenus, etc. Ultimately I think a "wrapper "component (like @react-native-menu/menu) or a component that lets you write up the menu in JSX (like FluentUI React Native's Menu) is the better extensible approach.

@AdrianFahrbach
Copy link
Author

I tried the FluentUI React Native Menu and can't get it to work. I also tested this on windows and force opening the menu with <Menu open> but it just doesn't show up. Maybe the theme provider is mandatory?

screenshot00.11.40.mp4

Ultimately I think a "wrapper "component (like @react-native-menu/menu) or a component that lets you write up the menu in JSX (like FluentUI React Native's Menu) is the better extensible approach.

My initial thought was a separate library as well, so I was actually looking for something like your FluentUI menu.
What I really like about the View prop approach though is its simplicity. I didn't test it, but it sounds like that way I also don't have to worry about making it feel 100% native and/or placing the popup in the right location. I also don't need to handle the open/close state or nest multiple components, I can just throw an object into that prop.
I'm going for a very native look & feel though, so in my case I don't want to place any custom views inside my menu.

@Saadnajmi
Copy link
Collaborator

@AdrianFahrbach there's another option you could use for a native NSmenu: ActionSheetIOS. We actually ported that to fire an NSMenu. I found an old internal component that gives you an idea of how it could work:

'use strict';
import * as React from 'react';
import { findNodeHandle, ActionSheetIOSOptions, ActionSheetIOS } from 'react-native';
import { IContextualMenuStatic } from '../../common/ContextualMenu/ContextualMenu.types';
import { IContextualMenuProps } from '../../common/ContextualMenu/ContextualMenu.Props';

declare module 'react-native' {
  // The TypeScript type definition at
  // https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-native
  // is missing anchor on ActionSheetIOSOptions.
  // Use declaration merging to add it here.
  interface ActionSheetIOSOptions {
    anchor?: number;
  }
}

class ContextualMenuImpl extends React.Component<IContextualMenuProps, {}> {
  public static showContextualMenu(menu: IContextualMenuProps, target: React.ReactNode) {
    const buttonTitles: Array<string> = [];

    for (const button of menu.items) {
      buttonTitles.push(button.name);
    }

    /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
    const reactTag = findNodeHandle(target as any);
    const options: ActionSheetIOSOptions = {
      options: buttonTitles,
      cancelButtonIndex: menu.cancelButtonIndex,
      destructiveButtonIndex: menu.destructiveButtonIndex,
      anchor: reactTag
    };

    ActionSheetIOS.showActionSheetWithOptions(options, buttonIndex => {
      menu.items[buttonIndex].onClick({ key: menu.items[buttonIndex].key });
    });
  }
}

export const ContextualMenu: IContextualMenuStatic = ContextualMenuImpl;

Note that while findNodeHandle works in Fabric, it's not recommended, and we're still looking for a good alternative.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Needs: Triage 🔍
Projects
None yet
Development

No branches or pull requests

2 participants