docs: add menu bar and optimize navigation

This commit is contained in:
unix
2020-04-06 08:56:06 +08:00
parent 565cc8f2b9
commit 73b45cc3e0
94 changed files with 523 additions and 172 deletions

View File

@@ -1,10 +1,9 @@
import React, { useCallback, useMemo } from 'react'
import { Button, useTheme, Spacer } from 'components'
import { Button, useTheme, Select, Spacer } from 'components'
import { useConfigs } from 'lib/config-context'
import Router, { useRouter } from 'next/router'
import MoonIcon from './icons/moon'
import SunIcon from './icons/sun'
import GithubIcon from './icons/github'
const Controls: React.FC<{}> = React.memo(({
}) => {
@@ -12,14 +11,14 @@ const Controls: React.FC<{}> = React.memo(({
const { onChange, updateChineseState } = useConfigs()
const { pathname } = useRouter()
const currentLocaleText = useMemo(() => {
return pathname.toLowerCase().includes('zh-cn') ? 'EN' : '中'
return pathname.toLowerCase().includes('zh-cn') ? 'English' : '中文文档'
}, [pathname])
const isDark = useMemo(() => theme.type === 'dark', [theme.type])
const switchThemes = useCallback(() => {
const isDark = theme.type === 'dark'
onChange && onChange(!isDark)
}, [theme.type])
const switchThemes = (val: string) => {
const isDark = val === 'dark'
onChange && onChange(isDark)
}
const switchLanguages = useCallback(() => {
const currentIsChinese = pathname.toLowerCase().includes('zh-cn')
const nextPath = `/${currentIsChinese ? 'en-us' : 'zh-cn'}`
@@ -36,39 +35,45 @@ const Controls: React.FC<{}> = React.memo(({
return (
<div className="controls">
<div className="tools">
<Spacer x={.5} />
<Button className="button" auto type="abort"
onClick={switchThemes}>
{isDark ? <SunIcon width={16} height={16} /> : <MoonIcon width={16} height={16} />}
</Button>
<Button className="button" auto type="abort"
onClick={redirectGithub}>
<GithubIcon width={16} height={16} />
</Button>
<Button className="button" auto type="abort"
onClick={switchLanguages}>
<span>{currentLocaleText}</span>
</Button>
<Button auto type="abort" size="small" onClick={redirectGithub}>Github</Button>
<Spacer x={.25} />
<Button auto type="abort" size="small" onClick={switchLanguages}>{currentLocaleText}</Button>
<Spacer x={.75} />
<Select size="small" pure onChange={switchThemes} initialValue={isDark ? 'dark' : 'light'}>
<Select.Option value="light">
<div className="select-content">
<SunIcon width={16} height={16} /> Light
</div>
</Select.Option>
<Select.Option value="dark">
<div className="select-content">
<MoonIcon width={16} height={16} /> Dark
</div>
</Select.Option>
</Select>
</div>
<style jsx>{`
.controls {
height: 110px;
height: 100%;
display: flex;
align-items: flex-start;
flex-direction: column-reverse;
margin: 0;
padding-bottom: ${theme.layout.gapHalf};
position: relative;
}
.controls :global(.button) {
width: 40px;
height: 40px;
padding: 0;
.controls :global(.select) {
width: min-content;
min-width: unset;
}
.select-content {
width: auto;
display: inline-flex;
justify-content: center;
align-items: center;
margin-right: 5px;
}
.select-content :global(svg) {
margin-right: .5rem;
}
.tools {
@@ -78,25 +83,6 @@ const Controls: React.FC<{}> = React.memo(({
align-items: center;
}
.tools:before {
content: "";
display: inline-block;
height: 1.25rem;
width: .3125rem;
background-color: ${theme.palette.accents_2};
}
.controls :global(.line) {
width: 150px;
height: 55px;
cursor: pointer;
background-color: ${theme.palette.background};
position: relative;
z-index: 100;
transition: all 200ms ease;
overflow: hidden;
}
@media only screen and (max-width: 767px) {
.controls {
display: none;

View File

@@ -1,12 +1,10 @@
import React, { useMemo, useState } from 'react'
import { useRouter } from 'next/router'
import React, { useState } from 'react'
import { useTheme } from 'components'
import Controls from './controls'
import Sidebar from './sidebar'
import { Sides } from './sidebar/side-item'
import TabbarMobile from './sidebar/tabbar-mobile'
import useBodyScroll from 'components/utils/use-body-scroll'
import sides from 'lib/data/metadata.json'
import { useConfigs } from '../config-context'
export interface Meta {
title: string
@@ -23,27 +21,19 @@ export interface MultilLocaleMetaInformation {
export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ children }) => {
const theme = useTheme()
const { pathname } = useRouter()
const { sides, tabbarFixed } = useConfigs()
const [, setBodyScroll] = useBodyScroll(null, { scrollLayer: true })
const [expanded, setExpanded] = useState<boolean>(false)
const mobileTabbarClickHandler = () => {
setExpanded(!expanded)
setBodyScroll(!expanded)
}
const sideData = useMemo(() => {
const language = pathname
.split('/')
.filter(r => !!r)
const locale: string = language[0] || 'en-us'
return (sides as MultilLocaleMetaInformation)[locale]
}, [pathname, sides])
return (
<div className="layout">
<TabbarMobile onClick={mobileTabbarClickHandler} />
<aside className="sidebar">
<Controls />
<Sidebar sides={sideData}/>
<Sidebar sides={sides}/>
</aside>
<div className="side-shadow" />
<main className="main">
@@ -52,7 +42,7 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
<style jsx>{`
.layout {
min-height: 100vh;
min-height: calc(100vh - 108px);
max-width: 1000px;
margin: 0 auto;
padding: 0 ${theme.layout.gap};
@@ -65,10 +55,15 @@ export const Layout: React.FC<React.PropsWithChildren<Props>> = React.memo(({ ch
margin-right: 20px;
-webkit-overflow-scrolling: touch;
-webkit-flex-shrink: 0;
height: 100%;
height: calc(100% - 2rem - 140px + ${tabbarFixed ? '60px' : 0});
position: fixed;
top: 140px;
bottom: 2rem;
transform: translateY(${tabbarFixed ? '-60px' : 0});
transition: transform 200ms ease-out;
z-index: 100;
}
.side-shadow {
width: 220px;
flex-shrink: 0;

View File

@@ -0,0 +1,14 @@
import React from 'react'
import MenuLinks from './menu-links'
import MenuSticker from './menu-sticker'
const Menu = () => {
return (
<div>
<MenuLinks />
<MenuSticker />
</div>
)
}
export default Menu

View File

@@ -0,0 +1,33 @@
import React from 'react'
import { useTheme } from 'components'
import Controls from 'lib/components/controls'
const MenuLinks = () => {
const theme = useTheme()
return (
<nav>
<div className="site-name">
<h3>ZEIT UI - React</h3>
</div>
<div className="links">
<Controls />
</div>
<style jsx>{`
nav {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1000px;
user-select: none;
position: relative;
margin: 0 auto;
padding: 0 ${theme.layout.gap};
height: 60px;
}
`}</style>
</nav>
)
}
export default MenuLinks

View File

@@ -0,0 +1,170 @@
import React, { useEffect, useMemo } from 'react'
import { Tabs, useTheme } from 'components'
import useCurrentState from 'components/utils/use-current-state'
import sides from 'lib/data/metadata.json'
import { Sides } from 'lib/components/sidebar/side-item'
import Router, { useRouter } from 'next/router'
import { useConfigs } from '../../config-context'
export interface MultilLocaleMetaInformation {
[key: string]: Sides[]
}
const MenuSticker = () => {
const theme = useTheme()
const { pathname } = useRouter()
const { updateSides, updateTabbarFixed } = useConfigs()
const [fixed, setFixed, fixedRef] = useCurrentState<boolean>(false)
useEffect(() => updateTabbarFixed(fixed), [fixed])
const tabbarData = useMemo(() => {
const language = pathname
.split('/')
.filter(r => !!r)
const locale: string = language[0] || 'en-us'
return (sides as MultilLocaleMetaInformation)[locale]
}, [pathname, sides])
const currentTabValue = useMemo(() => {
const language = pathname
.split('/')
.filter(r => !!r)
return language[1]
}, [pathname])
useEffect(() => {
const scrollHandler = () => {
const shouldFixed = document.documentElement.scrollTop > 60
if (shouldFixed === fixedRef.current) return
setFixed(shouldFixed)
}
document.addEventListener('scroll', scrollHandler)
return () => document.removeEventListener('scroll', scrollHandler)
}, [])
const tabChangeHandler = (value: string) => {
const currentTab = tabbarData.find(tab => tab.name === value)
if (!currentTab || !Array.isArray(currentTab.children)) return
let firstChildren = currentTab.children
if (Array.isArray(firstChildren[0].children)) {
firstChildren = firstChildren[0].children
}
const defaultPath = firstChildren[0].url
if (!defaultPath) return
updateSides(currentTab.children)
Router.push(defaultPath)
}
useEffect(() => {
tabbarData && tabChangeHandler(currentTabValue)
}, [])
return (
<>
<div className={`nav-fill ${fixed ? 'active' : ''}`} />
<nav className={fixed ? 'fixed' : ''}>
<div className="sticker">
<div className="inner">
<Tabs value={currentTabValue} onChange={tabChangeHandler}>
{tabbarData ? tabbarData.map(tab => (
<Tabs.Item label={tab.localeName || tab.name}
value={tab.name}
key={tab.name} />
)) : null}
</Tabs>
</div>
</div>
</nav>
<style jsx>{`
.nav-fill {
width: 0;
height: 0;
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.nav-fill.active {
height: 48px;
visibility: visible;
}
nav {
position: relative;
width: 100%;
height: 48px;
background-color: ${theme.palette.background};
}
nav.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 3000;
background-color: ${theme.palette.background};
box-shadow: rgba(0, 0, 0, 0.1) 0 0 15px 0;
}
.sticker {
position: relative;
height: 100%;
width: 100%;
}
.sticker:before {
position: absolute;
content: '';
height: 1px;
left: 0;
right: 0;
bottom: 0;
background-color: ${theme.palette.border};
}
.inner {
max-width: 1000px;
padding: 0 ${theme.layout.gap};
width: 100%;
display: flex;
align-items: flex-end;
height: 100%;
overflow: auto;
z-index: 1000;
margin: 0 auto;
}
.inner :global(.content) {
display: none;
}
.inner :global(.tabs), .inner :global(header) {
height: 100%;
border: none;
}
.inner :global(.tab) {
height: calc(100% - 2px);
padding-top: 0;
padding-bottom: 0;
color: ${theme.palette.accents_5};
font-size: .875rem;
}
.inner :global(.tab):hover {
color: ${theme.palette.foreground};
}
.inner :global(.active) {
color: ${theme.palette.foreground};
}
`}</style>
</>
)
}
export default MenuSticker

View File

@@ -56,7 +56,7 @@ export const Sidebar: React.FC<SideGroupProps> = React.memo(({ sides }) => {
.box {
overflow-y: auto;
overflow-x: hidden;
height: calc(100vh - 140px);
height: 100%;
display: flex;
flex-direction: column;
align-items: center;