Improve Docs navigation on handheld devices.

Summary:
Fixes #7519

JS detects handheld device by sniffing UA string (very primitive detection). If on handheld device, event listener is registered. Event handler toggles Docs Navigation overlay after clicking on "Docs" nav button.
Original Docs Navigation panel is taken out of the natural page flow using pure CSS and is styled to look "good" on device.  As a result of this, Navigation overlay is ONLY visible when you are at Docs page, otherwise "Docs" nav button takes you Docs page first.

iPhone/iPad previews
![iphone](https://cloud.githubusercontent.com/assets/829963/15409630/f1a64b1a-1e15-11e6-92eb-f85c5cd06754.gif)
![ipad](https://cloud.githubusercontent.com/assets/829963/15409631/f1a6f952-1e15-11e6-8f5c-6f89f54e6814.gif)
Closes https://github.com/facebook/react-native/pull/7640

Differential Revision: D3325440

Pulled By: vjeux

fbshipit-source-id: a06b21d743d56bfea5db5b750836856c3af9bbe2
This commit is contained in:
Andrej Badin 2016-05-20 02:41:21 -07:00 committed by Facebook Github Bot 9
parent 3ccfb58701
commit 00c77800c9
4 changed files with 133 additions and 38 deletions

View File

@ -69,32 +69,31 @@ var DocsSidebar = React.createClass({
},
getLink: function(metadata) {
if (metadata.permalink.match(/^https?:/)) {
return metadata.permalink;
}
return metadata.permalink + '#content';
return metadata.permalink;
},
render: function() {
return <div className="nav-docs">
{this.getCategories().map((category) =>
<div className="nav-docs-section" key={category.name}>
<h3>{category.name}</h3>
<ul>
{category.links.map((metadata) =>
<li key={metadata.id}>
<a
target={metadata.permalink.match(/^https?:/) && '_blank'}
style={{marginLeft: metadata.indent ? 20 : 0}}
className={metadata.id === this.props.metadata.id ? 'active' : ''}
href={this.getLink(metadata)}>
{metadata.title}
</a>
</li>
)}
</ul>
</div>
)}
<div className="nav-docs-viewport">
{this.getCategories().map((category) =>
<div className="nav-docs-section" key={category.name}>
<h3>{category.name}</h3>
<ul>
{category.links.map((metadata) =>
<li key={metadata.id}>
<a
target={metadata.permalink.match(/^https?:/) && '_blank'}
style={{marginLeft: metadata.indent ? 20 : 0}}
className={metadata.id === this.props.metadata.id ? 'active' : ''}
href={this.getLink(metadata)}>
{metadata.title}
</a>
</li>
)}
</ul>
</div>
)}
</div>
</div>;
}
});

View File

@ -14,7 +14,7 @@ var AlgoliaDocSearch = require('AlgoliaDocSearch');
var HeaderLinks = React.createClass({
linksInternal: [
{section: 'docs', href: 'docs/getting-started.html', text: 'Docs'},
{section: 'docs', href: 'docs/getting-started.html', text: 'Docs', target: '.nav-docs'},
{section: 'support', href: 'support.html', text: 'Support'},
{section: 'showcase', href: 'showcase.html', text: 'Showcase'},
{section: 'blog', href: 'blog/', text: 'Blog'},
@ -30,7 +30,8 @@ var HeaderLinks = React.createClass({
<li key={link.section}>
<a
href={link.href}
className={link.section === this.props.section ? 'active' : ''}>
className={link.section === this.props.section ? 'active' : ''}
data-target={link.target}>
{link.text}
</a>
</li>

View File

@ -481,6 +481,92 @@ h1:hover .hash-link, h2:hover .hash-link, h3:hover .hash-link, h4:hover .hash-li
border-bottom: 0;
}
@media screen and (max-device-width: 960px) {
.nav-docs {
position: fixed;
z-index: 90;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 53px 0 0 0;
background: #3B3738;
/* Transition these properties */
transition: opacity 0.3s, visibility 0.3s;
visibility: hidden;
opacity: 0;
}
.nav-docs-viewport {
border-top: 1px solid rgb(5, 165, 209);
height: 100%;
padding: 25px;
overflow: scroll;
-webkit-overflow-scrolling: touch;
top: -30px;
position: relative;
transition: top 0.3s;
}
/* Active state */
.nav-docs.in {
visibility: visible;
opacity: 1;
}
.nav-docs.in .nav-docs-viewport {
top: 0;
}
.nav-docs * {
-webkit-font-smoothing: antialiased;
}
.nav-docs-section + .nav-docs-section {
margin-top: 50px;
}
.nav-docs-section li {
margin: 5px 0;
}
.nav-docs-section h3,
.nav-docs-section a {
color: white;
}
.nav-docs-section h3 {
border-bottom: 1px solid white;
margin-bottom: 10px;
opacity: 0.3;
}
.nav-docs-section a {
margin-right: 25px;
font-size: 120%;
padding: 5px 0;
}
.nav-docs-section a.active {
border-bottom-style: solid;
border-bottom-width: 1px;
color: rgb(5, 165, 209);
}
}
@media screen and (min-device-width: 641px) and (max-device-width: 1024px) {
.nav-docs-section ul {
display: flex;
flex-wrap: wrap;
}
/* Display 2 columns on tablet */
.nav-docs-section li {
width: 50%;
}
}
.nav-blog li {
margin-bottom: 5px;
}
@ -999,13 +1085,13 @@ small code, li code, p code {
outline: none;
}
@media screen and (max-width: 960px) {
.nav-main {
position: static;
@media screen and (max-width: 680px) {
.container {
padding-top: 100px;
}
.container {
padding-top: 0;
.nav-docs {
padding-top: 103px;
}
}
@ -1345,15 +1431,6 @@ div[data-twttr-id] iframe {
border: none;
padding: 0;
}
.nav-docs h3 {
margin: 0;
}
.nav-docs {
float: none;
width: auto;
margin-top: -8px;
margin-bottom: 20px;
}
h1 {
font-size: 30px;
line-height: 30px;

View File

@ -7,6 +7,10 @@
document.addEventListener('DOMContentLoaded', init);
function init() {
if (isMobile()) {
document.querySelector('.nav-site-wrapper a[data-target]').addEventListener('click', toggleTargetNav);
}
var backdrop = document.querySelector('.modal-backdrop');
if (!backdrop) return;
@ -42,4 +46,18 @@
modal.classList.remove('modal-open');
}
function toggleTargetNav(event) {
var target = document.body.querySelector(event.target.getAttribute('data-target'));
if (target) {
event.preventDefault();
target.classList.toggle('in');
}
}
// Primitive mobile detection
function isMobile() {
return ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) );
}
}());