Skip to content

Commit 7d8f7c9

Browse files
authored
[UX] Add Accessibility option to disable closing dialogs when clicking outside (#3402)
* Add Accessibility option to disable closing dialogs when clicking outside * Add i18n
1 parent 3687527 commit 7d8f7c9

File tree

8 files changed

+68
-10
lines changed

8 files changed

+68
-10
lines changed

public/locales/en/translation.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"actions_font_family_no_default": "Actions Font Family (Default: ",
44
"all_tiles_in_color": "Show all game tiles in color",
55
"content_font_family_no_default": "Content Font Family (Default: ",
6+
"disable_dialog_backdrop_close": "Disable closing dialogs by clicking outside",
67
"fonts": "Fonts",
78
"title": "Accessibility",
89
"zoom": "Zoom"

src/common/types/electron_store.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export interface StoreStructure {
3434
contentFontFamily: string
3535
actionsFontFamily: string
3636
allTilesInColor: boolean
37+
disableDialogBackdropClose: boolean
3738
language: string
3839
'general-logs': {
3940
currentLogFile: string

src/frontend/components/UI/Dialog/components/Dialog.tsx

+26-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { faXmark } from '@fortawesome/free-solid-svg-icons'
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3+
import ContextProvider from 'frontend/state/ContextProvider'
34
import React, {
5+
KeyboardEvent,
46
ReactNode,
57
SyntheticEvent,
68
useCallback,
9+
useContext,
710
useEffect,
811
useRef,
912
useState
@@ -26,6 +29,7 @@ export const Dialog: React.FC<DialogProps> = ({
2629
const onCloseRef = useRef(onClose)
2730
onCloseRef.current = onClose
2831
const [focusOnClose, setFocusOnClose] = useState<HTMLElement | null>(null)
32+
const { disableDialogBackdropClose } = useContext(ContextProvider)
2933

3034
useEffect(() => {
3135
setFocusOnClose(document.querySelector('*:focus') as HTMLElement)
@@ -45,15 +49,25 @@ export const Dialog: React.FC<DialogProps> = ({
4549
close()
4650
}
4751
dialog.addEventListener('cancel', cancel)
48-
dialog['showPopover']()
4952

50-
return () => {
51-
dialog.removeEventListener('cancel', cancel)
52-
dialog['hidePopover']()
53+
if (disableDialogBackdropClose) {
54+
dialog['showPopover']()
55+
56+
return () => {
57+
dialog.removeEventListener('cancel', cancel)
58+
dialog['hidePopover']()
59+
}
60+
} else {
61+
dialog.showModal()
62+
63+
return () => {
64+
dialog.removeEventListener('cancel', cancel)
65+
dialog.close()
66+
}
5367
}
5468
}
5569
return
56-
}, [dialogRef.current])
70+
}, [dialogRef.current, disableDialogBackdropClose])
5771

5872
const onDialogClick = useCallback(
5973
(e: SyntheticEvent) => {
@@ -73,6 +87,12 @@ export const Dialog: React.FC<DialogProps> = ({
7387
[onClose]
7488
)
7589

90+
const closeIfEsc = (event: KeyboardEvent<HTMLDialogElement>) => {
91+
if (event.key === 'Escape') {
92+
close()
93+
}
94+
}
95+
7696
return (
7797
<div className="Dialog">
7898
<dialog
@@ -82,6 +102,7 @@ export const Dialog: React.FC<DialogProps> = ({
82102
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
83103
// @ts-ignore, this feature is new and not yet typed
84104
popover="manual"
105+
onKeyUp={closeIfEsc}
85106
>
86107
{showCloseButton && (
87108
<div className="Dialog__Close">

src/frontend/components/UI/Dialog/index.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
background: rgba(0, 0, 0, 0.4);
3535
}
3636

37-
.Dialog__element:popover-open {
37+
.Dialog__element:popover-open,
38+
.Dialog__element[open] {
3839
opacity: 1;
3940
transform: translateY(0);
4041
box-shadow: 0px 0px 0px 100vmax var(--modal-backdrop);

src/frontend/screens/Accessibility/index.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export default React.memo(function Accessibility() {
2626
allTilesInColor,
2727
setAllTilesInColor,
2828
setPrimaryFontFamily,
29-
setSecondaryFontFamily
29+
setSecondaryFontFamily,
30+
disableDialogBackdropClose,
31+
setDisableDialogBackdropClose
3032
} = useContext(ContextProvider)
3133

3234
hasHelp(
@@ -188,6 +190,7 @@ export default React.memo(function Accessibility() {
188190
</SelectField>
189191

190192
<ThemeSelector />
193+
191194
<span className="setting">
192195
<label className={classNames('toggleWrapper', { isRTL: isRTL })}>
193196
<ToggleSwitch
@@ -203,6 +206,22 @@ export default React.memo(function Accessibility() {
203206
/>
204207
</label>
205208
</span>
209+
210+
<span className="setting">
211+
<label className={classNames('toggleWrapper', { isRTL: isRTL })}>
212+
<ToggleSwitch
213+
htmlId="disableDialogBackdropClose"
214+
value={disableDialogBackdropClose}
215+
handleChange={() => {
216+
setDisableDialogBackdropClose(!disableDialogBackdropClose)
217+
}}
218+
title={t(
219+
'accessibility.disable_dialog_backdrop_close',
220+
'Disable closing dialogs by clicking outside'
221+
)}
222+
/>
223+
</label>
224+
</span>
206225
</div>
207226
</div>
208227
)

src/frontend/state/ContextProvider.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ const initialContext: ContextType = {
9696
enableHelp: false,
9797
automaticWinetricksFixes: false
9898
},
99-
handleExperimentalFeatures: () => null
99+
handleExperimentalFeatures: () => null,
100+
disableDialogBackdropClose: false,
101+
setDisableDialogBackdropClose: () => null
100102
}
101103

102104
export default React.createContext(initialContext)

src/frontend/state/GlobalState.tsx

+13-2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ interface StateProps {
113113
}
114114
helpItems: { [key: string]: HelpItem }
115115
experimentalFeatures: ExperimentalFeatures
116+
disableDialogBackdropClose: boolean
116117
}
117118

118119
// function to load the new key or fallback to the old one
@@ -215,7 +216,11 @@ class GlobalState extends PureComponent<Props> {
215216
enableNewDesign: false,
216217
enableHelp: false,
217218
automaticWinetricksFixes: false
218-
}
219+
},
220+
disableDialogBackdropClose: configStore.get(
221+
'disableDialogBackdropClose',
222+
false
223+
)
219224
}
220225

221226
setCurrentCustomCategories = (newCustomCategories: string[]) => {
@@ -269,6 +274,11 @@ class GlobalState extends PureComponent<Props> {
269274
this.setState({ allTilesInColor: value })
270275
}
271276

277+
setDisableDialogBackdropClose = (value: boolean) => {
278+
configStore.set('disableDialogBackdropClose', value)
279+
this.setState({ disableDialogBackdropClose: value })
280+
}
281+
272282
setSideBarCollapsed = (value: boolean) => {
273283
this.setState({ sidebarCollapsed: value })
274284
}
@@ -1013,7 +1023,8 @@ class GlobalState extends PureComponent<Props> {
10131023
items: this.state.helpItems,
10141024
addHelpItem: this.addHelpItem,
10151025
removeHelpItem: this.removeHelpItem
1016-
}
1026+
},
1027+
setDisableDialogBackdropClose: this.setDisableDialogBackdropClose
10171028
}}
10181029
>
10191030
{this.props.children}

src/frontend/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ export interface ContextType {
117117
}
118118
experimentalFeatures: ExperimentalFeatures
119119
handleExperimentalFeatures: (newSetting: ExperimentalFeatures) => void
120+
disableDialogBackdropClose: boolean
121+
setDisableDialogBackdropClose: (value: boolean) => void
120122
}
121123

122124
export type DialogModalOptions = {

0 commit comments

Comments
 (0)