Updated loot-search (now loot-search-toolbar)

Decoupled it from external code, which can now be run by handling
events.
This commit is contained in:
Oliver Hamlet
2016-03-05 11:49:26 +00:00
parent d20f449f9d
commit 224b49aa45
4 changed files with 259 additions and 170 deletions
+1
View File
@@ -66,6 +66,7 @@ function vulcanizeTests(releasePath) {
'test_loot-dropdown-menu.html',
'test_loot-message-dialog.html',
'test_loot-plugin-item.html',
'test_loot-search-toolbar.html',
];
tests.forEach((test) => {
@@ -0,0 +1,177 @@
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../../../../bower_components/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="../../../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../../../../bower_components/paper-input/paper-input.html">
<link rel="import" href="../../../../bower_components/paper-toolbar/paper-toolbar.html">
<link rel="import" href="../../../../bower_components/paper-tooltip/paper-tooltip.html">
<!-- events
loot-search-begin: Fired when a search begins.
loot-search-change-selection: Fired when the selected result changes.
loot-search-end: Fired when the close button is clicked.
-->
<dom-module id="loot-search-toolbar">
<template>
<style>
paper-input {
@apply(--layout-horizontal);
@apply(--layout-flex);
--paper-input-container: {
@apply(--layout-flex);
};
--paper-input-container-input-color: var(--loot-dark-theme-text-color);
--paper-input-container-label: {
color: var(--loot-dark-theme-disabled-color);
}
--paper-input-container-underline: {
background-color: var(--loot-dark-theme-divider-color);
};
}
#count {
font-weight: 400;
font-size: 0.857rem;
color: var(--loot-dark-theme-secondary-color);
}
</style>
<paper-toolbar>
<paper-input id="search" label="Search cards" no-label-float></paper-input>
<div id="count">
<span>[[_computeResultNum(_currentResult)]]</span>
/
<span><!-- number of results --></span>
</div>
<paper-icon-button id="prev" icon="expand-less" disabled></paper-icon-button>
<paper-icon-button id="next" icon="expand-more" disabled></paper-icon-button>
<paper-icon-button id="close" icon="close"></paper-icon-button>
</paper-toolbar>
</template>
<script>
'use strict';
Polymer({ // eslint-disable-line new-cap, no-undef
is: 'loot-search-toolbar',
properties: {
_currentResult: {
type: Number,
value: -1,
observer: '_currentResultChanged',
},
results: {
type: Array,
value: () => { return []; },
observer: '_resultsChanged',
},
},
_currentResultChanged(newValue) {
if (this.results && this.results.length > 0) {
if (this.results.length === 1) {
this.$.prev.disabled = true;
this.$.next.disabled = true;
} else if (newValue === 0) {
this.$.prev.disabled = true;
this.$.next.disabled = false;
} else if (newValue === this.results.length - 1) {
this.$.prev.disabled = false;
this.$.next.disabled = true;
} else {
this.$.prev.disabled = false;
this.$.next.disabled = false;
}
}
if (newValue > -1) {
this.dispatchEvent(new CustomEvent('loot-search-change-selection', {
detail: { selection: this.results[newValue] },
bubbles: true,
}));
}
},
_resultsChanged(newValue) {
this.$.count.lastElementChild.textContent = newValue.length;
if (newValue.length > 0) {
this._currentResult = 0;
}
},
attached() {
this.$.search.addEventListener('input', this._onSearch);
this.$.search.addEventListener('keyup', this._onEnter);
this.$.prev.addEventListener('click', this._onPrev);
this.$.next.addEventListener('click', this._onNext);
this.$.close.addEventListener('click', this._onClose);
},
detached() {
this.$.search.removeEventListener('input', this._onSearch);
this.$.search.removeEventListener('keyup', this._onEnter);
this.$.prev.removeEventListener('click', this._onPrev);
this.$.next.removeEventListener('click', this._onNext);
this.$.close.removeEventListener('change', this._onClose);
},
_onEnter(evt) {
const host = evt.target.parentElement.parentNode.host;
if (evt.keyCode !== 13 || host.results.length === 0) {
return;
}
if (host._currentResult === host.results.length - 1) {
host._currentResult = 0;
} else {
++host._currentResult;
}
},
focusInput() {
this.$.search.focus();
},
_resetResults() {
this.results = [];
this._currentResult = -1;
this.$.prev.setAttribute('disabled', '');
this.$.next.setAttribute('disabled', '');
},
search() {
this.$.search.dispatchEvent(new Event('input'));
},
_onSearch(evt) {
evt.target.parentElement.parentNode.host._resetResults();
const needle = evt.target.value.toLowerCase();
evt.target.dispatchEvent(new CustomEvent('loot-search-begin', {
detail: { needle },
bubbles: true,
}));
},
_onPrev(evt) {
--evt.target.parentElement.parentNode.host._currentResult;
},
_onNext(evt) {
++evt.target.parentElement.parentNode.host._currentResult;
},
_onClose(evt) {
const host = evt.target.parentElement.parentNode.host;
host._resetResults();
host.$.search.value = '';
host.$.count.classList.toggle('hidden', true);
evt.target.dispatchEvent(new CustomEvent('loot-search-end', {
bubbles: true,
}));
},
_computeResultNum(currentResult) {
return currentResult + 1;
},
});
</script>
</dom-module>
-170
View File
@@ -1,170 +0,0 @@
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/core-toolbar/core-toolbar.html">
<link rel="import" href="../../../../bower_components/core-tooltip/core-tooltip.html">
<link rel="import" href="../../../../bower_components/paper-icon-button/paper-icon-button.html">
<!--
searchTarget is the ID of the core-list element to search the elements of.
-->
<polymer-element name="loot-search" attributes="searchTarget">
<template>
<style>
core-toolbar {
background: #3F51B5;
color: white;
height: 56px;
}
core-toolbar > * {
top: -3px;
}
paper-input {
position: relative;
}
paper-input /deep/ paper-input-decorator::shadow .label-text,
paper-input /deep/ input::-webkit-input-placeholder {
color: rgba(255, 255, 255, 0.3) !important;
}
.caption {
font-weight: 400;
font-size: 0.857rem;
color: rgba(255, 255, 255, 0.7);
}
</style>
<core-toolbar>
<paper-input id="search" label="Search cards" flex></paper-input>
<div id="count" class="caption" hidden>
{{currentResult + 1}}/{{numResults}}
</div>
<paper-icon-button id="prev" icon="expand-less" disabled></paper-icon-button>
<paper-icon-button id="next" icon="expand-more" disabled></paper-icon-button>
<paper-icon-button id="close" icon="close"></paper-icon-button>
</core-toolbar>
</template>
<script>
Polymer({
currentResult: -1,
results: [],
numResults: 0,
searchTarget: undefined,
resultsChanged: function(oldValue, newValue) {
this.numResults = newValue.length;
},
currentResultChanged: function(oldValue, newValue) {
if (this.results.length > 0) {
if (this.results.length == 1) {
this.shadowRoot.getElementById('prev').setAttribute('disabled', '');
this.shadowRoot.getElementById('next').setAttribute('disabled', '');
} else if (newValue == 0) {
this.shadowRoot.getElementById('prev').setAttribute('disabled', '');
this.shadowRoot.getElementById('next').removeAttribute('disabled');
} else if (newValue == this.results.length - 1) {
this.shadowRoot.getElementById('prev').removeAttribute('disabled');
this.shadowRoot.getElementById('next').setAttribute('disabled', '');
} else {
this.shadowRoot.getElementById('prev').removeAttribute('disabled');
this.shadowRoot.getElementById('next').removeAttribute('disabled');
}
}
},
attached: function() {
this.shadowRoot.getElementById('search').addEventListener('input', this.onSearch, false);
this.shadowRoot.getElementById('search').addEventListener('keyup', this.onEnter, false);
this.shadowRoot.getElementById('prev').addEventListener('click', this.onPrev, false);
this.shadowRoot.getElementById('next').addEventListener('click', this.onNext, false);
this.shadowRoot.getElementById('close').addEventListener('click', this.onClose, false);
},
detached: function() {
this.shadowRoot.getElementById('search').removeEventListener('input', this.onSearch, false);
this.shadowRoot.getElementById('search').removeEventListener('keyup', this.onEnter, false);
this.shadowRoot.getElementById('prev').removeEventListener('click', this.onPrev, false);
this.shadowRoot.getElementById('next').removeEventListener('click', this.onNext, false);
this.shadowRoot.getElementById('close').removeEventListener('change', this.onClose, false);
},
resetResults: function() {
loot.game.plugins.forEach(function(plugin){
plugin.isSearchResult = false;
});
this.results = [];
this.currentResult = -1;
this.shadowRoot.getElementById('prev').setAttribute('disabled', '');
this.shadowRoot.getElementById('next').setAttribute('disabled', '');
},
onEnter: function(evt) {
var host = evt.target.parentElement.parentNode.host;
if (evt.keyCode != 13 || host.results.length == 0) {
return;
}
if (host.currentResult == host.results.length - 1) {
host.currentResult = 0;
} else {
++host.currentResult;
}
document.getElementById(host.searchTarget).scrollToItem(host.results[host.currentResult]);
},
focusInput: function() {
this.shadowRoot.getElementById('search').focus();
},
search: function() {
this.shadowRoot.getElementById('search').dispatchEvent(new Event('input'));
},
onSearch: function(evt) {
var needle = evt.target.value.toLowerCase();
var host = evt.target.parentElement.parentNode.host;
host.resetResults();
host.shadowRoot.getElementById('count').hidden = !evt.target.value;
if (!evt.target.value) {
return;
}
var versionHidden = document.getElementById('hideVersionNumbers').checked;
var crcHidden = document.getElementById('hideCRCs').checked;
var bashTagHidden = document.getElementById('hideBashTags').checked;
document.getElementById(host.searchTarget).data.forEach(function(plugin, index){
if (plugin.getCardContent(loot.filters).containsText(needle)) {
host.results.push(index);
plugin.isSearchResult = true;
return;
}
});
if (host.results.length > 0) {
host.currentResult = 0;
// For some reason change watcher doesn't run, call it manually.
host.currentResultChanged(-1, 0);
document.getElementById(host.searchTarget).scrollToItem(host.results[host.currentResult]);
}
},
onPrev: function(evt) {
var host = evt.target.parentElement.parentNode.host;
--host.currentResult;
document.getElementById(host.searchTarget).scrollToItem(host.results[host.currentResult]);
},
onNext: function(evt) {
var host = evt.target.parentElement.parentNode.host;
++host.currentResult;
document.getElementById(host.searchTarget).scrollToItem(host.results[host.currentResult]);
},
onClose: function(evt) {
evt.target.parentElement.parentNode.host.resetResults();
evt.target.parentElement.parentNode.getElementById('search').value = '';
evt.target.parentElement.parentNode.getElementById('count').hidden = true;
evt.target.dispatchEvent(new CustomEvent('loot-search-close'));
},
});
</script>
</polymer-element>
@@ -0,0 +1,81 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>test_loot-search-toolbar.html</title>
<link rel="import" href="../../../../gui/html/native-shadow-dom.html">
<link rel="import" href="../../../../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../../../../bower_components/iron-list/iron-list.html">
<link rel="import" href="../../../../gui/html/elements/loot-search-toolbar.html">
<script src="../../../../gui/html/js/plugin.js"></script>
<script src="../../../../gui/html/js/translator.js"></script>
<script src="../../../../gui/html/js/filters.js"></script>
</head>
<body>
<loot-search-toolbar id="searchBar"></loot-search-toolbar>
<iron-list id="pluginList" style="height: 500px;">
<template>
<div style="height: 300px;">[[item.name]]</div>
</template>
</iron-list>
<script>
'use strict';
const filters = new loot.Filters(new loot.Translator());
const plugins = [
new loot.Plugin({
name: 'Skyrim.esm',
}),
new loot.Plugin({
name: 'Hearthfires.esm',
}),
new loot.Plugin({
name: 'Dawnguard.esm',
}),
new loot.Plugin({
name: 'Dragonborn.esm',
}),
];
function onSearchEnd() {
plugins.forEach((plugin) => {
plugin.isSearchResult = false;
});
}
function onSearchBegin(evt) {
onSearchEnd();
if (!evt.detail.needle) {
return;
}
// Don't push to the target's results property directly, as the
// change observer doesn't work correctly unless special Polymer APIs
// are used, which I don't want to get into.
const results = [];
plugins.forEach((plugin, index) => {
if (plugin.getCardContent(filters).containsText(evt.detail.needle)) {
results.push(index);
plugin.isSearchResult = true;
}
});
evt.target.results = results;
}
function onSearchChangeSelection(evt) {
document.getElementById('pluginList').scrollToIndex(evt.detail.selection);
}
document.addEventListener('loot-search-begin', onSearchBegin);
document.addEventListener('loot-search-change-selection', onSearchChangeSelection);
document.addEventListener('loot-search-end', onSearchEnd);
document.getElementById('pluginList').items = plugins;
</script>
</body>