Source: control2/button/Option.js

(function() {
    /**
     * @typedef {Help4.control2.button.Button.Params} Help4.control2.button.Option.Params
     */

    /**
     * A button that can be used as a checkbox or a triple-value checkbox.
     * @augments Help4.control2.button.Button
     */
    Help4.control2.button.Option = class extends Help4.control2.button.Button {
        /**
         * @override
         * @param {Help4.control2.button.Option.Params} [params]
         */
        constructor(params) {
            const {APPEARANCES} = Help4.control2.button;

            params ||= {};

            let icon, role;
            if (params.appearance) {
                icon = params.appearance;
                role = params.appearance;
            } else {
                icon = APPEARANCES.checkbox;
                role = APPEARANCES.checkbox;
            }

            const T = Help4.jscore.ControlBase.TYPES;
            super(params, {
                params: {
                    active:     {type: T.atomic, init: false, allowTypeOverride: true},  // set atomic here to override boolean type!

                    icon:       {init: icon},
                    role:       {init: role},

                    appearance: {init: APPEARANCES.checkbox}
                },
                config: {
                    css: 'button-option option'
                }
            });
        }

        /**
         * @override
         * @param {Object} event - the received event
         */
        onEvent(event) {
            if (this.disabled) return;

            const key = Help4.service.HotkeyService.getKey(event);
            if (Help4.includes(['space', 'enter'], key)) event.type = 'click';

            if (event.type === 'click') {
                // checkbox unchecked (false, 0): check it
                // checkbox partially checked (2): check it
                // checkbox checked (true, 1): uncheck it
                const {active} = this;  // do not invoke getter twice
                const result = this.active = active > 1 || !active;
                this._fireEvent({type: 'select', data: {active: result, event: event}});
            }

            super.onEvent(event);
        }

        /**
         * @override
         * @param {Help4.jscore.ControlBase.PropertyChangeEvent} event - the change event
         */
        _applyPropertyToDom({name, value, oldValue}) {
            if (name === 'active') {
                // active will be set to {true,false,0,1,2}
                // 0,false: not active / not checked
                // 1,true : active / checked
                // 2      : partially (e.g. for subtrees, in case only some sub-nodes are checked)
                if (this.appearance === Help4.control2.button.APPEARANCES.checkbox) {
                    const {ICONS} = Help4.control2;
                    this.icon = value === 2 ? ICONS.minus : ICONS.checkbox;
                }

                value = !!value;
            }

            super._applyPropertyToDom({name, value, oldValue});
        }
    }
})();