Skip to content

Commit

Permalink
Anchor Component
Browse files Browse the repository at this point in the history
  • Loading branch information
RaoHai committed Nov 3, 2016
1 parent 616ecf1 commit 0a0e14d
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 133 deletions.
2 changes: 1 addition & 1 deletion components/affix/demo/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ title:
The simplest usage.

````jsx
import { Anchor } from 'antd';
import { Affix, Button } from 'antd';

ReactDOM.render(
<div>
Expand Down
74 changes: 63 additions & 11 deletions components/anchor/AnchorLink.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,72 @@
import React from 'react';
import classnames from 'classnames';
import AnchorHelper, { scrollTo } from './anchorHelper';

export interface AnchorLinkProps {
href: string;
onClick: (href: string) => {};
active: boolean;
onClick: (href: string) => void;
active?: boolean;
prefixCls?: string;
children?: any;
title?: Element;
bounds: number;
target?: () => HTMLElement | Window;
}

export default function AnchorLink(props) {
const { prefixCls, active, href, children, onClick } = props;
const cls = classnames({
[`${prefixCls}-link`]: true,
[`${prefixCls}-link-active`]: active,
});
export default class AnchorLink extends React.Component<AnchorLinkProps, any> {
static contextTypes = {
anchorHelper: React.PropTypes.any,
};

return <div className={cls} onClick={() => onClick(props.href)} >
{children}
static childContextTypes = {
anchorHelper: React.PropTypes.any,
};

static defaultProps = {
href: '#',
prefixCls: 'ant-anchor',
};

context: {
anchorHelper: AnchorHelper;
};

constructor(props, context) {
super(props, context);
}

getChildContext() {
return {
anchorHelper: this.context.anchorHelper,
};
}
renderAnchorLink = (child) => {
const { href } = child.props;
if (href) {
this.context.anchorHelper.addLink(href);
return React.cloneElement(child, {
onClick: this.context.anchorHelper.scrollTo,
prefixCls: this.props.prefixCls,
});
}
return child;
}
render() {
const { prefixCls, href, children, onClick, title, bounds } = this.props;
const { anchorHelper } = this.context;
const active = anchorHelper && anchorHelper.getCurrentAnchor(bounds) === href;
const cls = classnames({
[`${prefixCls}-link`]: true,
[`${prefixCls}-link-active`]: active,
});
const scrollToFn = anchorHelper ? anchorHelper.scrollTo : scrollTo;
return <div className={cls}>
<span
ref={(component) => component && active && anchorHelper ? anchorHelper.setActiveAnchor(component) : null}
className={`${prefixCls}-link-title`}
onClick={() => onClick ? onClick(href) : scrollToFn(href)}
>{title}</span>
{React.Children.map(children, this.renderAnchorLink)}
</div>;
}
}
}
98 changes: 98 additions & 0 deletions components/anchor/anchorHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import getScroll from '../_util/getScroll';
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';

export const reqAnimFrame = getRequestAnimationFrame();

export const easeInOutCubic = (t, b, c, d) => {
t /= d/2;
if (t < 1) return c/2*t*t*t + b;
t -= 2;
return c/2*(t*t*t + 2) + b;
};

export function getDefaultTarget() {
return typeof window !== 'undefined' ?
window : null;
}

export function getOffsetTop(element): number {
if (!element) {
return 0;
}

if (!element.getClientRects().length) {
return 0;
}

const rect = element.getBoundingClientRect();

if ( rect.width || rect.height ) {
const doc = element.ownerDocument;
const docElem = doc.documentElement;
return rect.top - docElem.clientTop;
}

return rect.top;
}

export function scrollTo(href, target = getDefaultTarget){
const scrollTop = getScroll(target(), true);
const offsetTop = getOffsetTop(document.querySelector(href));
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
document.body.scrollTop = easeInOutCubic(time, scrollTop, offsetTop, 450);
if (time < 450) {
reqAnimFrame(frameFunc);
}
};
reqAnimFrame(frameFunc);
history.pushState(null, undefined, href);
}

class AnchorHelper {
private links: Array<string>;
private currentAnchor: HTMLElement | null;

constructor() {
this.links = [];
this.currentAnchor = null;
}

addLink(link) {
if (this.links.indexOf(link) === -1) {
this.links.push(link);
}
}

getCurrentActiveAnchor(): HTMLElement | null {
return this.currentAnchor;
}

setActiveAnchor(component) {
this.currentAnchor = component;
}

getCurrentAnchor(bounds = 5) {
let activeAnchor = '';
this.links.forEach(section => {
const target = document.querySelector(section);
if (target) {
const top = getOffsetTop(target);
const bottom = top + target.clientHeight;
if ((top <= bounds) && (bottom >= -bounds)) {
activeAnchor = section;
}
}
});
return activeAnchor;
}

scrollTo(href, target = getDefaultTarget){
scrollTo(href, target);
}
}

// const anchorHelper = new AnchorHelper();
export default AnchorHelper;
10 changes: 4 additions & 6 deletions components/anchor/demo/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ title:
The simplest usage.

```jsx

const { Anchor } = antd;

const { AnchorLink } = Anchor;
import { Anchor } from 'antd';
const { AnchorLink } = Anchor;

ReactDOM.render(
<Anchor>
<AnchorLink href="#components-anchor-demo-basic">基本</AnchorLink>
<AnchorLink href="#components-anchor-demo-independ">独立使用</AnchorLink>
<AnchorLink href="#components-anchor-demo-basic" title="基本" />
<AnchorLink href="#components-anchor-demo-independ" title="独立使用" />
</Anchor>
, mountNode);
```
9 changes: 4 additions & 5 deletions components/anchor/demo/independ.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1
order: 2
title:
zh-CN: 独立使用 AnchorLink
en-US: Independent AnchorLink
Expand All @@ -14,11 +14,10 @@ title:
Independent AnchorLink

```jsx

const { Anchor } = antd;
const { AnchorLink } = Anchor;
import { Anchor } from 'antd';
const { AnchorLink } = Anchor;

ReactDOM.render(
<AnchorLink href="#components-anchor-demo-basic">锚点1</AnchorLink>
<AnchorLink href="#components-anchor-demo-basic" title="基本" />
, mountNode);
```
28 changes: 28 additions & 0 deletions components/anchor/demo/mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
order: 1
title:
zh-CN: 嵌套的 AnchorLink
en-US: Mixed AnchorLink
---

## zh-CN

最简单的用法。

## en-US

The simplest usage.

```jsx
import { Anchor } from 'antd';
const { AnchorLink } = Anchor;

ReactDOM.render(
<Anchor offsetTop={100}>
<AnchorLink href="#components-anchor-demo-basic" title="基本">
<AnchorLink href="#components-anchor-demo-mix" title="嵌套的 AnchorLink" />
</AnchorLink>
<AnchorLink href="#components-anchor-demo-independ" title="独立使用" />
</Anchor>
, mountNode);
```
22 changes: 11 additions & 11 deletions components/anchor/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ cols: 1
title: Anchor
---

Make an element sticky to viewport.
A hyperlink to a location on same page.

## When To Use

When user browses a long web page, some content need to sticky to viewport. It is common for menus and actions.

Please note that Affix should not cover other content in page, especially when the size of viewport is small.
For displaying anchor hyperlink on page, and jump between then.

## API

### Anchor Props

| Property | Description | Type | Default |
|--------------|-----------------------|----------|--------------|
| offsetTop | Pixels to offset from top when calculating position of scroll | Number | 0 |
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | Number | - |
| onChange | Callback when affix state is changed | Function(affixed) | - |
| bounds | Bounding distance of anchor area | Number | 5(px) |

### AnchorLink Props

**Note:** Children of `Affix` can not be `position: absolute`, but you can set `Affix` as `position: absolute`:
| Property | Description | Type | Default |
|-------------|----------------|--------------------|--------------|
| href | target of hyperlink | String | |
| title | content of hyperlink | String | |

```jsx
<Affix style={{ position: 'absolute', top: y, left: x}}>
...
</Affix>
```
Loading

0 comments on commit 0a0e14d

Please sign in to comment.