├── .babelrc ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.adoc ├── dist ├── example.html ├── excel-bootstrap-table-filter-bundle.js ├── excel-bootstrap-table-filter-bundle.js.map ├── excel-bootstrap-table-filter-bundle.min.js ├── excel-bootstrap-table-filter-bundle.min.js.map └── excel-bootstrap-table-filter-style.css ├── excel-table-filter-example.PNG ├── karma.conf.js ├── package.json ├── rollup.config.js ├── src ├── FilterCollection.ts ├── FilterMenu.ts ├── MenuItem.interface.ts ├── Options.interface.ts ├── example.html ├── excel-bootstrap-table-filter-style.css └── excel-bootstrap-table-filter.ts └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "es2015", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "plugins": [ 11 | "external-helpers" 12 | ] 13 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{ts,js,html}] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.json] 11 | indent_style = space 12 | indent_size = 2 13 | charset = utf-8 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | coverage/ 3 | node_modules/ 4 | !dist/excel-bootstrap-table-filter-style.css 5 | !dist/excel-bootstrap-table-filter-bundle.js 6 | !dist/excel-bootstrap-table-filter-bundle.js.map 7 | !dist/excel-bootstrap-table-filter-bundle.min.js 8 | !dist/excel-bootstrap-table-filter-bundle.min.js.map 9 | !dist/example.html 10 | dist/** -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chester Carmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Excel Bootstrap Table Filter jQuery Plugin 2 | 3 | A jQuery plugin to generate an excel-flavored table-filter. 4 | 5 | [[img-calendar]] 6 | .Excel Table Filter Example 7 | image::excel-table-filter-example.PNG[Calendar, 900, 450, link="https://github.com/chestercharles/excel-bootstrap-table-filter/blob/master/excel-bootstarp-table-filter-example.PNG"] 8 | 9 | == Usage 10 | 11 | . Install the dependencies by running: `npm install`. 12 | . Build the project by running: `npm run build`. 13 | . Add `dist/checkbox-dropdown-plugin.min.js` and `dist/checkbox-dropdown-style.css` to you project 14 | . Render the dropdown to a `#table` and pass an (optional) configuration object: 15 | 16 | $('#table').excelTableFilter({ 17 | columnSelector: '.apply-filter', // (optional) if present, will only select with specified class 18 | sort: true, // (optional) default true 19 | search: true // (optional) default true 20 | captions: Object // (optional) default { a_to_z: 'A to Z', z_to_a: 'Z to A', search: 'Search', select_all: 'Select All' } 21 | }); 22 | . Add 'no-sort', 'no-filter' or 'no-search' to element to customize its dropdown. 23 | 24 | == Support 25 | 26 | This plugin uses some methods like `Array.prototype.map`, `Array.prototype.reduce`, `Array.prototype.indexOf` that may need a polyfill in IE. 27 | 28 | 29 | -------------------------------------------------------------------------------- /dist/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Excel Bootstrap Table Filter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Excel-style Bootstrap Table filter jQuery plugin

22 |
23 |

24 |
25 |
26 |
27 |
28 |
29 |

Table 1

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
AnimalClassCollective NounA Number
BearMammalSleuth1
AntInsectArmy2
SalamanderAmphibianCongress3
OwlBirdParliament4
FrogAmphibianArmy5
SharkFishGam6
KookaburraBirdCackle7
CrowBirdMurder8
ElephantMammalHerd9
BarracudeFishGrist10
102 |
103 |
104 |
105 |
106 |

Table 2

107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
AnimalClassCollective NounA Number
BearMammalSleuth1
AntInsectArmy2
SalamanderAmphibianCongress3
OwlBirdParliament4
FrogAmphibianArmy5
SharkFishGam6
KookaburraBirdCackle7
CrowBirdMurder8
ElephantMammalHerd9
BarracudeFishGrist10
179 |
180 |
181 |
182 |
183 |

Table 3

184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 |
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
Ernst HandelRoland MendelAustria
Island TradingHelen BennettUK
Laughing Bacchus WinecellarsYoshi TannamuriCanada
Magazzini Alimentari RiunitiGiovanni RovelliItaly
225 |
226 |
227 |
228 | 229 | 230 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /dist/excel-bootstrap-table-filter-bundle.js: -------------------------------------------------------------------------------- 1 | (function ($$1) { 2 | 'use strict'; 3 | 4 | $$1 = 'default' in $$1 ? $$1['default'] : $$1; 5 | 6 | var FilterMenu = function () { 7 | function FilterMenu(target, th, column, index, options) { 8 | this.options = options; 9 | this.th = th; 10 | this.column = column; 11 | this.index = index; 12 | this.tds = target.find('tbody tr td:nth-child(' + (this.column + 1) + ')').toArray(); 13 | } 14 | FilterMenu.prototype.initialize = function () { 15 | this.menu = this.dropdownFilterDropdown(); 16 | this.th.appendChild(this.menu); 17 | var $trigger = $(this.menu.children[0]); 18 | var $content = $(this.menu.children[1]); 19 | var $menu = $(this.menu); 20 | $trigger.click(function () { 21 | return $content.toggle(); 22 | }); 23 | $(document).click(function (el) { 24 | if (!$menu.is(el.target) && $menu.has(el.target).length === 0) { 25 | $content.hide(); 26 | } 27 | }); 28 | }; 29 | FilterMenu.prototype.searchToggle = function (value) { 30 | if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = false; 31 | if (value.length === 0) { 32 | this.toggleAll(true); 33 | if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = true; 34 | return; 35 | } 36 | this.toggleAll(false); 37 | this.inputs.filter(function (input) { 38 | return input.value.toLowerCase().indexOf(value.toLowerCase()) > -1; 39 | }).forEach(function (input) { 40 | input.checked = true; 41 | }); 42 | }; 43 | FilterMenu.prototype.updateSelectAll = function () { 44 | if (this.selectAllCheckbox instanceof HTMLInputElement) { 45 | $(this.searchFilter).val(''); 46 | this.selectAllCheckbox.checked = this.inputs.length === this.inputs.filter(function (input) { 47 | return input.checked; 48 | }).length; 49 | } 50 | }; 51 | FilterMenu.prototype.selectAllUpdate = function (checked) { 52 | $(this.searchFilter).val(''); 53 | this.toggleAll(checked); 54 | }; 55 | FilterMenu.prototype.toggleAll = function (checked) { 56 | for (var i = 0; i < this.inputs.length; i++) { 57 | var input = this.inputs[i]; 58 | if (input instanceof HTMLInputElement) input.checked = checked; 59 | } 60 | }; 61 | FilterMenu.prototype.dropdownFilterItem = function (td, self) { 62 | var value = td.innerText; 63 | var dropdownFilterItem = document.createElement('div'); 64 | dropdownFilterItem.className = 'dropdown-filter-item'; 65 | var input = document.createElement('input'); 66 | input.type = 'checkbox'; 67 | input.value = value.trim().replace(/ +(?= )/g, ''); 68 | input.setAttribute('checked', 'checked'); 69 | input.className = 'dropdown-filter-menu-item item'; 70 | input.setAttribute('data-column', self.column.toString()); 71 | input.setAttribute('data-index', self.index.toString()); 72 | dropdownFilterItem.appendChild(input); 73 | dropdownFilterItem.innerHTML = dropdownFilterItem.innerHTML.trim() + ' ' + value; 74 | return dropdownFilterItem; 75 | }; 76 | FilterMenu.prototype.dropdownFilterItemSelectAll = function () { 77 | var value = this.options.captions.select_all; 78 | var dropdownFilterItemSelectAll = document.createElement('div'); 79 | dropdownFilterItemSelectAll.className = 'dropdown-filter-item'; 80 | var input = document.createElement('input'); 81 | input.type = 'checkbox'; 82 | input.value = this.options.captions.select_all; 83 | input.setAttribute('checked', 'checked'); 84 | input.className = 'dropdown-filter-menu-item select-all'; 85 | input.setAttribute('data-column', this.column.toString()); 86 | input.setAttribute('data-index', this.index.toString()); 87 | dropdownFilterItemSelectAll.appendChild(input); 88 | dropdownFilterItemSelectAll.innerHTML = dropdownFilterItemSelectAll.innerHTML + ' ' + value; 89 | return dropdownFilterItemSelectAll; 90 | }; 91 | FilterMenu.prototype.dropdownFilterSearch = function () { 92 | var dropdownFilterItem = document.createElement('div'); 93 | dropdownFilterItem.className = 'dropdown-filter-search'; 94 | var input = document.createElement('input'); 95 | input.type = 'text'; 96 | input.className = 'dropdown-filter-menu-search form-control'; 97 | input.setAttribute('data-column', this.column.toString()); 98 | input.setAttribute('data-index', this.index.toString()); 99 | input.setAttribute('placeholder', this.options.captions.search); 100 | dropdownFilterItem.appendChild(input); 101 | return dropdownFilterItem; 102 | }; 103 | FilterMenu.prototype.dropdownFilterSort = function (direction) { 104 | var dropdownFilterItem = document.createElement('div'); 105 | dropdownFilterItem.className = 'dropdown-filter-sort'; 106 | var span = document.createElement('span'); 107 | span.className = direction.toLowerCase().split(' ').join('-'); 108 | span.setAttribute('data-column', this.column.toString()); 109 | span.setAttribute('data-index', this.index.toString()); 110 | span.innerText = direction; 111 | dropdownFilterItem.appendChild(span); 112 | return dropdownFilterItem; 113 | }; 114 | FilterMenu.prototype.dropdownFilterContent = function () { 115 | var _this = this; 116 | var self = this; 117 | var dropdownFilterContent = document.createElement('div'); 118 | dropdownFilterContent.className = 'dropdown-filter-content'; 119 | var innerDivs = this.tds.reduce(function (arr, el) { 120 | var values = arr.map(function (el) { 121 | return el.innerText.trim(); 122 | }); 123 | if (values.indexOf(el.innerText.trim()) < 0) arr.push(el); 124 | return arr; 125 | }, []).sort(function (a, b) { 126 | var A = a.innerText.toLowerCase(); 127 | var B = b.innerText.toLowerCase(); 128 | if (!isNaN(Number(A)) && !isNaN(Number(B))) { 129 | if (Number(A) < Number(B)) return -1; 130 | if (Number(A) > Number(B)) return 1; 131 | } else { 132 | if (A < B) return -1; 133 | if (A > B) return 1; 134 | } 135 | return 0; 136 | }).map(function (td) { 137 | return _this.dropdownFilterItem(td, self); 138 | }); 139 | this.inputs = innerDivs.map(function (div) { 140 | return div.firstElementChild; 141 | }); 142 | var selectAllCheckboxDiv = this.dropdownFilterItemSelectAll(); 143 | this.selectAllCheckbox = selectAllCheckboxDiv.firstElementChild; 144 | innerDivs.unshift(selectAllCheckboxDiv); 145 | var searchFilterDiv = this.dropdownFilterSearch(); 146 | this.searchFilter = searchFilterDiv.firstElementChild; 147 | var outerDiv = innerDivs.reduce(function (outerDiv, innerDiv) { 148 | outerDiv.appendChild(innerDiv); 149 | return outerDiv; 150 | }, document.createElement('div')); 151 | outerDiv.className = 'checkbox-container'; 152 | var elements = []; 153 | if (this.options.sort) elements = elements.concat([this.dropdownFilterSort(this.options.captions.a_to_z), this.dropdownFilterSort(this.options.captions.z_to_a)]); 154 | if (this.options.search) elements.push(searchFilterDiv); 155 | return elements.concat(outerDiv).reduce(function (html, el) { 156 | html.appendChild(el); 157 | return html; 158 | }, dropdownFilterContent); 159 | }; 160 | FilterMenu.prototype.dropdownFilterDropdown = function () { 161 | var dropdownFilterDropdown = document.createElement('div'); 162 | dropdownFilterDropdown.className = 'dropdown-filter-dropdown'; 163 | var arrow = document.createElement('span'); 164 | arrow.className = 'glyphicon glyphicon-arrow-down dropdown-filter-icon'; 165 | var icon = document.createElement('i'); 166 | icon.className = 'arrow-down'; 167 | arrow.appendChild(icon); 168 | dropdownFilterDropdown.appendChild(arrow); 169 | dropdownFilterDropdown.appendChild(this.dropdownFilterContent()); 170 | if ($(this.th).hasClass('no-sort')) { 171 | $(dropdownFilterDropdown).find('.dropdown-filter-sort').remove(); 172 | } 173 | if ($(this.th).hasClass('no-filter')) { 174 | $(dropdownFilterDropdown).find('.checkbox-container').remove(); 175 | } 176 | if ($(this.th).hasClass('no-search')) { 177 | $(dropdownFilterDropdown).find('.dropdown-filter-search').remove(); 178 | } 179 | return dropdownFilterDropdown; 180 | }; 181 | return FilterMenu; 182 | }(); 183 | 184 | var FilterCollection = function () { 185 | function FilterCollection(target, options) { 186 | this.target = target; 187 | this.options = options; 188 | this.ths = target.find('th' + options.columnSelector).toArray(); 189 | this.filterMenus = this.ths.map(function (th, index) { 190 | var column = $(th).index(); 191 | return new FilterMenu(target, th, column, index, options); 192 | }); 193 | this.rows = target.find('tbody').find('tr').toArray(); 194 | this.table = target.get(0); 195 | } 196 | FilterCollection.prototype.initialize = function () { 197 | this.filterMenus.forEach(function (filterMenu) { 198 | filterMenu.initialize(); 199 | }); 200 | this.bindCheckboxes(); 201 | this.bindSelectAllCheckboxes(); 202 | this.bindSort(); 203 | this.bindSearch(); 204 | }; 205 | FilterCollection.prototype.bindCheckboxes = function () { 206 | var filterMenus = this.filterMenus; 207 | var rows = this.rows; 208 | var ths = this.ths; 209 | var updateRowVisibility = this.updateRowVisibility; 210 | this.target.find('.dropdown-filter-menu-item.item').change(function () { 211 | var index = $(this).data('index'); 212 | var value = $(this).val(); 213 | filterMenus[index].updateSelectAll(); 214 | updateRowVisibility(filterMenus, rows, ths); 215 | }); 216 | }; 217 | FilterCollection.prototype.bindSelectAllCheckboxes = function () { 218 | var filterMenus = this.filterMenus; 219 | var rows = this.rows; 220 | var ths = this.ths; 221 | var updateRowVisibility = this.updateRowVisibility; 222 | this.target.find('.dropdown-filter-menu-item.select-all').change(function () { 223 | var index = $(this).data('index'); 224 | var value = this.checked; 225 | filterMenus[index].selectAllUpdate(value); 226 | updateRowVisibility(filterMenus, rows, ths); 227 | }); 228 | }; 229 | FilterCollection.prototype.bindSort = function () { 230 | var filterMenus = this.filterMenus; 231 | var rows = this.rows; 232 | var ths = this.ths; 233 | var sort = this.sort; 234 | var table = this.table; 235 | var options = this.options; 236 | var updateRowVisibility = this.updateRowVisibility; 237 | this.target.find('.dropdown-filter-sort').click(function () { 238 | var $sortElement = $(this).find('span'); 239 | var column = $sortElement.data('column'); 240 | var order = $sortElement.attr('class'); 241 | sort(column, order, table, options); 242 | updateRowVisibility(filterMenus, rows, ths); 243 | }); 244 | }; 245 | FilterCollection.prototype.bindSearch = function () { 246 | var filterMenus = this.filterMenus; 247 | var rows = this.rows; 248 | var ths = this.ths; 249 | var updateRowVisibility = this.updateRowVisibility; 250 | this.target.find('.dropdown-filter-search').keyup(function () { 251 | var $input = $(this).find('input'); 252 | var index = $input.data('index'); 253 | var value = $input.val(); 254 | filterMenus[index].searchToggle(value); 255 | updateRowVisibility(filterMenus, rows, ths); 256 | }); 257 | }; 258 | FilterCollection.prototype.updateRowVisibility = function (filterMenus, rows, ths) { 259 | var showRows = rows; 260 | var hideRows = []; 261 | var selectedLists = filterMenus.map(function (filterMenu) { 262 | return { 263 | column: filterMenu.column, 264 | selected: filterMenu.inputs.filter(function (input) { 265 | return input.checked; 266 | }).map(function (input) { 267 | return input.value.trim().replace(/ +(?= )/g, ''); 268 | }) 269 | }; 270 | }); 271 | for (var i = 0; i < rows.length; i++) { 272 | var tds = rows[i].children; 273 | for (var j = 0; j < selectedLists.length; j++) { 274 | var content = tds[selectedLists[j].column].innerText.trim().replace(/ +(?= )/g, ''); 275 | if (selectedLists[j].selected.indexOf(content) === -1) { 276 | $(rows[i]).hide(); 277 | break; 278 | } 279 | $(rows[i]).show(); 280 | } 281 | } 282 | }; 283 | FilterCollection.prototype.sort = function (column, order, table, options) { 284 | var flip = 1; 285 | if (order === options.captions.z_to_a.toLowerCase().split(' ').join('-')) flip = -1; 286 | var tbody = $(table).find('tbody').get(0); 287 | var rows = $(tbody).find('tr').get(); 288 | rows.sort(function (a, b) { 289 | var A = a.children[column].innerText.toUpperCase(); 290 | var B = b.children[column].innerText.toUpperCase(); 291 | if (!isNaN(Number(A)) && !isNaN(Number(B))) { 292 | if (Number(A) < Number(B)) return -1 * flip; 293 | if (Number(A) > Number(B)) return 1 * flip; 294 | } else { 295 | if (A < B) return -1 * flip; 296 | if (A > B) return 1 * flip; 297 | } 298 | return 0; 299 | }); 300 | for (var i = 0; i < rows.length; i++) { 301 | tbody.appendChild(rows[i]); 302 | } 303 | }; 304 | return FilterCollection; 305 | }(); 306 | 307 | $$1.fn.excelTableFilter = function (options) { 308 | var target = this; 309 | options = $$1.extend({}, $$1.fn.excelTableFilter.options, options); 310 | if (typeof options.columnSelector === 'undefined') options.columnSelector = ''; 311 | if (typeof options.sort === 'undefined') options.sort = true; 312 | if (typeof options.search === 'undefined') options.search = true; 313 | if (typeof options.captions === 'undefined') options.captions = { 314 | a_to_z: 'A to Z', 315 | z_to_a: 'Z to A', 316 | search: 'Search', 317 | select_all: 'Select All' 318 | }; 319 | var filterCollection = new FilterCollection(target, options); 320 | filterCollection.initialize(); 321 | return target; 322 | }; 323 | $$1.fn.excelTableFilter.options = {}; 324 | 325 | }(jQuery)); 326 | //# sourceMappingURL=excel-bootstrap-table-filter-bundle.js.map 327 | -------------------------------------------------------------------------------- /dist/excel-bootstrap-table-filter-bundle.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"excel-bootstrap-table-filter-bundle.js","sources":["../src/FilterMenu.ts","../src/FilterCollection.ts","../src/excel-bootstrap-table-filter.ts"],"sourcesContent":["export class FilterMenu {\n\n th: HTMLElement;\n tds: Array;\n column: number;\n index: number;\n menu: HTMLElement;\n inputs: Array;\n selectAllCheckbox: Element;\n searchFilter: Element;\n options: Options;\n target: JQuery;\n\n constructor (target: JQuery, th: HTMLElement, column: number, index: number, options: Options) {\n this.options = options;\n this.th = th;\n this.column = column;\n this.index = index;\n this.tds = target.find('tbody tr td:nth-child(' + (this.column + 1) + ')').toArray();\n }\n\n public initialize(): void {\n this.menu = this.dropdownFilterDropdown();\n this.th.appendChild(this.menu);\n\n // variables for click handlers\n let $trigger = $(this.menu.children[0]);\n let $content = $(this.menu.children[1]);\n let $menu = $(this.menu);\n\n // toggle hide/show when the trigger is clicked\n $trigger.click(() => $content.toggle());\n\n $(document).click(function(el) {\n // hide the content if the user clicks outside of the menu\n if (!$menu.is(el.target) && $menu.has(el.target).length === 0) {\n $content.hide();\n }\n });\n }\n\n public searchToggle(value: string): void {\n if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = false;\n if (value.length === 0){\n this.toggleAll(true);\n if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = true;\n return;\n }\n // deselect all checkboxes initially\n this.toggleAll(false);\n // select checkboxes that match the search parameter\n this.inputs.filter(function(input: HTMLInputElement) {\n return input.value.toLowerCase().indexOf(value.toLowerCase()) > -1;\n }).forEach(function(input: HTMLInputElement) {\n input.checked = true;\n });\n }\n\n\n public updateSelectAll(): void {\n if (this.selectAllCheckbox instanceof HTMLInputElement) {\n // clear search parameters, if any\n $(this.searchFilter).val('');\n // Check if all inputs are selected\n this.selectAllCheckbox.checked = (this.inputs.length === this.inputs.filter(function(input: HTMLInputElement) {\n return input.checked;\n }).length);\n }\n }\n\n public selectAllUpdate(checked: boolean): void {\n // clear search parameters, if any\n $(this.searchFilter).val('');\n this.toggleAll(checked);\n }\n\n private toggleAll(checked: boolean): void {\n // loop through all inputs and check or uncheck each\n for (var i=0; i < this.inputs.length; i++) {\n let input = this.inputs[i];\n if (input instanceof HTMLInputElement) input.checked = checked;\n }\n }\n\n private dropdownFilterItem(td: HTMLElement, self: any): HTMLElement {\n // build holder div\n let value = td.innerText;\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-item';\n // build input\n let input = document.createElement('input');\n input.type = 'checkbox';\n input.value = value.trim().replace(/ +(?= )/g,'');\n input.setAttribute('checked','checked');\n input.className = 'dropdown-filter-menu-item item';\n // get index of td element\n input.setAttribute('data-column', self.column.toString());\n input.setAttribute('data-index', self.index.toString());\n // append input to holding div\n dropdownFilterItem.appendChild(input);\n dropdownFilterItem.innerHTML = dropdownFilterItem.innerHTML.trim() + ' ' + value;\n return dropdownFilterItem;\n }\n\n private dropdownFilterItemSelectAll(): HTMLElement {\n // build holder div\n let value = this.options.captions.select_all;\n let dropdownFilterItemSelectAll = document.createElement('div');\n dropdownFilterItemSelectAll.className = 'dropdown-filter-item';\n // build input\n let input = document.createElement('input');\n input.type = 'checkbox';\n input.value = this.options.captions.select_all;\n input.setAttribute('checked','checked');\n input.className = 'dropdown-filter-menu-item select-all';\n input.setAttribute('data-column', this.column.toString());\n input.setAttribute('data-index', this.index.toString());\n // append input to holding div\n dropdownFilterItemSelectAll.appendChild(input);\n dropdownFilterItemSelectAll.innerHTML = dropdownFilterItemSelectAll.innerHTML + ' ' + value;\n return dropdownFilterItemSelectAll;\n }\n\n private dropdownFilterSearch(): HTMLElement {\n // build holder div\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-search';\n // build input\n let input = document.createElement('input');\n input.type = 'text';\n input.className = 'dropdown-filter-menu-search form-control';\n input.setAttribute('data-column', this.column.toString());\n input.setAttribute('data-index', this.index.toString());\n input.setAttribute('placeholder', this.options.captions.search);\n // append input to holding div\n dropdownFilterItem.appendChild(input);\n return dropdownFilterItem;\n }\n\n private dropdownFilterSort(direction: string): HTMLElement {\n // build holder div\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-sort';\n // build span\n let span = document.createElement('span');\n span.className = direction.toLowerCase().split(' ').join('-');\n span.setAttribute('data-column', this.column.toString());\n span.setAttribute('data-index', this.index.toString());\n span.innerText = direction;\n // append input to holding div\n dropdownFilterItem.appendChild(span);\n return dropdownFilterItem;\n }\n\n private dropdownFilterContent(): HTMLElement {\n let self = this;\n // build holder div\n let dropdownFilterContent = document.createElement('div');\n dropdownFilterContent.className = 'dropdown-filter-content';\n\n let innerDivs = this.tds.reduce(function(arr, el) {\n // get unique values in column\n let values = arr.map((el) => el.innerText.trim());\n if (values.indexOf(el.innerText.trim()) < 0) arr.push(el);\n // return unique values\n return arr;\n }, [])\n .sort(function(a, b) {\n // sort values for display in dropdown\n var A = a.innerText.toLowerCase();\n var B = b.innerText.toLowerCase();\n\n if (!isNaN(Number(A)) && !isNaN(Number(B))) {\n\n // handle numbers\n if(Number(A) < Number(B)) return -1;\n if(Number(A) > Number(B)) return 1;\n\n } else {\n\n // handle strings\n if(A < B) return -1;\n if(A > B) return 1;\n\n }\n //return a.innerText.toLowerCase() > b.innerText.toLowerCase() ? 1 : -1;\n return 0;\n })\n // create dropdown filter items out of each value\n .map( (td) => {\n return this.dropdownFilterItem(td, self);\n })\n\n // map inputs to instance, we will need these later\n this.inputs = innerDivs.map((div) => div.firstElementChild);\n\n // add a select all checkbox\n let selectAllCheckboxDiv = this.dropdownFilterItemSelectAll();\n // map the select all checkbox to the instance, we will need it later\n this.selectAllCheckbox = selectAllCheckboxDiv.firstElementChild;\n // the checkbox will precede the other inputs\n innerDivs.unshift(selectAllCheckboxDiv);\n\n let searchFilterDiv = this.dropdownFilterSearch();\n this.searchFilter = searchFilterDiv.firstElementChild;\n\n // create outer div, and place all inner divs within it\n let outerDiv = innerDivs.reduce(function(outerDiv, innerDiv) {\n outerDiv.appendChild(innerDiv);\n return outerDiv;\n }, document.createElement('div'));\n outerDiv.className = 'checkbox-container';\n\n let elements: Array = [];\n if (this.options.sort ) elements= elements.concat([\n this.dropdownFilterSort(this.options.captions.a_to_z),\n this.dropdownFilterSort(this.options.captions.z_to_a)\n ]);\n if (this.options.search) elements.push(searchFilterDiv);\n\n return elements.concat(outerDiv).reduce(function(html, el) {\n html.appendChild(el);\n return html;\n }, dropdownFilterContent);\n }\n\n private dropdownFilterDropdown(): HTMLElement {\n // build holder div\n let dropdownFilterDropdown = document.createElement('div');\n dropdownFilterDropdown.className = 'dropdown-filter-dropdown';\n let arrow = document.createElement('span');\n arrow.className = 'glyphicon glyphicon-arrow-down dropdown-filter-icon';\n let icon = document.createElement('i');\n icon.className = 'arrow-down';\n arrow.appendChild(icon);\n dropdownFilterDropdown.appendChild(arrow);\n dropdownFilterDropdown.appendChild(this.dropdownFilterContent());\n\n if ($(this.th).hasClass('no-sort')) {\n $(dropdownFilterDropdown).find('.dropdown-filter-sort').remove();\n }\n if ($(this.th).hasClass('no-filter')) {\n $(dropdownFilterDropdown).find('.checkbox-container').remove();\n }\n if ($(this.th).hasClass('no-search')) {\n $(dropdownFilterDropdown).find('.dropdown-filter-search').remove();\n }\n return dropdownFilterDropdown;\n }\n\n}\n","import { FilterMenu } from './FilterMenu'\n\nexport class FilterCollection {\n\n filterMenus: Array;\n rows: Array;\n ths: Array;\n table: HTMLElement;\n options: Options;\n target: JQuery;\n\n constructor (target: JQuery, options: Options) {\n this.target = target;\n this.options = options;\n this.ths = target.find('th' + options.columnSelector).toArray()\n this.filterMenus = this.ths.map(function(th: HTMLElement, index: number) {\n let column = $(th).index();\n return new FilterMenu(target, th, column, index, options);\n });\n this.rows = target.find('tbody').find('tr').toArray();\n this.table = target.get(0);\n }\n\n public initialize(): void {\n this.filterMenus.forEach(function(filterMenu) {\n filterMenu.initialize();\n });\n this.bindCheckboxes();\n this.bindSelectAllCheckboxes();\n this.bindSort();\n this.bindSearch();\n }\n\n private bindCheckboxes(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-menu-item.item').change(function() {\n let index = $(this).data('index');\n let value = $(this).val();\n filterMenus[index].updateSelectAll();\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSelectAllCheckboxes(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-menu-item.select-all').change(function() {\n let index = $(this).data('index');\n let value = this.checked;\n filterMenus[index].selectAllUpdate(value);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSort(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let sort = this.sort;\n let table = this.table;\n let options = this.options;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-sort').click(function() {\n let $sortElement = $(this).find('span');\n let column = $sortElement.data('column');\n let order = $sortElement.attr('class');\n sort(column, order, table, options);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSearch(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-search').keyup(function() {\n let $input = $(this).find('input');\n let index = $input.data('index');\n let value = $input.val();\n filterMenus[index].searchToggle(value);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private updateRowVisibility(filterMenus: Array, rows: Array, ths: Array): void {\n let showRows = rows;\n let hideRows: Array = [];\n let selectedLists = filterMenus.map(function(filterMenu) {\n return {\n column: filterMenu.column,\n selected: filterMenu.inputs\n .filter(function(input: HTMLInputElement) {\n return input.checked\n }).map(function(input: HTMLInputElement) {\n return input.value.trim().replace(/ +(?= )/g,'');\n })\n };\n });\n for (let i=0; i < rows.length; i++) {\n let tds = rows[i].children;\n for (let j=0; j < selectedLists.length; j++) {\n let content = (tds[selectedLists[j].column] as HTMLElement).innerText.trim().replace(/ +(?= )/g,'')\n if (selectedLists[j].selected.indexOf(content) === -1 ) {\n $(rows[i]).hide();\n break;\n }\n $(rows[i]).show();\n }\n }\n }\n\n private sort(column: number, order: string, table: HTMLElement, options: Options): void {\n let flip = 1;\n if (order === options.captions.z_to_a.toLowerCase().split(' ').join('-')) flip = -1;\n let tbody = $(table).find('tbody').get(0);\n let rows = $(tbody).find('tr').get();\n\n rows.sort(function(a, b) {\n var A = (a.children[column] as HTMLElement).innerText.toUpperCase();\n var B = (b.children[column] as HTMLElement).innerText.toUpperCase();\n\n if (!isNaN(Number(A)) && !isNaN(Number(B))) {\n // handle numbers\n if(Number(A) < Number(B)) return -1*flip;\n if(Number(A) > Number(B)) return 1*flip;\n } else {\n // handle strings\n if(A < B) return -1*flip;\n if(A > B) return 1*flip;\n }\n return 0;\n });\n\n for (var i=0; i < rows.length; i++) {\n tbody.appendChild(rows[i]);\n }\n }\n\n\n}\n","import $ from 'jquery';\nimport { FilterCollection } from './FilterCollection'\n\n// Define the plugin function on the jQuery extension point.\n($.fn as any).excelTableFilter = function (this: JQuery, options: Options) {\n let target = this;\n // Merge the global options with the per-call options.\n options = $.extend({}, ($.fn as any).excelTableFilter.options, options);\n\n if (typeof options.columnSelector === 'undefined') options.columnSelector = '';\n if (typeof options.sort === 'undefined') options.sort = true;\n if (typeof options.search === 'undefined') options.search = true;\n\n if (typeof options.captions === 'undefined') options.captions = {\n a_to_z: 'A to Z',\n z_to_a: 'Z to A',\n search: 'Search',\n select_all: 'Select All'\n }\n\n let filterCollection = new FilterCollection(target, options);\n filterCollection.initialize();\n\n // Return the jQuery object for chaining.\n return target;\n};\n\n// Define the plugin's global default options.\n($.fn as any).excelTableFilter.options = {};\n"],"names":["target","th","column","index","options","tds","find","toArray","menu","dropdownFilterDropdown","appendChild","$trigger","$","children","$content","$menu","click","toggle","document","el","is","has","length","hide","value","selectAllCheckbox","HTMLInputElement","checked","toggleAll","inputs","filter","input","toLowerCase","indexOf","forEach","searchFilter","val","i","td","self","innerText","dropdownFilterItem","createElement","className","type","trim","replace","setAttribute","toString","innerHTML","captions","select_all","dropdownFilterItemSelectAll","search","direction","span","split","join","dropdownFilterContent","innerDivs","reduce","arr","values","map","push","sort","a","b","A","B","isNaN","Number","_this","div","firstElementChild","selectAllCheckboxDiv","unshift","searchFilterDiv","dropdownFilterSearch","outerDiv","innerDiv","elements","concat","dropdownFilterSort","a_to_z","z_to_a","html","arrow","icon","hasClass","remove","ths","columnSelector","filterMenus","FilterMenu","rows","table","get","filterMenu","initialize","bindCheckboxes","bindSelectAllCheckboxes","bindSort","bindSearch","updateRowVisibility","change","data","updateSelectAll","selectAllUpdate","$sortElement","order","attr","keyup","$input","searchToggle","showRows","hideRows","selectedLists","j","content","selected","show","flip","tbody","toUpperCase","fn","excelTableFilter","extend","filterCollection","FilterCollection"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/excel-bootstrap-table-filter-bundle.min.js: -------------------------------------------------------------------------------- 1 | (function($$1){"use strict";$$1="default"in $$1?$$1["default"]:$$1;var FilterMenu=function(){function FilterMenu(target,th,column,index,options){this.options=options;this.th=th;this.column=column;this.index=index;this.tds=target.find("tbody tr td:nth-child("+(this.column+1)+")").toArray()}FilterMenu.prototype.initialize=function(){this.menu=this.dropdownFilterDropdown();this.th.appendChild(this.menu);var $trigger=$(this.menu.children[0]);var $content=$(this.menu.children[1]);var $menu=$(this.menu);$trigger.click(function(){return $content.toggle()});$(document).click(function(el){if(!$menu.is(el.target)&&$menu.has(el.target).length===0){$content.hide()}})};FilterMenu.prototype.searchToggle=function(value){if(this.selectAllCheckbox instanceof HTMLInputElement)this.selectAllCheckbox.checked=false;if(value.length===0){this.toggleAll(true);if(this.selectAllCheckbox instanceof HTMLInputElement)this.selectAllCheckbox.checked=true;return}this.toggleAll(false);this.inputs.filter(function(input){return input.value.toLowerCase().indexOf(value.toLowerCase())>-1}).forEach(function(input){input.checked=true})};FilterMenu.prototype.updateSelectAll=function(){if(this.selectAllCheckbox instanceof HTMLInputElement){$(this.searchFilter).val("");this.selectAllCheckbox.checked=this.inputs.length===this.inputs.filter(function(input){return input.checked}).length}};FilterMenu.prototype.selectAllUpdate=function(checked){$(this.searchFilter).val("");this.toggleAll(checked)};FilterMenu.prototype.toggleAll=function(checked){for(var i=0;iNumber(B))return 1}else{if(AB)return 1}return 0}).map(function(td){return _this.dropdownFilterItem(td,self)});this.inputs=innerDivs.map(function(div){return div.firstElementChild});var selectAllCheckboxDiv=this.dropdownFilterItemSelectAll();this.selectAllCheckbox=selectAllCheckboxDiv.firstElementChild;innerDivs.unshift(selectAllCheckboxDiv);var searchFilterDiv=this.dropdownFilterSearch();this.searchFilter=searchFilterDiv.firstElementChild;var outerDiv=innerDivs.reduce(function(outerDiv,innerDiv){outerDiv.appendChild(innerDiv);return outerDiv},document.createElement("div"));outerDiv.className="checkbox-container";var elements=[];if(this.options.sort)elements=elements.concat([this.dropdownFilterSort(this.options.captions.a_to_z),this.dropdownFilterSort(this.options.captions.z_to_a)]);if(this.options.search)elements.push(searchFilterDiv);return elements.concat(outerDiv).reduce(function(html,el){html.appendChild(el);return html},dropdownFilterContent)};FilterMenu.prototype.dropdownFilterDropdown=function(){var dropdownFilterDropdown=document.createElement("div");dropdownFilterDropdown.className="dropdown-filter-dropdown";var arrow=document.createElement("span");arrow.className="glyphicon glyphicon-arrow-down dropdown-filter-icon";var icon=document.createElement("i");icon.className="arrow-down";arrow.appendChild(icon);dropdownFilterDropdown.appendChild(arrow);dropdownFilterDropdown.appendChild(this.dropdownFilterContent());if($(this.th).hasClass("no-sort")){$(dropdownFilterDropdown).find(".dropdown-filter-sort").remove()}if($(this.th).hasClass("no-filter")){$(dropdownFilterDropdown).find(".checkbox-container").remove()}if($(this.th).hasClass("no-search")){$(dropdownFilterDropdown).find(".dropdown-filter-search").remove()}return dropdownFilterDropdown};return FilterMenu}();var FilterCollection=function(){function FilterCollection(target,options){this.target=target;this.options=options;this.ths=target.find("th"+options.columnSelector).toArray();this.filterMenus=this.ths.map(function(th,index){var column=$(th).index();return new FilterMenu(target,th,column,index,options)});this.rows=target.find("tbody").find("tr").toArray();this.table=target.get(0)}FilterCollection.prototype.initialize=function(){this.filterMenus.forEach(function(filterMenu){filterMenu.initialize()});this.bindCheckboxes();this.bindSelectAllCheckboxes();this.bindSort();this.bindSearch()};FilterCollection.prototype.bindCheckboxes=function(){var filterMenus=this.filterMenus;var rows=this.rows;var ths=this.ths;var updateRowVisibility=this.updateRowVisibility;this.target.find(".dropdown-filter-menu-item.item").change(function(){var index=$(this).data("index");var value=$(this).val();filterMenus[index].updateSelectAll();updateRowVisibility(filterMenus,rows,ths)})};FilterCollection.prototype.bindSelectAllCheckboxes=function(){var filterMenus=this.filterMenus;var rows=this.rows;var ths=this.ths;var updateRowVisibility=this.updateRowVisibility;this.target.find(".dropdown-filter-menu-item.select-all").change(function(){var index=$(this).data("index");var value=this.checked;filterMenus[index].selectAllUpdate(value);updateRowVisibility(filterMenus,rows,ths)})};FilterCollection.prototype.bindSort=function(){var filterMenus=this.filterMenus;var rows=this.rows;var ths=this.ths;var sort=this.sort;var table=this.table;var options=this.options;var updateRowVisibility=this.updateRowVisibility;this.target.find(".dropdown-filter-sort").click(function(){var $sortElement=$(this).find("span");var column=$sortElement.data("column");var order=$sortElement.attr("class");sort(column,order,table,options);updateRowVisibility(filterMenus,rows,ths)})};FilterCollection.prototype.bindSearch=function(){var filterMenus=this.filterMenus;var rows=this.rows;var ths=this.ths;var updateRowVisibility=this.updateRowVisibility;this.target.find(".dropdown-filter-search").keyup(function(){var $input=$(this).find("input");var index=$input.data("index");var value=$input.val();filterMenus[index].searchToggle(value);updateRowVisibility(filterMenus,rows,ths)})};FilterCollection.prototype.updateRowVisibility=function(filterMenus,rows,ths){var showRows=rows;var hideRows=[];var selectedLists=filterMenus.map(function(filterMenu){return{column:filterMenu.column,selected:filterMenu.inputs.filter(function(input){return input.checked}).map(function(input){return input.value.trim().replace(/ +(?= )/g,"")})}});for(var i=0;iNumber(B))return 1*flip}else{if(AB)return 1*flip}return 0});for(var i=0;i;\n column: number;\n index: number;\n menu: HTMLElement;\n inputs: Array;\n selectAllCheckbox: Element;\n searchFilter: Element;\n options: Options;\n target: JQuery;\n\n constructor (target: JQuery, th: HTMLElement, column: number, index: number, options: Options) {\n this.options = options;\n this.th = th;\n this.column = column;\n this.index = index;\n this.tds = target.find('tbody tr td:nth-child(' + (this.column + 1) + ')').toArray();\n }\n\n public initialize(): void {\n this.menu = this.dropdownFilterDropdown();\n this.th.appendChild(this.menu);\n\n // variables for click handlers\n let $trigger = $(this.menu.children[0]);\n let $content = $(this.menu.children[1]);\n let $menu = $(this.menu);\n\n // toggle hide/show when the trigger is clicked\n $trigger.click(() => $content.toggle());\n\n $(document).click(function(el) {\n // hide the content if the user clicks outside of the menu\n if (!$menu.is(el.target) && $menu.has(el.target).length === 0) {\n $content.hide();\n }\n });\n }\n\n public searchToggle(value: string): void {\n if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = false;\n if (value.length === 0){\n this.toggleAll(true);\n if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = true;\n return;\n }\n // deselect all checkboxes initially\n this.toggleAll(false);\n // select checkboxes that match the search parameter\n this.inputs.filter(function(input: HTMLInputElement) {\n return input.value.toLowerCase().indexOf(value.toLowerCase()) > -1;\n }).forEach(function(input: HTMLInputElement) {\n input.checked = true;\n });\n }\n\n\n public updateSelectAll(): void {\n if (this.selectAllCheckbox instanceof HTMLInputElement) {\n // clear search parameters, if any\n $(this.searchFilter).val('');\n // Check if all inputs are selected\n this.selectAllCheckbox.checked = (this.inputs.length === this.inputs.filter(function(input: HTMLInputElement) {\n return input.checked;\n }).length);\n }\n }\n\n public selectAllUpdate(checked: boolean): void {\n // clear search parameters, if any\n $(this.searchFilter).val('');\n this.toggleAll(checked);\n }\n\n private toggleAll(checked: boolean): void {\n // loop through all inputs and check or uncheck each\n for (var i=0; i < this.inputs.length; i++) {\n let input = this.inputs[i];\n if (input instanceof HTMLInputElement) input.checked = checked;\n }\n }\n\n private dropdownFilterItem(td: HTMLElement, self: any): HTMLElement {\n // build holder div\n let value = td.innerText;\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-item';\n // build input\n let input = document.createElement('input');\n input.type = 'checkbox';\n input.value = value.trim().replace(/ +(?= )/g,'');\n input.setAttribute('checked','checked');\n input.className = 'dropdown-filter-menu-item item';\n // get index of td element\n input.setAttribute('data-column', self.column.toString());\n input.setAttribute('data-index', self.index.toString());\n // append input to holding div\n dropdownFilterItem.appendChild(input);\n dropdownFilterItem.innerHTML = dropdownFilterItem.innerHTML.trim() + ' ' + value;\n return dropdownFilterItem;\n }\n\n private dropdownFilterItemSelectAll(): HTMLElement {\n // build holder div\n let value = this.options.captions.select_all;\n let dropdownFilterItemSelectAll = document.createElement('div');\n dropdownFilterItemSelectAll.className = 'dropdown-filter-item';\n // build input\n let input = document.createElement('input');\n input.type = 'checkbox';\n input.value = this.options.captions.select_all;\n input.setAttribute('checked','checked');\n input.className = 'dropdown-filter-menu-item select-all';\n input.setAttribute('data-column', this.column.toString());\n input.setAttribute('data-index', this.index.toString());\n // append input to holding div\n dropdownFilterItemSelectAll.appendChild(input);\n dropdownFilterItemSelectAll.innerHTML = dropdownFilterItemSelectAll.innerHTML + ' ' + value;\n return dropdownFilterItemSelectAll;\n }\n\n private dropdownFilterSearch(): HTMLElement {\n // build holder div\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-search';\n // build input\n let input = document.createElement('input');\n input.type = 'text';\n input.className = 'dropdown-filter-menu-search form-control';\n input.setAttribute('data-column', this.column.toString());\n input.setAttribute('data-index', this.index.toString());\n input.setAttribute('placeholder', this.options.captions.search);\n // append input to holding div\n dropdownFilterItem.appendChild(input);\n return dropdownFilterItem;\n }\n\n private dropdownFilterSort(direction: string): HTMLElement {\n // build holder div\n let dropdownFilterItem = document.createElement('div');\n dropdownFilterItem.className = 'dropdown-filter-sort';\n // build span\n let span = document.createElement('span');\n span.className = direction.toLowerCase().split(' ').join('-');\n span.setAttribute('data-column', this.column.toString());\n span.setAttribute('data-index', this.index.toString());\n span.innerText = direction;\n // append input to holding div\n dropdownFilterItem.appendChild(span);\n return dropdownFilterItem;\n }\n\n private dropdownFilterContent(): HTMLElement {\n let self = this;\n // build holder div\n let dropdownFilterContent = document.createElement('div');\n dropdownFilterContent.className = 'dropdown-filter-content';\n\n let innerDivs = this.tds.reduce(function(arr, el) {\n // get unique values in column\n let values = arr.map((el) => el.innerText.trim());\n if (values.indexOf(el.innerText.trim()) < 0) arr.push(el);\n // return unique values\n return arr;\n }, [])\n .sort(function(a, b) {\n // sort values for display in dropdown\n var A = a.innerText.toLowerCase();\n var B = b.innerText.toLowerCase();\n\n if (!isNaN(Number(A)) && !isNaN(Number(B))) {\n\n // handle numbers\n if(Number(A) < Number(B)) return -1;\n if(Number(A) > Number(B)) return 1;\n\n } else {\n\n // handle strings\n if(A < B) return -1;\n if(A > B) return 1;\n\n }\n //return a.innerText.toLowerCase() > b.innerText.toLowerCase() ? 1 : -1;\n return 0;\n })\n // create dropdown filter items out of each value\n .map( (td) => {\n return this.dropdownFilterItem(td, self);\n })\n\n // map inputs to instance, we will need these later\n this.inputs = innerDivs.map((div) => div.firstElementChild);\n\n // add a select all checkbox\n let selectAllCheckboxDiv = this.dropdownFilterItemSelectAll();\n // map the select all checkbox to the instance, we will need it later\n this.selectAllCheckbox = selectAllCheckboxDiv.firstElementChild;\n // the checkbox will precede the other inputs\n innerDivs.unshift(selectAllCheckboxDiv);\n\n let searchFilterDiv = this.dropdownFilterSearch();\n this.searchFilter = searchFilterDiv.firstElementChild;\n\n // create outer div, and place all inner divs within it\n let outerDiv = innerDivs.reduce(function(outerDiv, innerDiv) {\n outerDiv.appendChild(innerDiv);\n return outerDiv;\n }, document.createElement('div'));\n outerDiv.className = 'checkbox-container';\n\n let elements: Array = [];\n if (this.options.sort ) elements= elements.concat([\n this.dropdownFilterSort(this.options.captions.a_to_z),\n this.dropdownFilterSort(this.options.captions.z_to_a)\n ]);\n if (this.options.search) elements.push(searchFilterDiv);\n\n return elements.concat(outerDiv).reduce(function(html, el) {\n html.appendChild(el);\n return html;\n }, dropdownFilterContent);\n }\n\n private dropdownFilterDropdown(): HTMLElement {\n // build holder div\n let dropdownFilterDropdown = document.createElement('div');\n dropdownFilterDropdown.className = 'dropdown-filter-dropdown';\n let arrow = document.createElement('span');\n arrow.className = 'glyphicon glyphicon-arrow-down dropdown-filter-icon';\n let icon = document.createElement('i');\n icon.className = 'arrow-down';\n arrow.appendChild(icon);\n dropdownFilterDropdown.appendChild(arrow);\n dropdownFilterDropdown.appendChild(this.dropdownFilterContent());\n\n if ($(this.th).hasClass('no-sort')) {\n $(dropdownFilterDropdown).find('.dropdown-filter-sort').remove();\n }\n if ($(this.th).hasClass('no-filter')) {\n $(dropdownFilterDropdown).find('.checkbox-container').remove();\n }\n if ($(this.th).hasClass('no-search')) {\n $(dropdownFilterDropdown).find('.dropdown-filter-search').remove();\n }\n return dropdownFilterDropdown;\n }\n\n}\n","import { FilterMenu } from './FilterMenu'\n\nexport class FilterCollection {\n\n filterMenus: Array;\n rows: Array;\n ths: Array;\n table: HTMLElement;\n options: Options;\n target: JQuery;\n\n constructor (target: JQuery, options: Options) {\n this.target = target;\n this.options = options;\n this.ths = target.find('th' + options.columnSelector).toArray()\n this.filterMenus = this.ths.map(function(th: HTMLElement, index: number) {\n let column = $(th).index();\n return new FilterMenu(target, th, column, index, options);\n });\n this.rows = target.find('tbody').find('tr').toArray();\n this.table = target.get(0);\n }\n\n public initialize(): void {\n this.filterMenus.forEach(function(filterMenu) {\n filterMenu.initialize();\n });\n this.bindCheckboxes();\n this.bindSelectAllCheckboxes();\n this.bindSort();\n this.bindSearch();\n }\n\n private bindCheckboxes(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-menu-item.item').change(function() {\n let index = $(this).data('index');\n let value = $(this).val();\n filterMenus[index].updateSelectAll();\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSelectAllCheckboxes(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-menu-item.select-all').change(function() {\n let index = $(this).data('index');\n let value = this.checked;\n filterMenus[index].selectAllUpdate(value);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSort(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let sort = this.sort;\n let table = this.table;\n let options = this.options;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-sort').click(function() {\n let $sortElement = $(this).find('span');\n let column = $sortElement.data('column');\n let order = $sortElement.attr('class');\n sort(column, order, table, options);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private bindSearch(): void {\n let filterMenus = this.filterMenus;\n let rows = this.rows;\n let ths = this.ths;\n let updateRowVisibility = this.updateRowVisibility;\n this.target.find('.dropdown-filter-search').keyup(function() {\n let $input = $(this).find('input');\n let index = $input.data('index');\n let value = $input.val();\n filterMenus[index].searchToggle(value);\n updateRowVisibility(filterMenus, rows, ths);\n });\n }\n\n private updateRowVisibility(filterMenus: Array, rows: Array, ths: Array): void {\n let showRows = rows;\n let hideRows: Array = [];\n let selectedLists = filterMenus.map(function(filterMenu) {\n return {\n column: filterMenu.column,\n selected: filterMenu.inputs\n .filter(function(input: HTMLInputElement) {\n return input.checked\n }).map(function(input: HTMLInputElement) {\n return input.value.trim().replace(/ +(?= )/g,'');\n })\n };\n });\n for (let i=0; i < rows.length; i++) {\n let tds = rows[i].children;\n for (let j=0; j < selectedLists.length; j++) {\n let content = (tds[selectedLists[j].column] as HTMLElement).innerText.trim().replace(/ +(?= )/g,'')\n if (selectedLists[j].selected.indexOf(content) === -1 ) {\n $(rows[i]).hide();\n break;\n }\n $(rows[i]).show();\n }\n }\n }\n\n private sort(column: number, order: string, table: HTMLElement, options: Options): void {\n let flip = 1;\n if (order === options.captions.z_to_a.toLowerCase().split(' ').join('-')) flip = -1;\n let tbody = $(table).find('tbody').get(0);\n let rows = $(tbody).find('tr').get();\n\n rows.sort(function(a, b) {\n var A = (a.children[column] as HTMLElement).innerText.toUpperCase();\n var B = (b.children[column] as HTMLElement).innerText.toUpperCase();\n\n if (!isNaN(Number(A)) && !isNaN(Number(B))) {\n // handle numbers\n if(Number(A) < Number(B)) return -1*flip;\n if(Number(A) > Number(B)) return 1*flip;\n } else {\n // handle strings\n if(A < B) return -1*flip;\n if(A > B) return 1*flip;\n }\n return 0;\n });\n\n for (var i=0; i < rows.length; i++) {\n tbody.appendChild(rows[i]);\n }\n }\n\n\n}\n","import $ from 'jquery';\nimport { FilterCollection } from './FilterCollection'\n\n// Define the plugin function on the jQuery extension point.\n($.fn as any).excelTableFilter = function (this: JQuery, options: Options) {\n let target = this;\n // Merge the global options with the per-call options.\n options = $.extend({}, ($.fn as any).excelTableFilter.options, options);\n\n if (typeof options.columnSelector === 'undefined') options.columnSelector = '';\n if (typeof options.sort === 'undefined') options.sort = true;\n if (typeof options.search === 'undefined') options.search = true;\n\n if (typeof options.captions === 'undefined') options.captions = {\n a_to_z: 'A to Z',\n z_to_a: 'Z to A',\n search: 'Search',\n select_all: 'Select All'\n }\n\n let filterCollection = new FilterCollection(target, options);\n filterCollection.initialize();\n\n // Return the jQuery object for chaining.\n return target;\n};\n\n// Define the plugin's global default options.\n($.fn as any).excelTableFilter.options = {};\n"]} -------------------------------------------------------------------------------- /dist/excel-bootstrap-table-filter-style.css: -------------------------------------------------------------------------------- 1 | .dropdown-filter-dropdown { 2 | position:relative; 3 | display:inline-block; 4 | } 5 | 6 | .dropdown-filter-icon { 7 | margin-left:5px; 8 | line-height:1.3; 9 | border:1px solid black; 10 | } 11 | 12 | 13 | .dropdown-filter-icon:hover { 14 | cursor:pointer; 15 | } 16 | 17 | .checkbox-container { 18 | max-height: 400px; 19 | overflow-y: scroll; 20 | } 21 | .dropdown-filter-content { 22 | display: none; 23 | position: absolute; 24 | background-color: #f9f9f9; 25 | min-width: 200px; 26 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); 27 | z-index: 1; 28 | padding-bottom:5px; 29 | padding-top:5px; 30 | padding-right:5px; 31 | padding-left:5px; 32 | } 33 | 34 | .dropdown-filter-content div { 35 | margin-top:5px; 36 | margin-left:5px; 37 | margin-right:5px; 38 | margin-bottom:5px; 39 | } 40 | 41 | .dropdown-filter-content div.dropdown-filter-search { 42 | margin-bottom:10px; 43 | margin-top:10px; 44 | } 45 | 46 | 47 | .dropdown-filter-content div.dropdown-filter-sort { 48 | padding-top:5px; 49 | padding-bottom:5px; 50 | } 51 | 52 | .dropdown-filter-content div.dropdown-filter-sort:hover { 53 | background-color:#e1e5e7; 54 | cursor:pointer; 55 | } 56 | 57 | .dropdown-filter-content div.dropdown-filter-sort span { 58 | margin-right:5px; 59 | margin-left:5px; 60 | margin-top:5px; 61 | margin-bottom:5px; 62 | color:#000000; 63 | } 64 | 65 | .arrow-down { 66 | border: solid black; 67 | border-width: 0 3px 3px 0; 68 | display: inline-block; 69 | padding: 3px; 70 | margin-right:5px; 71 | margin-left:5px; 72 | transform: rotate(45deg); 73 | -webkit-transform: rotate(45deg); 74 | } 75 | -------------------------------------------------------------------------------- /excel-table-filter-example.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chestercharles/excel-bootstrap-table-filter/4a88f70bb6857131d6b048cd4ff78da327064532/excel-table-filter-example.PNG -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | basePath: '', 4 | browsers: ['PhantomJS'], 5 | frameworks: ['jasmine', 'karma-typescript'], 6 | files: [ 7 | { pattern: "src/**/*.ts" } 8 | ], 9 | preprocessors: { 10 | '**/*.ts': ['karma-typescript'] 11 | }, 12 | karmaTypescriptConfig: { 13 | compilerOptions: { 14 | noImplicitAny: true, 15 | noImplicitReturns: true, 16 | noImplicitThis: true, 17 | allowSyntheticDefaultImports: true 18 | } 19 | }, 20 | reporters: ['progress', 'karma-typescript'], 21 | port: 9876, 22 | colors: true, 23 | logLevel: config.LOG_INFO, 24 | autoWatch: false, 25 | singleRun: true, 26 | concurrency: Infinity 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excel-bootstrap-table-filter", 3 | "version": "1.0.0", 4 | "description": "jQuery plugin to generate a checkbox drop-down table-filter", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/chetsercharles/excel-bootstrap-table-filter" 8 | }, 9 | "scripts": { 10 | "clean": "rimraf dist && rimraf coverage", 11 | "build": "npm run compile && npm run copy:html && npm run bundle && npm run minify", 12 | "compile": "tsc", 13 | "copy:html": "cpx 'src/**.{html,css}' dist", 14 | "test": "karma start", 15 | "bundle": "rollup -c", 16 | "minify": "uglifyjs dist/excel-bootstrap-table-filter-bundle.js --output dist/excel-bootstrap-table-filter-bundle.min.js --source-map dist/excel-bootstrap-table-filter-bundle.min.js.map --source-map-url excel-bootstrap-table-filter-bundle.min.js.map --in-source-map dist/excel-bootstrap-table-filter-bundle.js.map" 17 | }, 18 | "author": { 19 | "name": "Chester Carmer", 20 | "email": "chestercarmer@icloud.com" 21 | }, 22 | "license": "MIT", 23 | "dependencies": { 24 | "jquery": "~3.1.1" 25 | }, 26 | "devDependencies": { 27 | "@types/jasmine": "~2.5.43", 28 | "@types/jquery": "~2.0.40", 29 | "babel-plugin-external-helpers": "~6.22.0", 30 | "babel-preset-es2015": "~6.22.0", 31 | "cpx": "^1.5.0", 32 | "jasmine-core": "~2.5.2", 33 | "karma": "~1.5.0", 34 | "karma-jasmine": "~1.1.0", 35 | "karma-phantomjs-launcher": "~1.0.2", 36 | "karma-typescript": "~2.1.7", 37 | "rimraf": "~2.6.1", 38 | "rollup": "~0.41.4", 39 | "rollup-plugin-babel": "~2.7.1", 40 | "rollup-plugin-sourcemaps": "~0.4.1", 41 | "typescript": "~2.1.6", 42 | "uglify-js": "~2.7.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | export default { 5 | entry: 'dist/excel-bootstrap-table-filter.js', 6 | dest: 'dist/excel-bootstrap-table-filter-bundle.js', 7 | format: 'iife', 8 | external: [ 9 | 'jquery' 10 | ], 11 | globals: { 12 | jquery: 'jQuery' 13 | }, 14 | sourceMap: true, 15 | plugins: [ 16 | babel({ 17 | exclude: 'node_modules/**', 18 | }), 19 | sourcemaps() 20 | ] 21 | }; 22 | -------------------------------------------------------------------------------- /src/FilterCollection.ts: -------------------------------------------------------------------------------- 1 | import { FilterMenu } from './FilterMenu' 2 | 3 | export class FilterCollection { 4 | 5 | filterMenus: Array; 6 | rows: Array; 7 | ths: Array; 8 | table: HTMLElement; 9 | options: Options; 10 | target: JQuery; 11 | 12 | constructor (target: JQuery, options: Options) { 13 | this.target = target; 14 | this.options = options; 15 | this.ths = target.find('th' + options.columnSelector).toArray() 16 | this.filterMenus = this.ths.map(function(th: HTMLElement, index: number) { 17 | let column = $(th).index(); 18 | return new FilterMenu(target, th, column, index, options); 19 | }); 20 | this.rows = target.find('tbody').find('tr').toArray(); 21 | this.table = target.get(0); 22 | } 23 | 24 | public initialize(): void { 25 | this.filterMenus.forEach(function(filterMenu) { 26 | filterMenu.initialize(); 27 | }); 28 | this.bindCheckboxes(); 29 | this.bindSelectAllCheckboxes(); 30 | this.bindSort(); 31 | this.bindSearch(); 32 | } 33 | 34 | private bindCheckboxes(): void { 35 | let filterMenus = this.filterMenus; 36 | let rows = this.rows; 37 | let ths = this.ths; 38 | let updateRowVisibility = this.updateRowVisibility; 39 | this.target.find('.dropdown-filter-menu-item.item').change(function() { 40 | let index = $(this).data('index'); 41 | let value = $(this).val(); 42 | filterMenus[index].updateSelectAll(); 43 | updateRowVisibility(filterMenus, rows, ths); 44 | }); 45 | } 46 | 47 | private bindSelectAllCheckboxes(): void { 48 | let filterMenus = this.filterMenus; 49 | let rows = this.rows; 50 | let ths = this.ths; 51 | let updateRowVisibility = this.updateRowVisibility; 52 | this.target.find('.dropdown-filter-menu-item.select-all').change(function() { 53 | let index = $(this).data('index'); 54 | let value = this.checked; 55 | filterMenus[index].selectAllUpdate(value); 56 | updateRowVisibility(filterMenus, rows, ths); 57 | }); 58 | } 59 | 60 | private bindSort(): void { 61 | let filterMenus = this.filterMenus; 62 | let rows = this.rows; 63 | let ths = this.ths; 64 | let sort = this.sort; 65 | let table = this.table; 66 | let options = this.options; 67 | let updateRowVisibility = this.updateRowVisibility; 68 | this.target.find('.dropdown-filter-sort').click(function() { 69 | let $sortElement = $(this).find('span'); 70 | let column = $sortElement.data('column'); 71 | let order = $sortElement.attr('class'); 72 | sort(column, order, table, options); 73 | updateRowVisibility(filterMenus, rows, ths); 74 | }); 75 | } 76 | 77 | private bindSearch(): void { 78 | let filterMenus = this.filterMenus; 79 | let rows = this.rows; 80 | let ths = this.ths; 81 | let updateRowVisibility = this.updateRowVisibility; 82 | this.target.find('.dropdown-filter-search').keyup(function() { 83 | let $input = $(this).find('input'); 84 | let index = $input.data('index'); 85 | let value = $input.val(); 86 | filterMenus[index].searchToggle(value); 87 | updateRowVisibility(filterMenus, rows, ths); 88 | }); 89 | } 90 | 91 | private updateRowVisibility(filterMenus: Array, rows: Array, ths: Array): void { 92 | let showRows = rows; 93 | let hideRows: Array = []; 94 | let selectedLists = filterMenus.map(function(filterMenu) { 95 | return { 96 | column: filterMenu.column, 97 | selected: filterMenu.inputs 98 | .filter(function(input: HTMLInputElement) { 99 | return input.checked 100 | }).map(function(input: HTMLInputElement) { 101 | return input.value.trim().replace(/ +(?= )/g,''); 102 | }) 103 | }; 104 | }); 105 | for (let i=0; i < rows.length; i++) { 106 | let tds = rows[i].children; 107 | for (let j=0; j < selectedLists.length; j++) { 108 | let content = (tds[selectedLists[j].column] as HTMLElement).innerText.trim().replace(/ +(?= )/g,'') 109 | if (selectedLists[j].selected.indexOf(content) === -1 ) { 110 | $(rows[i]).hide(); 111 | break; 112 | } 113 | $(rows[i]).show(); 114 | } 115 | } 116 | } 117 | 118 | private sort(column: number, order: string, table: HTMLElement, options: Options): void { 119 | let flip = 1; 120 | if (order === options.captions.z_to_a.toLowerCase().split(' ').join('-')) flip = -1; 121 | let tbody = $(table).find('tbody').get(0); 122 | let rows = $(tbody).find('tr').get(); 123 | 124 | rows.sort(function(a, b) { 125 | var A = (a.children[column] as HTMLElement).innerText.toUpperCase(); 126 | var B = (b.children[column] as HTMLElement).innerText.toUpperCase(); 127 | 128 | if (!isNaN(Number(A)) && !isNaN(Number(B))) { 129 | // handle numbers 130 | if(Number(A) < Number(B)) return -1*flip; 131 | if(Number(A) > Number(B)) return 1*flip; 132 | } else { 133 | // handle strings 134 | if(A < B) return -1*flip; 135 | if(A > B) return 1*flip; 136 | } 137 | return 0; 138 | }); 139 | 140 | for (var i=0; i < rows.length; i++) { 141 | tbody.appendChild(rows[i]); 142 | } 143 | } 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/FilterMenu.ts: -------------------------------------------------------------------------------- 1 | export class FilterMenu { 2 | 3 | th: HTMLElement; 4 | tds: Array; 5 | column: number; 6 | index: number; 7 | menu: HTMLElement; 8 | inputs: Array; 9 | selectAllCheckbox: Element; 10 | searchFilter: Element; 11 | options: Options; 12 | target: JQuery; 13 | 14 | constructor (target: JQuery, th: HTMLElement, column: number, index: number, options: Options) { 15 | this.options = options; 16 | this.th = th; 17 | this.column = column; 18 | this.index = index; 19 | this.tds = target.find('tbody tr td:nth-child(' + (this.column + 1) + ')').toArray(); 20 | } 21 | 22 | public initialize(): void { 23 | this.menu = this.dropdownFilterDropdown(); 24 | this.th.appendChild(this.menu); 25 | 26 | // variables for click handlers 27 | let $trigger = $(this.menu.children[0]); 28 | let $content = $(this.menu.children[1]); 29 | let $menu = $(this.menu); 30 | 31 | // toggle hide/show when the trigger is clicked 32 | $trigger.click(() => $content.toggle()); 33 | 34 | $(document).click(function(el) { 35 | // hide the content if the user clicks outside of the menu 36 | if (!$menu.is(el.target) && $menu.has(el.target).length === 0) { 37 | $content.hide(); 38 | } 39 | }); 40 | } 41 | 42 | public searchToggle(value: string): void { 43 | if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = false; 44 | if (value.length === 0){ 45 | this.toggleAll(true); 46 | if (this.selectAllCheckbox instanceof HTMLInputElement) this.selectAllCheckbox.checked = true; 47 | return; 48 | } 49 | // deselect all checkboxes initially 50 | this.toggleAll(false); 51 | // select checkboxes that match the search parameter 52 | this.inputs.filter(function(input: HTMLInputElement) { 53 | return input.value.toLowerCase().indexOf(value.toLowerCase()) > -1; 54 | }).forEach(function(input: HTMLInputElement) { 55 | input.checked = true; 56 | }); 57 | } 58 | 59 | 60 | public updateSelectAll(): void { 61 | if (this.selectAllCheckbox instanceof HTMLInputElement) { 62 | // clear search parameters, if any 63 | $(this.searchFilter).val(''); 64 | // Check if all inputs are selected 65 | this.selectAllCheckbox.checked = (this.inputs.length === this.inputs.filter(function(input: HTMLInputElement) { 66 | return input.checked; 67 | }).length); 68 | } 69 | } 70 | 71 | public selectAllUpdate(checked: boolean): void { 72 | // clear search parameters, if any 73 | $(this.searchFilter).val(''); 74 | this.toggleAll(checked); 75 | } 76 | 77 | private toggleAll(checked: boolean): void { 78 | // loop through all inputs and check or uncheck each 79 | for (var i=0; i < this.inputs.length; i++) { 80 | let input = this.inputs[i]; 81 | if (input instanceof HTMLInputElement) input.checked = checked; 82 | } 83 | } 84 | 85 | private dropdownFilterItem(td: HTMLElement, self: any): HTMLElement { 86 | // build holder div 87 | let value = td.innerText; 88 | let dropdownFilterItem = document.createElement('div'); 89 | dropdownFilterItem.className = 'dropdown-filter-item'; 90 | // build input 91 | let input = document.createElement('input'); 92 | input.type = 'checkbox'; 93 | input.value = value.trim().replace(/ +(?= )/g,''); 94 | input.setAttribute('checked','checked'); 95 | input.className = 'dropdown-filter-menu-item item'; 96 | // get index of td element 97 | input.setAttribute('data-column', self.column.toString()); 98 | input.setAttribute('data-index', self.index.toString()); 99 | // append input to holding div 100 | dropdownFilterItem.appendChild(input); 101 | dropdownFilterItem.innerHTML = dropdownFilterItem.innerHTML.trim() + ' ' + value; 102 | return dropdownFilterItem; 103 | } 104 | 105 | private dropdownFilterItemSelectAll(): HTMLElement { 106 | // build holder div 107 | let value = this.options.captions.select_all; 108 | let dropdownFilterItemSelectAll = document.createElement('div'); 109 | dropdownFilterItemSelectAll.className = 'dropdown-filter-item'; 110 | // build input 111 | let input = document.createElement('input'); 112 | input.type = 'checkbox'; 113 | input.value = this.options.captions.select_all; 114 | input.setAttribute('checked','checked'); 115 | input.className = 'dropdown-filter-menu-item select-all'; 116 | input.setAttribute('data-column', this.column.toString()); 117 | input.setAttribute('data-index', this.index.toString()); 118 | // append input to holding div 119 | dropdownFilterItemSelectAll.appendChild(input); 120 | dropdownFilterItemSelectAll.innerHTML = dropdownFilterItemSelectAll.innerHTML + ' ' + value; 121 | return dropdownFilterItemSelectAll; 122 | } 123 | 124 | private dropdownFilterSearch(): HTMLElement { 125 | // build holder div 126 | let dropdownFilterItem = document.createElement('div'); 127 | dropdownFilterItem.className = 'dropdown-filter-search'; 128 | // build input 129 | let input = document.createElement('input'); 130 | input.type = 'text'; 131 | input.className = 'dropdown-filter-menu-search form-control'; 132 | input.setAttribute('data-column', this.column.toString()); 133 | input.setAttribute('data-index', this.index.toString()); 134 | input.setAttribute('placeholder', this.options.captions.search); 135 | // append input to holding div 136 | dropdownFilterItem.appendChild(input); 137 | return dropdownFilterItem; 138 | } 139 | 140 | private dropdownFilterSort(direction: string): HTMLElement { 141 | // build holder div 142 | let dropdownFilterItem = document.createElement('div'); 143 | dropdownFilterItem.className = 'dropdown-filter-sort'; 144 | // build span 145 | let span = document.createElement('span'); 146 | span.className = direction.toLowerCase().split(' ').join('-'); 147 | span.setAttribute('data-column', this.column.toString()); 148 | span.setAttribute('data-index', this.index.toString()); 149 | span.innerText = direction; 150 | // append input to holding div 151 | dropdownFilterItem.appendChild(span); 152 | return dropdownFilterItem; 153 | } 154 | 155 | private dropdownFilterContent(): HTMLElement { 156 | let self = this; 157 | // build holder div 158 | let dropdownFilterContent = document.createElement('div'); 159 | dropdownFilterContent.className = 'dropdown-filter-content'; 160 | 161 | let innerDivs = this.tds.reduce(function(arr, el) { 162 | // get unique values in column 163 | let values = arr.map((el) => el.innerText.trim()); 164 | if (values.indexOf(el.innerText.trim()) < 0) arr.push(el); 165 | // return unique values 166 | return arr; 167 | }, []) 168 | .sort(function(a, b) { 169 | // sort values for display in dropdown 170 | var A = a.innerText.toLowerCase(); 171 | var B = b.innerText.toLowerCase(); 172 | 173 | if (!isNaN(Number(A)) && !isNaN(Number(B))) { 174 | 175 | // handle numbers 176 | if(Number(A) < Number(B)) return -1; 177 | if(Number(A) > Number(B)) return 1; 178 | 179 | } else { 180 | 181 | // handle strings 182 | if(A < B) return -1; 183 | if(A > B) return 1; 184 | 185 | } 186 | //return a.innerText.toLowerCase() > b.innerText.toLowerCase() ? 1 : -1; 187 | return 0; 188 | }) 189 | // create dropdown filter items out of each value 190 | .map( (td) => { 191 | return this.dropdownFilterItem(td, self); 192 | }) 193 | 194 | // map inputs to instance, we will need these later 195 | this.inputs = innerDivs.map((div) => div.firstElementChild); 196 | 197 | // add a select all checkbox 198 | let selectAllCheckboxDiv = this.dropdownFilterItemSelectAll(); 199 | // map the select all checkbox to the instance, we will need it later 200 | this.selectAllCheckbox = selectAllCheckboxDiv.firstElementChild; 201 | // the checkbox will precede the other inputs 202 | innerDivs.unshift(selectAllCheckboxDiv); 203 | 204 | let searchFilterDiv = this.dropdownFilterSearch(); 205 | this.searchFilter = searchFilterDiv.firstElementChild; 206 | 207 | // create outer div, and place all inner divs within it 208 | let outerDiv = innerDivs.reduce(function(outerDiv, innerDiv) { 209 | outerDiv.appendChild(innerDiv); 210 | return outerDiv; 211 | }, document.createElement('div')); 212 | outerDiv.className = 'checkbox-container'; 213 | 214 | let elements: Array = []; 215 | if (this.options.sort ) elements= elements.concat([ 216 | this.dropdownFilterSort(this.options.captions.a_to_z), 217 | this.dropdownFilterSort(this.options.captions.z_to_a) 218 | ]); 219 | if (this.options.search) elements.push(searchFilterDiv); 220 | 221 | return elements.concat(outerDiv).reduce(function(html, el) { 222 | html.appendChild(el); 223 | return html; 224 | }, dropdownFilterContent); 225 | } 226 | 227 | private dropdownFilterDropdown(): HTMLElement { 228 | // build holder div 229 | let dropdownFilterDropdown = document.createElement('div'); 230 | dropdownFilterDropdown.className = 'dropdown-filter-dropdown'; 231 | let arrow = document.createElement('span'); 232 | arrow.className = 'glyphicon glyphicon-arrow-down dropdown-filter-icon'; 233 | let icon = document.createElement('i'); 234 | icon.className = 'arrow-down'; 235 | arrow.appendChild(icon); 236 | dropdownFilterDropdown.appendChild(arrow); 237 | dropdownFilterDropdown.appendChild(this.dropdownFilterContent()); 238 | 239 | if ($(this.th).hasClass('no-sort')) { 240 | $(dropdownFilterDropdown).find('.dropdown-filter-sort').remove(); 241 | } 242 | if ($(this.th).hasClass('no-filter')) { 243 | $(dropdownFilterDropdown).find('.checkbox-container').remove(); 244 | } 245 | if ($(this.th).hasClass('no-search')) { 246 | $(dropdownFilterDropdown).find('.dropdown-filter-search').remove(); 247 | } 248 | return dropdownFilterDropdown; 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/MenuItem.interface.ts: -------------------------------------------------------------------------------- 1 | interface MenuItem { 2 | column: number; 3 | row: number; 4 | value: string; 5 | selected: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/Options.interface.ts: -------------------------------------------------------------------------------- 1 | /** Plugin Options */ 2 | interface Options { 3 | columnSelector: string, 4 | sort: boolean, 5 | search: boolean, 6 | captions: { 7 | a_to_z: string, 8 | z_to_a: string, 9 | search: string, 10 | select_all: string 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Excel Bootstrap Table Filter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |

Excel-style Bootstrap Table filter jQuery plugin

22 |
23 |

24 |
25 |
26 |
27 |
28 |
29 |

Table 1

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
AnimalClassCollective NounA Number
BearMammalSleuth1
AntInsectArmy2
SalamanderAmphibianCongress3
OwlBirdParliament4
FrogAmphibianArmy5
SharkFishGam6
KookaburraBirdCackle7
CrowBirdMurder8
ElephantMammalHerd9
BarracudeFishGrist10
102 |
103 |
104 |
105 |
106 |

Table 2

107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 |
AnimalClassCollective NounA Number
BearMammalSleuth1
AntInsectArmy2
SalamanderAmphibianCongress3
OwlBirdParliament4
FrogAmphibianArmy5
SharkFishGam6
KookaburraBirdCackle7
CrowBirdMurder8
ElephantMammalHerd9
BarracudeFishGrist10
179 |
180 |
181 |
182 |
183 |

Table 3

184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 |
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
Ernst HandelRoland MendelAustria
Island TradingHelen BennettUK
Laughing Bacchus WinecellarsYoshi TannamuriCanada
Magazzini Alimentari RiunitiGiovanni RovelliItaly
225 |
226 |
227 |
228 | 229 | 230 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/excel-bootstrap-table-filter-style.css: -------------------------------------------------------------------------------- 1 | .dropdown-filter-dropdown { 2 | position:relative; 3 | display:inline-block; 4 | } 5 | 6 | .dropdown-filter-icon { 7 | margin-left:5px; 8 | line-height:1.3; 9 | border:1px solid black; 10 | } 11 | 12 | 13 | .dropdown-filter-icon:hover { 14 | cursor:pointer; 15 | } 16 | 17 | .checkbox-container { 18 | max-height: 400px; 19 | overflow-y: scroll; 20 | } 21 | .dropdown-filter-content { 22 | display: none; 23 | position: absolute; 24 | background-color: #f9f9f9; 25 | min-width: 200px; 26 | box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); 27 | z-index: 1; 28 | padding-bottom:5px; 29 | padding-top:5px; 30 | padding-right:5px; 31 | padding-left:5px; 32 | } 33 | 34 | .dropdown-filter-content div { 35 | margin-top:5px; 36 | margin-left:5px; 37 | margin-right:5px; 38 | margin-bottom:5px; 39 | } 40 | 41 | .dropdown-filter-content div.dropdown-filter-search { 42 | margin-bottom:10px; 43 | margin-top:10px; 44 | } 45 | 46 | 47 | .dropdown-filter-content div.dropdown-filter-sort { 48 | padding-top:5px; 49 | padding-bottom:5px; 50 | } 51 | 52 | .dropdown-filter-content div.dropdown-filter-sort:hover { 53 | background-color:#e1e5e7; 54 | cursor:pointer; 55 | } 56 | 57 | .dropdown-filter-content div.dropdown-filter-sort span { 58 | margin-right:5px; 59 | margin-left:5px; 60 | margin-top:5px; 61 | margin-bottom:5px; 62 | color:#000000; 63 | } 64 | 65 | .arrow-down { 66 | border: solid black; 67 | border-width: 0 3px 3px 0; 68 | display: inline-block; 69 | padding: 3px; 70 | margin-right:5px; 71 | margin-left:5px; 72 | transform: rotate(45deg); 73 | -webkit-transform: rotate(45deg); 74 | } 75 | -------------------------------------------------------------------------------- /src/excel-bootstrap-table-filter.ts: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | import { FilterCollection } from './FilterCollection' 3 | 4 | // Define the plugin function on the jQuery extension point. 5 | ($.fn as any).excelTableFilter = function (this: JQuery, options: Options) { 6 | let target = this; 7 | // Merge the global options with the per-call options. 8 | options = $.extend({}, ($.fn as any).excelTableFilter.options, options); 9 | 10 | if (typeof options.columnSelector === 'undefined') options.columnSelector = ''; 11 | if (typeof options.sort === 'undefined') options.sort = true; 12 | if (typeof options.search === 'undefined') options.search = true; 13 | 14 | if (typeof options.captions === 'undefined') options.captions = { 15 | a_to_z: 'A to Z', 16 | z_to_a: 'Z to A', 17 | search: 'Search', 18 | select_all: 'Select All' 19 | } 20 | 21 | let filterCollection = new FilterCollection(target, options); 22 | filterCollection.initialize(); 23 | 24 | // Return the jQuery object for chaining. 25 | return target; 26 | }; 27 | 28 | // Define the plugin's global default options. 29 | ($.fn as any).excelTableFilter.options = {}; 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "src", 4 | "outDir": "dist", 5 | "target": "es3", 6 | "module": "es6", 7 | "moduleResolution": "node", 8 | "noImplicitAny": true, 9 | "noImplicitReturns": true, 10 | "allowSyntheticDefaultImports": true, 11 | "removeComments": true, 12 | "preserveConstEnums": true, 13 | "sourceMap": true 14 | } 15 | } --------------------------------------------------------------------------------