From f07b12a5c3b1adc7bcc34acc6dad57465656bba0 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 6 Mar 2015 11:58:07 +0100 Subject: [PATCH] Added a flexible category list supporting up to 3 levels --- .../templates/flexibleCategoryList.tpl | 38 +++++ wcfsetup/install/files/js/WCF.js | 137 ++++++++++++++++++ wcfsetup/install/files/style/layout.less | 51 +++++++ 3 files changed, 226 insertions(+) create mode 100644 com.woltlab.wcf/templates/flexibleCategoryList.tpl diff --git a/com.woltlab.wcf/templates/flexibleCategoryList.tpl b/com.woltlab.wcf/templates/flexibleCategoryList.tpl new file mode 100644 index 0000000000..723678f66f --- /dev/null +++ b/com.woltlab.wcf/templates/flexibleCategoryList.tpl @@ -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} +
    + {foreach from=$flexibleCategoryList item=categoryItem} +
  1. +
    +

    getDescription()} class="jsTooltip" title="{$categoryItem->getDescription()}"{/if}>categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$categoryItem->getTitle()}

    +
    + + {if $categoryItem->hasChildren()} +
      + {foreach from=$categoryItem item=subCategoryItem} +
    1. + getDescription()} class="jsTooltip" title="{$subCategoryItem->getDescription()}"{/if} style="font-size: 1rem;">categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$subCategoryItem->getTitle()} + + {if $subCategoryItem->hasChildren()} +
        + {foreach from=$subCategoryItem item=subSubCategoryItem} +
      1. + getDescription()} class="jsTooltip" title="{$subSubCategoryItem->getDescription()}"{/if}>categoryID|in_array:$flexibleCategoryListSelectedIDs}checked="checked" {/if}/> {$subSubCategoryItem->getTitle()} +
      2. + {/foreach} +
      + {/if} +
    2. + {/foreach} +
    + {/if} +
  2. + {/foreach} +
+ diff --git a/wcfsetup/install/files/js/WCF.js b/wcfsetup/install/files/js/WCF.js index 1a1b9e2c1f..f0ac00885b 100755 --- a/wcfsetup/install/files/js/WCF.js +++ b/wcfsetup/install/files/js/WCF.js @@ -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 + */ + _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. */ diff --git a/wcfsetup/install/files/style/layout.less b/wcfsetup/install/files/style/layout.less index 7589b8367d..78caf220fb 100644 --- a/wcfsetup/install/files/style/layout.less +++ b/wcfsetup/install/files/style/layout.less @@ -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 { -- 2.20.1