Move the conflicts filter UI into the Filters sidebar tab

Instead of having a toggle button in each card's dropdown menu,
implement it as a dropdown menu in the Filters sidebar tab,
containing a list of plugin filenames, plus a "No plugin" option.
Closes #580.
This commit is contained in:
Oliver Hamlet
2016-06-07 18:54:40 +01:00
parent 8b0fbd5370
commit eb7bc503c2
12 changed files with 62 additions and 82 deletions
+4 -7
View File
@@ -321,7 +321,7 @@ figure > div {
<li>The <q>Empty Plugin</q> icon.
<li>The <q>Loads Archive</q> icon.
<li>The <q>Has User Metadata</q> icon.
<li>The plugin menu button, which provides access to a few plugin-specific features, including a conflict filter and access to it in the metadata editor. These are explained in later sections.
<li>The plugin menu button, which provides access to metadata-related features for the plugin. These are explained in later sections.
</ol>
<p>Bash Tag suggestions and messages are displayed below the plugin card's header.
@@ -342,13 +342,10 @@ figure > div {
<li><i>Hide inactive plugins</i>: Hides all plugins that are inactive.
<li><i>Hide messageless plugins</i>: Hides all plugins that have no visible messages.
</ul>
<p>The filter toggles have their states saved on quitting LOOT, and they are restored when LOOT is next launched. There are also two other filters:
<p>The filter toggles have their states saved on quitting LOOT, and they are restored when LOOT is next launched. There are also two other filters in the sidebar tab:
<ul>
<li>A content filter, located beneath the filter toggles in the <span class="button">Filters</span> sidebar tab. This hides any plugins that don't have the filter input value present in any of the text on their cards.
<li>A conflict filter that can be accessed through each plugin card's menu. This filters the plugin cards displayed so that only plugins which conflict with this plugin will be visible. If this plugin loads an archive, other plugins that load archives which <em>may</em> contain conflicting resources are also displayed.</p>
Only one plugin's conflict filter can be active at any one time, so activating a second conflict filter will deactivate the first. The plugin for which the filter is currently active has its card highlighted. Sorting with a conflict filter active will first deactivate it.
<li>A content filter. This hides any plugins that don't have the filter input value present in any of the text on their cards.
<li>A conflict filter. This filters the plugin cards displayed so that only plugins which conflict with this plugin will be visible. If this plugin loads an archive, other plugins that load archives which <em>may</em> contain conflicting resources are also displayed. Sorting with the conflict filter active will first deactivate it.
</ul>
<h3 id="usage-editing">Editing Plugin Metadata</h3>
+4
View File
@@ -78,6 +78,10 @@ div[drawer] > iron-pages {
padding: 0 16px;
height: 48px;
}
#conflictsFilter {
padding: 0 16px;
height: 48px;
}
/* Panel */
#main {
@@ -1,16 +1,3 @@
<!-- Fires the following events:
loot-editor-open
loot-filter-conflicts
detail: boolean
True if the filter has been activated.
loot-copy-metadata
loot-clear-metadata
-->
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../../bower_components/iron-flex-layout/iron-flex-layout.html">
@@ -104,13 +91,6 @@ loot-clear-metadata
#clearMetadata {
cursor: pointer;
}
#showOnlyConflicts {
min-height: 48px;
padding: 0 16px;
font-weight: 500;
--paper-toggle-button-label-spacing: 16px;
white-space: nowrap;
}
[hidden] {
display: none;
}
@@ -135,7 +115,6 @@ loot-clear-metadata
<paper-menu-button id="menu" horizontal-align="right">
<paper-icon-button icon="more-vert" class="dropdown-trigger"></paper-icon-button>
<loot-menu class="dropdown-content">
<paper-toggle-button id="showOnlyConflicts" checked="{{data.isConflictFilterChecked}}">Show Only Conflicts</paper-toggle-button>
<paper-icon-item id="editMetadata">
<iron-icon icon="create" item-icon></iron-icon>
Edit Metadata
@@ -177,14 +156,12 @@ loot-clear-metadata
},
attached() {
this.$.showOnlyConflicts.addEventListener('change', this._onShowOnlyConflicts);
this.$.editMetadata.addEventListener('click', this.onShowEditor);
this.$.copyMetadata.addEventListener('click', this._onCopyMetadata);
this.$.clearMetadata.addEventListener('click', this._onClearMetadata);
},
detached() {
this.$.showOnlyConflicts.removeEventListener('change', this._onShowOnlyConflicts);
this.$.editMetadata.removeEventListener('click', this.onShowEditor);
this.$.copyMetadata.removeEventListener('click', this._onCopyMetadata);
this.$.clearMetadata.removeEventListener('click', this._onClearMetadata);
@@ -201,9 +178,6 @@ loot-clear-metadata
this.$.isMaster.hidden = !this.data.isMaster;
this.$.isEmpty.hidden = !this.data.isEmpty;
this.$.loadsArchive.hidden = !this.data.loadsArchive;
/* Also set highlight if the conflict filter is active. */
this.classList.toggle('highlight', this.data.isConflictFilterChecked);
}
},
@@ -256,9 +230,6 @@ loot-clear-metadata
this.dispatchEvent(new CustomEvent('iron-resize', { bubbles: true }));
}
}
/* Update the conflict filter toggle state. */
this.$.showOnlyConflicts.checked = this.data.isConflictFilterChecked;
}
},
@@ -282,13 +253,6 @@ loot-clear-metadata
}));
},
_onShowOnlyConflicts(evt) {
evt.target.dispatchEvent(new CustomEvent('loot-filter-conflicts', {
detail: evt.currentTarget.checked,
bubbles: true,
}));
},
_onCopyMetadata(evt) {
evt.target.dispatchEvent(new CustomEvent('loot-copy-metadata', {
bubbles: true,
+19
View File
@@ -27,6 +27,7 @@
<link rel="import" href="../../../bower_components/neon-animation/animations/fade-out-animation.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item-shared-styles.html">
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
@@ -45,6 +46,7 @@
<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="../../../bower_components/paper-toolbar/paper-toolbar.html">
<link rel="import" href="../../../bower_components/paper-tooltip/paper-tooltip.html">
<style is="custom-style">
div[drawer],
div[main] {
@@ -81,6 +83,11 @@
@apply(--layout-horizontal);
@apply(--layout-center);
}
#conflictsFilter {
--paper-dropdown-menu: {
display: block;
}
}
/* Paper element styling.
Colour variables are from Polymer's paper-styles element. */
@@ -156,6 +163,14 @@
</head>
<!-- oncontextmenu attribute disables the right-click menu. -->
<body oncontextmenu="return false" unresolved>
<!-- This style element needs to be here so that it loads after the Vulcanized
imports, which are placed in a div that is the first child of body. -->
<style is="custom-style" include="paper-item-shared-styles">
.paper-item {
display: flex;
align-items: center;
}
</style>
<paper-drawer-panel id="container" drawer-width="33%">
<div drawer>
<paper-toolbar>
@@ -187,6 +202,10 @@
<paper-checkbox id="hideMessagelessPlugins">Hide messageless plugins</paper-checkbox>
<paper-input id="contentFilter" label="Filter content" no-label-float></paper-input>
<paper-tooltip for="contentFilter" position="bottom">Press Enter or click outside the input to set the filter.</paper-tooltip>
<loot-dropdown-menu id="conflictsFilter" label="Show only conflicting plugins for" vertical-align="bottom">
<div class="paper-item" value="">No plugin</div>
<!-- Generated <div> plugin elements go here. -->
</loot-dropdown-menu>
<div id="filtersSpacer"></div>
<div class="divider"></div>
<div id="hiddenPlugins">
+12 -23
View File
@@ -34,6 +34,9 @@ function onChangeGame(evt) {
const gameInfo = JSON.parse(result, loot.Plugin.fromJson);
loot.game = new loot.Game(gameInfo, loot.l10n);
/* Re-initialise conflicts filter plugin list. */
loot.Filters.fillConflictsFilterList(loot.game.plugins);
/* Now update virtual lists. */
filterPluginData(loot.game.plugins, loot.filters);
@@ -69,7 +72,7 @@ function onUpdateMasterlist() {
}).catch(loot.handlePromiseError);
}
function onSortPlugins() {
if (undoConflictsFilter()) {
if (loot.filters.deactivateConflictsFilter()) {
/* Conflicts filter was undone, update the displayed cards. */
filterPluginData(loot.game.plugins, loot.filters);
}
@@ -231,6 +234,9 @@ function onContentRefresh() {
const game = JSON.parse(result, loot.Plugin.fromJson);
loot.game = new loot.Game(game, loot.l10n);
/* Re-initialise conflicts filter plugin list. */
loot.Filters.fillConflictsFilterList(loot.game.plugins);
/* Reapply filters. */
filterPluginData(loot.game.plugins, loot.filters);
@@ -402,32 +408,15 @@ function onEditorClose(evt) {
loot.state.exitEditingState();
}).catch(loot.handlePromiseError);
}
function undoConflictsFilter() {
const wasConflictsFilterEnabled = loot.filters.deactivateConflictsFilter();
/* Deactivate any existing plugin conflict filter. */
loot.game.plugins.forEach((plugin) => {
plugin.isConflictFilterChecked = false;
});
/* Un-highlight any existing filter plugin. */
const cards = document.getElementById('main').getElementsByTagName('loot-plugin-card');
for (let i = 0; i < cards.length; ++i) {
cards[i].classList.toggle('highlight', false);
}
return wasConflictsFilterEnabled;
}
function onConflictsFilter(evt) {
/* Deactivate any existing plugin conflict filter. */
undoConflictsFilter();
/* evt.detail is true if the filter has been activated. */
if (evt.detail) {
evt.target.data.isConflictFilterChecked = true;
evt.target.classList.toggle('highlight', true);
loot.filters.deactivateConflictsFilter();
/* evt.currentTarget.value is the name of the target plugin, or an empty string
if the filter has been deactivated. */
if (evt.currentTarget.value) {
/* Now get conflicts for the plugin. */
loot.Dialog.showProgress(loot.l10n.translate('Identifying conflicting plugins...'));
loot.filters.activateConflictsFilter(evt.target.getName()).then((plugins) => {
loot.filters.activateConflictsFilter(evt.currentTarget.value).then((plugins) => {
plugins.forEach((plugin) => {
const gamePlugin = loot.game.plugins.find(item => item.name === plugin.name);
if (gamePlugin) {
+19
View File
@@ -94,4 +94,23 @@
return plugins;
}).catch(handlePromiseError);
}
static fillConflictsFilterList(plugins) {
const list = document.getElementById('conflictsFilter');
/* Remove any existing plugin items. */
while (list.children.length > 1) {
list.removeChild(list.lastElementChild);
}
plugins.forEach(plugin => {
const item = document.createElement('div');
item.className = 'paper-item';
item.setAttribute('value', plugin.name);
item.textContent = plugin.name;
list.appendChild(item);
});
}
}));
+2 -3
View File
@@ -49,10 +49,8 @@
document.getElementById('hideInactivePlugins').addEventListener('change', onSidebarFilterToggle);
document.getElementById('hideAllPluginMessages').addEventListener('change', onSidebarFilterToggle);
document.getElementById('hideMessagelessPlugins').addEventListener('change', onSidebarFilterToggle);
document.body.addEventListener('loot-filter-conflicts', onConflictsFilter);
/* Set up event handlers for content filter. */
document.getElementById('contentFilter').addEventListener('change', onSidebarFilterToggle);
document.getElementById('conflictsFilter').addEventListener('iron-select', onConflictsFilter);
/* Set up handlers for buttons. */
document.getElementById('redatePluginsButton').addEventListener('click', onRedatePlugins);
@@ -231,6 +229,7 @@
appData.game = new Game(game, appData.l10n);
document.getElementById('cardsNav').items = appData.game.plugins;
document.getElementById('pluginCardList').items = appData.game.plugins;
appData.Filters.fillConflictsFilterList(appData.game.plugins);
applyEnabledFilters(appData.filters, appData.settings, appData.game.plugins);
Dialog.closeProgress();
});
-1
View File
@@ -185,7 +185,6 @@
/* UI state variables */
this.id = this.name.replace(/\s+/g, '');
this._isEditorOpen = false;
this.isConflictFilterChecked = false;
this._isSearchResult = false;
}
+2 -1
View File
@@ -20,7 +20,6 @@
pluginCard.querySelector('paper-tooltip[for=loadsArchive]').textContent = l10n.translate('Loads Archive');
pluginCard.querySelector('paper-tooltip[for=hasUserEdits]').textContent = l10n.translate('Has User Metadata');
pluginCard.getElementById('showOnlyConflicts').textContent = l10n.translate('Show Only Conflicts');
pluginCard.getElementById('editMetadata').lastChild.textContent = l10n.translate('Edit Metadata');
pluginCard.getElementById('copyMetadata').lastChild.textContent = l10n.translate('Copy Metadata');
pluginCard.getElementById('clearMetadata').lastChild.textContent = l10n.translate('Clear User Metadata');
@@ -202,6 +201,8 @@
document.getElementById('hideAllPluginMessages').textContent = l10n.translate('Hide all plugin messages');
document.getElementById('hideInactivePlugins').textContent = l10n.translate('Hide inactive plugins');
document.getElementById('hideMessagelessPlugins').textContent = l10n.translate('Hide messageless plugins');
document.getElementById('conflictsFilter').label = l10n.translate('Show only conflicting plugins for');
document.getElementById('conflictsFilter').firstElementChild.textContent = l10n.translate('No plugin');
document.getElementById('hiddenPluginsTxt').textContent = l10n.translate('Hidden plugins:');
document.getElementById('hiddenMessagesTxt').textContent = l10n.translate('Hidden messages:');
}
@@ -45,9 +45,6 @@
function onEditorClose() {
alert('Editor closed');
}
function onFilterConflicts() {
alert('Conflicts filter toggle clicked');
}
function onCopyMetadata() {
alert('Copy metadata button clicked');
}
@@ -77,7 +74,6 @@
document.addEventListener('loot-editor-open', onEditorOpen);
document.addEventListener('loot-editor-close', onEditorClose);
document.addEventListener('loot-filter-conflicts', onFilterConflicts);
document.addEventListener('loot-copy-metadata', onCopyMetadata);
document.addEventListener('loot-clear-metadata', onClearMetadata);
-1
View File
@@ -408,7 +408,6 @@ describe('Game', () => {
id: '',
_isEditorOpen: true,
isConflictFilterChecked: true,
_isSearchResult: true,
}];
-6
View File
@@ -262,12 +262,6 @@ describe('Plugin', () => {
plugin.isEditorOpen.should.be.false();
});
it('should set isConflictFilterChecked to false', () => {
const plugin = new loot.Plugin({ name: 'test' });
plugin.isConflictFilterChecked.should.be.false();
});
it('should set isSearchResult to false', () => {
const plugin = new loot.Plugin({ name: 'test' });