-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
5009 mobile modal improvements #6065
base: develop
Are you sure you want to change the base?
Changes from all commits
1035c94
cde3b3c
aea039b
c5a7593
bd6b9a5
3d89baf
bf79571
34b53f9
f686c6b
65dd51f
5207e6a
8cf59d6
4ff8db4
1dd53fa
143e7fc
e4fcc4f
1b18f3a
166c727
af59d76
4e7423b
c16c6b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,11 @@ import DialogActions from '@mui/material/DialogActions'; | |
import DialogContent, { DialogContentProps } from '@mui/material/DialogContent'; | ||
import { TransitionProps } from '@mui/material/transitions'; | ||
import { Slide } from '../../ui/animations'; | ||
import { BasicModal, ModalTitle } from '@common/components'; | ||
import { useIntlUtils } from '@common/intl'; | ||
import { SxProps, Theme } from '@mui/material'; | ||
import { BasicModal, IconButton, ModalTitle } from '@common/components'; | ||
import { useIntlUtils, useTranslation } from '@common/intl'; | ||
import { SxProps, Theme, useMediaQuery } from '@mui/material'; | ||
import { useKeyboardContext } from '../useKeyboard'; | ||
import { CloseIcon } from '@common/icons'; | ||
|
||
type OkClickEvent = React.MouseEvent<HTMLButtonElement, MouseEvent>; | ||
|
||
|
@@ -53,6 +55,7 @@ export interface DialogProps { | |
animationTimeout?: number; | ||
disableBackdrop?: boolean; | ||
disableEscapeKey?: boolean; | ||
disableMobileFullScreen?: boolean; | ||
} | ||
|
||
interface DialogState { | ||
|
@@ -98,6 +101,7 @@ const useSlideAnimation = (isRtl: boolean, timeout: number) => { | |
* @property {number} [animationTimeout=500] the timeout for the slide animation | ||
* @property {boolean} [disableBackdrop=false] (optional) disable clicking the backdrop to close the modal | ||
* @property {boolean} [disableEscape=false] (optional) disable pressing of the escape key to close the modal | ||
* @property {boolean} [disableMobileFullScreen=false] (optional) disable modal entering fullscreen mode on smaller screens | ||
* @property {boolean} isOpen (optional) is the modal open | ||
* @property {function} onClose (optional) method to run on closing the modal | ||
* @return {DialogState} the dialog state. Properties are: | ||
|
@@ -113,6 +117,7 @@ export const useDialog = (dialogProps?: DialogProps): DialogState => { | |
animationTimeout = 500, | ||
disableBackdrop = true, | ||
disableEscapeKey = false, | ||
disableMobileFullScreen = false, | ||
} = dialogProps ?? {}; | ||
const [open, setOpen] = React.useState(false); | ||
const showDialog = useCallback(() => setOpen(true), []); | ||
|
@@ -161,6 +166,9 @@ export const useDialog = (dialogProps?: DialogProps): DialogState => { | |
isRtl, | ||
animationTimeout | ||
); | ||
const { isOpen: keyboardIsOpen } = useKeyboardContext(); | ||
const isSmallerScreen = useMediaQuery('(max-height: 850px)'); | ||
const t = useTranslation(); | ||
|
||
const defaultPreventedOnClick = | ||
(onClick: (e?: OkClickEvent) => Promise<boolean>) => | ||
|
@@ -215,6 +223,8 @@ export const useDialog = (dialogProps?: DialogProps): DialogState => { | |
width: width ? Math.min(window.innerWidth - 50, width) : undefined, | ||
}; | ||
|
||
const fullScreen = isSmallerScreen && !disableMobileFullScreen; | ||
|
||
return ( | ||
<BasicModal | ||
open={open} | ||
|
@@ -224,7 +234,20 @@ export const useDialog = (dialogProps?: DialogProps): DialogState => { | |
sx={sx} | ||
TransitionComponent={Transition} | ||
disableEscapeKeyDown={false} | ||
fullScreen={fullScreen} | ||
> | ||
{fullScreen && ( | ||
<IconButton | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some modals used clicking on the backdrop to close (e.g. internal order create) so added close button |
||
icon={<CloseIcon />} | ||
color="primary" | ||
onClick={() => { | ||
onClose && onClose(); | ||
hideDialog(); | ||
}} | ||
sx={{ position: 'absolute', right: 0, top: 0, padding: 2 }} | ||
label={t('button.close')} | ||
/> | ||
)} | ||
{title ? <ModalTitle title={title} /> : null} | ||
<form | ||
style={{ | ||
|
@@ -250,8 +273,8 @@ export const useDialog = (dialogProps?: DialogProps): DialogState => { | |
<DialogActions | ||
sx={{ | ||
justifyContent: 'center', | ||
marginBottom: '30px', | ||
marginTop: '30px', | ||
marginBottom: keyboardIsOpen ? 0 : '30px', | ||
marginTop: keyboardIsOpen ? 0 : '30px', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes the margins around the buttons smaller while using the keyboard (because they take up a lot of space) |
||
}} | ||
> | ||
{cancelButton} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import React, { useContext, useEffect, useState } from 'react'; | ||
import { Keyboard } from '@capacitor/keyboard'; | ||
import { Capacitor } from '@capacitor/core'; | ||
import { createRegisteredContext } from 'react-singleton-context'; | ||
|
||
interface KeyboardControl { | ||
isOpen: boolean; | ||
isEnabled: boolean; | ||
} | ||
|
||
const defaultKeyboardControl: KeyboardControl = { | ||
isOpen: false, | ||
isEnabled: false, | ||
}; | ||
|
||
const KeyboardContext = createRegisteredContext<KeyboardControl>( | ||
'keyboard-context', | ||
defaultKeyboardControl | ||
); | ||
|
||
const { Provider } = KeyboardContext; | ||
|
||
export const KeyboardProvider = ({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) => { | ||
const [isOpen, setOpen] = useState(false); | ||
const isEnabled = Capacitor.isPluginAvailable('Keyboard'); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
if (!isEnabled) return; | ||
|
||
const showListener = await Keyboard.addListener('keyboardDidShow', () => | ||
setOpen(true) | ||
); | ||
const hideListener = await Keyboard.addListener('keyboardDidHide', () => | ||
setOpen(false) | ||
); | ||
|
||
return () => { | ||
showListener.remove(); | ||
hideListener.remove(); | ||
}; | ||
})(); | ||
}, []); | ||
|
||
return ( | ||
<Provider | ||
value={{ | ||
isOpen, | ||
isEnabled, | ||
}} | ||
> | ||
{children} | ||
</Provider> | ||
); | ||
}; | ||
|
||
export const useKeyboardContext = () => { | ||
const keyboardControl = useContext(KeyboardContext); | ||
return keyboardControl; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Keyboard'; |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,11 @@ export const InputModal = ({ | |
onClose, | ||
title, | ||
}: InputModalProps) => { | ||
const { Modal } = useDialog({ isOpen, onClose }); | ||
const { Modal } = useDialog({ | ||
isOpen, | ||
onClose, | ||
disableMobileFullScreen: true, | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Input modal is small (used for like tax input) so don't need the full screen capability here |
||
const [loading, setLoading] = useState(false); | ||
|
||
return ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { Box, BoxProps, Portal } from '@mui/material'; | ||
import { styled } from '@mui/material/styles'; | ||
import React, { FC, ReactNode, useEffect, useRef } from 'react'; | ||
import { useHostContext } from '@common/hooks'; | ||
import { useHostContext, useKeyboardContext } from '@common/hooks'; | ||
import { useIsCentralServerApi } from '@openmsupply-client/common'; | ||
|
||
const Container = styled('div')(() => ({ | ||
|
@@ -17,6 +17,7 @@ const Container = styled('div')(() => ({ | |
export const AppFooter: FC = () => { | ||
const { setAppFooterRef, setAppSessionDetailsRef, fullScreen } = | ||
useHostContext(); | ||
const { isOpen: keyboardOpen } = useKeyboardContext(); | ||
const appFooterRef = useRef(null); | ||
const appSessionDetailsRef = useRef(null); | ||
const isCentralServer = useIsCentralServerApi(); | ||
|
@@ -26,8 +27,10 @@ export const AppFooter: FC = () => { | |
setAppSessionDetailsRef(appSessionDetailsRef); | ||
}, []); | ||
|
||
const hideFooter = fullScreen || keyboardOpen; | ||
|
||
return ( | ||
<Box sx={{ display: fullScreen ? 'none' : undefined }}> | ||
<Box sx={{ display: hideFooter ? 'none' : undefined }}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When not in modal, i.e. typing in a table filter, hide the footers so can see more of the results |
||
<Container ref={appFooterRef} style={{ flex: 0 }} /> | ||
<Container | ||
ref={appSessionDetailsRef} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Felt like a reasonable height to be okay with sticking with modals as they are, most tablets around 750-800 high... happy to play around with this