summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOliver <oliver@mohlin.dev>2025-02-06 14:51:04 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-02-12 13:25:56 +0100
commitf691c9ed2e5f841a81c660d2861ee8f9fc882d5b (patch)
tree62f27413f1a483f66e14b9f02831514bcd5f0699
parent54c80429e9e67029087fffb2129316a5d7f15ec6 (diff)
downloadmullvadvpn-f691c9ed2e5f841a81c660d2861ee8f9fc882d5b.tar.xz
mullvadvpn-f691c9ed2e5f841a81c660d2861ee8f9fc882d5b.zip
Add Progress component
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/Progress.tsx45
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/ProgressContext.tsx38
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressFooter.tsx10
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressPercent.tsx20
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressRange.tsx24
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressText.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressTrack.tsx33
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/index.ts5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/index.ts1
9 files changed, 190 insertions, 0 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/Progress.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/Progress.tsx
new file mode 100644
index 0000000000..950a07539d
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/Progress.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+import { Spacings } from '../../foundations';
+import { Flex } from '../flex';
+import {
+ ProgressFooter,
+ ProgressPercent,
+ ProgressRange,
+ ProgressText,
+ ProgressTrack,
+} from './components';
+import { ProgressProvider } from './ProgressContext';
+
+export interface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {
+ min?: number;
+ max?: number;
+ value: number;
+ disabled?: boolean;
+}
+
+const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
+ ({ min = 0, max = 100, value, disabled, children, ...props }, ref) => {
+ const normalizedValue = Math.min(Math.max(value, min), max);
+ const percent = ((normalizedValue - min) / (max - min)) * 100;
+ return (
+ <ProgressProvider value={value} min={min} max={max} percent={percent} disabled={disabled}>
+ <Flex $flexDirection="column" $gap={Spacings.spacing3} ref={ref} {...props}>
+ {children}
+ </Flex>
+ </ProgressProvider>
+ );
+ },
+);
+
+Progress.displayName = 'Progress';
+
+const ProgressNamespace = Object.assign(Progress, {
+ Footer: ProgressFooter,
+ Track: ProgressTrack,
+ Percent: ProgressPercent,
+ Range: ProgressRange,
+ Text: ProgressText,
+});
+
+export { ProgressNamespace as Progress };
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/ProgressContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/ProgressContext.tsx
new file mode 100644
index 0000000000..c47a345739
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/ProgressContext.tsx
@@ -0,0 +1,38 @@
+import React, { createContext, ReactNode, useContext } from 'react';
+
+interface ProgressContextType {
+ value: number;
+ min: number;
+ max: number;
+ percent: number;
+ disabled?: boolean;
+}
+
+const ProgressContext = createContext<ProgressContextType | undefined>(undefined);
+
+interface ProgressProviderProps extends ProgressContextType {
+ children: ReactNode;
+}
+
+export const ProgressProvider: React.FC<ProgressProviderProps> = ({
+ value,
+ min,
+ max,
+ percent,
+ disabled,
+ children,
+}) => {
+ return (
+ <ProgressContext.Provider value={{ min, max, percent, value, disabled }}>
+ {children}
+ </ProgressContext.Provider>
+ );
+};
+
+export const useProgress = (): ProgressContextType => {
+ const context = useContext(ProgressContext);
+ if (!context) {
+ throw new Error('useProgress must be used within a ProgressProvider');
+ }
+ return context;
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressFooter.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressFooter.tsx
new file mode 100644
index 0000000000..779c1cde6c
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressFooter.tsx
@@ -0,0 +1,10 @@
+import styled from 'styled-components';
+
+import { Spacings } from '../../../foundations';
+import { Flex } from '../../flex';
+
+export const ProgressFooter = styled(Flex).attrs({
+ $alignItems: 'center',
+ $justifyContent: 'space-between',
+ $gap: Spacings.spacing5,
+})``;
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressPercent.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressPercent.tsx
new file mode 100644
index 0000000000..b6ea92cf28
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressPercent.tsx
@@ -0,0 +1,20 @@
+import styled from 'styled-components';
+
+import { Colors } from '../../../foundations';
+import { LabelTiny, LabelTinyProps } from '../../typography';
+import { useProgress } from '../ProgressContext';
+
+export type ProgressPercentProps = Omit<LabelTinyProps, 'children'>;
+
+const StyledText = styled(LabelTiny)`
+ min-width: 26px;
+`;
+
+export const ProgressPercent = (props: ProgressPercentProps) => {
+ const { percent, disabled } = useProgress();
+ return (
+ <StyledText color={disabled ? Colors.white40 : Colors.white} {...props}>
+ {`${Math.round(percent)}%`}
+ </StyledText>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressRange.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressRange.tsx
new file mode 100644
index 0000000000..e0ffb6c745
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressRange.tsx
@@ -0,0 +1,24 @@
+import styled from 'styled-components';
+
+import { Colors, Radius } from '../../../foundations';
+import { useProgress } from '../ProgressContext';
+
+const StyledDiv = styled.div<{
+ disabled?: boolean;
+}>`
+ background-color: ${({ disabled }) => (disabled ? Colors.white50 : Colors.white)};
+ border-radius: ${Radius.radius4};
+ height: 100%;
+ width: 100%;
+ transition: transform 0.2s ease-out;
+ transform: var(--transform);
+`;
+
+export const ProgressRange = () => {
+ const { percent, disabled } = useProgress();
+ const transform = `translateX(${percent - 100}%)`;
+
+ return (
+ <StyledDiv disabled={disabled} style={{ '--transform': transform } as React.CSSProperties} />
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressText.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressText.tsx
new file mode 100644
index 0000000000..36ad555eec
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressText.tsx
@@ -0,0 +1,14 @@
+import { Colors } from '../../../foundations';
+import { LabelTiny, LabelTinyProps } from '../../typography';
+import { useProgress } from '../ProgressContext';
+
+export type ProgressTextProps = LabelTinyProps;
+
+export const ProgressText = ({ children, ...props }: ProgressTextProps) => {
+ const { disabled } = useProgress();
+ return (
+ <LabelTiny color={disabled ? Colors.white40 : Colors.white60} {...props}>
+ {children}
+ </LabelTiny>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressTrack.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressTrack.tsx
new file mode 100644
index 0000000000..58e4abb28b
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/ProgressTrack.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import styled from 'styled-components';
+
+import { Radius } from '../../../foundations';
+import { Flex, FlexProps } from '../../flex';
+import { useProgress } from '../ProgressContext';
+
+const StyledFlex = styled(Flex)`
+ // TODO: Replace with token when available
+ background-color: ${'rgba(27, 49, 74, 1)'};
+ border-radius: ${Radius.radius4};
+ width: 100%;
+ height: 8px;
+ overflow: hidden;
+ position: relative;
+`;
+
+export type ProgressTrackProps = FlexProps;
+
+export const ProgressTrack: React.FC<ProgressTrackProps> = ({ children, ...props }) => {
+ const { max, min, value } = useProgress();
+ return (
+ <StyledFlex
+ $alignItems="center"
+ role="progressbar"
+ aria-valuemin={min}
+ aria-valuemax={max}
+ aria-valuenow={value}
+ {...props}>
+ {children}
+ </StyledFlex>
+ );
+};
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/index.ts
new file mode 100644
index 0000000000..784071c766
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/components/index.ts
@@ -0,0 +1,5 @@
+export * from './ProgressFooter';
+export * from './ProgressPercent';
+export * from './ProgressRange';
+export * from './ProgressText';
+export * from './ProgressTrack';
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/index.ts
new file mode 100644
index 0000000000..1803677d94
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/progress/index.ts
@@ -0,0 +1 @@
+export * from './Progress';