diff --git a/README.md b/README.md
index ecd85f1..3036491 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Please read the Features section below and issues to make sure your issue is not
- [x] Error dialogs
- [x] Show / Hide hidden files
- [x] Show directories first
-- [ ] Create new files and directories
+- [x] Create new files and directories
- [ ] File Size
- [ ] Directory Child Count
- [ ] File Preview
diff --git a/build/main.js b/build/main.js
index 8989d56..9c8d980 100644
--- a/build/main.js
+++ b/build/main.js
@@ -22190,16 +22190,19 @@ exports.deleteFile = deleteFile;
var _actionsTypes = require('actions/types');
-function create(path, name) {
+function create(path) {
+ var directory = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
+
return {
type: _actionsTypes.CREATE_FILE,
- path: path, name: name
+ path: path, directory: directory
};
}
-function share() {
+function share(path) {
return {
- type: _actionsTypes.SHARE_FILE
+ type: _actionsTypes.SHARE_FILE,
+ path: path
};
}
@@ -23409,6 +23412,8 @@ var _reactRedux = require('react-redux');
var _actionsMenu = require('actions/menu');
+var _actionsDialog = require('actions/dialog');
+
var _actionsChangedir = require('actions/changedir');
var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
@@ -23436,6 +23441,9 @@ var DeleteDialog = (0, _reactRedux.connect)(function (state) {
var ErrorDialog = (0, _reactRedux.connect)(function (state) {
return state.get('errorDialog');
})(_componentsDialog2['default']);
+var CreateDialog = (0, _reactRedux.connect)(function (state) {
+ return state.get('createDialog');
+})(_componentsDialog2['default']);
var Root = (function (_Component) {
_inherits(Root, _Component);
@@ -23461,7 +23469,8 @@ var Root = (function (_Component) {
_react2['default'].createElement(DirectoryMenu, null),
_react2['default'].createElement(RenameDialog, null),
_react2['default'].createElement(DeleteDialog, null),
- _react2['default'].createElement(ErrorDialog, null)
+ _react2['default'].createElement(ErrorDialog, null),
+ _react2['default'].createElement(CreateDialog, null)
);
}
}, {
@@ -23469,6 +23478,7 @@ var Root = (function (_Component) {
value: function touchStart(e) {
if (!e.target.closest('.menu')) {
_store2['default'].dispatch((0, _actionsMenu.hideAll)());
+ _store2['default'].dispatch((0, _actionsDialog.hideAll)());
}
}
}]);
@@ -23479,7 +23489,7 @@ var Root = (function (_Component) {
exports['default'] = Root;
module.exports = exports['default'];
-},{"actions/changedir":215,"actions/menu":220,"components/breadcrumb":225,"components/dialog":226,"components/file-list":228,"components/header":230,"components/menu":231,"components/navigation":232,"components/toolbar":234,"react":205,"react-redux":45,"store":"store"}],234:[function(require,module,exports){
+},{"actions/changedir":215,"actions/dialog":216,"actions/menu":220,"components/breadcrumb":225,"components/dialog":226,"components/file-list":228,"components/header":230,"components/menu":231,"components/navigation":232,"components/toolbar":234,"react":205,"react-redux":45,"store":"store"}],234:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -23500,12 +23510,14 @@ var _react = require('react');
var _react2 = _interopRequireDefault(_react);
-var _actionsFile = require('actions/file');
-
var _actionsFilesView = require('actions/files-view');
+var _actionsDialog = require('actions/dialog');
+
var _store = require('store');
+var _store2 = _interopRequireDefault(_store);
+
var Toolbar = (function (_Component) {
_inherits(Toolbar, _Component);
@@ -23524,7 +23536,7 @@ var Toolbar = (function (_Component) {
_react2['default'].createElement('button', { className: 'icon-plus', onClick: this.newFile }),
_react2['default'].createElement('button', { className: 'icon-view', 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-share', onClick: (0, _store.bind)((0, _actionsFile.share)()) }),
+ _react2['default'].createElement('button', { className: 'icon-share', onClick: this.share }),
_react2['default'].createElement('button', { className: 'icon-more', onClick: this.showMore })
);
}
@@ -23533,7 +23545,13 @@ var Toolbar = (function (_Component) {
value: function showMore() {}
}, {
key: 'newFile',
- value: function newFile() {}
+ value: function newFile() {
+ var cwd = _store2['default'].getState().get('cwd');
+ var action = (0, _actionsDialog.show)('createDialog', {
+ description: 'Enter a name for the new file to be created in ' + cwd
+ });
+ _store2['default'].dispatch(action);
+ }
}]);
return Toolbar;
@@ -23542,7 +23560,7 @@ var Toolbar = (function (_Component) {
exports['default'] = Toolbar;
module.exports = exports['default'];
-},{"actions/file":217,"actions/files-view":218,"react":205,"store":"store"}],235:[function(require,module,exports){
+},{"actions/dialog":216,"actions/files-view":218,"react":205,"store":"store"}],235:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -23564,9 +23582,35 @@ var _store = require('store');
var _store2 = _interopRequireDefault(_store);
exports['default'] = {
+ createDialog: {
+ title: 'Create',
+ description: 'Enter a name for the new file',
+ input: true,
+ buttons: [{
+ text: 'File',
+ action: function action() {
+ var input = _react2['default'].findDOMNode(this.refs.input);
+
+ var cwd = _store2['default'].getState().get('cwd');
+ var action = (0, _actionsFile.create)(cwd + input.value);
+ this.props.dispatch(action);
+ this.props.dispatch((0, _actionsDialog.hideAll)());
+ }
+ }, {
+ text: 'Directory',
+ action: function action() {
+ var input = _react2['default'].findDOMNode(this.refs.input);
+
+ var cwd = _store2['default'].getState().get('cwd');
+ var action = (0, _actionsFile.create)(cwd + input.value, true);
+ this.props.dispatch(action);
+ this.props.dispatch((0, _actionsDialog.hideAll)());
+ }
+ }]
+ },
renameDialog: {
title: 'Rename',
- description: 'Enter your desired new name',
+ description: 'Enter the new name',
input: true,
buttons: [{
text: 'Cancel',
@@ -23658,8 +23702,13 @@ var entryMenu = {
items: [{
name: 'Rename',
action: function action() {
+ var files = _store2['default'].getState().get('files');
+ var active = _store2['default'].getState().get('activeFile');
+ var name = files[active].name;
+ var description = 'Are you sure you want to remove ' + name + '?';
+
_store2['default'].dispatch((0, _actionsMenu.hideAll)());
- _store2['default'].dispatch((0, _actionsDialog.show)('renameDialog'));
+ _store2['default'].dispatch((0, _actionsDialog.show)('renameDialog', { description: description }));
}
}, {
name: 'Delete',
@@ -23667,9 +23716,9 @@ var entryMenu = {
var files = _store2['default'].getState().get('files');
var active = _store2['default'].getState().get('activeFile');
var name = files[active].name;
- var MSG = 'Are you sure you want to remove ' + name + '?';
+ var description = 'Are you sure you want to remove ' + name + '?';
_store2['default'].dispatch((0, _actionsMenu.hideAll)());
- _store2['default'].dispatch((0, _actionsDialog.show)('deleteDialog', { description: MSG }));
+ _store2['default'].dispatch((0, _actionsDialog.show)('deleteDialog', { description: description }));
}
}]
};
@@ -23761,7 +23810,8 @@ exports['default'] = function (state, action) {
directoryMenu: (0, _menu2['default'])(state, action, 'directoryMenu'),
renameDialog: (0, _dialog2['default'])(state, action, 'renameDialog'),
deleteDialog: (0, _dialog2['default'])(state, action, 'deleteDialog'),
- errorDialog: (0, _dialog2['default'])(state, action, 'errorDialog')
+ errorDialog: (0, _dialog2['default'])(state, action, 'errorDialog'),
+ createDialog: (0, _dialog2['default'])(state, action, 'createDialog')
});
};
@@ -23873,6 +23923,10 @@ var _store = require('store');
var _store2 = _interopRequireDefault(_store);
+var _utils = require('utils');
+
+var boundRefresh = (0, _store.bind)((0, _actionsFilesView.refresh)());
+
exports['default'] = function (state, action) {
if (state === undefined) state = [];
@@ -23880,13 +23934,17 @@ exports['default'] = function (state, action) {
return action.files;
}
+ if (action.type === _actionsTypes.CREATE_FILE) {
+ var fn = action.directory ? _apiFiles.createDirectory : _apiFiles.createFile;
+ fn(action.path).then(boundRefresh, _utils.reportError);
+
+ return state;
+ }
+
if (action.type === _actionsTypes.RENAME_FILE) {
var file = state[action.file];
- (0, _apiFiles.move)(file, (file.path || '') + action.name).then(_actionsFilesView.refresh, function (err) {
- var action = (0, _actionsDialog.show)('errorDialog', { description: err.message });
- _store2['default'].dispatch(action);
- });
+ (0, _apiFiles.move)(file, (file.path || '') + action.name).then(boundRefresh, _utils.reportError);
return state;
}
@@ -23905,7 +23963,7 @@ exports['default'] = function (state, action) {
module.exports = exports['default'];
-},{"actions/dialog":216,"actions/files-view":218,"actions/types":223,"api/files":224,"store":"store"}],243:[function(require,module,exports){
+},{"actions/dialog":216,"actions/files-view":218,"actions/types":223,"api/files":224,"store":"store","utils":"utils"}],243:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -29004,6 +29062,7 @@ Object.defineProperty(exports, '__esModule', {
exports.type = type;
exports.template = template;
exports.getKey = getKey;
+exports.reportError = reportError;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
@@ -29011,6 +29070,8 @@ var _store = require('store');
var _store2 = _interopRequireDefault(_store);
+var _actionsDialog = require('actions/dialog');
+
function type(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
}
@@ -29037,4 +29098,9 @@ function getKey(object, key) {
return parent;
}
-},{"store":"store"}]},{},[215,216,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,"store","utils"]);
+function reportError(err) {
+ var action = (0, _actionsDialog.show)('errorDialog', { description: err.message });
+ _store2['default'].dispatch(action);
+}
+
+},{"actions/dialog":216,"store":"store"}]},{},[215,216,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,"store","utils"]);
diff --git a/build/style.css b/build/style.css
index 66bc42d..3281722 100644
--- a/build/style.css
+++ b/build/style.css
@@ -69,8 +69,11 @@
.thin-small {
font-size: 1.4rem;
}
-.shadow-bottom {
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+.shadow-16 {
+ box-shadow: 0 15px 24px 6px rgba(0, 0, 0, 0.2);
+}
+.shadow-8 {
+ box-shadow: 0 8px 16px 3px rgba(0, 0, 0, 0.2);
}
.shadow {
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
@@ -186,7 +189,7 @@ header button::before {
pointer-events: none;
opacity: 0;
transition: opacity 0.5s ease;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 8px 16px 3px rgba(0, 0, 0, 0.2);
}
.menu.active {
opacity: 1;
@@ -214,7 +217,7 @@ nav {
height: 100vh;
background: #39393a;
color: white;
- box-shadow: 1px 0 5px rgba(0, 0, 0, 0.2);
+ box-shadow: 3px 0 16px 5px rgba(0, 0, 0, 0.2);
z-index: 6;
transition: left 0.5s ease;
}
@@ -328,7 +331,7 @@ nav i {
height: auto;
padding: 1.5rem 1.7rem;
background: white;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 15px 24px 6px rgba(0, 0, 0, 0.2);
z-index: 3;
transition: opacity 0.5s ease;
opacity: 0;
diff --git a/src/js/actions/file.js b/src/js/actions/file.js
index 6c995ea..4b899e9 100644
--- a/src/js/actions/file.js
+++ b/src/js/actions/file.js
@@ -1,15 +1,16 @@
import { CREATE_FILE, SHARE_FILE, RENAME_FILE, ACTIVE_FILE, DELETE_FILE } from 'actions/types';
-export function create(path, name) {
+export function create(path, directory = false) {
return {
type: CREATE_FILE,
- path, name
+ path, directory
}
}
-export function share() {
+export function share(path) {
return {
- type: SHARE_FILE
+ type: SHARE_FILE,
+ path
}
}
diff --git a/src/js/components/root.js b/src/js/components/root.js
index 847ca16..8121471 100644
--- a/src/js/components/root.js
+++ b/src/js/components/root.js
@@ -7,7 +7,8 @@ import Toolbar from 'components/toolbar';
import Menu from 'components/menu';
import Dialog from 'components/dialog';
import { connect } from 'react-redux';
-import { hideAll } from 'actions/menu';
+import { hideAll as hideAllMenus } from 'actions/menu';
+import { hideAll as hideAllDialogs} from 'actions/dialog';
import changedir from 'actions/changedir';
import store from 'store';
@@ -21,6 +22,7 @@ let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
let RenameDialog = connect(state => state.get('renameDialog'))(Dialog);
let DeleteDialog = connect(state => state.get('deleteDialog'))(Dialog);
let ErrorDialog = connect(state => state.get('errorDialog'))(Dialog);
+let CreateDialog = connect(state => state.get('createDialog'))(Dialog);
export default class Root extends Component {
render() {
@@ -38,13 +40,15 @@ export default class Root extends Component {
+
);
}
touchStart(e) {
if (!e.target.closest('.menu')) {
- store.dispatch(hideAll());
+ store.dispatch(hideAllMenus());
+ store.dispatch(hideAllDialogs());
}
}
}
diff --git a/src/js/components/toolbar.js b/src/js/components/toolbar.js
index 97116a9..31340f9 100644
--- a/src/js/components/toolbar.js
+++ b/src/js/components/toolbar.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
-import { create, share } from 'actions/file';
import { toggle as toggleView, refresh } from 'actions/files-view';
-import { bind } from 'store';
+import { show as showDialog } from 'actions/dialog';
+import store, { bind } from 'store';
export default class Toolbar extends Component {
render() {
@@ -10,7 +10,7 @@ export default class Toolbar extends Component {
-
+
);
@@ -21,6 +21,10 @@ export default class Toolbar extends Component {
}
newFile() {
-
+ let cwd = store.getState().get('cwd');
+ let action = showDialog('createDialog', {
+ description: `Enter a name for the new file to be created in ${cwd}`
+ });
+ store.dispatch(action);
}
}
diff --git a/src/js/dialogs.js b/src/js/dialogs.js
index bebcee1..a4d1b2a 100644
--- a/src/js/dialogs.js
+++ b/src/js/dialogs.js
@@ -1,12 +1,41 @@
import React from 'react';
import { hide, hideAll } from 'actions/dialog';
-import { rename, deleteFile } from 'actions/file';
+import { rename, deleteFile, create } from 'actions/file';
import store, { bind } from 'store';
export default {
+ createDialog: {
+ title: 'Create',
+ description: 'Enter a name for the new file',
+ input: true,
+ buttons: [
+ {
+ text: 'File',
+ action() {
+ let input = React.findDOMNode(this.refs.input);
+
+ let cwd = store.getState().get('cwd');
+ let action = create(cwd + input.value);
+ this.props.dispatch(action);
+ this.props.dispatch(hideAll());
+ }
+ },
+ {
+ text: 'Directory',
+ action() {
+ let input = React.findDOMNode(this.refs.input);
+
+ let cwd = store.getState().get('cwd');
+ let action = create(cwd + input.value, true);
+ this.props.dispatch(action);
+ this.props.dispatch(hideAll());
+ }
+ }
+ ]
+ },
renameDialog: {
title: 'Rename',
- description: 'Enter your desired new name',
+ description: 'Enter the new name',
input: true,
buttons: [
{
diff --git a/src/js/menus.js b/src/js/menus.js
index b6c1baf..af12900 100644
--- a/src/js/menus.js
+++ b/src/js/menus.js
@@ -7,8 +7,13 @@ const entryMenu = {
{
name: 'Rename',
action() {
+ let files = store.getState().get('files');
+ let active = store.getState().get('activeFile');
+ const name = files[active].name;
+ const description = `Are you sure you want to remove ${name}?`;
+
store.dispatch(hideAll());
- store.dispatch(show('renameDialog'));
+ store.dispatch(show('renameDialog', {description}));
}
},
{
@@ -17,9 +22,9 @@ const entryMenu = {
let files = store.getState().get('files');
let active = store.getState().get('activeFile');
const name = files[active].name;
- const MSG = `Are you sure you want to remove ${name}?`;
+ const description = `Are you sure you want to remove ${name}?`;
store.dispatch(hideAll());
- store.dispatch(show('deleteDialog', {description: MSG}));
+ store.dispatch(show('deleteDialog', {description}));
}
}
]
diff --git a/src/js/reducers/all.js b/src/js/reducers/all.js
index 3105e3d..41397a1 100644
--- a/src/js/reducers/all.js
+++ b/src/js/reducers/all.js
@@ -21,6 +21,7 @@ export default function(state = new Immutable.Map(), action) {
directoryMenu: menu(state, action, 'directoryMenu'),
renameDialog: dialog(state, action, 'renameDialog'),
deleteDialog: dialog(state, action, 'deleteDialog'),
- errorDialog: dialog(state, action, 'errorDialog')
+ errorDialog: dialog(state, action, 'errorDialog'),
+ createDialog: dialog(state, action, 'createDialog')
});
}
diff --git a/src/js/reducers/files.js b/src/js/reducers/files.js
index fed50df..64f89e2 100644
--- a/src/js/reducers/files.js
+++ b/src/js/reducers/files.js
@@ -1,21 +1,28 @@
-import { LIST_FILES, RENAME_FILE, DELETE_FILE } from 'actions/types';
+import { LIST_FILES, RENAME_FILE, DELETE_FILE, CREATE_FILE } from 'actions/types';
import { refresh } from 'actions/files-view';
-import { move, sdcard } from 'api/files';
+import { move, sdcard, createFile, createDirectory } from 'api/files';
import { show } from 'actions/dialog';
-import store from 'store';
+import store, { bind } from 'store';
+import { reportError } from 'utils';
+
+let boundRefresh = bind(refresh());
export default function(state = [], action) {
if (action.type === LIST_FILES) {
return action.files;
}
+ if (action.type === CREATE_FILE) {
+ let fn = action.directory ? createDirectory : createFile;
+ fn(action.path).then(boundRefresh, reportError);
+
+ return state;
+ }
+
if (action.type === RENAME_FILE) {
let file = state[action.file];
- move(file, (file.path || '') + action.name).then(refresh, err => {
- let action = show('errorDialog', {description: err.message});
- store.dispatch(action);
- });
+ move(file, (file.path || '') + action.name).then(boundRefresh, reportError);
return state;
}
diff --git a/src/js/utils.js b/src/js/utils.js
index 4c6a9d0..3c36051 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -1,4 +1,5 @@
import store from 'store';
+import { show } from 'actions/dialog';
export function type(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
@@ -23,3 +24,8 @@ export function getKey(object = store.getState().toJS(), key) {
return parent;
}
+
+export function reportError(err) {
+ let action = show('errorDialog', {description: err.message});
+ store.dispatch(action);
+}
diff --git a/src/less/components/dialog.less b/src/less/components/dialog.less
index 2c3222f..b5d63e9 100644
--- a/src/less/components/dialog.less
+++ b/src/less/components/dialog.less
@@ -16,7 +16,7 @@
background: white;
- .shadow-bottom;
+ .shadow-16;
z-index: 3;
diff --git a/src/less/components/menu.less b/src/less/components/menu.less
index f8d690c..4e4a5dc 100644
--- a/src/less/components/menu.less
+++ b/src/less/components/menu.less
@@ -15,7 +15,7 @@
transition: opacity 0.5s ease;
- .shadow-bottom;
+ .shadow-8;
&.active {
opacity: 1;
diff --git a/src/less/components/navigation.less b/src/less/components/navigation.less
index 5309bac..29d8e5b 100644
--- a/src/less/components/navigation.less
+++ b/src/less/components/navigation.less
@@ -12,7 +12,7 @@ nav {
background: @dark;
color: white;
- box-shadow: 1px 0 5px @dark-transparent;
+ box-shadow: 3px 0 16px 5px @dark-transparent;
z-index: 6;
transition: left 0.5s ease;
diff --git a/src/less/styles/shadows.less b/src/less/styles/shadows.less
index 94b7f28..d542eb9 100644
--- a/src/less/styles/shadows.less
+++ b/src/less/styles/shadows.less
@@ -1,5 +1,9 @@
-.shadow-bottom {
- box-shadow: 0 1px 2px @dark-transparent;
+.shadow-16 {
+ box-shadow: 0 15px 24px 6px @dark-transparent;
+}
+
+.shadow-8 {
+ box-shadow: 0 8px 16px 3px @dark-transparent;
}
.shadow {