184 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			184 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * TableColumns class for toggle visibility of <table> columns.
 | |
|  */
 | |
| class TableColumns {
 | |
|   constructor($table, tableName) {
 | |
|     this.$table = $table;
 | |
|     this.tableName = tableName;
 | |
|     this.storageKey = `joomla-tablecolumns-${this.tableName}`;
 | |
|     this.$headers = [].slice.call($table.querySelector('thead tr').children);
 | |
|     this.$rows = $table.querySelectorAll('tbody tr');
 | |
|     this.listOfHidden = [];
 | |
| 
 | |
|     // Load previous state
 | |
|     this.loadState();
 | |
| 
 | |
|     // Find protected columns
 | |
|     this.protectedCols = [0];
 | |
|     if (this.$rows[0]) {
 | |
|       [].slice.call(this.$rows[0].children).forEach(($el, index) => {
 | |
|         if ($el.nodeName === 'TH') {
 | |
|           this.protectedCols.push(index);
 | |
| 
 | |
|           // Make sure it's not in the list of hidden
 | |
|           const ih = this.listOfHidden.indexOf(index);
 | |
|           if (ih !== -1) {
 | |
|             this.listOfHidden.splice(ih, 1);
 | |
|           }
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     // Set up toggle menu
 | |
|     this.createControls();
 | |
| 
 | |
|     // Restore state
 | |
|     this.listOfHidden.forEach(index => {
 | |
|       this.toggleColumn(index, true);
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Create a controls to select visible columns
 | |
|    */
 | |
|   createControls() {
 | |
|     const $divouter = document.createElement('div');
 | |
|     $divouter.setAttribute('class', 'dropdown float-end pb-2');
 | |
|     const $divinner = document.createElement('div');
 | |
|     $divinner.setAttribute('class', 'dropdown-menu dropdown-menu-end');
 | |
|     $divinner.setAttribute('data-bs-popper', 'static');
 | |
| 
 | |
|     // Create a toggle button
 | |
|     const $button = document.createElement('button');
 | |
|     $button.type = 'button';
 | |
|     $button.textContent = Joomla.Text._('JGLOBAL_COLUMNS');
 | |
|     $button.classList.add('btn', 'btn-primary', 'btn-sm', 'dropdown-toggle');
 | |
|     $button.setAttribute('data-bs-toggle', 'dropdown');
 | |
|     $button.setAttribute('data-bs-auto-close', 'false');
 | |
|     $button.setAttribute('aria-haspopup', 'true');
 | |
|     $button.setAttribute('aria-expanded', 'false');
 | |
|     const $ul = document.createElement('ul');
 | |
|     $ul.setAttribute('class', 'list-unstyled p-2 text-nowrap mb-0');
 | |
|     $ul.setAttribute('id', 'columnList');
 | |
| 
 | |
|     // Collect a list of headers for dropdown
 | |
|     this.$headers.forEach(($el, index) => {
 | |
|       // Skip the first column, unless it's a th, as we don't want to display the checkboxes
 | |
|       if (index === 0 && $el.nodeName !== 'TH') return;
 | |
|       const $li = document.createElement('li');
 | |
|       const $label = document.createElement('label');
 | |
|       const $input = document.createElement('input');
 | |
|       $input.classList.add('form-check-input', 'me-1');
 | |
|       $input.type = 'checkbox';
 | |
|       $input.name = 'table[column][]';
 | |
|       $input.checked = this.listOfHidden.indexOf(index) === -1;
 | |
|       $input.disabled = this.protectedCols.indexOf(index) !== -1;
 | |
|       $input.value = index;
 | |
| 
 | |
|       // Find the header name
 | |
|       let $titleEl = $el.querySelector('span');
 | |
|       let title = $titleEl ? $titleEl.textContent.trim() : '';
 | |
|       if (!title) {
 | |
|         $titleEl = $el.querySelector('span.visually-hidden') || $el;
 | |
|         title = $titleEl.textContent.trim();
 | |
|       }
 | |
|       if (title.includes(':')) {
 | |
|         title = title.split(':', 2)[1].trim();
 | |
|       }
 | |
|       $label.textContent = title;
 | |
|       $label.insertAdjacentElement('afterbegin', $input);
 | |
|       $li.appendChild($label);
 | |
|       $ul.appendChild($li);
 | |
|     });
 | |
|     this.$table.insertAdjacentElement('beforebegin', $divouter);
 | |
|     $divouter.appendChild($button);
 | |
|     $divouter.appendChild($divinner);
 | |
|     $divinner.appendChild($ul);
 | |
| 
 | |
|     // Listen to checkboxes change
 | |
|     $ul.addEventListener('change', event => {
 | |
|       this.toggleColumn(parseInt(event.target.value, 10));
 | |
|       this.saveState();
 | |
|     });
 | |
| 
 | |
|     // Remove "media query" classes, which may prevent toggling from working.
 | |
|     this.$headers.forEach($el => {
 | |
|       $el.classList.remove('d-none', 'd-xs-table-cell', 'd-sm-table-cell', 'd-md-table-cell', 'd-lg-table-cell', 'd-xl-table-cell', 'd-xxl-table-cell');
 | |
|     });
 | |
|     this.$rows.forEach($row => {
 | |
|       [].slice.call($row.children).forEach($el => {
 | |
|         $el.classList.remove('d-none', 'd-xs-table-cell', 'd-sm-table-cell', 'd-md-table-cell', 'd-lg-table-cell', 'd-xl-table-cell', 'd-xxl-table-cell');
 | |
|       });
 | |
|     });
 | |
|     this.$button = $button;
 | |
|     this.$menu = $ul;
 | |
|     this.updateCounter();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Update button text
 | |
|    */
 | |
|   updateCounter() {
 | |
|     // Don't count the checkboxes column in the total
 | |
|     const total = this.$headers.length - 1;
 | |
|     const visible = total - this.listOfHidden.length;
 | |
|     this.$button.textContent = `${visible}/${total} ${Joomla.Text._('JGLOBAL_COLUMNS')}`;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Toggle column visibility
 | |
|    *
 | |
|    * @param {Number} index  The column index
 | |
|    * @param {Boolean} force To force hide
 | |
|    */
 | |
|   toggleColumn(index, force) {
 | |
|     // Skip incorrect index
 | |
|     if (!this.$headers[index]) return;
 | |
| 
 | |
|     // Skip the protected columns
 | |
|     if (this.protectedCols.indexOf(index) !== -1) return;
 | |
|     const i = this.listOfHidden.indexOf(index);
 | |
|     if (i === -1) {
 | |
|       this.listOfHidden.push(index);
 | |
|     } else if (force !== true) {
 | |
|       this.listOfHidden.splice(i, 1);
 | |
|     }
 | |
|     this.$headers[index].classList.toggle('d-none', force);
 | |
|     this.$rows.forEach($col => {
 | |
|       $col.children[index].classList.toggle('d-none', force);
 | |
|     });
 | |
|     this.updateCounter();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Save state, list of hidden columns
 | |
|    */
 | |
|   saveState() {
 | |
|     window.localStorage.setItem(this.storageKey, this.listOfHidden.join(','));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Load state, list of hidden columns
 | |
|    */
 | |
|   loadState() {
 | |
|     const stored = window.localStorage.getItem(this.storageKey);
 | |
|     if (stored) {
 | |
|       this.listOfHidden = stored.split(',').map(val => parseInt(val, 10));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| if (window.innerWidth > 992) {
 | |
|   // Look for dataset name else page-title
 | |
|   [...document.querySelectorAll('table:not(.columns-order-ignore)')].forEach($table => {
 | |
|     const tableName = $table.dataset.name ? $table.dataset.name : document.querySelector('.page-title').textContent.trim().replace(/[^a-z0-9]/gi, '-').toLowerCase();
 | |
| 
 | |
|     // Skip unnamed table
 | |
|     if (!tableName) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     /* eslint-disable-next-line no-new */
 | |
|     new TableColumns($table, tableName);
 | |
|   });
 | |
| }
 |