Skip to content

Commit

Permalink
feat(Shortcut): Add removeAction function
Browse files Browse the repository at this point in the history
Add a function to unregister a registered Action

Closes #26
  • Loading branch information
dannysellers committed Nov 28, 2017
1 parent 70bee1e commit ae5e65b
Show file tree
Hide file tree
Showing 43 changed files with 1,773 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface ShortcutJS {
reset(): void
loadFromJson(json: any, options?: IOptions): void
addAction(action: Action): void
removeAction(action: Action): void
subscribe(actionName: string, cb: Function): void
unsubscribe(actionName: string, cb?: Function): void
processEvent(ev: KeyboardEvent): void
Expand Down Expand Up @@ -113,6 +114,18 @@ class Shortcut implements ShortcutJS {
this.actions.set(action.name, action)
}

public removeAction (action: Action) {
if (!(action instanceof Action)) {
throw new Error('You must pass an Action instance object')
}

if (!this.actions.has(action.name)) {
throw new Error(`Action ${action.name} does not exist`)
}

this.actions.delete(action.name)
}

public subscribe (actionName: string, cb: Function) {
if (this.actions.has(actionName)) {
const action = this.actions.get(actionName)
Expand Down
20 changes: 20 additions & 0 deletions test/shortcut.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ describe('shortcutJS', () => {
})
})

describe('removeAction', () => {
it('removes an action', () => {
const combo = KeyCombo.fromString('ctrl a')
const action = new Action('action', combo)
shortcutJS.addAction(action)
expect(shortcutJS.actions.size).toEqual(1)
shortcutJS.removeAction(action)
expect(shortcutJS.actions.size).toEqual(0)
})

it('throws an error if an action is not passed', () => {
expect(() => shortcutJS.removeAction(null as any)).toThrowError()
})

it('throws an error if an unregistered action is passed', () => {
const action = new Action('newaction', KeyCombo.fromString('ctrl b'))
expect(() => shortcutJS.removeAction(action)).toThrowError()
})
})

describe('subscribe', () => {
it('adds a new callback', () => {
const combo = KeyCombo.fromString('ctrl a')
Expand Down
19 changes: 19 additions & 0 deletions types/src/action.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { KeyCombo } from './key-combo';
/**
* Action class to use for the ShortcutJS
* @export
* @class Action
*/
export declare class Action {
name: string;
keyCombo: KeyCombo;
callbacks: Set<Function>;
/**
* Creates an instance of Action.
* @param {sring} name
* @param {KeyCombo} keyCombo
*/
constructor(name: string, keyCombo: KeyCombo);
addCallback(cb: Function): void;
removeCallback(cb?: Function): void;
}
50 changes: 50 additions & 0 deletions types/src/action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use strict";
/*
Copyright 2017 Alex Jover Morales (alexjovermorales@gmail.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const key_combo_1 = require("./key-combo");
/**
* Action class to use for the ShortcutJS
* @export
* @class Action
*/
class Action {
/**
* Creates an instance of Action.
* @param {sring} name
* @param {KeyCombo} keyCombo
*/
constructor(name, keyCombo) {
if (!(keyCombo instanceof key_combo_1.KeyCombo)) {
throw new Error('The second parameter (keyCombo) must be an instance of KeyCombo');
}
this.name = name;
this.keyCombo = keyCombo;
this.callbacks = new Set();
}
addCallback(cb) {
this.callbacks.add(cb);
}
removeCallback(cb = null) {
if (cb) {
this.callbacks.delete(cb);
}
else {
this.callbacks = new Set();
}
}
}
exports.Action = Action;
41 changes: 41 additions & 0 deletions types/src/event-processor.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Action } from './action';
import { KeyCombo } from './key-combo';
import { IOptions } from './options';
/**
* Process events, mantaining a temporal combo. Then compares combos to find matches.
*/
export declare class EventProcessor {
/**
* Combo composed by the Keyboard Events keys. It is cleaned on keyup.
*/
currentCombo: KeyCombo;
constructor();
/**
* Resets the current Combo
*/
reset(): void;
/**
* Process a keyboardEvent
*/
processEvent(ev: KeyboardEvent, actions: Map<string, Action>, options: IOptions): boolean;
/**
* Resets the combo and prints debug output
*/
cleanCombo(options: IOptions): void;
/**
* Search for matching actions, given a keyCombo, and execute its callbacks
*/
processActionCombos(ev: KeyboardEvent, actions: Map<string, Action>, options: IOptions): boolean;
/**
* Checks whether the currentCombo matches a particular action
*/
private matchesComboAction(action);
/**
* Prints keydown events
*/
private printDebugKeyPressed(ev);
/**
* Prints when action matches
*/
private printDebugActionFound(action);
}
97 changes: 97 additions & 0 deletions types/src/event-processor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const key_combo_1 = require("./key-combo");
const utils_1 = require("./utils");
/**
* Process events, mantaining a temporal combo. Then compares combos to find matches.
*/
class EventProcessor {
constructor() {
this.reset();
}
/**
* Resets the current Combo
*/
reset() {
this.currentCombo = new key_combo_1.KeyCombo();
}
/**
* Process a keyboardEvent
*/
processEvent(ev, actions, options) {
const wasAppended = this.currentCombo.addEvent(ev);
// Avoid repeated events
if (!wasAppended) {
return false;
}
const shouldProcess = !options.onlyStateCombos ||
options.onlyStateCombos && this.currentCombo.hasStateKeys();
if (shouldProcess) {
if (options.debug) {
this.printDebugKeyPressed(ev);
}
this.processActionCombos(ev, actions, options);
}
}
/**
* Resets the combo and prints debug output
*/
cleanCombo(options) {
this.reset();
if (options.debug) {
utils_1.logger.log('ShortcutJS: Cleaned keyMap');
}
}
/**
* Search for matching actions, given a keyCombo, and execute its callbacks
*/
processActionCombos(ev, actions, options) {
for (let action of actions.values()) {
if (this.matchesComboAction(action)) {
if (options.debug) {
this.printDebugActionFound(action);
}
if (options.preventDefault) {
ev.preventDefault();
}
for (let cb of action.callbacks) {
cb(ev);
}
// Don't continue after finding it
return false;
}
}
}
/**
* Checks whether the currentCombo matches a particular action
*/
matchesComboAction(action) {
return key_combo_1.KeyCombo.isEqual(this.currentCombo, action.keyCombo);
}
/**
* Prints keydown events
*/
printDebugKeyPressed(ev) {
utils_1.logger.group('ShortcutJS: KeyPressed');
utils_1.logger.log('Key: ', ev.keyCode);
utils_1.logger.group('Current combo:');
utils_1.logger.log('Keys: ', [...this.currentCombo.keys]);
utils_1.logger.log('State keys: ', this.currentCombo.stateKeys);
utils_1.logger.groupEnd();
utils_1.logger.groupEnd();
}
/**
* Prints when action matches
*/
printDebugActionFound(action) {
utils_1.logger.group('%cShortcutJS: Action Matched', 'color: green');
utils_1.logger.log('Action: ', action.name);
utils_1.logger.group('Current combo:');
utils_1.logger.log('Keys: ', [...this.currentCombo.keys]);
utils_1.logger.log('State keys: ', this.currentCombo.stateKeys);
utils_1.logger.groupEnd();
utils_1.logger.log(`${action.callbacks.size} callbacks found`);
utils_1.logger.groupEnd();
}
}
exports.EventProcessor = EventProcessor;
23 changes: 23 additions & 0 deletions types/src/json-parser.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ShortcutJS } from './shortcut';
/**
* Value object class representing an object from the json array loaded
* @class JsonActionCombo
*/
export declare class JsonActionCombo {
combo: any;
action: any;
constructor(obj: any);
}
/**
* Parses the json array of combo actions loaded externally
* @class JsonParser
*/
export declare class JsonParser {
/**
* Does the parsing
*
* @param {ShortcutJS} shortcutJS
* @param {any} json
*/
static parse(shortcutJS: ShortcutJS, json: any[]): void;
}
62 changes: 62 additions & 0 deletions types/src/json-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use strict";
/*
Copyright 2017 Alex Jover Morales (alexjovermorales@gmail.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const action_1 = require("./action");
const key_combo_1 = require("./key-combo");
/**
* Value object class representing an object from the json array loaded
* @class JsonActionCombo
*/
class JsonActionCombo {
constructor(obj) {
if (!obj || !obj.combo || !obj.action) {
throw new Error(`
The json provided must be an array of { combo, action } object. Example
[
{ action: 'openWindow', combo: 'ctrl a' }
]
`);
}
this.combo = obj.combo;
this.action = obj.action;
}
}
exports.JsonActionCombo = JsonActionCombo;
/**
* Parses the json array of combo actions loaded externally
* @class JsonParser
*/
class JsonParser {
/**
* Does the parsing
*
* @param {ShortcutJS} shortcutJS
* @param {any} json
*/
static parse(shortcutJS, json) {
if (!Array.isArray(json)) {
throw new Error('The json provided must be an array');
}
json.forEach(obj => {
const jsonActionCombo = new JsonActionCombo(obj);
const keyCombo = key_combo_1.KeyCombo.fromString(jsonActionCombo.combo);
const action = new action_1.Action(jsonActionCombo.action, keyCombo);
shortcutJS.addAction(action);
});
}
}
exports.JsonParser = JsonParser;
Loading

0 comments on commit ae5e65b

Please sign in to comment.