summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2025-11-03 09:25:07 +0100
committerTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-11-04 14:16:37 +0100
commit1182e3bbb5e900c513970006c0614fd0073a47cd (patch)
treeebdfbcd74c26c9ac3e222be187a17b097f48c441
parentd0f4e5fa0d48fe09293fdc61200ba7bf99759741 (diff)
downloadmullvadvpn-1182e3bbb5e900c513970006c0614fd0073a47cd.tar.xz
mullvadvpn-1182e3bbb5e900c513970006c0614fd0073a47cd.zip
Refactor remaining components to use new Switch
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx7
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx19
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx7
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Switch.tsx72
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/views/split-tunneling/components/split-tunneling-settings/components/split-tunneling-settings-header/components/split-tunneling-state-switch/SplitTunnelingStateSwitch.tsx11
7 files changed, 35 insertions, 100 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
index 1018f11259..61779e36d3 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx
@@ -8,6 +8,7 @@ import { RoutePath } from '../../shared/routes';
import { useAppContext } from '../context';
import { useApiAccessMethodTest } from '../lib/api-access-methods';
import { Button, Container, Flex, Spinner } from '../lib/components';
+import { Switch } from '../lib/components/switch';
import { colors, spacings } from '../lib/foundations';
import { useHistory } from '../lib/history';
import { generateRoutePath } from '../lib/routeHelpers';
@@ -293,7 +294,11 @@ function ApiAccessMethod(props: ApiAccessMethodProps) {
<ContextMenuTrigger />
<ContextMenu items={menuItems} align="right" />
</ContextMenuContainer>
- <Cell.Switch isOn={props.method.enabled} onChange={toggle} />
+ <Switch checked={props.method.enabled} onCheckedChange={toggle}>
+ <Switch.Trigger>
+ <Switch.Thumb />
+ </Switch.Trigger>
+ </Switch>
</Flex>
{/* Confirmation dialog for method removal */}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
index 1e7005c554..5fccace6b1 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
@@ -1,5 +1,6 @@
import { createContext, ReactNode, useCallback, useContext, useMemo, useState } from 'react';
import { sprintf } from 'sprintf-js';
+import styled from 'styled-components';
import { urls } from '../../shared/constants';
import { messages } from '../../shared/gettext';
@@ -8,13 +9,13 @@ import { RoutePath } from '../../shared/routes';
import { useAppContext } from '../context';
import { Button, Flex } from '../lib/components';
import { FlexColumn } from '../lib/components/flex-column';
+import { spacings } from '../lib/foundations';
import { useHistory } from '../lib/history';
import { useExclusiveTask } from '../lib/hooks/use-exclusive-task';
import { IconBadge } from '../lib/icon-badge';
import { formatDeviceName } from '../lib/utils';
import { useSelector } from '../redux/store';
import { AppMainHeader } from './app-main-header';
-import * as Cell from './cell';
import DeviceInfoButton from './DeviceInfoButton';
import {
StyledAccountNumberContainer,
@@ -25,11 +26,11 @@ import {
StyledCustomScrollbars,
StyledDeviceLabel,
StyledMessage,
- StyledModalCellContainer,
StyledTitle,
} from './ExpiredAccountErrorViewStyles';
import { Footer, Layout } from './Layout';
import { ModalAlert, ModalAlertType, ModalMessage } from './Modal';
+import { SettingsToggleListItem } from './settings-toggle-list-item';
enum RecoveryAction {
openBrowser,
@@ -37,6 +38,10 @@ enum RecoveryAction {
disableLockdownMode,
}
+const StyledSettingsToggleListItem = styled(SettingsToggleListItem)`
+ margin-top: ${spacings.medium};
+`;
+
export default function ExpiredAccountErrorView() {
return (
<ExpiredAccountContextProvider>
@@ -258,10 +263,12 @@ function LockdownModeAlert() {
'Remember, turning it off will allow network traffic while the VPN is disconnected until you turn it back on under Advanced settings.',
)}
</ModalMessage>
- <StyledModalCellContainer>
- <Cell.Label>{messages.pgettext('vpn-settings-view', 'Lockdown mode')}</Cell.Label>
- <Cell.Switch isOn={lockdownMode} onChange={onChange} />
- </StyledModalCellContainer>
+ <StyledSettingsToggleListItem checked={lockdownMode} onCheckedChange={onChange}>
+ <SettingsToggleListItem.Label>
+ {messages.pgettext('vpn-settings-view', 'Lockdown mode')}
+ </SettingsToggleListItem.Label>
+ <SettingsToggleListItem.Switch />
+ </StyledSettingsToggleListItem>
</ModalAlert>
);
}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
index b39e4900fb..fd1182f572 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
@@ -2,7 +2,6 @@ import styled from 'styled-components';
import { colors } from '../lib/foundations';
import AccountNumberLabel from './AccountNumberLabel';
-import * as Cell from './cell';
import { hugeText, measurements, tinyText } from './common-styles';
import CustomScrollbars from './CustomScrollbars';
import { Container } from './Layout';
@@ -15,12 +14,6 @@ export const StyledAccountNumberLabel = styled(AccountNumberLabel)({
color: colors.white,
});
-export const StyledModalCellContainer = styled(Cell.Container)({
- marginTop: '18px',
- paddingLeft: '12px',
- paddingRight: '12px',
-});
-
export const StyledCustomScrollbars = styled(CustomScrollbars)({
flex: 1,
});
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
index c90a0db195..3d36fff9c9 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
@@ -11,9 +11,9 @@ import {
import { messages } from '../../shared/gettext';
import { Button, Flex } from '../lib/components';
import { FlexRow } from '../lib/components/flex-row';
+import { Switch } from '../lib/components/switch';
import { IpAddress } from '../lib/ip';
import { useEffectEvent } from '../lib/utility-hooks';
-import * as Cell from './cell';
import { SettingsForm, useSettingsFormSubmittable } from './cell/SettingsForm';
import { SettingsGroup } from './cell/SettingsGroup';
import { SettingsRadioGroup } from './cell/SettingsRadioGroup';
@@ -424,7 +424,11 @@ function EditSocks5Remote(props: EditProxyProps<Socks5RemoteCustomProxy>) {
</SettingsRow>
<SettingsRow label={messages.pgettext('api-access-methods-view', 'Authentication')}>
- <Cell.Switch isOn={authentication} onChange={setAuthentication} />
+ <Switch checked={authentication} onCheckedChange={setAuthentication}>
+ <Switch.Trigger>
+ <Switch.Thumb />
+ </Switch.Trigger>
+ </Switch>
</SettingsRow>
{authentication && (
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Switch.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Switch.tsx
deleted file mode 100644
index 48c3ff6b1a..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Switch.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import styled from 'styled-components';
-
-import { colors, ColorVariables } from '../lib/foundations';
-
-interface IProps {
- id?: string;
- 'aria-labelledby'?: string;
- 'aria-describedby'?: string;
- isOn: boolean;
- onChange?: (isOn: boolean) => void;
- className?: string;
- disabled?: boolean;
- innerRef?: React.Ref<HTMLDivElement>;
-}
-
-const SwitchContainer = styled.div<{ disabled: boolean }>((props) => ({
- position: 'relative',
- width: '34px',
- height: '22px',
- borderColor: props.disabled ? colors.whiteAlpha20 : colors.whiteAlpha80,
- borderWidth: '2px',
- borderStyle: 'solid',
- borderRadius: '11px',
- padding: '1px',
-}));
-
-const Knob = styled.div<{ $isOn: boolean; disabled: boolean }>((props) => {
- let backgroundColor: ColorVariables = props.$isOn ? colors.green : colors.red;
- if (props.disabled) {
- backgroundColor = props.$isOn ? colors.green40 : colors.red40;
- }
-
- return {
- position: 'absolute',
- height: '16px',
- borderRadius: '8px',
- transition: 'all 200ms linear',
- width: '16px',
- backgroundColor,
- // When enabled the button should be placed all the way to the right (100%) minus padding (1px)
- // minus it's own width (16px).
- left: props.$isOn ? 'calc(100% - 1px - 16px)' : '1px',
- };
-});
-
-export default class Switch extends React.PureComponent<IProps> {
- public render() {
- return (
- <SwitchContainer
- ref={this.props.innerRef}
- id={this.props.id}
- role="checkbox"
- aria-labelledby={this.props['aria-labelledby']}
- aria-describedby={this.props['aria-describedby']}
- aria-checked={this.props.isOn}
- onClick={this.handleClick}
- disabled={this.props.disabled ?? false}
- aria-disabled={this.props.disabled ?? false}
- tabIndex={-1}
- className={this.props.className}>
- <Knob disabled={this.props.disabled ?? false} $isOn={this.props.isOn} />
- </SwitchContainer>
- );
- }
-
- private handleClick = () => {
- if (!this.props.disabled) {
- this.props.onChange?.(!this.props.isOn);
- }
- };
-}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
index 7955870c2d..20b685e10f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/Input.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useContext, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { IconButton } from '../../lib/components';
@@ -6,17 +6,8 @@ import { colors } from '../../lib/foundations';
import { useBoolean, useCombinedRefs, useEffectEvent, useStyledRef } from '../../lib/utility-hooks';
import { normalText } from '../common-styles';
import { BackAction } from '../KeyboardNavigation';
-import StandaloneSwitch from '../Switch';
import { CellDisabledContext, Container } from './Container';
-export const Switch = React.forwardRef(function SwitchT(
- props: StandaloneSwitch['props'],
- ref: React.Ref<StandaloneSwitch>,
-) {
- const disabled = useContext(CellDisabledContext);
- return <StandaloneSwitch ref={ref} disabled={disabled} {...props} />;
-});
-
const inputTextStyles: React.CSSProperties = {
...normalText,
height: '18px',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/split-tunneling/components/split-tunneling-settings/components/split-tunneling-settings-header/components/split-tunneling-state-switch/SplitTunnelingStateSwitch.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/split-tunneling/components/split-tunneling-settings/components/split-tunneling-settings-header/components/split-tunneling-state-switch/SplitTunnelingStateSwitch.tsx
index bf641a6f5e..0cac9fcca7 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/views/split-tunneling/components/split-tunneling-settings/components/split-tunneling-settings-header/components/split-tunneling-state-switch/SplitTunnelingStateSwitch.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/split-tunneling/components/split-tunneling-settings/components/split-tunneling-settings-header/components/split-tunneling-state-switch/SplitTunnelingStateSwitch.tsx
@@ -1,6 +1,6 @@
import { useAppContext } from '../../../../../../../../../context';
+import { Switch } from '../../../../../../../../../lib/components/switch';
import { useSelector } from '../../../../../../../../../redux/store';
-import { Switch } from '../../../../../../../../cell';
import { useDisabled } from './hooks';
export function SplitTunnelingStateSwitch() {
@@ -9,6 +9,13 @@ export function SplitTunnelingStateSwitch() {
const splitTunnelingEnabled = useSelector((state) => state.settings.splitTunneling);
return (
- <Switch isOn={splitTunnelingEnabled} disabled={disabled} onChange={setSplitTunnelingState} />
+ <Switch
+ checked={splitTunnelingEnabled}
+ disabled={disabled}
+ onCheckedChange={setSplitTunnelingState}>
+ <Switch.Trigger>
+ <Switch.Thumb />
+ </Switch.Trigger>
+ </Switch>
);
}