Skip to content

Commit

Permalink
fix: calculate Calendar position in DatePicker (SHRUI-212) (#1085)
Browse files Browse the repository at this point in the history
* chore: move calculation of position to Portal

* fix: enable to switch portal position top or bottom

* docs: add stories
  • Loading branch information
wmoai committed Oct 20, 2020
1 parent c355972 commit eeb9bda
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 15 deletions.
7 changes: 7 additions & 0 deletions src/components/DatePicker/DatePicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ storiesOf('DatePicker', module)
<dd>
<ExtendingDatePicker onChangeDate={action('change')} />
</dd>
<dt className="bottom">Place on the page bottom</dt>
<dd>
<DatePicker onChangeDate={action('change')} />
</dd>
</List>
)
})
Expand All @@ -53,6 +57,9 @@ const List = styled.dl`
dd {
margin: 10px 0 20px;
}
dt.bottom {
margin-top: 1000px;
}
`

const ExtendingDatePicker = styled(DatePicker)`
Expand Down
13 changes: 3 additions & 10 deletions src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,7 @@ export const DatePicker: FC<Props> = ({
const inputRef = useRef<HTMLInputElement>(null)
const inputWrapperRef = useRef<HTMLDivElement>(null)
const calendarRef = useRef<HTMLDivElement>(null)
const [calendarPosition, setCalendarPosition] = useState({
top: 0,
left: 0,
})
const [inputRect, setInputRect] = useState<DOMRect>(new DOMRect())
const [isInputFocused, setIsInputFocused] = useState(false)
const [isCalendarShown, setIsCalendarShown] = useState(false)

Expand Down Expand Up @@ -99,11 +96,7 @@ export const DatePicker: FC<Props> = ({
return
}
setIsCalendarShown(true)
const rect = inputWrapperRef.current.getBoundingClientRect()
setCalendarPosition({
top: rect.top + rect.height - 4 + window.pageYOffset,
left: rect.left + window.pageXOffset,
})
setInputRect(inputWrapperRef.current.getBoundingClientRect())
}, [])

useEffect(() => {
Expand Down Expand Up @@ -236,7 +229,7 @@ export const DatePicker: FC<Props> = ({
/>
</InputWrapper>
{isCalendarShown && (
<Portal {...calendarPosition}>
<Portal inputRect={inputRect}>
<Calendar
value={selectedDate || undefined}
onSelectDate={(_, selected) => {
Expand Down
33 changes: 28 additions & 5 deletions src/components/DatePicker/Portal.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { FC, ReactNode, useEffect } from 'react'
import React, { FC, ReactNode, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import styled, { css } from 'styled-components'
import { Theme, useTheme } from '../../hooks/useTheme'

import { usePortal } from '../../hooks/usePortal'
import { getPortalPosition } from './datePickerHelper'

type Props = {
top: number
left: number
inputRect: DOMRect
children: ReactNode
}

export const Portal: FC<Props> = ({ top, left, children }) => {
export const Portal: FC<Props> = ({ inputRect, children }) => {
const themes = useTheme()
const { portalRoot } = usePortal()
useEffect(() => {
Expand All @@ -22,8 +22,26 @@ export const Portal: FC<Props> = ({ top, left, children }) => {
}
}, [portalRoot])

const [position, setPosition] = useState({
top: 0,
left: 0,
})
const [isReady, setIsReady] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
// wait for createPortal
requestAnimationFrame(() => {
if (!containerRef.current) {
return
}
setPosition(getPortalPosition(inputRect, containerRef.current.offsetHeight))
setIsReady(true)
})
}, [inputRect])

return createPortal(
<Container top={top} left={left} themes={themes}>
<Container {...position} themes={themes} className={isReady ? 'ready' : ''} ref={containerRef}>
{children}
</Container>,
portalRoot,
Expand All @@ -36,5 +54,10 @@ const Container = styled.div<{ top: number; left: number; themes: Theme }>(
top: ${top}px;
left: ${left}px;
z-index: ${themes.zIndex.OVERLAP};
visibility: hidden;
&.ready {
visibility: visible;
}
`,
)
22 changes: 22 additions & 0 deletions src/components/DatePicker/datePickerHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,25 @@ export function parseJpnDateString(dateString: string): Date {

return dayjs(converted).toDate()
}

export function getPortalPosition(inputRect: DOMRect, contentHeihgt: number) {
const margin = 4
const { innerHeight, pageYOffset } = window

const left = pageXOffset + inputRect.left

const hasNoSpaceOnBottomSide = inputRect.bottom + contentHeihgt > innerHeight
const isTopSideSpaceBiggerThanBottomSide = inputRect.top > innerHeight - inputRect.bottom
if (hasNoSpaceOnBottomSide && isTopSideSpaceBiggerThanBottomSide) {
// display on top side
return {
top: pageYOffset + inputRect.top - contentHeihgt + margin,
left,
}
}
// display on bottom side
return {
top: pageYOffset + inputRect.bottom - margin,
left,
}
}

0 comments on commit eeb9bda

Please sign in to comment.