ui: Expands create-listeners API to allow recursive and functions (#5616)

1. Adds a Listeners class, which lets us...
2. Add Listeners recursively. So you can
createListeners().add(createListeners())
3. Also add the ability to `.add` as a plain function

This moves the entire idea more towards a generic teardown utility
This commit is contained in:
John Cowen 2019-05-15 14:21:19 +01:00 committed by John Cowen
parent 4ed5515fca
commit eccff12a9b
2 changed files with 61 additions and 29 deletions

View File

@ -1,32 +1,40 @@
export default function(listeners = []) {
const add = function(target, event, handler) {
let addEventListener = 'addEventListener';
let removeEventListener = 'removeEventListener';
if (typeof target[addEventListener] === 'undefined') {
addEventListener = 'on';
removeEventListener = 'off';
class Listeners {
constructor(listeners = []) {
this.listeners = listeners;
}
add(target, event, handler) {
let remove;
if (typeof target === 'function') {
remove = target;
} else if (target instanceof Listeners) {
remove = target.remove.bind(target);
} else {
let addEventListener = 'addEventListener';
let removeEventListener = 'removeEventListener';
if (typeof target[addEventListener] === 'undefined') {
addEventListener = 'on';
removeEventListener = 'off';
}
target[addEventListener](event, handler);
remove = function() {
target[removeEventListener](event, handler);
return handler;
};
}
target[addEventListener](event, handler);
const remove = function() {
target[removeEventListener](event, handler);
return handler;
this.listeners.push(remove);
return () => {
const pos = this.listeners.findIndex(function(item) {
return item === remove;
});
return this.listeners.splice(pos, 1)[0]();
};
listeners.push(remove);
return remove;
};
// TODO: Allow passing of a 'listener remove' in here
// call it, find in the array and remove
// Post-thoughts, pretty sure this is covered now by returning the remove
// function above, use-case for wanting to use this method to remove individual
// listeners is probably pretty limited, this method itself could be easily implemented
// from the outside also, but I suppose its handy to keep here
const remove = function() {
const handlers = listeners.map(item => item());
listeners.splice(0, listeners.length);
}
remove() {
const handlers = this.listeners.map(item => item());
this.listeners.splice(0, this.listeners.length);
return handlers;
};
return {
add: add,
remove: remove,
};
}
}
export default function(listeners = []) {
return new Listeners(listeners);
}

View File

@ -9,7 +9,7 @@ test('it has add and remove methods', function(assert) {
assert.ok(typeof listeners.add === 'function');
assert.ok(typeof listeners.remove === 'function');
});
test('add returns an remove function', function(assert) {
test('add returns a remove function', function(assert) {
const listeners = createListeners();
const remove = listeners.add({
addEventListener: function() {},
@ -64,6 +64,30 @@ test('listeners are removed on remove', function(assert) {
assert.ok(stub.calledOnce);
assert.ok(stub.calledWith(name, handler));
});
test('listeners as functions are removed on remove', function(assert) {
const listeners = createListeners();
const stub = this.stub();
const remove = listeners.add(stub);
remove();
assert.ok(stub.calledOnce);
});
test('listeners as other listeners are removed on remove', function(assert) {
const listeners = createListeners();
const listeners2 = createListeners();
const stub = this.stub();
listeners2.add(stub);
const remove = listeners.add(listeners2);
remove();
assert.ok(stub.calledOnce);
});
test('listeners as functions of other listeners are removed on remove', function(assert) {
const listeners = createListeners();
const listeners2 = createListeners();
const stub = this.stub();
const remove = listeners.add(listeners2.add(stub));
remove();
assert.ok(stub.calledOnce);
});
test('remove returns the original handler', function(assert) {
const listeners = createListeners();
const target = {