Skip to content

Commit

Permalink
Merge pull request #389 from pixiv/toshusai/fix-modal-overflow
Browse files Browse the repository at this point in the history
fix: Modalの挙動と外観を修正
  • Loading branch information
toshusai authored Oct 20, 2023
2 parents 3c52f45 + 1f78e48 commit fd86546
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 108 deletions.
20 changes: 16 additions & 4 deletions packages/react/src/components/Modal/ModalPlumbing.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { ModalTitle } from '.'
import styled from 'styled-components'
import { BottomSheet, ModalContext, ModalTitle } from '.'
import styled, { css } from 'styled-components'
import { theme } from '../../styled'
import { useContext } from 'react'
import { maxWidth } from '@charcoal-ui/utils'

export function ModalHeader() {
const modalCtx = useContext(ModalContext)
return (
<ModalHeaderRoot>
<ModalHeaderRoot $bottomSheet={modalCtx.bottomSheet}>
<StyledModalTitle />
</ModalHeaderRoot>
)
}

const ModalHeaderRoot = styled.div`
const ModalHeaderRoot = styled.div<{
$bottomSheet: BottomSheet
}>`
height: 64px;
display: grid;
align-content: center;
justify-content: center;
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
${(p) =>
p.$bottomSheet !== false &&
css`
height: 48px;
`}
}
`

const StyledModalTitle = styled(ModalTitle)`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Story } from '../../../_lib/compat'
import Modal, { BottomSheet, ModalProps } from '..'
import { OverlayProvider } from '@react-aria/overlays'
import { useOverlayTriggerState } from 'react-stately'
import Button from '../../Button'
import { ModalBody, ModalButtons, ModalHeader } from '../ModalPlumbing'
import styled, { css } from 'styled-components'
import { maxWidth } from '@charcoal-ui/utils'

export const InternalScrollStory: Story<ModalProps> = (args: ModalProps) => {
const state = useOverlayTriggerState({})
return (
<OverlayProvider>
<Button onClick={() => state.open()}>Open Modal</Button>

<Modal {...args} isOpen={state.isOpen} onClose={() => state.close()}>
<ModalHeader />
<ModalBody>
<ModalBodyOverflowDiv $offset={56} $bottomSheet={args.bottomSheet}>
<div
style={{
height: 1000,
background: `linear-gradient(#e66465, #9198e5)`,
}}
></div>
</ModalBodyOverflowDiv>
<TopBorderButtons>
<Button fullWidth onClick={() => state.close()}>
Close
</Button>
</TopBorderButtons>
</ModalBody>
</Modal>
</OverlayProvider>
)
}

// underlay padding-top: 40px (desktop)
// underlay padding-bottom: 40px (desktop)
// modal header: 64px (desktop)
// modal header: 48px (mobile-bottom-sheet)
// modal padding-bottom: 40px
// button and space: 56px
const MAX_HEIGHT_OFFSET = 64 + 40 + 40 + 40
const MAX_HEIGHT_OFFSET_MOBILE = MAX_HEIGHT_OFFSET - 40 * 2 - 16
const ModalBodyOverflowDiv = styled.div<{
$offset: number
$bottomSheet?: BottomSheet
}>`
overflow: auto;
max-height: calc(
100vh - ${MAX_HEIGHT_OFFSET}px - ${({ $offset }) => $offset}px
);
${({ $bottomSheet, $offset }) =>
($bottomSheet === true || $bottomSheet === 'full') &&
css`
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
max-height: calc(100vh - ${MAX_HEIGHT_OFFSET_MOBILE}px - ${$offset}px);
}
`}
`

const TopBorderButtons = styled(ModalButtons)`
position: relative;
&::before {
content: '';
pointer-events: none;
border-top: 1px solid ${({ theme }) => theme.border.default.color};
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
`
79 changes: 41 additions & 38 deletions packages/react/src/components/Modal/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,51 +43,52 @@ const DefaultStory = (args: ModalProps) => {
// hidden from screen readers when a modal opens.
<OverlayProvider>
<Button onClick={() => state.open()}>Open Modal</Button>

<Modal
<M
{...args}
isDismissable
isOpen={state.isOpen}
onClose={() => state.close()}
isDismissable
>
<ModalHeader />
<ModalBody>
<ModalVStack>
<StyledModalText>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
placeat tenetur, necessitatibus laudantium cumque exercitationem
provident. Quaerat iure enim, eveniet dolores earum odio quo
possimus fugiat aspernatur, numquam, commodi repellat.
</StyledModalText>
<ModalAlign>
<TextField
showLabel
label="Name"
placeholder="Nagisa"
></TextField>
</ModalAlign>
<ModalAlign>
<TextField
showLabel
label="Country"
placeholder="Tokyo"
></TextField>
</ModalAlign>
</ModalVStack>
<ModalButtons>
<Button variant="Primary" onClick={() => state.close()} fullWidth>
Apply
</Button>
<Button onClick={() => state.close()} fullWidth>
Cancel
</Button>
</ModalButtons>
</ModalBody>
</Modal>
/>
</OverlayProvider>
)
}

const M = (props: ModalProps) => {
return (
<Modal {...props}>
<ModalHeader />
<ModalBody>
<ModalVStack>
<StyledModalText>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
placeat tenetur, necessitatibus laudantium cumque exercitationem
provident. Quaerat iure enim, eveniet dolores earum odio quo
possimus fugiat aspernatur, numquam, commodi repellat.
</StyledModalText>
<ModalAlign>
<TextField showLabel label="Name" placeholder="Nagisa"></TextField>
</ModalAlign>
<ModalAlign>
<TextField
showLabel
label="Country"
placeholder="Tokyo"
></TextField>
</ModalAlign>
</ModalVStack>
<ModalButtons>
<Button variant="Primary" onClick={() => props.onClose()} fullWidth>
Apply
</Button>
<Button onClick={() => props.onClose()} fullWidth>
Cancel
</Button>
</ModalButtons>
</ModalBody>
</Modal>
)
}

const ModalVStack = styled.div`
display: grid;
gap: 24px;
Expand Down Expand Up @@ -192,3 +193,5 @@ const BottomSheetStory = (args: ModalProps) => {
}

export const BottomSheet: Story<ModalProps> = BottomSheetStory.bind({})

export { InternalScrollStory as InternalScroll } from './__stories__/InternalScrollStory'
Loading

0 comments on commit fd86546

Please sign in to comment.