From 5d589ee31d773b9570daf815263e56708075d340 Mon Sep 17 00:00:00 2001 From: coderaiser Date: Sun, 26 Apr 2020 23:44:27 +0300 Subject: [PATCH] feature(user-menu) add ability to run selected items without showing dialog --- client/modules/user-menu/index.js | 54 ++++++++++--------- client/modules/user-menu/parse-user-menu.js | 35 ++++++++++++ .../modules/user-menu/parse-user-menu.spec.js | 32 +++++++++++ client/modules/user-menu/run.js | 8 +++ client/modules/user-menu/run.spec.js | 23 ++++++++ static/user-menu.js | 10 +++- 6 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 client/modules/user-menu/parse-user-menu.js create mode 100644 client/modules/user-menu/parse-user-menu.spec.js create mode 100644 client/modules/user-menu/run.js create mode 100644 client/modules/user-menu/run.spec.js diff --git a/client/modules/user-menu/index.js b/client/modules/user-menu/index.js index 2d93bbc2..34728161 100644 --- a/client/modules/user-menu/index.js +++ b/client/modules/user-menu/index.js @@ -17,7 +17,9 @@ const Images = require('../../dom/images'); const Dialog = require('../../dom/dialog'); const getUserMenu = require('./get-user-menu'); const navigate = require('./navigate'); -const parse = require('./parse-error'); +const parseError = require('./parse-error'); +const parseUserMenu = require('./parse-user-menu'); +const {runSelected} = require('./run'); const loadCSS = load.css; const sourceStore = fullstore(); @@ -37,10 +39,6 @@ module.exports.init = async () => { module.exports.show = show; module.exports.hide = hide; -const getKey = (a) => a.split(' - ')[0]; -const beginWith = (a) => (b) => a === getKey(b); -const notPrivate = ([a]) => a !== '_'; - const {CurrentInfo} = DOM; async function show() { @@ -58,9 +56,15 @@ async function show() { sourceStore(source); - const options = Object - .keys(userMenu) - .filter(notPrivate); + const { + names, + keys, + items, + settings, + } = parseUserMenu(userMenu); + + if (settings.run) + return runSelected(settings.select, items, runUserMenu); const button = createElement('button', { className: 'cloudcmd-user-menu-button', @@ -69,15 +73,13 @@ async function show() { const select = createElement('select', { className: 'cloudcmd-user-menu', - innerHTML: fillTemplate(options), + innerHTML: fillTemplate(names), size: 10, }); - const keys = options.map(getKey); - - button.addEventListener('click', onButtonClick(options, userMenu, select)); - select.addEventListener('keydown', onKeyDown(keys, options, userMenu)); - select.addEventListener('dblclick', onDblClick(options, userMenu)); + button.addEventListener('click', onButtonClick(items, select)); + select.addEventListener('keydown', onKeyDown(keys)); + select.addEventListener('dblclick', onDblClick(userMenu)); const afterShow = () => select.focus(); const autoSize = true; @@ -101,22 +103,22 @@ function hide() { CloudCmd.View.hide(); } -const onDblClick = currify(async (options, userMenu, e) => { +const onDblClick = currify(async (items, e) => { const {value} = e.target; - await runUserMenu(value, options, userMenu); + await runUserMenu(items[value]); }); -const onButtonClick = wraptile(async (options, userMenu, {value}) => { - await runUserMenu(value, options, userMenu); +const onButtonClick = wraptile(async (items, {value}) => { + await runUserMenu(items[value]); }); -const onKeyDown = currify(async (keys, options, userMenu, e) => { +const onKeyDown = currify(async (keys, e) => { const { keyCode, target, } = e; - const key = e.key.toUpperCase(); + const keyName = e.key.toUpperCase(); e.preventDefault(); e.stopPropagation(); @@ -127,18 +129,18 @@ const onKeyDown = currify(async (keys, options, userMenu, e) => { return hide(); else if (keyCode === Key.ENTER) ({value} = target); - else if (keys.includes(key)) - value = options.find(beginWith(key)); + else if (keys[keyName]) + value = keys[keyName]; else return navigate(target, e); - await runUserMenu(value, options, userMenu); + await runUserMenu(value); }); -const runUserMenu = async (value, options, userMenu) => { +const runUserMenu = async (fn) => { hide(); - const [error] = await tryToCatch(userMenu[value], { + const [error] = await tryToCatch(fn, { DOM, CloudCmd, tryToCatch, @@ -160,7 +162,7 @@ function getCodeFrame({error, source}) { if (!code || code === 'frame') return error.message; - const [line, column] = parse(error); + const [line, column] = parseError(error); const start = { line, column, diff --git a/client/modules/user-menu/parse-user-menu.js b/client/modules/user-menu/parse-user-menu.js new file mode 100644 index 00000000..78ce016f --- /dev/null +++ b/client/modules/user-menu/parse-user-menu.js @@ -0,0 +1,35 @@ +'use strict'; + +const {entries, assign} = Object; + +module.exports = (userMenu) => { + const names = []; + const keys = {}; + const items = {}; + const settings = {}; + + for (const [str, fn] of entries(userMenu)) { + if (str === '__settings') { + assign(settings, userMenu[str]); + continue; + } + + if (/^_/.test(str)) { + continue; + } + + names.push(str); + const [key, name] = str.split(' - '); + + keys[key] = fn; + items[name] = fn; + } + + return { + names, + keys, + items, + settings, + }; +}; + diff --git a/client/modules/user-menu/parse-user-menu.spec.js b/client/modules/user-menu/parse-user-menu.spec.js new file mode 100644 index 00000000..6273419f --- /dev/null +++ b/client/modules/user-menu/parse-user-menu.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +const test = require('supertape'); +const stub = require('@cloudcmd/stub'); +const parse = require('./parse-user-menu'); + +test('cloudcmd: user menu: parse', (t) => { + const fn = stub(); + const __settings = {}; + const result = parse({ + __settings, + 'F2 - Rename file': fn, + '_f': fn, + }); + + const keys = { + F2: fn, + }; + + const items = { + 'Rename file': fn, + }; + + const expected = { + keys, + items, + settings: __settings, + }; + + t.deepEqual(result, expected); + t.end(); +}); diff --git a/client/modules/user-menu/run.js b/client/modules/user-menu/run.js new file mode 100644 index 00000000..463396d2 --- /dev/null +++ b/client/modules/user-menu/run.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports.runSelected = async (selectedItems, items, runUserMenu) => { + for (const selected of selectedItems) { + await runUserMenu(items[selected]); + } +}; + diff --git a/client/modules/user-menu/run.spec.js b/client/modules/user-menu/run.spec.js new file mode 100644 index 00000000..e2f18b47 --- /dev/null +++ b/client/modules/user-menu/run.spec.js @@ -0,0 +1,23 @@ +'use strict'; + +const test = require('supertape'); +const stub = require('@cloudcmd/stub'); +const {runSelected} = require('./run'); + +test('cloudcmd: client: user menu: run', async (t) => { + const runUserMenu = stub(); + const fn = stub(); + const selected = [ + 'hello', + ]; + + const items = { + hello: fn, + }; + + await runSelected(selected, items, runUserMenu); + + t.ok(runUserMenu.calledWith(fn)); + t.end(); +}); + diff --git a/static/user-menu.js b/static/user-menu.js index 42acb5da..7566c80f 100644 --- a/static/user-menu.js +++ b/static/user-menu.js @@ -1,7 +1,15 @@ 'use strict'; +const RENAME_FILE= 'Rename file'; + module.exports = { - 'F2 - Rename file': async ({DOM}) => { + __settings: { + select: [ + RENAME_FILE, + ], + run: false, + }, + [`F2 - ${RENAME_FILE}`]: async ({DOM}) => { await DOM.renameCurrent(); },