feat tour: Show new users around
This commit is contained in:
parent
7f6884cea8
commit
5b41b8d6dc
@ -11,7 +11,8 @@ module.exports = function(grunt) {
|
|||||||
options: {
|
options: {
|
||||||
alias: {
|
alias: {
|
||||||
store: './src/js/store.js',
|
store: './src/js/store.js',
|
||||||
utils: './src/js/utils.js'
|
utils: './src/js/utils.js',
|
||||||
|
tour: './src/js/tour.js'
|
||||||
},
|
},
|
||||||
transform: [['babelify', {
|
transform: [['babelify', {
|
||||||
optional: ['es7.asyncFunctions', 'asyncToGenerator',
|
optional: ['es7.asyncFunctions', 'asyncToGenerator',
|
||||||
|
@ -19,11 +19,11 @@ Please read the Features section below and issues to make sure your issue is not
|
|||||||
- [x] Directory Child Count
|
- [x] Directory Child Count
|
||||||
- [x] Actions on multiple files (selection)
|
- [x] Actions on multiple files (selection)
|
||||||
- [x] Copy and Paste/Move files
|
- [x] Copy and Paste/Move files
|
||||||
- [x] File Preview
|
- [x] File Preview (Supports PDF files!)
|
||||||
- [x] Filter Files
|
- [x] Filter Files
|
||||||
- [x] Swipe Gestures (Up directory by swiping right)
|
- [x] Swipe Gestures (Up directory by swiping right)
|
||||||
- [x] Search
|
- [x] Search
|
||||||
- [ ] Intro
|
- [x] Intro
|
||||||
- [ ] Different views (List, Icons, etc)
|
- [ ] Different views (List, Icons, etc)
|
||||||
- [ ] Share Files
|
- [ ] Share Files
|
||||||
- [ ] Preferences
|
- [ ] Preferences
|
||||||
|
@ -1,25 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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">
|
<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 -->
|
<!-- Generator: Sketch 3.3.3 (12072) - http://www.bohemiancoding.com/sketch -->
|
||||||
<title>More</title>
|
<title>More Copy</title>
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<defs>
|
<defs></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>
|
|
||||||
<g id="Components" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
<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 id="Toolbar" sketch:type="MSArtboardGroup" transform="translate(-336.000000, -14.000000)" fill="#63B0CD">
|
||||||
<g sketch:type="MSLayerGroup">
|
<g sketch:type="MSLayerGroup" id="Buttons">
|
||||||
<rect id="Box" stroke="#CDCDCD" fill="#F0F0F0" filter="url(#filter-1)" sketch:type="MSShapeGroup" x="0" y="0" width="360" height="50"></rect>
|
<g transform="translate(26.000000, 7.000000)" sketch:type="MSShapeGroup">
|
||||||
<g id="Buttons" transform="translate(26.000000, 10.000000)" fill="#63B0CD" 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>
|
||||||
<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>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.3 KiB |
160
build/main.js
160
build/main.js
@ -30706,13 +30706,13 @@ var Header = (function (_Component) {
|
|||||||
if (this.props.search) {
|
if (this.props.search) {
|
||||||
i = _react2['default'].createElement('i', { className: 'icon-cross', onClick: (0, _store.bind)((0, _actionsFilesView.search)()) });
|
i = _react2['default'].createElement('i', { className: 'icon-cross', onClick: (0, _store.bind)((0, _actionsFilesView.search)()) });
|
||||||
} else {
|
} else {
|
||||||
i = _react2['default'].createElement('i', { className: 'icon-search', onClick: (0, _store.bind)((0, _actionsDialog.show)('searchDialog')) });
|
i = _react2['default'].createElement('i', { className: 'icon-search tour-item', onClick: (0, _store.bind)((0, _actionsDialog.show)('searchDialog')) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return _react2['default'].createElement(
|
return _react2['default'].createElement(
|
||||||
'header',
|
'header',
|
||||||
null,
|
null,
|
||||||
_react2['default'].createElement('button', { className: 'drawer', onTouchStart: (0, _store.bind)((0, _actionsNavigation.toggle)()) }),
|
_react2['default'].createElement('button', { className: 'drawer tour-item', onTouchStart: (0, _store.bind)((0, _actionsNavigation.toggle)()) }),
|
||||||
_react2['default'].createElement(
|
_react2['default'].createElement(
|
||||||
'h1',
|
'h1',
|
||||||
{ className: 'regular-medium' },
|
{ className: 'regular-medium' },
|
||||||
@ -31168,6 +31168,10 @@ var _actionsMenu = require('actions/menu');
|
|||||||
|
|
||||||
var _actionsDialog = require('actions/dialog');
|
var _actionsDialog = require('actions/dialog');
|
||||||
|
|
||||||
|
var _tour = require('tour');
|
||||||
|
|
||||||
|
var _tour2 = _interopRequireDefault(_tour);
|
||||||
|
|
||||||
var _actionsChangedir = require('actions/changedir');
|
var _actionsChangedir = require('actions/changedir');
|
||||||
|
|
||||||
var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
|
var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
|
||||||
@ -31233,9 +31237,19 @@ var Root = (function (_Component) {
|
|||||||
_react2['default'].createElement(ErrorDialog, null),
|
_react2['default'].createElement(ErrorDialog, null),
|
||||||
_react2['default'].createElement(CreateDialog, null),
|
_react2['default'].createElement(CreateDialog, null),
|
||||||
_react2['default'].createElement(SearchDialog, null),
|
_react2['default'].createElement(SearchDialog, null),
|
||||||
_react2['default'].createElement(_componentsSpinner2['default'], null)
|
_react2['default'].createElement(_componentsSpinner2['default'], null),
|
||||||
|
_react2['default'].createElement(
|
||||||
|
'div',
|
||||||
|
{ className: 'tour-dialog' },
|
||||||
|
'Hello! Tap each highlighted button to get an understanding of how they work.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
key: 'componentDidMount',
|
||||||
|
value: function componentDidMount() {
|
||||||
|
(0, _tour2['default'])();
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
key: 'touchStart',
|
key: 'touchStart',
|
||||||
value: function touchStart(e) {
|
value: function touchStart(e) {
|
||||||
@ -31282,7 +31296,7 @@ var Root = (function (_Component) {
|
|||||||
exports['default'] = Root;
|
exports['default'] = Root;
|
||||||
module.exports = exports['default'];
|
module.exports = exports['default'];
|
||||||
|
|
||||||
},{"actions/changedir":217,"actions/dialog":218,"actions/menu":221,"components/breadcrumb":227,"components/dialog":228,"components/file-list":230,"components/header":232,"components/menu":233,"components/navigation":235,"components/spinner":237,"components/toolbar":238,"react":207,"react-redux":47,"store":"store"}],237:[function(require,module,exports){
|
},{"actions/changedir":217,"actions/dialog":218,"actions/menu":221,"components/breadcrumb":227,"components/dialog":228,"components/file-list":230,"components/header":232,"components/menu":233,"components/navigation":235,"components/spinner":237,"components/toolbar":238,"react":207,"react-redux":47,"store":"store","tour":"tour"}],237:[function(require,module,exports){
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
Object.defineProperty(exports, '__esModule', {
|
Object.defineProperty(exports, '__esModule', {
|
||||||
@ -31396,11 +31410,11 @@ var Toolbar = (function (_Component) {
|
|||||||
return _react2['default'].createElement(
|
return _react2['default'].createElement(
|
||||||
'div',
|
'div',
|
||||||
{ className: 'toolbar' },
|
{ className: 'toolbar' },
|
||||||
_react2['default'].createElement('button', { className: 'icon-plus', onClick: this.newFile }),
|
_react2['default'].createElement('button', { className: 'icon-plus tour-item', onClick: this.newFile }),
|
||||||
_react2['default'].createElement('button', { className: 'icon-view coming-soon', onClick: (0, _store.bind)((0, _actionsFilesView.toggle)()) }),
|
_react2['default'].createElement('button', { className: 'icon-view coming-soon', onClick: (0, _store.bind)((0, _actionsFilesView.toggle)()) }),
|
||||||
_react2['default'].createElement('button', { className: 'icon-refresh', onClick: (0, _store.bind)((0, _actionsFilesView.refresh)()) }),
|
_react2['default'].createElement('button', { className: 'icon-refresh tour-item', onClick: (0, _store.bind)((0, _actionsFilesView.refresh)()) }),
|
||||||
_react2['default'].createElement('button', { className: 'icon-select', onClick: (0, _store.bind)((0, _actionsFilesView.selectView)('toggle')) }),
|
_react2['default'].createElement('button', { className: 'icon-select tour-item', onClick: (0, _store.bind)((0, _actionsFilesView.selectView)('toggle')) }),
|
||||||
_react2['default'].createElement('button', { className: 'icon-more', onClick: this.showMore.bind(this), ref: 'more' })
|
_react2['default'].createElement('button', { className: 'icon-more tour-item', onClick: this.showMore.bind(this), ref: 'more' })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@ -32287,7 +32301,133 @@ exports['default'] = store;
|
|||||||
|
|
||||||
store.dispatch((0, _actionsChangedir2['default'])(DEFAULT.get('dir')));
|
store.dispatch((0, _actionsChangedir2['default'])(DEFAULT.get('dir')));
|
||||||
|
|
||||||
},{"./dialogs":239,"./menus":241,"actions/changedir":217,"immutable":3,"reducers/all":243,"redux":209}],"utils":[function(require,module,exports){
|
},{"./dialogs":239,"./menus":241,"actions/changedir":217,"immutable":3,"reducers/all":243,"redux":209}],"tour":[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
|
||||||
|
|
||||||
|
var 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'
|
||||||
|
};
|
||||||
|
|
||||||
|
var DIALOG_HIDE_DELAY = 2000;
|
||||||
|
|
||||||
|
exports['default'] = function () {
|
||||||
|
var tourRan = localStorage.getItem('tourRan');
|
||||||
|
var wrapper = document.querySelector('#wrapper');
|
||||||
|
var tour = document.querySelector('.tour-dialog');
|
||||||
|
|
||||||
|
var timeout = undefined;
|
||||||
|
var shown = 0;
|
||||||
|
|
||||||
|
if (!tourRan) {
|
||||||
|
var _iteratorNormalCompletion;
|
||||||
|
|
||||||
|
var _didIteratorError;
|
||||||
|
|
||||||
|
var _iteratorError;
|
||||||
|
|
||||||
|
var _iterator, _step;
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
wrapper.classList.add('tour');
|
||||||
|
|
||||||
|
var items = [].concat(_toConsumableArray(document.querySelectorAll('.tour-item'))).sort(function (a, b) {
|
||||||
|
return +a.dataset.index - +b.dataset.index;
|
||||||
|
});
|
||||||
|
|
||||||
|
var listeners = [];
|
||||||
|
|
||||||
|
_iteratorNormalCompletion = true;
|
||||||
|
_didIteratorError = false;
|
||||||
|
_iteratorError = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var _loop = function () {
|
||||||
|
var item = _step.value;
|
||||||
|
|
||||||
|
item.addEventListener('touchstart', function listener(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
clearTimeout(timeout);
|
||||||
|
listeners.push({ item: item, listener: listener });
|
||||||
|
|
||||||
|
shown++;
|
||||||
|
|
||||||
|
var firstClass = item.className.slice(0, item.className.indexOf(' '));
|
||||||
|
tour.innerHTML = MESSAGES[firstClass];
|
||||||
|
|
||||||
|
timeout = setTimeout(function () {
|
||||||
|
if (shown >= items.length) {
|
||||||
|
wrapper.classList.remove('tour');
|
||||||
|
localStorage.setItem('tourRan', 'true');
|
||||||
|
|
||||||
|
var _iteratorNormalCompletion2 = true;
|
||||||
|
var _didIteratorError2 = false;
|
||||||
|
var _iteratorError2 = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var _iterator2 = listeners[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||||
|
var _step2$value = _step2.value;
|
||||||
|
var _item = _step2$value.item;
|
||||||
|
var _listener = _step2$value.listener;
|
||||||
|
|
||||||
|
console.log(_item, _listener);
|
||||||
|
_item.removeEventListener('touchstart', _listener);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
_didIteratorError2 = true;
|
||||||
|
_iteratorError2 = err;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
|
||||||
|
_iterator2['return']();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (_didIteratorError2) {
|
||||||
|
throw _iteratorError2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, DIALOG_HIDE_DELAY);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (_iterator = items[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||||
|
_loop();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
_didIteratorError = true;
|
||||||
|
_iteratorError = err;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (!_iteratorNormalCompletion && _iterator['return']) {
|
||||||
|
_iterator['return']();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (_didIteratorError) {
|
||||||
|
throw _iteratorError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = exports['default'];
|
||||||
|
|
||||||
|
},{}],"utils":[function(require,module,exports){
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
Object.defineProperty(exports, '__esModule', {
|
Object.defineProperty(exports, '__esModule', {
|
||||||
@ -32355,4 +32495,4 @@ function humanSize(size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"actions/dialog":218,"store":"store"}]},{},[217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,"store","utils"]);
|
},{"actions/dialog":218,"store":"store"}]},{},[217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,"store","tour","utils"]);
|
||||||
|
@ -139,6 +139,63 @@ input[type='radio'] {
|
|||||||
input:checked + label::after {
|
input:checked + label::after {
|
||||||
background: #63b0cd;
|
background: #63b0cd;
|
||||||
}
|
}
|
||||||
|
#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 .tour-item {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.tour .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 .tour-dialog {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 90vw;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
background: #f8f8f8;
|
||||||
|
border-radius: 2rem;
|
||||||
|
box-shadow: 0 15px 24px 6px rgba(0, 0, 0, 0.2);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
.tour-dialog {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
.coming-soon::after {
|
.coming-soon::after {
|
||||||
content: 'soon...';
|
content: 'soon...';
|
||||||
background: #f7c59f;
|
background: #f7c59f;
|
||||||
@ -223,7 +280,7 @@ header button {
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
width: 8rem;
|
width: 8rem;
|
||||||
height: 2rem;
|
height: 4rem;
|
||||||
}
|
}
|
||||||
header button::before {
|
header button::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
Binary file not shown.
@ -1,25 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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">
|
<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 -->
|
<!-- Generator: Sketch 3.3.3 (12072) - http://www.bohemiancoding.com/sketch -->
|
||||||
<title>More</title>
|
<title>More Copy</title>
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<defs>
|
<defs></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>
|
|
||||||
<g id="Components" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
<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 id="Toolbar" sketch:type="MSArtboardGroup" transform="translate(-336.000000, -14.000000)" fill="#63B0CD">
|
||||||
<g sketch:type="MSLayerGroup">
|
<g sketch:type="MSLayerGroup" id="Buttons">
|
||||||
<rect id="Box" stroke="#CDCDCD" fill="#F0F0F0" filter="url(#filter-1)" sketch:type="MSShapeGroup" x="0" y="0" width="360" height="50"></rect>
|
<g transform="translate(26.000000, 7.000000)" sketch:type="MSShapeGroup">
|
||||||
<g id="Buttons" transform="translate(26.000000, 10.000000)" fill="#63B0CD" 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>
|
||||||
<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>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.3 KiB |
@ -13,12 +13,12 @@ export default class Header extends Component {
|
|||||||
if (this.props.search) {
|
if (this.props.search) {
|
||||||
i = <i className='icon-cross' onClick={bind(search())} />
|
i = <i className='icon-cross' onClick={bind(search())} />
|
||||||
} else {
|
} else {
|
||||||
i = <i className='icon-search' onClick={bind(show('searchDialog'))} />
|
i = <i className='icon-search tour-item' onClick={bind(show('searchDialog'))} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<button className='drawer' onTouchStart={bind(toggle())} />
|
<button className='drawer tour-item' onTouchStart={bind(toggle())} />
|
||||||
<h1 className='regular-medium'>Hawk</h1>
|
<h1 className='regular-medium'>Hawk</h1>
|
||||||
|
|
||||||
{i}
|
{i}
|
||||||
|
@ -11,6 +11,7 @@ import { connect } from 'react-redux';
|
|||||||
import { hideAll as hideAllMenus } from 'actions/menu';
|
import { hideAll as hideAllMenus } from 'actions/menu';
|
||||||
import { hideAll as hideAllDialogs} from 'actions/dialog';
|
import { hideAll as hideAllDialogs} from 'actions/dialog';
|
||||||
|
|
||||||
|
import tour from 'tour';
|
||||||
import changedir from 'actions/changedir';
|
import changedir from 'actions/changedir';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
@ -48,10 +49,18 @@ export default class Root extends Component {
|
|||||||
<SearchDialog />
|
<SearchDialog />
|
||||||
|
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
|
||||||
|
<div className='tour-dialog'>
|
||||||
|
Hello! Tap each highlighted button to get an understanding of how they work.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
tour();
|
||||||
|
}
|
||||||
|
|
||||||
touchStart(e) {
|
touchStart(e) {
|
||||||
let active = document.querySelector('.active');
|
let active = document.querySelector('.active');
|
||||||
let inside = e.target.closest('.active');
|
let inside = e.target.closest('.active');
|
||||||
|
@ -9,11 +9,11 @@ export default class Toolbar extends Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className='toolbar'>
|
<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-view coming-soon' onClick={bind(toggleView())} />
|
||||||
<button className='icon-refresh' onClick={bind(refresh())} />
|
<button className='icon-refresh tour-item' onClick={bind(refresh())} />
|
||||||
<button className='icon-select' onClick={bind(selectView('toggle'))} />
|
<button className='icon-select tour-item' onClick={bind(selectView('toggle'))} />
|
||||||
<button className='icon-more' onClick={this.showMore.bind(this)} ref='more' />
|
<button className='icon-more tour-item' onClick={this.showMore.bind(this)} ref='more' />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
56
src/js/tour.js
Normal file
56
src/js/tour.js
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@ header {
|
|||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
width: 8rem;
|
width: 8rem;
|
||||||
height: 2rem;
|
height: 4rem;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@import 'shadows';
|
@import 'shadows';
|
||||||
@import 'buttons';
|
@import 'buttons';
|
||||||
@import 'forms';
|
@import 'forms';
|
||||||
|
@import 'tour';
|
||||||
|
|
||||||
.coming-soon::after {
|
.coming-soon::after {
|
||||||
content: 'soon...';
|
content: 'soon...';
|
||||||
|
88
src/less/styles/tour.less
Normal file
88
src/less/styles/tour.less
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user