From 304d078411ef4595a0be8b3f2727d187793f8e53 Mon Sep 17 00:00:00 2001 From: Can Saner Date: Mon, 27 Mar 2017 20:29:17 +0300 Subject: [PATCH 1/2] - added category plugin that enables adding category for the filters. --- examples/index.html | 986 +++++++++++++++---------------- src/plugins/category/plugin.js | 470 +++++++++++++++ src/plugins/category/plugin.scss | 9 + 3 files changed, 957 insertions(+), 508 deletions(-) create mode 100644 src/plugins/category/plugin.js create mode 100644 src/plugins/category/plugin.scss diff --git a/examples/index.html b/examples/index.html index 6704b236..5d4d4ca6 100644 --- a/examples/index.html +++ b/examples/index.html @@ -16,9 +16,7 @@ @@ -30,55 +28,46 @@ -

jQuery QueryBuilder - Example -

+

jQuery QueryBuilder Example

- - + +
@@ -86,9 +75,7 @@

jQuery QueryBuilder
- +
@@ -124,528 +111,511 @@

Output

- - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/plugins/category/plugin.js b/src/plugins/category/plugin.js new file mode 100644 index 00000000..7f3316b3 --- /dev/null +++ b/src/plugins/category/plugin.js @@ -0,0 +1,470 @@ +/*! + * jQuery QueryBuilder Category + * Enables adding category for the filters. + * Author: Can Saner + */ + +// Selectors added for Category Plugin +// =============================== +Selectors.category_container = '.rule-category-container'; +Selectors.rule_category = '.rule-category-container [name$=_category]'; +Model.defineModelProperties(Rule, ['category']); + +// DEFAULT CONFIG +// =============================== +QueryBuilder.defaults({ + categories: [], + sort_categories: false, + display_empty_category: true, + default_category: null, + templates: { + categorySelect: '\ + {{ var optgroup = null; }} \ + ' + } +}); + +QueryBuilder.define('category', function(options) { + var self = this; + + // SETTINGS SHORTCUT + this.categories = this.settings.categories; + + // CHECK ALL CATEGORIES + this.categories = this.checkCategories(this.categories); + /** + * Bind events + */ + this.on('afterInit', function() { + // change event bindind for rule category select box + self.$el.on('change.queryBuilder', Selectors.rule_category, function() { + var $rule = $(this).closest(Selectors.rule_container); + Model($rule).category = self.getCategoryById($(this).val()); + }); + + // update event binding for category field of rule model + self.model.on('update', function(e, node, field, value, oldValue) { + if (node instanceof Rule && field === 'category') { + self.updateRuleCategory(node, oldValue); + } + }); + }); + + /** + * Initialize category property of a rule and create rule categories select box when new rule is added + */ + this.on('afterAddRule', function(e, rule) { + rule.__.category = null; + self.createRuleCategories(rule); + }); + + /** + * Modify templates + */ + this.on('getRuleTemplate.filter', function(h) { + var $h = $(h.value); + $h.find(Selectors.filter_container).before('
'); + h.value = $h.prop('outerHTML'); + }); + + // init selectpicker + this.on('afterCreateRuleCategories', function(e, rule) { + if (self.settings.plugins && self.settings.plugins['bt-selectpicker']) { + self.$el.find(Selectors.rule_category).removeClass('form-control').selectpicker(options); + } + }); + + // update selectpicker on change + this.on('afterUpdateRuleCategory', function(e, rule) { + if (self.settings.plugins && self.settings.plugins['bt-selectpicker']) { + self.$el.find(Selectors.rule_category).selectpicker('render'); + } + }); + + /** + * Export "category" to JSON + */ + this.on('ruleToJson.filter', function(e, rule) { + e.value.category = rule.category.id; + }); + + /** + * Read "category" from JSON + */ + this.on('jsonToRule.filter', function(e, json) { + //e.value.category = e.builder.getCategoryById(json.category ? json.category : '-1'); + }); +}, { + default_no_category: false, + container: 'body', + style: 'btn-inverse btn-xs', + width: 'auto', + showIcon: false +}); + +// keep a pointer to the original addRule method +var originaAddRule = QueryBuilder.prototype.addRule; + +QueryBuilder.extend({ + /** //OVERRIDEN to change flow of creating rule filter when new rule is added, prevented filter select creation and created category select instead. + * Add a new rule + * @param parent {Group} + * @param data {mixed,optional} rule custom data + * @param flags {object,optional} flags to apply to the rule + * @return rule {Rule} + */ + addRule: function(parent, data, flags) { + //originaAddRule.call(this); + var e = this.trigger('beforeAddRule', parent); + if (e.isDefaultPrevented()) { + return null; + } + + var rule_id = this.nextRuleId(); + var $rule = $(this.getRuleTemplate(rule_id)); + var model = parent.addRule($rule); + + if (data !== undefined) { + model.data = data; + } + + model.__.flags = $.extend({}, this.settings.default_rule_flags, flags); + + this.trigger('afterAddRule', model); + + this.createRuleCategories(model); + + if (this.settings.default_category || !this.settings.display_empty_category) { + model.category = this.change('getDefaultCategory', + this.getCategoryById(this.settings.default_category || this.categories[0].id), + model + ); + } + + return model; + }, + + /** //OVERRIDEN to change flow of creating rule filters + * Create the filters HTML + * @param rule {Rule} + * @param categories {array} + * @return {string} + */ + getRuleCategorySelect: function(rule, categories) { + var h = this.templates.categorySelect({ + builder: this, + rule: rule, + categories: categories, + icons: this.icons, + lang: this.lang, + settings: this.settings, + translate: this.translateLabel + }); + + return this.change('getRuleCategorySelect', h, rule); + }, + + /** + * Create the categories \ + categories: [], + sort_categories: false, + display_empty_category: true, + default_category: null, + templates: { + categorySelect: '\ + {{ var optgroup = null; }} \ + ' - } + ' + } }); QueryBuilder.define('category', function(options) { @@ -56,7 +56,7 @@ QueryBuilder.define('category', function(options) { Model($rule).category = self.getCategoryById($(this).val()); }); - // update event binding for category field of rule model + // update event binding for category field of rule model self.model.on('update', function(e, node, field, value, oldValue) { if (node instanceof Rule && field === 'category') { self.updateRuleCategory(node, oldValue); @@ -101,13 +101,6 @@ QueryBuilder.define('category', function(options) { this.on('ruleToJson.filter', function(e, rule) { e.value.category = rule.category.id; }); - - /** - * Read "category" from JSON - */ - this.on('jsonToRule.filter', function(e, json) { - //e.value.category = e.builder.getCategoryById(json.category ? json.category : '-1'); - }); }, { default_no_category: false, container: 'body', @@ -116,9 +109,6 @@ QueryBuilder.define('category', function(options) { showIcon: false }); -// keep a pointer to the original addRule method -var originaAddRule = QueryBuilder.prototype.addRule; - QueryBuilder.extend({ /** //OVERRIDEN to change flow of creating rule filter when new rule is added, prevented filter select creation and created category select instead. * Add a new rule @@ -127,8 +117,7 @@ QueryBuilder.extend({ * @param flags {object,optional} flags to apply to the rule * @return rule {Rule} */ - addRule: function(parent, data, flags) { - //originaAddRule.call(this); + addRule: function(parent, data, flags) { var e = this.trigger('beforeAddRule', parent); if (e.isDefaultPrevented()) { return null; @@ -253,9 +242,9 @@ QueryBuilder.extend({ } if (!item.empty) { - if (item.category){ + if (item.category) { model.category = self.getCategoryById(item.category ? item.category : '-1'); - if (self.isFilterOKForCategory(item.id, item.category)){ + if (self.isFilterOKForCategory(item.id, item.category)) { model.filter = self.getFilterById(item.id, !options.allow_invalid); @@ -298,17 +287,16 @@ QueryBuilder.extend({ isFilterOKForCategory: function(filterId, categoryId) { var filtersOfCategory = this.getFiltersForCategoryId(categoryId); - for (var i = 0; i < filtersOfCategory.length; i++) { - if (filtersOfCategory[i].id == filterId){ + for (var i = 0; i < filtersOfCategory.length; i++) { + if (filtersOfCategory[i].id == filterId) { return true; } } - return false; }, /** - * Filter filters array according to categories of filters + * Filter filters array according to categories of filters * @param categoryId {string} * @return {array} */ @@ -317,7 +305,7 @@ QueryBuilder.extend({ var category = this.getCategoryById(categoryId); this.filters.forEach(function(filter, i) { category.filters.forEach(function(filterOfCategory, i) { - if (filter.id == filterOfCategory){ + if (filter.id == filterOfCategory) { filtersOfCategory.push(filter); } }, this); @@ -355,7 +343,6 @@ QueryBuilder.extend({ var $categorySelect = $(this.getRuleCategorySelect(rule, categories)); rule.$el.find(Selectors.category_container).html($categorySelect); - this.trigger('afterCreateRuleCategories', rule); }, @@ -467,4 +454,4 @@ QueryBuilder.extend({ return categories; } -}); \ No newline at end of file +}); diff --git a/src/plugins/category/plugin.scss b/src/plugins/category/plugin.scss index 52aabdaf..594c13ff 100644 --- a/src/plugins/category/plugin.scss +++ b/src/plugins/category/plugin.scss @@ -6,4 +6,4 @@ vertical-align: middle; } } -} \ No newline at end of file +}