diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-05-07 09:06:47 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-05-15 15:42:06 +0200 |
| commit | 11bfac6e9dc3284057299611933a4f6fc92473e1 (patch) | |
| tree | 16ebcd2c67f4d5af1a798b2632694ace856f3387 /gui/src | |
| parent | 98c62cd60e88926b73168042a9bb46a95bcff92e (diff) | |
| download | mullvadvpn-11bfac6e9dc3284057299611933a4f6fc92473e1.tar.xz mullvadvpn-11bfac6e9dc3284057299611933a4f6fc92473e1.zip | |
Convert Cell to Styled components
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettings.tsx | 70 | ||||
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettingsStyles.tsx | 24 | ||||
| -rw-r--r-- | gui/src/renderer/components/Cell.tsx | 385 | ||||
| -rw-r--r-- | gui/src/renderer/components/CellStyles.tsx | 266 | ||||
| -rw-r--r-- | gui/src/renderer/components/CityRow.tsx | 34 | ||||
| -rw-r--r-- | gui/src/renderer/components/CountryRow.tsx | 27 | ||||
| -rw-r--r-- | gui/src/renderer/components/LocationList.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/Login.tsx | 22 | ||||
| -rw-r--r-- | gui/src/renderer/components/LoginStyles.tsx | 57 | ||||
| -rw-r--r-- | gui/src/renderer/components/RelayRow.tsx | 26 | ||||
| -rw-r--r-- | gui/src/renderer/components/SelectLanguage.tsx | 11 | ||||
| -rw-r--r-- | gui/src/renderer/components/Selector.tsx | 25 | ||||
| -rw-r--r-- | gui/src/renderer/components/Settings.tsx | 22 | ||||
| -rw-r--r-- | gui/src/renderer/components/SettingsStyles.tsx | 12 | ||||
| -rw-r--r-- | gui/src/renderer/components/Switch.tsx | 6 |
15 files changed, 401 insertions, 588 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx index 2bcd0d44d8..ecc93a2544 100644 --- a/gui/src/renderer/components/AdvancedSettings.tsx +++ b/gui/src/renderer/components/AdvancedSettings.tsx @@ -4,7 +4,11 @@ import { sprintf } from 'sprintf-js'; import { BridgeState, RelayProtocol, TunnelProtocol } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; import { WgKeyState } from '../redux/settings/reducers'; -import styles from './AdvancedSettingsStyles'; +import styles, { + BlockWhenDisconnectedLabel, + InputFrame, + TunnelProtocolSelector, +} from './AdvancedSettingsStyles'; import * as AppButton from './AppButton'; import * as Cell from './Cell'; import { Container, Layout } from './Layout'; @@ -181,10 +185,9 @@ export default class AdvancedSettings extends Component<IProps, IState> { </Cell.Footer> <Cell.Container> - <Cell.Label - textStyle={styles.advanced_settings__block_when_disconnected_label}> + <BlockWhenDisconnectedLabel> {messages.pgettext('advanced-settings-view', 'Always require VPN')} - </Cell.Label> + </BlockWhenDisconnectedLabel> <Cell.Switch isOn={this.props.blockWhenDisconnected} onChange={this.setBlockWhenDisconnected} @@ -204,12 +207,11 @@ export default class AdvancedSettings extends Component<IProps, IState> { styles.advanced_settings__content, styles.advanced_settings__tunnel_protocol, ]}> - <Selector + <TunnelProtocolSelector title={messages.pgettext('advanced-settings-view', 'Tunnel protocol')} values={this.tunnelProtocolItems(hasWireguardKey)} value={this.props.tunnelProtocol} onSelect={this.onSelectTunnelProtocol} - style={styles.advanced_settings__tunnel_protocol_selector} /> {!hasWireguardKey && ( <Text style={styles.advanced_settings__wg_no_key}> @@ -281,20 +283,18 @@ export default class AdvancedSettings extends Component<IProps, IState> { <Cell.Label> {messages.pgettext('advanced-settings-view', 'OpenVPN Mssfix')} </Cell.Label> - <Cell.InputFrame style={styles.advanced_settings__input_frame}> - <Cell.AutoSizingTextInputContainer> - <Cell.Input - value={this.props.mssfix ? this.props.mssfix.toString() : ''} - keyboardType={'numeric'} - maxLength={4} - placeholder={messages.pgettext('advanced-settings-view', 'Default')} - onSubmit={this.onMssfixSubmit} - validateValue={AdvancedSettings.mssfixIsValid} - submitOnBlur={true} - modifyValue={AdvancedSettings.removeNonNumericCharacters} - /> - </Cell.AutoSizingTextInputContainer> - </Cell.InputFrame> + <InputFrame> + <Cell.AutoSizingTextInput + value={this.props.mssfix ? this.props.mssfix.toString() : ''} + inputMode={'numeric'} + maxLength={4} + placeholder={messages.pgettext('advanced-settings-view', 'Default')} + onSubmitValue={this.onMssfixSubmit} + validateValue={AdvancedSettings.mssfixIsValid} + submitOnBlur={true} + modifyValue={AdvancedSettings.removeNonNumericCharacters} + /> + </InputFrame> </Cell.Container> <Cell.Footer> <Cell.FooterText> @@ -319,22 +319,18 @@ export default class AdvancedSettings extends Component<IProps, IState> { <Cell.Label> {messages.pgettext('advanced-settings-view', 'WireGuard MTU')} </Cell.Label> - <Cell.InputFrame style={styles.advanced_settings__input_frame}> - <Cell.AutoSizingTextInputContainer> - <Cell.Input - value={ - this.props.wireguardMtu ? this.props.wireguardMtu.toString() : '' - } - keyboardType={'numeric'} - maxLength={4} - placeholder={messages.pgettext('advanced-settings-view', 'Default')} - onSubmit={this.onWireguardMtuSubmit} - validateValue={AdvancedSettings.wireguarMtuIsValid} - submitOnBlur={true} - modifyValue={AdvancedSettings.removeNonNumericCharacters} - /> - </Cell.AutoSizingTextInputContainer> - </Cell.InputFrame> + <InputFrame> + <Cell.AutoSizingTextInput + value={this.props.wireguardMtu ? this.props.wireguardMtu.toString() : ''} + inputMode={'numeric'} + maxLength={4} + placeholder={messages.pgettext('advanced-settings-view', 'Default')} + onSubmitValue={this.onWireguardMtuSubmit} + validateValue={AdvancedSettings.wireguarMtuIsValid} + submitOnBlur={true} + modifyValue={AdvancedSettings.removeNonNumericCharacters} + /> + </InputFrame> </Cell.Container> <Cell.Footer> <Cell.FooterText> @@ -356,7 +352,7 @@ export default class AdvancedSettings extends Component<IProps, IState> { </Cell.Footer> <View style={styles.advanced_settings__wgkeys_cell}> - <Cell.CellButton onPress={this.props.onViewWireguardKeys}> + <Cell.CellButton onClick={this.props.onViewWireguardKeys}> <Cell.Label> {messages.pgettext('advanced-settings-view', 'WireGuard key')} </Cell.Label> diff --git a/gui/src/renderer/components/AdvancedSettingsStyles.tsx b/gui/src/renderer/components/AdvancedSettingsStyles.tsx index 737a3e1acd..721df6cf55 100644 --- a/gui/src/renderer/components/AdvancedSettingsStyles.tsx +++ b/gui/src/renderer/components/AdvancedSettingsStyles.tsx @@ -1,5 +1,20 @@ import { Styles } from 'reactxp'; +import styled from 'styled-components'; import { colors } from '../../config.json'; +import * as Cell from './Cell'; +import Selector from './Selector'; + +export const InputFrame = styled(Cell.InputFrame)({ + flex: 0, +}); + +export const BlockWhenDisconnectedLabel = styled(Cell.Label)({ + letterSpacing: -0.5, +}); + +export const TunnelProtocolSelector = (styled(Selector)({ + marginBottom: 0, +}) as unknown) as new <T>() => Selector<T>; export default { advanced_settings: Styles.createViewStyle({ @@ -19,9 +34,6 @@ export default { advanced_settings__tunnel_protocol: Styles.createViewStyle({ marginBottom: 24, }), - advanced_settings__tunnel_protocol_selector: Styles.createViewStyle({ - marginBottom: 0, - }), advanced_settings__wgkeys_cell: Styles.createViewStyle({ marginBottom: 24, }), @@ -55,10 +67,4 @@ export default { advanced_settings__cell_footer_internet_warning_label: Styles.createTextStyle({ marginTop: 4, }), - advanced_settings__input_frame: Styles.createViewStyle({ - flex: 0, - }), - advanced_settings__block_when_disconnected_label: Styles.createTextStyle({ - letterSpacing: -0.5, - }), }; diff --git a/gui/src/renderer/components/Cell.tsx b/gui/src/renderer/components/Cell.tsx index 04fc0729b8..23f63114b7 100644 --- a/gui/src/renderer/components/Cell.tsx +++ b/gui/src/renderer/components/Cell.tsx @@ -1,164 +1,62 @@ -import * as React from 'react'; -import { Button, Component, Styles, Text, TextInput, Types, View } from 'reactxp'; -import { colors } from '../../config.json'; -import styles, { StyledIcon } from './CellStyles'; -import { IImageViewProps } from './ImageView'; -import { default as SwitchControl } from './Switch'; +import React, { useCallback, useContext, useState } from 'react'; +import { + StyledAutoSizingTextInputContainer, + StyledAutoSizingTextInputWrapper, + StyledAutoSizingTextInputFiller, + StyledCellButton, + StyledSection, + StyledInput, +} from './CellStyles'; -export { StyledIcon as UntintedIcon } from './CellStyles'; +export { + StyledContainer as Container, + StyledFooter as Footer, + StyledFooterBoldText as FooterBoldText, + StyledFooterText as FooterText, + StyledIcon as UntintedIcon, + StyledInputFrame as InputFrame, + StyledLabel as Label, + StyledSectionTitle as SectionTitle, + StyledSubText as SubText, + StyledTintedIcon as Icon, +} from './CellStyles'; -interface ICellButtonProps { - children?: React.ReactNode; - disabled?: boolean; - selected?: boolean; - style?: Types.StyleRuleSetRecursive<Types.ButtonStyleRuleSet>; - hoverStyle?: Types.StyleRuleSetRecursive<Types.ButtonStyleRuleSet>; - onPress?: () => void; -} - -interface IState { - hovered: boolean; -} +export { default as Switch } from './Switch'; const CellSectionContext = React.createContext<boolean>(false); -const CellHoverContext = React.createContext<boolean>(false); - -export class CellButton extends Component<ICellButtonProps, IState> { - public state: IState = { hovered: false }; - - public onHoverStart = () => (!this.props.disabled ? this.setState({ hovered: true }) : null); - public onHoverEnd = () => (!this.props.disabled ? this.setState({ hovered: false }) : null); - - public render() { - const { children, style, hoverStyle, ...otherProps } = this.props; - - const stateStyle = this.props.selected - ? styles.cellButton.selected - : this.state.hovered - ? hoverStyle || styles.cellButton.hover - : undefined; - return ( - <CellSectionContext.Consumer> - {(containedInSection) => ( - <Button - style={[ - styles.cellButton.base, - containedInSection ? styles.cellButton.section : undefined, - style, - stateStyle, - ]} - onHoverStart={this.onHoverStart} - onHoverEnd={this.onHoverEnd} - {...otherProps}> - <CellHoverContext.Provider value={this.state.hovered}> - {children} - </CellHoverContext.Provider> - </Button> - )} - </CellSectionContext.Consumer> - ); - } -} - -interface ISectionTitleProps { - children?: React.ReactText; +interface ICellButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { + selected?: boolean; } -export const SectionTitle = function CellSectionTitle(props: ISectionTitleProps) { - return <Text style={styles.sectionTitle}>{props.children}</Text>; -}; +export const CellButton = React.forwardRef(function Button( + props: ICellButtonProps, + ref: React.Ref<HTMLButtonElement>, +) { + const containedInSection = useContext(CellSectionContext); + return <StyledCellButton ref={ref} containedInSection={containedInSection} {...props} />; +}); interface ISectionProps { children?: React.ReactNode; - style?: Types.StyleRuleSetRecursive<Types.ViewStyleRuleSet>; -} - -export const Section = class CellSection extends Component<ISectionProps> { - public render() { - return ( - <View style={this.props.style}> - <CellSectionContext.Provider value={true}> - {this.props.children} - </CellSectionContext.Provider> - </View> - ); - } -}; - -interface IContainerProps { - children: React.ReactNode; -} - -export const Container = function CellContainer({ children }: IContainerProps) { - return <View style={styles.cellContainer}>{children}</View>; -}; - -interface ILabelProps { - containerStyle?: Types.ViewStyleRuleSet; - textStyle?: Types.TextStyleRuleSet; - cellHoverContainerStyle?: Types.ViewStyleRuleSet; - cellHoverTextStyle?: Types.TextStyleRuleSet; - onPress?: (event: Types.SyntheticEvent) => void; - children?: React.ReactNode; + className?: string; } -export const Label = function CellLabel(props: ILabelProps) { - const { - children, - containerStyle, - textStyle, - cellHoverContainerStyle, - cellHoverTextStyle, - ...otherProps - } = props; - - return ( - <CellHoverContext.Consumer> - {(hovered) => ( - <View - style={[ - styles.label.container, - containerStyle, - hovered ? cellHoverContainerStyle : undefined, - ]} - {...otherProps}> - <Text style={[styles.label.text, textStyle, hovered ? cellHoverTextStyle : undefined]}> - {children} - </Text> - </View> - )} - </CellHoverContext.Consumer> - ); -}; - -export const Switch = React.forwardRef(function CellSwitch( - props: SwitchControl['props'], - ref?: React.Ref<SwitchControl>, -) { +export function Section(props: ISectionProps) { return ( - <View style={styles.switch}> - <SwitchControl ref={ref} {...props} /> - </View> + <StyledSection className={props.className}> + <CellSectionContext.Provider value={true}>{props.children}</CellSectionContext.Provider> + </StyledSection> ); -}); - -interface IInputFrameProps { - children?: React.ReactNode; - style?: Types.StyleRuleSetRecursive<Types.ViewStyleRuleSet>; } -export const InputFrame = function CellInputFrame(props: IInputFrameProps) { - const { style, children } = props; - - return <View style={[styles.input.frame, style]}>{children}</View>; -}; - -interface IInputProps extends Types.TextInputProps { +interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> { + value?: string; validateValue?: (value: string) => boolean; modifyValue?: (value: string) => string; submitOnBlur?: boolean; - onSubmit?: (value: string) => void; + onSubmitValue?: (value: string) => void; + onChangeValue?: (value: string) => void; } interface IInputState { @@ -166,9 +64,9 @@ interface IInputState { focused: boolean; } -export class Input extends Component<IInputProps, IInputState> { +export class Input extends React.Component<IInputProps, IInputState> { public state = { - value: this.props.value || '', + value: this.props.value ?? '', focused: false, }; @@ -178,187 +76,94 @@ export class Input extends Component<IInputProps, IInputState> { prevProps.value !== this.props.value && this.props.value !== this.state.value ) { - this.setState((_state, props) => ({ - value: props.value, - })); + this.setState( + (_state, props) => ({ + value: props.value, + }), + () => { + this.props.onChangeValue?.(this.state.value); + }, + ); } } public render() { const { - style, - value: _value, - onChangeText: _onChangeText, + type: _type, + onChange: _onChange, onFocus: _onFocus, onBlur: _onBlur, - onSubmitEditing: _onSubmitEditing, + onKeyPress: _onKeyPress, + value: _value, + modifyValue: _modifyValue, + submitOnBlur: _submitOnBlur, + onChangeValue: _onChangeValue, + onSubmitValue: _onSubmitValue, + validateValue, ...otherProps } = this.props; - const validityStyle = - this.props.validateValue && this.props.validateValue(this.state.value) - ? styles.input.validValue - : styles.input.invalidValue; - return ( - <TextInput - placeholderTextColor={colors.white60} - autoCorrect={false} - style={[styles.input.text, validityStyle, style]} - onChangeText={this.onChangeText} + <StyledInput + type="text" + valid={validateValue?.(this.state.value)} + onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur} - onSubmitEditing={this.onSubmitEditing} + onKeyPress={this.onKeyPress} value={this.state.value} {...otherProps} /> ); } - private onChangeText = (value: string) => { + private onChange = (event: React.ChangeEvent<HTMLInputElement>) => { + const value = this.props.modifyValue?.(event.target.value) ?? event.target.value; this.setState({ value }); - if (this.props.onChangeText) { - this.props.onChangeText(value); - } + this.props.onChange?.(event); + this.props.onChangeValue?.(value); }; - private onFocus = (e: Types.FocusEvent) => { + private onFocus = (event: React.FocusEvent<HTMLInputElement>) => { this.setState({ focused: true }); - if (this.props.onFocus) { - this.props.onFocus(e); - } + this.props.onFocus?.(event); }; - private onBlur = (e: Types.FocusEvent) => { + private onBlur = (event: React.FocusEvent<HTMLInputElement>) => { this.setState({ focused: false }); - if (this.props.onBlur) { - this.props.onBlur(e); - } - if (this.props.submitOnBlur && this.props.onSubmit) { - this.props.onSubmit(this.state.value); + this.props.onBlur?.(event); + if (this.props.submitOnBlur) { + this.props.onSubmitValue?.(this.state.value); } }; - private onSubmitEditing = () => { - if (this.props.onSubmit) { - this.props.onSubmit(this.state.value); - } - if (this.props.onSubmitEditing) { - this.props.onSubmitEditing(); + private onKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => { + if (event.key === 'Enter') { + this.props.onSubmitValue?.(this.state.value); } + this.props.onKeyPress?.(event); }; } -interface IAutoSizingTextInputContainerProps { - style?: Types.StyleRuleSetRecursive<Types.ViewStyleRuleSet>; - children: React.ReactElement<Types.TextInputProps>; -} - -interface IAutoSizingTextInputContainerState { - placeholderWidth?: number; - widthStyle?: Types.TextInputStyleRuleSet; -} - -export const AutoSizingTextInputContainer = class CellAutoSizingTextInputContainer extends Component< - IAutoSizingTextInputContainerProps, - IAutoSizingTextInputContainerState -> { - public state: IAutoSizingTextInputContainerState = {}; - - public render() { - const children: React.ReactElement<Types.TextInputProps> = this.props.children; - - return ( - <View style={this.props.style}> - <View style={styles.autoSizingInputContainer.measuringView} onLayout={this.onLayout}> - <Text - style={[ - styles.input.text, - - // TextInputStyle is basically an alias for TextStyle, so it's legit to assume that we - // can use both of them interchangably. - children.props.style, - - // this style resets any style properties that could constraint the text width. - styles.autoSizingInputContainer.measureText, - ]} - numberOfLines={1}> - {children.props.placeholder} - </Text> - </View> +export function AutoSizingTextInput({ onChangeValue, ...otherProps }: IInputProps) { + const [value, setValue] = useState(otherProps.value ?? ''); - {React.cloneElement(children, { - ...children.props, - style: [children.props.style, this.state.widthStyle], - })} - </View> - ); - } - - private onLayout = (layout: Types.ViewOnLayoutEvent) => { - if (this.state.placeholderWidth !== layout.width) { - this.setState({ - placeholderWidth: layout.width, - widthStyle: Styles.createTextInputStyle( - { - width: layout.width, - }, - false, - ), - }); - } - }; -}; - -type SubTextProps = Types.TextProps & { - cellHoverStyle?: Types.ViewStyle; -}; - -export const SubText = function CellSubText(props: SubTextProps) { - const { children, ref: _, style, cellHoverStyle, ...otherProps } = props; - - return ( - <CellHoverContext.Consumer> - {(hovered) => ( - <Text style={[styles.subtext, style, hovered ? cellHoverStyle : undefined]} {...otherProps}> - {children} - </Text> - )} - </CellHoverContext.Consumer> + const onChangeValueWrapper = useCallback( + (value: string) => { + setValue(value); + onChangeValue?.(value); + }, + [onChangeValue], ); -}; -export const Icon = function CellIcon(props: IImageViewProps) { - const { tintColor, tintHoverColor, ...otherProps } = props; - - return ( - <CellHoverContext.Consumer> - {(hovered) => ( - <StyledIcon - tintColor={(hovered && tintHoverColor) || tintColor || colors.white60} - {...otherProps} - /> - )} - </CellHoverContext.Consumer> - ); -}; - -export const Footer = function CellFooter({ children }: IContainerProps) { - return <View style={styles.footer.container}>{children}</View>; -}; - -export const FooterText = function CellFooterText(props: Text['props']) { return ( - <Text {...props} style={[styles.footer.text, props.style]}> - {props.children} - </Text> + <StyledAutoSizingTextInputContainer> + <StyledAutoSizingTextInputWrapper> + <Input onChangeValue={onChangeValueWrapper} {...otherProps} /> + </StyledAutoSizingTextInputWrapper> + <StyledAutoSizingTextInputFiller className={otherProps.className}> + {value === '' ? otherProps.placeholder : value} + </StyledAutoSizingTextInputFiller> + </StyledAutoSizingTextInputContainer> ); -}; - -export const FooterBoldText = function CellFooterText(props: Text['props']) { - return ( - <Text {...props} style={[styles.footer.text, styles.footer.boldText, props.style]}> - {props.children} - </Text> - ); -}; +} diff --git a/gui/src/renderer/components/CellStyles.tsx b/gui/src/renderer/components/CellStyles.tsx index 045ad3c6ff..40b27abbb0 100644 --- a/gui/src/renderer/components/CellStyles.tsx +++ b/gui/src/renderer/components/CellStyles.tsx @@ -1,133 +1,151 @@ -import { Styles } from 'reactxp'; +import React from 'react'; import styled from 'styled-components'; import { colors } from '../../config.json'; -import ImageView from './ImageView'; +import ImageView, { IImageViewProps } from './ImageView'; + +export const StyledContainer = styled.div({ + display: 'flex', + backgroundColor: colors.blue, + alignItems: 'center', + paddingLeft: '16px', + paddingRight: '12px', +}); + +export const StyledSection = styled.div({ + display: 'flex', + flexDirection: 'column', +}); + +export const StyledSectionTitle = styled.span({ + backgroundColor: colors.blue, + padding: '14px 24px', + marginBottom: '1px', + fontFamily: 'DINPro', + fontSize: '20px', + fontWeight: 900, + lineHeight: '26px', + color: colors.white, +}); + +interface IStyledCellButtonProps { + selected?: boolean; + containedInSection: boolean; +} + +export const StyledCellButton = styled.button({}, (props: IStyledCellButtonProps) => ({ + display: 'flex', + padding: '0 16px', + marginBottom: '1px', + flex: 1, + alignItems: 'center', + alignContent: 'center', + cursor: 'default', + border: 'none', + backgroundColor: props.selected + ? colors.green + : props.containedInSection + ? colors.blue40 + : colors.blue, + ':not(:disabled):hover': { + backgroundColor: props.selected ? colors.green : colors.blue80, + }, +})); + +export const StyledLabel = styled.div({ + margin: '14px 0 14px 8px', + flex: 1, + fontFamily: 'DINPro', + fontSize: '20px', + fontWeight: 900, + lineHeight: '26px', + letterSpacing: -0.2, + color: colors.white, + textAlign: 'left', +}); + +export const StyledSubText = styled.span({ + color: colors.white60, + fontFamily: 'Open Sans', + fontSize: '13px', + fontWeight: 800, + flex: -1, + textAlign: 'right', + marginLeft: '8px', +}); export const StyledIcon = styled(ImageView)({ marginLeft: '8px', }); -export default { - cellButton: { - base: Styles.createButtonStyle({ - backgroundColor: colors.blue, - paddingVertical: 0, - paddingHorizontal: 16, - marginBottom: 1, - flex: 1, - flexDirection: 'row', - alignItems: 'center', - alignContent: 'center', - cursor: 'default', - }), - section: Styles.createButtonStyle({ - backgroundColor: colors.blue40, - }), - hover: Styles.createButtonStyle({ - backgroundColor: colors.blue80, - }), - selected: Styles.createViewStyle({ - backgroundColor: colors.green, - }), - }, - cellContainer: Styles.createViewStyle({ - backgroundColor: colors.blue, - flexDirection: 'row', - alignItems: 'center', - paddingLeft: 16, - paddingRight: 12, - }), - footer: { - container: Styles.createViewStyle({ - paddingTop: 8, - paddingRight: 24, - paddingBottom: 24, - paddingLeft: 24, - }), - text: Styles.createTextStyle({ - fontFamily: 'Open Sans', - fontSize: 13, - fontWeight: '600', - lineHeight: 20, - letterSpacing: -0.2, - color: colors.white80, - }), - boldText: Styles.createTextStyle({ - fontWeight: '900', - }), +export const StyledTintedIcon = styled(StyledIcon).attrs((props: IImageViewProps) => ({ + tintColor: props.tintColor ?? colors.white60, + tintHoverColor: props.tintHoverColor ?? props.tintColor ?? colors.white60, +}))((props: IImageViewProps) => ({ + [StyledCellButton + ':hover &']: { + backgroundColor: props.tintHoverColor, }, - label: { - container: Styles.createViewStyle({ - marginLeft: 8, - marginTop: 14, - marginBottom: 14, - flex: 1, - }), - text: Styles.createTextStyle({ - fontFamily: 'DINPro', - fontSize: 20, - fontWeight: '900', - lineHeight: 26, - letterSpacing: -0.2, - color: colors.white, - }), - }, - switch: Styles.createViewStyle({ - flex: 0, - }), - input: { - frame: Styles.createViewStyle({ - flexGrow: 0, - backgroundColor: 'rgba(255,255,255,0.1)', - borderRadius: 4, - padding: 4, - }), - text: Styles.createTextInputStyle({ - color: colors.white, - backgroundColor: 'transparent', - fontFamily: 'Open Sans', - fontSize: 20, - fontWeight: '600', - lineHeight: 26, - textAlign: 'right', - padding: 0, - minWidth: 80, - }), - validValue: Styles.createTextStyle({ - color: colors.white, - }), - invalidValue: Styles.createTextStyle({ - color: colors.red, - }), - }, - autoSizingInputContainer: { - measuringView: Styles.createViewStyle({ - position: 'absolute', - opacity: 0, - }), - measureText: Styles.createTextStyle({ - width: undefined, - flexBasis: undefined, - }), - }, - subtext: Styles.createTextStyle({ - color: colors.white60, - fontFamily: 'Open Sans', - fontSize: 13, - fontWeight: '800', - flex: -1, - textAlign: 'right', - marginLeft: 8, - }), - sectionTitle: Styles.createTextStyle({ - backgroundColor: colors.blue, - paddingVertical: 14, - paddingHorizontal: 24, - marginBottom: 1, - fontFamily: 'DINPro', - fontSize: 20, - fontWeight: '900', - lineHeight: 26, - color: colors.white, - }), +})); + +export const StyledFooter = styled.div({ + padding: '8px 24px 24px', +}); + +export const StyledFooterText = styled.span({ + fontFamily: 'Open Sans', + fontSize: '13px', + fontWeight: 600, + lineHeight: '20px', + letterSpacing: -0.2, + color: colors.white80, +}); + +export const StyledFooterBoldText = styled(StyledFooterText)({ + fontWeight: 900, +}); + +export const StyledInputFrame = styled.div({ + flexGrow: 0, + backgroundColor: 'rgba(255,255,255,0.1)', + borderRadius: '4px', + padding: '4px', +}); + +const inputTextStyles: React.CSSProperties = { + fontFamily: 'Open Sans', + fontSize: '20px', + fontWeight: 600, + lineHeight: '26px', + height: '26px', + textAlign: 'right', + padding: '0px', }; + +export const StyledInput = styled.input({}, (props: { valid?: boolean }) => ({ + ...inputTextStyles, + backgroundColor: 'transparent', + border: 'none', + width: '100%', + height: '100%', + color: props.valid !== false ? colors.white : colors.red, + '::placeholder': { + color: colors.white60, + }, +})); + +export const StyledAutoSizingTextInputContainer = styled.div({ + position: 'relative', +}); + +export const StyledAutoSizingTextInputFiller = styled.pre({ + ...inputTextStyles, + minWidth: '80px', + color: 'transparent', +}); + +export const StyledAutoSizingTextInputWrapper = styled.div({ + position: 'absolute', + top: '0px', + left: '0px', + width: '100%', + height: '100%', +}); diff --git a/gui/src/renderer/components/CityRow.tsx b/gui/src/renderer/components/CityRow.tsx index 472c468222..65f8e1075c 100644 --- a/gui/src/renderer/components/CityRow.tsx +++ b/gui/src/renderer/components/CityRow.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import ReactDOM from 'react-dom'; -import { Component, Styles, View } from 'reactxp'; +import { Component, View } from 'reactxp'; +import styled from 'styled-components'; import { colors } from '../../config.json'; import { compareRelayLocation, RelayLocation } from '../../shared/daemon-rpc-types'; import Accordion from './Accordion'; @@ -24,16 +24,14 @@ interface IProps { children?: RelayRowElement | RelayRowElement[]; } -const styles = { - base: Styles.createButtonStyle({ - paddingRight: 0, - paddingLeft: 32, - backgroundColor: colors.blue40, - }), -}; +const Button = styled(Cell.CellButton)((props: { selected: boolean }) => ({ + paddingRight: 0, + paddingLeft: 32, + backgroundColor: !props.selected ? colors.blue40 : undefined, +})); export default class CityRow extends Component<IProps> { - private buttonRef = React.createRef<Cell.CellButton>(); + private buttonRef = React.createRef<HTMLButtonElement>(); public static compareProps(oldProps: IProps, nextProps: IProps): boolean { if (React.Children.count(oldProps.children) !== React.Children.count(nextProps.children)) { @@ -74,12 +72,11 @@ export default class CityRow extends Component<IProps> { return ( <View> - <Cell.CellButton + <Button ref={this.buttonRef} - onPress={this.handlePress} + onClick={this.handleClick} disabled={!this.props.hasActiveRelays} - selected={this.props.selected} - style={styles.base}> + selected={this.props.selected}> <RelayStatusIndicator active={this.props.hasActiveRelays} selected={this.props.selected} @@ -87,7 +84,7 @@ export default class CityRow extends Component<IProps> { <Cell.Label>{this.props.name}</Cell.Label> {hasChildren && <ChevronButton onClick={this.toggleCollapse} up={this.props.expanded} />} - </Cell.CellButton> + </Button> {hasChildren && ( <Accordion @@ -109,16 +106,15 @@ export default class CityRow extends Component<IProps> { event.stopPropagation(); }; - private handlePress = () => { + private handleClick = () => { if (this.props.onSelect) { this.props.onSelect(this.props.location); } }; private onWillExpand = (nextHeight: number) => { - const buttonNode = ReactDOM.findDOMNode(this.buttonRef.current); - if (buttonNode instanceof HTMLElement) { - const buttonRect = buttonNode.getBoundingClientRect(); + const buttonRect = this.buttonRef.current?.getBoundingClientRect(); + if (buttonRect) { this.props.onWillExpand?.(buttonRect, nextHeight); } }; diff --git a/gui/src/renderer/components/CountryRow.tsx b/gui/src/renderer/components/CountryRow.tsx index a541403cbd..35a082a27a 100644 --- a/gui/src/renderer/components/CountryRow.tsx +++ b/gui/src/renderer/components/CountryRow.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import ReactDOM from 'react-dom'; import { Component, Styles, View } from 'reactxp'; +import styled from 'styled-components'; import { compareRelayLocation, RelayLocation } from '../../shared/daemon-rpc-types'; import Accordion from './Accordion'; import * as Cell from './Cell'; @@ -28,14 +28,15 @@ const styles = { flexDirection: 'column', flex: 0, }), - base: Styles.createViewStyle({ - paddingRight: 0, - paddingLeft: 16, - }), }; +const Button = styled(Cell.CellButton)({ + paddingRight: 0, + paddingLeft: 16, +}); + export default class CountryRow extends Component<IProps> { - private buttonRef = React.createRef<Cell.CellButton>(); + private buttonRef = React.createRef<HTMLButtonElement>(); public static compareProps(oldProps: IProps, nextProps: IProps) { if (React.Children.count(oldProps.children) !== React.Children.count(nextProps.children)) { @@ -82,10 +83,9 @@ export default class CountryRow extends Component<IProps> { return ( <View style={styles.container}> - <Cell.CellButton + <Button ref={this.buttonRef} - style={styles.base} - onPress={this.handlePress} + onClick={this.handleClick} disabled={!this.props.hasActiveRelays} selected={this.props.selected}> <RelayStatusIndicator @@ -96,7 +96,7 @@ export default class CountryRow extends Component<IProps> { {hasChildren ? ( <ChevronButton onClick={this.toggleCollapse} up={this.props.expanded} /> ) : null} - </Cell.CellButton> + </Button> {hasChildren && ( <Accordion @@ -118,16 +118,15 @@ export default class CountryRow extends Component<IProps> { event.stopPropagation(); }; - private handlePress = () => { + private handleClick = () => { if (this.props.onSelect) { this.props.onSelect(this.props.location); } }; private onWillExpand = (nextHeight: number) => { - const buttonNode = ReactDOM.findDOMNode(this.buttonRef.current); - if (buttonNode instanceof HTMLElement) { - const buttonRect = buttonNode.getBoundingClientRect(); + const buttonRect = this.buttonRef.current?.getBoundingClientRect(); + if (buttonRect) { this.props.onWillExpand?.(buttonRect, nextHeight); } }; diff --git a/gui/src/renderer/components/LocationList.tsx b/gui/src/renderer/components/LocationList.tsx index efd8969e4f..06c2221314 100644 --- a/gui/src/renderer/components/LocationList.tsx +++ b/gui/src/renderer/components/LocationList.tsx @@ -220,7 +220,7 @@ interface ISpecialLocationProps<T> { export class SpecialLocation<T> extends Component<ISpecialLocationProps<T>> { public render() { return ( - <Cell.CellButton selected={this.props.isSelected} onPress={this.onSelect}> + <Cell.CellButton selected={this.props.isSelected} onClick={this.onSelect}> <Cell.Icon source={this.props.isSelected ? 'icon-tick' : this.props.icon} tintColor={colors.white} diff --git a/gui/src/renderer/components/Login.tsx b/gui/src/renderer/components/Login.tsx index e77ddcbe09..9d317091ac 100644 --- a/gui/src/renderer/components/Login.tsx +++ b/gui/src/renderer/components/Login.tsx @@ -6,11 +6,15 @@ import { messages } from '../../shared/gettext'; import { formatAccountToken } from '../lib/account'; import Accordion from './Accordion'; import * as AppButton from './AppButton'; -import * as Cell from './Cell'; import { Brand, SettingsBarButton } from './HeaderBar'; import ImageView from './ImageView'; import { Container, Header, Layout } from './Layout'; -import styles, { AccountDropdownRemoveIcon, InputSubmitIcon } from './LoginStyles'; +import styles, { + AccountDropdownItemButton, + AccountDropdownItemButtonLabel, + AccountDropdownRemoveIcon, + InputSubmitIcon, +} from './LoginStyles'; import { AccountToken } from '../../shared/daemon-rpc-types'; import { LoginState } from '../redux/account/reducers'; @@ -445,16 +449,10 @@ class AccountDropdownItem extends Component<IAccountDropdownItemProps> { return ( <View> <View style={styles.account_dropdown__spacer} /> - <Cell.CellButton - style={styles.account_dropdown__item} - hoverStyle={styles.account_dropdown__item_hover}> - <Cell.Label - textStyle={styles.account_dropdown__label} - containerStyle={styles.account_dropdown__label_container} - cellHoverTextStyle={styles.account_dropdown__label_hover} - onPress={this.handleSelect}> + <AccountDropdownItemButton> + <AccountDropdownItemButtonLabel onClick={this.handleSelect}> {this.props.label} - </Cell.Label> + </AccountDropdownItemButtonLabel> <AccountDropdownRemoveIcon tintColor={colors.blue40} tintHoverColor={colors.blue} @@ -463,7 +461,7 @@ class AccountDropdownItem extends Component<IAccountDropdownItemProps> { width={16} onClick={this.handleRemove} /> - </Cell.CellButton> + </AccountDropdownItemButton> </View> ); } diff --git a/gui/src/renderer/components/LoginStyles.tsx b/gui/src/renderer/components/LoginStyles.tsx index c60b7471ca..d935eb195e 100644 --- a/gui/src/renderer/components/LoginStyles.tsx +++ b/gui/src/renderer/components/LoginStyles.tsx @@ -2,6 +2,7 @@ import { Styles } from 'reactxp'; import styled from 'styled-components'; import { colors } from '../../config.json'; import ImageView from './ImageView'; +import * as Cell from './Cell'; export const AccountDropdownRemoveIcon = styled(ImageView)({ justifyContent: 'center', @@ -21,6 +22,31 @@ export const InputSubmitIcon = styled(ImageView)((props: { visible: boolean }) = opacity: props.visible ? 1 : 0, })); +export const AccountDropdownItemButton = styled(Cell.CellButton)({ + padding: '0px', + marginBottom: '0px', + flexDirection: 'row', + alignItems: 'stretch', + backgroundColor: colors.white60, + cursor: 'default', + ':not(:disabled):hover': { + backgroundColor: colors.white40, + }, +}); + +export const AccountDropdownItemButtonLabel = styled(Cell.Label)({ + padding: '11px 0px 11px 12px', + margin: '0', + color: colors.blue80, + borderWidth: 0, + textAlign: 'left', + marginLeft: 0, + cursor: 'default', + [AccountDropdownItemButton + ':hover']: { + color: colors.blue, + }, +}); + export default { login_footer: Styles.createViewStyle({ flex: 0, @@ -83,30 +109,6 @@ export default { height: 1, backgroundColor: colors.darkBlue, }), - account_dropdown__item: Styles.createViewStyle({ - paddingTop: 0, - paddingRight: 0, - paddingLeft: 0, - paddingBottom: 0, - marginBottom: 0, - flexDirection: 'row', - alignItems: 'stretch', - backgroundColor: colors.white60, - cursor: 'default', - }), - account_dropdown__item_hover: Styles.createViewStyle({ - backgroundColor: colors.white40, - }), - account_dropdown__label_hover: Styles.createTextStyle({ - color: colors.blue, - }), - account_dropdown__label_container: Styles.createViewStyle({ - paddingLeft: 12, - paddingTop: 11, - paddingBottom: 11, - marginHorizontal: 0, - marginVertical: 0, - }), login_footer__prompt: Styles.createTextStyle({ color: colors.white80, @@ -150,11 +152,4 @@ export default { backgroundColor: 'transparent', flex: 1, }), - account_dropdown__label: Styles.createTextStyle({ - color: colors.blue80, - borderWidth: 0, - textAlign: 'left', - marginLeft: 0, - cursor: 'default', - }), }; diff --git a/gui/src/renderer/components/RelayRow.tsx b/gui/src/renderer/components/RelayRow.tsx index cd87610944..f7f3998ab5 100644 --- a/gui/src/renderer/components/RelayRow.tsx +++ b/gui/src/renderer/components/RelayRow.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { Component, Styles } from 'reactxp'; +import { Component } from 'reactxp'; +import styled from 'styled-components'; import { colors } from '../../config.json'; import { compareRelayLocation, RelayLocation } from '../../shared/daemon-rpc-types'; import * as Cell from './Cell'; @@ -13,13 +14,11 @@ interface IProps { onSelect?: (location: RelayLocation) => void; } -const styles = { - base: Styles.createViewStyle({ - paddingRight: 0, - paddingLeft: 48, - backgroundColor: colors.blue20, - }), -}; +const Button = styled(Cell.CellButton)((props: { selected: boolean }) => ({ + paddingRight: 0, + paddingLeft: 48, + backgroundColor: !props.selected ? colors.blue20 : undefined, +})); export default class RelayRow extends Component<IProps> { public static compareProps(oldProps: IProps, nextProps: IProps) { @@ -37,19 +36,18 @@ export default class RelayRow extends Component<IProps> { public render() { return ( - <Cell.CellButton - onPress={this.handlePress} + <Button + onClick={this.handleClick} selected={this.props.selected} - disabled={!this.props.active} - style={styles.base}> + disabled={!this.props.active}> <RelayStatusIndicator active={this.props.active} selected={this.props.selected} /> <Cell.Label>{this.props.hostname}</Cell.Label> - </Cell.CellButton> + </Button> ); } - private handlePress = () => { + private handleClick = () => { if (this.props.onSelect) { this.props.onSelect(this.props.location); } diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx index 254272cc46..1a294a05ba 100644 --- a/gui/src/renderer/components/SelectLanguage.tsx +++ b/gui/src/renderer/components/SelectLanguage.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { Component, Styles, View } from 'reactxp'; +import styled from 'styled-components'; import { colors } from '../../config.json'; import { messages } from '../../shared/gettext'; import CustomScrollbars from './CustomScrollbars'; @@ -35,15 +36,16 @@ const styles = { container: Styles.createViewStyle({ flex: 1, }), - selector: Styles.createViewStyle({ - marginBottom: 0, - }), // plain CSS style scrollview: { flex: 1, }, }; +const StyledSelector = (styled(Selector)({ + marginBottom: 0, +}) as unknown) as new <T>() => Selector<T>; + export default class SelectLanguage extends Component<IProps, IState> { private scrollView = React.createRef<CustomScrollbars>(); private selectedCellRef = React.createRef<SelectorCell<string>>(); @@ -92,8 +94,7 @@ export default class SelectLanguage extends Component<IProps, IState> { {messages.pgettext('select-language-nav', 'Select language')} </HeaderTitle> </SettingsHeader> - <Selector - style={styles.selector} + <StyledSelector title="" values={this.state.source} value={this.props.preferredLocale} diff --git a/gui/src/renderer/components/Selector.tsx b/gui/src/renderer/components/Selector.tsx index 1c3fdcdaf7..57f2323cef 100644 --- a/gui/src/renderer/components/Selector.tsx +++ b/gui/src/renderer/components/Selector.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { Component, Styles, Types, View } from 'reactxp'; import styled from 'styled-components'; import { colors } from '../../config.json'; import * as Cell from './Cell'; @@ -11,21 +10,19 @@ export interface ISelectorItem<T> { } interface ISelectorProps<T> { - style?: Types.ViewStyleRuleSet; title?: string; values: Array<ISelectorItem<T>>; value: T; onSelect: (value: T) => void; selectedCellRef?: React.Ref<SelectorCell<T>>; + className?: string; } -const styles = { - section: Styles.createViewStyle({ - marginBottom: 24, - }), -}; +const Section = styled(Cell.Section)({ + marginBottom: 24, +}); -export default class Selector<T> extends Component<ISelectorProps<T>> { +export default class Selector<T> extends React.Component<ISelectorProps<T>> { public render() { const items = this.props.values.map((item, i) => { const selected = item.value === this.props.value; @@ -45,13 +42,13 @@ export default class Selector<T> extends Component<ISelectorProps<T>> { if (this.props.title) { return ( - <Cell.Section style={[styles.section, this.props.style]}> + <Section className={this.props.className}> <Cell.SectionTitle>{this.props.title}</Cell.SectionTitle> {items} - </Cell.Section> + </Section> ); } else { - return <View style={[styles.section, this.props.style]}>{items}</View>; + return <Section className={this.props.className}>{items}</Section>; } } } @@ -68,11 +65,11 @@ interface ISelectorCellProps<T> { children?: React.ReactText; } -export class SelectorCell<T> extends Component<ISelectorCellProps<T>> { +export class SelectorCell<T> extends React.Component<ISelectorCellProps<T>> { public render() { return ( <Cell.CellButton - onPress={this.onPress} + onClick={this.onClick} selected={this.props.selected} disabled={this.props.disabled}> <StyledCellIcon @@ -87,7 +84,7 @@ export class SelectorCell<T> extends Component<ISelectorCellProps<T>> { ); } - private onPress = () => { + private onClick = () => { if (!this.props.selected) { this.props.onSelect(this.props.value); } diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx index eb12187dc9..644190a4db 100644 --- a/gui/src/renderer/components/Settings.tsx +++ b/gui/src/renderer/components/Settings.tsx @@ -15,7 +15,7 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; -import styles from './SettingsStyles'; +import styles, { OutOfTimeSubText } from './SettingsStyles'; import { LoginState } from '../redux/account/reducers'; @@ -113,21 +113,21 @@ export default class Settings extends Component<IProps> { return ( <View> <View> - <Cell.CellButton onPress={this.props.onViewAccount}> + <Cell.CellButton onClick={this.props.onViewAccount}> <Cell.Label> { // TRANSLATORS: Navigation button to the 'Account' view messages.pgettext('settings-view', 'Account') } </Cell.Label> - <Cell.SubText style={isOutOfTime ? styles.accountPaidUntilErrorLabel : undefined}> + <OutOfTimeSubText isOutOfTime={isOutOfTime}> {isOutOfTime ? outOfTimeMessage : formattedExpiry} - </Cell.SubText> + </OutOfTimeSubText> <Cell.Icon height={12} width={7} source="icon-chevron" /> </Cell.CellButton> </View> - <Cell.CellButton onPress={this.props.onViewPreferences}> + <Cell.CellButton onClick={this.props.onViewPreferences}> <Cell.Label> { // TRANSLATORS: Navigation button to the 'Preferences' view @@ -137,7 +137,7 @@ export default class Settings extends Component<IProps> { <Cell.Icon height={12} width={7} source="icon-chevron" /> </Cell.CellButton> - <Cell.CellButton onPress={this.props.onViewAdvancedSettings}> + <Cell.CellButton onClick={this.props.onViewAdvancedSettings}> <Cell.Label> { // TRANSLATORS: Navigation button to the 'Advanced' settings view @@ -181,10 +181,10 @@ export default class Settings extends Component<IProps> { return ( <View> - <Cell.CellButton disabled={this.props.isOffline} onPress={this.openDownloadLink}> + <Cell.CellButton disabled={this.props.isOffline} onClick={this.openDownloadLink}> {icon} <Cell.Label>{messages.pgettext('settings-view', 'App version')}</Cell.Label> - <Cell.SubText style={styles.appVersionLabel}>{this.props.appVersion}</Cell.SubText> + <Cell.SubText>{this.props.appVersion}</Cell.SubText> <Cell.Icon height={16} width={16} source="icon-extLink" /> </Cell.CellButton> {footer} @@ -195,7 +195,7 @@ export default class Settings extends Component<IProps> { private renderBottomButtons() { return ( <View> - <Cell.CellButton onPress={this.props.onViewSupport}> + <Cell.CellButton onClick={this.props.onViewSupport}> <Cell.Label> { // TRANSLATORS: Navigation button to the 'Report a problem' help view @@ -205,7 +205,7 @@ export default class Settings extends Component<IProps> { <Cell.Icon height={12} width={7} source="icon-chevron" /> </Cell.CellButton> - <Cell.CellButton disabled={this.props.isOffline} onPress={this.openFaqLink}> + <Cell.CellButton disabled={this.props.isOffline} onClick={this.openFaqLink}> <Cell.Label> { // TRANSLATORS: Link to the webpage @@ -215,7 +215,7 @@ export default class Settings extends Component<IProps> { <Cell.Icon height={16} width={16} source="icon-extLink" /> </Cell.CellButton> - <Cell.CellButton onPress={this.props.onViewSelectLanguage}> + <Cell.CellButton onClick={this.props.onViewSelectLanguage}> <Cell.UntintedIcon width={24} height={24} source="icon-language" /> <Cell.Label> { diff --git a/gui/src/renderer/components/SettingsStyles.tsx b/gui/src/renderer/components/SettingsStyles.tsx index 51a215d0fc..1406fba209 100644 --- a/gui/src/renderer/components/SettingsStyles.tsx +++ b/gui/src/renderer/components/SettingsStyles.tsx @@ -1,5 +1,11 @@ import { Styles } from 'reactxp'; +import styled from 'styled-components'; import { colors } from '../../config.json'; +import * as Cell from './Cell'; + +export const OutOfTimeSubText = styled(Cell.SubText)((props: { isOutOfTime: boolean }) => ({ + color: props.isOutOfTime ? colors.red : undefined, +})); export default { settings: Styles.createViewStyle({ @@ -36,9 +42,6 @@ export default { paddingLeft: 24, paddingRight: 24, }), - accountPaidUntilErrorLabel: Styles.createTextStyle({ - color: colors.red, - }), cellFooterLabel: Styles.createTextStyle({ fontFamily: 'Open Sans', fontSize: 13, @@ -47,7 +50,4 @@ export default { letterSpacing: -0.2, color: colors.white60, }), - appVersionLabel: Styles.createTextStyle({ - flex: 0, - }), }; diff --git a/gui/src/renderer/components/Switch.tsx b/gui/src/renderer/components/Switch.tsx index ceda826fb0..7a1555c761 100644 --- a/gui/src/renderer/components/Switch.tsx +++ b/gui/src/renderer/components/Switch.tsx @@ -5,6 +5,7 @@ import { colors } from '../../config.json'; interface IProps { isOn: boolean; onChange?: (isOn: boolean) => void; + className?: string; } interface IState { @@ -70,7 +71,10 @@ export default class Switch extends React.Component<IProps, IState> { public render() { return ( - <SwitchContainer ref={this.containerRef} onClick={this.handleClick}> + <SwitchContainer + ref={this.containerRef} + onClick={this.handleClick} + className={this.props.className}> <Knob isOn={this.state.isOn} isPressed={this.state.isPressed} |
