diff options
| author | Oliver <oliver@mohlin.dev> | 2025-08-27 10:27:26 +0200 |
|---|---|---|
| committer | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-09-22 12:35:43 +0200 |
| commit | 444900c3581bcc538eccf28160053110700751ca (patch) | |
| tree | 784ca5819d4c77a62db767a4347e1cb903ff1d3d /desktop | |
| parent | 543e2521877627574a22ff183702fb1b75002446 (diff) | |
| download | mullvadvpn-444900c3581bcc538eccf28160053110700751ca.tar.xz mullvadvpn-444900c3581bcc538eccf28160053110700751ca.zip | |
Add FeatureIndicator component
Diffstat (limited to 'desktop')
6 files changed, 151 insertions, 0 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicator.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicator.tsx new file mode 100644 index 0000000000..5535e7212d --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicator.tsx @@ -0,0 +1,104 @@ +import styled, { css } from 'styled-components'; + +import { colors, Radius } from '../../foundations'; +import { Flex } from '../flex'; +import { FeatureIndicatorText } from './components'; +import { FeatureIndicatorProvider } from './FeatureIndicatorContext'; + +export type FeatureIndicatorProps = { + variant?: 'primary' | 'transparent'; +} & React.ComponentPropsWithRef<'button'>; + +const styles = { + radius: Radius.radius4, + variants: { + primary: { + backgroundColor: colors.blue10, + borderColor: colors.blue, + borderColorHover: colors.whiteAlpha80, + borderColorPressed: colors.white, + }, + transparent: { + backgroundColor: 'transparent', + borderColor: 'transparent', + borderColorHover: 'transparent', + borderColorPressed: 'transparent', + }, + }, +}; + +const StyledFeatureIndicator = styled.button<{ + $variant: FeatureIndicatorProps['variant']; + $clickable?: boolean; +}>` + ${({ $variant: variantProp = 'primary', $clickable }) => { + const variant = styles.variants[variantProp]; + return css` + display: flex; + align-items: center; + + border-radius: ${Radius.radius8}; + background: ${variant.backgroundColor}; + border: 1px solid ${variant.borderColor}; + + ${() => { + if ($clickable) { + return css` + &&:not(:disabled):hover { + border-color: ${variant.borderColorHover}; + } + &&:not(:disabled):active { + border-color: ${variant.borderColorPressed}; + } + `; + } + return null; + }} + + &&:disabled { + background: var(--disabled); + } + &&:focus-visible { + outline: 2px solid ${colors.white}; + outline-offset: -2px; + } + `; + }} +`; + +const StyledFlex = styled(Flex)` + padding: 2px 8px; +`; + +function FeatureIndicator({ + ref, + variant, + children, + disabled, + style, + onClick, + ...props +}: FeatureIndicatorProps) { + const clickable = !disabled && !!onClick; + return ( + <FeatureIndicatorProvider disabled={disabled}> + <StyledFeatureIndicator + ref={ref} + $variant={variant} + $clickable={clickable} + disabled={disabled} + onClick={onClick} + {...props}> + <StyledFlex $flex={1} $alignItems="center"> + {children} + </StyledFlex> + </StyledFeatureIndicator> + </FeatureIndicatorProvider> + ); +} + +const FeatureIndicatorNamespace = Object.assign(FeatureIndicator, { + Text: FeatureIndicatorText, +}); + +export { FeatureIndicatorNamespace as FeatureIndicator }; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicatorContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicatorContext.tsx new file mode 100644 index 0000000000..cfe16337c1 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicatorContext.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +interface FeatureIndicatorContextProps { + disabled?: boolean; +} +const FeatureIndicatorContext = React.createContext<FeatureIndicatorContextProps | undefined>( + undefined, +); + +export const useFeatureIndicatorContext = (): FeatureIndicatorContextProps => { + const context = React.useContext(FeatureIndicatorContext); + if (!context) { + throw new Error('useFeatureIndicatorContext must be used within a FeatureIndicatorProvider'); + } + return context; +}; + +interface FeatureIndicatorProviderProps { + disabled?: boolean; + children: React.ReactNode; +} + +export const FeatureIndicatorProvider = ({ disabled, children }: FeatureIndicatorProviderProps) => { + return ( + <FeatureIndicatorContext.Provider value={{ disabled }}> + {children} + </FeatureIndicatorContext.Provider> + ); +}; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/FeatureIndicatorText.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/FeatureIndicatorText.tsx new file mode 100644 index 0000000000..49d8d5fac9 --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/FeatureIndicatorText.tsx @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +import { BodySmallSemiBoldProps, LabelTiny } from '../../typography'; +import { useFeatureIndicatorContext } from '../FeatureIndicatorContext'; + +export type FeatureIndicatorTextProps<T extends React.ElementType = 'span'> = + BodySmallSemiBoldProps<T>; + +export const StyledFeatureIndicatorText = styled(LabelTiny)``; + +export const FeatureIndicatorText = <T extends React.ElementType = 'span'>( + props: FeatureIndicatorTextProps<T>, +) => { + const { disabled } = useFeatureIndicatorContext(); + return <StyledFeatureIndicatorText color={disabled ? 'whiteAlpha40' : 'white'} {...props} />; +}; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/index.ts new file mode 100644 index 0000000000..a55496b31e --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/index.ts @@ -0,0 +1 @@ +export * from './FeatureIndicatorText'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/index.ts new file mode 100644 index 0000000000..1b3d6b0bde --- /dev/null +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/index.ts @@ -0,0 +1 @@ +export * from './FeatureIndicator'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/index.ts index a425560461..fce0e5c7ca 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/index.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/index.ts @@ -2,6 +2,7 @@ export * from './box'; export * from './button'; export * from './filter-chip'; export * from './container'; +export * from './feature-indicator'; export * from './flex'; export * from './image'; export * from './icon'; |
