diff --git a/components/index.ts b/components/index.ts index ed08e20..5e6d841 100644 --- a/components/index.ts +++ b/components/index.ts @@ -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' diff --git a/components/page/index.ts b/components/page/index.ts new file mode 100644 index 0000000..21b2f26 --- /dev/null +++ b/components/page/index.ts @@ -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 diff --git a/components/page/page-content.tsx b/components/page/page-content.tsx new file mode 100644 index 0000000..95f4fdd --- /dev/null +++ b/components/page/page-content.tsx @@ -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, keyof Props> +export type PageContentProps = Props & typeof defaultProps & NativeAttrs + +const PageContent: React.FC> = ({ + className, + children, + ...props +}) => { + const theme = useTheme() + + return ( +
+ {children} + +
+ ) +} + +export default withDefaults(PageContent, defaultProps) diff --git a/components/page/page-footer.tsx b/components/page/page-footer.tsx new file mode 100644 index 0000000..b431fc1 --- /dev/null +++ b/components/page/page-footer.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import withDefaults from '../utils/with-defaults' + +interface Props { + className?: string +} + +const defaultProps = { + className: '', +} + +type NativeAttrs = Omit, keyof Props> +export type PageHeaderProps = Props & typeof defaultProps & NativeAttrs + +const PageFooter: React.FC> = ({ children, ...props }) => { + return ( +
+ {children} + +
+ ) +} + +export default withDefaults(PageFooter, defaultProps) diff --git a/components/page/page-header.tsx b/components/page/page-header.tsx new file mode 100644 index 0000000..1efc3e8 --- /dev/null +++ b/components/page/page-header.tsx @@ -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, keyof Props> +export type PageHeaderProps = Props & typeof defaultProps & NativeAttrs + +const PageHeader: React.FC> = ({ + children, + center, + className, + ...props +}) => { + return ( +
+ {children} + +
+ ) +} + +export default withDefaults(PageHeader, defaultProps) diff --git a/components/page/page.tsx b/components/page/page.tsx new file mode 100644 index 0000000..7310722 --- /dev/null +++ b/components/page/page.tsx @@ -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<{}> = () => ( + + + +) + +type NativeAttrs = Omit, keyof Props> +export type NoteProps = Props & typeof defaultProps & NativeAttrs + +const Page: React.FC> = ({ + children, + size, + render, + dotBackdrop, + className, + ...props +}) => { + const theme = useTheme() + const width = useMemo(() => getPageSize(size, theme.layout), [size, theme.layout]) + const showDot = useMemo(() => { + if (theme.type === 'dark') return false + return dotBackdrop + }, [dotBackdrop, theme.type]) + const [preventRender, setPreventRender] = useState(render !== 'default') + + useEffect(() => { + setPreventRender(false) + }, []) + + if (preventRender) { + const renderSEO = render === 'effect-seo' + if (!renderSEO) return null + return ( + + ) + } + + const hasContent = hasChild(children, PageContent) + + return ( +
+ {hasContent ? children : {children}} + {showDot && } + +
+ ) +} + +type MemoPageComponent

= React.NamedExoticComponent

& { + Header: typeof PageHeader + Content: typeof PageContent + Footer: typeof PageFooter +} +type ComponentProps = Partial & + Omit & + NativeAttrs + +Page.defaultProps = defaultProps + +export default React.memo(Page) as MemoPageComponent diff --git a/components/page/styles.ts b/components/page/styles.ts new file mode 100644 index 0000000..4e57a17 --- /dev/null +++ b/components/page/styles.ts @@ -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 +} diff --git a/lib/components/displays/mock-page.tsx b/lib/components/displays/mock-page.tsx new file mode 100644 index 0000000..584d4d2 --- /dev/null +++ b/lib/components/displays/mock-page.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState } from 'react' +import { useTheme } from 'components' + +interface Props { + visible: boolean + onClose?: () => void +} + +const MockPage: React.FC> = ({ + visible: customVisible, + onClose, + children, +}) => { + const theme = useTheme() + const [visible, setVisible] = useState(false) + + useEffect(() => { + if (customVisible !== undefined) { + setVisible(customVisible) + } + }, [customVisible]) + + const clickHandler = () => { + setVisible(false) + onClose && onClose() + } + return ( +

+ {children} + +
+ ) +} + +export default MockPage diff --git a/lib/data/metadata-en-us.json b/lib/data/metadata-en-us.json index 802fd82..f602a72 100644 --- a/lib/data/metadata-en-us.json +++ b/lib/data/metadata-en-us.json @@ -1 +1 @@ -[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"text","url":"/en-us/components/text","index":10,"group":"General"},{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Icons","url":"/en-us/components/icons","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"Slider","url":"/en-us/components/slider","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"},{"name":"User","url":"/en-us/components/user","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Divider","url":"/en-us/components/divider","index":100,"group":"Others"},{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]},{"name":"Utils","children":[{"name":"use-body-scroll","url":"/en-us/components/use-body-scroll","index":100,"group":"Utils"},{"name":"use-click-away","url":"/en-us/components/use-click-away","index":100,"group":"Utils"},{"name":"use-clipboard","url":"/en-us/components/use-clipboard","index":100,"group":"Utils"},{"name":"use-current-state","url":"/en-us/components/use-current-state","index":100,"group":"Utils"}]}]},{"name":"customization","children":[]}] +[{"name":"guide","children":[{"name":"getting-started","children":[{"name":"introduction","url":"/en-us/guide/introduction","index":5,"group":"getting-started"},{"name":"installation","url":"/en-us/guide/installation","index":10,"group":"getting-started"},{"name":"Server Render","url":"/en-us/guide/server-render","index":15,"group":"getting-started"}]},{"name":"customization","children":[{"name":"Colors","url":"/en-us/guide/colors","index":100,"group":"customization"},{"name":"Themes","url":"/en-us/guide/themes","index":100,"group":"customization"}]}]},{"name":"components","children":[{"name":"General","children":[{"name":"text","url":"/en-us/components/text","index":10,"group":"General"},{"name":"button","url":"/en-us/components/button","index":100,"group":"General"},{"name":"Code","url":"/en-us/components/code","index":100,"group":"General"},{"name":"Icons","url":"/en-us/components/icons","index":100,"group":"General"}]},{"name":"layout","children":[{"name":"layout","url":"/en-us/components/layout","index":100,"group":"layout"},{"name":"page","url":"/en-us/components/page","index":100,"group":"layout"},{"name":"Spacer","url":"/en-us/components/spacer","index":100,"group":"layout"}]},{"name":"Surfaces","children":[{"name":"card","url":"/en-us/components/card","index":100,"group":"Surfaces"},{"name":"collapse","url":"/en-us/components/collapse","index":100,"group":"Surfaces"},{"name":"fieldset","url":"/en-us/components/fieldset","index":100,"group":"Surfaces"}]},{"name":"Data Entry","children":[{"name":"Auto-Complete","url":"/en-us/components/auto-complete","index":100,"group":"Data Entry"},{"name":"checkbox","url":"/en-us/components/checkbox","index":100,"group":"Data Entry"},{"name":"Input","url":"/en-us/components/input","index":100,"group":"Data Entry"},{"name":"radio","url":"/en-us/components/radio","index":100,"group":"Data Entry"},{"name":"select","url":"/en-us/components/select","index":100,"group":"Data Entry"},{"name":"Slider","url":"/en-us/components/slider","index":100,"group":"Data Entry"},{"name":"textarea","url":"/en-us/components/textarea","index":100,"group":"Data Entry"},{"name":"Toggle","url":"/en-us/components/toggle","index":100,"group":"Data Entry"}]},{"name":"Data Display","children":[{"name":"avatar","url":"/en-us/components/avatar","index":100,"group":"Data Display"},{"name":"Badge","url":"/en-us/components/badge","index":100,"group":"Data Display"},{"name":"Capacity","url":"/en-us/components/capacity","index":100,"group":"Data Display"},{"name":"Description","url":"/en-us/components/description","index":100,"group":"Data Display"},{"name":"Display","url":"/en-us/components/display","index":100,"group":"Data Display"},{"name":"Dot","url":"/en-us/components/dot","index":100,"group":"Data Display"},{"name":"File-Tree","url":"/en-us/components/file-tree","index":100,"group":"Data Display"},{"name":"Image","url":"/en-us/components/image","index":100,"group":"Data Display"},{"name":"keyboard","url":"/en-us/components/keyboard","index":100,"group":"Data Display"},{"name":"Popover","url":"/en-us/components/popover","index":100,"group":"Data Display"},{"name":"Table","url":"/en-us/components/table","index":100,"group":"Data Display"},{"name":"Tag","url":"/en-us/components/tag","index":100,"group":"Data Display"},{"name":"Tooltip","url":"/en-us/components/tooltip","index":100,"group":"Data Display"},{"name":"User","url":"/en-us/components/user","index":100,"group":"Data Display"}]},{"name":"Feedback","children":[{"name":"Loading","url":"/en-us/components/loading","index":100,"group":"Feedback"},{"name":"modal","url":"/en-us/components/modal","index":100,"group":"Feedback"},{"name":"note","url":"/en-us/components/note","index":100,"group":"Feedback"},{"name":"Progress","url":"/en-us/components/progress","index":100,"group":"Feedback"},{"name":"Spinner","url":"/en-us/components/spinner","index":100,"group":"Feedback"},{"name":"toast","url":"/en-us/components/toast","index":100,"group":"Feedback"}]},{"name":"Navigation","children":[{"name":"link","url":"/en-us/components/link","index":100,"group":"Navigation"},{"name":"tabs","url":"/en-us/components/tabs","index":100,"group":"Navigation"},{"name":"button-dropdown","url":"/en-us/components/button-dropdown","index":101,"group":"Navigation"}]},{"name":"Others","children":[{"name":"Divider","url":"/en-us/components/divider","index":100,"group":"Others"},{"name":"Snippet","url":"/en-us/components/snippet","index":100,"group":"Others"}]},{"name":"Utils","children":[{"name":"use-body-scroll","url":"/en-us/components/use-body-scroll","index":100,"group":"Utils"},{"name":"use-click-away","url":"/en-us/components/use-click-away","index":100,"group":"Utils"},{"name":"use-clipboard","url":"/en-us/components/use-clipboard","index":100,"group":"Utils"},{"name":"use-current-state","url":"/en-us/components/use-current-state","index":100,"group":"Utils"}]}]},{"name":"customization","children":[]}] diff --git a/pages/en-us/components/page.mdx b/pages/en-us/components/page.mdx new file mode 100644 index 0000000..f68a620 --- /dev/null +++ b/pages/en-us/components/page.mdx @@ -0,0 +1,90 @@ +import { Layout, Playground, ExampleBlock, Attributes } from 'lib/components' +import { Page, Button } from 'components' +import MockPage from 'lib/components/displays/mock-page' + +export const meta = { + title: 'page', + group: 'layout', +} + +## Page + +Container of general for display page content. + + { + const Child = () => ( + <> +

Hello, Everyone.

+

This is a simulated page, you can click anywhere to close it.

+ + ) + const [visible, setVisible] = React.useState(false) + return ( + <> + + setVisible(false)}> + + + + ) +} +`} /> + + { + const [visible, setVisible] = React.useState(false) + return ( + <> + + setVisible(false)}> + + +

Header

+
+ +

Hello, Everyone.

+

This is a simulated page, you can click anywhere to close it.

+
+ +

Footer

+
+
+
+ + ) +} +`} /> + + +Page.Props + +| Attribute | Description | Type | Accepted values | Default +| ---------- | ---------- | ---- | -------------- | ------ | +| **size** | size of page | [PageSize](#pagesize) | - | `medium` | +| **render** | render mode | [PageRenderMode](#pagerendermode) | - | `default` | + +PageSize + +```ts +type PageSize = string | 'mini' | 'small' | 'medium' | 'large' +``` + +PageRenderMode + +```ts +type PageRenderMode = 'default' // Normal rendering + | 'effect' // Render after the first effect trigger + | 'effect-seo' // like "effect", but prerender strings for SEO +``` + + + +export default ({ children }) => {children}