mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-29 04:35:32 +08:00
feat(breadcrumbs): add component
docs(breadcrumbs): add docs fix(breadcrumbs): fix item types error
This commit is contained in:
12
components/breadcrumbs/breadcrumbs-context.ts
Normal file
12
components/breadcrumbs/breadcrumbs-context.ts
Normal 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)
|
||||||
74
components/breadcrumbs/breadcrumbs-item.tsx
Normal file
74
components/breadcrumbs/breadcrumbs-item.tsx
Normal 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)
|
||||||
40
components/breadcrumbs/breadcrumbs-separator.tsx
Normal file
40
components/breadcrumbs/breadcrumbs-separator.tsx
Normal 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)
|
||||||
88
components/breadcrumbs/breadcrumbs.tsx
Normal file
88
components/breadcrumbs/breadcrumbs.tsx
Normal 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>
|
||||||
8
components/breadcrumbs/index.ts
Normal file
8
components/breadcrumbs/index.ts
Normal 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
|
||||||
@@ -58,3 +58,4 @@ export { default as User } from './user'
|
|||||||
export { default as Page } from './page'
|
export { default as Page } from './page'
|
||||||
export { default as Grid } from './grid'
|
export { default as Grid } from './grid'
|
||||||
export { default as ButtonGroup } from './button-group'
|
export { default as ButtonGroup } from './button-group'
|
||||||
|
export { default as Breadcrumbs } from './breadcrumbs'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import useTheme from '../styles/use-theme'
|
|||||||
import useWarning from '../utils/use-warning'
|
import useWarning from '../utils/use-warning'
|
||||||
import LinkIcon from './icon'
|
import LinkIcon from './icon'
|
||||||
|
|
||||||
interface Props {
|
export interface Props {
|
||||||
href?: string
|
href?: string
|
||||||
color?: boolean
|
color?: boolean
|
||||||
pure?: boolean
|
pure?: boolean
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
104
pages/en-us/components/breadcrumbs.mdx
Normal file
104
pages/en-us/components/breadcrumbs.mdx
Normal 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>
|
||||||
Reference in New Issue
Block a user