Merge pull request #20 from unix/styles

Improve styles & add body scroll hooks
This commit is contained in:
witt
2020-03-26 12:20:41 +08:00
committed by GitHub
8 changed files with 103 additions and 18 deletions

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { createPortal } from 'react-dom'
import usePortal from '../utils/use-portal'
import ModalTitle from './modal-title'
@@ -10,6 +10,7 @@ import ModalActions from './modal-actions'
import Backdrop from '../shared/backdrop'
import { ModalConfig, ModalContext } from './modal-context'
import { pickChild } from '../utils/collections'
import useBodyScroll from '../utils/use-body-scroll'
interface Props {
disableBackdropClick?: boolean
@@ -31,27 +32,30 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> = React.memo(({
children, disableBackdropClick, onClose, onOpen, open
}) => {
const portal = usePortal('modal')
const [, setBodyHidden] = useBodyScroll()
const [visible, setVisible] = useState<boolean>(open)
const [withoutActionsChildren, ActionsChildren] = pickChild(children, ModalAction)
const hasActions = ActionsChildren && React.Children.count(ActionsChildren) > 0
const closeModal = useCallback(() => {
const closeModal = () => {
setVisible(false)
onClose && onClose()
}, [open])
const openModal = useCallback(() => {
}
const openModal = () => {
setVisible(true)
onOpen && onOpen()
}, [])
}
useEffect(() => {
setVisible(open)
setBodyHidden(open)
}, [open])
const closeFromBackdrop = useCallback(() => {
const closeFromBackdrop = () => {
if (disableBackdropClick && hasActions) return
closeModal()
}, [disableBackdropClick])
}
const modalConfig: ModalConfig = useMemo(() => ({
close: closeModal,

View File

@@ -94,6 +94,7 @@ const ToastItem: React.FC<ToatItemProps> = React.memo(({
<style jsx>{`
.toast {
width: 420px;
max-width: 90vw;
max-height: 75px;
display: flex;
justify-content: space-between;

View File

@@ -0,0 +1,76 @@
import { Dispatch, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react'
export type ElementStackItem = {
last: string
}
export type BodyScrollOptions = {
scrollLayer: boolean
}
const defaultOptions: BodyScrollOptions = {
scrollLayer: false,
}
const elementStack = new Map<HTMLElement, ElementStackItem>()
const isIos = () => {
if (typeof window === 'undefined' || !window.navigator) return false
return /iP(ad|hone|od)/.test(window.navigator.platform)
}
const touchHandler = (event: TouchEvent): boolean => {
if (event.touches.length > 1) return true
event.preventDefault()
return false
}
const useBodyScroll = (
elementRef?: MutableRefObject<HTMLElement> | null,
options?: BodyScrollOptions,
): [boolean, Dispatch<SetStateAction<boolean>>] => {
if (typeof document === 'undefined') return [false, (t: boolean) => t]
const elRef = elementRef || useRef<HTMLElement>(document.body)
const [hidden, setHidden] = useState<boolean>(false)
const safeOptions = {
...defaultOptions,
...(options || {}),
}
// don't prevent touch event when layer contain scroll
const isIosWithCustom = () => {
if (safeOptions.scrollLayer) return false
return isIos()
}
useEffect(() => {
const lastOverflow = elRef.current.style.overflow
if (hidden) {
if (elementStack.has(elRef.current)) return
if (!isIosWithCustom()) {
elRef.current.style.overflow = 'hidden'
} else {
document.addEventListener('touchmove', touchHandler, { passive: false })
}
elementStack.set(elRef.current, {
last: lastOverflow,
})
return
}
// reset element overflow
if (!elementStack.has(elRef.current)) return
if (!isIosWithCustom()) {
const store = elementStack.get(elRef.current) || { last: 'auto' }
elRef.current.style.overflow = store.last
} else {
document.removeEventListener('touchmove', touchHandler)
}
elementStack.delete(elRef.current)
}, [hidden, elRef.current])
return [hidden, setHidden]
}
export default useBodyScroll

View File

@@ -5,6 +5,7 @@ import Sidebar from './sidebar'
import Controls from 'lib/components/controls'
import sides from 'lib/data/metadata.json'
import TabbarMobile from './sidebar/tabbar-mobile'
import useBodyScroll from 'components/utils/use-body-scroll'
export interface Meta {
title: string
@@ -18,9 +19,11 @@ export interface Props {
export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ children }) => {
const theme = useTheme()
const [, setBodyScroll] = useBodyScroll(null, { scrollLayer: true })
const [expanded, setExpanded] = useState<boolean>(false)
const mobileTabbarClickHandler = () => {
setExpanded(!expanded)
setBodyScroll(!expanded)
}
return (
@@ -42,6 +45,7 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
margin: 0 auto;
padding: 0 ${theme.layout.gap};
display: flex;
box-sizing: border-box;
}
.sidebar {
@@ -85,7 +89,7 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
width: 100vw;
height: ${expanded ? '100vh' : '0'};
background-color: ${theme.palette.background};
padding: 50px 12vw 0;
padding: 0;
overflow: hidden;
transition: height 250ms ease;
}

View File

@@ -90,6 +90,7 @@ const editor = (code: string) => {
justify-content: center;
align-items: center;
height: 100%;
width: fit-content;
}
summary :global(svg) {

View File

@@ -21,7 +21,6 @@ const ActiveCatalog: React.FC<Props> = React.memo(
transition: all .2s ease;
color: ${theme.palette.accents_3};
text-transform: uppercase;
padding-bottom: .5rem;
}
.active {

View File

@@ -66,7 +66,9 @@ export const Sidebar: React.FC<SideGroupProps> = React.memo(({ sides }) => {
@media only screen and (max-width: 767px) {
.box {
padding-top: calc(1.5 * ${theme.layout.gap});
padding: calc(3.5 * ${theme.layout.gap}) 15vw;
width: 100vw;
height: 100%;
}
}
`}</style>

View File

@@ -43,11 +43,6 @@ const SideItem: React.FC<React.PropsWithChildren<SideItemProps>> = React.memo(({
})}
<style jsx>{`
.item {
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
align-items: flex-start;
width: 100%;
}
@@ -55,7 +50,7 @@ const SideItem: React.FC<React.PropsWithChildren<SideItemProps>> = React.memo(({
width: 100%;
color: ${theme.palette.accents_5};
display: flex;
height: 36px;
height: 2.25rem;
align-items: center;
justify-content: flex-start;
cursor: pointer;
@@ -82,9 +77,9 @@ const SideItem: React.FC<React.PropsWithChildren<SideItemProps>> = React.memo(({
align-items: flex-start;
flex-direction: column;
padding-left: ${theme.layout.gapHalf};
overflow: hidden;
transition: all .2s ease-in-out;
position: relative;
margin-top: .5rem;
}
.active-title {
@@ -92,7 +87,10 @@ const SideItem: React.FC<React.PropsWithChildren<SideItemProps>> = React.memo(({
}
@media only screen and (max-width: 767px) {
.link {
border-bottom: 1px solid ${theme.palette.border};
height: 3.5rem;
}
}
`}</style>
</>