Compare commits
38 Commits
gh-pages
...
firefoxos-
Author | SHA1 | Date | |
---|---|---|---|
be8ae4f0b9 | |||
5c5305d243 | |||
e449402928 | |||
88f8909fcc | |||
d22e2e5527 | |||
25ff79af90 | |||
b3b2ddf4f8 | |||
cb30112c40 | |||
92b5fa2fee | |||
419b4010d1 | |||
19f6960a6d | |||
ccf24e513b | |||
a88ff826e7 | |||
11672c58f0 | |||
31a873d2bb | |||
d0c8c91250 | |||
b2b71b5d10 | |||
ce00cb25dc | |||
427cbca2dc | |||
43239b4a4c | |||
596799b6f0 | |||
19e306712f | |||
29fc832287 | |||
d52fe9f9bc | |||
6e52ca6246 | |||
66504df4cb | |||
8408fd5319 | |||
ceb8cd3b21 | |||
8a3c5de65d | |||
d925dfb082 | |||
2b51a7df09 | |||
f06d521bbf | |||
59f7991c10 | |||
336dd01dcb | |||
779d890513 | |||
d56ea95e9b | |||
49eb62ef2f | |||
1d6769d4e5 |
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
# DS_Store
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
@ -77,7 +77,7 @@ module.exports = function(grunt) {
|
||||
expand: true,
|
||||
cwd: 'src',
|
||||
dest: 'build',
|
||||
src: ['index.html', 'manifest.webapp',
|
||||
src: ['index.html', 'manifest.webapp', 'polyfill.js',
|
||||
'fonts/**', 'img/**', 'js/libs/**', 'icon/**']
|
||||
}]
|
||||
}
|
||||
@ -99,7 +99,7 @@ module.exports = function(grunt) {
|
||||
tasks: ['browserify:dev']
|
||||
},
|
||||
assets: {
|
||||
files: ['src/index.html', 'src/manifest.webapp',
|
||||
files: ['src/index.html', 'src/manifest.webapp', 'src/polyfill.js',
|
||||
'src/fonts/**', 'src/img/**', 'src/data/**'],
|
||||
tasks: ['copy']
|
||||
}
|
||||
|
29
README.md
@ -7,11 +7,31 @@ Please read the Features section below and issues to make sure your issue is not
|
||||
|
||||
Firefox OS 2.2 and up are supported. Sadly 2.0 and 1.3 miss a lot of ES6 functionalities as well as CSS3 features (flexbox, etc) which break our application.
|
||||
|
||||

|
||||
<p align='center'>
|
||||
<a href='https://github.com/mdibaiee/Hawk/raw/master/Mobile%20Portrait.png'>
|
||||
<img src='https://github.com/mdibaiee/Hawk/raw/master/Mobile%20Portrait.png' width='300px' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
#Thanks to
|
||||
# Thanks to
|
||||
|
||||
Sergio Muriel [@tfeserver](https://twitter.com/tfeserver) for testing the application and helping me fix issues. ❤️🙏
|
||||
|
||||
Mohammad Jahani [@mamal72](https://twitter.com/mamal72) for ideas, and helping in designing the [webpage](http://dibaiee.ir/Hawk)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
**Q: Why does Hawk create an `.empty` file inside new folders I create?**
|
||||
|
||||
This happens on Firefox OS devices below version 3, and that's because the API doesn't allow
|
||||
listing empty folders, in order to show you the folder, Hawk has to fake the folder to have a child.
|
||||
|
||||
|
||||
**Q: Why is Hawk slow?**
|
||||
|
||||
Hawk is much faster on Firefox OS 3.0 and up, and that's because the way old Device Storage API works,
|
||||
it's slow by nature. Nothing we can do about it, sadly.
|
||||
|
||||
Sergio Muriel [@tfeserver](https://twitter.com/tfeserver) for testing application
|
||||
|
||||
# Features
|
||||
|
||||
@ -40,7 +60,8 @@ Version 1.0
|
||||
|
||||
Version 2.0
|
||||
------------
|
||||
- [ ] Different views (List, Icons, etc)
|
||||
- [x] Different views (List, Grid)
|
||||
- [ ] Show storage usage statistics (free/used)
|
||||
- [ ] Sort Files
|
||||
- [ ] Zip / Unzip
|
||||
- [ ] Image Thumbnails
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 753 B After Width: | Height: | Size: 796 B |
BIN
build/icon/Icon-340.png
Normal file
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 58 KiB |
16
build/img/Back.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="15px" height="24px" viewBox="0 0 15 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>Back</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<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(-26.000000, -14.000000)" stroke="#63B0CD" fill="#63B0CD">
|
||||
<g sketch:type="MSLayerGroup" id="Buttons">
|
||||
<g transform="translate(26.000000, 7.000000)" sketch:type="MSShapeGroup">
|
||||
<path d="M7.57710348,12.9048675 C7.04180408,12.7876066 6.46141311,12.9389926 6.04498016,13.3554255 L-2.89216446,22.2925701 C-3.53902314,22.9394288 -3.5418885,23.9953224 -2.89240043,24.6448105 C-2.23838425,25.2988266 -1.18977849,25.2941929 -0.540160097,24.6445745 L7.22721202,16.8772024 L14.9945841,24.6445745 C15.6414428,25.2914332 16.6973364,25.2942985 17.3468245,24.6448105 C18.0008407,23.9907943 17.9962069,22.9421885 17.3465885,22.2925701 L8.40944389,13.3554255 C8.17328851,13.1192701 7.88261547,12.9689485 7.57710348,12.9048675 Z" id="Back" transform="translate(7.228579, 19.000000) rotate(-90.000000) translate(-7.228579, -19.000000) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
3
build/img/Close.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
|
||||
<path fill="#fff" d="m 17.402,14.33 4.023,-4.022 C 21.117,9.142 20.695,8.076 20.185,7.132 L 14.515,12.8 8.83,7.115 C 8.32,8.057 7.895,9.121 7.586,10.285 l 4.044,4.046 c 0.006,0.01 0.015,0.02 0.02,0.02 0.33,0.33 0.34,0.86 0.033,1.2 l 0.003,0.01 -4.1,4.1 c 0.31,1.166 0.734,2.228 1.244,3.168 l 5.686,-5.685 5.668,5.668 c 0.51,-0.94 0.933,-2.004 1.24,-3.17 l -4.08,-4.076 0.005,-0.004 c -0.31,-0.343 -0.3,-0.87 0.03,-1.2 l 0.02,-0.017 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 510 B |
3
build/img/Menu.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
|
||||
<path fill="#fff" d="M 10.993,9 H 19 C 19.415,9 19.758,8.275 19.758,7.446 19.758,6.617 19.415,6 19,6 H 10.993 C 10.578,6 10.249,6.617 10.249,7.446 10.249,8.275 10.57,9 10.99,9 z M 19,14 h -8.007 c -0.415,0 -0.744,0.625 -0.744,1.454 0,0.829 0.33,1.5 0.75,1.5 h 8 c 0.41,0 0.75,-0.67 0.75,-1.5 C 19.749,14.624 19.41,14 19,14 z m 0,8 h -8.007 c -0.415,0 -0.744,0.633 -0.744,1.462 0,0.829 0.321,1.538 0.741,1.538 H 19 c 0.415,0 0.758,-0.71 0.758,-1.538 C 19.758,22.634 19.415,22 19,22 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 579 B |
@ -1,14 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="19px" height="27px" viewBox="0 0 19 27" 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>Search</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Components" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="Header" sketch:type="MSArtboardGroup" transform="translate(-327.000000, -12.000000)" fill="#FAFAFA">
|
||||
<g id="Search" sketch:type="MSLayerGroup" transform="translate(327.000000, 12.000000)">
|
||||
<path d="M9.4369836,17.9549819 C13.9937934,17.9549819 17.6878176,14.2609577 17.6878176,9.70414786 C17.6878176,5.14733806 13.9937934,1.45331385 9.4369836,1.45331385 C4.8801738,1.45331385 1.18614959,5.14733806 1.18614959,9.70414786 C1.18614959,14.2609577 4.8801738,17.9549819 9.4369836,17.9549819 L9.4369836,17.9549819 L9.4369836,17.9549819 Z M9.4369836,14.9549819 C6.53702805,14.9549819 4.18614959,12.6041034 4.18614959,9.70414786 C4.18614959,6.80419231 6.53702805,4.45331385 9.4369836,4.45331385 C12.3369391,4.45331385 14.6878176,6.80419231 14.6878176,9.70414786 C14.6878176,12.6041034 12.3369391,14.9549819 9.4369836,14.9549819 Z M8.55898442,17.9088177 L6.03959979,24.4720391 C5.64616063,25.4969831 4.50444038,26.0120308 3.47355444,25.6163108 C2.44980588,25.2233305 1.93924273,24.0728244 2.33227833,23.0489317 L4.82760148,16.5483926 C5.91506785,17.2822068 7.18685775,17.7636774 8.55898442,17.9088177 L8.55898442,17.9088177 Z" id="Shape" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
|
||||
<path fill="#fff" d="m 24.98,21.794 -3.973,-4.248 c 0.788,-1.312 1.25,-2.847 1.25,-4.495 0,-4.79 -3.836,-8.67 -8.568,-8.67 -4.732,0 -8.57,3.88 -8.57,8.67 0,4.8 3.83,8.68 8.57,8.68 1.61,0 3.12,-0.46 4.41,-1.25 l 4.212,4.014 c 0.372,0.378 1.27,0.08 2.004,-0.66 0.734,-0.74 1.03,-1.654 0.654,-2.032 z M 13.69,19.31 c -3.414,0 -6.182,-2.803 -6.182,-6.26 0,-3.457 2.768,-6.258 6.182,-6.258 3.414,0 6.18,2.802 6.18,6.26 0,3.455 -2.766,6.258 -6.18,6.258 z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 545 B |
@ -3,14 +3,15 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hawk</title>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<link rel='stylesheet' href='style.css' />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1"/>
|
||||
<meta name="theme-color" content="#39393A"/>
|
||||
<link rel='stylesheet' href='style.css'/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id='wrapper'></div>
|
||||
|
||||
<script src='polyfill.js'></script>
|
||||
<script src='main.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
|
547
build/main.js
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"name": "Hawk",
|
||||
"description": "Keep an eye on your files with a full-featured file manager",
|
||||
"launch_path": "/index.html",
|
||||
@ -41,9 +41,6 @@
|
||||
"pick": {
|
||||
"href": "./index.html",
|
||||
"disposition": "inline",
|
||||
"filters": {
|
||||
"type": "*"
|
||||
},
|
||||
"returnValue": true
|
||||
}
|
||||
}
|
||||
|
85
build/polyfill.js
Normal file
159
build/style.css
@ -1,6 +1,12 @@
|
||||
.icon {
|
||||
display: block;
|
||||
}
|
||||
.icon-menu {
|
||||
display: block;
|
||||
background: url(/img/Menu.svg) no-repeat;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.icon-directory {
|
||||
display: block;
|
||||
background: url(/img/Directory.svg) no-repeat;
|
||||
@ -46,18 +52,20 @@
|
||||
.icon-search {
|
||||
display: block;
|
||||
background: url(/img/Search.svg) no-repeat;
|
||||
width: 19px;
|
||||
height: 27px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.icon-cross {
|
||||
display: block;
|
||||
background: url(/img/Plus.svg) no-repeat;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transform: rotate(45deg);
|
||||
background: url(/img/Close.svg) no-repeat;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.icon-cross svg * {
|
||||
fill: white;
|
||||
.icon-back {
|
||||
display: block;
|
||||
background: url(/img/Back.svg) no-repeat;
|
||||
width: 15px;
|
||||
height: 24px;
|
||||
}
|
||||
.regular-medium {
|
||||
font-weight: normal;
|
||||
@ -209,7 +217,21 @@ input:checked + label::after {
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 5;
|
||||
}
|
||||
.tour-dialog {
|
||||
.tour #skip-tour {
|
||||
font-size: 2rem;
|
||||
display: block;
|
||||
padding: 0.5rem 5rem;
|
||||
margin: 1rem auto;
|
||||
background: #b8e986;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 65%;
|
||||
transform: translate(-50%, -50%);
|
||||
box-shadow: 0 15px 24px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
.tour-dialog,
|
||||
#skip-tour {
|
||||
display: none;
|
||||
}
|
||||
@keyframes pulse {
|
||||
@ -260,34 +282,62 @@ button.coming-soon::after {
|
||||
opacity: 0.8;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
.list .file,
|
||||
.list .directory {
|
||||
flex: 1 1 100%;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.list .file p,
|
||||
.list .directory p {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
.list .file > span,
|
||||
.list .directory > span {
|
||||
font-weight: 100;
|
||||
font-size: 1.5rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.list .file i,
|
||||
.list .directory i {
|
||||
margin-right: 1.4rem;
|
||||
}
|
||||
.grid .file,
|
||||
.grid .directory {
|
||||
flex: 1 0 33.33%;
|
||||
max-width: 33.33%;
|
||||
padding: 1.4rem 0.5rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
.grid .file p,
|
||||
.grid .directory p {
|
||||
max-height: 1.5em;
|
||||
max-width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid .file span,
|
||||
.grid .directory span {
|
||||
display: none;
|
||||
}
|
||||
.file,
|
||||
.directory {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
padding: 1.4rem;
|
||||
width: 100%;
|
||||
font-weight: 200;
|
||||
font-size: 1.8rem;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.file p,
|
||||
.directory p {
|
||||
flex: 1 1;
|
||||
max-width: calc(100% - 9rem);
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.file > span,
|
||||
.directory > span {
|
||||
font-weight: 100;
|
||||
font-size: 1.5rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.file i,
|
||||
.directory i {
|
||||
margin-right: 1.4rem;
|
||||
.file:active,
|
||||
.directory:active {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.directory i {
|
||||
display: block;
|
||||
@ -313,27 +363,22 @@ header {
|
||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
header h1 {
|
||||
margin-left: -3rem;
|
||||
font-size: 2.3rem;
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
header i {
|
||||
margin-right: 16px;
|
||||
}
|
||||
header button {
|
||||
background: none;
|
||||
border: none;
|
||||
width: 8rem;
|
||||
height: 4rem;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
padding-top: 1rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
header button::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 2rem;
|
||||
height: 4px;
|
||||
margin-top: -9px;
|
||||
border-radius: 4px;
|
||||
background: #9b9b93;
|
||||
box-shadow: 0 7px 0 #9b9b93, 0 14px 0 #9b9b93;
|
||||
header button i {
|
||||
background-position: center;
|
||||
}
|
||||
.menu {
|
||||
width: 24.5rem;
|
||||
@ -367,6 +412,9 @@ header button::before {
|
||||
color: #9b9b93;
|
||||
pointer-events: none;
|
||||
}
|
||||
.menu li:active {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
@ -449,26 +497,50 @@ nav i {
|
||||
width: 100vw;
|
||||
height: 4.5rem;
|
||||
overflow-x: auto;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
font-weight: 200;
|
||||
font-size: 1.6rem;
|
||||
padding-right: 8px;
|
||||
background: #f8f8f8;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.breadcrumb span {
|
||||
white-space: nowrap;
|
||||
.breadcrumb div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.breadcrumb i {
|
||||
margin: 0 2px;
|
||||
.breadcrumb span {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 4.5rem;
|
||||
white-space: nowrap;
|
||||
padding: 0 5px 0 30px;
|
||||
background: #f0f0f0;
|
||||
filter: drop-shadow(1px 0 0 rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
.breadcrumb span:first-of-type {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.breadcrumb span::after {
|
||||
position: absolute;
|
||||
right: -46px;
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
border: 23px solid transparent;
|
||||
border-left-color: #f0f0f0;
|
||||
}
|
||||
.breadcrumb span.history {
|
||||
color: #9b9b93;
|
||||
}
|
||||
.file-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
height: calc(100vh - 14.5rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
@ -507,6 +579,7 @@ nav i {
|
||||
.dialog .foot {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.dialog .foot button {
|
||||
flex: 1;
|
||||
|
BIN
design/.DS_Store
vendored
Normal file
@ -36,7 +36,6 @@
|
||||
"grunt-contrib-copy": "^0.8.1",
|
||||
"grunt-contrib-less": "^1.0.1",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-fxos": "^0.1.2",
|
||||
"grunt-task-loader": "^0.6.0",
|
||||
"grunt-zip": "^0.17.0",
|
||||
"hammerjs": "^2.0.4",
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 753 B After Width: | Height: | Size: 796 B |
BIN
src/icon/Icon-340.png
Normal file
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 58 KiB |
16
src/img/Back.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="15px" height="24px" viewBox="0 0 15 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>Back</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<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(-26.000000, -14.000000)" stroke="#63B0CD" fill="#63B0CD">
|
||||
<g sketch:type="MSLayerGroup" id="Buttons">
|
||||
<g transform="translate(26.000000, 7.000000)" sketch:type="MSShapeGroup">
|
||||
<path d="M7.57710348,12.9048675 C7.04180408,12.7876066 6.46141311,12.9389926 6.04498016,13.3554255 L-2.89216446,22.2925701 C-3.53902314,22.9394288 -3.5418885,23.9953224 -2.89240043,24.6448105 C-2.23838425,25.2988266 -1.18977849,25.2941929 -0.540160097,24.6445745 L7.22721202,16.8772024 L14.9945841,24.6445745 C15.6414428,25.2914332 16.6973364,25.2942985 17.3468245,24.6448105 C18.0008407,23.9907943 17.9962069,22.9421885 17.3465885,22.2925701 L8.40944389,13.3554255 C8.17328851,13.1192701 7.88261547,12.9689485 7.57710348,12.9048675 Z" id="Back" transform="translate(7.228579, 19.000000) rotate(-90.000000) translate(-7.228579, -19.000000) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
3
src/img/Close.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
|
||||
<path fill="#fff" d="m 17.402,14.33 4.023,-4.022 C 21.117,9.142 20.695,8.076 20.185,7.132 L 14.515,12.8 8.83,7.115 C 8.32,8.057 7.895,9.121 7.586,10.285 l 4.044,4.046 c 0.006,0.01 0.015,0.02 0.02,0.02 0.33,0.33 0.34,0.86 0.033,1.2 l 0.003,0.01 -4.1,4.1 c 0.31,1.166 0.734,2.228 1.244,3.168 l 5.686,-5.685 5.668,5.668 c 0.51,-0.94 0.933,-2.004 1.24,-3.17 l -4.08,-4.076 0.005,-0.004 c -0.31,-0.343 -0.3,-0.87 0.03,-1.2 l 0.02,-0.017 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 510 B |
3
src/img/Menu.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
|
||||
<path fill="#fff" d="M 10.993,9 H 19 C 19.415,9 19.758,8.275 19.758,7.446 19.758,6.617 19.415,6 19,6 H 10.993 C 10.578,6 10.249,6.617 10.249,7.446 10.249,8.275 10.57,9 10.99,9 z M 19,14 h -8.007 c -0.415,0 -0.744,0.625 -0.744,1.454 0,0.829 0.33,1.5 0.75,1.5 h 8 c 0.41,0 0.75,-0.67 0.75,-1.5 C 19.749,14.624 19.41,14 19,14 z m 0,8 h -8.007 c -0.415,0 -0.744,0.633 -0.744,1.462 0,0.829 0.321,1.538 0.741,1.538 H 19 c 0.415,0 0.758,-0.71 0.758,-1.538 C 19.758,22.634 19.415,22 19,22 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 579 B |
@ -1,14 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="19px" height="27px" viewBox="0 0 19 27" 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>Search</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Components" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
||||
<g id="Header" sketch:type="MSArtboardGroup" transform="translate(-327.000000, -12.000000)" fill="#FAFAFA">
|
||||
<g id="Search" sketch:type="MSLayerGroup" transform="translate(327.000000, 12.000000)">
|
||||
<path d="M9.4369836,17.9549819 C13.9937934,17.9549819 17.6878176,14.2609577 17.6878176,9.70414786 C17.6878176,5.14733806 13.9937934,1.45331385 9.4369836,1.45331385 C4.8801738,1.45331385 1.18614959,5.14733806 1.18614959,9.70414786 C1.18614959,14.2609577 4.8801738,17.9549819 9.4369836,17.9549819 L9.4369836,17.9549819 L9.4369836,17.9549819 Z M9.4369836,14.9549819 C6.53702805,14.9549819 4.18614959,12.6041034 4.18614959,9.70414786 C4.18614959,6.80419231 6.53702805,4.45331385 9.4369836,4.45331385 C12.3369391,4.45331385 14.6878176,6.80419231 14.6878176,9.70414786 C14.6878176,12.6041034 12.3369391,14.9549819 9.4369836,14.9549819 Z M8.55898442,17.9088177 L6.03959979,24.4720391 C5.64616063,25.4969831 4.50444038,26.0120308 3.47355444,25.6163108 C2.44980588,25.2233305 1.93924273,24.0728244 2.33227833,23.0489317 L4.82760148,16.5483926 C5.91506785,17.2822068 7.18685775,17.7636774 8.55898442,17.9088177 L8.55898442,17.9088177 Z" id="Shape" sketch:type="MSShapeGroup"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
|
||||
<path fill="#fff" d="m 24.98,21.794 -3.973,-4.248 c 0.788,-1.312 1.25,-2.847 1.25,-4.495 0,-4.79 -3.836,-8.67 -8.568,-8.67 -4.732,0 -8.57,3.88 -8.57,8.67 0,4.8 3.83,8.68 8.57,8.68 1.61,0 3.12,-0.46 4.41,-1.25 l 4.212,4.014 c 0.372,0.378 1.27,0.08 2.004,-0.66 0.734,-0.74 1.03,-1.654 0.654,-2.032 z M 13.69,19.31 c -3.414,0 -6.182,-2.803 -6.182,-6.26 0,-3.457 2.768,-6.258 6.182,-6.258 3.414,0 6.18,2.802 6.18,6.26 0,3.455 -2.766,6.258 -6.18,6.258 z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 545 B |
@ -3,14 +3,15 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hawk</title>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
|
||||
<link rel='stylesheet' href='style.css' />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1"/>
|
||||
<meta name="theme-color" content="#39393A"/>
|
||||
<link rel='stylesheet' href='style.css'/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id='wrapper'></div>
|
||||
|
||||
<script src='polyfill.js'></script>
|
||||
<script src='main.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -5,5 +5,5 @@ export default function changedir(dir) {
|
||||
return {
|
||||
type: CHANGE_DIRECTORY,
|
||||
dir
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { LIST_FILES, FILES_VIEW, SELECT_VIEW, REFRESH, SEARCH } from 'actions/types';
|
||||
import { LIST_FILES, VIEW, SELECT_VIEW, REFRESH, SEARCH } from 'actions/types';
|
||||
import store from 'store';
|
||||
|
||||
export function listFiles(files) {
|
||||
@ -14,27 +14,6 @@ export function refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
export function toggle() {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'toggle'
|
||||
}
|
||||
}
|
||||
|
||||
export function details() {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'details'
|
||||
}
|
||||
}
|
||||
|
||||
export function list() {
|
||||
return {
|
||||
type: FILES_VIEW,
|
||||
view: 'list'
|
||||
}
|
||||
}
|
||||
|
||||
export function selectView(active = true) {
|
||||
return {
|
||||
type: SELECT_VIEW,
|
||||
|
@ -2,7 +2,6 @@ const TYPES = {
|
||||
CHANGE_DIRECTORY: Symbol('CHANGE_DIRECTORY'),
|
||||
|
||||
LIST_FILES: Symbol('LIST_FILES'),
|
||||
FILES_VIEW: Symbol('FILES_VIEW'),
|
||||
SELECT_VIEW: Symbol('SELECT_VIEW'),
|
||||
|
||||
NAVIGATION: Symbol('NAVIGATION'),
|
||||
|
@ -1,11 +1,18 @@
|
||||
import { type } from 'utils';
|
||||
import { type, normalize } from 'utils';
|
||||
import { refresh } from 'actions/files-view';
|
||||
import { bind } from 'store';
|
||||
|
||||
let SD_CACHE;
|
||||
export let CACHE = {};
|
||||
|
||||
localStorage.setItem('cache', '{}');
|
||||
|
||||
export function sdcard() {
|
||||
if (SD_CACHE) return SD_CACHE;
|
||||
|
||||
SD_CACHE = navigator.getDeviceStorage('sdcard');
|
||||
window.sdcard = SD_CACHE;
|
||||
|
||||
return SD_CACHE;
|
||||
}
|
||||
|
||||
@ -13,7 +20,11 @@ let ROOT_CACHE;
|
||||
export async function root() {
|
||||
if (ROOT_CACHE) return ROOT_CACHE;
|
||||
|
||||
ROOT_CACHE = await sdcard().getRoot();
|
||||
ROOT_CACHE = shimDirectory(await sdcard().getRoot());
|
||||
Object.defineProperty(ROOT_CACHE, 'name', {
|
||||
value: '',
|
||||
enumerable: true
|
||||
});
|
||||
window.root = ROOT_CACHE;
|
||||
return ROOT_CACHE;
|
||||
}
|
||||
@ -23,30 +34,39 @@ export async function getFile(dir = '/') {
|
||||
|
||||
if (dir === '/' || !dir) return parent;
|
||||
|
||||
return await parent.get(dir);
|
||||
return await parent.get(normalize(dir));
|
||||
}
|
||||
|
||||
export async function children(dir, gatherInfo) {
|
||||
let parent = await getFile(dir);
|
||||
if (CACHE[dir]) return CACHE[dir];
|
||||
|
||||
let parent = shimDirectory(await getFile(dir));
|
||||
if (!parent.path) {
|
||||
parent.path = dir.slice(0, dir.lastIndexOf('/') + 1);
|
||||
}
|
||||
let childs = await parent.getFilesAndDirectories();
|
||||
|
||||
if (gatherInfo) {
|
||||
if (gatherInfo && !window.needsShim) {
|
||||
for (let child of childs) {
|
||||
if (type(child) === 'Directory') {
|
||||
let subchildren;
|
||||
try {
|
||||
subchildren = await child.getFilesAndDirectories();
|
||||
subchildren = await shimDirectory(child).getFilesAndDirectories();
|
||||
} catch(e) {
|
||||
subchildren = [];
|
||||
}
|
||||
|
||||
child.children = subchildren.length;
|
||||
} else {
|
||||
child.path = dir + '/';
|
||||
if (typeof child.path === 'undefined') {
|
||||
child.path = dir + '/';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CACHE[dir] = childs;
|
||||
|
||||
return childs;
|
||||
}
|
||||
|
||||
@ -74,17 +94,22 @@ export async function createFile(...args) {
|
||||
export async function createDirectory(...args) {
|
||||
let parent = await root();
|
||||
|
||||
return parent.createDirectory(...args);
|
||||
return parent.createDirectory(...args).then(() => {
|
||||
if (window.needsShim) {
|
||||
return createFile(args[0] + '/.empty');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function remove(file, deep) {
|
||||
let path = normalize(file);
|
||||
let parent = await root();
|
||||
|
||||
return parent[deep ? 'removeDeep' : 'remove'](file);
|
||||
return parent[deep ? 'removeDeep' : 'remove'](path);
|
||||
}
|
||||
|
||||
export async function move(file, newPath) {
|
||||
let path = (file.path || '').replace(/^\//, ''); // remove starting slash
|
||||
let path = normalize(file.path || '');
|
||||
let oldPath = path + file.name;
|
||||
|
||||
let process = await copy(file, newPath);
|
||||
@ -92,21 +117,25 @@ export async function move(file, newPath) {
|
||||
}
|
||||
|
||||
export async function copy(file, newPath) {
|
||||
let path = (file.path || '').replace(/^\//, ''); // remove starting slash
|
||||
let oldPath = path + file.name;
|
||||
let path = normalize(file.path || '').replace(/^\//, '');
|
||||
let oldPath = normalize(path + file.name);
|
||||
|
||||
newPath = newPath.replace(/^\//, '');
|
||||
newPath = normalize(newPath);
|
||||
|
||||
let target = await getFile(oldPath);
|
||||
let parent = await root();
|
||||
|
||||
if (type(target) === 'Directory') {
|
||||
await parent.createDirectory(newPath);
|
||||
let childs = await target.getFilesAndDirectories();
|
||||
let childs = await shimDirectory(target).getFilesAndDirectories();
|
||||
|
||||
for (let child of childs) {
|
||||
if (type(child) === 'File') {
|
||||
child.path = oldPath + '/';
|
||||
Object.defineProperty(child, 'path', {
|
||||
value: oldPath + '/',
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
await copy(child, newPath + '/' + child.name);
|
||||
|
@ -3,54 +3,72 @@ import { connect } from 'react-redux';
|
||||
import changedir from 'actions/changedir';
|
||||
import { bind } from 'store';
|
||||
|
||||
// TODO: Fix history not working when clicking on sdcard
|
||||
@connect(props)
|
||||
export default class Breadcrumb extends Component {
|
||||
render() {
|
||||
let directories = this.props.cwd.split('/').filter(a => a);
|
||||
directories.unshift('sdcard');
|
||||
let els = [];
|
||||
|
||||
let els = directories.map((dir, index, arr) => {
|
||||
let path = arr.slice(1, index + 1).join('/');
|
||||
if (this.props.search) {
|
||||
console.log('search');
|
||||
els = [
|
||||
<span key='000'>Search: {this.props.search}</span>
|
||||
]
|
||||
} else {
|
||||
let directories = this.props.cwd.split('/').filter(a => a);
|
||||
let lastDirectories = this.props.lwd.split('/').filter(a => a);
|
||||
directories.unshift('sdcard');
|
||||
|
||||
return (
|
||||
<span key={index} onClick={bind(changedir(path))}>
|
||||
<i>/</i>{dir}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
let sumLength = directories.length + lastDirectories.length;
|
||||
|
||||
let lastDirectories = this.props.lwd.split('/').filter(a => a);
|
||||
if (lastDirectories.length > directories.length - 1) {
|
||||
lastDirectories.splice(0, directories.length - 1);
|
||||
|
||||
let history = lastDirectories.map((dir, index, arr) => {
|
||||
let current = directories.slice(1).concat(arr.slice(0, index + 1));
|
||||
let path = current.join('/').replace(/^\//, ''); // remove starting slash
|
||||
els = els.concat(directories.map((dir, index, arr) => {
|
||||
let path = arr.slice(1, index + 1).join('/');
|
||||
let style = { zIndex: sumLength - index };
|
||||
|
||||
return (
|
||||
<span key={directories.length + index} className='history' onClick={bind(changedir(path))}>
|
||||
<i>/</i>{dir}
|
||||
</span>
|
||||
)
|
||||
});
|
||||
<span key={index} onClick={bind(changedir(path))} style={style}>{dir}</span>
|
||||
);
|
||||
}));
|
||||
|
||||
els = els.concat(history);
|
||||
if (lastDirectories.length > directories.length - 1) {
|
||||
lastDirectories.splice(0, directories.length - 1);
|
||||
|
||||
let history = lastDirectories.map((dir, index, arr) => {
|
||||
let current = directories.slice(1).concat(arr.slice(0, index + 1));
|
||||
let path = current.join('/').replace(/^\//, ''); // remove starting slash
|
||||
let key = directories.length + index;
|
||||
let style = { zIndex: arr.length - index};
|
||||
console.log('history', dir)
|
||||
|
||||
return (
|
||||
<span key={key} className='history' onClick={bind(changedir(path))} style={style}>{dir}</span>
|
||||
)
|
||||
});
|
||||
|
||||
els = els.concat(history);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='breadcrumb'>
|
||||
<div className='breadcrumb' ref='container'>
|
||||
<div>
|
||||
{els}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
let container = React.findDOMNode(this.refs.container);
|
||||
let currents = container.querySelectorAll('span:not(.history)');
|
||||
|
||||
container.scrollLeft = currents[currents.length - 1].offsetLeft;
|
||||
}
|
||||
}
|
||||
|
||||
function props(state) {
|
||||
return {
|
||||
lwd: state.get('lwd'), // last working directory
|
||||
cwd: state.get('cwd')
|
||||
cwd: state.get('cwd'),
|
||||
search: state.get('search')
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,27 @@ export default class Dialog extends Component {
|
||||
let conditionalInput = input ? <input ref='input' /> : '';
|
||||
|
||||
let buttons = this.props.buttons.map((button, i) => {
|
||||
return <button className={button.className + ' btn'} key={i}
|
||||
onClick={button.action.bind(this)}>
|
||||
{button.text}
|
||||
</button>;
|
||||
return (
|
||||
<button className={button.className + ' btn'} key={i}
|
||||
onClick={button.action.bind(this)}>
|
||||
{button.text}
|
||||
</button>
|
||||
)
|
||||
});
|
||||
|
||||
let groupButtons = [];
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
if (i % 2 === 0) {
|
||||
groupButtons.push(
|
||||
<div className='foot' key={i / 2}>
|
||||
{buttons[i]}
|
||||
{buttons[i+1]}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let className = active ? 'dialog active' : 'dialog';
|
||||
|
||||
return (
|
||||
@ -22,10 +37,16 @@ export default class Dialog extends Component {
|
||||
|
||||
{conditionalInput}
|
||||
|
||||
<div className='foot'>
|
||||
{buttons}
|
||||
</div>
|
||||
{groupButtons}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (!this.props.value) return;
|
||||
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
input.value = this.props.value;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export default class Directory extends Component {
|
||||
|
||||
let input, label;
|
||||
if (this.props.selectView) {
|
||||
input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly />;
|
||||
input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly ref='check' />;
|
||||
label = <label htmlFor={checkId}></label>;
|
||||
}
|
||||
|
||||
@ -31,14 +31,16 @@ export default class Directory extends Component {
|
||||
|
||||
<i></i>
|
||||
<p>{this.props.name}</p>
|
||||
<span>{this.props.children} items</span>
|
||||
<span>{this.props.children ? this.props.children + ' items' : ''}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
peek() {
|
||||
if (document.querySelector('#file-menu.active')) return;
|
||||
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
|
||||
store.dispatch(changedir(file.path.slice(1) + file.name));
|
||||
store.dispatch(changedir(file.path.replace(/^\//, '') + file.name));
|
||||
}
|
||||
}
|
||||
|
@ -14,21 +14,23 @@ export default class FileList extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { files, selectView, activeFile } = this.props;
|
||||
let { files, selectView, activeFile, view } = this.props;
|
||||
activeFile = activeFile || [];
|
||||
let settings = store.getState().get('settings');
|
||||
|
||||
let els = files.map((file, index) => {
|
||||
let selected = activeFile.indexOf(file) > -1;
|
||||
if (type(file) === 'File') {
|
||||
return <File selectView={selectView} selected={selected} key={index} index={index} name={file.name} size={file.size} />;
|
||||
return <File selectView={selectView} selected={selected} key={index} index={index} name={file.name} size={file.size} type={file.type} />;
|
||||
} else {
|
||||
return <Directory selectView={selectView} selected={selected} key={index} index={index} name={file.name} children={file.children} />
|
||||
return <Directory selectView={selectView} selected={selected} key={index} index={index} name={file.name} children={file.children} type={file.type} />
|
||||
}
|
||||
});
|
||||
|
||||
let className= `file-list ${view}`;
|
||||
|
||||
return (
|
||||
<div className='file-list' ref='container'>
|
||||
<div className={className} ref='container'>
|
||||
{els}
|
||||
</div>
|
||||
);
|
||||
@ -53,7 +55,8 @@ function props(state) {
|
||||
return {
|
||||
files: state.get('files'),
|
||||
selectView: state.get('selectView'),
|
||||
activeFile: state.get('activeFile')
|
||||
activeFile: state.get('activeFile'),
|
||||
view: state.get('settings').view || 'list'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ export default class File extends Component {
|
||||
|
||||
let input, label;
|
||||
if (this.props.selectView) {
|
||||
input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly />;
|
||||
input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly ref='check' />;
|
||||
label = <label htmlFor={checkId}></label>;
|
||||
}
|
||||
|
||||
@ -37,6 +37,8 @@ export default class File extends Component {
|
||||
}
|
||||
|
||||
open(e) {
|
||||
if (document.querySelector('#file-menu.active')) return;
|
||||
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
|
||||
let name = file.type === 'application/pdf' ? 'view' : 'open';
|
||||
|
@ -11,15 +11,17 @@ export default class Header extends Component {
|
||||
let i;
|
||||
|
||||
if (this.props.search) {
|
||||
i = <i className='icon-cross' onClick={bind(search())} />
|
||||
i = <button onClick={bind(search())}><i className='icon-cross' /></button>
|
||||
} else {
|
||||
i = <i className='icon-search tour-item' onClick={bind(show('searchDialog'))} />
|
||||
i = <button onClick={bind(show('searchDialog'))}><i className='icon-search tour-item' /></button>
|
||||
}
|
||||
|
||||
return (
|
||||
<header>
|
||||
<button className='drawer tour-item' onTouchStart={bind(toggle())} />
|
||||
<h1 className='regular-medium'>Hawk</h1>
|
||||
<button className='drawer tour-item' onTouchStart={bind(toggle())}>
|
||||
<i className='icon-menu'></i>
|
||||
</button>
|
||||
<h1>Hawk</h1>
|
||||
|
||||
{i}
|
||||
</header>
|
||||
|
@ -8,15 +8,16 @@ const MENU_TOP_SPACE = 20;
|
||||
export default {
|
||||
contextMenu(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
let rect = React.findDOMNode(this.refs.container).getBoundingClientRect();
|
||||
let {x, y, width, height} = rect;
|
||||
|
||||
let left = x + width / 2 - MENU_WIDTH / 2,
|
||||
let left = window.innerWidth / 2 - MENU_WIDTH / 2,
|
||||
top = y + height / 2 + MENU_TOP_SPACE;
|
||||
|
||||
let dialogHeight = document.getElementById('fileMenu').offsetHeight;
|
||||
let dialogHeight = document.getElementById('file-menu').offsetHeight;
|
||||
|
||||
let diff = window.innerHeight - (dialogHeight + top);
|
||||
if (diff <= 0) {
|
||||
@ -27,14 +28,20 @@ export default {
|
||||
store.dispatch(active([file]));
|
||||
},
|
||||
|
||||
select() {
|
||||
select(e) {
|
||||
if (document.querySelector('#file-menu.active')) return;
|
||||
|
||||
let current = (store.getState().get('activeFile') || []).slice(0);
|
||||
let file = store.getState().get('files')[this.props.index];
|
||||
|
||||
let check = React.findDOMNode(this.refs.check);
|
||||
|
||||
if (current.indexOf(file) > -1) {
|
||||
current.splice(current.indexOf(file), 1);
|
||||
check.checked = false;
|
||||
} else {
|
||||
current.push(file)
|
||||
check.checked = true;
|
||||
}
|
||||
store.dispatch(active(current));
|
||||
}
|
||||
|
@ -10,8 +10,14 @@ export default class Navigation extends Component {
|
||||
render() {
|
||||
let { settings } = this.props;
|
||||
|
||||
let noFlex = typeof getComputedStyle(document.body)['flex-flow'] === 'undefined';
|
||||
|
||||
let style = noFlex ? {display: 'block'} : {};
|
||||
|
||||
return (
|
||||
<nav className={this.props.active ? 'active' : ''} onChange={this.onChange.bind(this)}>
|
||||
<nav className={this.props.active ? 'active' : ''}
|
||||
onChange={this.onChange.bind(this)}
|
||||
style={style}>
|
||||
<i onTouchStart={this.hide} />
|
||||
|
||||
<p>Filter</p>
|
||||
@ -34,6 +40,18 @@ export default class Navigation extends Component {
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>View</p>
|
||||
<ul>
|
||||
<li>
|
||||
<input id='view-list' name='view' data-value='list' type='radio' defaultChecked={settings.filter === 'list'} />
|
||||
<label htmlFor='view-list'>List</label>
|
||||
</li>
|
||||
<li>
|
||||
<input id='view-grid' name='view' data-value='grid' type='radio' defaultChecked={settings.filter === 'grid'} />
|
||||
<label htmlFor='view-grid'>Grid</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Tools</p>
|
||||
<ul>
|
||||
<li className='coming-soon'>
|
||||
|
@ -39,8 +39,8 @@ export default class Root extends Component {
|
||||
<FileList />
|
||||
<Toolbar />
|
||||
|
||||
<FileMenu id='fileMenu' />
|
||||
<MoreMenu id='moreMenu' />
|
||||
<FileMenu id='file-menu' />
|
||||
<MoreMenu id='more-menu' />
|
||||
|
||||
<RenameDialog />
|
||||
<DeleteDialog />
|
||||
@ -55,6 +55,7 @@ export default class Root extends Component {
|
||||
<div className='tour-dialog'>
|
||||
Hello! Tap each highlighted button to get an understanding of how they work.
|
||||
</div>
|
||||
<button id='skip-tour'>Skip</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { toggle as toggleView, refresh, selectView } from 'actions/files-view';
|
||||
import { refresh, selectView } from 'actions/files-view';
|
||||
import { show as showDialog } from 'actions/dialog';
|
||||
import { show as showMenu } from 'actions/menu';
|
||||
import settings from 'actions/settings';
|
||||
import store, { bind } from 'store';
|
||||
import { MENU_WIDTH } from './menu';
|
||||
|
||||
@ -9,8 +10,8 @@ export default class Toolbar extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className='toolbar'>
|
||||
<button className='icon-back tour-item' onClick={this.goUp} />
|
||||
<button className='icon-plus tour-item' onClick={this.newFile} />
|
||||
<button className='icon-view coming-soon' onClick={bind(toggleView())} />
|
||||
<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' />
|
||||
@ -29,6 +30,15 @@ export default class Toolbar extends Component {
|
||||
store.dispatch(showMenu('moreMenu', {style: {left, top, transform}}));
|
||||
}
|
||||
|
||||
goUp() {
|
||||
let current = store.getState().get('cwd');
|
||||
let up = current.split('/').slice(0, -1).join('/');
|
||||
|
||||
if (up === current) return;
|
||||
|
||||
store.dispatch(changedir(up));
|
||||
}
|
||||
|
||||
newFile() {
|
||||
let cwd = store.getState().get('cwd');
|
||||
let action = showDialog('createDialog', {
|
||||
@ -36,4 +46,11 @@ export default class Toolbar extends Component {
|
||||
});
|
||||
store.dispatch(action);
|
||||
}
|
||||
|
||||
toggleView() {
|
||||
let current = store.getState().get('settings').view;
|
||||
let value = current === 'list' ? 'grid' : 'list';
|
||||
|
||||
store.dispatch(settings({view: value}));
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
import React from 'react';
|
||||
import { hide, hideAll } from 'actions/dialog';
|
||||
import { hide, hideAll, show } from 'actions/dialog';
|
||||
import { rename, remove, create, active } from 'actions/file';
|
||||
import { search } from 'actions/files-view';
|
||||
import store, { bind } from 'store';
|
||||
|
||||
const INVALID_NAME = 'Please enter a valid name.';
|
||||
const INVALID_SEARCH = 'You can\'t leave the input empty';
|
||||
|
||||
export default {
|
||||
createDialog: {
|
||||
title: 'Create',
|
||||
@ -15,11 +18,20 @@ export default {
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
if (!input.value) {
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
this.props.dispatch(show('errorDialog', {description: INVALID_NAME}));
|
||||
return;
|
||||
}
|
||||
|
||||
let cwd = store.getState().get('cwd');
|
||||
let action = create(cwd + input.value);
|
||||
let path = cwd + '/' + input.value;
|
||||
let action = create(path.replace(/^\//, ''));
|
||||
this.props.dispatch(action);
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
input.value = '';
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -27,11 +39,28 @@ export default {
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
if (!input.value) {
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
this.props.dispatch(show('errorDialog', {description: INVALID_NAME}));
|
||||
return;
|
||||
}
|
||||
|
||||
let cwd = store.getState().get('cwd');
|
||||
let action = create(cwd + input.value, true);
|
||||
let path = cwd + '/' + input.value;
|
||||
let action = create(path.replace(/^\//, ''), true);
|
||||
this.props.dispatch(action);
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
input.value = '';
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Cancel',
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
this.props.dispatch(hideAll());
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -43,17 +72,29 @@ export default {
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
action: bind(hideAll())
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
this.props.dispatch(hideAll());
|
||||
input.value = '';
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Rename',
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
if (!input.value) {
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
this.props.dispatch(show('errorDialog', {description: INVALID_NAME}));
|
||||
return;
|
||||
}
|
||||
|
||||
let activeFile = store.getState().get('activeFile');
|
||||
this.props.dispatch(rename(activeFile, input.value))
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
input.value = '';
|
||||
},
|
||||
className: 'success'
|
||||
}
|
||||
@ -93,16 +134,28 @@ export default {
|
||||
buttons: [
|
||||
{
|
||||
text: 'Cancel',
|
||||
action: bind(hideAll())
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
this.props.dispatch(hideAll());
|
||||
input.value = '';
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Search',
|
||||
action() {
|
||||
let input = React.findDOMNode(this.refs.input);
|
||||
|
||||
if (!input.value) {
|
||||
this.props.dispatch(hideAll());
|
||||
this.props.dispatch(active());
|
||||
this.props.dispatch(show('errorDialog', {description: INVALID_SEARCH}));
|
||||
return;
|
||||
}
|
||||
|
||||
let action = search(input.value);
|
||||
this.props.dispatch(action);
|
||||
this.props.dispatch(hideAll());
|
||||
input.value = '';
|
||||
},
|
||||
className: 'success'
|
||||
}
|
||||
|
@ -11,10 +11,11 @@ const entryMenu = {
|
||||
action() {
|
||||
let files = store.getState().get('files');
|
||||
let active = store.getState().get('activeFile');
|
||||
const description = `Enter the new name for ${active[0].name}`;
|
||||
let name = active[0].name;
|
||||
const description = `Enter the new name for ${name}`;
|
||||
|
||||
store.dispatch(hideAll());
|
||||
store.dispatch(show('renameDialog', {description}));
|
||||
store.dispatch(show('renameDialog', {description, value: name}));
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CHANGE_DIRECTORY, REFRESH, SETTINGS } from 'actions/types';
|
||||
import { children } from 'api/files';
|
||||
import { children, CACHE } from 'api/files';
|
||||
import store from 'store';
|
||||
import { reportError } from 'utils';
|
||||
import { reportError, normalize } from 'utils';
|
||||
import { listFiles } from 'actions/files-view';
|
||||
|
||||
export default function(state = '', action) {
|
||||
@ -11,6 +11,10 @@ export default function(state = '', action) {
|
||||
return action.dir;
|
||||
}
|
||||
|
||||
if (action.type === REFRESH) {
|
||||
CACHE[state] = null;
|
||||
}
|
||||
|
||||
if (action.type === REFRESH || action.type === SETTINGS) {
|
||||
changeTo(state);
|
||||
|
||||
@ -21,7 +25,8 @@ export default function(state = '', action) {
|
||||
}
|
||||
|
||||
function changeTo(dir) {
|
||||
dir = normalize(dir);
|
||||
children(dir, true).then(files => {
|
||||
store.dispatch(listFiles(files));
|
||||
}, reportError);
|
||||
}, reportError)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { SEARCH } from 'actions/types';
|
||||
import { SEARCH, CHANGE_DIRECTORY, REFRESH } from 'actions/types';
|
||||
import store from 'store';
|
||||
import { reportError } from 'utils';
|
||||
import { listFiles } from 'actions/files-view';
|
||||
import { children } from 'api/files';
|
||||
import { type } from 'utils';
|
||||
import { type, normalize } from 'utils';
|
||||
|
||||
export default function(state = '', action) {
|
||||
if (action.type === SEARCH) {
|
||||
@ -12,6 +12,10 @@ export default function(state = '', action) {
|
||||
return action.keywords;
|
||||
}
|
||||
|
||||
if (action.type === CHANGE_DIRECTORY || action.type === REFRESH) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -28,7 +32,7 @@ function search(keywords) {
|
||||
// We don't want to show all the currently visible files from the
|
||||
// first iteration
|
||||
let once = true;
|
||||
children('/', true).then(function showResults(files) {
|
||||
children('', true).then(function showResults(files) {
|
||||
if (!store.getState().get('search')) return;
|
||||
|
||||
let current = once ? [] : store.getState().get('files');
|
||||
@ -36,7 +40,7 @@ function search(keywords) {
|
||||
|
||||
let filtered = files.filter(file => {
|
||||
if (type(file) === 'Directory') {
|
||||
let path = (file.path + file.name).replace(/^\//, '');
|
||||
let path = normalize(file.path + file.name);
|
||||
children(path, true).then(showResults, reportError);
|
||||
}
|
||||
return keys.some(key => {
|
||||
|
@ -3,7 +3,8 @@ import omit from 'lodash/object/omit';
|
||||
|
||||
const DEFAULT = {
|
||||
showHiddenFiles: false,
|
||||
showDirectoriesFirst: true
|
||||
showDirectoriesFirst: true,
|
||||
view: 'list'
|
||||
}
|
||||
|
||||
export default function(state = DEFAULT, action) {
|
||||
|
@ -3,6 +3,7 @@ const MESSAGES = {
|
||||
'icon-refresh': 'Refresh File List',
|
||||
'icon-select': 'Select files for batch actions',
|
||||
'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
|
||||
'icon-back': 'Navigate to top directory',
|
||||
'drawer': 'Extra options, tools and links are here',
|
||||
'icon-search': 'Search your storage for a certain file',
|
||||
'swipe-instruction': 'Swipe from left to right to go to parent folder'
|
||||
@ -14,30 +15,39 @@ export default function() {
|
||||
let tourRan = localStorage.getItem('tourRan');
|
||||
let wrapper = document.querySelector('#wrapper');
|
||||
let tour = document.querySelector('.tour-dialog');
|
||||
let skip = document.querySelector('#skip-tour');
|
||||
|
||||
let timeout;
|
||||
let shown = 0;
|
||||
|
||||
if (!tourRan) {
|
||||
let listeners = [];
|
||||
|
||||
wrapper.classList.add('tour');
|
||||
|
||||
skip.addEventListener('touchstart', () => {
|
||||
wrapper.classList.remove('tour');
|
||||
localStorage.setItem('tourRan', 'true');
|
||||
|
||||
for (let {item, listener} of listeners) {
|
||||
item.removeEventListener('touchstart', listener);
|
||||
}
|
||||
})
|
||||
|
||||
let items = [...document.querySelectorAll('.tour-item')].sort((a, b) => {
|
||||
return (+a.dataset.index) - (+b.dataset.index);
|
||||
});
|
||||
|
||||
let listeners = [];
|
||||
|
||||
for (let item of items) {
|
||||
|
||||
let firstClass = item.className.slice(0, item.className.indexOf(' '));
|
||||
let ev = firstClass === 'drawer' ? 'touchstart' : 'click';
|
||||
|
||||
item.addEventListener(ev, function listener(e) {
|
||||
item.addEventListener('touchstart', function listener(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
clearTimeout(timeout);
|
||||
listeners.push({item, listener, ev});
|
||||
listeners.push({item, listener});
|
||||
|
||||
shown++;
|
||||
|
||||
@ -48,8 +58,8 @@ export default function() {
|
||||
wrapper.classList.remove('tour');
|
||||
localStorage.setItem('tourRan', 'true');
|
||||
|
||||
for (let {item, listener, ev} of listeners) {
|
||||
item.removeEventListener(ev, listener);
|
||||
for (let {item, listener} of listeners) {
|
||||
item.removeEventListener('touchstart', listener);
|
||||
}
|
||||
}
|
||||
}, DIALOG_HIDE_DELAY);
|
||||
|
@ -2,7 +2,7 @@ import store from 'store';
|
||||
import { show } from 'actions/dialog';
|
||||
|
||||
export function type(obj) {
|
||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
||||
return obj.toString().slice(8, -1);
|
||||
}
|
||||
|
||||
export function template(string, props) {
|
||||
@ -26,10 +26,15 @@ export function getKey(object = store.getState().toJS(), key) {
|
||||
}
|
||||
|
||||
export function reportError(err) {
|
||||
console.error(err);
|
||||
let action = show('errorDialog', {description: err.message});
|
||||
store.dispatch(action);
|
||||
}
|
||||
|
||||
export function normalize(path) {
|
||||
return path.replace(/^\//, '').replace('sdcard/', '');
|
||||
}
|
||||
|
||||
const sizes = {
|
||||
'GB': Math.pow(2, 30),
|
||||
'MB': Math.pow(2, 20),
|
||||
@ -40,8 +45,8 @@ export function humanSize(size) {
|
||||
for (let key in sizes) {
|
||||
let value = sizes[key];
|
||||
|
||||
if (size > value) {
|
||||
return Math.round(size / value) + key;
|
||||
if (size >= value) {
|
||||
return Math.abs(Math.round(size / value)) + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
overflow-x: auto;
|
||||
|
||||
padding: 8px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
.light-medium;
|
||||
|
||||
padding-right: 8px;
|
||||
|
||||
background: @light-gray;
|
||||
|
||||
border-bottom: 1px solid @dark-transparent;
|
||||
@ -22,12 +22,43 @@
|
||||
overflow-y: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
div {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0 2px;
|
||||
span {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
height: 4.5rem;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
padding: 0 5px 0 30px;
|
||||
background: @gray;
|
||||
|
||||
filter: drop-shadow(1px 0 0 @dark-transparent);
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
right: -46px;
|
||||
top: 0;
|
||||
|
||||
content: '';
|
||||
|
||||
display: block;
|
||||
|
||||
border: 23px solid transparent;
|
||||
border-left-color: @gray;
|
||||
}
|
||||
}
|
||||
|
||||
span.history {
|
||||
|
@ -48,6 +48,8 @@
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
|
||||
|
@ -1,25 +1,11 @@
|
||||
.file, .directory {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
|
||||
padding: 1.4rem;
|
||||
|
||||
width: 100%;
|
||||
.light-big;
|
||||
|
||||
box-sizing: border-box;
|
||||
.list .file, .list .directory {
|
||||
flex: 1 1 100%;
|
||||
|
||||
border-bottom: 1px solid @dark-separator;
|
||||
|
||||
p {
|
||||
flex: 1 1;
|
||||
|
||||
max-width: ~'calc(100% - 9rem)';
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
> span {
|
||||
@ -33,6 +19,49 @@
|
||||
}
|
||||
}
|
||||
|
||||
.grid .file, .grid .directory {
|
||||
flex: 1 0 33.33%;
|
||||
|
||||
max-width: 33.33%;
|
||||
|
||||
padding: 1.4rem 0.5rem;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
p {
|
||||
max-height: 1.5em;
|
||||
max-width: 100%;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.file, .directory {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
|
||||
padding: 1.4rem;
|
||||
|
||||
.light-big;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
p {
|
||||
flex: 1 1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @gray;
|
||||
}
|
||||
}
|
||||
|
||||
.directory i {
|
||||
.icon-directory;
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
.file-list {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
|
||||
height: ~'calc(100vh - 14.5rem)';
|
||||
|
||||
overflow-x: hidden;
|
||||
|
@ -14,37 +14,25 @@ header {
|
||||
.shadow;
|
||||
|
||||
h1 {
|
||||
margin-left: -3rem;
|
||||
font-size: 2.3rem;
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
width: 8rem;
|
||||
height: 4rem;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
padding-top: 1rem;
|
||||
margin-top: -1rem;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
|
||||
width: 2rem;
|
||||
height: 4px;
|
||||
|
||||
margin-top: -9px;
|
||||
|
||||
border-radius: 4px;
|
||||
|
||||
background: @overlay;
|
||||
|
||||
box-shadow: 0 7px 0 @overlay,
|
||||
0 14px 0 @overlay;
|
||||
i {
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,5 +45,9 @@
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: @gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,12 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-menu {
|
||||
.icon;
|
||||
background: url(/img/Menu.svg) no-repeat;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.icon-directory {
|
||||
.icon;
|
||||
background: url(/img/Directory.svg) no-repeat;
|
||||
@ -54,14 +60,20 @@
|
||||
.icon-search {
|
||||
.icon;
|
||||
background: url(/img/Search.svg) no-repeat;
|
||||
width: 19px;
|
||||
height: 27px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.icon-cross {
|
||||
.icon-plus;
|
||||
transform: rotate(45deg);
|
||||
svg * {
|
||||
fill: white;
|
||||
}
|
||||
.icon;
|
||||
background: url(/img/Close.svg) no-repeat;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.icon-back {
|
||||
.icon;
|
||||
background: url(/img/Back.svg) no-repeat;
|
||||
width: 15px;
|
||||
height: 24px;
|
||||
}
|
||||
|
@ -108,9 +108,32 @@
|
||||
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
#skip-tour {
|
||||
font-size: 2rem;
|
||||
|
||||
display: block;
|
||||
|
||||
padding: 0.5rem 5rem;
|
||||
|
||||
margin: 1rem auto;
|
||||
|
||||
.btn.success;
|
||||
|
||||
position: fixed;
|
||||
|
||||
left: 50%;
|
||||
top: 65%;
|
||||
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
.shadow-16;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tour-dialog {
|
||||
.tour-dialog, #skip-tour {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"name": "Hawk",
|
||||
"description": "Keep an eye on your files with a full-featured file manager",
|
||||
"launch_path": "/index.html",
|
||||
@ -41,9 +41,6 @@
|
||||
"pick": {
|
||||
"href": "./index.html",
|
||||
"disposition": "inline",
|
||||
"filters": {
|
||||
"type": "*"
|
||||
},
|
||||
"returnValue": true
|
||||
}
|
||||
}
|
||||
|