summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-09-20 17:13:20 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-09-27 10:30:53 +0200
commit7d05e6624ac4fcde6964422241b0b5a9aeb8f840 (patch)
tree73cbf2f77859ba7d67d1428a045c14db1228b34d /gui/src
parentde9bf32918295037d1616d256afedf52ab7d4b6d (diff)
downloadmullvadvpn-7d05e6624ac4fcde6964422241b0b5a9aeb8f840.tar.xz
mullvadvpn-7d05e6624ac4fcde6964422241b0b5a9aeb8f840.zip
Update selector to handle automatic values
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/cell/Selector.tsx103
1 files changed, 61 insertions, 42 deletions
diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx
index e815519552..f5024989e3 100644
--- a/gui/src/renderer/components/cell/Selector.tsx
+++ b/gui/src/renderer/components/cell/Selector.tsx
@@ -1,7 +1,8 @@
-import * as React from 'react';
+import { useCallback } from 'react';
import styled from 'styled-components';
import { colors } from '../../../config.json';
+import { messages } from '../../../shared/gettext';
import { useBoolean } from '../../lib/utilityHooks';
import Accordion from '../Accordion';
import { AriaDetails, AriaInput, AriaLabel } from '../AriaGroup';
@@ -24,44 +25,64 @@ const StyledChevronButton = styled(ChevronButton)({
marginRight: '16px',
});
-export interface ISelectorItem<T> {
+export interface SelectorItem<T> {
label: string;
value: T;
disabled?: boolean;
}
-interface ISelectorProps<T> {
+interface SelectorProps<T, U> {
title?: string;
- values: Array<ISelectorItem<T>>;
- value: T;
- onSelect: (value: T) => void;
- selectedCellRef?: React.Ref<HTMLButtonElement>;
+ items: Array<SelectorItem<T>>;
+ value: T | U;
+ onSelect: (value: T | U) => void;
+ selectedCellRef?: React.Ref<HTMLElement>;
className?: string;
details?: React.ReactElement;
expandable?: boolean;
disabled?: boolean;
thinTitle?: boolean;
+ automaticLabel?: string;
+ automaticValue?: U;
}
-export default function Selector<T>(props: ISelectorProps<T>) {
+export default function Selector<T, U>(props: SelectorProps<T, U>) {
const [expanded, , , toggleExpanded] = useBoolean(!props.expandable);
- const items = props.values.map((item, i) => {
- const selected = item.value === props.value;
+ const items = props.items.map((item) => {
+ const selected = props.value === item.value;
+ const ref = selected ? (props.selectedCellRef as React.Ref<HTMLButtonElement>) : undefined;
return (
<SelectorCell
- key={i}
+ key={`value-${item.value}`}
value={item.value}
- selected={selected}
+ isSelected={selected}
disabled={props.disabled || item.disabled}
- forwardedRef={selected ? props.selectedCellRef : undefined}
+ forwardedRef={ref}
onSelect={props.onSelect}>
{item.label}
</SelectorCell>
);
});
+ if (props.automaticValue !== undefined) {
+ const selected = props.value === props.automaticValue;
+ const ref = selected ? (props.selectedCellRef as React.Ref<HTMLButtonElement>) : undefined;
+
+ items.unshift(
+ <SelectorCell
+ key={'automatic'}
+ value={props.automaticValue}
+ isSelected={selected}
+ disabled={props.disabled}
+ forwardedRef={ref}
+ onSelect={props.onSelect}>
+ {props.automaticLabel ?? messages.gettext('Automatic')}
+ </SelectorCell>,
+ );
+ }
+
const title = props.title && (
<StyledTitle>
<AriaLabel>
@@ -97,40 +118,38 @@ const StyledLabel = styled(Cell.Label)(normalText, {
fontWeight: 400,
});
-interface ISelectorCellProps<T> {
+interface SelectorCellProps<T> {
value: T;
- selected: boolean;
+ isSelected: boolean;
disabled?: boolean;
onSelect: (value: T) => void;
- children?: React.ReactText;
+ children: React.ReactNode | Array<React.ReactNode>;
forwardedRef?: React.Ref<HTMLButtonElement>;
}
-class SelectorCell<T> extends React.Component<ISelectorCellProps<T>> {
- public render() {
- return (
- <Cell.CellButton
- ref={this.props.forwardedRef}
- onClick={this.onClick}
- selected={this.props.selected}
- disabled={this.props.disabled}
- role="option"
- aria-selected={this.props.selected}
- aria-disabled={this.props.disabled}>
- <StyledCellIcon
- visible={this.props.selected}
- source="icon-tick"
- width={18}
- tintColor={colors.white}
- />
- <StyledLabel>{this.props.children}</StyledLabel>
- </Cell.CellButton>
- );
- }
-
- private onClick = () => {
- if (!this.props.selected) {
- this.props.onSelect(this.props.value);
+function SelectorCell<T>(props: SelectorCellProps<T>) {
+ const handleClick = useCallback(() => {
+ if (!props.isSelected) {
+ props.onSelect(props.value);
}
- };
+ }, [props.isSelected, props.onSelect, props.value]);
+
+ return (
+ <Cell.CellButton
+ ref={props.forwardedRef}
+ onClick={handleClick}
+ selected={props.isSelected}
+ disabled={props.disabled}
+ role="option"
+ aria-selected={props.isSelected}
+ aria-disabled={props.disabled}>
+ <StyledCellIcon
+ visible={props.isSelected}
+ source="icon-tick"
+ width={18}
+ tintColor={colors.white}
+ />
+ <StyledLabel>{props.children}</StyledLabel>
+ </Cell.CellButton>
+ );
}