feat tour: Show new users around

This commit is contained in:
Mahdi Dibaiee
2015-09-06 17:32:50 +04:30
parent 7f6884cea8
commit 5b41b8d6dc
14 changed files with 385 additions and 55 deletions

View File

@ -1,25 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="6px" height="24px" viewBox="0 0 6 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.3.3 (12072) - http://www.bohemiancoding.com/sketch -->
<title>More</title>
<title>More Copy</title>
<desc>Created with Sketch.</desc>
<defs>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-1">
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.14 0" in="shadowBlurOuter1" type="matrix" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<defs></defs>
<g id="Components" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Toolbar" sketch:type="MSArtboardGroup" transform="translate(-336.000000, -14.000000)">
<g sketch:type="MSLayerGroup">
<rect id="Box" stroke="#CDCDCD" fill="#F0F0F0" filter="url(#filter-1)" sketch:type="MSShapeGroup" x="0" y="0" width="360" height="50"></rect>
<g id="Buttons" transform="translate(26.000000, 10.000000)" fill="#63B0CD" sketch:type="MSShapeGroup">
<path d="M313,10 C314.656854,10 316,8.65685425 316,7 C316,5.34314575 314.656854,4 313,4 C311.343146,4 310,5.34314575 310,7 C310,8.65685425 311.343146,10 313,10 Z M313,19 C314.656854,19 316,17.6568542 316,16 C316,14.3431458 314.656854,13 313,13 C311.343146,13 310,14.3431458 310,16 C310,17.6568542 311.343146,19 313,19 Z M313,28 C314.656854,28 316,26.6568542 316,25 C316,23.3431458 314.656854,22 313,22 C311.343146,22 310,23.3431458 310,25 C310,26.6568542 311.343146,28 313,28 Z" id="More"></path>
<g id="Toolbar" sketch:type="MSArtboardGroup" transform="translate(-336.000000, -14.000000)" fill="#63B0CD">
<g sketch:type="MSLayerGroup" id="Buttons">
<g transform="translate(26.000000, 7.000000)" sketch:type="MSShapeGroup">
<path d="M313,13 C314.656854,13 316,11.6568542 316,10 C316,8.34314575 314.656854,7 313,7 C311.343146,7 310,8.34314575 310,10 C310,11.6568542 311.343146,13 313,13 Z M313,22 C314.656854,22 316,20.6568542 316,19 C316,17.3431458 314.656854,16 313,16 C311.343146,16 310,17.3431458 310,19 C310,20.6568542 311.343146,22 313,22 Z M313,31 C314.656854,31 316,29.6568542 316,28 C316,26.3431458 314.656854,25 313,25 C311.343146,25 310,26.3431458 310,28 C310,29.6568542 311.343146,31 313,31 Z" id="More"></path>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -13,12 +13,12 @@ export default class Header extends Component {
if (this.props.search) {
i = <i className='icon-cross' onClick={bind(search())} />
} else {
i = <i className='icon-search' onClick={bind(show('searchDialog'))} />
i = <i className='icon-search tour-item' onClick={bind(show('searchDialog'))} />
}
return (
<header>
<button className='drawer' onTouchStart={bind(toggle())} />
<button className='drawer tour-item' onTouchStart={bind(toggle())} />
<h1 className='regular-medium'>Hawk</h1>
{i}

View File

@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import { hideAll as hideAllMenus } from 'actions/menu';
import { hideAll as hideAllDialogs} from 'actions/dialog';
import tour from 'tour';
import changedir from 'actions/changedir';
import store from 'store';
@ -48,10 +49,18 @@ export default class Root extends Component {
<SearchDialog />
<Spinner />
<div className='tour-dialog'>
Hello! Tap each highlighted button to get an understanding of how they work.
</div>
</div>
);
}
componentDidMount() {
tour();
}
touchStart(e) {
let active = document.querySelector('.active');
let inside = e.target.closest('.active');

View File

@ -9,11 +9,11 @@ export default class Toolbar extends Component {
render() {
return (
<div className='toolbar'>
<button className='icon-plus' onClick={this.newFile} />
<button className='icon-plus tour-item' onClick={this.newFile} />
<button className='icon-view coming-soon' onClick={bind(toggleView())} />
<button className='icon-refresh' onClick={bind(refresh())} />
<button className='icon-select' onClick={bind(selectView('toggle'))} />
<button className='icon-more' onClick={this.showMore.bind(this)} ref='more' />
<button className='icon-refresh tour-item' onClick={bind(refresh())} />
<button className='icon-select tour-item' onClick={bind(selectView('toggle'))} />
<button className='icon-more tour-item' onClick={this.showMore.bind(this)} ref='more' />
</div>
);
}

56
src/js/tour.js Normal file
View File

@ -0,0 +1,56 @@
const MESSAGES = {
'icon-plus': 'Create Files and Folders',
'icon-refresh': 'Refresh File List',
'icon-select': 'Select files for batch actions',
'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
'drawer': 'Extra options, tools and links are here',
'icon-search': 'Search your storage for a certain file'
}
const DIALOG_HIDE_DELAY = 2000;
export default function() {
let tourRan = localStorage.getItem('tourRan');
let wrapper = document.querySelector('#wrapper');
let tour = document.querySelector('.tour-dialog');
let timeout;
let shown = 0;
if (!tourRan) {
wrapper.classList.add('tour');
let items = [...document.querySelectorAll('.tour-item')].sort((a, b) => {
return (+a.dataset.index) - (+b.dataset.index);
});
let listeners = [];
for (let item of items) {
item.addEventListener('touchstart', function listener(e) {
e.preventDefault();
e.stopPropagation();
clearTimeout(timeout);
listeners.push({item, listener});
shown++;
let firstClass = item.className.slice(0, item.className.indexOf(' '));
tour.innerHTML = MESSAGES[firstClass];
timeout = setTimeout(() => {
if (shown >= items.length) {
wrapper.classList.remove('tour');
localStorage.setItem('tourRan', 'true');
for (let {item, listener} of listeners) {
console.log(item, listener);
item.removeEventListener('touchstart', listener);
}
}
}, DIALOG_HIDE_DELAY);
});
}
}
}

View File

@ -28,7 +28,7 @@ header {
border: none;
width: 8rem;
height: 2rem;
height: 4rem;
&::before {
content: '';

View File

@ -2,6 +2,7 @@
@import 'shadows';
@import 'buttons';
@import 'forms';
@import 'tour';
.coming-soon::after {
content: 'soon...';

88
src/less/styles/tour.less Normal file
View File

@ -0,0 +1,88 @@
#wrapper::after {
content: '';
background: rgba(0, 0, 0, 0);
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
transition: background 0.5s ease;
}
.tour {
&::after {
background: rgba(0, 0, 0, 0.55) !important;
}
.tour-item {
position: relative;
z-index: 1;
}
.tour-item::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 1rem;
height: 1rem;
margin-top: -0.5rem;
margin-left: -0.5rem;
border-radius: 50%;
background: #50E3C2;
opacity: 1;
transform: scale(0);
animation: pulse 2s ease-out infinite;
}
.tour-dialog {
display: block;
box-sizing: border-box;
width: 90vw;
text-align: center;
padding: 1rem 2rem;
position: fixed;
left: 50%;
top: 50%;
background: @light-gray;
border-radius: 2rem;
.shadow-16;
transform: translate(-50%, -50%);
z-index: 5;
}
}
.tour-dialog {
display: none;
}
@keyframes pulse {
100% {
opacity: 0;
transform: scale(5);
}
}