234 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @package         Regular Labs Library
 | |
|  * @version         24.11.1459
 | |
|  * 
 | |
|  * @author          Peter van Westen <info@regularlabs.com>
 | |
|  * @link            https://regularlabs.com
 | |
|  * @copyright       Copyright © 2024 Regular Labs All Rights Reserved
 | |
|  * @license         GNU General Public License version 2 or later
 | |
|  */
 | |
| 
 | |
| (function() {
 | |
|     'use strict';
 | |
| 
 | |
|     window.RegularLabs = window.RegularLabs || {};
 | |
| 
 | |
|     window.RegularLabs.TreeSelect = window.RegularLabs.TreeSelect || {
 | |
|         direction: (document.dir !== undefined) ? document.dir : document.getElementsByTagName("html")[0].getAttribute("dir"),
 | |
| 
 | |
|         init: function(id) {
 | |
|             const menu = document.querySelector('div#rl-treeselect-' + id);
 | |
| 
 | |
|             if ( ! menu) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const list             = menu.querySelector('ul');
 | |
|             const top_level_items  = list.querySelectorAll(':scope > li');
 | |
|             const items            = list.querySelectorAll('li');
 | |
|             const search_field     = menu.querySelector('[name="treeselectfilter"]');
 | |
|             const sub_tree_select  = menu.querySelector('div.sub-tree-select > *');
 | |
|             const no_results_found = menu.querySelector('joomla-alert');
 | |
| 
 | |
|             items.forEach((item) => {
 | |
|                 // Store the innerText for filtering
 | |
|                 // because if done later, also the text from added menus and buttons is added
 | |
|                 item.text = item.innerText;
 | |
|             });
 | |
| 
 | |
|             items.forEach((item) => {
 | |
|                 const checkbox = item.querySelector(':scope > .treeselect-item > input');
 | |
| 
 | |
|                 if ( ! checkbox) {
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 item.classList.toggle('rl-item-checked', checkbox.checked);
 | |
| 
 | |
|                 checkbox.addEventListener('change', () => {
 | |
|                     item.classList.toggle('rl-item-checked', checkbox.checked);
 | |
|                 });
 | |
| 
 | |
|                 const child_list = item.querySelector(':scope > ul.treeselect-sub');
 | |
| 
 | |
|                 if ( ! child_list) {
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 const label = item.querySelector('label');
 | |
| 
 | |
|                 const sub_tree_select_el = sub_tree_select.cloneNode(true);
 | |
| 
 | |
|                 const sub_tree_expand     = document.createElement('span');
 | |
|                 sub_tree_expand.className = 'treeselect-toggle icon-chevron-down';
 | |
|                 sub_tree_expand.collapsed = false;
 | |
| 
 | |
|                 sub_tree_expand.addEventListener('click', () => {
 | |
|                     this.expand(child_list, sub_tree_expand);
 | |
|                 });
 | |
| 
 | |
|                 sub_tree_select_el.querySelector('[data-action="checkNested"]').addEventListener('click', () => {
 | |
|                     this.check(child_list, true);
 | |
|                 });
 | |
| 
 | |
|                 sub_tree_select_el.querySelector('[data-action="uncheckNested"]').addEventListener('click', () => {
 | |
|                     this.check(child_list, false);
 | |
|                 });
 | |
| 
 | |
|                 sub_tree_select_el.querySelector('[data-action="toggleNested"]').addEventListener('click', () => {
 | |
|                     this.check(child_list, 'toggle');
 | |
|                 });
 | |
| 
 | |
|                 if (checkbox.dataset['rlTreeselectCollapseChildren']) {
 | |
|                     // Collapse children if top level parent is selected
 | |
|                     if (checkbox.checked) {
 | |
|                         sub_tree_expand.collapsed = false;
 | |
|                         this.expand(child_list, sub_tree_expand);
 | |
|                         this.check(child_list, false);
 | |
|                     }
 | |
| 
 | |
|                     // Add event when (un)checking top level parent
 | |
|                     checkbox.addEventListener('click', () => {
 | |
|                         sub_tree_expand.collapsed = ! checkbox.checked;
 | |
|                         this.expand(child_list, sub_tree_expand);
 | |
|                         this.check(child_list, false);
 | |
|                     });
 | |
|                 }
 | |
| 
 | |
|                 item.insertBefore(sub_tree_expand, item.firstChild);
 | |
|                 label.append(sub_tree_select_el);
 | |
|             });
 | |
| 
 | |
|             menu.querySelector('[data-action="checkAll"]').addEventListener('click', () => {
 | |
|                 this.check(menu, true);
 | |
|             });
 | |
|             menu.querySelector('[data-action="uncheckAll"]').addEventListener('click', () => {
 | |
|                 this.check(menu, false);
 | |
|             });
 | |
|             menu.querySelector('[data-action="toggleAll"]').addEventListener('click', () => {
 | |
|                 this.check(menu, 'toggle');
 | |
|             });
 | |
| 
 | |
|             menu.querySelector('[data-action="expandAll"]').addEventListener('click', () => {
 | |
|                 top_level_items.forEach((item) => {
 | |
|                     const child_list      = item.querySelector('ul.treeselect-sub');
 | |
|                     const sub_tree_expand = item.querySelector('.treeselect-toggle');
 | |
|                     if ( ! child_list || ! sub_tree_expand) {
 | |
|                         return;
 | |
|                     }
 | |
|                     sub_tree_expand.collapsed = true;
 | |
|                     this.expand(child_list, sub_tree_expand);
 | |
|                 });
 | |
|             });
 | |
| 
 | |
|             menu.querySelector('[data-action="collapseAll"]').addEventListener('click', () => {
 | |
|                 top_level_items.forEach((item) => {
 | |
|                     const child_list      = item.querySelector('ul.treeselect-sub');
 | |
|                     const sub_tree_expand = item.querySelector('.treeselect-toggle');
 | |
|                     if ( ! child_list || ! sub_tree_expand) {
 | |
|                         return;
 | |
|                     }
 | |
|                     sub_tree_expand.collapsed = false;
 | |
|                     this.expand(child_list, sub_tree_expand);
 | |
|                 });
 | |
|             });
 | |
|             menu.querySelector('[data-action="showAll"]').addEventListener('click', () => {
 | |
|                 this.resetSearch(items, search_field, no_results_found);
 | |
|             });
 | |
|             menu.querySelector('[data-action="showSelected"]').addEventListener('click', (e) => {
 | |
|                 this.resetSearch(items, search_field, no_results_found, true);
 | |
|             });
 | |
| 
 | |
|             // Takes care of the filtering
 | |
|             search_field.addEventListener('keyup', () => {
 | |
|                 this.doSearch(items, search_field, no_results_found);
 | |
|             });
 | |
|         },
 | |
| 
 | |
|         resetSearch: function(items, search_field, no_results_found, has_checked) {
 | |
|             search_field.value = '';
 | |
|             this.doSearch(items, search_field, no_results_found, has_checked);
 | |
|         },
 | |
| 
 | |
|         doSearch: function(items, search_field, no_results_found, has_checked) {
 | |
|             const text = search_field.value.toLowerCase();
 | |
| 
 | |
|             no_results_found.style.display = 'none';
 | |
| 
 | |
|             let results_found = 0;
 | |
| 
 | |
|             items.forEach((item) => {
 | |
|                 if (has_checked && ! item.querySelector('input:checked')) {
 | |
|                     item.style.display = 'none';
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 if (text !== '') {
 | |
|                     let item_text = item.text.toLowerCase();
 | |
|                     item_text     = item_text.replace(/\s+/g, ' ').trim();
 | |
| 
 | |
|                     if (item_text.indexOf(text) == -1) {
 | |
|                         item.style.display = 'none';
 | |
|                         return;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 results_found++;
 | |
|                 item.style.display = 'block';
 | |
|             });
 | |
| 
 | |
|             if ( ! results_found) {
 | |
|                 no_results_found.style.display = 'block';
 | |
|             }
 | |
|         },
 | |
| 
 | |
|         check: function(parent, checked) {
 | |
|             const items = parent.querySelectorAll('li');
 | |
| 
 | |
|             items.forEach((item) => {
 | |
|                 if (item.style.display === 'none') {
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 const checkbox = item.querySelector(':scope > .treeselect-item input:enabled');
 | |
| 
 | |
|                 if ( ! checkbox) {
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 checkbox.checked = checked === 'toggle' ? ! checkbox.checked : checked;
 | |
| 
 | |
|                 item.classList.toggle('rl-item-checked', checked);
 | |
|             });
 | |
|         },
 | |
| 
 | |
|         expand: function(element, button) {
 | |
|             const show = button.collapsed;
 | |
| 
 | |
|             element.style.display = show ? 'block' : 'none';
 | |
| 
 | |
|             button.classList.toggle('icon-chevron-down', show);
 | |
|             button.classList.toggle(this.direction === 'rtl' ? 'icon-chevron-left' : 'icon-chevron-right', ! show);
 | |
| 
 | |
|             button.collapsed = ! button.collapsed;
 | |
| 
 | |
|             if ( ! show) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             const child_lists = element.querySelectorAll(':scope > li > ul.treeselect-sub');
 | |
| 
 | |
|             if ( ! child_lists.length) {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             child_lists.forEach((child_list) => {
 | |
|                 const child_button     = child_list.closest('li').querySelector('.treeselect-toggle');
 | |
|                 child_button.collapsed = true;
 | |
|                 this.expand(child_list, child_button);
 | |
|             });
 | |
| 
 | |
|         }
 | |
|     };
 | |
| })();
 |