summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2023-12-21 20:25:08 +0100
committerOskar Nyberg <oskar@mullvad.net>2024-01-29 09:44:53 +0100
commit2f6004e35484e07988463b2a9097114b17718fea (patch)
treed85c62b6c243f90b7babd6bc0b75a090689c3439 /gui/src
parent9cc0fe0f18801c5f605fe05864e12c2984415300 (diff)
downloadmullvadvpn-2f6004e35484e07988463b2a9097114b17718fea.tar.xz
mullvadvpn-2f6004e35484e07988463b2a9097114b17718fea.zip
Add radio button group component
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/cell/SettingsRadioGroup.tsx119
1 files changed, 119 insertions, 0 deletions
diff --git a/gui/src/renderer/components/cell/SettingsRadioGroup.tsx b/gui/src/renderer/components/cell/SettingsRadioGroup.tsx
new file mode 100644
index 0000000000..46f3ccded7
--- /dev/null
+++ b/gui/src/renderer/components/cell/SettingsRadioGroup.tsx
@@ -0,0 +1,119 @@
+import { useCallback, useId, useState } from 'react';
+import { styled } from 'styled-components';
+
+import { colors } from '../../../config.json';
+import { AriaInput, AriaInputGroup, AriaLabel } from '../AriaGroup';
+import { smallNormalText } from '../common-styles';
+import { SettingsSelectItem } from './SettingsSelect';
+
+const StyledRadioGroup = styled.div({
+ display: 'flex',
+});
+
+interface SettingsSelectProps<T extends string> {
+ defaultValue?: T;
+ items: Array<SettingsSelectItem<T>>;
+ onUpdate: (value: T) => void;
+}
+
+export function SettingsRadioGroup<T extends string>(props: SettingsSelectProps<T>) {
+ const [value, setValue] = useState<T>(props.defaultValue ?? props.items[0]?.value ?? '');
+ const key = useId();
+
+ const onSelect = useCallback((value: T) => {
+ setValue(value);
+ props.onUpdate(value);
+ }, []);
+
+ return (
+ <StyledRadioGroup>
+ {props.items.map((item) => (
+ <RadioButton
+ key={item.value}
+ group={key}
+ item={item}
+ selected={item.value === value}
+ onSelect={onSelect}
+ />
+ ))}
+ </StyledRadioGroup>
+ );
+}
+
+const StyledRadioButton = styled.input.attrs({ type: 'radio' })({
+ position: 'relative',
+ margin: 0,
+ appearance: 'none',
+ backgroundColor: 'transparent',
+ width: '12px',
+ height: '12px',
+
+ '&&::before': {
+ position: 'absolute',
+ content: '""',
+ width: '12px',
+ height: '12px',
+ borderRadius: '50%',
+ backgroundColor: 'transparent',
+ border: `1px ${colors.white} solid`,
+ top: 0,
+ left: 0,
+ },
+
+ '&&:checked::after': {
+ position: 'absolute',
+ content: '""',
+ width: '8px',
+ height: '8px',
+ borderRadius: '50%',
+ backgroundColor: colors.white,
+ top: '3px',
+ left: '3px',
+ },
+});
+
+const StyledRadioButtonContainer = styled.div({
+ display: 'flex',
+ alignItems: 'center',
+ flexWrap: 'nowrap',
+ marginLeft: '16px',
+});
+
+const StyledRadioButtonLabel = styled.label(smallNormalText, {
+ color: colors.white,
+ marginLeft: '8px',
+});
+
+interface RadioButtonProps<T extends string> {
+ group: string;
+ item: SettingsSelectItem<T>;
+ selected: boolean;
+ onSelect: (value: T) => void;
+}
+
+function RadioButton<T extends string>(props: RadioButtonProps<T>) {
+ const onChange = useCallback(
+ (event: React.ChangeEvent<HTMLInputElement>) => {
+ props.onSelect(event.target.value as T);
+ },
+ [props.onSelect],
+ );
+
+ return (
+ <StyledRadioButtonContainer>
+ <AriaInputGroup>
+ <AriaInput>
+ <StyledRadioButton
+ name={props.group}
+ value={props.item.value}
+ onChange={onChange}
+ checked={props.selected}
+ />
+ </AriaInput>
+ <AriaLabel>
+ <StyledRadioButtonLabel>{props.item.label}</StyledRadioButtonLabel>
+ </AriaLabel>
+ </AriaInputGroup>
+ </StyledRadioButtonContainer>
+ );
+}