blob: af794477d09ccdb1943b229da1e84d5c344e4e10 [file] [log] [blame]
<!-- The <query2-sk> custom element declaration.
Starting from a serialized paramtools.ParamSet, this control allows the user
to build up a query, suitable for passing to query.New.
Attributes:
current_query - The current query formatted as a URL formatted query string.
Events:
'query-change'
The 'query2-sk' element will produce 'query-change' events when the query
parameters chosen have changed. The event contains the current
selections formatted as a URL query, found in e.detail.q.
Methods:
setCurrentQuery(s) - Sets the query selections of the control, where 's'
is a URL formatted query string, as returned from current_query.
setParamset(p) - A serialized paramtools.ParamSet, that is the source
of the options for the query.
-->
<link rel="stylesheet" href="/res/common/css/md.css">
<link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html">
<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html">
<dom-module id="query2-sk">
<style include="iron-flex iron-flex-alignment iron-positioning">
input,
select {
margin: 0 1em;
}
paper-checkbox {
--paper-checkbox-checked-color: #1F78B4;
--paper-checkbox-checked-ink-color: #1F78B4;
--paper-checkbox-unchecked-color: #1F78B4;
--paper-checkbox-unchecked-ink-color: #1F78B4;
margin: 0 1em 0.5em 1em;
}
.hidden {
display: none;
}
</style>
<template>
<div class="horizontal layout">
<div class="vertical layout">
<div class="horizontal layout">
<paper-input id="fast" label="Filter" on-input="_fastFilter"></paper-input>
<button on-tap="_clearFilter">Clear Filter</button>
</div>
<select id=keys size=12 on-change="_keyChange">
<template is="dom-repeat" items="[[_keys]]">
<option value="[[item]]">[[item]]</option>
</template>
</select>
<button on-tap="_clear">Clear Selections</button>
</div>
<div class="vertical layout">
<paper-checkbox
disabled
id=invert
on-change="_valueChange"
title="Match items that aren't selected below.">Invert</paper-checkbox>
<paper-checkbox
disabled
id=regex
on-tap="_regexTap"
title="Match items via regular expression.">Regex</paper-checkbox>
<input
disabled
type="text"
id="regexValue"
value="[[_regexDisplayValue(_selectedValue)]]"
class=hidden
on-input="_valueChange"
/>
<select id=values size=10 on-change="_valueChange" multiple>
<template is="dom-repeat" items="[[_values]]">
<option value="[[item]]" selected="[[_isSelected(item, _selectedValue)]]">[[item]]</option>
</template>
</select>
</div>
</div>
</template>
</dom-module>
<script>
Polymer({
is: "query2-sk",
properties: {
// The serialized form of _query.
current_query: {
type: String,
value: "",
reflectToAttribute: true,
},
// A serialized paramtools.ParamSet, which we are building a query around.
//
// Might contain a subset of _originalParamset if filtering has been applied.
_paramset: {
type: Object,
value: function() { return {}; },
},
// A serialized paramtools.ParamSet, which we are building a query around.
_originalParamset: {
type: Object,
value: function() { return {}; },
},
// The keys of paramset.
_keys: {
type: Array,
value: function() { return []; },
},
// The currently selected key from _keys.
_selectedKey: {
type: String,
value: "",
},
// The currently selected value, i.e. _query[_selectedKey].
_selectedValue: {
type: Array,
value: function() { return []; },
},
// The values of paramset at _selectedKey.
_values: {
type: Array,
value: function() { return []; },
},
// The current set of user selections.
_query: {
type: Object,
value: function() { return {}; },
},
},
setCurrentQuery: function(s) {
this._query = sk.query.toParamSet(s);
this._queryChanged();
},
setParamset: function(paramset) {
// Keep a backup, for when we do filtering.
this._originalParamset = paramset;
this._paramset = paramset;
this._recalcKeys();
},
_recalcKeys: function() {
var keys = Object.keys(this._paramset);
keys.sort();
this.set("_keys", keys);
this.$.keys.selectedIndex = -1;
this.set("_values", []);
this.$.invert.disabled = true;
this.$.regex.disabled = true;
this.$.regexValue.disabled = true;
},
_fastFilter: function() {
var filters = this.$.fast.value.trim().toLowerCase().split(/\s+/);
// Create a closure that returns true if the given label
// matches the filter.
var matches = function(s) {
s = s.toLowerCase();
for (var i = 0; i < filters.length; i++) {
if (s.indexOf(filters[i]) >= 0) {
return true;
}
}
return false;
};
// Loop over this._originalParamset.
var filtered = {};
Object.keys(this._originalParamset).forEach(function(paramkey) {
// If the param key matches, then all the values go over.
if (matches(paramkey)) {
filtered[paramkey] = this._originalParamset[paramkey];
} else {
// Look for matches in the param values.
var valueMatches = [];
this._originalParamset[paramkey].forEach(function(paramvalue) {
if (matches(paramvalue)) {
valueMatches.push(paramvalue);
}
}.bind(this));
if (valueMatches.length > 0) {
filtered[paramkey] = valueMatches;
}
}
}.bind(this));
this._paramset = filtered;
this._recalcKeys();
},
_clearFilter: function() {
this._paramset = this._originalParamset;
this._recalcKeys();
this.$.fast.value = "";
},
// Returns the prefixed value, for example, "!" if it is an inverted search.
_prefixValue: function(v) {
return (this.$.invert.checked ? '!' : '') + v;
},
// Toggles between displaying the text box for a regular expression and
// the select box of the param values, depending on the regex checkbox
// being checked.
_toggleRegexDisplay: function() {
if (this.$.regex.checked) {
this.$.regexValue.classList.remove("hidden");
this.$.values.classList.add("hidden");
this.$.invert.disabled = true;
} else {
this.$.regexValue.classList.add("hidden");
this.$.values.classList.remove("hidden");
this.$.invert.disabled = false;
}
},
_regexTap: function() {
this._toggleRegexDisplay();
this._valueChange();
},
_keyChange: function() {
var key = this.$.keys.selectedOptions[0].value;
this.set("_values", this._paramset[key]);
this.set("_selectedValue", this._query[key] || []);
this.set("_selectedKey", key);
this.$.invert.disabled = false;
this.$.regex.disabled = false;
this.$.regexValue.disabled = false;
// Use !! to force a boolean value, to work around: https://github.com/PolymerElements/paper-checkbox/issues/140
this.$.regex.checked = !!(this._query[key] && this._query[key].length == 1 && this._query[key][0][0] === "~");
this.$.invert.checked = !!(this._query[key] && this._query[key].length >= 1 && this._query[key][0][0] === "!");
this._toggleRegexDisplay();
},
_valueChange: function(e) {
if (this.$.regex.checked) {
this._query[this._selectedKey] = ["~" + this.$.regexValue.value];
} else {
var selectedValues = [];
$$("#values option", this).forEach(function(e){
if (e.selected) {
selectedValues.push(this._prefixValue(e.value));
}
}.bind(this));
this._query[this._selectedKey] = selectedValues;
}
this._queryChanged();
},
_queryChanged: function() {
this.current_query = sk.query.fromParamSet(this._query);
this.fire('query-change', {q: this.current_query});
},
_clear: function() {
this._query = {};
this._recalcKeys();
this._queryChanged();
},
_isSelected: function(paramValue, selectedValues) {
if (!selectedValues) {
return false;
}
return selectedValues.indexOf(this._prefixValue(paramValue)) !== -1;
},
_regexDisplayValue: function(arr) {
if (arr && arr.length > 0) {
return arr[0].slice(1);
}
return "";
},
});
</script>