Source: ui/CheckboxGroup.js

import { UiElement } from "./UiElement.js";
import { resolveTemplate } from "../utils/template/resolveTemplate.js";
import "./CheckboxGroup.css";

export { CheckboxGroup };

/**
 * A slider based on Date objects as specialization of Slider.
 * 
 * Events:
 * * changed
 * * selected
 * * deselected
 * 
 * @author awalter <andreas.walter@awi.de>
 * @author rhess <robin.hess@awi.de>
 * 
 * @memberof vef.ui
 */

class CheckboxGroup extends UiElement {

    options = {
        title: 'Checkbox Group',
        nodes: [],
        multiColumn: false
    };

    nodeTemplate = `
        <div class="checkboxgroup-checkbox" tabindex="0">
            <span class="fa-stack">
                <i class="far fa-square fa-stack-1x"></i>
                <i class="fas fa-check fa-stack-1x"></i>
            </span>
        </div>
        <div class="checkboxgroup-value" title="{description}">
            <div class="checkboxgroup-value-inner">{title}</div>
        </div>
    `;

    constructor(target, options) {
        super(target, {
            'changed': [],
            'selected': [],
            'deselected': [],
        });

        this.nodes = [];
        this.options = { ...this.options, ...options };

        this.setClass('checkboxgroup');
        if (this.options.multiColumn) this.setClass('multi-column');

        this.setNodes(this.options.nodes);
    }

    /**
     * @private
     */
    createNode_(config) {
        if (!config) return;

        const element = document.createElement("div");
        element.classList.add("checkboxgroup-item");

        if (typeof config.selected != "boolean") config.selected = false;
        if (config.selected) element.classList.add('selected');

        element.innerHTML = resolveTemplate(this.nodeTemplate, {
            description: config.description || config.title || '',
            title: config.title
        });

        const node = {
            element: element,
            config: config
        };

        element.addEventListener("click", (event) => {
            if (element.classList.contains("selected")) {
                this.deselect(node)
            } else {
                this.select(node)
            }
        });
        element.addEventListener('keypress', (e) => {
            if(e.key == "Enter"){
                if (element.classList.contains("selected")) {
                    this.deselect(node)
                } else {
                    this.select(node)
                }
            }
        });

        this.nodes.push(node);
        this.getElement().appendChild(element);
    }

    /**
     * @private
     */
    setNodes(nodes) {
        this.options.nodes = nodes;
        this.nodes = [];
        this.getElement().innerHTML = "";
        const rows = (this.options.multiColumn) ? Math.ceil(nodes.length / 2) : nodes.length;
        for (let i = 0; i < rows; ++i) {
            this.createNode_(nodes[i]);
            if (this.options.multiColumn) {
                this.createNode_(nodes[i + rows]);
            }
        }
    }

    /**
     * @private
     */
    select(node) {
        if (!node.config.selected) {
            if (this.options.singleSelect) this.deselectAll();
            node.config.selected = true;
            node.element.classList.add('selected');
            this.fire('selected', node);
            this.fire('changed', node);
        }
    }

    /**
     * @private
     */
    deselect(node) {
        if (node.config.selected) {
            node.config.selected = false;
            node.element.classList.remove('selected');
            this.fire('deselected', node);
            this.fire('changed', node);
        }
    }

    /**
     * @private
     */
    selectAll() {
        const changed = [];
        for (let i = 0; i < this.nodes.length; ++i) {
            const node = this.nodes[i];
            if (!node.config.selected) {
                node.config.selected = true;
                node.element.classList.add("selected");
                this.fire('selected', node);
                changed.push(node);
            }
        }
        if (changed.length) this.fire('changed', changed);
    }

    /**
     * @private
     */
    deselectAll() {
        const changed = [];
        for (let i = 0; i < this.nodes.length; ++i) {
            const node = this.nodes[i];
            if (node.config.selected) {
                node.config.selected = false;
                node.element.classList.remove("selected");
                this.fire('deselected', node);
                changed.push(node);
            }
        }
        if (changed.length) this.fire('changed', changed);
    }

    /**
     * @private
     */
    getSelected() {
        return this.nodes.filter(node => {
            return node.config.selected;
        });
    }

    /**
     * @private
     */
    getDeselected() {
        return this.nodes.filter(node => {
            return !node.config.selected;
        });
    }
}