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:
committed by
Facebook GitHub Bot
parent
fdd2151532
commit
b256bc68fa
@@ -7,16 +7,15 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {useContext, useState, useRef, useCallback} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import electron, {MenuItemConstructorOptions} from 'electron';
|
import electron, {MenuItemConstructorOptions} from 'electron';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {colors} from './colors';
|
import {colors} from './colors';
|
||||||
import {connect} from 'react-redux';
|
|
||||||
import {findDOMNode} from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import {keyframes} from 'emotion';
|
import {keyframes} from 'emotion';
|
||||||
import {State as Store} from '../../reducers/index';
|
|
||||||
import Glyph, {IconSize} from './Glyph';
|
import Glyph, {IconSize} from './Glyph';
|
||||||
|
import {ButtonGroupContext} from './ButtonGroup';
|
||||||
|
import {useStore} from '../../utils/useStore';
|
||||||
|
|
||||||
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
|
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
|
||||||
|
|
||||||
@@ -190,7 +189,7 @@ const Icon = styled(Glyph)<{hasText: boolean}>(({hasText}) => ({
|
|||||||
}));
|
}));
|
||||||
Icon.displayName = 'Button:Icon';
|
Icon.displayName = 'Button:Icon';
|
||||||
|
|
||||||
type OwnProps = {
|
type Props = {
|
||||||
/**
|
/**
|
||||||
* onMouseUp handler.
|
* onMouseUp handler.
|
||||||
*/
|
*/
|
||||||
@@ -257,46 +256,39 @@ type OwnProps = {
|
|||||||
padded?: boolean;
|
padded?: boolean;
|
||||||
} & React.HTMLProps<HTMLDivElement>;
|
} & 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.
|
* A simple button, used in many parts of the application.
|
||||||
*/
|
*/
|
||||||
class Button extends React.Component<Props, State> {
|
export default function Button(props: Props) {
|
||||||
static contextTypes = {
|
const windowIsFocused = useStore(
|
||||||
inButtonGroup: PropTypes.bool,
|
(state) => state.application.windowIsFocused,
|
||||||
};
|
);
|
||||||
|
const inButtonGroup = useContext(ButtonGroupContext);
|
||||||
|
const [active, setActive] = useState(false);
|
||||||
|
const [wasClosed, setWasClosed] = useState(false);
|
||||||
|
|
||||||
state = {
|
const _ref = useRef<React.Component<typeof StyledButton>>();
|
||||||
active: false,
|
|
||||||
wasClosed: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
_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) => {
|
const onMouseUp = useCallback(() => {
|
||||||
this.setState({active: true, wasClosed: false});
|
if (props.disabled === true) {
|
||||||
if (this.props.onMouseDown != null) {
|
|
||||||
this.props.onMouseDown(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
onMouseUp = () => {
|
|
||||||
if (this.props.disabled === true) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.props.dropdown && !this.state.wasClosed) {
|
if (props.dropdown && !wasClosed) {
|
||||||
const menu = electron.remote.Menu.buildFromTemplate(this.props.dropdown);
|
const menu = electron.remote.Menu.buildFromTemplate(props.dropdown);
|
||||||
const position: {
|
const position: {
|
||||||
x?: number;
|
x?: number;
|
||||||
y?: number;
|
y?: number;
|
||||||
} = {};
|
} = {};
|
||||||
const {current} = this._ref;
|
const {current} = _ref;
|
||||||
if (current) {
|
if (current) {
|
||||||
const node = findDOMNode(current);
|
const node = findDOMNode(current);
|
||||||
if (node instanceof Element) {
|
if (node instanceof Element) {
|
||||||
@@ -311,36 +303,28 @@ class Button extends React.Component<Props, State> {
|
|||||||
async: true,
|
async: true,
|
||||||
...position,
|
...position,
|
||||||
callback: () => {
|
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) => {
|
const onClick = useCallback(
|
||||||
if (this.props.disabled === true) {
|
(e: React.MouseEvent) => {
|
||||||
|
if (props.disabled === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.props.onClick) {
|
props.onClick?.(e);
|
||||||
this.props.onClick(e);
|
if (props.href != null) {
|
||||||
|
electron.shell.openExternal(props.href);
|
||||||
}
|
}
|
||||||
if (this.props.href != null) {
|
},
|
||||||
electron.shell.openExternal(this.props.href);
|
[props.disabled, props.onClick, props.href],
|
||||||
}
|
);
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
const {icon, children, selected, iconSize, iconVariant, ...restProps} = props;
|
||||||
const {
|
|
||||||
icon,
|
|
||||||
children,
|
|
||||||
selected,
|
|
||||||
iconSize,
|
|
||||||
windowIsFocused,
|
|
||||||
iconVariant,
|
|
||||||
...props
|
|
||||||
} = this.props;
|
|
||||||
const {active} = this.state;
|
|
||||||
|
|
||||||
let color = colors.macOSTitleBarIcon;
|
let color = colors.macOSTitleBarIcon;
|
||||||
if (props.disabled === true) {
|
if (props.disabled === true) {
|
||||||
@@ -362,7 +346,7 @@ class Button extends React.Component<Props, State> {
|
|||||||
iconComponent = (
|
iconComponent = (
|
||||||
<Icon
|
<Icon
|
||||||
name={icon}
|
name={icon}
|
||||||
size={iconSize || (this.props.compact === true ? 12 : 16)}
|
size={iconSize || (props.compact === true ? 12 : 16)}
|
||||||
color={color}
|
color={color}
|
||||||
variant={iconVariant || 'filled'}
|
variant={iconVariant || 'filled'}
|
||||||
hasText={Boolean(children)}
|
hasText={Boolean(children)}
|
||||||
@@ -372,22 +356,15 @@ class Button extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
{...props}
|
{...restProps}
|
||||||
ref={this._ref as any}
|
ref={_ref as any}
|
||||||
windowIsFocused={windowIsFocused}
|
windowIsFocused={windowIsFocused}
|
||||||
onClick={this.onClick}
|
onClick={onClick}
|
||||||
onMouseDown={this.onMouseDown}
|
onMouseDown={onMouseDown}
|
||||||
onMouseUp={this.onMouseUp}
|
onMouseUp={onMouseUp}
|
||||||
inButtonGroup={this.context.inButtonGroup}>
|
inButtonGroup={inButtonGroup}>
|
||||||
{iconComponent}
|
{iconComponent}
|
||||||
{children}
|
{children}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateFromProps, {}, OwnProps, Store>(
|
|
||||||
({application: {windowIsFocused}}) => ({
|
|
||||||
windowIsFocused,
|
|
||||||
}),
|
|
||||||
)(Button);
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import React, {Component} from 'react';
|
import React, {createContext} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const ButtonGroupContainer = styled.div({
|
const ButtonGroupContainer = styled.div({
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
@@ -20,6 +19,8 @@ const ButtonGroupContainer = styled.div({
|
|||||||
});
|
});
|
||||||
ButtonGroupContainer.displayName = 'ButtonGroup:ButtonGroupContainer';
|
ButtonGroupContainer.displayName = 'ButtonGroup:ButtonGroupContainer';
|
||||||
|
|
||||||
|
export const ButtonGroupContext = createContext(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group a series of buttons together.
|
* Group a series of buttons together.
|
||||||
*
|
*
|
||||||
@@ -31,18 +32,10 @@ ButtonGroupContainer.displayName = 'ButtonGroup:ButtonGroupContainer';
|
|||||||
* </ButtonGroup>
|
* </ButtonGroup>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export default class ButtonGroup extends Component<{
|
export default function ButtonGroup({children}: {children: React.ReactNode}) {
|
||||||
children: React.ReactNode;
|
return (
|
||||||
}> {
|
<ButtonGroupContext.Provider value={true}>
|
||||||
static childContextTypes = {
|
<ButtonGroupContainer>{children}</ButtonGroupContainer>
|
||||||
inButtonGroup: PropTypes.bool,
|
</ButtonGroupContext.Provider>
|
||||||
};
|
);
|
||||||
|
|
||||||
getChildContext() {
|
|
||||||
return {inButtonGroup: true};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <ButtonGroupContainer>{this.props.children}</ButtonGroupContainer>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import Glyph from './Glyph';
|
import Glyph from './Glyph';
|
||||||
|
import {ButtonGroupContext} from './ButtonGroup';
|
||||||
|
|
||||||
const IconContainer = styled.div({
|
const IconContainer = styled.div({
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -68,19 +68,9 @@ type Props = {
|
|||||||
* </ButtonGroupChain>
|
* </ButtonGroupChain>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export default class ButtonGroupChain extends Component<Props> {
|
export default function ButtonGroupChain({children, iconSize, icon}: Props) {
|
||||||
static childContextTypes = {
|
|
||||||
inButtonGroup: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
getChildContext() {
|
|
||||||
return {inButtonGroup: true};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {children, iconSize, icon} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<ButtonGroupContext.Provider value={true}>
|
||||||
<ButtonGroupChainContainer iconSize={iconSize}>
|
<ButtonGroupChainContainer iconSize={iconSize}>
|
||||||
{React.Children.map(children, (child, idx) => {
|
{React.Children.map(children, (child, idx) => {
|
||||||
if (idx === 0) {
|
if (idx === 0) {
|
||||||
@@ -96,6 +86,6 @@ export default class ButtonGroupChain extends Component<Props> {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ButtonGroupChainContainer>
|
</ButtonGroupChainContainer>
|
||||||
|
</ButtonGroupContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user