mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-30 21:22:27 +08:00
fix(modal): fix typography and alignment on mobile (#382)
* fix(modal): fix layout of modal actions on safari mobile * fix(modal): fix vertical alignment on safari mobile * test(modal): update snapshots * docs: hide menu when modal active * docs(modal): add overlong case
This commit is contained in:
@@ -18,9 +18,8 @@ exports[`Modal customization should be supported 1`] = `
|
||||
}
|
||||
</style><div tabindex=\\"0\\" class=\\"hide-tab\\" aria-hidden=\\"true\\"></div><style>
|
||||
.wrapper {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
width: 100px;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -87,13 +86,12 @@ exports[`Modal should render correctly 1`] = `
|
||||
</style><p class=\\"\\">This is a modal</p><style>
|
||||
p {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
line-height: 1.5rem;
|
||||
height: 1.5rem;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
word-break: break-word;
|
||||
text-transform: uppercase;
|
||||
color: #666;
|
||||
@@ -102,8 +100,8 @@ exports[`Modal should render correctly 1`] = `
|
||||
.content {
|
||||
margin: 0 -16pt;
|
||||
padding: 16pt 16pt 8pt;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content > :global(*:first-child) {
|
||||
@@ -262,9 +260,8 @@ exports[`Modal should render correctly 1`] = `
|
||||
}
|
||||
</style><div tabindex=\\"0\\" class=\\"hide-tab\\" aria-hidden=\\"true\\"></div><style>
|
||||
.wrapper {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
width: 26rem;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -309,36 +306,37 @@ exports[`Modal should render correctly 1`] = `
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
</style></div></div><div class=\\"offset\\"></div><style>
|
||||
</style></div></div><style>
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
z-index: 1000;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
outline: none;
|
||||
max-width: 90%;
|
||||
width: 26rem;
|
||||
margin: 20px auto;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.offset {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
background-color: transparent;
|
||||
.backdrop:before {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.layer {
|
||||
|
||||
@@ -70,6 +70,7 @@ const ModalAction = React.forwardRef<HTMLButtonElement, React.PropsWithChildren<
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
button.btn:hover,
|
||||
button.btn:focus {
|
||||
|
||||
@@ -25,8 +25,8 @@ const ModalContent: React.FC<ModalContentProps> = ({ className, children, ...pro
|
||||
.content {
|
||||
margin: 0 -${theme.layout.gap};
|
||||
padding: ${theme.layout.gap} ${theme.layout.gap} ${theme.layout.gapHalf};
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content > :global(*:first-child) {
|
||||
|
||||
@@ -24,13 +24,12 @@ const ModalSubtitle: React.FC<ModalSubtitleProps> = ({ className, children, ...p
|
||||
<style jsx>{`
|
||||
p {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.6;
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
line-height: 1.5rem;
|
||||
height: 1.5rem;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
word-break: break-word;
|
||||
text-transform: uppercase;
|
||||
color: ${theme.palette.accents_5};
|
||||
|
||||
@@ -6,7 +6,6 @@ import { isChildElement } from '../utils/collections'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
width?: string
|
||||
visible?: boolean
|
||||
}
|
||||
|
||||
@@ -19,7 +18,6 @@ export type ModalWrapperProps = Props & typeof defaultProps
|
||||
|
||||
const ModalWrapper: React.FC<React.PropsWithChildren<ModalWrapperProps>> = ({
|
||||
className,
|
||||
width,
|
||||
children,
|
||||
visible,
|
||||
...props
|
||||
@@ -66,9 +64,8 @@ const ModalWrapper: React.FC<React.PropsWithChildren<ModalWrapperProps>> = ({
|
||||
<div tabIndex={0} className="hide-tab" aria-hidden="true" ref={tabEnd} />
|
||||
<style jsx>{`
|
||||
.wrapper {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
width: ${width};
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -41,7 +41,7 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> = ({
|
||||
wrapClassName,
|
||||
}) => {
|
||||
const portal = usePortal('modal')
|
||||
const [, setBodyHidden] = useBodyScroll()
|
||||
const [, setBodyHidden] = useBodyScroll(null, { scrollLayer: true })
|
||||
const [visible, setVisible, visibleRef] = useCurrentState<boolean>(false)
|
||||
const [withoutActionsChildren, ActionsChildren] = pickChild(children, ModalAction)
|
||||
const hasActions = ActionsChildren && React.Children.count(ActionsChildren) > 0
|
||||
@@ -80,8 +80,8 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> = ({
|
||||
if (!portal) return null
|
||||
return createPortal(
|
||||
<ModalContext.Provider value={modalConfig}>
|
||||
<Backdrop onClick={closeFromBackdrop} visible={visible}>
|
||||
<ModalWrapper visible={visible} className={wrapClassName} width={wrapperWidth}>
|
||||
<Backdrop onClick={closeFromBackdrop} visible={visible} width={wrapperWidth}>
|
||||
<ModalWrapper visible={visible} className={wrapClassName}>
|
||||
{withoutActionsChildren}
|
||||
{hasActions && <ModalActions>{ActionsChildren}</ModalActions>}
|
||||
</ModalWrapper>
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Backdrop should render correctly 1`] = `
|
||||
"<div class=\\"backdrop backdrop-wrapper-enter\\"><div class=\\"layer\\"></div><div class=\\"content\\"><span>test-value</span></div><div class=\\"offset\\"></div><style>
|
||||
"<div class=\\"backdrop backdrop-wrapper-enter\\"><div class=\\"layer\\"></div><div class=\\"content\\"><span>test-value</span></div><style>
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
z-index: 1000;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
outline: none;
|
||||
max-width: 90%;
|
||||
width: undefined;
|
||||
margin: 20px auto;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.offset {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
background-color: transparent;
|
||||
.backdrop:before {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.layer {
|
||||
|
||||
@@ -56,7 +56,6 @@ describe('Backdrop', () => {
|
||||
</Backdrop>,
|
||||
)
|
||||
wrapper.find('.content').simulate('click', nativeEvent)
|
||||
wrapper.find('.offset').simulate('click', nativeEvent)
|
||||
expect(handler).not.toHaveBeenCalled()
|
||||
handler.mockRestore()
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import useCurrentState from '../utils/use-current-state'
|
||||
interface Props {
|
||||
onClick?: (event: MouseEvent<HTMLElement>) => void
|
||||
visible?: boolean
|
||||
width?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
@@ -18,7 +19,7 @@ type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type BackdropProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const Backdrop: React.FC<React.PropsWithChildren<BackdropProps>> = React.memo(
|
||||
({ children, onClick, visible, ...props }) => {
|
||||
({ children, onClick, visible, width, ...props }) => {
|
||||
const theme = useTheme()
|
||||
const [, setIsContentMouseDown, IsContentMouseDownRef] = useCurrentState(false)
|
||||
const clickHandler = (event: MouseEvent<HTMLElement>) => {
|
||||
@@ -46,37 +47,37 @@ const Backdrop: React.FC<React.PropsWithChildren<BackdropProps>> = React.memo(
|
||||
onMouseDown={() => setIsContentMouseDown(true)}>
|
||||
{children}
|
||||
</div>
|
||||
<div onClick={childrenClickHandler} className="offset" />
|
||||
<style jsx>{`
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
z-index: 1000;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
outline: none;
|
||||
max-width: 90%;
|
||||
width: ${width};
|
||||
margin: 20px auto;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.offset {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
background-color: transparent;
|
||||
.backdrop:before {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.layer {
|
||||
|
||||
@@ -81,7 +81,7 @@ const MenuSticker = () => {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1500;
|
||||
z-index: 999;
|
||||
background-color: ${theme.palette.background};
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 0 15px 0;
|
||||
}
|
||||
|
||||
@@ -183,6 +183,51 @@ Display popup content that requires attention or provides additional information
|
||||
`}
|
||||
/>
|
||||
|
||||
<Playground
|
||||
title="Overlong"
|
||||
desc="Whether it is desktop or mobile, the content beyond the scope has been well handled."
|
||||
scope={{ Modal, Button, useModal }}
|
||||
code={`
|
||||
() => {
|
||||
const { visible, setVisible, bindings } = useModal()
|
||||
return (
|
||||
<>
|
||||
<Button auto onClick={() => setVisible(true)}>Show Modal</Button>
|
||||
<Modal {...bindings}>
|
||||
<Modal.Title>Modal</Modal.Title>
|
||||
<Modal.Subtitle>Modal with a lot of content</Modal.Subtitle>
|
||||
<Modal.Content>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
<p>An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications.
|
||||
An open-source design system for building modern websites and applications. </p>
|
||||
</Modal.Content>
|
||||
<Modal.Action passive onClick={() => setVisible(false)}>Cancel</Modal.Action>
|
||||
<Modal.Action>Submit</Modal.Action>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
`}
|
||||
/>
|
||||
|
||||
<Attributes edit="/pages/en-us/components/modal.mdx">
|
||||
<Attributes.Title>Modal.Props</Attributes.Title>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user