diff options
Diffstat (limited to 'gui')
41 files changed, 339 insertions, 301 deletions
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index 7c7df73796..bab17524d6 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -1,6 +1,7 @@ import { batch, Provider } from 'react-redux'; import { Router } from 'react-router'; import { bindActionCreators } from 'redux'; +import { StyleSheetManager } from 'styled-components'; import { hasExpired } from '../shared/account-expiry'; import { ILinuxSplitTunnelingApplication, IWindowsApplication } from '../shared/application-types'; @@ -276,19 +277,21 @@ export default class AppRenderer { return ( <AppContext.Provider value={{ app: this }}> <Provider store={this.reduxStore}> - <Lang> - <Router history={this.history.asHistory}> - <ErrorBoundary> - <ModalContainer> - <KeyboardNavigation> - <AppRouter /> - <Changelog /> - </KeyboardNavigation> - {window.env.platform === 'darwin' && <MacOsScrollbarDetection />} - </ModalContainer> - </ErrorBoundary> - </Router> - </Lang> + <StyleSheetManager enableVendorPrefixes> + <Lang> + <Router history={this.history.asHistory}> + <ErrorBoundary> + <ModalContainer> + <KeyboardNavigation> + <AppRouter /> + <Changelog /> + </KeyboardNavigation> + {window.env.platform === 'darwin' && <MacOsScrollbarDetection />} + </ModalContainer> + </ErrorBoundary> + </Router> + </Lang> + </StyleSheetManager> </Provider> </AppContext.Provider> ); diff --git a/gui/src/renderer/components/Accordion.tsx b/gui/src/renderer/components/Accordion.tsx index 682f5aa779..f70258f103 100644 --- a/gui/src/renderer/components/Accordion.tsx +++ b/gui/src/renderer/components/Accordion.tsx @@ -14,11 +14,11 @@ interface IState { containerHeight: string; } -const Container = styled.div((props: { height: string; animationDuration: number }) => ({ +const Container = styled.div<{ $height: string; $animationDuration: number }>((props) => ({ display: 'flex', - height: props.height, + height: props.$height, overflow: 'hidden', - transition: `height ${props.animationDuration}ms ease-in-out`, + transition: `height ${props.$animationDuration}ms ease-in-out`, })); const Content = styled.div({ @@ -55,8 +55,8 @@ export default class Accordion extends React.Component<IProps, IState> { return ( <Container ref={this.containerRef} - height={this.state.containerHeight} - animationDuration={this.props.animationDuration} + $height={this.state.containerHeight} + $animationDuration={this.props.animationDuration} onTransitionEnd={this.onTransitionEnd}> <Content ref={this.contentRef}>{this.state.mountChildren && this.props.children}</Content> </Container> diff --git a/gui/src/renderer/components/AppButton.tsx b/gui/src/renderer/components/AppButton.tsx index 45c93e1b2a..753f64745e 100644 --- a/gui/src/renderer/components/AppButton.tsx +++ b/gui/src/renderer/components/AppButton.tsx @@ -22,7 +22,7 @@ interface ILabelProps { } export function Label(props: ILabelProps) { - return <StyledLabel textOffset={props.textOffset ?? 0}>{props.children}</StyledLabel>; + return <StyledLabel $textOffset={props.textOffset ?? 0}>{props.children}</StyledLabel>; } interface IIconProps { @@ -108,7 +108,7 @@ const StyledSimpleButton = styled(SimpleButton)({ borderRadius: 4, border: 'none', padding: 0, - ':disabled': { + '&&:disabled': { opacity: 0.5, }, }); @@ -156,35 +156,35 @@ export function BlockingButton(props: IBlockingProps) { export const RedButton = styled(BaseButton)({ backgroundColor: colors.red, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.red95, }, }); export const GreenButton = styled(BaseButton)({ backgroundColor: colors.green, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.green90, }, }); export const BlueButton = styled(BaseButton)({ backgroundColor: colors.blue80, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.blue60, }, }); export const TransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.white20, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.white40, }, }); export const RedTransparentButton = styled(BaseButton)(transparentButton, { backgroundColor: colors.red60, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: colors.red80, }, }); @@ -193,7 +193,7 @@ const StyledButtonWrapper = styled.div({ display: 'flex', flexDirection: 'column', flex: 0, - ':not(:last-child)': { + '&&:not(:last-child)': { marginBottom: measurements.buttonVerticalMargin, }, }); diff --git a/gui/src/renderer/components/AppButtonStyles.tsx b/gui/src/renderer/components/AppButtonStyles.tsx index f22eb3473a..ac65f951f6 100644 --- a/gui/src/renderer/components/AppButtonStyles.tsx +++ b/gui/src/renderer/components/AppButtonStyles.tsx @@ -2,9 +2,9 @@ import styled from 'styled-components'; import { buttonText } from './common-styles'; -export const StyledLabel = styled.span(buttonText, (props: { textOffset: number }) => ({ - paddingLeft: props.textOffset > 0 ? `${props.textOffset}px` : 0, - paddingRight: props.textOffset < 0 ? `${-props.textOffset}px` : 0, +export const StyledLabel = styled.span<{ $textOffset: number }>(buttonText, (props) => ({ + paddingLeft: props.$textOffset > 0 ? `${props.$textOffset}px` : 0, + paddingRight: props.$textOffset < 0 ? `${-props.$textOffset}px` : 0, textAlign: 'center', wordBreak: 'break-word', })); diff --git a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx index 11ec9a0136..3941c3d88f 100644 --- a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx +++ b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx @@ -11,18 +11,18 @@ const Container = styled.div({ width: '100%', }); -const Caption = styled.span(normalText, (props: { open: boolean }) => ({ +const Caption = styled.span<{ $open: boolean }>(normalText, (props) => ({ fontWeight: 600, lineHeight: '20px', minWidth: '0px', - color: props.open ? colors.white : colors.white40, - [Container + ':hover &']: { + color: props.$open ? colors.white : colors.white40, + [Container + ':hover &&']: { color: colors.white, }, })); const Chevron = styled(ImageView)({ - [Container + ':hover &']: { + [Container + ':hover &&']: { backgroundColor: colors.white, }, }); @@ -37,7 +37,7 @@ interface IProps { export default function ConnectionPanelDisclosure(props: IProps) { return ( <Container className={props.className} onClick={props.onToggle}> - <Caption open={props.pointsUp}>{props.children}</Caption> + <Caption $open={props.pointsUp}>{props.children}</Caption> <Chevron source={props.pointsUp ? 'icon-chevron-up' : 'icon-chevron-down'} width={22} diff --git a/gui/src/renderer/components/CustomDnsSettingsStyles.tsx b/gui/src/renderer/components/CustomDnsSettingsStyles.tsx index b660504a01..b8fdc7fd38 100644 --- a/gui/src/renderer/components/CustomDnsSettingsStyles.tsx +++ b/gui/src/renderer/components/CustomDnsSettingsStyles.tsx @@ -12,19 +12,16 @@ export const StyledAddCustomDnsButton = styled(Cell.CellButton)({ backgroundColor: colors.blue40, }); -export const StyledAddCustomDnsLabel = styled(Cell.Label)( - {}, - (props: { paddingLeft?: number }) => ({ - fontFamily: 'Open Sans', - fontWeight: 400, - fontSize: '16px', - paddingLeft: (props.paddingLeft ?? 32) + 'px', - whiteSpace: 'pre-wrap', - overflowWrap: 'break-word', - width: '171px', - marginRight: '25px', - }), -); +export const StyledAddCustomDnsLabel = styled(Cell.Label)<{ $paddingLeft?: number }>((props) => ({ + fontFamily: 'Open Sans', + fontWeight: 400, + fontSize: '16px', + paddingLeft: (props.$paddingLeft ?? 32) + 'px', + whiteSpace: 'pre-wrap', + overflowWrap: 'break-word', + width: '171px', + marginRight: '25px', +})); export const StyledContainer = styled(Cell.Container)({ display: 'flex', @@ -59,7 +56,7 @@ export const StyledRemoveButton = styled.button({ }); export const StyledRemoveIcon = styled(ImageView)({ - [StyledRemoveButton + ':hover &']: { + [StyledRemoveButton + ':hover &&']: { backgroundColor: colors.white80, }, }); diff --git a/gui/src/renderer/components/CustomScrollbars.tsx b/gui/src/renderer/components/CustomScrollbars.tsx index ab95d66511..65db8c87fe 100644 --- a/gui/src/renderer/components/CustomScrollbars.tsx +++ b/gui/src/renderer/components/CustomScrollbars.tsx @@ -19,44 +19,44 @@ const StyledCustomScrollbars = styled.div({ overflow: 'hidden', }); -const StyledScrollable = styled.div((props: { fillContainer?: boolean }) => ({ - flex: props.fillContainer ? '1' : undefined, +const StyledScrollable = styled.div<{ $fillContainer?: boolean }>((props) => ({ + flex: props.$fillContainer ? '1' : undefined, width: '100%', overflow: 'auto', - '::-webkit-scrollbar': { + '&&::-webkit-scrollbar': { display: 'none', }, })); -const StyledTrack = styled.div({}, (props: { canScroll: boolean; show: boolean }) => ({ +const StyledTrack = styled.div<{ $canScroll: boolean; $show: boolean }>((props) => ({ position: 'absolute', top: 0, right: 0, bottom: 0, width: '16px', - backgroundColor: props.show ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0)', + backgroundColor: props.$show ? 'rgba(0, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0)', borderRadius: '8px', transition: 'width 0.1s ease-in-out, background-color 0.25s ease-in-out', zIndex: 99, - pointerEvents: props.canScroll ? 'auto' : 'none', - // Thumb should be less transparent when track is hovered. - [`&:hover ${StyledThumb}`]: { - backgroundColor: 'rgba(255, 255, 255, 0.65)', - }, + pointerEvents: props.$canScroll ? 'auto' : 'none', })); -const StyledThumb = styled.div( - {}, - (props: { show: boolean; isDragging: boolean; wide: boolean }) => ({ +const StyledThumb = styled.div<{ $show: boolean; $isDragging: boolean; $wide: boolean }>( + (props) => ({ position: 'absolute', top: 0, right: 0, - borderRadius: props.wide ? '6px' : '4px', - width: props.wide ? '12px' : '8px', + borderRadius: props.$wide ? '6px' : '4px', + width: props.$wide ? '12px' : '8px', transition: 'width 0.25s ease-in-out, border-radius 0.25s ease-in-out, height 0.25s ease-in-out, opacity 0.25s ease-in-out, background-color 0.1s ease-in-out', - opacity: props.show ? 1 : 0, - backgroundColor: props.isDragging ? 'rgba(255, 255, 255, 0.65)' : 'rgba(255, 255, 255, 0.4)', + opacity: props.$show ? 1 : 0, + backgroundColor: props.$isDragging ? 'rgba(255, 255, 255, 0.65)' : 'rgba(255, 255, 255, 0.4)', + + // Thumb should be less transparent when track is hovered. + [`${StyledTrack}:hover &&`]: { + backgroundColor: 'rgba(255, 255, 255, 0.65)', + }, }), ); @@ -260,20 +260,20 @@ class CustomScrollbars extends React.Component<IProps, IState> { <StyledCustomScrollbars {...otherProps}> <StyledTrack ref={this.trackRef} - show={showScrollbars && this.state.active} - canScroll={this.state.canScroll} + $show={showScrollbars && this.state.active} + $canScroll={this.state.canScroll} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <StyledThumb ref={this.thumbRef} - show={showScrollbars} - isDragging={this.state.isDragging} - wide={this.state.active} + $show={showScrollbars} + $isDragging={this.state.isDragging} + $wide={this.state.active} onMouseDown={this.handleMouseDown} /> </StyledTrack> <StyledScrollable - fillContainer={fillContainer} + $fillContainer={fillContainer} onScroll={this.onScroll} ref={this.scrollableRef}> <StyledScrollableContent ref={this.scrollableContentRef}> diff --git a/gui/src/renderer/components/Filter.tsx b/gui/src/renderer/components/Filter.tsx index a8e67b3f9c..2cbc9ab239 100644 --- a/gui/src/renderer/components/Filter.tsx +++ b/gui/src/renderer/components/Filter.tsx @@ -201,7 +201,7 @@ function providersSelector(state: IReduxState): Record<string, boolean> { const StyledSelector = styled(Selector)({ marginBottom: 0, -}) as typeof Selector; +}); interface IFilterByOwnershipProps { ownership: Ownership; @@ -290,7 +290,7 @@ function FilterByProvider(props: IFilterByProviderProps) { <Accordion expanded={expanded}> <CheckboxRow label={messages.pgettext('filter-view', 'All providers')} - bold + $bold checked={Object.values(props.providers).every((value) => value)} onChange={toggleAll} /> @@ -310,7 +310,7 @@ function toggleAllProviders(providers: Record<string, boolean>, value?: boolean) } interface IStyledRowTitleProps { - bold?: boolean; + $bold?: boolean; } const StyledCheckbox = styled.div({ @@ -325,13 +325,13 @@ const StyledCheckbox = styled.div({ const StyledRow = styled(Cell.Row)({ backgroundColor: colors.blue40, - ':hover': { + '&&:hover': { backgroundColor: colors.blue80, }, }); -const StyledRowTitle = styled.label(normalText, (props: IStyledRowTitleProps) => ({ - fontWeight: props.bold ? 600 : 400, +const StyledRowTitle = styled.label<IStyledRowTitleProps>(normalText, (props) => ({ + fontWeight: props.$bold ? 600 : 400, color: colors.white, marginLeft: '22px', })); @@ -350,7 +350,7 @@ function CheckboxRow(props: ICheckboxRowProps) { <StyledCheckbox role="checkbox" aria-label={props.label} aria-checked={props.checked}> {props.checked && <ImageView source="icon-tick" width={18} tintColor={colors.green} />} </StyledCheckbox> - <StyledRowTitle aria-hidden bold={props.bold}> + <StyledRowTitle aria-hidden $bold={props.$bold}> {props.label} </StyledRowTitle> </StyledRow> diff --git a/gui/src/renderer/components/HeaderBar.tsx b/gui/src/renderer/components/HeaderBar.tsx index a5c3969f08..98502a6346 100644 --- a/gui/src/renderer/components/HeaderBar.tsx +++ b/gui/src/renderer/components/HeaderBar.tsx @@ -29,16 +29,16 @@ const headerBarStyleColorMap = { }; interface IHeaderBarContainerProps { - barStyle?: HeaderBarStyle; - accountInfoVisible: boolean; - unpinnedWindow: boolean; + $barStyle?: HeaderBarStyle; + $accountInfoVisible: boolean; + $unpinnedWindow: boolean; } -const HeaderBarContainer = styled.header({}, (props: IHeaderBarContainerProps) => ({ +const HeaderBarContainer = styled.header<IHeaderBarContainerProps>((props) => ({ padding: '15px 11px 0px 16px', - minHeight: props.accountInfoVisible ? '80px' : '68px', - height: props.accountInfoVisible ? '80px' : '68px', - backgroundColor: headerBarStyleColorMap[props.barStyle ?? HeaderBarStyle.default], + minHeight: props.$accountInfoVisible ? '80px' : '68px', + height: props.$accountInfoVisible ? '80px' : '68px', + backgroundColor: headerBarStyleColorMap[props.$barStyle ?? HeaderBarStyle.default], transitionProperty: 'height, min-height', transitionDuration: '250ms', transitionTimingFunction: 'ease-in-out', @@ -63,10 +63,10 @@ export default function HeaderBar(props: IHeaderBarProps) { return ( <HeaderBarContainer - barStyle={props.barStyle} + $barStyle={props.barStyle} className={props.className} - accountInfoVisible={props.showAccountInfo ?? false} - unpinnedWindow={unpinnedWindow}> + $accountInfoVisible={props.showAccountInfo ?? false} + $unpinnedWindow={unpinnedWindow}> <HeaderBarContent>{props.children}</HeaderBarContent> {props.showAccountInfo && <HeaderBarDeviceInfo />} </HeaderBarContainer> diff --git a/gui/src/renderer/components/ImageView.tsx b/gui/src/renderer/components/ImageView.tsx index 3be04159a2..fa3855b21a 100644 --- a/gui/src/renderer/components/ImageView.tsx +++ b/gui/src/renderer/components/ImageView.tsx @@ -1,7 +1,10 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; -export interface IImageViewProps extends IImageMaskProps { +import { NonTransientProps } from '../lib/styles'; + +export interface IImageViewProps + extends NonTransientProps<IImageMaskProps, 'tintColor' | 'tintHoverColor'> { source: string; onClick?: (event: React.MouseEvent) => void; className?: string; @@ -11,8 +14,8 @@ interface IImageMaskProps extends React.HTMLAttributes<HTMLElement> { width?: number; height?: number; disabled?: boolean; - tintColor?: string; - tintHoverColor?: string; + $tintColor?: string; + $tintHoverColor?: string; } const Wrapper = styled.div({ @@ -21,7 +24,7 @@ const Wrapper = styled.div({ justifyContent: 'center', }); -const ImageMask = styled.div((props: IImageMaskProps) => { +const ImageMask = styled.div<IImageMaskProps>((props) => { const maskWidth = props.width ? `${props.width}px` : 'auto'; const maskHeight = props.height ? `${props.height}px` : 'auto'; return { @@ -29,9 +32,9 @@ const ImageMask = styled.div((props: IImageMaskProps) => { maskSize: `${maskWidth} ${maskHeight}`, maskPosition: 'center', lineHeight: 0, - backgroundColor: props.tintColor, - ':hover': { - backgroundColor: (!props.disabled && props.tintHoverColor) || props.tintColor, + backgroundColor: props.$tintColor, + '&&:hover': { + backgroundColor: (!props.disabled && props.$tintHoverColor) || props.$tintColor, }, }; }); @@ -47,9 +50,13 @@ export default function ImageView(props: IImageViewProps) { const style = useMemo(() => ({ WebkitMaskImage: `url('${url}')` }), [url]); if (props.tintColor) { - const { source: _source, ...otherProps } = props; + const { source: _source, tintColor, tintHoverColor, ...otherProps } = props; return ( - <ImageMask style={style} {...otherProps}> + <ImageMask + style={style} + $tintColor={tintColor} + $tintHoverColor={tintHoverColor} + {...otherProps}> <HiddenImage src={url} width={props.width} height={props.height} /> </ImageMask> ); diff --git a/gui/src/renderer/components/Launch.tsx b/gui/src/renderer/components/Launch.tsx index 5f12fcce8f..3f86e4d19c 100644 --- a/gui/src/renderer/components/Launch.tsx +++ b/gui/src/renderer/components/Launch.tsx @@ -21,10 +21,10 @@ export default function Launch() { ); } -const StyledFooter = styled(Footer)({}, (props: { show: boolean }) => ({ +const StyledFooter = styled(Footer)<{ $show: boolean }>((props) => ({ backgroundColor: colors.blue, padding: `0 14px ${measurements.viewMargin}`, - opacity: props.show ? 1 : 0, + opacity: props.$show ? 1 : 0, transition: 'opacity 250ms ease-in-out', })); @@ -55,7 +55,7 @@ function SettingsFooter(props: ISettingsFooterProps) { }, []); return ( - <StyledFooter show={props.show}> + <StyledFooter $show={props.show}> <StyledSystemSettingsContainer> <StyledLaunchFooterPrompt> {messages.pgettext( diff --git a/gui/src/renderer/components/Login.tsx b/gui/src/renderer/components/Login.tsx index 07d4814f1b..32eb5bbe82 100644 --- a/gui/src/renderer/components/Login.tsx +++ b/gui/src/renderer/components/Login.tsx @@ -108,7 +108,7 @@ export default class Login extends React.Component<IProps, IState> { {this.createLoginForm()} </StyledLoginForm> - <StyledFooter show={allowInteraction}>{this.createFooter()}</StyledFooter> + <StyledFooter $show={allowInteraction}>{this.createFooter()}</StyledFooter> </Container> </Layout> ); @@ -297,9 +297,9 @@ export default class Login extends React.Component<IProps, IState> { <> <StyledSubtitle data-testid="subtitle">{this.formSubtitle()}</StyledSubtitle> <StyledAccountInputGroup - active={allowInteraction && this.state.isActive} - editable={allowInteraction} - error={hasError} + $active={allowInteraction && this.state.isActive} + $editable={allowInteraction} + $error={hasError} onSubmit={this.onSubmit}> <StyledAccountInputBackdrop> <StyledInput @@ -318,14 +318,14 @@ export default class Login extends React.Component<IProps, IState> { /> <StyledInputButton type="submit" - visible={allowLogin} + $visible={allowLogin} disabled={!allowLogin} aria-label={ // TRANSLATORS: This is used by screenreaders to communicate the login button. messages.pgettext('accessibility', 'Login') }> <StyledInputSubmitIcon - visible={ + $visible={ this.props.loginState.type !== 'logging in' && !this.props.isPerformingPostUpgrade } source="icon-arrow" diff --git a/gui/src/renderer/components/LoginStyles.tsx b/gui/src/renderer/components/LoginStyles.tsx index cff3cdfc67..4a597b07b3 100644 --- a/gui/src/renderer/components/LoginStyles.tsx +++ b/gui/src/renderer/components/LoginStyles.tsx @@ -26,13 +26,13 @@ export const StyledAccountDropdownRemoveIcon = styled(ImageView)({ marginLeft: '0px', }); -export const StyledInputSubmitIcon = styled(ImageView)((props: { visible: boolean }) => ({ +export const StyledInputSubmitIcon = styled(ImageView)<{ $visible: boolean }>((props) => ({ flex: 0, borderWidth: '0px', width: '48px', alignItems: 'center', justifyContent: 'center', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, })); export const StyledAccountDropdownItem = styled.li({ @@ -40,7 +40,7 @@ export const StyledAccountDropdownItem = styled.li({ flex: 1, backgroundColor: colors.white60, cursor: 'default', - ':hover': { + '&&:hover': { backgroundColor: colors.white40, }, }); @@ -51,7 +51,7 @@ export const StyledAccountDropdownItemButton = styled(Cell.CellButton)({ flexDirection: 'row', alignItems: 'stretch', backgroundColor: 'transparent', - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: 'transparent', }, }); @@ -75,11 +75,11 @@ export const StyledTopInfo = styled.div({ flex: 1, }); -export const StyledFooter = styled(Footer)({}, (props: { show: boolean }) => ({ +export const StyledFooter = styled(Footer)<{ $show: boolean }>((props) => ({ position: 'relative', width: '100%', bottom: 0, - transform: `translateY(${props.show ? 0 : 100}%)`, + transform: `translateY(${props.$show ? 0 : 100}%)`, backgroundColor: colors.darkBlue, transition: 'transform 250ms ease-in-out', })); @@ -103,18 +103,18 @@ export const StyledLoginForm = styled.div({ }); interface IStyledAccountInputGroupProps { - editable: boolean; - active: boolean; - error: boolean; + $editable: boolean; + $active: boolean; + $error: boolean; } -export const StyledAccountInputGroup = styled.form((props: IStyledAccountInputGroupProps) => ({ +export const StyledAccountInputGroup = styled.form<IStyledAccountInputGroupProps>((props) => ({ borderWidth: '2px', borderStyle: 'solid', borderRadius: '8px', overflow: 'hidden', - borderColor: props.error ? colors.red40 : props.active ? colors.darkBlue : 'transparent', - opacity: props.editable ? 1 : 0.6, + borderColor: props.$error ? colors.red40 : props.$active ? colors.darkBlue : 'transparent', + opacity: props.$editable ? 1 : 0.6, })); export const StyledAccountInputBackdrop = styled.div({ @@ -123,14 +123,14 @@ export const StyledAccountInputBackdrop = styled.div({ borderColor: colors.darkBlue, }); -export const StyledInputButton = styled.button((props: { visible: boolean }) => ({ +export const StyledInputButton = styled.button<{ $visible: boolean }>((props) => ({ display: 'flex', flex: 0, borderWidth: 0, width: '48px', alignItems: 'center', justifyContent: 'center', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, transition: 'opacity 250ms ease-in-out', backgroundColor: colors.green, })); @@ -165,7 +165,7 @@ export const StyledInput = styled(FormattableTextInput)(largeText, { color: colors.blue, backgroundColor: 'transparent', flex: 1, - '::placeholder': { + '&&::placeholder': { color: colors.blue40, }, }); diff --git a/gui/src/renderer/components/Marquee.tsx b/gui/src/renderer/components/Marquee.tsx index 5175fdc4a0..c654e473d2 100644 --- a/gui/src/renderer/components/Marquee.tsx +++ b/gui/src/renderer/components/Marquee.tsx @@ -7,14 +7,16 @@ const Container = styled.div({ overflow: 'hidden', }); -const Text = styled.span({}, (props: { overflow: number; alignRight: boolean }) => ({ +const Text = styled.span<{ $overflow: number; $alignRight: boolean }>((props) => ({ display: 'inline-block', // Prevents Container from adding 2px below the text. verticalAlign: 'middle', whiteSpace: 'nowrap', - willChange: props.overflow > 0 ? 'transform' : 'auto', - transform: props.alignRight ? `translate3d(${-props.overflow}px, 0, 0)` : 'translate3d(0, 0, 0)', - transition: `transform linear ${props.overflow * 80}ms`, + willChange: props.$overflow > 0 ? 'transform' : 'auto', + transform: props.$alignRight + ? `translate3d(${-props.$overflow}px, 0, 0)` + : 'translate3d(0, 0, 0)', + transition: `transform linear ${props.$overflow * 80}ms`, })); interface IMarqueeProps { @@ -67,8 +69,8 @@ export default class Marquee extends React.Component<IMarqueeProps, IMarqueeStat <Text key={this.state.uniqueKey} ref={this.textRef} - overflow={this.calculateOverflow()} - alignRight={this.state.alignRight} + $overflow={this.calculateOverflow()} + $alignRight={this.state.alignRight} onTransitionEnd={this.scheduleToggleAlignRight} {...otherProps}> {children} diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx index d9be9e767d..e87e2113f2 100644 --- a/gui/src/renderer/components/Modal.tsx +++ b/gui/src/renderer/components/Modal.tsx @@ -25,9 +25,9 @@ const ModalContent = styled.div({ overflow: 'hidden', }); -const ModalBackground = styled.div({}, (props: { visible: boolean }) => ({ - backgroundColor: props.visible ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)', - backdropFilter: props.visible ? 'blur(1.5px)' : '', +const ModalBackground = styled.div<{ $visible: boolean }>((props) => ({ + backgroundColor: props.$visible ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0)', + backdropFilter: props.$visible ? 'blur(1.5px)' : '', position: 'absolute', display: 'flex', flexDirection: 'column', @@ -37,7 +37,7 @@ const ModalBackground = styled.div({}, (props: { visible: boolean }) => ({ right: 0, bottom: 0, transition: 'background-color 150ms ease-out', - pointerEvents: props.visible ? 'auto' : 'none', + pointerEvents: props.$visible ? 'auto' : 'none', zIndex: 2, })); @@ -111,11 +111,11 @@ const ModalAlertContainer = styled.div({ padding: '14px', }); -const StyledModalAlert = styled.div({}, (props: { visible: boolean; closing: boolean }) => { +const StyledModalAlert = styled.div<{ $visible: boolean; $closing: boolean }>((props) => { let transform = ''; - if (props.visible && props.closing) { + if (props.$visible && props.$closing) { transform = 'scale(80%)'; - } else if (!props.visible) { + } else if (!props.$visible) { transform = 'translateY(10px) scale(98%)'; } @@ -126,7 +126,7 @@ const StyledModalAlert = styled.div({}, (props: { visible: boolean; closing: boo borderRadius: '11px', padding: '16px 0 16px 16px', maxHeight: '80vh', - opacity: props.visible && !props.closing ? 1 : 0, + opacity: props.$visible && !props.$closing ? 1 : 0, transform, boxShadow: ' 0px 15px 35px 5px rgba(0,0,0,0.5)', transition: 'all 150ms ease-out', @@ -254,15 +254,15 @@ class ModalAlertImpl extends React.Component<IModalAlertImplProps, IModalAlertSt private renderModal() { return ( <BackAction action={this.close}> - <ModalBackground visible={this.state.visible && !this.props.closing}> + <ModalBackground $visible={this.state.visible && !this.props.closing}> <ModalAlertContainer> <StyledModalAlert ref={this.modalRef} tabIndex={-1} role="dialog" aria-modal - visible={this.state.visible} - closing={this.props.closing} + $visible={this.state.visible} + $closing={this.props.closing} onTransitionEnd={this.onTransitionEnd}> <StyledCustomScrollbars> {this.props.type && ( diff --git a/gui/src/renderer/components/NavigationBar.tsx b/gui/src/renderer/components/NavigationBar.tsx index 56c3d2b8a3..d375fc34d6 100644 --- a/gui/src/renderer/components/NavigationBar.tsx +++ b/gui/src/renderer/components/NavigationBar.tsx @@ -180,7 +180,7 @@ interface ITitleBarItemProps { export const TitleBarItem = React.memo(function TitleBarItemT(props: ITitleBarItemProps) { const { visible } = useContext(TitleBarItemContext); - return <StyledTitleBarItemLabel visible={visible}>{props.children}</StyledTitleBarItemLabel>; + return <StyledTitleBarItemLabel $visible={visible}>{props.children}</StyledTitleBarItemLabel>; }); export function BackBarItem() { diff --git a/gui/src/renderer/components/NavigationBarStyles.tsx b/gui/src/renderer/components/NavigationBarStyles.tsx index d773c868d8..eb4472c900 100644 --- a/gui/src/renderer/components/NavigationBarStyles.tsx +++ b/gui/src/renderer/components/NavigationBarStyles.tsx @@ -25,7 +25,7 @@ export const StyledNavigationBar = styled.nav({ padding: '12px', }); -export const StyledTitleBarItemLabel = styled.h1(normalText, (props: { visible?: boolean }) => ({ +export const StyledTitleBarItemLabel = styled.h1<{ $visible?: boolean }>(normalText, (props) => ({ fontWeight: 400, lineHeight: '22px', color: colors.white, @@ -33,7 +33,7 @@ export const StyledTitleBarItemLabel = styled.h1(normalText, (props: { visible?: overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - opacity: props.visible ? 1 : 0, + opacity: props.$visible ? 1 : 0, transition: 'opacity 250ms ease-in-out', })); @@ -51,7 +51,7 @@ export const StyledBackBarItemButton = styled.button({ export const StyledBackBarItemIcon = styled(ImageView)({ marginRight: '8px', - [StyledBackBarItemButton + ':hover &']: { + [StyledBackBarItemButton + ':hover &&']: { backgroundColor: colors.white60, }, }); diff --git a/gui/src/renderer/components/NotificationArea.tsx b/gui/src/renderer/components/NotificationArea.tsx index 79fb3ef17c..9bce47eea6 100644 --- a/gui/src/renderer/components/NotificationArea.tsx +++ b/gui/src/renderer/components/NotificationArea.tsx @@ -97,7 +97,7 @@ export default function NotificationArea(props: IProps) { if (notification) { return ( <NotificationBanner className={props.className}> - <NotificationIndicator type={notification.indicator} /> + <NotificationIndicator $type={notification.indicator} /> <NotificationContent role="status" aria-live="polite"> <NotificationTitle>{notification.title}</NotificationTitle> <NotificationSubtitle>{formatHtml(notification.subtitle ?? '')}</NotificationSubtitle> diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx index 0c63d3ba2d..3b66abb204 100644 --- a/gui/src/renderer/components/NotificationBanner.tsx +++ b/gui/src/renderer/components/NotificationBanner.tsx @@ -105,7 +105,7 @@ export const NotificationActions = styled.div({ }); interface INotificationIndicatorProps { - type?: InAppNotificationIndicatorType; + $type?: InAppNotificationIndicatorType; } const notificationIndicatorTypeColorMap = { @@ -114,29 +114,29 @@ const notificationIndicatorTypeColorMap = { error: colors.red, }; -export const NotificationIndicator = styled.div((props: INotificationIndicatorProps) => ({ +export const NotificationIndicator = styled.div<INotificationIndicatorProps>((props) => ({ width: '10px', height: '10px', borderRadius: '5px', marginTop: '4px', marginRight: '8px', - backgroundColor: props.type ? notificationIndicatorTypeColorMap[props.type] : 'transparent', + backgroundColor: props.$type ? notificationIndicatorTypeColorMap[props.$type] : 'transparent', })); interface ICollapsibleProps { - alignBottom: boolean; - height?: number; + $alignBottom: boolean; + $height?: number; } -const Collapsible = styled.div({}, (props: ICollapsibleProps) => { +const Collapsible = styled.div<ICollapsibleProps>((props) => { return { display: 'flex', flexDirection: 'column', - justifyContent: props.alignBottom ? 'flex-end' : 'flex-start', + justifyContent: props.$alignBottom ? 'flex-end' : 'flex-start', backgroundColor: 'rgba(25, 38, 56, 0.95)', overflow: 'hidden', // Using auto as the initial value prevents transition if a notification is visible on mount. - height: props.height === undefined ? 'auto' : `${props.height}px`, + height: props.$height === undefined ? 'auto' : `${props.$height}px`, transition: 'height 250ms ease-in-out', }; }); @@ -175,7 +175,7 @@ export function NotificationBanner(props: INotificationBannerProps) { }); return ( - <Collapsible height={contentHeight} className={props.className} alignBottom={alignBottom}> + <Collapsible $height={contentHeight} className={props.className} $alignBottom={alignBottom}> <Content ref={contentRef}>{props.children ?? prevChildren.current}</Content> </Collapsible> ); diff --git a/gui/src/renderer/components/RedeemVoucherStyles.tsx b/gui/src/renderer/components/RedeemVoucherStyles.tsx index 2ef0cc2634..37f51ee5e4 100644 --- a/gui/src/renderer/components/RedeemVoucherStyles.tsx +++ b/gui/src/renderer/components/RedeemVoucherStyles.tsx @@ -20,7 +20,7 @@ export const StyledInput = styled(FormattableTextInput)(normalText, { backgroundColor: colors.white, border: 'none', borderRadius: '4px', - '::placeholder': { + '&&::placeholder': { color: colors.blue40, }, }); diff --git a/gui/src/renderer/components/RelayStatusIndicator.tsx b/gui/src/renderer/components/RelayStatusIndicator.tsx index 7d90e1491d..549fec4e70 100644 --- a/gui/src/renderer/components/RelayStatusIndicator.tsx +++ b/gui/src/renderer/components/RelayStatusIndicator.tsx @@ -3,12 +3,12 @@ import styled from 'styled-components'; import { colors } from '../../config.json'; import * as Cell from './cell'; -const StyledRelayStatus = styled.div((props: { active: boolean }) => ({ +const StyledRelayStatus = styled.div<{ $active: boolean }>((props) => ({ width: '16px', height: '16px', borderRadius: '8px', margin: '0 12px 0 4px', - backgroundColor: props.active ? colors.green90 : colors.red95, + backgroundColor: props.$active ? colors.green90 : colors.red95, })); const TickIcon = styled(Cell.Icon)({ @@ -25,6 +25,6 @@ export default function RelayStatusIndicator(props: IProps) { return props.selected ? ( <TickIcon tintColor={colors.white} source="icon-tick" width={18} /> ) : ( - <StyledRelayStatus active={props.active} /> + <StyledRelayStatus $active={props.active} /> ); } diff --git a/gui/src/renderer/components/SearchBar.tsx b/gui/src/renderer/components/SearchBar.tsx index fbe7d3573a..e21439fbbf 100644 --- a/gui/src/renderer/components/SearchBar.tsx +++ b/gui/src/renderer/components/SearchBar.tsx @@ -22,15 +22,15 @@ export const StyledSearchInput = styled.input.attrs({ type: 'text' })({ lineHeight: '24px', color: colors.white60, backgroundColor: colors.white10, - '::placeholder': { + '&&::placeholder': { color: colors.white60, }, - ':focus': { + '&&:focus': { color: colors.blue, backgroundColor: colors.white, - '::placeholder': { - color: colors.blue40, - }, + }, + '&&:focus::placeholder': { + color: colors.blue40, }, }); @@ -49,20 +49,20 @@ export const StyledSearchIcon = styled(ImageView)({ top: '50%', transform: 'translateY(-50%)', left: '9px', - [`${StyledSearchInput}:focus ~ &`]: { + [`${StyledSearchInput}:focus ~ &&`]: { backgroundColor: colors.blue, }, }); export const StyledClearIcon = styled(ImageView)({ - ':hover': { + '&&:hover': { backgroundColor: colors.white60, }, - [`${StyledSearchInput}:focus ~ ${StyledClearButton} &`]: { + [`${StyledSearchInput}:focus ~ ${StyledClearButton} &&`]: { backgroundColor: colors.blue40, - ':hover': { - backgroundColor: colors.blue, - }, + }, + [`${StyledSearchInput}:focus ~ ${StyledClearButton} &&:hover`]: { + backgroundColor: colors.blue, }, }); diff --git a/gui/src/renderer/components/SecuredLabel.tsx b/gui/src/renderer/components/SecuredLabel.tsx index 736297b480..534a01a58c 100644 --- a/gui/src/renderer/components/SecuredLabel.tsx +++ b/gui/src/renderer/components/SecuredLabel.tsx @@ -25,10 +25,10 @@ const securedDisplayStyleColorMap = { [SecuredDisplayStyle.failedToSecure]: colors.red, }; -const StyledSecuredLabel = styled.span((props: { displayStyle: SecuredDisplayStyle }) => ({ +const StyledSecuredLabel = styled.span<{ $displayStyle: SecuredDisplayStyle }>((props) => ({ display: 'inline-block', minHeight: '22px', - color: securedDisplayStyleColorMap[props.displayStyle], + color: securedDisplayStyleColorMap[props.$displayStyle], })); interface ISecuredLabelProps { @@ -37,8 +37,13 @@ interface ISecuredLabelProps { } export default function SecuredLabel(props: ISecuredLabelProps) { + const { displayStyle, ...otherProps } = props; return ( - <StyledSecuredLabel {...props} role="status" aria-live="polite"> + <StyledSecuredLabel + $displayStyle={displayStyle} + {...otherProps} + role="status" + aria-live="polite"> {getLabelText(props.displayStyle)} </StyledSecuredLabel> ); diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx index b97243a456..eb0ad038da 100644 --- a/gui/src/renderer/components/SelectLanguage.tsx +++ b/gui/src/renderer/components/SelectLanguage.tsx @@ -21,7 +21,7 @@ import SettingsHeader, { HeaderTitle } from './SettingsHeader'; const StyledSelector = styled(Selector)({ marginBottom: 0, -}) as typeof Selector; +}); export default function SelectLanguage() { const history = useHistory(); diff --git a/gui/src/renderer/components/SettingsHeader.tsx b/gui/src/renderer/components/SettingsHeader.tsx index 8724ea93d9..47e1f47f7b 100644 --- a/gui/src/renderer/components/SettingsHeader.tsx +++ b/gui/src/renderer/components/SettingsHeader.tsx @@ -13,7 +13,7 @@ export const Container = styled.div({ }); export const ContentWrapper = styled.div({ - ':not(:first-child)': { + '&&:not(:first-child)': { paddingTop: '8px', }, }); diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx index aa1f82b2fc..cb4de70deb 100644 --- a/gui/src/renderer/components/SplitTunnelingSettings.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx @@ -55,7 +55,7 @@ export default function SplitTunneling() { return ( <> - <StyledPageCover show={browsing} /> + <StyledPageCover $show={browsing} /> <BackAction action={pop}> <Layout> <SettingsContainer> @@ -272,18 +272,18 @@ function LinuxApplicationRow(props: ILinuxApplicationRowProps) { <> <StyledCellButton onClick={props.application.warning ? showWarningDialog : launch} - lookDisabled={disabled}> + $lookDisabled={disabled}> {props.application.icon ? ( <StyledIcon source={props.application.icon} width={35} height={35} - lookDisabled={disabled} + $lookDisabled={disabled} /> ) : ( <StyledIconPlaceholder /> )} - <StyledCellLabel lookDisabled={disabled}>{props.application.name}</StyledCellLabel> + <StyledCellLabel $lookDisabled={disabled}>{props.application.name}</StyledCellLabel> {props.application.warning && ( <StyledCellWarningIcon source="icon-alert" tintColor={warningColor} width={18} /> )} diff --git a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx index 61a1606b2e..1aea5108a1 100644 --- a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx +++ b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx @@ -9,7 +9,7 @@ import { NavigationScrollbars } from './NavigationBar'; import SearchBar from './SearchBar'; import { HeaderTitle } from './SettingsHeader'; -export const StyledPageCover = styled.div({}, (props: { show: boolean }) => ({ +export const StyledPageCover = styled.div<{ $show: boolean }>((props) => ({ position: 'absolute', zIndex: 2, top: 0, @@ -18,7 +18,7 @@ export const StyledPageCover = styled.div({}, (props: { show: boolean }) => ({ bottom: 0, backgroundColor: colors.black, opacity: 0.5, - display: props.show ? 'block' : 'none', + display: props.$show ? 'block' : 'none', })); export const StyledNavigationScrollbars = styled(NavigationScrollbars)({ @@ -31,17 +31,21 @@ export const StyledContent = styled.div({ flex: 1, }); -export const StyledCellButton = styled(Cell.CellButton)((props: { lookDisabled?: boolean }) => ({ - ':not(:disabled):hover': { - backgroundColor: props.lookDisabled ? colors.blue : undefined, +export const StyledCellButton = styled(Cell.CellButton)<{ $lookDisabled?: boolean }>((props) => ({ + '&&:not(:disabled):hover': { + backgroundColor: props.$lookDisabled ? colors.blue : undefined, }, })); -const disabledApplication = (props: { lookDisabled?: boolean }) => ({ - opacity: props.lookDisabled ? 0.6 : undefined, +interface DisabledApplicationProps { + $lookDisabled?: boolean; +} + +const disabledApplication = (props: DisabledApplicationProps) => ({ + opacity: props.$lookDisabled ? 0.6 : undefined, }); -export const StyledIcon = styled(Cell.UntintedIcon)(disabledApplication, { +export const StyledIcon = styled(Cell.UntintedIcon)<DisabledApplicationProps>(disabledApplication, { marginRight: '12px', }); @@ -54,11 +58,15 @@ export const StyledCellWarningIcon = styled(Cell.Icon)({ marginRight: '3px', }); -export const StyledCellLabel = styled(Cell.Label)(disabledApplication, normalText, { - fontWeight: 400, - wordWrap: 'break-word', - overflow: 'hidden', -}); +export const StyledCellLabel = styled(Cell.Label)<DisabledApplicationProps>( + disabledApplication, + normalText, + { + fontWeight: 400, + wordWrap: 'break-word', + overflow: 'hidden', + }, +); export const StyledIconPlaceholder = styled.div({ width: '35px', diff --git a/gui/src/renderer/components/Switch.tsx b/gui/src/renderer/components/Switch.tsx index cb9e86b219..e5aaec4bb0 100644 --- a/gui/src/renderer/components/Switch.tsx +++ b/gui/src/renderer/components/Switch.tsx @@ -14,7 +14,7 @@ interface IProps { innerRef?: React.Ref<HTMLDivElement>; } -const SwitchContainer = styled.div({}, (props: { disabled: boolean }) => ({ +const SwitchContainer = styled.div<{ disabled: boolean }>((props) => ({ position: 'relative', width: '48px', height: '30px', @@ -25,10 +25,10 @@ const SwitchContainer = styled.div({}, (props: { disabled: boolean }) => ({ padding: '2px', })); -const Knob = styled.div({}, (props: { isOn: boolean; disabled: boolean }) => { - let backgroundColor = props.isOn ? colors.green : colors.red; +const Knob = styled.div<{ $isOn: boolean; disabled: boolean }>((props) => { + let backgroundColor = props.$isOn ? colors.green : colors.red; if (props.disabled) { - backgroundColor = props.isOn ? colors.green40 : colors.red40; + backgroundColor = props.$isOn ? colors.green40 : colors.red40; } return { @@ -40,7 +40,7 @@ const Knob = styled.div({}, (props: { isOn: boolean; disabled: boolean }) => { backgroundColor, // When enabled the button should be placed all the way to the right (100%) minus padding (2px) // minus it's own width (22px). - left: props.isOn ? 'calc(100% - 2px - 22px)' : '2px', + left: props.$isOn ? 'calc(100% - 2px - 22px)' : '2px', }; }); @@ -59,7 +59,7 @@ export default class Switch extends React.PureComponent<IProps> { aria-disabled={this.props.disabled ?? false} tabIndex={-1} className={this.props.className}> - <Knob disabled={this.props.disabled ?? false} isOn={this.props.isOn} /> + <Knob disabled={this.props.disabled ?? false} $isOn={this.props.isOn} /> </SwitchContainer> ); } diff --git a/gui/src/renderer/components/TransitionContainer.tsx b/gui/src/renderer/components/TransitionContainer.tsx index 56315d5dcb..b9a60185ed 100644 --- a/gui/src/renderer/components/TransitionContainer.tsx +++ b/gui/src/renderer/components/TransitionContainer.tsx @@ -41,29 +41,36 @@ interface IState { export const StyledTransitionContainer = styled.div({ flex: 1 }); -export const StyledTransitionContent = styled.div.attrs({ 'data-testid': 'transition-content' })( - {}, - (props: { transition?: IItemStyle; disableUserInteraction?: boolean }) => { - const x = `${props.transition?.x ?? 0}%`; - const y = `${props.transition?.y ?? 0}%`; - const duration = props.transition?.duration ?? 450; +interface StyledTransitionContentProps { + $transition?: IItemStyle; + $disableUserInteraction?: boolean; +} - return { - display: 'flex', - flexDirection: 'column', - position: 'absolute', - left: 0, - right: 0, - top: 0, - bottom: 0, - zIndex: props.transition?.inFront ? 1 : 0, - willChange: 'transform', - transform: `translate3d(${x}, ${y}, 0)`, - transition: `transform ${duration}ms ease-in-out`, - pointerEvents: props.disableUserInteraction ? 'none' : undefined, - }; - }, -); +export const StyledTransitionContent = styled.div.attrs< + StyledTransitionContentProps, + { 'data-testid': string } +>({ + 'data-testid': 'transition-content', +})((props) => { + const x = `${props.$transition?.x ?? 0}%`; + const y = `${props.$transition?.y ?? 0}%`; + const duration = props.$transition?.duration ?? 450; + + return { + display: 'flex', + flexDirection: 'column', + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + zIndex: props.$transition?.inFront ? 1 : 0, + willChange: 'transform', + transform: `translate3d(${x}, ${y}, 0)`, + transition: `transform ${duration}ms ease-in-out`, + pointerEvents: props.$disableUserInteraction ? 'none' : undefined, + }; +}); export const StyledTransitionView = styled.div({ display: 'flex', @@ -132,9 +139,9 @@ export default class TransitionContainer extends React.Component<IProps, IState> <WillExit key={this.state.currentItem.view.props.routePath} value={willExit}> <StyledTransitionContent ref={this.setCurrentContentRef} - transition={this.state.currentItemStyle} + $transition={this.state.currentItemStyle} onTransitionEnd={this.onTransitionEnd} - disableUserInteraction={willExit}> + $disableUserInteraction={willExit}> {this.state.currentItem.view} </StyledTransitionContent> </WillExit> @@ -144,7 +151,7 @@ export default class TransitionContainer extends React.Component<IProps, IState> <WillExit key={this.state.nextItem.view.props.routePath} value={false}> <StyledTransitionContent ref={this.setNextContentRef} - transition={this.state.nextItemStyle} + $transition={this.state.nextItemStyle} onTransitionEnd={this.onTransitionEnd}> {this.state.nextItem.view} </StyledTransitionContent> diff --git a/gui/src/renderer/components/cell/CellButton.tsx b/gui/src/renderer/components/cell/CellButton.tsx index 0111574f70..b0079226f3 100644 --- a/gui/src/renderer/components/cell/CellButton.tsx +++ b/gui/src/renderer/components/cell/CellButton.tsx @@ -8,17 +8,17 @@ import { Row } from './Row'; import { CellSectionContext } from './Section'; interface IStyledCellButtonProps extends React.HTMLAttributes<HTMLButtonElement> { - selected?: boolean; - containedInSection: boolean; + $selected?: boolean; + $containedInSection: boolean; } -const StyledCellButton = styled(Row)({}, (props: IStyledCellButtonProps) => { - const backgroundColor = props.selected +const StyledCellButton = styled(Row)<IStyledCellButtonProps>((props) => { + const backgroundColor = props.$selected ? colors.green - : props.containedInSection + : props.$containedInSection ? colors.blue40 : colors.blue; - const backgroundColorHover = props.selected ? colors.green : colors.blue80; + const backgroundColorHover = props.$selected ? colors.green : colors.blue80; return { paddingRight: '16px', @@ -27,7 +27,7 @@ const StyledCellButton = styled(Row)({}, (props: IStyledCellButtonProps) => { cursor: 'default', border: 'none', backgroundColor, - ':not(:disabled):hover': { + '&&:not(:disabled):hover': { backgroundColor: props.onClick ? backgroundColorHover : backgroundColor, }, }; @@ -39,14 +39,16 @@ interface ICellButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> export const CellButton = styled( React.forwardRef(function Button(props: ICellButtonProps, ref: React.Ref<HTMLButtonElement>) { + const { selected, ...otherProps } = props; const containedInSection = useContext(CellSectionContext); return ( <CellDisabledContext.Provider value={props.disabled ?? false}> <StyledCellButton as="button" ref={ref} - containedInSection={containedInSection} - {...props} + $selected={selected} + $containedInSection={containedInSection} + {...otherProps} /> </CellDisabledContext.Provider> ); diff --git a/gui/src/renderer/components/cell/Group.tsx b/gui/src/renderer/components/cell/Group.tsx index a3198cabb0..86d95f0a33 100644 --- a/gui/src/renderer/components/cell/Group.tsx +++ b/gui/src/renderer/components/cell/Group.tsx @@ -3,12 +3,12 @@ import styled from 'styled-components'; import { measurements } from '../common-styles'; interface IStyledGroupProps { - noMarginBottom?: boolean; + $noMarginBottom?: boolean; } -export const Group = styled.div({}, (props: IStyledGroupProps) => ({ +export const Group = styled.div<IStyledGroupProps>((props) => ({ display: 'flex', flexDirection: 'column', flex: 1, - marginBottom: props.noMarginBottom ? '0px' : measurements.rowVerticalMargin, + marginBottom: props.$noMarginBottom ? '0px' : measurements.rowVerticalMargin, })); diff --git a/gui/src/renderer/components/cell/Input.tsx b/gui/src/renderer/components/cell/Input.tsx index 55260f6703..97fe01a185 100644 --- a/gui/src/renderer/components/cell/Input.tsx +++ b/gui/src/renderer/components/cell/Input.tsx @@ -24,15 +24,15 @@ const inputTextStyles: React.CSSProperties = { padding: '0px', }; -const StyledInput = styled.input({}, (props: { focused: boolean; valid?: boolean }) => ({ +const StyledInput = styled.input<{ $focused: boolean; $valid?: boolean }>((props) => ({ ...inputTextStyles, backgroundColor: 'transparent', border: 'none', width: '100%', height: '100%', - color: props.valid === false ? colors.red : props.focused ? colors.blue : colors.white, - '::placeholder': { - color: props.focused ? colors.blue60 : colors.white60, + color: props.$valid === false ? colors.red : props.$focused ? colors.blue : colors.white, + '&&::placeholder': { + color: props.$focused ? colors.blue60 : colors.white60, }, })); @@ -150,8 +150,8 @@ function InputWithRef(props: IInputProps, forwardedRef: React.Ref<HTMLInputEleme {...otherProps} ref={combinedRef} type="text" - valid={valid} - focused={isFocused} + $valid={valid} + $focused={isFocused} aria-invalid={!valid} onChange={onChange} onFocus={onFocus} @@ -167,10 +167,10 @@ function InputWithRef(props: IInputProps, forwardedRef: React.Ref<HTMLInputEleme export const Input = React.memo(React.forwardRef(InputWithRef)); -const InputFrame = styled.div((props: { focused: boolean }) => ({ +const InputFrame = styled.div<{ $focused: boolean }>((props) => ({ display: 'flex', flexGrow: 0, - backgroundColor: props.focused ? colors.white : 'rgba(255,255,255,0.1)', + backgroundColor: props.$focused ? colors.white : 'rgba(255,255,255,0.1)', borderRadius: '4px', padding: '6px 8px', })); @@ -222,7 +222,7 @@ function AutoSizingTextInputWithRef(props: IInputProps, forwardedRef: React.Ref< return ( <BackAction disabled={!focused} action={blur}> - <InputFrame focused={focused}> + <InputFrame $focused={focused}> <StyledAutoSizingTextInputContainer> <StyledAutoSizingTextInputWrapper> <Input @@ -254,11 +254,11 @@ const StyledSubmitButton = styled.button({ padding: '10px 0', }); -const StyledInputWrapper = styled.div(normalText, (props: { marginLeft: number }) => ({ +const StyledInputWrapper = styled.div<{ $marginLeft: number }>(normalText, (props) => ({ position: 'relative', flex: 1, width: '171px', - marginLeft: props.marginLeft + 'px', + marginLeft: props.$marginLeft + 'px', lineHeight: '24px', minHeight: '24px', fontWeight: 400, @@ -266,7 +266,7 @@ const StyledInputWrapper = styled.div(normalText, (props: { marginLeft: number } maxWidth: '100%', })); -const StyledTextArea = styled.textarea(normalText, (props: { invalid?: boolean }) => ({ +const StyledTextArea = styled.textarea<{ $invalid?: boolean }>(normalText, (props) => ({ position: 'absolute', top: 0, left: 0, @@ -279,7 +279,7 @@ const StyledTextArea = styled.textarea(normalText, (props: { invalid?: boolean } fontWeight: 400, resize: 'none', padding: '10px 25px 10px 0', - color: props.invalid ? colors.red : 'auto', + color: props.$invalid ? colors.red : 'auto', })); const StyledInputFiller = styled.div({ @@ -366,7 +366,7 @@ export function RowInput(props: IRowInputProps) { return ( <BackAction disabled={!focused} action={blur}> <StyledCellInputRowContainer> - <StyledInputWrapper marginLeft={props.paddingLeft ?? 0}> + <StyledInputWrapper $marginLeft={props.paddingLeft ?? 0}> <StyledInputFiller>{value}</StyledInputFiller> <StyledTextArea ref={textAreaRef} @@ -374,7 +374,7 @@ export function RowInput(props: IRowInputProps) { onKeyDown={onKeyDown} rows={1} value={value} - invalid={props.invalid} + $invalid={props.invalid} onFocus={onFocus} onBlur={onBlur} placeholder={props.placeholder} diff --git a/gui/src/renderer/components/cell/Label.tsx b/gui/src/renderer/components/cell/Label.tsx index f3d3624d45..5e9963ded9 100644 --- a/gui/src/renderer/components/cell/Label.tsx +++ b/gui/src/renderer/components/cell/Label.tsx @@ -7,14 +7,14 @@ import ImageView, { IImageViewProps } from '../ImageView'; import { CellButton } from './CellButton'; import { CellDisabledContext } from './Container'; -const StyledLabel = styled.div(buttonText, (props: { disabled: boolean }) => ({ +const StyledLabel = styled.div<{ disabled: boolean }>(buttonText, (props) => ({ margin: '10px 0', flex: 1, color: props.disabled ? colors.white40 : colors.white, textAlign: 'left', })); -const StyledSubText = styled.span(tinyText, (props: { disabled: boolean }) => ({ +const StyledSubText = styled.span<{ disabled: boolean }>(tinyText, (props) => ({ color: props.disabled ? colors.white20 : colors.white60, flex: -1, textAlign: 'right', @@ -22,7 +22,7 @@ const StyledSubText = styled.span(tinyText, (props: { disabled: boolean }) => ({ marginRight: '8px', })); -const StyledIconContainer = styled.div((props: { disabled: boolean }) => ({ +const StyledIconContainer = styled.div<{ disabled: boolean }>((props) => ({ opacity: props.disabled ? 0.4 : 1, })); @@ -30,7 +30,7 @@ const StyledTintedIcon = styled(ImageView).attrs((props: IImageViewProps) => ({ tintColor: props.tintColor ?? colors.white60, tintHoverColor: props.tintHoverColor ?? props.tintColor ?? colors.white60, }))((props: IImageViewProps) => ({ - ':hover': { + '&&:hover': { backgroundColor: props.tintColor, }, [`${CellButton}:not(:disabled):hover &&`]: { diff --git a/gui/src/renderer/components/cell/Row.tsx b/gui/src/renderer/components/cell/Row.tsx index 08309d6843..9aca25d3a0 100644 --- a/gui/src/renderer/components/cell/Row.tsx +++ b/gui/src/renderer/components/cell/Row.tsx @@ -1,10 +1,17 @@ +import React from 'react'; import styled from 'styled-components'; import { colors } from '../../../config.json'; import { measurements } from '../common-styles'; import { Group } from './Group'; -export const Row = styled.div((props: { includeMarginBottomOnLast?: boolean }) => ({ +interface RowProps extends React.HTMLAttributes<HTMLDivElement> { + includeMarginBottomOnLast?: boolean; +} + +export const Row = styled.div.withConfig({ + shouldForwardProp: (prop) => prop !== 'includeMarginBottomOnLast', +})<RowProps>((props) => ({ display: 'flex', alignItems: 'center', backgroundColor: colors.blue, @@ -12,7 +19,7 @@ export const Row = styled.div((props: { includeMarginBottomOnLast?: boolean }) = paddingLeft: measurements.viewMargin, paddingRight: measurements.viewMargin, marginBottom: '1px', - [`${Group} > &:last-child`]: { + [`${Group} > &&:last-child`]: { marginBottom: props.includeMarginBottomOnLast ? '1px' : '0px', }, })); diff --git a/gui/src/renderer/components/cell/Section.tsx b/gui/src/renderer/components/cell/Section.tsx index 8ba5975476..056aa8ceac 100644 --- a/gui/src/renderer/components/cell/Section.tsx +++ b/gui/src/renderer/components/cell/Section.tsx @@ -18,15 +18,15 @@ const StyledSection = styled.div({ interface SectionTitleProps { disabled?: boolean; - thin?: boolean; + $thin?: boolean; } -export const SectionTitle = styled(Row)(buttonText, (props: SectionTitleProps) => ({ +export const SectionTitle = styled(Row)<SectionTitleProps>(buttonText, (props) => ({ paddingRight: '16px', color: props.disabled ? colors.white20 : colors.white, - fontWeight: props.thin ? 400 : 600, - fontSize: props.thin ? '15px' : '18px', - ...(props.thin ? openSans : sourceSansPro), + fontWeight: props.$thin ? 400 : 600, + fontSize: props.$thin ? '15px' : '18px', + ...(props.$thin ? openSans : sourceSansPro), })); export const CellSectionContext = React.createContext<boolean>(false); diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx index 0842ce2c0e..f1d9e14360 100644 --- a/gui/src/renderer/components/cell/Selector.tsx +++ b/gui/src/renderer/components/cell/Selector.tsx @@ -77,7 +77,7 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { const title = props.title ? ( <> <AriaLabel> - <StyledTitleLabel as="label" disabled={props.disabled} thin={props.thinTitle}> + <StyledTitleLabel as="label" disabled={props.disabled} $thin={props.thinTitle}> {props.title} </StyledTitleLabel> </AriaLabel> @@ -91,7 +91,7 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { // Add potential additional items to the list. Used for custom entry. const children = ( - <Cell.Group noMarginBottom> + <Cell.Group $noMarginBottom> {items} {props.children} </Cell.Group> @@ -121,8 +121,8 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) { } } -const StyledCellIcon = styled(Cell.Icon)((props: { visible: boolean }) => ({ - opacity: props.visible ? 1 : 0, +const StyledCellIcon = styled(Cell.Icon)<{ $visible: boolean }>((props) => ({ + opacity: props.$visible ? 1 : 0, marginRight: '8px', })); @@ -156,7 +156,7 @@ function SelectorCell<T>(props: SelectorCellProps<T>) { aria-selected={props.isSelected} aria-disabled={props.disabled}> <StyledCellIcon - visible={props.isSelected} + $visible={props.isSelected} source="icon-tick" width={18} tintColor={colors.white} @@ -170,9 +170,9 @@ interface StyledCustomContainerProps { selected: boolean; } -const StyledCustomContainer = styled(Cell.Container)((props: StyledCustomContainerProps) => ({ +const StyledCustomContainer = styled(Cell.Container)<StyledCustomContainerProps>((props) => ({ backgroundColor: props.selected ? colors.green : colors.blue40, - ':hover': { + '&&:hover': { backgroundColor: props.selected ? colors.green : colors.blue, }, })); @@ -286,7 +286,7 @@ export function SelectorWithCustomItem<T, U>(props: SelectorWithCustomItemProps< aria-selected={customIsSelected} aria-disabled={props.disabled}> <StyledCellIcon - visible={customIsSelected} + $visible={customIsSelected} source="icon-tick" width={18} tintColor={colors.white} diff --git a/gui/src/renderer/components/select-location/CustomLists.tsx b/gui/src/renderer/components/select-location/CustomLists.tsx index 358070ae46..7fb66f630e 100644 --- a/gui/src/renderer/components/select-location/CustomLists.tsx +++ b/gui/src/renderer/components/select-location/CustomLists.tsx @@ -57,8 +57,8 @@ const StyledSideButtonIcon = styled(Cell.Icon)({ }, }); -const StyledInput = styled(SimpleInput)((props: { error: boolean }) => ({ - color: props.error ? colors.red : 'auto', +const StyledInput = styled(SimpleInput)<{ $error: boolean }>((props) => ({ + color: props.$error ? colors.red : 'auto', })); interface CustomListsProps { @@ -93,8 +93,8 @@ export default function CustomLists(props: CustomListsProps) { {messages.pgettext('select-location-view', 'Custom lists')} </StyledHeaderLabel> <StyledCellButton - backgroundColor={colors.blue} - backgroundColorHover={colors.blue80} + $backgroundColor={colors.blue} + $backgroundColorHover={colors.blue80} onClick={showAddList}> <StyledSideButtonIcon source="icon-add" tintColor={colors.white60} width={18} /> </StyledCellButton> @@ -172,14 +172,14 @@ function AddListForm(props: AddListFormProps) { onSubmitValue={createList} onBlur={onBlur} maxLength={30} - error={error} + $error={error} autoFocus /> </StyledInputContainer> <StyledAddListCellButton - backgroundColor={colors.blue} - backgroundColorHover={colors.blue80} + $backgroundColor={colors.blue} + $backgroundColorHover={colors.blue80} onClick={createList}> <StyledSideButtonIcon source="icon-check" tintColor={colors.white60} width={18} /> </StyledAddListCellButton> diff --git a/gui/src/renderer/components/select-location/LocationRow.tsx b/gui/src/renderer/components/select-location/LocationRow.tsx index 7ebae688a6..97c8528fa9 100644 --- a/gui/src/renderer/components/select-location/LocationRow.tsx +++ b/gui/src/renderer/components/select-location/LocationRow.tsx @@ -29,15 +29,15 @@ import { } from './select-location-types'; interface IButtonColorProps { - backgroundColor: string; - backgroundColorHover: string; + $backgroundColor: string; + $backgroundColorHover: string; } const buttonColor = (props: IButtonColorProps) => { return { - backgroundColor: props.backgroundColor, - ':not(:disabled):hover': { - backgroundColor: props.backgroundColorHover, + backgroundColor: props.$backgroundColor, + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColorHover, }, }; }; @@ -48,10 +48,10 @@ export const StyledLocationRowContainer = styled(Cell.Container)({ background: 'none', }); -export const StyledLocationRowButton = styled(Cell.Row)( +export const StyledLocationRowButton = styled(Cell.Row)<IButtonColorProps & { $level: number }>( buttonColor, - (props: IButtonColorProps & { level: number }) => { - const paddingLeft = (props.level + 1) * 16 + 2; + (props) => { + const paddingLeft = (props.$level + 1) * 16 + 2; return { display: 'flex', @@ -64,13 +64,13 @@ export const StyledLocationRowButton = styled(Cell.Row)( }, ); -export const StyledLocationRowIcon = styled.button(buttonColor, { +export const StyledLocationRowIcon = styled.button<IButtonColorProps>(buttonColor, { position: 'relative', alignSelf: 'stretch', paddingLeft: measurements.viewMargin, paddingRight: measurements.viewMargin, - '&::before': { + '&&::before': { content: '""', position: 'absolute', margin: 'auto', @@ -93,26 +93,26 @@ export const StyledLocationRowLabel = styled(Cell.Label)(normalText, { whiteSpace: 'nowrap', }); -const StyledHoverIconButton = styled.button( +const StyledHoverIconButton = styled.button<IButtonColorProps & { $isLast?: boolean }>( buttonColor, - (props: { isLast?: boolean } & IButtonColorProps) => ({ + (props) => ({ flex: 0, display: 'none', padding: '0 10px', - paddingRight: props.isLast ? '17px' : '10px', + paddingRight: props.$isLast ? '17px' : '10px', margin: 0, border: 0, height: measurements.rowMinHeight, appearance: 'none', - ':not(:disabled):hover': { - backgroundColor: props.backgroundColor, + '&&:not(:disabled):hover': { + backgroundColor: props.$backgroundColor, }, [`${StyledLocationRowContainer}:hover &&`]: { display: 'block', }, [`${StyledLocationRowButton}:hover ~ &&`]: { - backgroundColor: props.backgroundColorHover, + backgroundColor: props.$backgroundColorHover, }, }), ); @@ -240,7 +240,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { as="button" ref={buttonRef} onClick={handleClick} - level={props.level} + $level={props.level} disabled={props.source.disabled} includeMarginBottomOnLast {...background}> @@ -249,7 +249,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { </StyledLocationRowButton> {props.allowAddToCustomList ? ( - <StyledHoverIconButton onClick={showAddToListDialog} isLast {...background}> + <StyledHoverIconButton onClick={showAddToListDialog} $isLast {...background}> <StyledHoverIcon source="icon-add" /> </StyledHoverIconButton> ) : null} @@ -258,7 +258,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { {'customList' in props.source.location && 'country' in props.source.location && props.level === 1 ? ( - <StyledHoverIconButton onClick={onRemoveFromList} isLast {...background}> + <StyledHoverIconButton onClick={onRemoveFromList} $isLast {...background}> <StyledHoverIcon source="icon-remove" /> </StyledHoverIconButton> ) : null} @@ -269,7 +269,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { <StyledHoverIconButton onClick={showEditDialog} {...background}> <StyledHoverIcon source="icon-edit" /> </StyledHoverIconButton> - <StyledHoverIconButton onClick={onRemoveCustomList} isLast {...background}> + <StyledHoverIconButton onClick={onRemoveCustomList} $isLast {...background}> <StyledHoverIcon source="icon-close" /> </StyledHoverIconButton> </> @@ -297,7 +297,7 @@ function LocationRow<C extends LocationSpecification>(props: IProps<C>) { onWillExpand={onWillExpand} onTransitionEnd={props.onTransitionEnd} animationDuration={150}> - <Cell.Group noMarginBottom>{props.children}</Cell.Group> + <Cell.Group $noMarginBottom>{props.children}</Cell.Group> </Accordion> )} @@ -333,8 +333,8 @@ export function getButtonColor(selected: boolean, level: number, disabled?: bool } return { - backgroundColor, - backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, + $backgroundColor: backgroundColor, + $backgroundColorHover: selected || disabled ? backgroundColor : colors.blue80, }; } diff --git a/gui/src/renderer/components/select-location/RelayLocationList.tsx b/gui/src/renderer/components/select-location/RelayLocationList.tsx index 27fe75ea50..49e1f2a38c 100644 --- a/gui/src/renderer/components/select-location/RelayLocationList.tsx +++ b/gui/src/renderer/components/select-location/RelayLocationList.tsx @@ -25,7 +25,7 @@ interface RelayLocationsProps extends CommonProps { export default function RelayLocationList({ source, ...props }: RelayLocationsProps) { return ( - <Cell.Group noMarginBottom> + <Cell.Group $noMarginBottom> {source.map((country) => ( <RelayLocation key={getLocationKey(country.location)} diff --git a/gui/src/renderer/components/select-location/ScopeBar.tsx b/gui/src/renderer/components/select-location/ScopeBar.tsx index 94c80dea7c..bbe4935993 100644 --- a/gui/src/renderer/components/select-location/ScopeBar.tsx +++ b/gui/src/renderer/components/select-location/ScopeBar.tsx @@ -35,7 +35,7 @@ export function ScopeBar(props: IScopeBarProps) { return <StyledScopeBar className={props.className}>{children}</StyledScopeBar>; } -const StyledScopeBarItem = styled.button(smallText, (props: { selected?: boolean }) => ({ +const StyledScopeBarItem = styled.button<{ selected?: boolean }>(smallText, (props) => ({ cursor: 'default', flex: 1, flexBasis: 0, @@ -44,7 +44,7 @@ const StyledScopeBarItem = styled.button(smallText, (props: { selected?: boolean textAlign: 'center', border: 'none', backgroundColor: props.selected ? colors.green : 'transparent', - ':hover': { + '&&:hover': { backgroundColor: props.selected ? colors.green : colors.blue40, }, })); diff --git a/gui/src/renderer/components/select-location/SpecialLocationList.tsx b/gui/src/renderer/components/select-location/SpecialLocationList.tsx index c8c0c8e787..030520fc73 100644 --- a/gui/src/renderer/components/select-location/SpecialLocationList.tsx +++ b/gui/src/renderer/components/select-location/SpecialLocationList.tsx @@ -64,7 +64,7 @@ function SpecialLocationRow<T>(props: SpecialLocationRowProps<T>) { const background = getButtonColor(props.source.selected, 0, props.source.disabled); return ( <StyledLocationRowContainerWithMargin ref={selectedRef}> - <StyledLocationRowButton onClick={onSelect} level={0} {...background}> + <StyledLocationRowButton onClick={onSelect} $level={0} {...background}> {icon && ( <StyledSpecialLocationIcon source={icon} @@ -79,8 +79,8 @@ function SpecialLocationRow<T>(props: SpecialLocationRowProps<T>) { <StyledLocationRowIcon as={StyledSpecialLocationInfoButton} message={props.source.info} - selected={props.source.selected} aria-label={messages.pgettext('accessibility', 'info')} + {...background} /> )} </StyledLocationRowContainerWithMargin> |
