Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] Add a Table Of Contents #12368

Merged
merged 1 commit into from
Aug 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions docs/src/modules/components/AppContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@ import classNames from 'classnames';
import { withStyles } from '@material-ui/core/styles';

const styles = theme => ({
root: theme.mixins.gutters({
root: {
paddingTop: 80,
flex: '1 1 100%',
maxWidth: '100%',
margin: '0 auto',
}),
[theme.breakpoints.up('md')]: {
root: {
maxWidth: theme.breakpoints.values.md,
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
[theme.breakpoints.up('sm')]: {
paddingLeft: theme.spacing.unit * 4,
paddingRight: theme.spacing.unit * 4,
maxWidth: 'calc(100% - 160px)',
},
[theme.breakpoints.up('lg')]: {
paddingLeft: theme.spacing.unit * 5,
paddingRight: theme.spacing.unit * 9,
maxWidth: 'calc(100% - 240px - 160px)',
},
},
});

function AppContent(props) {
const { className, classes, children } = props;

return <div className={classNames(classes.root, className)}>{children}</div>;
return <main className={classNames(classes.root, className)}>{children}</main>;
}

AppContent.propTypes = {
Expand Down
6 changes: 3 additions & 3 deletions docs/src/modules/components/AppDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { pageToTitle } from 'docs/src/modules/utils/helpers';

const styles = theme => ({
paper: {
width: 250,
width: 240,
backgroundColor: theme.palette.background.paper,
},
title: {
Expand Down Expand Up @@ -117,7 +117,7 @@ function AppDrawer(props, context) {
);

return (
<div className={className}>
<nav className={className}>
<Hidden lgUp={!disablePermanent} implementation="js">
<SwipeableDrawer
classes={{
Expand Down Expand Up @@ -148,7 +148,7 @@ function AppDrawer(props, context) {
</Drawer>
</Hidden>
)}
</div>
</nav>
);
}

Expand Down
5 changes: 3 additions & 2 deletions docs/src/modules/components/AppFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,13 @@ const styles = theme => ({
},
appBarShift: {
[theme.breakpoints.up('lg')]: {
width: 'calc(100% - 250px)',
width: 'calc(100% - 240px)',
},
},
drawer: {
[theme.breakpoints.up('lg')]: {
width: 250,
flexShrink: 0,
width: 240,
},
},
navIconHide: {
Expand Down
2 changes: 0 additions & 2 deletions docs/src/modules/components/AppSearch.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import keycode from 'keycode';
import compose from 'recompose/compose';
import pure from 'recompose/pure';
import EventListener from 'react-event-listener';
import PropTypes from 'prop-types';
import Router from 'next/router';
Expand Down Expand Up @@ -196,5 +195,4 @@ AppSearch.propTypes = {
export default compose(
withStyles(styles),
withWidth(),
pure,
)(AppSearch);
183 changes: 183 additions & 0 deletions docs/src/modules/components/AppTableOfContents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/* eslint-disable react/no-danger */

import React from 'react';
import PropTypes from 'prop-types';
import Link from 'docs/src/modules/components/Link';
import marked from 'marked';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import EventListener from 'react-event-listener';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import { textToHash } from '@material-ui/docs/MarkdownElement/MarkdownElement';

const renderer = new marked.Renderer();

let itemsServer = null;
renderer.heading = (text, level) => {
if (level === 1 || level > 3) {
return;
}

if (level === 2) {
itemsServer.push({
text,
level,
hash: textToHash(text),
children: [],
});
}

if (level === 3) {
itemsServer[itemsServer.length - 1].children.push({
text,
level,
hash: textToHash(text),
});
}
};

const styles = theme => ({
root: {
top: 70,
width: 160,
flexShrink: 0,
order: 2,
position: 'sticky',
wordBreak: 'break-word',
height: 'calc(100vh - 70px)',
overflowY: 'auto',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 2}px ${theme.spacing.unit *
2}px 0`,
display: 'none',
[theme.breakpoints.up('sm')]: {
display: 'block',
},
},
ul: {
padding: 0,
margin: 0,
listStyleType: 'none',
},
item: {
fontSize: 13,
padding: `${theme.spacing.unit / 2}px 0`,
},
});

class AppTableOfContents extends React.Component {
handleScroll = debounce(() => {
this.findActiveIndex();
}, 166); // Corresponds to 10 frames at 60 Hz.

constructor(props) {
super(props);
itemsServer = [];
marked(props.contents.join(''), {
renderer,
});
}

state = {
active: null,
};

componentDidMount() {
this.itemsClient = [];

itemsServer.forEach(item2 => {
this.itemsClient.push({
...item2,
node: document.getElementById(item2.hash),
});

if (item2.children.length > 0) {
item2.children.forEach(item3 => {
this.itemsClient.push({
...item3,
node: document.getElementById(item3.hash),
});
});
}
});

this.findActiveIndex();
}

componentWillUnmount() {
this.handleScroll.clear();
}

findActiveIndex = () => {
const active = this.itemsClient.find(
(item, index) =>
document.documentElement.scrollTop < item.node.offsetTop + 100 ||
index === this.itemsClient.length - 1,
);

if (active && this.state.active !== active.hash) {
this.setState({
active: active.hash,
});
}
};

render() {
const { classes } = this.props;
const { active } = this.state;

return (
<nav className={classes.root}>
{itemsServer.length > 0 ? (
<React.Fragment>
<Typography variant="body2" gutterBottom>
Contents
</Typography>
<EventListener target="window" onScroll={this.handleScroll} />
<ul className={classes.ul}>
{itemsServer.map(item2 => (
<li key={item2.text}>
<Typography
color={active === item2.hash ? 'textPrimary' : 'textSecondary'}
className={classes.item}
component={linkProps => (
<Link {...linkProps} variant="inherit" href={`#${item2.hash}`} />
)}
>
<span dangerouslySetInnerHTML={{ __html: item2.text }} />
</Typography>
{item2.children.length > 0 ? (
<ul className={classes.ul}>
{item2.children.map(item3 => (
<li key={item3.text}>
<Typography
className={classes.item}
style={{
paddingLeft: 8 * 2,
}}
color={active === item3.hash ? 'textPrimary' : 'textSecondary'}
component={linkProps => (
<Link {...linkProps} variant="inherit" href={`#${item3.hash}`} />
)}
>
<span dangerouslySetInnerHTML={{ __html: item3.text }} />
</Typography>
</li>
))}
</ul>
) : null}
</li>
))}
</ul>
</React.Fragment>
) : null}
</nav>
);
}
}

AppTableOfContents.propTypes = {
classes: PropTypes.object.isRequired,
contents: PropTypes.array.isRequired,
};

export default withStyles(styles)(AppTableOfContents);
10 changes: 8 additions & 2 deletions docs/src/modules/components/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ function Link(props) {
} = props;

let ComponentRoot;
const className = classNames(classes.root, classes[variant], classNameProp);
const className = classNames(
classes.root,
{
[classes[variant]]: variant !== 'inherit',
},
classNameProp,
);
let RootProps;
let children = childrenProp;

Expand Down Expand Up @@ -103,7 +109,7 @@ Link.propTypes = {
router: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
variant: PropTypes.oneOf(['default', 'primary', 'secondary', 'button']),
variant: PropTypes.oneOf(['default', 'primary', 'secondary', 'button', 'inherit']),
};

export default compose(
Expand Down
43 changes: 21 additions & 22 deletions docs/src/modules/components/MarkdownDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AppContent from 'docs/src/modules/components/AppContent';
import Demo from 'docs/src/modules/components/Demo';
import Carbon from 'docs/src/modules/components/Carbon';
import AppFrame from 'docs/src/modules/components/AppFrame';
import AppTableOfContents from 'docs/src/modules/components/AppTableOfContents';
import {
getHeaders,
getContents,
Expand Down Expand Up @@ -55,15 +56,30 @@ function MarkdownDocs(props, context) {
}
}

const section = markdownLocation.split('/')[4];
if (headers.components.length > 0) {
const section = markdownLocation.split('/')[4];
contents.push(`
## API

${headers.components
.map(
component =>
`- [&lt;${component} /&gt;](${section === 'lab' ? '/lab/api' : '/api'}/${kebabCase(
component,
)})`,
)
.join('\n')}
`);
}

return (
<AppFrame>
<Head
title={`${headers.title || getTitle(markdown)} - Material-UI`}
description={getDescription(markdown)}
/>
<AppTableOfContents contents={contents} />
<AppContent className={classes.root}>
<Head
title={`${headers.title || getTitle(markdown)} - Material-UI`}
description={getDescription(markdown)}
/>
<div className={classes.header}>
<Button component="a" href={`${SOURCE_CODE_ROOT_URL}${markdownLocation}`}>
{'Edit this page'}
Expand Down Expand Up @@ -94,23 +110,6 @@ function MarkdownDocs(props, context) {
<MarkdownElement className={classes.markdownElement} key={content} text={content} />
);
})}
{headers.components.length > 0 ? (
<MarkdownElement
className={classes.markdownElement}
text={`
## API

${headers.components
.map(
component =>
`- [&lt;${component} /&gt;](${
section === 'lab' ? '/lab/api' : '/api'
}/${kebabCase(component)})`,
)
.join('\n')}
`}
/>
) : null}
</AppContent>
</AppFrame>
);
Expand Down
10 changes: 5 additions & 5 deletions docs/src/pages/demos/lists/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ components: Collapse, Divider, List, ListItem, ListItemAvatar, ListItemIcon, Lis

[Lists](https://material.io/design/components/lists.html) are a continuous group of text or images. They are composed of items containing primary and supplemental actions, which are represented by icons and text.

### Simple List
## Simple List

{{"demo": "pages/demos/lists/SimpleList.js"}}

### Folder List
## Folder List

{{"demo": "pages/demos/lists/FolderList.js"}}

### Inset List
## Inset List

{{"demo": "pages/demos/lists/InsetList.js"}}

### Nested List
## Nested List

{{"demo": "pages/demos/lists/NestedList.js"}}

### Pinned Subheader List
## Pinned Subheader List

Upon scrolling, subheaders remain pinned to the top of the screen until pushed off screen by the next subheader.

Expand Down
Loading