diff options
| author | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-09-26 11:06:20 +0200 |
|---|---|---|
| committer | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-09-26 11:06:20 +0200 |
| commit | 7a21043f4629332de2f8b9fd61c3dc0ee648cb67 (patch) | |
| tree | edba318279b7e253dfd867921d3e23d015438295 | |
| parent | 10400ce365fdd0faeec3ee7b0c60abdf0c597036 (diff) | |
| parent | 80a45a5096dac60481c18315cb12e025a46a8262 (diff) | |
| download | mullvadvpn-7a21043f4629332de2f8b9fd61c3dc0ee648cb67.tar.xz mullvadvpn-7a21043f4629332de2f8b9fd61c3dc0ee648cb67.zip | |
Merge branch 'fix-icon-button-states'
11 files changed, 97 insertions, 101 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/Button.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/Button.tsx index 27bda0a6d8..0b5e05afa4 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/Button.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/Button.tsx @@ -1,14 +1,14 @@ -import React, { forwardRef } from 'react'; +import React from 'react'; import styled, { css } from 'styled-components'; import { colors, Radius, spacings } from '../../foundations'; import { ButtonProvider } from './ButtonContext'; import { ButtonIcon, ButtonText, StyledButtonIcon, StyledButtonText } from './components'; -export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { +export type ButtonProps = React.ComponentPropsWithRef<'button'> & { variant?: 'primary' | 'success' | 'destructive'; width?: 'fill' | 'fit'; -} +}; const styles = { radius: Radius.radius4, @@ -123,18 +123,15 @@ export const StyledButton = styled.button<ButtonProps>` }} `; -const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button( - { children, disabled = false, style, ...props }, - ref, -) { +function Button({ children, disabled = false, ...props }: ButtonProps) { return ( <ButtonProvider disabled={disabled}> - <StyledButton ref={ref} disabled={disabled} {...props}> + <StyledButton disabled={disabled} {...props}> {children} </StyledButton> </ButtonProvider> ); -}); +} const ButtonNamespace = Object.assign(Button, { Text: ButtonText, diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/ButtonIcon.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-icon/ButtonIcon.tsx index 94054d909d..ad07a442da 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/ButtonIcon.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-icon/ButtonIcon.tsx @@ -1,7 +1,7 @@ import styled from 'styled-components'; -import { Icon, IconProps } from '../../icon'; -import { useButtonContext } from '../ButtonContext'; +import { Icon, IconProps } from '../../../icon'; +import { useButtonContext } from '../../ButtonContext'; type ButtonIconProps = Omit<IconProps, 'size'>; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-icon/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-icon/index.ts new file mode 100644 index 0000000000..d016c203ef --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-icon/index.ts @@ -0,0 +1 @@ +export * from './ButtonIcon'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/ButtonText.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-text/ButtonText.tsx index 79e8291142..202318995a 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/ButtonText.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-text/ButtonText.tsx @@ -1,8 +1,8 @@ import React from 'react'; import styled from 'styled-components'; -import { BodySmallSemiBold, BodySmallSemiBoldProps } from '../../typography'; -import { useButtonContext } from '../ButtonContext'; +import { BodySmallSemiBold, BodySmallSemiBoldProps } from '../../../typography'; +import { useButtonContext } from '../../ButtonContext'; export type ButtonTextProps<T extends React.ElementType = 'span'> = BodySmallSemiBoldProps<T>; export const StyledButtonText = styled(BodySmallSemiBold)``; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-text/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-text/index.ts new file mode 100644 index 0000000000..8cfa5f2b8d --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/button-text/index.ts @@ -0,0 +1 @@ +export * from './ButtonText'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/index.ts index 904258378e..26e29b335d 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/index.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/button/components/index.ts @@ -1,2 +1,2 @@ -export * from './ButtonIcon'; -export * from './ButtonText'; +export * from './button-icon'; +export * from './button-text'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx index fa363c6e3b..ee3caa25cb 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/IconButton.tsx @@ -1,50 +1,101 @@ -import React, { forwardRef } from 'react'; +import React from 'react'; import styled, { css } from 'styled-components'; import { colors } from '../../foundations'; import { IconProps, iconSizes } from '../icon/Icon'; -import { IconButtonIcon } from './components/IconButtonIcon'; +import { IconButtonIcon, StyledIconButtonIcon } from './components'; import { IconButtonProvider } from './IconButtonContext'; export type IconButtonVariant = 'primary' | 'secondary'; -export interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { +export type IconButtonProps = React.ComponentPropsWithRef<'button'> & { variant?: IconButtonVariant; size?: IconProps['size']; -} +}; -const StyledButton = styled.button<{ $size: IconButtonProps['size'] }>` - ${({ $size = 'medium' }) => { +const variants: Record< + IconButtonVariant, + { + background: string; + hover: string; + pressed: string; + disabled: string; + } +> = { + primary: { + background: colors.white, + hover: colors.whiteAlpha60, + pressed: colors.whiteAlpha40, + disabled: colors.whiteAlpha40, + }, + secondary: { + background: colors.whiteAlpha60, + hover: colors.whiteAlpha80, + pressed: colors.white, + disabled: colors.whiteAlpha40, + }, +} as const; + +const StyledButton = styled.button<{ + $size: IconButtonProps['size']; + $variant: IconButtonVariant; +}>` + ${({ $size = 'medium', $variant = 'primary' }) => { const size = iconSizes[$size]; + const variant = variants[$variant]; return css` --size: ${size}px; + --background: ${variant.background}; + --hover: ${variant.hover}; + --pressed: ${variant.pressed}; + --disabled: ${variant.disabled}; + --transition-duration: 0.15s; + background: ${colors.transparent}; height: var(--size); width: var(--size); border-radius: 100%; - &:focus-visible { + + &&:focus-visible { outline: 2px solid ${colors.white}; outline-offset: 1px; } + + ${StyledIconButtonIcon} { + background-color: var(--background); + @media (prefers-reduced-motion: no-preference) { + transition: background-color var(--transition-duration) ease; + } + } + + &&:not(:disabled):hover ${StyledIconButtonIcon} { + --transition-duration: 0s; + background-color: var(--hover); + } + + &&:not(:disabled):active ${StyledIconButtonIcon} { + --transition-duration: 0s; + background-color: var(--pressed); + } + + &&:disabled ${StyledIconButtonIcon} { + background-color: var(--disabled); + } `; }} `; -const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>( - ({ variant = 'primary', size = 'medium', disabled, style, ...props }, ref) => { - return ( - <IconButtonProvider size={size} variant={variant} disabled={disabled}> - <StyledButton ref={ref} disabled={disabled} $size={size} {...props} /> - </IconButtonProvider> - ); - }, -); +function IconButton({ variant = 'primary', size = 'medium', disabled, ...props }: IconButtonProps) { + return ( + <IconButtonProvider size={size} variant={variant} disabled={disabled}> + <StyledButton disabled={disabled} $variant={variant} $size={size} {...props} /> + </IconButtonProvider> + ); +} const IconButtonNamespace = Object.assign(IconButton, { Icon: IconButtonIcon, }); export { IconButtonNamespace as IconButton }; - -IconButton.displayName = 'Button'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx deleted file mode 100644 index 6247cbb6b9..0000000000 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/IconButtonIcon.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import styled, { css } from 'styled-components'; - -import { colors } from '../../../foundations'; -import { Icon, IconProps } from '../../icon/Icon'; -import { IconButtonVariant } from '../IconButton'; -import { useIconButtonContext } from '../IconButtonContext'; -export type IconButtonIconProps = IconProps; - -const variants: Record< - IconButtonVariant, - { - background: string; - hover: string; - pressed: string; - disabled: string; - } -> = { - primary: { - background: colors.white, - hover: colors.whiteAlpha60, - pressed: colors.whiteAlpha40, - disabled: colors.whiteAlpha40, - }, - secondary: { - background: colors.whiteAlpha60, - hover: colors.whiteAlpha80, - pressed: colors.white, - disabled: colors.whiteAlpha40, - }, -} as const; - -const StyledIconButtonIcon = styled(Icon)<{ $variant: IconButtonVariant }>(({ $variant }) => { - const variant = variants[$variant]; - return css` - --background: ${variant.background}; - --hover: ${variant.hover}; - --pressed: ${variant.pressed}; - --disabled: ${variant.disabled}; - --transition-duration: 0.15s; - - @media (prefers-reduced-motion: no-preference) { - transition: background-color var(--transition-duration) ease; - } - - background-color: var(--background); - - &&:not([data-disabled]):hover { - --transition-duration: 0s; - background-color: var(--hover); - } - - &&:not([data-disabled]):active { - --transition-duration: 0s; - background-color: var(--pressed); - } - - &[data-disabled='true'] { - background-color: var(--disabled); - } - `; -}); - -export const IconButtonIcon = (props: IconButtonIconProps) => { - const { variant = 'primary', size, disabled } = useIconButtonContext(); - return ( - <StyledIconButtonIcon size={size} data-disabled={disabled} $variant={variant} {...props} /> - ); -}; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/IconButtonIcon.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/IconButtonIcon.tsx new file mode 100644 index 0000000000..31892ff4ee --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/IconButtonIcon.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +import { Icon, IconProps } from '../../../icon/Icon'; +import { useIconButtonContext } from '../../IconButtonContext'; +export type IconButtonIconProps = IconProps; + +export const StyledIconButtonIcon = styled(Icon)``; + +export const IconButtonIcon = (props: IconButtonIconProps) => { + const { size } = useIconButtonContext(); + return <StyledIconButtonIcon size={size} {...props} />; +}; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/index.ts new file mode 100644 index 0000000000..970aa41372 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/icon-button-icon/index.ts @@ -0,0 +1 @@ +export * from './IconButtonIcon'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/index.ts new file mode 100644 index 0000000000..cb56fa6cb5 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/icon-button/components/index.ts @@ -0,0 +1 @@ +export * from './icon-button-icon'; |
