assets_js_controllers_select-all-switch-list-controller.js

import { Controller } from "@hotwired/stimulus"

/**
 * **INTERNAL CODE DOCUMENTATION COMPLETE**
 * 
 * Select All Switch List Controller
 * 
 * A sophisticated Stimulus controller that provides automatic "Select All" functionality for
 * Bootstrap form-switch checkbox lists. Creates and manages a master checkbox that controls
 * all individual checkboxes with bidirectional synchronization.
 * 
 * Key Features:
 * - Automatic "Select All" header checkbox generation
 * - Bidirectional synchronization between master and individual checkboxes
 * - Bootstrap form-switch styling consistency
 * - Dynamic checkbox discovery and event management
 * - Accessibility support with ARIA labels
 * - Automatic state management for partial selections
 * 
 * @class SelectAllListController
 * @extends Controller
 * 
 * HTML Structure Example:
 * ```html
 * <div data-controller="select-all-switch">
 *   <!-- Individual checkboxes (Select All checkbox will be auto-generated) -->
 *   <div class="form-check form-switch">
 *     <input class="form-check-input" type="checkbox" id="item1" name="items[]" value="1">
 *     <label class="form-check-label" for="item1">Item 1</label>
 *   </div>
 *   
 *   <div class="form-check form-switch">
 *     <input class="form-check-input" type="checkbox" id="item2" name="items[]" value="2">
 *     <label class="form-check-label" for="item2">Item 2</label>
 *   </div>
 *   
 *   <div class="form-check form-switch">
 *     <input class="form-check-input" type="checkbox" id="item3" name="items[]" value="3">
 *     <label class="form-check-label" for="item3">Item 3</label>
 *   </div>
 * </div>
 * ```
 */
class SelectAllListController extends Controller {
    /** @type {NodeList} Collection of all checkboxes in the list including the master checkbox */
    allCheckboxes;

    /**
     * Initialize controller and generate Select All functionality
     * Creates master checkbox, sets up event listeners, and establishes synchronization
     */
    connect() {
        //copy the first form-check form-switch checkbox and make it a select all checkbox
        const selectAllCheckbox = this.element.querySelector('.form-check.form-switch').cloneNode(true);
        selectAllCheckbox.querySelector('input[type="checkbox"]').setAttribute('data-select-all', 'true');
        selectAllCheckbox.querySelector('input[type="checkbox"]').setAttribute('aria-label', 'Select All');
        selectAllCheckbox.querySelector('label').innerText = 'Select All';
        // get the first form-check form-switch checkbox and set the id to select-all
        const firstCheckbox = this.element.querySelector('.form-check.form-switch');
        firstCheckbox.parentNode.insertBefore(selectAllCheckbox, firstCheckbox);
        this.allCheckboxes = this.element.querySelectorAll('input[type="checkbox"]');
        this.allCheckboxes.forEach((checkbox) => {
            checkbox.addEventListener('change', this.updateSelectAll.bind(this));
        });
    }

    /**
     * Handle checkbox state changes and maintain synchronization
     * Manages bidirectional relationship between master and individual checkboxes
     * Updates master checkbox state based on individual selections
     * 
     * @param {Event} event - The change event from any checkbox in the list
     */
    updateSelectAll(event) {
        const selectAllCheckbox = this.element.querySelector('input[type="checkbox"][data-select-all]');
        if (event.target === selectAllCheckbox) {
            this.allCheckboxes.forEach((checkbox) => {
                if (checkbox !== selectAllCheckbox) {
                    checkbox.checked = selectAllCheckbox.checked;
                }
            });
        } else {
            const allChecked = Array.from(this.allCheckboxes).every((checkbox) => checkbox.checked && checkbox !== selectAllCheckbox);
            selectAllCheckbox.checked = allChecked;
        }
    }

}
// add to window.Controllers with a name of the controller
if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["select-all-switch"] = SelectAllListController;