summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2024-12-10 14:09:42 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2024-12-17 12:15:39 +0100
commit101406af03e85a35becec2ecfd3f1c439ee564e9 (patch)
tree0bbe13f43e8f72d6ea0bccc93d07426112285e84
parenta68ef5cf39e79310ebe0a7891e2b5312da467136 (diff)
downloadmullvadvpn-101406af03e85a35becec2ecfd3f1c439ee564e9.tar.xz
mullvadvpn-101406af03e85a35becec2ecfd3f1c439ee564e9.zip
Add Button and IconButton components
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx105
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx65
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts2
5 files changed, 183 insertions, 0 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts
new file mode 100644
index 0000000000..4ef5844150
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/button-reset.ts
@@ -0,0 +1,10 @@
+export const buttonReset = {
+ border: 'none',
+ padding: 0,
+ margin: 0,
+ font: 'inherit',
+ color: 'inherit',
+ textAlign: 'inherit',
+ lineHeight: 'inherit',
+ cursor: 'default',
+} as React.CSSProperties;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts
new file mode 100644
index 0000000000..c546f737b6
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/mixins/index.ts
@@ -0,0 +1 @@
+export * from './button-reset';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx
new file mode 100644
index 0000000000..c4db496a21
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/Button.tsx
@@ -0,0 +1,105 @@
+import React, { forwardRef } from 'react';
+import styled from 'styled-components';
+
+import { Colors, Radius, Spacings } from '../../../tokens';
+import { Flex } from '../layout';
+import { buttonReset } from '../mixins';
+import { BodySmallSemiBold } from '../text';
+
+export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
+ variant?: 'primary' | 'success' | 'destructive';
+ size?: 'tiny' | 'small' | 'regular' | 'full' | '1/2';
+ leading?: React.ReactNode;
+ trailing?: React.ReactNode;
+}
+
+const variants = {
+ primary: {
+ background: Colors.blue,
+ hover: Colors.blue60,
+ disabled: Colors.blue50,
+ },
+ success: {
+ background: Colors.green,
+ hover: Colors.green90,
+ disabled: Colors.green40,
+ },
+ destructive: {
+ background: Colors.red,
+ hover: Colors.red80,
+ disabled: Colors.red60,
+ },
+} as const;
+
+const sizes = {
+ tiny: '44px',
+ small: '60px',
+ regular: '272px',
+ full: '100%',
+ '1/2': '50%',
+};
+
+const StyledButton = styled.button({
+ ...buttonReset,
+
+ minHeight: '32px',
+ borderRadius: Radius.radius4,
+ width: 'var(--size)',
+ background: 'var(--background)',
+ '&:not(:disabled):hover': {
+ background: 'var(--hover)',
+ },
+ '&:disabled': {
+ background: 'var(--disabled)',
+ },
+ '&:focus-visible': {
+ outline: `2px solid ${Colors.white}`,
+ outlineOffset: '2px',
+ },
+});
+
+export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
+ (
+ { variant = 'primary', size = 'regular', leading, trailing, children, disabled, ...props },
+ ref,
+ ) => {
+ const styles = variants[variant];
+ return (
+ <StyledButton
+ ref={ref}
+ style={
+ {
+ '--background': styles.background,
+ '--hover': styles.hover,
+ '--disabled': styles.disabled,
+ '--size': sizes[size],
+ } as React.CSSProperties
+ }
+ disabled={disabled}
+ {...props}>
+ <Flex
+ $flex={1}
+ $gap={Spacings.spacing4}
+ $justifyContent="space-between"
+ $padding={{
+ horizontal: Spacings.spacing3,
+ }}
+ $alignItems="center">
+ {leading}
+ <Flex $flex={1} $justifyContent="center" $alignItems="center">
+ {typeof children === 'string' ? (
+ <BodySmallSemiBold color={disabled ? Colors.white40 : Colors.white}>
+ {children}
+ </BodySmallSemiBold>
+ ) : (
+ children
+ )}
+ </Flex>
+ {trailing}
+ </Flex>
+ </StyledButton>
+ );
+ },
+);
+
+Button.displayName = 'Button';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx
new file mode 100644
index 0000000000..bdbe7ba8e4
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/IconButton.tsx
@@ -0,0 +1,65 @@
+import React, { forwardRef } from 'react';
+import styled from 'styled-components';
+
+import { Colors } from '../../../tokens';
+import ImageView from '../../ImageView';
+import { buttonReset } from '../mixins';
+
+export interface IconButtonProps
+ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
+ variant?: 'primary' | 'secondary';
+ size?: 'small' | 'regular';
+ icon: string;
+}
+
+const variants = {
+ primary: {
+ background: Colors.white,
+ hover: Colors.white60,
+ disabled: Colors.white50,
+ },
+ secondary: {
+ background: Colors.white60,
+ hover: Colors.white80,
+ disabled: Colors.white50,
+ },
+} as const;
+
+const sizes = {
+ small: 16,
+ regular: 24,
+};
+
+const StyledButton = styled.button({
+ ...buttonReset,
+
+ background: 'transparent',
+ height: 'var(--size)',
+ width: 'var(--size)',
+ '&:focus-visible': {
+ outline: `2px solid ${Colors.white}`,
+ outlineOffset: '2px',
+ borderRadius: '100%',
+ },
+});
+
+export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
+ ({ icon, variant = 'primary', size: sizeProp = 'regular', disabled, ...props }, ref) => {
+ const styles = variants[variant];
+ const size = sizes[sizeProp];
+ return (
+ <StyledButton ref={ref} disabled={disabled} {...props}>
+ <ImageView
+ source={icon}
+ tintColor={styles.background}
+ tintHoverColor={styles.hover}
+ disabled={disabled}
+ height={size}
+ width={size}
+ />
+ </StyledButton>
+ );
+ },
+);
+
+IconButton.displayName = 'Button';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts
new file mode 100644
index 0000000000..66f0cf90a7
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/common/molecules/index.ts
@@ -0,0 +1,2 @@
+export * from './Button';
+export * from './IconButton';