convert buttons to React hooks

Summary: Converted Buttons to hooks, to make it easier to use context in the future. No further changes.

Reviewed By: cekkaewnumchai

Differential Revision: D23812921

fbshipit-source-id: 3739ad49e734dbe4d903a23d58da7cc267f6e109
This commit is contained in:
Michel Weststrate
2020-09-22 12:01:46 -07:00
committed by Facebook GitHub Bot
parent fdd2151532
commit b256bc68fa
3 changed files with 96 additions and 136 deletions

View File

@@ -7,16 +7,15 @@
* @format
*/
import React from 'react';
import PropTypes from 'prop-types';
import React, {useContext, useState, useRef, useCallback} from 'react';
import electron, {MenuItemConstructorOptions} from 'electron';
import styled from '@emotion/styled';
import {colors} from './colors';
import {connect} from 'react-redux';
import {findDOMNode} from 'react-dom';
import {keyframes} from 'emotion';
import {State as Store} from '../../reducers/index';
import Glyph, {IconSize} from './Glyph';
import {ButtonGroupContext} from './ButtonGroup';
import {useStore} from '../../utils/useStore';
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
@@ -190,7 +189,7 @@ const Icon = styled(Glyph)<{hasText: boolean}>(({hasText}) => ({
}));
Icon.displayName = 'Button:Icon';
type OwnProps = {
type Props = {
/**
* onMouseUp handler.
*/
@@ -257,46 +256,39 @@ type OwnProps = {
padded?: boolean;
} & React.HTMLProps<HTMLDivElement>;
type State = {
active: boolean;
wasClosed: boolean;
};
type StateFromProps = {windowIsFocused: boolean};
type Props = OwnProps & StateFromProps;
/**
* A simple button, used in many parts of the application.
*/
class Button extends React.Component<Props, State> {
static contextTypes = {
inButtonGroup: PropTypes.bool,
};
export default function Button(props: Props) {
const windowIsFocused = useStore(
(state) => state.application.windowIsFocused,
);
const inButtonGroup = useContext(ButtonGroupContext);
const [active, setActive] = useState(false);
const [wasClosed, setWasClosed] = useState(false);
state = {
active: false,
wasClosed: false,
};
const _ref = useRef<React.Component<typeof StyledButton>>();
_ref = React.createRef<React.Component<typeof StyledButton>>();
const onMouseDown = useCallback(
(e: React.MouseEvent) => {
setActive(true);
setWasClosed(false);
props.onMouseDown?.(e);
},
[props.onMouseDown],
);
onMouseDown = (e: React.MouseEvent) => {
this.setState({active: true, wasClosed: false});
if (this.props.onMouseDown != null) {
this.props.onMouseDown(e);
}
};
onMouseUp = () => {
if (this.props.disabled === true) {
const onMouseUp = useCallback(() => {
if (props.disabled === true) {
return;
}
if (this.props.dropdown && !this.state.wasClosed) {
const menu = electron.remote.Menu.buildFromTemplate(this.props.dropdown);
if (props.dropdown && !wasClosed) {
const menu = electron.remote.Menu.buildFromTemplate(props.dropdown);
const position: {
x?: number;
y?: number;
} = {};
const {current} = this._ref;
const {current} = _ref;
if (current) {
const node = findDOMNode(current);
if (node instanceof Element) {
@@ -311,83 +303,68 @@ class Button extends React.Component<Props, State> {
async: true,
...position,
callback: () => {
this.setState({wasClosed: true});
setWasClosed(true);
},
});
}
this.setState({active: false, wasClosed: false});
};
setActive(false);
setWasClosed(false);
}, [props.disabled, props.dropdown, wasClosed]);
onClick = (e: React.MouseEvent) => {
if (this.props.disabled === true) {
return;
}
if (this.props.onClick) {
this.props.onClick(e);
}
if (this.props.href != null) {
electron.shell.openExternal(this.props.href);
}
};
const onClick = useCallback(
(e: React.MouseEvent) => {
if (props.disabled === true) {
return;
}
props.onClick?.(e);
if (props.href != null) {
electron.shell.openExternal(props.href);
}
},
[props.disabled, props.onClick, props.href],
);
render() {
const {
icon,
children,
selected,
iconSize,
windowIsFocused,
iconVariant,
...props
} = this.props;
const {active} = this.state;
const {icon, children, selected, iconSize, iconVariant, ...restProps} = props;
let color = colors.macOSTitleBarIcon;
if (props.disabled === true) {
color = colors.macOSTitleBarIconBlur;
} else if (windowIsFocused && selected === true) {
color = colors.macOSTitleBarIconSelected;
} else if (!windowIsFocused && (selected == null || selected === false)) {
color = colors.macOSTitleBarIconBlur;
} else if (!windowIsFocused && selected === true) {
color = colors.macOSTitleBarIconSelectedBlur;
} else if (selected == null && active) {
color = colors.macOSTitleBarIconActive;
} else if (props.type === 'danger') {
color = colors.red;
}
let color = colors.macOSTitleBarIcon;
if (props.disabled === true) {
color = colors.macOSTitleBarIconBlur;
} else if (windowIsFocused && selected === true) {
color = colors.macOSTitleBarIconSelected;
} else if (!windowIsFocused && (selected == null || selected === false)) {
color = colors.macOSTitleBarIconBlur;
} else if (!windowIsFocused && selected === true) {
color = colors.macOSTitleBarIconSelectedBlur;
} else if (selected == null && active) {
color = colors.macOSTitleBarIconActive;
} else if (props.type === 'danger') {
color = colors.red;
}
let iconComponent;
if (icon != null) {
iconComponent = (
<Icon
name={icon}
size={iconSize || (this.props.compact === true ? 12 : 16)}
color={color}
variant={iconVariant || 'filled'}
hasText={Boolean(children)}
/>
);
}
return (
<StyledButton
{...props}
ref={this._ref as any}
windowIsFocused={windowIsFocused}
onClick={this.onClick}
onMouseDown={this.onMouseDown}
onMouseUp={this.onMouseUp}
inButtonGroup={this.context.inButtonGroup}>
{iconComponent}
{children}
</StyledButton>
let iconComponent;
if (icon != null) {
iconComponent = (
<Icon
name={icon}
size={iconSize || (props.compact === true ? 12 : 16)}
color={color}
variant={iconVariant || 'filled'}
hasText={Boolean(children)}
/>
);
}
}
export default connect<StateFromProps, {}, OwnProps, Store>(
({application: {windowIsFocused}}) => ({
windowIsFocused,
}),
)(Button);
return (
<StyledButton
{...restProps}
ref={_ref as any}
windowIsFocused={windowIsFocused}
onClick={onClick}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
inButtonGroup={inButtonGroup}>
{iconComponent}
{children}
</StyledButton>
);
}

View File

@@ -8,8 +8,7 @@
*/
import styled from '@emotion/styled';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import React, {createContext} from 'react';
const ButtonGroupContainer = styled.div({
display: 'inline-flex',
@@ -20,6 +19,8 @@ const ButtonGroupContainer = styled.div({
});
ButtonGroupContainer.displayName = 'ButtonGroup:ButtonGroupContainer';
export const ButtonGroupContext = createContext(false);
/**
* Group a series of buttons together.
*
@@ -31,18 +32,10 @@ ButtonGroupContainer.displayName = 'ButtonGroup:ButtonGroupContainer';
* </ButtonGroup>
* ```
*/
export default class ButtonGroup extends Component<{
children: React.ReactNode;
}> {
static childContextTypes = {
inButtonGroup: PropTypes.bool,
};
getChildContext() {
return {inButtonGroup: true};
}
render() {
return <ButtonGroupContainer>{this.props.children}</ButtonGroupContainer>;
}
export default function ButtonGroup({children}: {children: React.ReactNode}) {
return (
<ButtonGroupContext.Provider value={true}>
<ButtonGroupContainer>{children}</ButtonGroupContainer>
</ButtonGroupContext.Provider>
);
}

View File

@@ -7,10 +7,10 @@
* @format
*/
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import styled from '@emotion/styled';
import Glyph from './Glyph';
import {ButtonGroupContext} from './ButtonGroup';
const IconContainer = styled.div({
width: 0,
@@ -68,19 +68,9 @@ type Props = {
* </ButtonGroupChain>
* ```
*/
export default class ButtonGroupChain extends Component<Props> {
static childContextTypes = {
inButtonGroup: PropTypes.bool,
};
getChildContext() {
return {inButtonGroup: true};
}
render() {
const {children, iconSize, icon} = this.props;
return (
export default function ButtonGroupChain({children, iconSize, icon}: Props) {
return (
<ButtonGroupContext.Provider value={true}>
<ButtonGroupChainContainer iconSize={iconSize}>
{React.Children.map(children, (child, idx) => {
if (idx === 0) {
@@ -96,6 +86,6 @@ export default class ButtonGroupChain extends Component<Props> {
);
})}
</ButtonGroupChainContainer>
);
}
</ButtonGroupContext.Provider>
);
}