chore: auto scroll to target

This commit is contained in:
Benjy Cui 2016-03-07 17:33:38 +08:00
parent a9659287ed
commit b695aaece0
8 changed files with 68 additions and 28 deletions

View File

@ -86,6 +86,7 @@
"chalk": "^1.1.0",
"css-loader": "^0.23.0",
"cz-conventional-changelog": "^1.1.5",
"dom-scroll-into-view": "^1.1.0",
"dora": "^0.2.3",
"dora-plugin-atool-build": "^0.4.1",
"es3ify-loader": "^0.1.0",

View File

@ -1,4 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import classNames from 'classnames';
import ImagePreview from './ImagePreview';
import * as utils from '../utils';
@ -9,7 +10,7 @@ export default class Article extends React.Component {
this.imgToPreview = this.imgToPreview.bind(this);
}
isPreviewImg(string) {
return /^<img\s/i.test(string) && /preview-img/gi.test(string);
}
@ -30,11 +31,17 @@ export default class Article extends React.Component {
}
render() {
const content = this.props.content;
const { content, location } = this.props;
const jumper = content.description.filter((node) => {
return node.type === 'h2';
}).map((node) => {
return <li key={node.children}><a href={`#${node.children}`}>{ node.children }</a></li>;
return (
<li key={node.children}>
<Link to={{ pathname: location.pathname, query: { scrollTo: node.children } }}>
{ node.children }
</Link>
</li>
);
});
content.description = content.description.map(this.imgToPreview);
@ -47,7 +54,7 @@ export default class Article extends React.Component {
<section className="toc"><ul>{ jumper }</ul></section> :
null
}
{ content.description.map(utils.objectToComponent) }
{ content.description.map(utils.objectToComponent.bind(null, location.pathname)) }
</article>
);
}

View File

@ -1,4 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import classNames from 'classnames';
import { Row, Col, Icon } from '../../../';
import Demo from '../Demo';
@ -21,7 +22,7 @@ export default class ComponentDoc extends React.Component {
}
render() {
const { doc } = this.props;
const { doc, location } = this.props;
const { description, meta } = doc;
const demos = demosList[meta.fileName] || [];
const expand = this.state.expandAll;
@ -34,9 +35,15 @@ export default class ComponentDoc extends React.Component {
return a.order - b.order;
}).forEach((demoData, index) => {
if (index % 2 === 0 || isSingleCol) {
leftChildren.push(<Demo {...demoData} expand={expand} key={index} parentId={parentId} />);
leftChildren.push(
<Demo {...demoData} key={index}
expand={expand} parentId={parentId} pathname={location.pathname} />
);
} else {
rightChildren.push(<Demo {...demoData} expand={expand} key={index} parentId={parentId} />);
rightChildren.push(
<Demo {...demoData} key={index}
expand={expand} parentId={parentId} pathname={location.pathname} />
);
}
});
const expandTriggerClass = classNames({
@ -47,7 +54,9 @@ export default class ComponentDoc extends React.Component {
const jumper = demos.map((demo) => {
return (
<li key={demo.id}>
<a href={`#${parentId}-${demo.id}`}>{ demo.title }</a>
<Link to={{ pathname: location.pathname, query: { scrollTo: `${parentId}-${demo.id}` } }}>
{ demo.title }
</Link>
</li>
);
});
@ -59,7 +68,7 @@ export default class ComponentDoc extends React.Component {
</ul>
<section className="markdown">
<h1>{meta.chinese || meta.english}</h1>
{ description.map(utils.objectToComponent) }
{ description.map(utils.objectToComponent.bind(null, location.pathname)) }
<h2>
代码演示
<Icon type="appstore" className={expandTriggerClass}
@ -73,7 +82,7 @@ export default class ComponentDoc extends React.Component {
{ isSingleCol ? null : <Col className="demo-list-right" span="12">{ rightChildren }</Col> }
</Row>
<section className="markdown">
{ (doc.api || []).map(utils.objectToComponent) }
{ (doc.api || []).map(utils.objectToComponent.bind(null, location.pathname)) }
</section>
</article>
);

View File

@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router';
import antd, { Collapse } from '../../../';
import BrowserDemo from '../BrowserDemo';
import * as utils from '../utils';
@ -22,9 +23,10 @@ export default class Demo extends React.Component {
}
render() {
const { id, parentId, preview, title, intro, code, style, expand } = this.props;
const { id, parentId, preview, title,
intro, code, style, expand, pathname } = this.props;
const demoId = `${parentId}-${id}`;
const introChildren = intro.map(utils.objectToComponent);
const introChildren = intro.map(utils.objectToComponent.bind(null, pathname));
const highlightedCode = hljs.highlight('javascript', code).value;
return (
@ -39,7 +41,7 @@ export default class Demo extends React.Component {
</section>
<section className="code-box-meta markdown">
<div className="code-box-title">
<a href={`#${demoId}`}>{ title }</a>
<Link to={{ pathname, query: { scrollTo: demoId } }}>{ title }</Link>
</div>
<Collapse activeKey={expand ? 'code' : this.state.activeKey}
onChange={this.handleChange.bind(this)}>

View File

@ -9,7 +9,7 @@ const CopyableIcon = React.createClass({
justCopied: false
};
},
onCopied(e) {
onCopied() {
this.setState({ justCopied: true }, () => {
setTimeout(() => {
this.setState({ justCopied: false });
@ -17,7 +17,7 @@ const CopyableIcon = React.createClass({
});
},
render() {
const text = '<Icon type="' + this.props.type + '" />';
const text = `<Icon type="${this.props.type}" />`;
return (
<CopyToClipboard text={text} onCopy={this.onCopied}>
<li className={this.state.justCopied ? 'copied' : ''}>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router';
import scrollIntoView from 'dom-scroll-into-view';
import { Row, Col, Menu } from '../../../';
import config from '../../website.config';
const SubMenu = Menu.SubMenu;
@ -9,10 +10,23 @@ function dashed(name) {
}
export default class MainContent extends React.Component {
constructor(props) {
super(props);
componentDidMount() {
this.componentDidUpdate();
}
this.generateMenuItem = this.generateMenuItem.bind(this);
componentDidUpdate() {
const scrollTo = this.props.location.query.scrollTo;
if (scrollTo !== undefined) {
const target = document.getElementById(scrollTo);
if (target !== null) {
scrollIntoView(
target,
document,
{ alignWithTop: true, onlyScrollIfNeeded: false }
);
}
}
}
getActiveMenuItem(props, index) {
@ -20,9 +34,14 @@ export default class MainContent extends React.Component {
return routes[routes.length - 1].path || index;
}
generateMenuItem(item) {
generateMenuItem(isTop, item) {
const key = dashed(item.english);
const text = item.chinese || item.english;
const text = isTop ?
item.chinese || item.english :
[
<span key="english">{ item.english }</span>,
<span className="chinese" key="chinese">{ item.chinese }</span>
];
const disabled = item.disabled === 'true';
const child = !item.link ?
@ -45,13 +64,13 @@ export default class MainContent extends React.Component {
}
generateSubMenuItems(obj) {
const topLevel = (obj.topLevel || []).map(this.generateMenuItem);
const topLevel = (obj.topLevel || []).map(this.generateMenuItem.bind(this, true));
const itemGroups = Object.keys(obj).filter(this.isNotTopLevel)
.sort((a, b) => {
return config.typeOrder[a] - config.typeOrder[b];
})
.map((type, index) => {
const groupItems = obj[type].map(this.generateMenuItem);
const groupItems = obj[type].map(this.generateMenuItem.bind(this, false));
return (
<Menu.ItemGroup title={type} key={index}>
@ -117,7 +136,7 @@ export default class MainContent extends React.Component {
<Row>
<Col span="4">
<Menu className="sidebar" mode="inline"
defaultOpenKeys={Object.keys(this.props.menuItems)}
defaultOpenKeys={Object.keys(this.props.menuItems)}
selectedKeys={[activeMenuItem]}>
{ menuItems }
</Menu>

View File

@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router';
import hljs from 'highlight.js';
import antd from '../../';
@ -13,7 +14,7 @@ function mdLangToHljsLang(lang) {
lang;
}
export function objectToComponent(object, index) {
export function objectToComponent(pathname, object, index) {
if (object === null) return;
if (React.isValidElement(object)) {
@ -41,9 +42,10 @@ export function objectToComponent(object, index) {
if (isHeading(object.type)) {
return React.createElement(object.type, {
key: index,
id: children,
}, [
object.children,
<a className="anchor" key="anchor">#</a>,
<Link to={{ pathname, query: { scrollTo: object.children } }} className="anchor" key="anchor">#</Link>,
]);
}
@ -71,6 +73,6 @@ export function objectToComponent(object, index) {
return React.createElement(
object.type, { key: index },
children && children.map(objectToComponent) // `hr` has no children
children && children.map(objectToComponent.bind(null, pathname)) // `hr` has no children
);
}

View File

@ -55,8 +55,8 @@ export function generateChildren(data) {
const children = pagesData.map((pageData, index) => {
const hasDemos = demosList[pageData.meta.fileName];
const Wrapper = !hasDemos ?
() => <Article content={pageData} /> :
() => <ComponentDoc doc={pageData} />;
(props) => <Article {...props} content={pageData} /> :
(props) => <ComponentDoc {...props} doc={pageData} />;
return (
<Route key={index}
path={dashed(pageData.meta.english)}