diff options
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettings.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorView.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx | 1 | ||||
| -rw-r--r-- | gui/src/renderer/components/Modal.tsx | 66 | ||||
| -rw-r--r-- | gui/src/renderer/components/RedeemVoucher.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/Support.tsx | 2 |
6 files changed, 59 insertions, 19 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx index 2e9dd497be..5baeaced3a 100644 --- a/gui/src/renderer/components/AdvancedSettings.tsx +++ b/gui/src/renderer/components/AdvancedSettings.tsx @@ -459,7 +459,8 @@ export default class AdvancedSettings extends React.Component<IProps, IState> { <AppButton.BlueButton key="back" onClick={this.hideConfirmBlockWhenDisconnectedAlert}> {messages.gettext('Back')} </AppButton.BlueButton>, - ]}> + ]} + close={this.hideConfirmBlockWhenDisconnectedAlert}> <ModalMessage> {messages.pgettext( 'advanced-settings-view', diff --git a/gui/src/renderer/components/ExpiredAccountErrorView.tsx b/gui/src/renderer/components/ExpiredAccountErrorView.tsx index 95177765a5..d64c780808 100644 --- a/gui/src/renderer/components/ExpiredAccountErrorView.tsx +++ b/gui/src/renderer/components/ExpiredAccountErrorView.tsx @@ -194,7 +194,8 @@ export default class ExpiredAccountErrorView extends React.Component< onClick={this.onCloseBlockWhenDisconnectedInstructions}> {messages.gettext('Close')} </AppButton.BlueButton>, - ]}> + ]} + close={this.onCloseBlockWhenDisconnectedInstructions}> <ModalMessage> {messages.pgettext( 'connect-view', diff --git a/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx b/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx index 914f5d818f..c5b5521f05 100644 --- a/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx +++ b/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx @@ -274,6 +274,7 @@ function ApplicationRow(props: IApplicationRowProps) { iconColor={warningColor} message={warningMessage} buttons={warningDialogButtons} + close={hideWarningDialog} /> )} </> diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx index 3db4dcd626..b045803626 100644 --- a/gui/src/renderer/components/Modal.tsx +++ b/gui/src/renderer/components/Modal.tsx @@ -1,12 +1,10 @@ -import * as React from 'react'; +import React, { useContext, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import styled from 'styled-components'; import { colors } from '../../config.json'; import { Scheduler } from '../../shared/scheduler'; import ImageView from './ImageView'; -const MODAL_CONTAINER_ID = 'modalContainer'; - const ModalContent = styled.div({ position: 'absolute', display: 'flex', @@ -39,11 +37,35 @@ interface IModalContainerProps { children?: React.ReactNode; } +interface IModalContext { + activeModal: boolean; + setActiveModal: (value: boolean) => void; + modalContainerRef: React.RefObject<HTMLDivElement>; +} + +const noActiveModalContextError = new Error('ActiveModalContext.Provider missing'); +const ActiveModalContext = React.createContext<IModalContext>({ + get activeModal(): boolean { + throw noActiveModalContextError; + }, + setActiveModal(_value) { + throw noActiveModalContextError; + }, + get modalContainerRef(): React.RefObject<HTMLDivElement> { + throw noActiveModalContextError; + }, +}); + export function ModalContainer(props: IModalContainerProps) { + const [activeModal, setActiveModal] = useState(false); + const modalContainerRef = useRef() as React.RefObject<HTMLDivElement>; + return ( - <StyledModalContainer id={MODAL_CONTAINER_ID}> - <ModalContent>{props.children}</ModalContent> - </StyledModalContainer> + <ActiveModalContext.Provider value={{ activeModal, setActiveModal, modalContainerRef }}> + <StyledModalContainer ref={modalContainerRef}> + <ModalContent aria-hidden={activeModal}>{props.children}</ModalContent> + </StyledModalContainer> + </ActiveModalContext.Provider> ); } @@ -86,18 +108,24 @@ interface IModalAlertProps { message?: string; buttons: React.ReactNode[]; children?: React.ReactNode; + close?: () => void; +} + +export function ModalAlert(props: IModalAlertProps) { + const activeModalContext = useContext(ActiveModalContext); + return <ModalAlertWithContext {...activeModalContext} {...props} />; } -export class ModalAlert extends React.Component<IModalAlertProps> { +class ModalAlertWithContext extends React.Component<IModalAlertProps & IModalContext> { private element = document.createElement('div'); - private modalContainer?: Element; private appendScheduler = new Scheduler(); public componentDidMount() { - const modalContainer = document.getElementById(MODAL_CONTAINER_ID); - if (modalContainer) { - this.modalContainer = modalContainer; + this.props.setActiveModal(true); + document.addEventListener('keydown', this.handleKeyPress); + const modalContainer = this.props.modalContainerRef.current; + if (modalContainer) { // Mounting the container element immediately results in a graphical issue with the dialog // first rendering with the wrong proportions and then changing to the correct proportions. // Postponing it to the next event cycle solves this issue. @@ -110,11 +138,11 @@ export class ModalAlert extends React.Component<IModalAlertProps> { } public componentWillUnmount() { - this.appendScheduler.cancel(); + this.props.setActiveModal(false); + document.removeEventListener('keydown', this.handleKeyPress); - if (this.modalContainer) { - this.modalContainer.removeChild(this.element); - } + this.appendScheduler.cancel(); + this.props.modalContainerRef.current?.removeChild(this.element); } public render() { @@ -125,7 +153,7 @@ export class ModalAlert extends React.Component<IModalAlertProps> { return ( <ModalBackground> <ModalAlertContainer> - <StyledModalAlert> + <StyledModalAlert role="alertdialog"> {this.props.type && ( <ModalAlertIcon>{this.renderTypeIcon(this.props.type)}</ModalAlertIcon> )} @@ -157,6 +185,12 @@ export class ModalAlert extends React.Component<IModalAlertProps> { <ImageView height={44} width={44} source={source} tintColor={this.props.iconColor ?? color} /> ); } + + private handleKeyPress = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + this.props.close?.(); + } + }; } export const ModalMessage = styled.span({ diff --git a/gui/src/renderer/components/RedeemVoucher.tsx b/gui/src/renderer/components/RedeemVoucher.tsx index 7ca83c43d1..29a84f868c 100644 --- a/gui/src/renderer/components/RedeemVoucher.tsx +++ b/gui/src/renderer/components/RedeemVoucher.tsx @@ -198,7 +198,8 @@ export function RedeemVoucherAlert(props: IRedeemVoucherAlertProps) { <AppButton.BlueButton key="cancel" disabled={cancelDisabled} onClick={props.onClose}> {messages.pgettext('redeem-voucher-alert', 'Cancel')} </AppButton.BlueButton>, - ]}> + ]} + close={props.onClose}> <StyledLabel>{messages.pgettext('redeem-voucher-alert', 'Enter voucher code')}</StyledLabel> <RedeemVoucherInput /> <RedeemVoucherResponse /> diff --git a/gui/src/renderer/components/Support.tsx b/gui/src/renderer/components/Support.tsx index 295855a9a6..c685ebe946 100644 --- a/gui/src/renderer/components/Support.tsx +++ b/gui/src/renderer/components/Support.tsx @@ -252,6 +252,7 @@ export default class Support extends React.Component<ISupportProps, ISupportStat {messages.gettext('Back')} </AppButton.BlueButton>, ]} + close={this.onCancelNoEmailDialog} /> ); } @@ -286,6 +287,7 @@ export default class Support extends React.Component<ISupportProps, ISupportStat {messages.gettext('Cancel')} </AppButton.BlueButton>, ]} + close={this.props.onClose} /> ); } |
