summaryrefslogtreecommitdiffhomepage
path: root/desktop
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2025-08-27 10:27:26 +0200
committerTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-09-22 12:35:43 +0200
commit444900c3581bcc538eccf28160053110700751ca (patch)
tree784ca5819d4c77a62db767a4347e1cb903ff1d3d /desktop
parent543e2521877627574a22ff183702fb1b75002446 (diff)
downloadmullvadvpn-444900c3581bcc538eccf28160053110700751ca.tar.xz
mullvadvpn-444900c3581bcc538eccf28160053110700751ca.zip
Add FeatureIndicator component
Diffstat (limited to 'desktop')
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicator.tsx104
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/FeatureIndicatorContext.tsx28
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/FeatureIndicatorText.tsx16
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/components/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/feature-indicator/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/index.ts1
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';