feat(breadcrumbs): add component

docs(breadcrumbs): add docs

fix(breadcrumbs): fix item types error
This commit is contained in:
unix
2020-05-30 21:44:44 +08:00
parent 0bedd898c1
commit 8418ebfed9
10 changed files with 329 additions and 2 deletions

View File

@@ -0,0 +1,12 @@
import React from 'react'
export interface BreadcrumbsConfig {
separator?: string
}
const defaultContext = {}
export const BreadcrumbsContext = React.createContext<BreadcrumbsConfig>(defaultContext)
export const useBreadcrumbsContext = (): BreadcrumbsConfig =>
React.useContext<BreadcrumbsConfig>(BreadcrumbsContext)

View File

@@ -0,0 +1,74 @@
import Link from '../link'
import { Props as LinkBasicProps } from '../link/link'
import React, { useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import { pickChild } from '../utils/collections'
import BreadcrumbsSeparator from './breadcrumbs-separator'
import { useBreadcrumbsContext } from './breadcrumbs-context'
interface Props {
href?: string
nextLink?: boolean
onClick?: (event: React.MouseEvent) => void
className?: string
}
const defaultProps = {
nextLink: false,
className: '',
}
type NativeAttrs = Omit<React.AnchorHTMLAttributes<any>, keyof Props>
type NativeLinkAttrs = Omit<NativeAttrs, keyof LinkBasicProps>
export type BreadcrumbsProps = Props & typeof defaultProps & NativeLinkAttrs
const BreadcrumbsItem = React.forwardRef<
HTMLAnchorElement,
React.PropsWithChildren<BreadcrumbsProps>
>(
(
{ href, nextLink, onClick, children, className, ...props },
ref: React.Ref<HTMLAnchorElement>,
) => {
const { separator } = useBreadcrumbsContext()
const isLink = useMemo(() => href !== undefined || nextLink, [href, nextLink])
const [withoutSepChildren, sepChildren] = pickChild(children, BreadcrumbsSeparator)
const composeSeparator = useMemo(() => {
if (React.Children.count(sepChildren) > 0) return sepChildren
return <BreadcrumbsSeparator>{separator}</BreadcrumbsSeparator>
}, [separator])
const clickHandler = (event: React.MouseEvent) => {
onClick && onClick(event)
}
if (!isLink) {
return (
<>
<span className="breadcrums-item" onClick={clickHandler}>
{withoutSepChildren}
</span>
{composeSeparator}
</>
)
}
return (
<>
<Link
className={`breadcrums-item ${className}`}
href={href}
onClick={clickHandler}
ref={ref}
{...props}>
{withoutSepChildren}
</Link>
{composeSeparator}
</>
)
},
)
const MemoBreadcrumbsItem = React.memo(BreadcrumbsItem)
export default withDefaults(MemoBreadcrumbsItem, defaultProps)

View File

@@ -0,0 +1,40 @@
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 BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
const BreadcrumbsSeparator: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
children,
className,
}) => {
// const theme = useTheme()
return (
<div className={`separator ${className}`}>
{children}
<style jsx>{`
.separator {
display: inline-flex;
margin: 0 8px;
user-select: none;
pointer-events: none;
align-items: center;
}
`}</style>
</div>
)
}
const MemoBreadcrumbsSeparator = React.memo(BreadcrumbsSeparator)
export default withDefaults(MemoBreadcrumbsSeparator, defaultProps)

View File

@@ -0,0 +1,88 @@
import React, { ReactNode, useMemo } from 'react'
import useTheme from '../styles/use-theme'
import BreadcrumbsItem from './breadcrumbs-item'
import BreadcrumbsSeparator from './breadcrumbs-separator'
import { addColorAlpha } from '../utils/color'
import { BreadcrumbsContext } from './breadcrumbs-context'
interface Props {
separator?: string | ReactNode
className?: string
}
const defaultProps = {
separator: '/',
className: '',
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type BreadcrumbsProps = Props & typeof defaultProps & NativeAttrs
const Breadcrumbs: React.FC<React.PropsWithChildren<BreadcrumbsProps>> = ({
separator,
children,
className,
}) => {
const theme = useTheme()
const initialValue = useMemo(
() => ({
separator,
}),
[separator],
)
return (
<BreadcrumbsContext.Provider value={initialValue}>
<nav className={className}>
{children}
<style jsx>{`
nav {
margin: 0;
padding: 0;
line-height: inherit;
color: ${theme.palette.accents_4};
font-size: 1rem;
box-sizing: border-box;
display: flex;
align-items: center;
}
nav :global(.link:hover) {
color: ${addColorAlpha(theme.palette.link, 0.85)};
}
nav > :global(span:last-of-type) {
color: ${theme.palette.accents_6};
}
nav > :global(.separator:last-child) {
display: none;
}
nav :global(svg) {
width: 1em;
height: 1em;
margin: 0 4px;
}
nav :global(.breadcrums-item) {
display: inline-flex;
align-items: center;
}
`}</style>
</nav>
</BreadcrumbsContext.Provider>
)
}
type MemoBreadcrumbsComponent<P = {}> = React.NamedExoticComponent<P> & {
Item: typeof BreadcrumbsItem
Separator: typeof BreadcrumbsSeparator
}
type ComponentProps = Partial<typeof defaultProps> &
Omit<Props, keyof typeof defaultProps> &
NativeAttrs
Breadcrumbs.defaultProps = defaultProps
export default React.memo(Breadcrumbs) as MemoBreadcrumbsComponent<ComponentProps>

View File

@@ -0,0 +1,8 @@
import Breadcrumbs from './breadcrumbs'
import BreadcrumbsItem from './breadcrumbs-item'
import BreadcrumbsSeparator from './breadcrumbs-separator'
Breadcrumbs.Item = BreadcrumbsItem
Breadcrumbs.Separator = BreadcrumbsSeparator
export default Breadcrumbs

View File

@@ -58,3 +58,4 @@ export { default as User } from './user'
export { default as Page } from './page'
export { default as Grid } from './grid'
export { default as ButtonGroup } from './button-group'
export { default as Breadcrumbs } from './breadcrumbs'

View File

@@ -4,7 +4,7 @@ import useTheme from '../styles/use-theme'
import useWarning from '../utils/use-warning'
import LinkIcon from './icon'
interface Props {
export interface Props {
href?: string
color?: boolean
pure?: boolean

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
import { Layout, Playground, Attributes } from 'lib/components'
import { Breadcrumbs, Spacer } from 'components'
import NextLink from 'next/link'
import Home from '@zeit-ui/react-icons/home'
import Inbox from '@zeit-ui/react-icons/inbox'
export const meta = {
title: 'breadcrumbs',
group: 'Navigation',
}
## Breadcrumbs
Show where users are in the application.
<Playground
scope={{ Breadcrumbs }}
code={`
<Breadcrumbs>
<Breadcrumbs.Item>Home</Breadcrumbs.Item>
<Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
<Breadcrumbs.Item >Page</Breadcrumbs.Item>
</Breadcrumbs>
`} />
<Playground
title="Separator"
desc="Custom separator in characters."
scope={{ Breadcrumbs, Spacer }}
code={`
<>
<Breadcrumbs separator="-">
<Breadcrumbs.Item>Home</Breadcrumbs.Item>
<Breadcrumbs.Item href="">Catalog</Breadcrumbs.Item>
<Breadcrumbs.Item >Page</Breadcrumbs.Item>
</Breadcrumbs>
<Spacer y={.5} />
<Breadcrumbs separator=">">
<Breadcrumbs.Item>
Home
<Breadcrumbs.Separator>:</Breadcrumbs.Separator>
</Breadcrumbs.Item>
<Breadcrumbs.Item href="">Components</Breadcrumbs.Item>
<Breadcrumbs.Item href="">Basic</Breadcrumbs.Item>
<Breadcrumbs.Item >Button</Breadcrumbs.Item>
</Breadcrumbs>
</>
`} />
<Playground
title="Icons"
desc="Show more information with icons."
scope={{ Breadcrumbs, Home, Inbox }}
code={`
<Breadcrumbs>
<Breadcrumbs.Item><Home /></Breadcrumbs.Item>
<Breadcrumbs.Item href=""><Inbox /> Inbox</Breadcrumbs.Item>
<Breadcrumbs.Item >Page</Breadcrumbs.Item>
</Breadcrumbs>
`} />
<Playground
title="With NextJS"
desc="Example for use with `next.js`."
scope={{ Breadcrumbs, NextLink }}
code={`
<Breadcrumbs>
<NextLink href="/">
<Breadcrumbs.Item nextLink>Home</Breadcrumbs.Item>
</NextLink>
<NextLink href="/en-us/components">
<Breadcrumbs.Item nextLink>Components</Breadcrumbs.Item>
</NextLink>
<Breadcrumbs.Item>Breadcrumbs</Breadcrumbs.Item>
</Breadcrumbs>
`} />
<Attributes edit="/pages/en-us/components/breadcrumbs.mdx">
<Attributes.Title>Breadcrumbs.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **separator** | separator string | `string` | - | `/` |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>Breadcrumbs.Item.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **href** | link address | `string` | - | - |
| **nextLink** | in `next.js` route | `boolean` | - | `false` |
| **onClick** | click event | `(event: MouseEvent) => void` | - | - |
| **onClick** | click event | `(event: MouseEvent) => void` | - | - |
| ... | native props | `AnchorHTMLAttributes` | `'id', 'className', ...` | - |
<Attributes.Title>Breadcrumbs.Separator.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| ... | native props | `HTMLAttributes` | `'id', 'className', ...` | - |
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>