Rewrite ContextMenu and ContextMenuProvider to Use Modern Context
Summary: per title Reviewed By: mweststrate Differential Revision: D20197959 fbshipit-source-id: 395eba0f1e2f4606a70067a63aa91a2f2a732afd
This commit is contained in:
committed by
Facebook Github Bot
parent
ec13009673
commit
31bafadaa3
@@ -7,9 +7,9 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {createElement, useContext, useCallback} from 'react';
|
||||
import {ContextMenuContext} from './ContextMenuProvider';
|
||||
import FlexColumn from './FlexColumn';
|
||||
import PropTypes from 'prop-types';
|
||||
import {MenuItemConstructorOptions} from 'electron';
|
||||
|
||||
export type MenuTemplate = Array<MenuItemConstructorOptions>;
|
||||
@@ -22,7 +22,7 @@ type Props<C> = {
|
||||
/** Nodes that should have a context menu */
|
||||
children: React.ReactNode;
|
||||
/** The component that is used to wrap the children. Defaults to `FlexColumn`. */
|
||||
component: React.ComponentType<any> | string;
|
||||
component?: React.ComponentType<any> | string;
|
||||
onMouseDown?: (e: React.MouseEvent) => any;
|
||||
} & C;
|
||||
|
||||
@@ -33,39 +33,27 @@ type Props<C> = {
|
||||
*
|
||||
* Separators can be added by `{type: 'separator'}`
|
||||
*/
|
||||
export default class ContextMenu<C = any> extends React.Component<Props<C>> {
|
||||
static defaultProps = {
|
||||
component: FlexColumn,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
appendToContextMenu: PropTypes.func,
|
||||
};
|
||||
|
||||
onContextMenu = () => {
|
||||
if (typeof this.context.appendToContextMenu === 'function') {
|
||||
if (this.props.items != null) {
|
||||
this.context.appendToContextMenu(this.props.items);
|
||||
} else if (this.props.buildItems != null) {
|
||||
this.context.appendToContextMenu(this.props.buildItems());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
items: _items,
|
||||
buildItems: _buildItems,
|
||||
component,
|
||||
...props
|
||||
} = this.props;
|
||||
return React.createElement(
|
||||
export default function ContextMenu<C>({
|
||||
items,
|
||||
buildItems,
|
||||
component,
|
||||
children,
|
||||
...otherProps
|
||||
}: Props<C>) {
|
||||
const contextMenuManager = useContext(ContextMenuContext);
|
||||
const onContextMenu = useCallback(() => {
|
||||
if (items != null) {
|
||||
contextMenuManager?.appendToContextMenu(items);
|
||||
} else if (buildItems != null) {
|
||||
contextMenuManager?.appendToContextMenu(buildItems());
|
||||
}
|
||||
}, []);
|
||||
return createElement(
|
||||
component || FlexColumn,
|
||||
{
|
||||
onContextMenu: this.onContextMenu,
|
||||
...props,
|
||||
onContextMenu,
|
||||
...otherProps,
|
||||
},
|
||||
this.props.children,
|
||||
children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,52 +7,50 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {Component} from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import electron, {MenuItemConstructorOptions} from 'electron';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, {useRef, memo, createContext, useMemo, useCallback} from 'react';
|
||||
|
||||
type MenuTemplate = Array<MenuItemConstructorOptions>;
|
||||
interface ContextMenuManager {
|
||||
appendToContextMenu(items: MenuTemplate): void;
|
||||
}
|
||||
|
||||
const Container = styled.div({
|
||||
display: 'contents',
|
||||
});
|
||||
Container.displayName = 'ContextMenuProvider:Container';
|
||||
|
||||
export const ContextMenuContext = createContext<ContextMenuManager | undefined>(
|
||||
undefined,
|
||||
);
|
||||
/**
|
||||
* Flipper's root is already wrapped with this component, so plugins should not
|
||||
* need to use this. ContextMenu is what you probably want to use.
|
||||
*/
|
||||
export default class ContextMenuProvider extends Component<{
|
||||
children: React.ReactNode;
|
||||
}> {
|
||||
static childContextTypes = {
|
||||
appendToContextMenu: PropTypes.func,
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {appendToContextMenu: this.appendToContextMenu};
|
||||
}
|
||||
|
||||
_menuTemplate: MenuTemplate = [];
|
||||
|
||||
appendToContextMenu = (items: MenuTemplate) => {
|
||||
this._menuTemplate = this._menuTemplate.concat(items);
|
||||
};
|
||||
|
||||
onContextMenu = () => {
|
||||
const menu = electron.remote.Menu.buildFromTemplate(this._menuTemplate);
|
||||
this._menuTemplate = [];
|
||||
// @ts-ignore: async is private electron API
|
||||
menu.popup({window: electron.remote.getCurrentWindow(), async: true});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container onContextMenu={this.onContextMenu}>
|
||||
{this.props.children}
|
||||
</Container>
|
||||
const ContextMenuProvider: React.FC<{}> = memo(function ContextMenuProvider({
|
||||
children,
|
||||
}) {
|
||||
const menuTemplate = useRef<MenuTemplate>([]);
|
||||
const contextMenuManager = useMemo(
|
||||
() => ({
|
||||
appendToContextMenu(items: MenuTemplate) {
|
||||
menuTemplate.current = menuTemplate.current.concat(items);
|
||||
},
|
||||
}),
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
const onContextMenu = useCallback(() => {
|
||||
const menu = electron.remote.Menu.buildFromTemplate(menuTemplate.current);
|
||||
menuTemplate.current = [];
|
||||
menu.popup({window: electron.remote.getCurrentWindow()});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ContextMenuContext.Provider value={contextMenuManager}>
|
||||
<Container onContextMenu={onContextMenu}>{children}</Container>
|
||||
</ContextMenuContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export default ContextMenuProvider;
|
||||
|
||||
Reference in New Issue
Block a user