Skip to content

Commit

Permalink
feat(Breadcrumb): maxNode support auto
Browse files Browse the repository at this point in the history
  • Loading branch information
myronliu347 committed Aug 8, 2019
1 parent 67f0d49 commit 9ce0323
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 9 deletions.
2 changes: 1 addition & 1 deletion docs/breadcrumb/index.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ It is used to inform the user of the current position and the position of the cu
| Param | Description | Type | Default Value |
| --------- | -------------------------- | --------- | ------------------------------ |
| children | Children components, hsould be an Breadcrumb.Item | custom | - |
| maxNode | The maximum number of breadcrumbs is displayed and the excess is hidden | Number | 100 |
| maxNode | The maximum number of breadcrumbs is displayed and the excess is hidden, can set auto compute maximum number | Number | 100, 'auto' |
| separator | Separator, can be text or Icon | ReactNode | <Icon type="arrow-right" /> |
| component | Set Element type | String/Function | 'nav' |

Expand Down
2 changes: 1 addition & 1 deletion docs/breadcrumb/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
| 参数 | 说明 | 类型 | 默认值 |
| --------- | -------------------------- | --------------- | ------------------------------ |
| children | 面包屑子节点,需传入 Breadcrumb.Item | custom | - |
| maxNode | 面包屑最多显示个数,超出部分会被隐藏 | Number | 100 |
| maxNode | 面包屑最多显示个数,超出部分会被隐藏, 设置成 `auto` 之后会自动根据容器宽度计算 | Number, `auto` | 100 |
| separator | 分隔符,可以是文本或 Icon | ReactNode | <Icon type="arrow-right" /> |
| component | 设置标签类型 | String/Function | 'nav' |

Expand Down
102 changes: 97 additions & 5 deletions src/breadcrumb/index.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { Component, Children } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Icon from '../icon';
import ConfigProvider from '../config-provider';
import Item from './item';
import { events } from '../util';

/**
* Breadcrumb
Expand Down Expand Up @@ -38,9 +38,12 @@ class Breadcrumb extends Component {
},
/*eslint-enable*/
/**
* 面包屑最多显示个数,超出部分会被隐藏
* 面包屑最多显示个数,超出部分会被隐藏, 设置为 auto 会自动根据父元素的宽度适配。
*/
maxNode: PropTypes.number,
maxNode: PropTypes.oneOfType([
PropTypes.number,
PropTypes.oneOf(['auto']),
]),
/**
* 分隔符,可以是文本或 Icon
*/
Expand All @@ -59,18 +62,82 @@ class Breadcrumb extends Component {
component: 'nav',
};

constructor(props) {
super(props);
this.state = {
maxNode: props.maxNode === 'auto' ? 100 : props.maxNode,
};
}

componentDidMount() {
this.computeMaxNode();
events.on(window, 'resize', this.computeMaxNode);
}

componentWillReceiveProps(nextProps) {
if (nextProps.maxNode !== this.props.maxNode) {
this.setState({
maxNode: nextProps.maxNode === 'auto' ? 100 : nextProps.maxNode,
});
}
}

componentDidUpdate() {
this.computeMaxNode();
}

componentWillUnmount() {
events.off(window, 'resize', this.computeMaxNode);
}

computeMaxNode = () => {
// 计算最大node节点,无法获取到 ... 节点的宽度,目前会有 nodeWidth - ellipsisNodeWidth 的误差
if (this.props.maxNode !== 'auto' || !this.breadcrumbEl) return;
const scrollWidth = this.breadcrumbEl.scrollWidth;
const rect = this.breadcrumbEl.getBoundingClientRect();

if (scrollWidth <= rect.width) return;
let maxNode = this.breadcrumbEl.children.length;
let index = 1;
let fullWidth = scrollWidth;

while (index < this.breadcrumbEl.children.length - 1) {
const el = this.breadcrumbEl.children[index];
maxNode--;
fullWidth -= el.getBoundingClientRect().width;
if (fullWidth <= rect.width) {
break;
}
index++;
}

maxNode = Math.max(3, maxNode);

if (maxNode !== this.state.maxNode) {
this.setState({
maxNode,
});
}
};

saveBreadcrumbRef = ref => {
this.breadcrumbEl = ref;
};

render() {
const {
prefix,
rtl,
className,
maxNode,
children,
separator,
component,
maxNode: maxNodeProp,
...others
} = this.props;
// const clazz = classNames(``, className);

const { maxNode } = this.state;

let items;
const length = Children.count(children);

Expand Down Expand Up @@ -136,13 +203,38 @@ class Breadcrumb extends Component {

const BreadcrumbComponent = component;

delete others.maxNode;

return (
<BreadcrumbComponent
aria-label="Breadcrumb"
className={className}
{...others}
style={{ position: 'relative', ...(others.style || {}) }}
>
<ul className={`${prefix}breadcrumb`}>{items}</ul>
{maxNodeProp === 'auto' ? (
<ul
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
visibility: 'hidden',
}}
ref={this.saveBreadcrumbRef}
className={`${prefix}breadcrumb`}
>
{Children.map(children, (item, i) => {
return React.cloneElement(item, {
separator,
prefix,
activated: i === length - 1,
key: i,
});
})}
</ul>
) : null}
</BreadcrumbComponent>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/breadcrumb/scss/mixin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
display: block;
margin: 0;
padding: 0;
white-space: nowrap;

.#{$css-prefix}breadcrumb-item {
display: inline-block;
Expand Down
31 changes: 30 additions & 1 deletion test/breadcrumb/index-spec.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import assert from 'power-assert';
import Enzyme, { shallow, mount } from 'enzyme';
import Enzyme, { shallow, mount, } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Breadcrumb from '../../src/breadcrumb';
import '../../src/breadcrumb/style';
import ConfigProvider from '../../src/config-provider';

Enzyme.configure({ adapter: new Adapter() });
Expand Down Expand Up @@ -46,6 +48,18 @@ describe('Item', () => {
});

describe('Breadcrumb', () => {
let mountNode;

beforeEach(() => {
mountNode = document.createElement('div');
document.body.appendChild(mountNode);
});

afterEach(() => {
ReactDOM.unmountComponentAtNode(mountNode);
document.body.removeChild(mountNode);
});

it("should throw error if you don't pass Item as children", () => {
try {
shallow(<Breadcrumb>Breadcrumb</Breadcrumb>);
Expand Down Expand Up @@ -75,6 +89,21 @@ describe('Breadcrumb', () => {
wrapper.unmount();
});

it('should render ellipsis if maxNode set auto', () => {
ReactDOM.render(
<Breadcrumb maxNode="auto" style={{ width: '1px' }}>
<Item>Home 1</Item>
<Item>Whatever 2</Item>
<Item>All Categories 3</Item>
<Item>Women’s Clothing 4</Item>
<Item>Blouses & Shirts 5</Item>
<Item>T-shirts 6</Item>
</Breadcrumb>
, mountNode);
const ellipsisItem = mountNode.querySelectorAll('.next-breadcrumb-text')[1];
assert(ellipsisItem.textContent === '...');
});

it('should not render the separator of the last item', () => {
const wrapper = mount(
<Breadcrumb>
Expand Down
2 changes: 1 addition & 1 deletion types/breadcrumb/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface BreadcrumbProps extends React.HTMLAttributes<HTMLElement> {
/**
* 面包屑最多显示个数,超出部分会被隐藏
*/
maxNode?: number;
maxNode?: number | 'auto';

/**
* 分隔符,可以是文本或 Icon
Expand Down

0 comments on commit 9ce0323

Please sign in to comment.