Added a flexible category list supporting up to 3 levels
authorAlexander Ebert <ebert@woltlab.com>
Fri, 6 Mar 2015 10:58:07 +0000 (11:58 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 6 Mar 2015 10:58:07 +0000 (11:58 +0100)
com.woltlab.wcf/templates/flexibleCategoryList.tpl [new file with mode: 0644]
wcfsetup/install/files/js/WCF.js
wcfsetup/install/files/style/layout.less

diff --git a/com.woltlab.wcf/templates/flexibleCategoryList.tpl b/com.woltlab.wcf/templates/flexibleCategoryList.tpl
new file mode 100644 (file)
index 0000000..723678f
--- /dev/null
@@ -0,0 +1,38 @@
+{if !$flexibleCategoryList|isset}{assign var=flexibleCategoryList value=$categoryList}{/if}
+{if !$flexibleCategoryListName|isset}{assign var=flexibleCategoryListName value='categoryIDs'}{/if}
+{if !$flexibleCategoryListID|isset}{assign var=flexibleCategoryListID value='flexibleCategoryList'}{/if}
+{if !$flexibleCategoryListSelectedIDs|isset}{assign var=flexibleCategoryListSelectedIDs value=$categoryIDs}{/if}
+<ol class="flexibleCategoryList" id="{$flexibleCategoryListID}">
+       {foreach from=$flexibleCategoryList item=categoryItem}
+               <li>
+                       <div class="containerHeadline">
+                               <h3><label{if $categoryItem->getDescription()} class="jsTooltip" title="{$categoryItem->getDescription()}"{/if}><input type="checkbox" name="{$flexibleCategoryListName}[]" value="{@$categoryItem->categoryID}" class="jsCategory"{if $categoryItem->categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$categoryItem->getTitle()}</label></h3>
+                       </div>
+                       
+                       {if $categoryItem->hasChildren()}
+                               <ol>
+                                       {foreach from=$categoryItem item=subCategoryItem}
+                                               <li>
+                                                       <label{if $subCategoryItem->getDescription()} class="jsTooltip" title="{$subCategoryItem->getDescription()}"{/if} style="font-size: 1rem;"><input type="checkbox" name="{$flexibleCategoryListName}[]" value="{@$subCategoryItem->categoryID}" class="jsChildCategory"{if $subCategoryItem->categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$subCategoryItem->getTitle()}</label>
+                                                       
+                                                       {if $subCategoryItem->hasChildren()}
+                                                               <ol class="dtdesign">
+                                                                       {foreach from=$subCategoryItem item=subSubCategoryItem}
+                                                                               <li>
+                                                                                       <label{if $subSubCategoryItem->getDescription()} class="jsTooltip" title="{$subSubCategoryItem->getDescription()}"{/if}><input type="checkbox" name="{$flexibleCategoryListName}[]" value="{@$subSubCategoryItem->categoryID}" class="jsSubChildCategory"{if $subSubCategoryItem->categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$subSubCategoryItem->getTitle()}</label>
+                                                                               </li>
+                                                                       {/foreach}
+                                                               </ol>
+                                                       {/if}
+                                               </li>
+                                       {/foreach}
+                               </ol>
+                       {/if}
+               </li>
+       {/foreach}
+</ol>
+<script data-relocate="true">
+       $(function() {
+               new WCF.Category.FlexibleCategoryList('{$flexibleCategoryListID}');
+       });
+</script>
index 1a1b9e2c1f1b25afc737e4edd41f58e0202fa4dc..f0ac00885b55efc31682539ca8cf3323b08d73f9 100755 (executable)
@@ -12188,6 +12188,143 @@ WCF.Category.NestedList = Class.extend({
        }
 });
 
+/**
+ * Handles selection of categories.
+ */
+WCF.Category.FlexibleCategoryList = Class.extend({
+       /**
+        * category list container
+        * @var jQuery
+        */
+       _list: null,
+       
+       /**
+        * list of children per category id
+        * @var object<integer>
+        */
+       _categories: { },
+       
+       init: function(elementID) {
+               this._list = $('#' + elementID);
+               
+               this._buildStructure();
+               
+               if ($.browser.chrome) {
+                       this._resize();
+                       
+                       $(window).resize(this._resize.bind(this));
+               }
+       },
+       
+       _buildStructure: function() {
+               var self = this;
+               this._list.find('.jsCategory').each(function(i, category) {
+                       var $category = $(category).change(self._updateSelection.bind(self));
+                       var $categoryID = parseInt($category.val());
+                       var $childCategories = [ ];
+                       
+                       $category.parents('li:eq(0)').find('.jsChildCategory').each(function(j, childCategory) {
+                               var $childCategory = $(childCategory);
+                               $childCategory.data('parentCategory', $category).change(self._updateSelection.bind(self));
+                               
+                               var $childCategoryID = parseInt($childCategory.val());
+                               $childCategories.push($childCategory);
+                               
+                               var $subChildCategories = [ ];
+                               
+                               $childCategory.parents('li:eq(0)').find('.jsSubChildCategory').each(function(k, subChildCategory) {
+                                       var $subChildCategory = $(subChildCategory);
+                                       $subChildCategory.data('parentCategory', $childCategory).change(self._updateSelection.bind(self));
+                                       $subChildCategories.push($subChildCategory);
+                               });
+                               
+                               self._categories[$childCategoryID] = $subChildCategories;
+                       });
+                       
+                       self._categories[$categoryID] = $childCategories;
+               });
+       },
+       
+       _resize: function() {
+               var $referenceOffset = -1;
+               var $realBottom = 0;
+               var $items = this._list.children('li');
+               
+               $items.each(function(index, item) {
+                       if ($referenceOffset === -1) {
+                               $referenceOffset = item.offsetLeft;
+                       }
+                       else {
+                               if (index + 1 === $items.length || $items[index + 1].offsetLeft != $referenceOffset) {
+                                       var $item = $(item);
+                                       var $height = $item.outerHeight(true);
+                                       var $offset = $item.position();
+                                       
+                                       $realBottom = Math.max($realBottom, $offset.top + $height);
+                                       $referenceOffset = item.offsetLeft;
+                               }
+                       }
+               });
+               
+               this._list.css('max-height', $realBottom + 'px');
+       },
+       
+       _updateSelection: function(event) {
+               var $category = $(event.currentTarget);
+               var $categoryID = parseInt($category.val());
+               var $parentCategory = $category.data('parentCategory');
+               
+               if ($category.is(':checked')) {
+                       if ($parentCategory) {
+                               $parentCategory.prop('checked', 'checked');
+                               
+                               $parentCategory = $parentCategory.data('parentCategory');
+                               if ($parentCategory) {
+                                       $parentCategory.prop('checked', 'checked');
+                               }
+                       }
+               }
+               else {
+                       // uncheck child categories
+                       if (this._categories[$categoryID]) {
+                               for (var $i = 0, $length = this._categories[$categoryID].length; $i < $length; $i++) {
+                                       var $childCategory = this._categories[$categoryID][$i];
+                                       $childCategory.prop('checked', false);
+                                       
+                                       var $childCategoryID = parseInt($childCategory.val());
+                                       if (this._categories[$childCategoryID]) {
+                                               for (var $j = 0, $innerLength = this._categories[$childCategoryID].length; $j < $innerLength; $j++) {
+                                                       this._categories[$childCategoryID][$j].prop('checked', false);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       // uncheck direct parent if it has no more checked children
+                       if ($parentCategory) {
+                               var $parentCategoryID = parseInt($parentCategory.val());
+                               for (var $i = 0, $length = this._categories[$parentCategoryID].length; $i < $length; $i++) {
+                                       if (this._categories[$parentCategoryID][$i].prop('checked')) {
+                                               // at least one child is checked, break
+                                               return;
+                                       }
+                               }
+                               
+                               $parentCategory = $parentCategory.data('parentCategory');
+                               if ($parentCategory) {
+                                       $parentCategoryID = parseInt($parentCategory.val());
+                                       for (var $i = 0, $length = this._categories[$parentCategoryID].length; $i < $length; $i++) {
+                                               if (this._categories[$parentCategoryID][$i].prop('checked')) {
+                                                       // at least one child is checked, break
+                                                       return;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+});
+
 /**
  * Initializes WCF.Condition namespace.
  */
index 7589b8367d7d1af1efc7159d7c28507b7ed7f60a..78caf220fb4c21052cf5a4c99dd7953e77453980 100644 (file)
@@ -1134,6 +1134,27 @@ html[dir="rtl"] {
        }
 }
 
+.flexibleCategoryList {
+       position: relative;
+       
+       > li {
+               margin-bottom: @wcfGapMedium;
+               
+               > ol {
+                       margin-left: @wcfGapLarge;
+               }
+               
+               > ol > li > ol {
+                       margin-bottom: @wcfGapSmall;
+                       margin-left: @wcfGapLarge;
+                       
+                       > li {
+                               font-size: @wcfSmallFontSize;
+                       }
+               }
+       }
+}
+
 @media all and (min-width: 801px) {
        .nestedCategoryList.doubleColumned {
                > li {
@@ -1155,6 +1176,36 @@ html[dir="rtl"] {
                        clear: left;
                }
        }
+       
+       .flexibleCategoryList {
+               /* WebKit */
+               -webkit-column-count: 2;
+               
+               /* Firefox */
+               -moz-column-count: 2;
+               
+               /* CSS 3 / Internet Explorer */
+               column-count: 2;
+               
+               > li {
+                       /* WebKit */
+                       -webkit-column-break-inside: avoid;
+                       
+                       /* Firefox */
+                       page-break-inside: avoid;
+                       
+                       /* CSS 3 / Internet Explorer */
+                       break-inside: avoid;
+                       
+                       > ol > li > ol {
+                               font-size: 0;
+                               
+                               > li {
+                                       display: inline-block;
+                               }
+                       }
+               }
+       }
 }
 
 .sidebarNestedCategoryList {