summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-05-18 16:08:03 +0200
committerOskar Nyberg <oskar@mullvad.net>2020-05-25 12:56:47 +0200
commit51009aa592af34e168b152a4e10c841d690962d7 (patch)
treea43e18da3efdf24f808bba3e62efa8ac1365e404 /gui
parent00591c5fdeb5d56e21c5bc717201d743a289b9db (diff)
downloadmullvadvpn-51009aa592af34e168b152a4e10c841d690962d7.tar.xz
mullvadvpn-51009aa592af34e168b152a4e10c841d690962d7.zip
Add disabled functionality to switch, cell input and cell label
Diffstat (limited to 'gui')
-rw-r--r--gui/src/renderer/components/Cell.tsx57
-rw-r--r--gui/src/renderer/components/CellStyles.tsx6
-rw-r--r--gui/src/renderer/components/Switch.tsx56
3 files changed, 86 insertions, 33 deletions
diff --git a/gui/src/renderer/components/Cell.tsx b/gui/src/renderer/components/Cell.tsx
index 23f63114b7..4abcb9f49e 100644
--- a/gui/src/renderer/components/Cell.tsx
+++ b/gui/src/renderer/components/Cell.tsx
@@ -4,26 +4,38 @@ import {
StyledAutoSizingTextInputWrapper,
StyledAutoSizingTextInputFiller,
StyledCellButton,
- StyledSection,
+ StyledContainer,
+ StyledLabel,
StyledInput,
+ StyledSection,
} from './CellStyles';
+import { default as StandaloneSwitch } from './Switch';
export {
- StyledContainer as Container,
StyledFooter as Footer,
StyledFooterBoldText as FooterBoldText,
StyledFooterText as FooterText,
StyledIcon as UntintedIcon,
StyledInputFrame as InputFrame,
- StyledLabel as Label,
StyledSectionTitle as SectionTitle,
StyledSubText as SubText,
StyledTintedIcon as Icon,
} from './CellStyles';
-export { default as Switch } from './Switch';
-
const CellSectionContext = React.createContext<boolean>(false);
+const CellDisabledContext = React.createContext<boolean>(false);
+
+interface IContainerProps extends React.HTMLAttributes<HTMLDivElement> {
+ disabled?: boolean;
+}
+
+export function Container({ disabled, ...otherProps }: IContainerProps) {
+ return (
+ <CellDisabledContext.Provider value={disabled ?? false}>
+ <StyledContainer {...otherProps} />
+ </CellDisabledContext.Provider>
+ );
+}
interface ICellButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
selected?: boolean;
@@ -50,6 +62,16 @@ export function Section(props: ISectionProps) {
);
}
+export function Label(props: React.HTMLAttributes<HTMLDivElement>) {
+ const disabled = useContext(CellDisabledContext);
+ return <StyledLabel disabled={disabled} {...props} />;
+}
+
+export function Switch(props: StandaloneSwitch['props']) {
+ const disabled = useContext(CellDisabledContext);
+ return <StandaloneSwitch disabled={disabled} {...props} />;
+}
+
interface IInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
value?: string;
validateValue?: (value: string) => boolean;
@@ -104,16 +126,21 @@ export class Input extends React.Component<IInputProps, IInputState> {
} = this.props;
return (
- <StyledInput
- type="text"
- valid={validateValue?.(this.state.value)}
- onChange={this.onChange}
- onFocus={this.onFocus}
- onBlur={this.onBlur}
- onKeyPress={this.onKeyPress}
- value={this.state.value}
- {...otherProps}
- />
+ <CellDisabledContext.Consumer>
+ {(disabled) => (
+ <StyledInput
+ type="text"
+ valid={validateValue?.(this.state.value)}
+ onChange={this.onChange}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
+ onKeyPress={this.onKeyPress}
+ value={this.state.value}
+ disabled={disabled}
+ {...otherProps}
+ />
+ )}
+ </CellDisabledContext.Consumer>
);
}
diff --git a/gui/src/renderer/components/CellStyles.tsx b/gui/src/renderer/components/CellStyles.tsx
index 40b27abbb0..9ae67f733c 100644
--- a/gui/src/renderer/components/CellStyles.tsx
+++ b/gui/src/renderer/components/CellStyles.tsx
@@ -51,7 +51,7 @@ export const StyledCellButton = styled.button({}, (props: IStyledCellButtonProps
},
}));
-export const StyledLabel = styled.div({
+export const StyledLabel = styled.div({}, (props: { disabled: boolean }) => ({
margin: '14px 0 14px 8px',
flex: 1,
fontFamily: 'DINPro',
@@ -59,9 +59,9 @@ export const StyledLabel = styled.div({
fontWeight: 900,
lineHeight: '26px',
letterSpacing: -0.2,
- color: colors.white,
+ color: props.disabled ? colors.white40 : colors.white,
textAlign: 'left',
-});
+}));
export const StyledSubText = styled.span({
color: colors.white60,
diff --git a/gui/src/renderer/components/Switch.tsx b/gui/src/renderer/components/Switch.tsx
index 7a1555c761..5279181cda 100644
--- a/gui/src/renderer/components/Switch.tsx
+++ b/gui/src/renderer/components/Switch.tsx
@@ -6,6 +6,7 @@ interface IProps {
isOn: boolean;
onChange?: (isOn: boolean) => void;
className?: string;
+ disabled?: boolean;
}
interface IState {
@@ -15,30 +16,37 @@ interface IState {
const PAN_DISTANCE = 10;
-const SwitchContainer = styled.div({
+const SwitchContainer = styled.div({}, (props: { disabled: boolean }) => ({
position: 'relative',
width: '52px',
height: '32px',
- borderColor: colors.white,
+ borderColor: props.disabled ? colors.white20 : colors.white80,
borderWidth: '2px',
borderStyle: 'solid',
borderRadius: '16px',
padding: '2px',
-});
-
-const Knob = styled.div({}, (props: { isOn: boolean; isPressed: boolean }) => ({
- position: 'absolute',
- height: '24px',
- borderRadius: '12px',
- transition: 'all 200ms linear',
- width: props.isPressed ? '28px' : '24px',
- backgroundColor: props.isOn ? colors.green : colors.red,
- // When enabled the button should be placed all the way to the right (100%) minus padding (2px).
- left: props.isOn ? 'calc(100% - 2px)' : '2px',
- // This moves the knob to the left making the right side aligned with the parent's right side.
- transform: `translateX(${props.isOn ? '-100%' : '0'})`,
}));
+const Knob = styled.div({}, (props: { isOn: boolean; isPressed: boolean; disabled: boolean }) => {
+ let backgroundColor = props.isOn ? colors.green : colors.red;
+ if (props.disabled) {
+ backgroundColor = props.isOn ? colors.green40 : colors.red40;
+ }
+
+ return {
+ position: 'absolute',
+ height: '24px',
+ borderRadius: '12px',
+ transition: 'all 200ms linear',
+ width: props.isPressed ? '28px' : '24px',
+ backgroundColor,
+ // When enabled the button should be placed all the way to the right (100%) minus padding (2px).
+ left: props.isOn ? 'calc(100% - 2px)' : '2px',
+ // This moves the knob to the left making the right side aligned with the parent's right side.
+ transform: `translateX(${props.isOn ? '-100%' : '0'})`,
+ };
+});
+
export default class Switch extends React.Component<IProps, IState> {
public state: IState = {
isOn: this.props.isOn,
@@ -74,8 +82,10 @@ export default class Switch extends React.Component<IProps, IState> {
<SwitchContainer
ref={this.containerRef}
onClick={this.handleClick}
+ disabled={this.props.disabled ?? false}
className={this.props.className}>
<Knob
+ disabled={this.props.disabled ?? false}
isOn={this.state.isOn}
isPressed={this.state.isPressed}
onMouseDown={this.handleMouseDown}
@@ -85,6 +95,10 @@ export default class Switch extends React.Component<IProps, IState> {
}
private handleClick = () => {
+ if (this.props.disabled) {
+ return;
+ }
+
if (!this.changedDuringPan) {
this.setState((state) => ({ isOn: !state.isOn }), this.notify);
}
@@ -94,6 +108,10 @@ export default class Switch extends React.Component<IProps, IState> {
};
private handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
+ if (this.props.disabled) {
+ return;
+ }
+
this.isPanning = true;
this.startPos = event.clientX;
this.changedDuringPan = false;
@@ -103,6 +121,10 @@ export default class Switch extends React.Component<IProps, IState> {
};
private handleMouseUp = (event: MouseEvent) => {
+ if (this.props.disabled) {
+ return;
+ }
+
document.removeEventListener('mouseup', this.handleMouseUp);
document.removeEventListener('mousemove', this.handleMouseMove);
@@ -119,6 +141,10 @@ export default class Switch extends React.Component<IProps, IState> {
};
private handleMouseMove = (event: MouseEvent) => {
+ if (this.props.disabled) {
+ return;
+ }
+
if (this.isPanning) {
this.setState({ isPressed: true });