diff --git a/packages/app-desktop/gui/Dialog.tsx b/packages/app-desktop/gui/Dialog.tsx index ffdda6f61..e8cb3bda5 100644 --- a/packages/app-desktop/gui/Dialog.tsx +++ b/packages/app-desktop/gui/Dialog.tsx @@ -1,49 +1,46 @@ -import styled from 'styled-components'; - -const DialogModalLayer = styled.div` - z-index: 9999; - display: flex; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.6); - align-items: flex-start; - justify-content: center; - - overflow: auto; - scrollbar-width: none; - &::-webkit-scrollbar { - display: none; - } -`; - -const DialogRoot = styled.div` - background-color: ${props => props.theme.backgroundColor}; - padding: 16px; - box-shadow: 6px 6px 20px rgba(0,0,0,0.5); - margin: 20px; - min-height: fit-content; - display: flex; - flex-direction: column; - border-radius: 10px; -`; +import * as React from 'react'; +import { ReactElement, ReactEventHandler, useCallback, useEffect, useRef, useState } from 'react'; interface Props { - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - renderContent: Function; + renderContent: ()=> ReactElement; className?: string; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - onClose?: Function; + onClose?: ()=> void; } export default function Dialog(props: Props) { + const [dialogElement, setDialogRef] = useState(); + + useEffect(() => { + if (!dialogElement) return; + + // Use .showModal instead of the open attribute: .showModal correctly + // traps the keyboard focus in the dialog + dialogElement.showModal(); + }, [dialogElement]); + + const onCloseRef = useRef(props.onClose); + onCloseRef.current = props.onClose; + + const onCancel: ReactEventHandler = useCallback((event) => { + const canCancel = !!onCloseRef.current; + if (canCancel) { + // Prevents [Escape] from closing the dialog. In many places, this is handled + // elsewhere. + // See https://stackoverflow.com/a/61021326 + event.preventDefault(); + } + }, []); + return ( - - + +
{props.renderContent()} - - +
+
); } diff --git a/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx b/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx index d7f6bc5dd..69f3089d2 100644 --- a/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx +++ b/packages/app-desktop/gui/EditFolderDialog/IconSelector.tsx @@ -21,7 +21,7 @@ interface Props { export const IconSelector = (props: Props) => { const [emojiButtonClassReady, setEmojiButtonClassReady] = useState(false); const [picker, setPicker] = useState(); - const buttonRef = useRef(null); + const buttonRef = useRef(null); useAsyncEffect(async (event: AsyncEffectEvent) => { const loadScripts = async () => { @@ -61,6 +61,7 @@ export const IconSelector = (props: Props) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const p: EmojiButton = new (window as any).EmojiButton({ zIndex: 10000, + rootElement: buttonRef.current?.parentElement, }); const onEmoji = (selection: FolderIcon) => { @@ -73,6 +74,7 @@ export const IconSelector = (props: Props) => { return () => { p.off('emoji', onEmoji); + p.destroyPicker(); }; }, [emojiButtonClassReady, props.onChange]); diff --git a/packages/app-desktop/gui/SyncWizard/Dialog.tsx b/packages/app-desktop/gui/SyncWizard/Dialog.tsx index 8096142ca..30814a6ff 100644 --- a/packages/app-desktop/gui/SyncWizard/Dialog.tsx +++ b/packages/app-desktop/gui/SyncWizard/Dialog.tsx @@ -140,17 +140,16 @@ type SyncTargetInfoName = 'dropbox' | 'onedrive' | 'joplinCloud'; export default function(props: Props) { const joplinCloudDescriptionRef = useRef(null); - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - function closeDialog(dispatch: Function) { - dispatch({ + const closeDialog = useCallback(() => { + props.dispatch({ type: 'DIALOG_CLOSE', name: 'syncWizard', }); - } + }, [props.dispatch]); const onButtonRowClick = useCallback(() => { - closeDialog(props.dispatch); - }, [props.dispatch]); + closeDialog(); + }, [closeDialog]); const { height: descriptionHeight } = useElementSize(joplinCloudDescriptionRef); @@ -184,12 +183,12 @@ export default function(props: Props) { Setting.setValue('sync.target', route.target); await Setting.saveAll(); - closeDialog(props.dispatch); + closeDialog(); props.dispatch({ type: 'NAV_GO', routeName: route.name, }); - }, [props.dispatch]); + }, [props.dispatch, closeDialog]); function renderSelectArea(info: SyncTargetInfo) { return ( @@ -229,7 +228,7 @@ export default function(props: Props) { } const onSelfHostingClick = useCallback(() => { - closeDialog(props.dispatch); + closeDialog(); props.dispatch({ type: 'NAV_GO', @@ -238,7 +237,7 @@ export default function(props: Props) { defaultSection: 'sync', }, }); - }, [props.dispatch]); + }, [props.dispatch, closeDialog]); function renderContent() { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied @@ -278,6 +277,6 @@ export default function(props: Props) { } return ( - + ); } diff --git a/packages/app-desktop/gui/styles/dialog-modal-layer.scss b/packages/app-desktop/gui/styles/dialog-modal-layer.scss new file mode 100644 index 000000000..2576f7a14 --- /dev/null +++ b/packages/app-desktop/gui/styles/dialog-modal-layer.scss @@ -0,0 +1,28 @@ + +.dialog-modal-layer { + display: flex; + top: 0; + left: 0; + width: 100%; + height: 100%; + align-items: flex-start; + justify-content: center; + border: none; + margin: 0; + background-color: transparent; + + > .content { + background-color: var(--joplin-background-color); + padding: 16px; + box-shadow: 6px 6px 20px rgba(0,0,0,0.5); + margin: 20px; + min-height: fit-content; + display: flex; + flex-direction: column; + border-radius: 10px; + } + + &::backdrop { + background-color: rgba(0,0,0,0.6); + } +} diff --git a/packages/app-desktop/gui/styles/index.scss b/packages/app-desktop/gui/styles/index.scss new file mode 100644 index 000000000..736f60da5 --- /dev/null +++ b/packages/app-desktop/gui/styles/index.scss @@ -0,0 +1,2 @@ + +@use './dialog-modal-layer.scss'; \ No newline at end of file diff --git a/packages/app-desktop/style.scss b/packages/app-desktop/style.scss index d561298cc..c8e4008e5 100644 --- a/packages/app-desktop/style.scss +++ b/packages/app-desktop/style.scss @@ -10,4 +10,5 @@ @use 'gui/NoteListHeader/style.scss' as note-list-header; @use 'gui/TrashNotification/style.scss' as trash-notification; @use 'gui/Sidebar/style.scss' as sidebar-styles; +@use 'gui/styles/index.scss'; @use 'main.scss' as main; \ No newline at end of file