Toggle improvements (#17)

* Toggle improvements

* Fix tests
This commit is contained in:
Tom MacWright
2016-05-20 14:08:29 -04:00
parent 847124a40d
commit ffdf4a6123
12 changed files with 87 additions and 107 deletions

View File

@ -1321,39 +1321,6 @@ table.fixed {
.scroll-styled { .scroll-styled {
overflow:auto; overflow:auto;
} }
.scroll-styled .highlight pre::webkit-scrollbar,
.scroll-styled::-webkit-scrollbar {
width:8px;
height:8px;
border-left:0;
background:rgba(0,0,0,0.1);
}
.scroll-styled .highlight pre::webkit-scrollbar:hover,
.scroll-styled::-webkit-scrollbar:hover {
background:rgba(0,0,0,0.15);
}
.scroll-styled .highlight pre::webkit-scrollbar-track,
.scroll-styled::-webkit-scrollbar-track {
background:none;
}
.scroll-styled .highlight pre::webkit-scrollbar-thumb,
.scroll-styled::-webkit-scrollbar-thumb {
background:rgba(0,0,0,0.1);
border-radius:0;
}
.dark .scroll-styled::-webkit-scrollbar {
width:8px;
height:8px;
background:rgba(255,255,255,0.1);
border-radius:0;
}
.dark .scroll-styled::-webkit-scrollbar:hover {
background:rgba(255,255,255,0.15);
}
.dark .scroll-styled::-webkit-scrollbar-thumb {
background:rgba(255,255,255,0.1);
}
/* Inline Elements: Formatted for read content /* Inline Elements: Formatted for read content
------------------------------------------------------- */ ------------------------------------------------------- */

View File

@ -44,30 +44,6 @@
padding: 5px; padding: 5px;
} }
.scroll-styled::-webkit-scrollbar {
width: 6px;
height: 6px;
background: transparent;
}
.scroll-styled::-webkit-scrollbar:hover {
background: transparent;
}
.scroll-styled::-webkit-scrollbar-track {
background:none;
}
.scroll-styled::-webkit-scrollbar-thumb {
background: rgba(0,0,0,.18);
width: 6px;
border:none;
border-radius: 3px;
}
.scroll-styled::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,.25);
}
.scroll-styled::-webkit-scrollbar-track:hover {
background: transparent;
}
.dark.keyline-top, .dark.keyline-top,
.dark.keyline-bottom { .dark.keyline-bottom {
border-color: #313131; border-color: #313131;

View File

@ -5,14 +5,37 @@ import RoundedToggle from './rounded_toggle';
import PureRenderMixin from 'react-pure-render/mixin'; import PureRenderMixin from 'react-pure-render/mixin';
import GithubSlugger from 'github-slugger'; import GithubSlugger from 'github-slugger';
import debounce from 'lodash.debounce'; import debounce from 'lodash.debounce';
import { brandNames, brandClasses } from '../../custom'; import { brandNames, brandClasses } from '../custom';
import qs from 'querystring'; import qs from 'querystring';
let slugger = new GithubSlugger(); let slugger = new GithubSlugger();
let slug = title => { slugger.reset(); return slugger.slug(title); }; let slug = title => { slugger.reset(); return slugger.slug(title); };
let languageOptions = ['cURL', 'CLI', 'Python', 'JavaScript']; let languageOptions = [
let defaultLanguage = 'cURL'; { title: 'cURL',
short: 'cURL',
value: 'curl' },
{ title: 'CLI',
short: 'cli',
value: 'cli' },
{ title: 'Python',
short: 'Python',
value: 'python' },
{ title: 'JavaScript',
short: 'JS',
value: 'javascript' },
{ title: 'Java',
short: 'Java',
value: 'java' },
{ title: 'Objective-C',
short: 'ObjC',
value: 'objc' },
{ title: 'Swift',
short: 'Swift',
value: 'swift' }
];
let defaultLanguage = languageOptions[0];
let debouncedReplaceState = debounce(hash => { let debouncedReplaceState = debounce(hash => {
window.history.replaceState('', '', hash); window.history.replaceState('', '', hash);
@ -30,16 +53,15 @@ 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 languageFromURL = qs.parse(window.location.search.substring(1)).language; let languageFromURL = qs.parse(window.location.search.substring(1)).language;
let language = languageOptions.includes(languageFromURL) ? let language = languageOptions.find(option => option.title === languageFromURL) ||
languageFromURL : defaultLanguage; defaultLanguage;
let mqls = { let mqls = [
desktop: window.matchMedia('(min-width: 961px)'), { name: 'widescreen', query: window.matchMedia('(min-width: 1200px)') },
tablet: window.matchMedia('(max-width: 960px)'), { name: 'desktop', query: window.matchMedia('(min-width: 961px)') },
mobile: window.matchMedia('(max-width: 640px)') { name: 'tablet', query: window.matchMedia('(max-width: 960px)') },
}; { name: 'mobile', query: window.matchMedia('(max-width: 640px)') }
Object.keys(mqls).forEach(key => { ];
mqls[key].addListener(this.mediaQueryChanged); mqls.forEach(q => q.query.addListener(this.mediaQueryChanged));
});
if (hash) { if (hash) {
let headingForHash = this.props.ast.children let headingForHash = this.props.ast.children
.filter(child => child.type === 'heading') .filter(child => child.type === 'heading')
@ -61,9 +83,7 @@ var App = React.createClass({
}; };
} else { } else {
return { return {
mqls: { mqls: { },
desktop: true
},
queryMatches: { queryMatches: {
desktop: true desktop: true
}, },
@ -78,11 +98,11 @@ var App = React.createClass({
}, },
componentDidMount() { componentDidMount() {
this.mediaQueryChanged(); this.mediaQueryChanged();
this.onScroll = debounce(this._onScroll, 100); this.onScroll = debounce(this.onScrollImmediate, 100);
document.addEventListener('scroll', this.onScroll); document.addEventListener('scroll', this.onScroll);
this._onScroll(); this.onScrollImmediate();
}, },
_onScroll() { onScrollImmediate() {
var sections = document.querySelectorAll('div.section'); var sections = document.querySelectorAll('div.section');
if (!sections.length) return; if (!sections.length) return;
for (var i = 0; i < sections.length; i++) { for (var i = 0; i < sections.length; i++) {
@ -97,23 +117,21 @@ var App = React.createClass({
}, },
mediaQueryChanged() { mediaQueryChanged() {
this.setState({ this.setState({
queryMatches: { queryMatches: this.state.mqls.reduce((memo, q) => {
mobile: this.state.mqls.mobile.matches, memo[q.name] = q.query.matches;
tablet: this.state.mqls.tablet.matches, return memo;
desktop: this.state.mqls.desktop.matches }, {})
}
}); });
}, },
componentWillUnmount() { componentWillUnmount() {
Object.keys(this.state.mqls).forEach(key => this.state.mqls.forEach(q => q.removeListener(this.mediaQueryChanged));
this.state.mqls[key].removeListener(this.mediaQueryChanged));
document.body.removeEventListener('scroll', this.onScroll); document.body.removeEventListener('scroll', this.onScroll);
}, },
onChangeLanguage(language) { onChangeLanguage(language) {
this.setState({ language }, () => { this.setState({ language }, () => {
if (window.history) { if (window.history) {
window.history.pushState(null, null, window.history.pushState(null, null,
`?${qs.stringify({ language })}${window.location.hash}`); `?${qs.stringify({ language: language.title })}${window.location.hash}`);
} }
}); });
}, },
@ -121,7 +139,7 @@ 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.title !== this.state.language.title ||
prevState.columnMode !== this.state.columnMode) { 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.hash = window.location.hash; window.location.hash = window.location.hash;
@ -141,7 +159,7 @@ var App = React.createClass({
}); });
}, },
render() { render() {
let { ast } = this.props; let ast = JSON.parse(JSON.stringify(this.props.ast));
let { activeSection, queryMatches, showNav, columnMode } = this.state; let { activeSection, queryMatches, showNav, columnMode } = this.state;
let col1 = columnMode === 1 && queryMatches.desktop; let col1 = columnMode === 1 && queryMatches.desktop;
return (<div className='container unlimiter'> return (<div className='container unlimiter'>
@ -152,7 +170,7 @@ var App = React.createClass({
</div>} </div>}
{/* Desktop nav */ } {/* Desktop nav */ }
{queryMatches.desktop && <div className='space-top5 scroll-styled pad1 width16 sidebar fixed-left fill-dark dark'> {queryMatches.desktop && <div className='space-top5 scroll-styled overflow-auto pad1 width16 sidebar fixed-left fill-dark dark'>
<Navigation <Navigation
navigationItemClicked={this.navigationItemClicked} navigationItemClicked={this.navigationItemClicked}
activeSection={activeSection} activeSection={activeSection}
@ -166,7 +184,7 @@ var App = React.createClass({
leftClassname={col1 ? 'space-bottom4 pad2x prose clip' : 'space-bottom8 col6 pad2x prose clip'} leftClassname={col1 ? 'space-bottom4 pad2x prose clip' : 'space-bottom8 col6 pad2x prose clip'}
rightClassname={col1 ? 'space-bottom2 pad2 prose clip fill-light space-top5' : 'space-bottom4 col6 pad2 prose clip fill-light space-top5'} rightClassname={col1 ? 'space-bottom2 pad2 prose clip fill-light space-top5' : 'space-bottom4 col6 pad2 prose clip fill-light space-top5'}
ast={ast} ast={ast}
language={this.state.language.toLowerCase()}/> language={this.state.language}/>
</div> </div>
</div> </div>
@ -177,6 +195,7 @@ var App = React.createClass({
Show examples in: Show examples in:
</div> </div>
<RoundedToggle <RoundedToggle
short={!queryMatches.widescreen}
options={languageOptions} options={languageOptions}
onChange={this.onChangeLanguage} onChange={this.onChangeLanguage}
active={this.state.language} /> active={this.state.language} />

View File

@ -2,10 +2,15 @@ import React from 'react';
import Section from './section'; import Section from './section';
import PureRenderMixin from 'react-pure-render/mixin'; import PureRenderMixin from 'react-pure-render/mixin';
import GithubSlugger from 'github-slugger'; import GithubSlugger from 'github-slugger';
import { transformURL } from '../../custom'; import { transformURL } from '../custom';
let slugger = new GithubSlugger(); let slugger = new GithubSlugger();
let slug = title => { slugger.reset(); return slugger.slug(title); }; let slug = title => { slugger.reset(); return slugger.slug(title); };
var roundedToggleOptionType = React.PropTypes.shape({
title: React.PropTypes.string,
value: React.PropTypes.string
});
function chunkifyAST(ast, language) { function chunkifyAST(ast, language) {
var preview = false; var preview = false;
return ast.children.reduce((chunks, node) => { return ast.children.reduce((chunks, node) => {
@ -70,14 +75,14 @@ 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: roundedToggleOptionType,
leftClassname: React.PropTypes.string.isRequired, leftClassname: React.PropTypes.string.isRequired,
rightClassname: React.PropTypes.string.isRequired rightClassname: React.PropTypes.string.isRequired
}, },
render() { render() {
let { ast, language, leftClassname, rightClassname } = this.props; let { ast, language, leftClassname, rightClassname } = this.props;
return (<div className='clearfix'> return (<div className='clearfix'>
{chunkifyAST(ast, language).map((chunk, i) => <Section {chunkifyAST(ast, language.value).map((chunk, i) => <Section
leftClassname={leftClassname} leftClassname={leftClassname}
rightClassname={rightClassname} rightClassname={rightClassname}
chunk={chunk} chunk={chunk}

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PureRenderMixin from 'react-pure-render/mixin'; import PureRenderMixin from 'react-pure-render/mixin';
import NavigationItem from './navigation_item'; import NavigationItem from './navigation_item';
import { backLink } from '../../custom'; import { footerContent } from '../custom';
function getAllInSectionFromChild(headings, idx) { function getAllInSectionFromChild(headings, idx) {
for (var i = idx; i > 0; i--) { for (var i = idx; i > 0; i--) {
@ -88,7 +88,7 @@ var Navigation = React.createClass({
} }
} }
})} })}
<a href='/' className='space-top2 pad1y dark keyline-top block small quiet'>{backLink}</a> {footerContent}
</div>); </div>);
} }
}); });

View File

@ -1,11 +1,17 @@
import React from 'react'; import React from 'react';
import PureRenderMixin from 'react-pure-render/mixin'; import PureRenderMixin from 'react-pure-render/mixin';
var roundedToggleOptionType = React.PropTypes.shape({
title: React.PropTypes.string,
value: React.PropTypes.string
});
var RoundedToggle = React.createClass({ var RoundedToggle = React.createClass({
mixins: [PureRenderMixin], mixins: [PureRenderMixin],
propTypes: { propTypes: {
options: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, options: React.PropTypes.arrayOf(roundedToggleOptionType).isRequired,
active: React.PropTypes.string.isRequired, active: roundedToggleOptionType,
short: React.PropTypes.bool,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}, },
render() { render() {
@ -13,10 +19,11 @@ var RoundedToggle = React.createClass({
return (<div className='rounded-toggle inline short'> return (<div className='rounded-toggle inline short'>
{options.map(option => {options.map(option =>
<RoundedToggleOption <RoundedToggleOption
key={option} key={option.value}
option={option} option={option}
short={this.props.short}
onClick={this.props.onChange} onClick={this.props.onChange}
className={`strong ${option === active ? 'active': ''}`} />)} className={`strong ${option.value === active.value ? 'active': ''}`} />)}
</div>); </div>);
} }
}); });
@ -24,8 +31,9 @@ var RoundedToggle = React.createClass({
var RoundedToggleOption = React.createClass({ var RoundedToggleOption = React.createClass({
mixins: [PureRenderMixin], mixins: [PureRenderMixin],
propTypes: { propTypes: {
option: React.PropTypes.string.isRequired, option: roundedToggleOptionType,
className: React.PropTypes.string.isRequired, className: React.PropTypes.string.isRequired,
short: React.PropTypes.bool,
onClick: React.PropTypes.func.isRequired onClick: React.PropTypes.func.isRequired
}, },
onClick() { onClick() {
@ -35,7 +43,7 @@ var RoundedToggleOption = React.createClass({
let { className, option } = this.props; let { className, option } = this.props;
return (<a return (<a
onClick={this.onClick} onClick={this.onClick}
className={className}>{option}</a>); className={className}>{this.props.short ? option.short : option.title}</a>);
} }
}); });

View File

@ -3,16 +3,19 @@ import remark from 'remark';
import remarkHTML from 'remark-html'; import remarkHTML from 'remark-html';
import remarkHighlight from '../highlight'; import remarkHighlight from '../highlight';
import PureRenderMixin from 'react-pure-render/mixin'; import PureRenderMixin from 'react-pure-render/mixin';
import { postHighlight } from '../../custom'; import { postHighlight, remarkPlugins } from '../custom';
function renderHighlighted(nodes) { function renderHighlighted(nodes) {
return { return {
__html: postHighlight(remark() __html: postHighlight(remark()
.use(remarkHTML) .use(remarkHTML)
.stringify(remark().use(remarkHighlight).run({ .stringify(remark()
type: 'root', .use(remarkHighlight)
children: nodes .use(remarkPlugins)
}))) .run({
type: 'root',
children: nodes
})))
}; };
} }

View File

@ -59,3 +59,5 @@ module.exports.transformURL = function(value) {
</div>` </div>`
}; };
}; };
module.exports.remarkPlugins = [];

View File

@ -4,7 +4,7 @@ import ReactDOM from 'react-dom';
import App from './components/app'; import App from './components/app';
import remark from 'remark'; import remark from 'remark';
import slug from 'remark-slug'; import slug from 'remark-slug';
import content from '../custom/content'; import content from './custom/content';
var ast = remark() var ast = remark()
.use(slug) .use(slug)

View File

@ -3,7 +3,7 @@ import ReactDOMServer from 'react-dom/server';
import App from './components/app'; import App from './components/app';
import remark from 'remark'; import remark from 'remark';
import slug from 'remark-slug'; import slug from 'remark-slug';
import content from '../custom/content'; import content from './custom/content';
import fs from 'fs'; import fs from 'fs';
var ast = remark() var ast = remark()

View File

@ -6,7 +6,7 @@ var select = require('unist-util-select');
var fs = require('fs'); var fs = require('fs');
var GithubSlugger = require('github-slugger'); var GithubSlugger = require('github-slugger');
var { linter } = require('eslint'); var { linter } = require('eslint');
var allPages = require('../custom/content'); var allPages = require('../src/custom/content');
var slugger = new GithubSlugger(); var slugger = new GithubSlugger();
var actionVerbs = /^(List|Retrieve|Remove|Search|Create|Delete)/; var actionVerbs = /^(List|Retrieve|Remove|Search|Create|Delete)/;