diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2019-03-05 13:16:09 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2019-03-07 13:48:30 +0100 |
| commit | 35a531578061085c4724eb54f03c4f7204ebfe5e (patch) | |
| tree | 0626e0ec6dbd952b7880106f10ca40e21c910872 /gui/src | |
| parent | afca6149d3d385ec1b95dcd77d9e75a08d874c31 (diff) | |
| download | mullvadvpn-35a531578061085c4724eb54f03c4f7204ebfe5e.tar.xz mullvadvpn-35a531578061085c4724eb54f03c4f7204ebfe5e.zip | |
Automatically center the text label within the AppButton
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/AppButton.tsx | 137 | ||||
| -rw-r--r-- | gui/src/renderer/components/AppButtonStyles.tsx | 23 |
2 files changed, 123 insertions, 37 deletions
diff --git a/gui/src/renderer/components/AppButton.tsx b/gui/src/renderer/components/AppButton.tsx index 17aafb7fc1..34a7f46e04 100644 --- a/gui/src/renderer/components/AppButton.tsx +++ b/gui/src/renderer/components/AppButton.tsx @@ -1,16 +1,53 @@ import * as React from 'react'; -import { Button, Component, Text, Types } from 'reactxp'; +import { Button, Component, Styles, Text, Types, UserInterface, View } from 'reactxp'; import { colors } from '../../config.json'; import styles from './AppButtonStyles'; import ImageView from './ImageView'; +const ButtonContext = React.createContext({ + textAdjustment: 0, + textRef: React.createRef<PrivateLabel>(), +}); + interface ILabelProps { children?: React.ReactText; } +interface IPrivateLabelProps { + textAdjustment: number; + children?: React.ReactText; +} + +class PrivateLabel extends Component<IPrivateLabelProps> { + public render() { + const { textAdjustment, children } = this.props; + const textAdjustmentStyle = Styles.createViewStyle( + { + paddingRight: textAdjustment > 0 ? textAdjustment : 0, + paddingLeft: textAdjustment < 0 ? Math.abs(textAdjustment) : 0, + }, + false, + ); + + return ( + <View style={[styles.labelContainer, textAdjustmentStyle]}> + <Text style={styles.label}>{children}</Text> + </View> + ); + } +} + export class Label extends Component<ILabelProps> { public render() { - return <Text style={styles.label}>{this.props.children}</Text>; + return ( + <ButtonContext.Consumer> + {(context) => ( + <PrivateLabel ref={context.textRef} textAdjustment={context.textAdjustment}> + {this.props.children} + </PrivateLabel> + )} + </ButtonContext.Consumer> + ); } } @@ -28,7 +65,6 @@ export class Icon extends Component<IIconProps> { width={this.props.width} height={this.props.height} tintColor={colors.white} - style={styles.icon} /> ); } @@ -43,52 +79,107 @@ interface IProps { interface IState { hovered: boolean; + textAdjustment: number; } class BaseButton extends Component<IProps, IState> { - public state: IState = { hovered: false }; - - public backgroundStyle = (): Types.ButtonStyleRuleSet => { - throw new Error('Implement backgroundStyle in subclasses.'); + public state: IState = { + hovered: false, + textAdjustment: 0, }; - public onHoverStart = () => (!this.props.disabled ? this.setState({ hovered: true }) : null); - public onHoverEnd = () => (!this.props.disabled ? this.setState({ hovered: false }) : null); + + private containerRef = React.createRef<View>(); + private textViewRef = React.createRef<PrivateLabel>(); + + public componentDidMount() { + this.forceUpdateTextAdjustment(); + } public render() { const { children, style, ...otherProps } = this.props; return ( - <Button - {...otherProps} - style={[styles.common, this.backgroundStyle(), style]} - onHoverStart={this.onHoverStart} - onHoverEnd={this.onHoverEnd}> - {React.Children.map(children, (child) => - typeof child === 'string' ? <Label>{child as string}</Label> : child, - )} - </Button> + <ButtonContext.Provider + value={{ + textAdjustment: this.state.textAdjustment, + textRef: this.textViewRef, + }}> + <Button + {...otherProps} + style={[styles.common, this.backgroundStyle(), style]} + onHoverStart={this.onHoverStart} + onHoverEnd={this.onHoverEnd}> + <View style={styles.content} ref={this.containerRef} onLayout={this.onLayout}> + {React.Children.map(children, (child) => + typeof child === 'string' ? <Label>{child as string}</Label> : child, + )} + </View> + </Button> + </ButtonContext.Provider> ); } + + protected backgroundStyle = (): Types.ButtonStyleRuleSet => { + throw new Error('Implement backgroundStyle in subclasses.'); + }; + protected onHoverStart = () => (!this.props.disabled ? this.setState({ hovered: true }) : null); + protected onHoverEnd = () => (!this.props.disabled ? this.setState({ hovered: false }) : null); + + private async forceUpdateTextAdjustment() { + const containerView = this.containerRef.current; + if (containerView) { + const containerLayout = await UserInterface.measureLayoutRelativeToAncestor( + containerView, + this, + ); + + this.updateTextAdjustment(containerLayout); + } + } + + private async updateTextAdjustment(containerLayout: Types.LayoutInfo) { + const labelView = this.textViewRef.current; + + if (labelView) { + // calculate the title layout frame + const labelLayout = await UserInterface.measureLayoutRelativeToAncestor(labelView, this); + + // calculate the remaining space at the right hand side + const trailingSpace = containerLayout.width - (labelLayout.x + labelLayout.width); + + // calculate text adjustment + const textAdjustment = labelLayout.x - trailingSpace; + + // re-render the view with the new text adjustment if it changed + if (this.state.textAdjustment !== textAdjustment) { + this.setState({ textAdjustment }); + } + } + } + + private onLayout = async (containerLayout: Types.ViewOnLayoutEvent) => { + this.updateTextAdjustment(containerLayout); + }; } export class RedButton extends BaseButton { - public backgroundStyle = () => (this.state.hovered ? styles.redHover : styles.red); + protected backgroundStyle = () => (this.state.hovered ? styles.redHover : styles.red); } export class GreenButton extends BaseButton { - public backgroundStyle = () => (this.state.hovered ? styles.greenHover : styles.green); + protected backgroundStyle = () => (this.state.hovered ? styles.greenHover : styles.green); } export class BlueButton extends BaseButton { - public backgroundStyle = () => (this.state.hovered ? styles.blueHover : styles.blue); + protected backgroundStyle = () => (this.state.hovered ? styles.blueHover : styles.blue); } export class TransparentButton extends BaseButton { - public backgroundStyle = () => + protected backgroundStyle = () => this.state.hovered ? styles.transparentHover : styles.transparent; } export class RedTransparentButton extends BaseButton { - public backgroundStyle = () => + protected backgroundStyle = () => this.state.hovered ? styles.redTransparentHover : styles.redTransparent; } diff --git a/gui/src/renderer/components/AppButtonStyles.tsx b/gui/src/renderer/components/AppButtonStyles.tsx index c5cc6dfc6b..055ce9b031 100644 --- a/gui/src/renderer/components/AppButtonStyles.tsx +++ b/gui/src/renderer/components/AppButtonStyles.tsx @@ -32,31 +32,26 @@ export default { redTransparentHover: Styles.createButtonStyle({ backgroundColor: colors.red45, }), - icon: Styles.createViewStyle({ - position: 'absolute', - alignSelf: 'flex-end', - right: 8, - marginLeft: 8, - }), common: Styles.createViewStyle({ cursor: 'default', - paddingTop: 9, - paddingLeft: 9, - paddingRight: 9, - paddingBottom: 9, borderRadius: 4, + }), + content: Styles.createViewStyle({ + flex: 1, + flexDirection: 'row', + alignItems: 'center', + padding: 9, + }), + labelContainer: Styles.createViewStyle({ flex: 1, - flexDirection: 'column', - alignContent: 'center', - justifyContent: 'center', }), label: Styles.createTextStyle({ - alignSelf: 'center', fontFamily: 'DINPro', fontSize: 20, fontWeight: '900', lineHeight: 26, flex: 1, color: colors.white, + textAlign: 'center', }), }; |
