Don't use Electron menus for dropdowns

Summary: A bunch of dropdowns in Flipper are using native context menu to show their items. Rewrote to use Antd instead and reduce coupling with Electron

Reviewed By: timur-valiev

Differential Revision: D29694035

fbshipit-source-id: 5fd80c255c308567daf3e46e03bc27494c8ba142
This commit is contained in:
Michel Weststrate
2021-07-15 01:51:58 -07:00
committed by Facebook GitHub Bot
parent 9b9f5d15a1
commit ac24bbed3e
2 changed files with 31 additions and 81 deletions

View File

@@ -8,15 +8,17 @@
*/ */
import {useMemo} from 'react'; import {useMemo} from 'react';
import {Button, Layout} from '../ui';
import React from 'react'; import React from 'react';
import {Console, Hook} from 'console-feed'; import {Console, Hook} from 'console-feed';
import type {Methods} from 'console-feed/lib/definitions/Methods'; import type {Methods} from 'console-feed/lib/definitions/Methods';
import type {Styles} from 'console-feed/lib/definitions/Styles'; import type {Styles} from 'console-feed/lib/definitions/Styles';
import {createState, useValue} from 'flipper-plugin'; import {createState, useValue} from 'flipper-plugin';
import {useLocalStorageState} from 'flipper-plugin'; import {useLocalStorageState} from 'flipper-plugin';
import {theme, Toolbar} from 'flipper-plugin'; import {theme, Toolbar, Layout} from 'flipper-plugin';
import {useIsDarkMode} from '../utils/useIsDarkMode'; import {useIsDarkMode} from '../utils/useIsDarkMode';
import {Button, Dropdown, Menu, Checkbox} from 'antd';
import {DownOutlined} from '@ant-design/icons';
import {DeleteOutlined} from '@ant-design/icons';
const MAX_LOG_ITEMS = 1000; const MAX_LOG_ITEMS = 1000;
@@ -71,32 +73,37 @@ export function ConsoleLogs() {
defaultLogLevels, defaultLogLevels,
); );
const dropdown = useMemo(() => {
return allLogLevels.map(
(l): Electron.MenuItemConstructorOptions => ({
label: l,
checked: logLevels.includes(l),
type: 'checkbox',
click() {
setLogLevels((state) =>
state.includes(l)
? state.filter((level) => level !== l)
: [l, ...state],
);
},
}),
);
}, [logLevels, setLogLevels]);
const styles = useMemo(buildTheme, []); const styles = useMemo(buildTheme, []);
return ( return (
<Layout.Top> <Layout.Top>
<Toolbar wash> <Toolbar wash>
<Button onClick={clearLogs} icon="trash"> <Button onClick={clearLogs} icon={<DeleteOutlined />}>
Clear Logs Clear Logs
</Button> </Button>
<Button dropdown={dropdown}>Log Levels</Button> <Dropdown
overlay={
<Menu>
{allLogLevels.map((l) => (
<Menu.Item
key={l}
onClick={() => {
setLogLevels((state) =>
state.includes(l)
? state.filter((level) => level !== l)
: [l, ...state],
);
}}>
<Checkbox checked={logLevels.includes(l)}>{l}</Checkbox>
</Menu.Item>
))}
</Menu>
}>
<Button>
Log Levels
<DownOutlined />
</Button>
</Dropdown>
</Toolbar> </Toolbar>
<Layout.ScrollContainer vertical> <Layout.ScrollContainer vertical>
<Console <Console

View File

@@ -7,14 +7,12 @@
* @format * @format
*/ */
import React, {useState, useCallback, useMemo} from 'react'; import React, {useCallback} from 'react';
import {MenuItemConstructorOptions} from 'electron';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import {Button as AntdButton, Dropdown, Menu} from 'antd'; import {Button as AntdButton} from 'antd';
import Glyph, {IconSize} from './Glyph'; import Glyph, {IconSize} from './Glyph';
import type {ButtonProps} from 'antd/lib/button'; import type {ButtonProps} from 'antd/lib/button';
import {DownOutlined, CheckOutlined} from '@ant-design/icons';
import {theme, getFlipperLib} from 'flipper-plugin'; import {theme, getFlipperLib} from 'flipper-plugin';
type ButtonType = 'primary' | 'success' | 'warning' | 'danger'; type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
@@ -53,10 +51,6 @@ type Props = {
* Children. * Children.
*/ */
children?: React.ReactNode; children?: React.ReactNode;
/**
* Dropdown menu template shown on click.
*/
dropdown?: Array<MenuItemConstructorOptions>;
/** /**
* Name of the icon dispalyed next to the text * Name of the icon dispalyed next to the text
*/ */
@@ -109,14 +103,11 @@ function SandyButton({
children, children,
iconSize, iconSize,
iconVariant, iconVariant,
dropdown,
type, type,
onClick, onClick,
href, href,
...restProps ...restProps
}: Props) { }: Props) {
const [dropdownVisible, setDropdownVible] = useState(false);
const handleClick = useCallback( const handleClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
if (disabled === true) { if (disabled === true) {
@@ -129,9 +120,6 @@ function SandyButton({
}, },
[disabled, onClick, href], [disabled, onClick, href],
); );
const handleVisibleChange = useCallback((flag: boolean) => {
setDropdownVible(flag);
}, []);
let iconComponent; let iconComponent;
if (icon != null) { if (icon != null) {
iconComponent = ( iconComponent = (
@@ -145,39 +133,7 @@ function SandyButton({
); );
} }
const dropdownItems = useMemo( return (
() =>
dropdown && (
<Menu>
{dropdown!.map((item, idx) => (
<Menu.Item
onClick={(e) => {
// @ts-ignore this event args are bound to electron, remove in the future
item.click(item);
if (item.checked !== undefined) {
// keep the menu item for check lists
e.domEvent.stopPropagation();
e.domEvent.preventDefault();
}
}}
disabled={item.enabled === false}
icon={
item.checked !== undefined && (
<CheckOutlined
style={{visibility: item.checked ? 'visible' : 'hidden'}}
/>
)
}
key={idx}>
{item.label}
</Menu.Item>
))}
</Menu>
),
[dropdown],
);
const button = (
<AntdButton <AntdButton
/* Probably more properties need passing on, but lets be explicit about it */ /* Probably more properties need passing on, but lets be explicit about it */
style={restProps.style} style={restProps.style}
@@ -187,19 +143,6 @@ function SandyButton({
onClick={handleClick} onClick={handleClick}
icon={iconComponent}> icon={iconComponent}>
{children} {children}
{dropdown ? <DownOutlined /> : null}
</AntdButton> </AntdButton>
); );
if (dropdown) {
return (
<Dropdown
overlay={dropdownItems!}
visible={dropdownVisible}
onVisibleChange={handleVisibleChange}>
{button}
</Dropdown>
);
} else {
return button;
}
} }