Added a visibility filter for `UiItemListFilter`
authorAlexander Ebert <ebert@woltlab.com>
Mon, 23 Oct 2017 16:49:17 +0000 (18:49 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 23 Oct 2017 16:49:17 +0000 (18:49 +0200)
Closes #2451

wcfsetup/install/files/acp/templates/scrollablePageCheckboxList.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/Filter.js
wcfsetup/install/files/style/ui/scrollableCheckboxList.scss
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 7b6198a35d6e83316e503af8d22c5bc2708ef126..ffeaa32db56afb138407742f36687fbf28659207 100644 (file)
@@ -1,9 +1,13 @@
 <script data-relocate="true">
        require(['Language', 'WoltLabSuite/Core/Ui/ItemList/Filter'], function(Language, UiItemListFilter) {
                Language.addObject({
+                       'wcf.global.filter.button.visibility': '{lang}wcf.global.filter.button.visibility{/lang}',
                        'wcf.global.filter.button.clear': '{lang}wcf.global.filter.button.clear{/lang}',
                        'wcf.global.filter.error.noMatches': '{lang}wcf.global.filter.error.noMatches{/lang}',
-                       'wcf.global.filter.placeholder': '{lang}wcf.global.filter.placeholder{/lang}'
+                       'wcf.global.filter.placeholder': '{lang}wcf.global.filter.placeholder{/lang}',
+                       'wcf.global.filter.visibility.activeOnly': '{lang}wcf.global.filter.visibility.activeOnly{/lang}',
+                       'wcf.global.filter.visibility.highlightActive': '{lang}wcf.global.filter.visibility.highlightActive{/lang}',
+                       'wcf.global.filter.visibility.showAll': '{lang}wcf.global.filter.visibility.showAll{/lang}'
                });
                
                new UiItemListFilter('{@$pageCheckboxListContainerID}');
index 42a7de9a2882fd8db68729560f60708b75463e30..1b4b44760ac9334b92f822e57b572259e9cca0d5 100644 (file)
@@ -6,7 +6,7 @@
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module     WoltLabSuite/Core/Ui/ItemList/Filter
  */
-define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util'], function (Core, EventKey, Language, List, StringUtil, DomUtil) {
+define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util', 'Ui/SimpleDropdown'], function (Core, EventKey, Language, List, StringUtil, DomUtil, UiSimpleDropdown) {
        "use strict";
        
        if (!COMPILER_TARGET_DEFAULT) {
@@ -15,7 +15,10 @@ define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util'], funct
                        init: function() {},
                        _buildItems: function() {},
                        _prepareItem: function() {},
-                       _keyup: function() {}
+                       _keyup: function() {},
+                       _toggleVisibility: function () {},
+                       _setupVisibilityFilter: function () {},
+                       _setVisibility: function () {}
                };
                return Fake;
        }
@@ -50,6 +53,8 @@ define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util'], funct
                                throw new Error("Filter only works with elements with the CSS class 'scrollableCheckboxList'.");
                        }
                        
+                       elData(element, 'filter', 'showAll');
+                       
                        var container = elCreate('div');
                        container.className = 'itemListFilter';
                        
@@ -81,12 +86,22 @@ define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util'], funct
                                this.reset();
                        }).bind(this));
                        
+                       var visibilityButton = elCreate('a');
+                       visibilityButton.href = '#';
+                       visibilityButton.className = 'button inputSuffix jsTooltip';
+                       visibilityButton.title = Language.get('wcf.global.filter.button.visibility');
+                       visibilityButton.innerHTML = '<span class="icon icon16 fa-eye"></span>';
+                       visibilityButton.addEventListener(WCF_CLICK_EVENT, this._toggleVisibility.bind(this));
+                       
                        inputAddon.appendChild(input);
                        inputAddon.appendChild(clearButton);
+                       inputAddon.appendChild(visibilityButton);
                        
                        container.appendChild(inputAddon);
                        
                        this._container = container;
+                       this._dropdown = null;
+                       this._dropdownId = '';
                        this._element = element;
                        this._input = input;
                        this._items = null;
@@ -210,6 +225,120 @@ define(['Core', 'EventKey', 'Language', 'List', 'StringUtil', 'Dom/Util'], funct
                                        DomUtil.insertAfter(innerError, this._container);
                                }
                        }
+               },
+               
+               /**
+                * Toggles the visibility mode for marked items.
+                *
+                * @param       {Event}         event
+                * @protected
+                */
+               _toggleVisibility: function (event) {
+                       event.preventDefault();
+                       event.stopPropagation();
+                       
+                       var button = event.currentTarget;
+                       if (this._dropdown === null) {
+                               var dropdown = elCreate('ul');
+                               dropdown.className = 'dropdownMenu';
+                               
+                               ['activeOnly', 'highlightActive', 'showAll'].forEach((function (type) {
+                                       var link = elCreate('a');
+                                       elData(link, 'type', type);
+                                       link.href = '#';
+                                       link.textContent = Language.get('wcf.global.filter.visibility.' + type);
+                                       link.addEventListener(WCF_CLICK_EVENT, this._setVisibility.bind(this));
+                                       
+                                       var li = elCreate('li');
+                                       li.appendChild(link);
+                                       
+                                       if (type === 'showAll') {
+                                               li.className = 'active';
+                                               
+                                               var divider = elCreate('li');
+                                               divider.className = 'dropdownDivider';
+                                               dropdown.appendChild(divider);
+                                       }
+                                       
+                                       dropdown.appendChild(li);
+                               }).bind(this));
+                               
+                               UiSimpleDropdown.initFragment(button, dropdown);
+                               
+                               // add `active` classes required for the visibility filter
+                               this._setupVisibilityFilter();
+                               
+                               this._dropdown = dropdown;
+                               this._dropdownId = button.id;
+                       }
+                       
+                       UiSimpleDropdown.toggleDropdown(button.id, button);
+               },
+               
+               /**
+                * Set-ups the visibility filter by assigning an active class to the
+                * list items that hold the checkboxes and observing the checkboxes
+                * for any changes.
+                *
+                * This process involves quite a few DOM changes and new event listeners,
+                * therefore we'll delay this until the filter has been accessed for
+                * the first time, because none of these changes matter before that.
+                *
+                * @protected
+                */
+               _setupVisibilityFilter: function () {
+                       var nextSibling = this._element.nextSibling;
+                       var parent = this._element.parentNode;
+                       var scrollTop = this._element.scrollTop;
+                       
+                       // mass-editing of DOM elements is slow while they're part of the document 
+                       var fragment = document.createDocumentFragment();
+                       fragment.appendChild(this._element);
+                       
+                       elBySelAll('li', this._element, function(li) {
+                               var checkbox = elBySel('input[type="checkbox"]', li);
+                               if (checkbox.checked) li.classList.add('active');
+                               
+                               checkbox.addEventListener('change', function() {
+                                       li.classList[(checkbox.checked ? 'add' : 'remove')]('active');
+                               });
+                       });
+                       
+                       // re-insert the modified DOM
+                       parent.insertBefore(this._element, nextSibling);
+                       this._element.scrollTop = scrollTop;
+               },
+               
+               /**
+                * Sets the visibility of marked items.
+                *
+                * @param       {Event}         event
+                * @protected
+                */
+               _setVisibility: function (event) {
+                       event.preventDefault();
+                       
+                       var link = event.currentTarget;
+                       var type = elData(link, 'type');
+                       
+                       UiSimpleDropdown.close(this._dropdownId);
+                       
+                       if (elData(this._element, 'filter') === type) {
+                               // filter did not change
+                               return;
+                       }
+                       
+                       elData(this._element, 'filter', type);
+                       
+                       elBySel('.active', this._dropdown).classList.remove('active');
+                       link.parentNode.classList.add('active');
+                       
+                       var button = elById(this._dropdownId);
+                       button.classList[(type === 'showAll' ? 'remove' : 'add')]('active');
+                       
+                       var icon = elBySel('.icon', button);
+                       icon.classList[(type === 'showAll' ? 'add' : 'remove')]('fa-eye');
+                       icon.classList[(type === 'showAll' ? 'remove' : 'add')]('fa-eye-slash');
                }
        };
        
index 9114516d189c64ccf6b54316979f6e066aefac4b..34d65f45be76e62229245df4acb09d15b61a5a60 100644 (file)
        > .inputAddon {
                margin-top: 5px;
        }
+       
+       .scrollableCheckboxList {
+               &[data-filter="highlightActive"] > li:not(.active) {
+                       opacity: .6;
+               }
+               
+               &[data-filter="activeOnly"] > li:not(.active) {
+                       display: none;
+               }
+       }
 }
index 72e2d1c4e50eb8fa30d604396d5d0cd41c932e33..f6e1ab98e5f75a4139e8df136490552ecad2c22e 100644 (file)
@@ -2897,8 +2897,12 @@ Fehler sind beispielsweise:
                <item name="wcf.global.exception.title"><![CDATA[Ein Fehler ist aufgetreten]]></item>
                <item name="wcf.global.exception.subtitle"><![CDATA[Interner Fehlercode: <span class="exceptionInlineCodeWrapper"><span class="exceptionInlineCode">{$exceptionID}</span></span>]]></item>
                <item name="wcf.global.filter.button.clear"><![CDATA[Filter löschen]]></item>
+               <item name="wcf.global.filter.button.visibility"><![CDATA[Ansicht filtern]]></item>
                <item name="wcf.global.filter.error.noMatches"><![CDATA[Keine Übereinstimmungen gefunden.]]></item>
                <item name="wcf.global.filter.placeholder"><![CDATA[Nach Name filtern]]></item>
+               <item name="wcf.global.filter.visibility.activeOnly"><![CDATA[Nur markierte Einträge anzeigen]]></item>
+               <item name="wcf.global.filter.visibility.highlightActive"><![CDATA[Aktive Einträge hervorheben]]></item>
+               <item name="wcf.global.filter.visibility.showAll"><![CDATA[Alles anzeigen]]></item>
                <item name="wcf.global.success"><![CDATA[Die Aktion wurde erfolgreich ausgeführt.]]></item>
                <item name="wcf.global.success.add"><![CDATA[Der Eintrag wurde gespeichert.]]></item>
                <item name="wcf.global.success.edit"><![CDATA[Die Änderungen wurden gespeichert.]]></item>
index 367c502493635161baea6f584d3322eda4b0ad0d..b7edfce4e10c36509c2c1be24756c92aaf1c2e3a 100644 (file)
@@ -2845,8 +2845,12 @@ Errors are:
                <item name="wcf.global.exception.title"><![CDATA[An error has occured]]></item>
                <item name="wcf.global.exception.subtitle"><![CDATA[Internal error code: <span class="exceptionInlineCodeWrapper"><span class="exceptionInlineCode">{$exceptionID}</span></span>]]></item>
                <item name="wcf.global.filter.button.clear"><![CDATA[Clear Filter]]></item>
+               <item name="wcf.global.filter.button.visibility"><![CDATA[Visibility Filter]]></item>
                <item name="wcf.global.filter.error.noMatches"><![CDATA[Filter does not match anything.]]></item>
                <item name="wcf.global.filter.placeholder"><![CDATA[Filter by name]]></item>
+               <item name="wcf.global.filter.visibility.activeOnly"><![CDATA[Show marked items only]]></item>
+               <item name="wcf.global.filter.visibility.highlightActive"><![CDATA[Highlight marked items]]></item>
+               <item name="wcf.global.filter.visibility.showAll"><![CDATA[Show all]]></item>
                <item name="wcf.global.success"><![CDATA[The action completed successfully.]]></item>
                <item name="wcf.global.success.add"><![CDATA[The entry has been saved.]]></item>
                <item name="wcf.global.success.edit"><![CDATA[Your changes have been saved.]]></item>