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:
committed by
Facebook GitHub Bot
parent
9b9f5d15a1
commit
ac24bbed3e
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user