2018-10-19 15:17:02 +00:00
|
|
|
import Service from '@ember/service';
|
|
|
|
import { getOwner } from '@ember/application';
|
|
|
|
import { get } from '@ember/object';
|
|
|
|
|
2018-11-19 14:47:20 +00:00
|
|
|
// selecting
|
2018-10-19 15:17:02 +00:00
|
|
|
import qsaFactory from 'consul-ui/utils/dom/qsa-factory';
|
2018-11-19 14:47:20 +00:00
|
|
|
// TODO: sibling and closest seem to have 'PHP-like' guess the order arguments
|
|
|
|
// ie. one `string, element` and the other has `element, string`
|
|
|
|
// see if its possible to standardize
|
|
|
|
import sibling from 'consul-ui/utils/dom/sibling';
|
|
|
|
import closest from 'consul-ui/utils/dom/closest';
|
|
|
|
import getComponentFactory from 'consul-ui/utils/dom/get-component-factory';
|
|
|
|
|
|
|
|
// events
|
2018-10-19 15:17:02 +00:00
|
|
|
import normalizeEvent from 'consul-ui/utils/dom/normalize-event';
|
ui: Async Search (#4859)
This does several things to make improving the search experience easier
moving forwards:
1. Separate searching off from filtering. 'Searching' can be thought of
as specifically 'text searching' whilst filtering is more of a
boolean/flag search.
2. Decouple the actual searching functionality to almost pure,
isolated / unit testable units and unit test. (I still import embers get
which, once I upgrade to 3.5, I shouldn't need)
3. Searching rules are now configurable from the outside, i.e. not
wrapped in Controllers or Components.
4. General searching itself now can use an asynchronous approach based on
events. This prepares for future possibilities of handing off the
searching to a web worker or elsewhere, which should aid in large scale
searching and prepares the way for other searching methods.
5. Adds the possibility of have multiple searches in one
template/route/page.
Additionally, this adds a WithSearching mixin to aid linking the
searching to ember in an ember-like way in a single place. Plus a
WithListeners mixin to aid with cleaning up of event listeners on
Controller/Component destruction.
Post-initial work I slightly changed the API of create listeners:
Returning the handler from a `remover` means you can re-add it again if you
want to, this avoids having to save a reference to the handler elsewhere
to do the same.
The `remove` method itself now returns an array of handlers, again you
might want to use these again or something, and its also more useful
then just returning an empty array.
The more I look at this the more I doubt that you'll ever use `remove`
to remove individual handlers, you may aswell just use the `remover`
returned from add. I've added some comments to reflect this, but they'll
likely be removed once I'm absolutely sure of this.
I also added some comments for WithSearching to explain possible further
work re: moving `searchParams` so it can be `hung` off the
controller object
2018-11-06 09:10:20 +00:00
|
|
|
import createListeners from 'consul-ui/utils/dom/create-listeners';
|
2018-11-19 14:47:20 +00:00
|
|
|
import clickFirstAnchorFactory from 'consul-ui/utils/dom/click-first-anchor';
|
2018-10-19 15:17:02 +00:00
|
|
|
|
|
|
|
// ember-eslint doesn't like you using a single $ so use double
|
|
|
|
// use $_ for components
|
|
|
|
const $$ = qsaFactory();
|
|
|
|
let $_;
|
2018-11-19 14:47:20 +00:00
|
|
|
const clickFirstAnchor = clickFirstAnchorFactory(closest);
|
2018-10-19 15:17:02 +00:00
|
|
|
export default Service.extend({
|
|
|
|
doc: document,
|
2018-12-05 09:07:16 +00:00
|
|
|
win: window,
|
2018-10-19 15:17:02 +00:00
|
|
|
init: function() {
|
|
|
|
this._super(...arguments);
|
|
|
|
$_ = getComponentFactory(getOwner(this));
|
|
|
|
},
|
2018-12-05 09:07:16 +00:00
|
|
|
document: function() {
|
|
|
|
return get(this, 'doc');
|
|
|
|
},
|
|
|
|
viewport: function() {
|
|
|
|
return get(this, 'win');
|
|
|
|
},
|
2018-11-19 14:47:20 +00:00
|
|
|
// TODO: should this be here? Needs a better name at least
|
|
|
|
clickFirstAnchor: clickFirstAnchor,
|
|
|
|
closest: closest,
|
|
|
|
sibling: sibling,
|
|
|
|
normalizeEvent: normalizeEvent,
|
ui: Async Search (#4859)
This does several things to make improving the search experience easier
moving forwards:
1. Separate searching off from filtering. 'Searching' can be thought of
as specifically 'text searching' whilst filtering is more of a
boolean/flag search.
2. Decouple the actual searching functionality to almost pure,
isolated / unit testable units and unit test. (I still import embers get
which, once I upgrade to 3.5, I shouldn't need)
3. Searching rules are now configurable from the outside, i.e. not
wrapped in Controllers or Components.
4. General searching itself now can use an asynchronous approach based on
events. This prepares for future possibilities of handing off the
searching to a web worker or elsewhere, which should aid in large scale
searching and prepares the way for other searching methods.
5. Adds the possibility of have multiple searches in one
template/route/page.
Additionally, this adds a WithSearching mixin to aid linking the
searching to ember in an ember-like way in a single place. Plus a
WithListeners mixin to aid with cleaning up of event listeners on
Controller/Component destruction.
Post-initial work I slightly changed the API of create listeners:
Returning the handler from a `remover` means you can re-add it again if you
want to, this avoids having to save a reference to the handler elsewhere
to do the same.
The `remove` method itself now returns an array of handlers, again you
might want to use these again or something, and its also more useful
then just returning an empty array.
The more I look at this the more I doubt that you'll ever use `remove`
to remove individual handlers, you may aswell just use the `remover`
returned from add. I've added some comments to reflect this, but they'll
likely be removed once I'm absolutely sure of this.
I also added some comments for WithSearching to explain possible further
work re: moving `searchParams` so it can be `hung` off the
controller object
2018-11-06 09:10:20 +00:00
|
|
|
listeners: createListeners,
|
2018-10-19 15:17:02 +00:00
|
|
|
root: function() {
|
|
|
|
return get(this, 'doc').documentElement;
|
|
|
|
},
|
|
|
|
// TODO: Should I change these to use the standard names
|
|
|
|
// even though they don't have a standard signature (querySelector*)
|
|
|
|
elementById: function(id) {
|
|
|
|
return get(this, 'doc').getElementById(id);
|
|
|
|
},
|
|
|
|
elementsByTagName: function(name, context) {
|
|
|
|
context = typeof context === 'undefined' ? get(this, 'doc') : context;
|
2019-01-25 12:30:51 +00:00
|
|
|
return context.getElementsByTagName(name);
|
2018-10-19 15:17:02 +00:00
|
|
|
},
|
|
|
|
elements: function(selector, context) {
|
2018-11-19 14:47:20 +00:00
|
|
|
// don't ever be tempted to [...$$()] here
|
|
|
|
// it should return a NodeList
|
2018-10-19 15:17:02 +00:00
|
|
|
return $$(selector, context);
|
|
|
|
},
|
|
|
|
element: function(selector, context) {
|
|
|
|
if (selector.substr(0, 1) === '#') {
|
|
|
|
return this.elementById(selector.substr(1));
|
|
|
|
}
|
|
|
|
// TODO: This can just use querySelector
|
|
|
|
return [...$$(selector, context)][0];
|
|
|
|
},
|
|
|
|
// ember components aren't strictly 'dom-like'
|
|
|
|
// but if you think of them as a web component 'shim'
|
|
|
|
// then it makes more sense to think of them as part of the dom
|
|
|
|
// with traditional/standard web components you wouldn't actually need this
|
|
|
|
// method as you could just get to their methods from the dom element
|
|
|
|
component: function(selector, context) {
|
2019-02-21 13:10:53 +00:00
|
|
|
if (typeof selector !== 'string') {
|
|
|
|
return $_(selector);
|
|
|
|
}
|
2018-10-19 15:17:02 +00:00
|
|
|
return $_(this.element(selector, context));
|
|
|
|
},
|
2018-11-19 14:47:20 +00:00
|
|
|
components: function(selector, context) {
|
|
|
|
return [...this.elements(selector, context)]
|
|
|
|
.map(function(item) {
|
|
|
|
return $_(item);
|
|
|
|
})
|
|
|
|
.filter(function(item) {
|
|
|
|
return item != null;
|
|
|
|
});
|
|
|
|
},
|
2018-10-19 15:17:02 +00:00
|
|
|
});
|