Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"version":3,"file":"status.min.js","sources":["../../../src/local/dropdown/status.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Dropdown status JS controls.\n *\n * The status controls enable extra configurarions for the dropdown like:\n * - Sync the button text with the selected option.\n * - Update the status of the button when the selected option changes. This will\n *   trigger a \"change\" event when the status changes.\n *\n * @module      core/local/dropdown/status\n * @copyright   2023 Ferran Recio <ferran@moodle.com>\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {DropdownDialog} from 'core/local/dropdown/dialog';\n\nconst Selectors = {\n    checkedIcon: '[data-for=\"checkedIcon\"]',\n    option: '[role=\"option\"]',\n    optionItem: '[data-optionnumber]',\n    optionIcon: '.option-icon',\n    selectedOption: '[role=\"option\"][aria-selected=\"true\"]',\n    uncheckedIcon: '[data-for=\"uncheckedIcon\"]',\n};\n\nconst Classes = {\n    selected: 'selected',\n    disabled: 'disabled',\n    hidden: 'd-none',\n};\n\n/**\n * Dropdown dialog class.\n * @private\n */\nexport class DropdownStatus extends DropdownDialog {\n    /**\n     * Constructor.\n     * @param {HTMLElement} element The element to initialize.\n     */\n    constructor(element) {\n        super(element);\n        this.buttonSync = element.dataset.buttonSync == 'true';\n        this.updateStatus = element.dataset.updateStatus == 'true';\n    }\n\n    /**\n     * Initialize the subpanel element.\n     *\n     * This method adds the event listeners to the subpanel and the position classes.\n     * @private\n     */\n    init() {\n        super.init();\n\n        if (this.element.dataset.dropdownStatusInitialized) {\n            return;\n        }\n\n        this.panel.addEventListener('click', this._contentClickHandler.bind(this));\n\n        if (this.element.dataset.buttonSync == 'true') {\n            this.setButtonSyncEnabled(true);\n        }\n        if (this.element.dataset.updateStatus == 'true') {\n            this.setUpdateStatusEnabled(true);\n        }\n\n        this.element.dataset.dropdownStatusInitialized = true;\n    }\n\n    /**\n     * Handle click events on the status content.\n     * @param {Event} event The event.\n     * @private\n     */\n    _contentClickHandler(event) {\n        const option = event.target.closest(Selectors.option);\n        if (!option) {\n            return;\n        }\n        if (option.getAttribute('aria-disabled') === 'true') {\n            return;\n        }\n        if (option.getAttribute('aria-selected') === 'true') {\n            return;\n        }\n        if (this.isUpdateStatusEnabled()) {\n            this.setSelectedValue(option.dataset.value);\n        }\n    }\n\n    /**\n     * Sets the selected value.\n     * @param {string} value The value to set.\n     */\n    setSelectedValue(value) {\n        const selected = this.panel.querySelector(Selectors.selectedOption);\n        if (selected && selected.dataset.value === value) {\n            return;\n        }\n        if (selected) {\n            this._updateOptionChecked(selected, false);\n        }\n        const option = this.panel.querySelector(`${Selectors.option}[data-value=\"${value}\"]`);\n        if (option) {\n            this._updateOptionChecked(option, true);\n        }\n        if (this.isButtonSyncEnabled()) {\n            this.syncButtonText();\n        }\n        // Emit standard radio button event with the selected option.\n        this.element.dispatchEvent(new Event('change'));\n    }\n\n    /**\n     * Update the option checked content.\n     * @private\n     * @param {HTMLElement} option the option element to set\n     * @param {Boolean} checked the new checked value\n     */\n    _updateOptionChecked(option, checked) {\n        option.setAttribute('aria-selected', checked.toString());\n        option.classList.toggle(Classes.selected, checked);\n        option.classList.toggle(Classes.disabled, checked);\n\n        const optionItem = option.closest(Selectors.optionItem);\n        if (optionItem) {\n            this._updateOptionItemChecked(optionItem, checked);\n        }\n\n        if (checked) {\n            this.element.dataset.value = option.dataset.value;\n        } else if (this.element.dataset.value === option.dataset.value) {\n            delete this.element.dataset.value;\n        }\n    }\n\n    /**\n     * Update the option item checked content.\n     * @private\n     * @param {HTMLElement} optionItem\n     * @param {Boolean} checked\n     */\n    _updateOptionItemChecked(optionItem, checked) {\n        const selectedClasses = optionItem.dataset.selectedClasses ?? Classes.selected;\n        for (const selectedClass of selectedClasses.split(' ')) {\n            optionItem.classList.toggle(selectedClass, checked);\n        }\n        if (checked) {\n            optionItem.dataset.selected = checked;\n        } else {\n            delete optionItem?.dataset.selected;\n        }\n        const checkedIcon = optionItem.querySelector(Selectors.checkedIcon);\n        if (checkedIcon) {\n            checkedIcon.classList.toggle(Classes.hidden, !checked);\n        }\n        const uncheckedIcon = optionItem.querySelector(Selectors.uncheckedIcon);\n        if (uncheckedIcon) {\n            uncheckedIcon.classList.toggle(Classes.hidden, checked);\n        }\n    }\n\n\n    /**\n     * Return the selected value.\n     * @returns {string|null} The selected value.\n     */\n    getSelectedValue() {\n        const selected = this.panel.querySelector(Selectors.selectedOption);\n        return selected?.dataset.value ?? null;\n    }\n\n    /**\n     * Set the button sync value.\n     *\n     * If the sync is enabled, the button text will show the selected option.\n     *\n     * @param {Boolean} value The value to set.\n     */\n    setButtonSyncEnabled(value) {\n        if (value) {\n            this.element.dataset.buttonSync = 'true';\n        } else {\n            delete this.element.dataset.buttonSync;\n        }\n        if (value) {\n            this.syncButtonText();\n        }\n    }\n\n    /**\n     * Return if the button sync is enabled.\n     * @returns {Boolean} The button sync value.\n     */\n    isButtonSyncEnabled() {\n        return this.element.dataset.buttonSync == 'true';\n    }\n\n    /**\n     * Sync the button text with the selected option.\n     */\n    syncButtonText() {\n        const selected = this.panel.querySelector(Selectors.selectedOption);\n        if (!selected) {\n            return;\n        }\n        let newText = selected.textContent;\n        const optionIcon = this._getOptionIcon(selected);\n        if (optionIcon) {\n            newText = optionIcon.innerHTML + newText;\n        }\n        this.button.innerHTML = newText;\n    }\n\n    /**\n     * Set the update status value.\n     *\n     * @param {Boolean} value The value to set.\n     */\n    setUpdateStatusEnabled(value) {\n        if (value) {\n            this.element.dataset.updateStatus = 'true';\n        } else {\n            delete this.element.dataset.updateStatus;\n        }\n    }\n\n    /**\n     * Return if the update status is enabled.\n     * @returns {Boolean} The update status value.\n     */\n    isUpdateStatusEnabled() {\n        return this.element.dataset.updateStatus == 'true';\n    }\n\n    _getOptionIcon(option) {\n        const optionItem = option.closest(Selectors.optionItem);\n        if (!optionItem) {\n            return null;\n        }\n        return optionItem.querySelector(Selectors.optionIcon);\n    }\n\n}\n\n/**\n * Get the dropdown dialog instance form a selector.\n * @param {string} selector The query selector to init.\n * @returns {DropdownStatus|null} The dropdown dialog instance if any.\n */\nexport const getDropdownStatus = (selector) => {\n    const dropdownElement = document.querySelector(selector);\n    if (!dropdownElement) {\n        return null;\n    }\n    return new DropdownStatus(dropdownElement);\n};\n\n/**\n * Initialize module.\n *\n * @method\n * @param {string} selector The query selector to init.\n */\nexport const init = (selector) => {\n    const dropdown = getDropdownStatus(selector);\n    if (!dropdown) {\n        throw new Error(`Dopdown status element not found: ${selector}`);\n    }\n    dropdown.init();\n};\n"],"names":["Selectors","Classes","DropdownStatus","DropdownDialog","constructor","element","buttonSync","dataset","updateStatus","init","this","dropdownStatusInitialized","panel","addEventListener","_contentClickHandler","bind","setButtonSyncEnabled","setUpdateStatusEnabled","event","option","target","closest","getAttribute","isUpdateStatusEnabled","setSelectedValue","value","selected","querySelector","_updateOptionChecked","isButtonSyncEnabled","syncButtonText","dispatchEvent","Event","checked","setAttribute","toString","classList","toggle","optionItem","_updateOptionItemChecked","selectedClasses","selectedClass","split","checkedIcon","uncheckedIcon","getSelectedValue","newText","textContent","optionIcon","_getOptionIcon","innerHTML","button","getDropdownStatus","selector","dropdownElement","document","dropdown","Error"],"mappings":"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"}