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
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import {createElement, useContext, useCallback} from 'react';
|
||||||
|
import {ContextMenuContext} from './ContextMenuProvider';
|
||||||
import FlexColumn from './FlexColumn';
|
import FlexColumn from './FlexColumn';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {MenuItemConstructorOptions} from 'electron';
|
import {MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
export type MenuTemplate = Array<MenuItemConstructorOptions>;
|
export type MenuTemplate = Array<MenuItemConstructorOptions>;
|
||||||
@@ -22,7 +22,7 @@ type Props<C> = {
|
|||||||
/** Nodes that should have a context menu */
|
/** Nodes that should have a context menu */
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
/** The component that is used to wrap the children. Defaults to `FlexColumn`. */
|
/** 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;
|
onMouseDown?: (e: React.MouseEvent) => any;
|
||||||
} & C;
|
} & C;
|
||||||
|
|
||||||
@@ -33,39 +33,27 @@ type Props<C> = {
|
|||||||
*
|
*
|
||||||
* Separators can be added by `{type: 'separator'}`
|
* Separators can be added by `{type: 'separator'}`
|
||||||
*/
|
*/
|
||||||
export default class ContextMenu<C = any> extends React.Component<Props<C>> {
|
export default function ContextMenu<C>({
|
||||||
static defaultProps = {
|
items,
|
||||||
component: FlexColumn,
|
buildItems,
|
||||||
};
|
|
||||||
|
|
||||||
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(
|
|
||||||
component,
|
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,
|
onContextMenu,
|
||||||
...props,
|
...otherProps,
|
||||||
},
|
},
|
||||||
this.props.children,
|
children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,52 +7,50 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component} from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import electron, {MenuItemConstructorOptions} from 'electron';
|
import electron, {MenuItemConstructorOptions} from 'electron';
|
||||||
import React from 'react';
|
import React, {useRef, memo, createContext, useMemo, useCallback} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
type MenuTemplate = Array<MenuItemConstructorOptions>;
|
type MenuTemplate = Array<MenuItemConstructorOptions>;
|
||||||
|
interface ContextMenuManager {
|
||||||
|
appendToContextMenu(items: MenuTemplate): void;
|
||||||
|
}
|
||||||
|
|
||||||
const Container = styled.div({
|
const Container = styled.div({
|
||||||
display: 'contents',
|
display: 'contents',
|
||||||
});
|
});
|
||||||
Container.displayName = 'ContextMenuProvider:Container';
|
Container.displayName = 'ContextMenuProvider:Container';
|
||||||
|
|
||||||
|
export const ContextMenuContext = createContext<ContextMenuManager | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* Flipper's root is already wrapped with this component, so plugins should not
|
* 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.
|
* need to use this. ContextMenu is what you probably want to use.
|
||||||
*/
|
*/
|
||||||
export default class ContextMenuProvider extends Component<{
|
const ContextMenuProvider: React.FC<{}> = memo(function ContextMenuProvider({
|
||||||
children: React.ReactNode;
|
children,
|
||||||
}> {
|
}) {
|
||||||
static childContextTypes = {
|
const menuTemplate = useRef<MenuTemplate>([]);
|
||||||
appendToContextMenu: PropTypes.func,
|
const contextMenuManager = useMemo(
|
||||||
};
|
() => ({
|
||||||
|
appendToContextMenu(items: MenuTemplate) {
|
||||||
getChildContext() {
|
menuTemplate.current = menuTemplate.current.concat(items);
|
||||||
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 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