feature(user-menu) add (#221)

This commit is contained in:
coderaiser 2019-05-05 17:40:05 +03:00
parent f60af287d3
commit eb4f7c0d7c
34 changed files with 562 additions and 41 deletions

View file

@ -1,7 +1,6 @@
'use strict';
/* global DOM */
/* global CloudCmd */
const {
Dialog,

View file

@ -0,0 +1,146 @@
'use strict';
/* global CloudCmd, gritty */
const {promisify} = require('es6-promisify');
const tryToCatch = require('try-to-catch/legacy');
require('../../css/terminal.css');
const exec = require('execon');
const load = require('load.js');
const DOM = require('../dom');
const Images = require('../dom/images');
const loadParallel = promisify(load.parallel);
const {Dialog} = DOM;
const {
Key,
config,
} = CloudCmd;
CloudCmd.TerminalRun = exports;
let Loaded;
let Terminal;
let Socket;
const loadAll = async () => {
const {prefix} = CloudCmd;
const prefixGritty = getPrefix();
const js = `${prefixGritty}/gritty.js`;
const css = `${prefix}/dist/terminal.css`;
const [e] = await tryToCatch(loadParallel, [js, css]);
if (e) {
const src = e.target.src.replace(window.location.href, '');
return Dialog.alert(`file ${src} could not be loaded`);
}
Loaded = true;
};
module.exports.init = async () => {
if (!config('terminal'))
return;
Images.show.load('top');
await CloudCmd.View();
await loadAll();
};
module.exports.show = show;
module.exports.hide = hide;
function hide () {
CloudCmd.View.hide();
}
function getPrefix() {
return CloudCmd.prefix + '/gritty';
}
function getPrefixSocket() {
return CloudCmd.prefixSocket + '/gritty';
}
function getEnv() {
return {
ACTIVE_DIR: DOM.getCurrentDirPath,
PASSIVE_DIR: DOM.getNotCurrentDirPath,
CURRENT_NAME: DOM.getCurrentName,
CURRENT_PATH: DOM.getCurrentPath,
};
}
function create(createOptions) {
const {
command,
autoClose,
closeMessage = 'Press any key to close Terminal...',
} = createOptions;
const options = {
env: getEnv(),
prefix: getPrefixSocket(),
socketPath: CloudCmd.prefix,
fontFamily: 'Droid Sans Mono',
command,
autoRestart: false,
};
let commandExit = false;
const {socket, terminal} = gritty(document.body, options);
Socket = socket;
Terminal = terminal;
Terminal.on('key', (char, {keyCode, shiftKey}) => {
if (commandExit)
hide();
if (shiftKey && keyCode === Key.ESC) {
hide();
}
});
Socket.on('exit', () => {
if (autoClose)
return hide();
terminal.write(`\n${closeMessage}`);
commandExit = true;
});
Socket.on('connect', exec.with(authCheck, socket));
}
function authCheck(spawn) {
spawn.emit('auth', config('username'), config('password'));
spawn.on('reject', () => {
Dialog.alert('Wrong credentials!');
});
}
async function show(options = {}) {
if (!Loaded)
return;
if (!config('terminal'))
return;
await create(options);
CloudCmd.View.show(Terminal.element, {
afterShow: () => {
Terminal.focus();
},
});
}

View file

@ -24,6 +24,7 @@ CloudCmd.Terminal = exports;
let Loaded;
let Terminal;
let Socket;
const loadAll = async () => {
const {prefix} = CloudCmd;
@ -84,17 +85,19 @@ function create() {
socketPath: CloudCmd.prefix,
fontFamily: 'Droid Sans Mono',
};
const {socket, terminal} = gritty(document.body, options);
Socket = socket;
Terminal = terminal;
terminal.on('key', (char, {keyCode, shiftKey}) => {
Terminal.on('key', (char, {keyCode, shiftKey}) => {
if (shiftKey && keyCode === Key.ESC) {
hide();
}
});
socket.on('connect', exec.with(authCheck, socket));
Socket.on('connect', exec.with(authCheck, socket));
}
function authCheck(spawn) {
@ -105,7 +108,7 @@ function authCheck(spawn) {
});
}
function show(callback) {
function show() {
if (!Loaded)
return;
@ -114,10 +117,7 @@ function show(callback) {
CloudCmd.View.show(Terminal.element, {
afterShow: () => {
if (Terminal)
Terminal.focus();
exec(callback);
Terminal.focus();
},
});
}

View file

@ -0,0 +1,20 @@
'use strict';
const defaultUserMenu = {
'F2 - Rename file': async ({DOM}) => {
DOM.renameCurrent();
},
};
module.exports = (menuFn) => {
if (!menuFn)
return defaultUserMenu;
const module = {};
const fn = Function('module', menuFn);
fn(module);
return module.exports;
};

View file

@ -0,0 +1,29 @@
'use strict';
const test = require('supertape');
const getUserMenu = require('./get-user-menu');
test('user-menu: getUserMenu', (t) => {
const menu = `module.exports = {
'F2 - Rename file': ({DOM}) => {
const {element} = DOM.CurrentInfo;
DOM.renameCurrent(element);
}
}`;
const result = getUserMenu(menu);
const [key] = Object.keys(result);
t.equal(key, 'F2 - Rename file', 'should equal');
t.end();
});
test('user-menu: getUserMenu: no args', (t) => {
const result = getUserMenu();
const [key] = Object.keys(result);
t.equal(key, 'F2 - Rename file', 'should equal');
t.end();
});

View file

@ -0,0 +1,112 @@
'use strict';
/* global CloudCmd, DOM */
require('../../../css/user-menu.css');
const currify = require('currify/legacy');
const {promisify} = require('es6-promisify');
const load = require('load.js');
const createElement = require('@cloudcmd/create-element');
const Images = require('../../dom/images');
const getUserMenu = require('./get-user-menu');
const loadCSS = promisify(load.css);
const Name = 'UserMenu';
CloudCmd[Name] = module.exports;
const {Key} = CloudCmd;
module.exports.init = async () => {
await Promise.all([
loadCSS(`${CloudCmd.prefix}/dist/user-menu.css`),
CloudCmd.View(),
]);
};
module.exports.show = show;
module.exports.hide = hide;
const getKey = (a) => a.split(' - ')[0];
const beginWith = (a) => (b) => !b.indexOf(a);
const {CurrentInfo} = DOM;
async function show() {
Images.show.load('top');
const {dirPath} = CurrentInfo;
const res = await fetch(`/api/v1/user-menu?dir=${dirPath}`);
const userMenu = getUserMenu(await res.text());
const options = Object.keys(userMenu);
const el = createElement('select', {
className: 'cloudcmd-user-menu',
innerHTML: fillTemplate(options),
size: 10,
});
const keys = options.map(getKey);
el.addEventListener('keydown', onKeyDown(keys, options, userMenu));
el.addEventListener('dblclick', onDblClick(options, userMenu));
const afterShow = () => el.focus();
const autoSize = true;
Images.hide();
CloudCmd.View.show(el, {
autoSize,
afterShow,
});
}
function fillTemplate(options) {
const result = [];
for (const option of options) {
result.push(`<option>${option}</option>`);
}
return result.join('');
}
function hide() {
CloudCmd.View.hide();
}
const onDblClick = currify(async (options, userMenu, e) => {
const {value} = e.target;
await runUserMenu(value, options, userMenu);
});
const onKeyDown = currify(async (keys, options, userMenu, e) => {
const {keyCode} = e;
const key = e.key.toUpperCase();
let value;
if (keyCode === Key.ENTER)
({value} = e.target);
else if (keys.includes(key))
value = options.find(beginWith(key));
else
return;
e.preventDefault();
e.stopPropagation();
await runUserMenu(value, options, userMenu);
});
const runUserMenu = async (value, options, userMenu) => {
hide();
await userMenu[value]({
DOM,
CloudCmd,
});
};