Add column-mode toggle
This commit is contained in:
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
### March 14, 2016
|
||||||
|
|
||||||
|
* Support for toggling between 1 and 2 column mode
|
@ -28,9 +28,9 @@ var App = React.createClass({
|
|||||||
if (process.browser) {
|
if (process.browser) {
|
||||||
let hash = window.location.hash.split('#').pop();
|
let hash = window.location.hash.split('#').pop();
|
||||||
let mqls = {
|
let mqls = {
|
||||||
'desktop': window.matchMedia('(min-width: 961px)'),
|
desktop: window.matchMedia('(min-width: 961px)'),
|
||||||
'tablet': window.matchMedia('(max-width: 960px)'),
|
tablet: window.matchMedia('(max-width: 960px)'),
|
||||||
'mobile': window.matchMedia('(max-width: 640px)')
|
mobile: window.matchMedia('(max-width: 640px)')
|
||||||
};
|
};
|
||||||
Object.keys(mqls).forEach(key => {
|
Object.keys(mqls).forEach(key => {
|
||||||
mqls[key].addListener(this.mediaQueryChanged);
|
mqls[key].addListener(this.mediaQueryChanged);
|
||||||
@ -44,10 +44,14 @@ var App = React.createClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
// media queryMatches
|
||||||
mqls: mqls,
|
mqls: mqls,
|
||||||
queries: {},
|
// object of currently matched queries, like { desktop: true }
|
||||||
|
queryMatches: {},
|
||||||
language: 'cURL',
|
language: 'cURL',
|
||||||
|
columnMode: 2,
|
||||||
activeSection: active,
|
activeSection: active,
|
||||||
|
// for the toggle-able navigation on mobile
|
||||||
showNav: false
|
showNav: false
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -55,7 +59,7 @@ var App = React.createClass({
|
|||||||
mqls: {
|
mqls: {
|
||||||
desktop: true
|
desktop: true
|
||||||
},
|
},
|
||||||
queries: {
|
queryMatches: {
|
||||||
desktop: true
|
desktop: true
|
||||||
},
|
},
|
||||||
language: 'cURL',
|
language: 'cURL',
|
||||||
@ -88,7 +92,7 @@ var App = React.createClass({
|
|||||||
},
|
},
|
||||||
mediaQueryChanged() {
|
mediaQueryChanged() {
|
||||||
this.setState({
|
this.setState({
|
||||||
queries: {
|
queryMatches: {
|
||||||
mobile: this.state.mqls.mobile.matches,
|
mobile: this.state.mqls.mobile.matches,
|
||||||
tablet: this.state.mqls.tablet.matches,
|
tablet: this.state.mqls.tablet.matches,
|
||||||
desktop: this.state.mqls.desktop.matches
|
desktop: this.state.mqls.desktop.matches
|
||||||
@ -107,48 +111,58 @@ var App = React.createClass({
|
|||||||
if (prevState.activeSection !== this.state.activeSection) {
|
if (prevState.activeSection !== this.state.activeSection) {
|
||||||
// when the section changes, replace the hash
|
// when the section changes, replace the hash
|
||||||
debouncedReplaceState(`#${slug(this.state.activeSection)}`);
|
debouncedReplaceState(`#${slug(this.state.activeSection)}`);
|
||||||
} else if (prevState.language !== this.state.language) {
|
} else if (prevState.language !== this.state.language ||
|
||||||
|
prevState.columnMode !== this.state.columnMode) {
|
||||||
// when the language changes, use the hash to set scroll
|
// when the language changes, use the hash to set scroll
|
||||||
window.location = window.location;
|
window.location.hash = window.location.hash;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleClick(activeSection) {
|
navigationItemClicked(activeSection) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState({ activeSection });
|
this.setState({ activeSection });
|
||||||
}, 10);
|
}, 10);
|
||||||
if (!this.state.queries.desktop) {
|
if (!this.state.queryMatches.desktop) {
|
||||||
this.toggleNav();
|
this.toggleNav();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleColumnMode() {
|
||||||
|
this.setState({
|
||||||
|
columnMode: this.state.columnMode === 1 ? 2 : 1
|
||||||
|
});
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
let { ast } = this.props;
|
let { ast } = this.props;
|
||||||
let { activeSection, queries, showNav } = this.state;
|
let { activeSection, queryMatches, showNav, columnMode } = this.state;
|
||||||
|
let col1 = columnMode === 1 && queryMatches.desktop;
|
||||||
return (<div className='container unlimiter'>
|
return (<div className='container unlimiter'>
|
||||||
|
|
||||||
{/* Content background */ }
|
{/* Content background */ }
|
||||||
{!queries.mobile && <div className={`fixed-top fixed-right ${queries.desktop && 'space-left16'}`}>
|
{(!col1 && !queryMatches.mobile) && <div className={`fixed-top fixed-right ${queryMatches.desktop && 'space-left16'}`}>
|
||||||
<div className='fill-light col6 pin-right'></div>
|
<div className='fill-light col6 pin-right'></div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* Desktop nav */ }
|
{/* Desktop nav */ }
|
||||||
{queries.desktop && <div className='space-top5 scroll-styled pad1 width16 sidebar fixed-left fill-dark dark'>
|
{queryMatches.desktop && <div className='space-top5 scroll-styled pad1 width16 sidebar fixed-left fill-dark dark'>
|
||||||
<Navigation
|
<Navigation
|
||||||
handleClick={this.handleClick}
|
navigationItemClicked={this.navigationItemClicked}
|
||||||
activeSection={activeSection}
|
activeSection={activeSection}
|
||||||
ast={ast} />
|
ast={ast} />
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* Content */ }
|
{/* Content */ }
|
||||||
<div className={`${queries.desktop && 'space-left16'}`}>
|
<div className={`${queryMatches.desktop && 'space-left16'}`}>
|
||||||
<Content
|
<div className={col1 ? 'col8 margin1' : ''}>
|
||||||
ast={ast}
|
<Content
|
||||||
queries={queries}
|
leftClassname={col1 ? 'space-bottom4 pad2x prose clip' : 'space-bottom8 col6 pad2x prose clip'}
|
||||||
language={this.state.language.toLowerCase()}/>
|
rightClassname={col1 ? 'space-bottom2 pad2 prose clip fill-light space-top5' : 'space-bottom4 col6 pad2 prose clip fill-light space-top5'}
|
||||||
|
ast={ast}
|
||||||
|
language={this.state.language.toLowerCase()}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Language toggle */ }
|
{/* Language toggle */ }
|
||||||
<div className={`fixed-top ${queries.desktop && 'space-left16'}`}>
|
<div className={`fixed-top ${queryMatches.desktop && 'space-left16'}`}>
|
||||||
<div className={`events fill-light bottom-shadow pad1 col6 pin-topright ${queries.tablet && 'dark fill-blue'} ${queries.mobile && 'space-top5 fixed-topright'}`}>
|
<div className={`events fill-light bottom-shadow pad1 ${col1 ? '' : 'col6 pin-topright'} ${queryMatches.tablet ? 'dark fill-blue' : ''} ${queryMatches.mobile ? 'space-top5 fixed-topright' : ''}`}>
|
||||||
<div className='space-right1 small quiet inline'>
|
<div className='space-right1 small quiet inline'>
|
||||||
Show examples in:
|
Show examples in:
|
||||||
</div>
|
</div>
|
||||||
@ -156,29 +170,39 @@ var App = React.createClass({
|
|||||||
options={languageOptions}
|
options={languageOptions}
|
||||||
onChange={this.onChangeLanguage}
|
onChange={this.onChangeLanguage}
|
||||||
active={this.state.language} />
|
active={this.state.language} />
|
||||||
|
<div className='fr pad0'>
|
||||||
|
{queryMatches.desktop ?
|
||||||
|
<a
|
||||||
|
title={`Display as ${col1 ? 2 : 1} column`}
|
||||||
|
onClick={this.toggleColumnMode}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
className={`icon quiet caret-${col1 ? 'right' : 'left'} pad0 fill-darken0 round`}></a> : null}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Header */ }
|
{/* Header */ }
|
||||||
<div className={`fill-dark dark bottom-shadow fixed-top ${queries.tablet ? 'pad1y pad2x col6' : 'pad0 width16'}`}>
|
<div className={`fill-dark dark bottom-shadow fixed-top ${queryMatches.tablet ? 'pad1y pad2x col6' : 'pad0 width16'}`}>
|
||||||
<a href='/' className={`active space-top1 space-left1 pin-topleft icon round dark pad0 ${brandClasses}`}></a>
|
<a href='/' className={`active space-top1 space-left1 pin-topleft icon round dark pad0 ${brandClasses}`}></a>
|
||||||
<div className={`strong small pad0 ${queries.mobile && 'space-left3'} ${queries.tablet ? 'space-left2' : 'space-left4 line-height15' }`}>
|
<div className={`strong small pad0
|
||||||
{queries.desktop ? brandNames.desktop :
|
${queryMatches.mobile ? 'space-left3' : ''}
|
||||||
queries.mobile ? brandNames.mobile : brandNames.tablet}
|
${queryMatches.tablet ? 'space-left2' : 'space-left4 line-height15' }`}>
|
||||||
|
{queryMatches.desktop ? brandNames.desktop :
|
||||||
|
queryMatches.mobile ? brandNames.mobile : brandNames.tablet}
|
||||||
</div>
|
</div>
|
||||||
{queries.tablet && <div>
|
{queryMatches.tablet && <div>
|
||||||
<button
|
<button
|
||||||
onClick={this.toggleNav}
|
onClick={this.toggleNav}
|
||||||
className={`short quiet pin-topright button rcon ${showNav ? 'caret-up' : 'caret-down'} space-right1 space-top1`}>
|
className={`short quiet pin-topright button rcon ${showNav ? 'caret-up' : 'caret-down'} space-right1 space-top1`}>
|
||||||
<span className='micro'>{activeSection}</span>
|
<span className='micro'>{activeSection}</span>
|
||||||
</button>
|
</button>
|
||||||
{showNav && <div
|
{showNav && <div
|
||||||
className='fixed-left keyline-top fill-dark pin-left col6 pad2 scroll-styled space-top5'>
|
className='fixed-left keyline-top fill-dark pin-left col6 pad2 scroll-styled space-top5'>
|
||||||
<Navigation
|
<Navigation
|
||||||
handleClick={this.handleClick}
|
navigationItemClicked={this.navigationItemClicked}
|
||||||
activeSection={activeSection}
|
activeSection={activeSection}
|
||||||
ast={ast} />
|
ast={ast} />
|
||||||
</div>}
|
</div>}
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -70,11 +70,16 @@ var Content = React.createClass({
|
|||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
propTypes: {
|
propTypes: {
|
||||||
ast: React.PropTypes.object.isRequired,
|
ast: React.PropTypes.object.isRequired,
|
||||||
language: React.PropTypes.string.isRequired
|
language: React.PropTypes.string.isRequired,
|
||||||
|
leftClassname: React.PropTypes.string.isRequired,
|
||||||
|
rightClassname: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
let { ast, language, leftClassname, rightClassname } = this.props;
|
||||||
return (<div className='clearfix'>
|
return (<div className='clearfix'>
|
||||||
{chunkifyAST(this.props.ast, this.props.language).map((chunk, i) => <Section
|
{chunkifyAST(ast, language).map((chunk, i) => <Section
|
||||||
|
leftClassname={leftClassname}
|
||||||
|
rightClassname={rightClassname}
|
||||||
chunk={chunk}
|
chunk={chunk}
|
||||||
key={i} />)}
|
key={i} />)}
|
||||||
</div>);
|
</div>);
|
||||||
|
@ -28,7 +28,7 @@ var Navigation = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
ast: React.PropTypes.object.isRequired,
|
ast: React.PropTypes.object.isRequired,
|
||||||
activeSection: React.PropTypes.string,
|
activeSection: React.PropTypes.string,
|
||||||
handleClick: React.PropTypes.func.isRequired
|
navigationItemClicked: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
var activeHeadings = [];
|
var activeHeadings = [];
|
||||||
@ -65,13 +65,13 @@ var Navigation = React.createClass({
|
|||||||
var active = sectionName === this.props.activeSection;
|
var active = sectionName === this.props.activeSection;
|
||||||
if (child.depth === 1) {
|
if (child.depth === 1) {
|
||||||
return (<div key={i}
|
return (<div key={i}
|
||||||
onClick={this.handleClick}
|
onClick={this.navigationItemClicked}
|
||||||
className='small pad0x quiet space-top1'>{sectionName}</div>);
|
className='small pad0x quiet space-top1'>{sectionName}</div>);
|
||||||
} else if (child.depth === 2) {
|
} else if (child.depth === 2) {
|
||||||
return (<NavigationItem
|
return (<NavigationItem
|
||||||
key={i}
|
key={i}
|
||||||
href={`#${child.data.id}`}
|
href={`#${child.data.id}`}
|
||||||
handleClick={this.props.handleClick}
|
onClick={this.props.navigationItemClicked}
|
||||||
active={active}
|
active={active}
|
||||||
sectionName={sectionName} />);
|
sectionName={sectionName} />);
|
||||||
} else if (child.depth === 3) {
|
} else if (child.depth === 3) {
|
||||||
@ -81,7 +81,7 @@ var Navigation = React.createClass({
|
|||||||
className='space-left1'>
|
className='space-left1'>
|
||||||
<NavigationItem
|
<NavigationItem
|
||||||
href={`#${child.data.id}`}
|
href={`#${child.data.id}`}
|
||||||
handleClick={this.props.handleClick}
|
onClick={this.props.navigationItemClicked}
|
||||||
active={active}
|
active={active}
|
||||||
sectionName={sectionName} />
|
sectionName={sectionName} />
|
||||||
</div>);
|
</div>);
|
||||||
|
@ -6,11 +6,11 @@ var NavigationItem = React.createClass({
|
|||||||
propTypes: {
|
propTypes: {
|
||||||
sectionName: React.PropTypes.string.isRequired,
|
sectionName: React.PropTypes.string.isRequired,
|
||||||
active: React.PropTypes.bool.isRequired,
|
active: React.PropTypes.bool.isRequired,
|
||||||
handleClick: React.PropTypes.func.isRequired,
|
onClick: React.PropTypes.func.isRequired,
|
||||||
href: React.PropTypes.string.isRequired
|
href: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
onClick() {
|
onClick() {
|
||||||
this.props.handleClick(this.props.sectionName);
|
this.props.onClick(this.props.sectionName);
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
var {sectionName, href, active} = this.props;
|
var {sectionName, href, active} = this.props;
|
||||||
|
@ -4,7 +4,7 @@ import PureRenderMixin from 'react-pure-render/mixin';
|
|||||||
var RoundedToggle = React.createClass({
|
var RoundedToggle = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
propTypes: {
|
propTypes: {
|
||||||
options: React.PropTypes.array.isRequired,
|
options: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
|
||||||
active: React.PropTypes.string.isRequired,
|
active: React.PropTypes.string.isRequired,
|
||||||
onChange: React.PropTypes.func.isRequired
|
onChange: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
@ -16,7 +16,7 @@ var RoundedToggle = React.createClass({
|
|||||||
key={option}
|
key={option}
|
||||||
option={option}
|
option={option}
|
||||||
onClick={this.props.onChange}
|
onClick={this.props.onChange}
|
||||||
className={'strong ' + (option === active ? 'active': '')} />)}
|
className={`strong ${option === active ? 'active': ''}`} />)}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -19,19 +19,21 @@ function renderHighlighted(nodes) {
|
|||||||
var Section = React.createClass({
|
var Section = React.createClass({
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
propTypes: {
|
propTypes: {
|
||||||
chunk: React.PropTypes.object.isRequired
|
chunk: React.PropTypes.object.isRequired,
|
||||||
|
leftClassname: React.PropTypes.string.isRequired,
|
||||||
|
rightClassname: React.PropTypes.string.isRequired
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
let { chunk } = this.props;
|
let { chunk, leftClassname, rightClassname } = this.props;
|
||||||
let { left, right, preview } = chunk;
|
let { left, right, preview } = chunk;
|
||||||
return (<div
|
return (<div
|
||||||
data-title={chunk.title}
|
data-title={chunk.title}
|
||||||
className={`keyline-top section contain clearfix ${preview ? 'preview' : ''}`}>
|
className={`keyline-top section contain clearfix ${preview ? 'preview' : ''}`}>
|
||||||
<div
|
<div
|
||||||
className='space-bottom8 col6 pad2x prose clip'
|
className={leftClassname}
|
||||||
dangerouslySetInnerHTML={renderHighlighted(left)} />
|
dangerouslySetInnerHTML={renderHighlighted(left)} />
|
||||||
{right.length > 0 && <div
|
{right.length > 0 && <div
|
||||||
className='space-bottom4 col6 pad2 prose clip fill-light space-top5'
|
className={rightClassname}
|
||||||
dangerouslySetInnerHTML={renderHighlighted(right)} />}
|
dangerouslySetInnerHTML={renderHighlighted(right)} />}
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user