salix/front/core/components/searchbar/searchbar.js

311 lines
8.2 KiB
JavaScript
Raw Normal View History

import ngModule from '../../module';
import Component from '../../lib/component';
2018-12-27 11:54:16 +00:00
import {buildFilter} from 'vn-loopback/util/filter';
2019-11-10 10:08:44 +00:00
import focus from '../../lib/focus';
import './style.scss';
/**
* An input specialized to perform searches, it allows to use a panel
* for advanced searches when the panel property is defined.
* When model and exprBuilder properties are used, the model is updated
* automatically with an and-filter exprexion in which each operand is built
* by calling the exprBuilder function for each non-null parameter.
*
* @property {Object} filter A key-value object with filter parameters
* @property {SearchPanel} panel The panel used for advanced searches
*/
export default class Controller extends Component {
2019-11-10 10:08:44 +00:00
constructor($element, $) {
super($element, $);
this.searchState = '.';
2018-12-19 07:42:07 +00:00
2019-11-10 10:08:44 +00:00
let criteria = {};
this.deregisterCallback = this.$transitions.onSuccess(
criteria, () => this.onStateChange());
}
2018-12-19 07:42:07 +00:00
$postLink() {
2019-11-10 10:08:44 +00:00
this.onStateChange();
2018-12-19 07:42:07 +00:00
}
2019-11-10 10:08:44 +00:00
$onDestroy() {
this.deregisterCallback();
2018-12-19 07:42:07 +00:00
}
2019-11-10 12:10:52 +00:00
get filter() {
return this._filter;
}
set filter(value) {
this._filter = value;
this.toBar(value);
}
2019-11-10 10:08:44 +00:00
get shownFilter() {
return this.filter != null
? this.filter
: this.suggestedFilter;
}
2019-11-10 12:10:52 +00:00
get searchString() {
return this._searchString;
}
set searchString(value) {
this._searchString = value;
if (value == null) this.params = [];
}
2018-12-19 07:42:07 +00:00
onStateChange() {
2019-11-10 12:10:52 +00:00
let filter = null;
2019-11-10 10:08:44 +00:00
if (this.$state.is(this.searchState)) {
2019-11-10 12:10:52 +00:00
if (this.$params.q) {
2019-11-10 10:08:44 +00:00
try {
2019-11-10 12:10:52 +00:00
filter = JSON.parse(this.$params.q);
2019-11-10 10:08:44 +00:00
} catch (e) {
console.error(e);
}
2019-11-10 12:10:52 +00:00
}
2018-12-19 07:42:07 +00:00
2019-11-10 10:08:44 +00:00
focus(this.element.querySelector('vn-textfield input'));
2019-11-10 12:10:52 +00:00
}
2019-11-10 12:10:52 +00:00
this.filter = filter;
2019-08-29 06:32:46 +00:00
}
openPanel(event) {
if (event.defaultPrevented) return;
this.$.popover.show(this.element);
2019-02-15 10:03:27 +00:00
this.$panelScope = this.$.$new();
this.panelEl = this.$compile(`<${this.panel}/>`)(this.$panelScope)[0];
let panel = this.panelEl.$ctrl;
2019-08-29 06:32:46 +00:00
if (this.shownFilter)
panel.filter = JSON.parse(JSON.stringify(this.shownFilter));
panel.onSubmit = filter => this.onPanelSubmit(filter.$filter);
this.$.popover.content.appendChild(this.panelEl);
}
onPopoverClose() {
2019-02-15 10:03:27 +00:00
this.$panelScope.$destroy();
this.$panelScope = null;
this.panelEl.remove();
this.panelEl = null;
}
onPanelSubmit(filter) {
this.$.popover.hide();
2019-01-16 14:29:01 +00:00
filter = compact(filter);
2019-11-10 10:08:44 +00:00
filter = filter != null ? filter : {};
this.doSearch(filter);
}
onSubmit() {
2019-11-10 10:08:44 +00:00
this.doSearch(this.fromBar());
}
2019-11-10 10:08:44 +00:00
removeParam(index) {
this.params.splice(index, 1);
this.doSearch(this.fromBar());
}
doSearch(filter) {
2019-11-10 12:10:52 +00:00
this.filter = filter;
2019-11-10 10:08:44 +00:00
let opts = this.$state.is(this.searchState)
? {location: 'replace'} : null;
this.$state.go(this.searchState,
{q: JSON.stringify(filter)}, opts);
}
fromBar() {
let filter = {};
if (this.searchString)
filter.search = this.searchString;
if (this.params) {
for (let param of this.params)
filter[param.key] = param.value;
}
return filter;
}
toBar(filter) {
this.params = [];
this.searchString = filter && filter.search;
if (!filter) return;
let keys = Object.keys(filter);
keys.forEach(key => {
if (key == 'search') return;
let value = filter[key];
let chip;
if (typeof value == 'string'
&& /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value))
value = new Date(value);
switch (typeof value) {
case 'boolean':
chip = `${value ? '' : 'not '}${key}`;
break;
case 'number':
case 'string':
chip = `${key}: ${value}`;
break;
default:
if (value instanceof Date) {
let format = 'yyyy-MM-dd';
if (value.getHours() || value.getMinutes())
format += ' HH:mm';
chip = `${key}: ${this.$filter('date')(value, format)}`;
} else
chip = key;
}
this.params.push({chip, key, value});
});
}
}
ngModule.vnComponent('vnSearchbar', {
controller: Controller,
template: require('./searchbar.html'),
bindings: {
searchState: '@?',
filter: '<?',
suggestedFilter: '<?',
panel: '@',
info: '@?'
}
});
/**
* @property {CrudModel} model The model used for searching
* @property {Function} exprBuilder If defined, is used to build each non-null param expresion
* @property {Function} onSearch Function to call when search is submited
*/
class AutoSearch {
constructor($state, $transitions) {
this.$state = $state;
this.$transitions = $transitions;
let criteria = {to: this.$state.current.name};
this.deregisterCallback = this.$transitions.onSuccess(criteria,
() => this.onStateChange());
this.fetchFilter();
}
$postLink() {
if (this.filter !== null)
this.doSearch();
}
$onDestroy() {
this.deregisterCallback();
}
fetchFilter() {
if (this.$state.params.q) {
try {
this.filter = JSON.parse(this.$state.params.q);
} catch (e) {
console.error(e);
}
} else
this.filter = null;
}
onStateChange() {
this.fetchFilter();
this.doSearch();
}
2018-12-19 07:42:07 +00:00
2019-11-10 10:08:44 +00:00
doSearch() {
let filter = this.filter;
2019-01-16 14:29:01 +00:00
if (filter == null && this.autoload)
2018-12-19 07:42:07 +00:00
filter = {};
if (this.onSearch)
2018-12-19 07:42:07 +00:00
this.onSearch({$params: filter});
if (this.model) {
2018-12-19 07:42:07 +00:00
if (filter !== null) {
let where = buildFilter(filter,
(param, value) => this.exprBuilder({param, value}));
let userParams = {};
let hasParams = false;
if (this.paramBuilder) {
for (let param in filter) {
let value = filter[param];
if (value == null) continue;
let expr = this.paramBuilder({param, value});
if (expr) {
Object.assign(userParams, expr);
hasParams = true;
}
2018-09-05 11:01:21 +00:00
}
}
2018-12-19 07:42:07 +00:00
this.model.applyFilter(
where ? {where} : null,
hasParams ? userParams : null
);
} else
this.model.clear();
}
}
2018-12-19 07:42:07 +00:00
exprBuilder(param, value) {
return {[param]: value};
}
}
2019-11-10 10:08:44 +00:00
AutoSearch.$inject = ['$state', '$transitions'];
2019-11-10 10:08:44 +00:00
ngModule.vnComponent('vnAutoSearch', {
controller: AutoSearch,
bindings: {
model: '<?',
2019-11-10 10:08:44 +00:00
onSearch: '&?',
2018-09-05 11:01:21 +00:00
exprBuilder: '&?',
2019-11-10 10:08:44 +00:00
paramBuilder: '&?'
}
});
2019-01-16 14:29:01 +00:00
/**
* Removes null, undefined, empty objects and empty arrays from an object.
* It also applies to nested objects/arrays.
*
* @param {*} obj The value to format
* @return {*} The formatted value
*/
function compact(obj) {
if (obj == null)
return undefined;
else if (Array.isArray(obj)) {
for (let i = obj.length - 1; i >= 0; i--) {
if (compact(obj[i]) === undefined)
obj.splice(i, 1);
}
if (obj.length == 0)
return undefined;
} else if (typeof obj == 'object' && obj.constructor == Object) {
let keys = Object.keys(obj);
for (let key of keys) {
2019-11-10 10:08:44 +00:00
if (key.charAt(0) == '$' || compact(obj[key]) === undefined)
2019-01-16 14:29:01 +00:00
delete obj[key];
}
if (Object.keys(obj).length == 0)
return undefined;
}
return obj;
}