mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-24 04:15:54 +08:00
feat(page): add component
feat(page): add header and footer feat(page): add dot-backdrop decoration
This commit is contained in:
@@ -51,3 +51,4 @@ export { default as Popover } from './popover'
|
||||
export { default as Slider } from './slider'
|
||||
export { default as Divider } from './divider'
|
||||
export { default as User } from './user'
|
||||
export { default as Page } from './page'
|
||||
|
||||
10
components/page/index.ts
Normal file
10
components/page/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Page from './page'
|
||||
import PageHeader from './page-header'
|
||||
import PageContent from './page-content'
|
||||
import PageFooter from './page-footer'
|
||||
|
||||
Page.Header = PageHeader
|
||||
Page.Content = PageContent
|
||||
Page.Footer = PageFooter
|
||||
|
||||
export default Page
|
||||
36
components/page/page-content.tsx
Normal file
36
components/page/page-content.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type PageContentProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const PageContent: React.FC<React.PropsWithChildren<PageContentProps>> = ({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<main className={className} {...props}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
main {
|
||||
width: 100%;
|
||||
padding: calc(${theme.layout.gap} * 2.5) 0;
|
||||
}
|
||||
`}</style>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default withDefaults(PageContent, defaultProps)
|
||||
30
components/page/page-footer.tsx
Normal file
30
components/page/page-footer.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type PageHeaderProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const PageFooter: React.FC<React.PropsWithChildren<PageHeaderProps>> = ({ children, ...props }) => {
|
||||
return (
|
||||
<footer {...props}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
footer {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
`}</style>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default withDefaults(PageFooter, defaultProps)
|
||||
41
components/page/page-header.tsx
Normal file
41
components/page/page-header.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
|
||||
interface Props {
|
||||
center?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
center: false,
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type PageHeaderProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const PageHeader: React.FC<React.PropsWithChildren<PageHeaderProps>> = ({
|
||||
children,
|
||||
center,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<header className={`${center ? 'center' : ''} ${className}`} {...props}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
`}</style>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default withDefaults(PageHeader, defaultProps)
|
||||
118
components/page/page.tsx
Normal file
118
components/page/page.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { NormalSizes, tuple } from '../utils/prop-types'
|
||||
import { getPageSize } from './styles'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import PageHeader from './page-header'
|
||||
import PageContent from './page-content'
|
||||
import { hasChild } from '../utils/collections'
|
||||
import PageFooter from './page-footer'
|
||||
|
||||
const renderMode = tuple('default', 'effect', 'effect-seo')
|
||||
|
||||
export type PageSize = NormalSizes | string
|
||||
export type PageRenderMode = typeof renderMode[number]
|
||||
|
||||
interface Props {
|
||||
size?: PageSize
|
||||
render?: PageRenderMode
|
||||
dotBackdrop: boolean
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
size: 'medium' as PageSize,
|
||||
render: 'default' as PageRenderMode,
|
||||
dotBackdrop: false,
|
||||
}
|
||||
|
||||
const DotStyles: React.FC<{}> = () => (
|
||||
<span>
|
||||
<style jsx>{`
|
||||
:global(body) {
|
||||
background-image: radial-gradient(#e3e3e3 1px, transparent 0),
|
||||
radial-gradient(#e3e3e3 1px, transparent 0);
|
||||
background-position: 0 0, 25px 25px;
|
||||
background-attachment: fixed;
|
||||
background-size: 50px 50px;
|
||||
}
|
||||
`}</style>
|
||||
</span>
|
||||
)
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type NoteProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const Page: React.FC<React.PropsWithChildren<NoteProps>> = ({
|
||||
children,
|
||||
size,
|
||||
render,
|
||||
dotBackdrop,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const width = useMemo(() => getPageSize(size, theme.layout), [size, theme.layout])
|
||||
const showDot = useMemo<boolean>(() => {
|
||||
if (theme.type === 'dark') return false
|
||||
return dotBackdrop
|
||||
}, [dotBackdrop, theme.type])
|
||||
const [preventRender, setPreventRender] = useState<boolean>(render !== 'default')
|
||||
|
||||
useEffect(() => {
|
||||
setPreventRender(false)
|
||||
}, [])
|
||||
|
||||
if (preventRender) {
|
||||
const renderSEO = render === 'effect-seo'
|
||||
if (!renderSEO) return null
|
||||
return (
|
||||
<div className="hidden" aria-hidden="true">
|
||||
{children}
|
||||
<style jsx>{`
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const hasContent = hasChild(children, PageContent)
|
||||
|
||||
return (
|
||||
<section className={className} {...props}>
|
||||
{hasContent ? children : <PageContent>{children}</PageContent>}
|
||||
{showDot && <DotStyles />}
|
||||
<style jsx>{`
|
||||
section {
|
||||
width: ${width};
|
||||
max-width: 100vw;
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: ${theme.layout.breakpointMobile}) {
|
||||
section {
|
||||
max-width: 90vw;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
type MemoPageComponent<P = {}> = React.NamedExoticComponent<P> & {
|
||||
Header: typeof PageHeader
|
||||
Content: typeof PageContent
|
||||
Footer: typeof PageFooter
|
||||
}
|
||||
type ComponentProps = Partial<typeof defaultProps> &
|
||||
Omit<Props, keyof typeof defaultProps> &
|
||||
NativeAttrs
|
||||
|
||||
Page.defaultProps = defaultProps
|
||||
|
||||
export default React.memo(Page) as MemoPageComponent<ComponentProps>
|
||||
15
components/page/styles.ts
Normal file
15
components/page/styles.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PageSize } from './page'
|
||||
import { NormalSizes } from '../utils/prop-types'
|
||||
import { ZeitUIThemesLayout } from '../styles/themes'
|
||||
|
||||
export const getPageSize = (size: PageSize, layout: ZeitUIThemesLayout): string => {
|
||||
const presets: { [key in NormalSizes]: string } = {
|
||||
medium: layout.pageWidth,
|
||||
small: `calc(${layout.pageWidth} - 100pt)`,
|
||||
mini: `calc(${layout.pageWidth} - 180pt)`,
|
||||
large: `calc(${layout.pageWidth} + 100pt)`,
|
||||
}
|
||||
const presetValue = presets[size as NormalSizes]
|
||||
if (!presetValue) return size as string
|
||||
return presetValue
|
||||
}
|
||||
Reference in New Issue
Block a user