MDL-79985 output: prepare dropdown components for forms
Add new features to the dropdown components (dialog and status) needed for using them in quick forms. Now the dialog dropdown have methods to disable the dropdown button (to disable the field). The status dropdown has one minor style bugfix and the selected item styles are now stored as a data attribute.
This commit is contained in:
@@ -232,6 +232,7 @@ Both classes provide the following methods:
|
||||
- `setVisible(Boolean)` to open or close the dropdown.
|
||||
- `isVisible()` to know if it is open or closed.
|
||||
- `setButtonContent(String)` to replace the button content.
|
||||
- `setButtonDisabled(Boolean)` to disable or enable the dropdown button.
|
||||
- `getElement()`to get the main HTMLElement to add eventListeners.
|
||||
|
||||
The following example uses the module to open the dropdown when an extra button is preset:
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@ define("core/local/dropdown/dialog",["exports","jquery","core/pagehelpers","core
|
||||
* @module core/local/dropdown/dialog
|
||||
* @copyright 2023 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.getDropdownDialog=_exports.DropdownDialog=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const Selectors_dropdownButton='[data-for="dropdowndialog_button"]',Selectors_dropdownDialog='[data-for="dropdowndialog_dialog"]';class DropdownDialog{constructor(element){this.element=element,this.button=element.querySelector(Selectors_dropdownButton),this.panel=element.querySelector(Selectors_dropdownDialog)}init(){this.element.dataset.dropdownDialogInitialized||(this.button.addEventListener("keydown",this._buttonKeyHandler.bind(this)),this.panel.addEventListener("keydown",this._contentKeyHandler.bind(this)),this.element.dataset.dropdownDialogInitialized=!0)}_buttonKeyHandler(event){if("ArrowUp"===event.key||"ArrowLeft"===event.key)return event.stopPropagation(),event.preventDefault(),void this.setVisible(!1);"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(event.stopPropagation(),event.preventDefault(),this.setVisible(!0),this._focusPanelContent())}_contentKeyHandler(event){let newFocus=null;"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panel)),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panel)),"ArrowUp"!==event.key&&"ArrowLeft"!==event.key||(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panel,!1),newFocus||(newFocus=this.button)),"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panel,!1)),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/dropdown/dialog:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panel);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisible(visible){visible!==this.isVisible()&&(0,_jquery.default)(this.button).dropdown("toggle")}isVisible(){return"true"===this.button.getAttribute("aria-expanded")}setButtonContent(content){this.button.innerHTML=content}getElement(){return this.element}}_exports.DropdownDialog=DropdownDialog;const getDropdownDialog=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownDialog(dropdownElement):null};_exports.getDropdownDialog=getDropdownDialog;_exports.init=selector=>{const dropdown=getDropdownDialog(selector);if(!dropdown)throw new Error("Dopdown dialog element not found: ".concat(selector));dropdown.init()}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.getDropdownDialog=_exports.DropdownDialog=void 0,_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending);const Selectors_dropdownButton='[data-for="dropdowndialog_button"]',Selectors_dropdownDialog='[data-for="dropdowndialog_dialog"]';class DropdownDialog{constructor(element){this.element=element,this.button=element.querySelector(Selectors_dropdownButton),this.panel=element.querySelector(Selectors_dropdownDialog)}init(){this.element.dataset.dropdownDialogInitialized||(this.button.addEventListener("keydown",this._buttonKeyHandler.bind(this)),this.panel.addEventListener("keydown",this._contentKeyHandler.bind(this)),this.element.dataset.dropdownDialogInitialized=!0)}_buttonKeyHandler(event){if("ArrowUp"===event.key||"ArrowLeft"===event.key)return event.stopPropagation(),event.preventDefault(),void this.setVisible(!1);"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(event.stopPropagation(),event.preventDefault(),this.setVisible(!0),this._focusPanelContent())}_contentKeyHandler(event){let newFocus=null;"End"===event.key&&(newFocus=(0,_pagehelpers.lastFocusableElement)(this.panel)),"Home"===event.key&&(newFocus=(0,_pagehelpers.firstFocusableElement)(this.panel)),"ArrowUp"!==event.key&&"ArrowLeft"!==event.key||(newFocus=(0,_pagehelpers.previousFocusableElement)(this.panel,!1),newFocus||(newFocus=this.button)),"ArrowDown"!==event.key&&"ArrowRight"!==event.key||(newFocus=(0,_pagehelpers.nextFocusableElement)(this.panel,!1)),null!==newFocus&&(event.stopPropagation(),event.preventDefault(),newFocus.focus())}_focusPanelContent(){const pendingPromise=new _pending.default("core/dropdown/dialog:focuscontent");setTimeout((()=>{const firstFocusable=(0,_pagehelpers.firstFocusableElement)(this.panel);firstFocusable&&firstFocusable.focus(),pendingPromise.resolve()}),100)}setVisible(visible){visible!==this.isVisible()&&(0,_jquery.default)(this.button).dropdown("toggle")}isVisible(){return"true"===this.button.getAttribute("aria-expanded")}setButtonContent(content){this.button.innerHTML=content}setButtonDisabled(disabled){disabled?this.button.setAttribute("disabled","disabled"):this.button.removeAttribute("disabled")}getElement(){return this.element}}_exports.DropdownDialog=DropdownDialog;const getDropdownDialog=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownDialog(dropdownElement):null};_exports.getDropdownDialog=getDropdownDialog;_exports.init=selector=>{const dropdown=getDropdownDialog(selector);if(!dropdown)throw new Error("Dopdown dialog element not found: ".concat(selector));dropdown.init()}}));
|
||||
|
||||
//# sourceMappingURL=dialog.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+2
-2
@@ -7,10 +7,10 @@ define("core/local/dropdown/status",["exports","core/local/dropdown/dialog"],(fu
|
||||
* - Update the status of the button when the selected option changes. This will
|
||||
* trigger a "change" event when the status changes.
|
||||
*
|
||||
* @module core/local/dropdown/dialog
|
||||
* @module core/local/dropdown/status
|
||||
* @copyright 2023 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const Selectors_checkedIcon='[data-for="checkedIcon"]',Selectors_option='[role="option"]',Selectors_optionItem="[data-optionnumber]",Selectors_optionIcon=".option-icon",Selectors_selectedOption='[role="option"][aria-selected="true"]',Selectors_uncheckedIcon='[data-for="uncheckedIcon"]',Classes_selectedBg="bg-light",Classes_selected="selected",Classes_disabled="disabled",Classes_hidden="d-none";class DropdownStatus extends _dialog.DropdownDialog{constructor(element){super(element),this.buttonSync="true"==element.dataset.buttonSync,this.updateStatus="true"==element.dataset.updateStatus}init(){super.init(),this.element.dataset.dropdownStatusInitialized||(this.panel.addEventListener("click",this._contentClickHandler.bind(this)),"true"==this.element.dataset.buttonSync&&this.setButtonSyncEnabled(!0),"true"==this.element.dataset.updateStatus&&this.setUpdateStatusEnabled(!0),this.element.dataset.dropdownStatusInitialized=!0)}_contentClickHandler(event){const option=event.target.closest(Selectors_option);option&&"true"!==option.getAttribute("aria-disabled")&&"true"!==option.getAttribute("aria-selected")&&this.isUpdateStatusEnabled()&&this.setSelectedValue(option.dataset.value)}setSelectedValue(value){const selected=this.panel.querySelector(Selectors_selectedOption);if(selected&&selected.dataset.value===value)return;selected&&this._updateOptionChecked(selected,!1);const option=this.panel.querySelector("".concat(Selectors_option,'[data-value="').concat(value,'"]'));option&&this._updateOptionChecked(option,!0),this.isButtonSyncEnabled()&&this.syncButtonText(),this.element.dispatchEvent(new Event("change"))}_updateOptionChecked(option,checked){option.setAttribute("aria-selected",checked.toString()),option.classList.toggle(Classes_selected,checked),option.classList.toggle(Classes_disabled,checked);const optionItem=option.closest(Selectors_optionItem);optionItem&&this._updateOptionItemChecked(optionItem,checked),checked?this.element.dataset.value=option.dataset.value:this.element.dataset.value===option.dataset.value&&delete this.element.dataset.value}_updateOptionItemChecked(optionItem,checked){optionItem.classList.toggle(Classes_selectedBg,checked),optionItem.classList.toggle(Classes_selected,checked),checked?optionItem.dataset.selected=checked:null==optionItem||delete optionItem.dataset.selected;const checkedIcon=optionItem.querySelector(Selectors_checkedIcon);checkedIcon&&checkedIcon.classList.toggle(Classes_hidden,!checked);const uncheckedIcon=optionItem.querySelector(Selectors_uncheckedIcon);uncheckedIcon&&uncheckedIcon.classList.toggle(Classes_hidden,checked)}getSelectedValue(){var _selected$dataset$val;const selected=this.panel.querySelector(Selectors_selectedOption);return null!==(_selected$dataset$val=null==selected?void 0:selected.dataset.value)&&void 0!==_selected$dataset$val?_selected$dataset$val:null}setButtonSyncEnabled(value){value?this.element.dataset.buttonSync="true":delete this.element.dataset.buttonSync,value&&this.syncButtonText()}isButtonSyncEnabled(){return"true"==this.element.dataset.buttonSync}syncButtonText(){const selected=this.panel.querySelector(Selectors_selectedOption);if(!selected)return;let newText=selected.textContent;const optionIcon=this._getOptionIcon(selected);optionIcon&&(newText=optionIcon.innerHTML+newText),this.button.innerHTML=newText}setUpdateStatusEnabled(value){value?this.element.dataset.updateStatus="true":delete this.element.dataset.updateStatus}isUpdateStatusEnabled(){return"true"==this.element.dataset.updateStatus}_getOptionIcon(option){const optionItem=option.closest(Selectors_optionItem);return optionItem?optionItem.querySelector(Selectors_optionIcon):null}}_exports.DropdownStatus=DropdownStatus;const getDropdownStatus=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownStatus(dropdownElement):null};_exports.getDropdownStatus=getDropdownStatus;_exports.init=selector=>{const dropdown=getDropdownStatus(selector);if(!dropdown)throw new Error("Dopdown status element not found: ".concat(selector));dropdown.init()}}));
|
||||
const Selectors_checkedIcon='[data-for="checkedIcon"]',Selectors_option='[role="option"]',Selectors_optionItem="[data-optionnumber]",Selectors_optionIcon=".option-icon",Selectors_selectedOption='[role="option"][aria-selected="true"]',Selectors_uncheckedIcon='[data-for="uncheckedIcon"]',Classes_selected="selected",Classes_disabled="disabled",Classes_hidden="d-none";class DropdownStatus extends _dialog.DropdownDialog{constructor(element){super(element),this.buttonSync="true"==element.dataset.buttonSync,this.updateStatus="true"==element.dataset.updateStatus}init(){super.init(),this.element.dataset.dropdownStatusInitialized||(this.panel.addEventListener("click",this._contentClickHandler.bind(this)),"true"==this.element.dataset.buttonSync&&this.setButtonSyncEnabled(!0),"true"==this.element.dataset.updateStatus&&this.setUpdateStatusEnabled(!0),this.element.dataset.dropdownStatusInitialized=!0)}_contentClickHandler(event){const option=event.target.closest(Selectors_option);option&&"true"!==option.getAttribute("aria-disabled")&&"true"!==option.getAttribute("aria-selected")&&this.isUpdateStatusEnabled()&&this.setSelectedValue(option.dataset.value)}setSelectedValue(value){const selected=this.panel.querySelector(Selectors_selectedOption);if(selected&&selected.dataset.value===value)return;selected&&this._updateOptionChecked(selected,!1);const option=this.panel.querySelector("".concat(Selectors_option,'[data-value="').concat(value,'"]'));option&&this._updateOptionChecked(option,!0),this.isButtonSyncEnabled()&&this.syncButtonText(),this.element.dispatchEvent(new Event("change"))}_updateOptionChecked(option,checked){option.setAttribute("aria-selected",checked.toString()),option.classList.toggle(Classes_selected,checked),option.classList.toggle(Classes_disabled,checked);const optionItem=option.closest(Selectors_optionItem);optionItem&&this._updateOptionItemChecked(optionItem,checked),checked?this.element.dataset.value=option.dataset.value:this.element.dataset.value===option.dataset.value&&delete this.element.dataset.value}_updateOptionItemChecked(optionItem,checked){var _optionItem$dataset$s;const selectedClasses=null!==(_optionItem$dataset$s=optionItem.dataset.selectedClasses)&&void 0!==_optionItem$dataset$s?_optionItem$dataset$s:Classes_selected;for(const selectedClass of selectedClasses.split(" "))optionItem.classList.toggle(selectedClass,checked);checked?optionItem.dataset.selected=checked:null==optionItem||delete optionItem.dataset.selected;const checkedIcon=optionItem.querySelector(Selectors_checkedIcon);checkedIcon&&checkedIcon.classList.toggle(Classes_hidden,!checked);const uncheckedIcon=optionItem.querySelector(Selectors_uncheckedIcon);uncheckedIcon&&uncheckedIcon.classList.toggle(Classes_hidden,checked)}getSelectedValue(){var _selected$dataset$val;const selected=this.panel.querySelector(Selectors_selectedOption);return null!==(_selected$dataset$val=null==selected?void 0:selected.dataset.value)&&void 0!==_selected$dataset$val?_selected$dataset$val:null}setButtonSyncEnabled(value){value?this.element.dataset.buttonSync="true":delete this.element.dataset.buttonSync,value&&this.syncButtonText()}isButtonSyncEnabled(){return"true"==this.element.dataset.buttonSync}syncButtonText(){const selected=this.panel.querySelector(Selectors_selectedOption);if(!selected)return;let newText=selected.textContent;const optionIcon=this._getOptionIcon(selected);optionIcon&&(newText=optionIcon.innerHTML+newText),this.button.innerHTML=newText}setUpdateStatusEnabled(value){value?this.element.dataset.updateStatus="true":delete this.element.dataset.updateStatus}isUpdateStatusEnabled(){return"true"==this.element.dataset.updateStatus}_getOptionIcon(option){const optionItem=option.closest(Selectors_optionItem);return optionItem?optionItem.querySelector(Selectors_optionIcon):null}}_exports.DropdownStatus=DropdownStatus;const getDropdownStatus=selector=>{const dropdownElement=document.querySelector(selector);return dropdownElement?new DropdownStatus(dropdownElement):null};_exports.getDropdownStatus=getDropdownStatus;_exports.init=selector=>{const dropdown=getDropdownStatus(selector);if(!dropdown)throw new Error("Dopdown status element not found: ".concat(selector));dropdown.init()}}));
|
||||
|
||||
//# sourceMappingURL=status.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -165,6 +165,18 @@ export class DropdownDialog {
|
||||
this.button.innerHTML = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the disabled state of the button.
|
||||
* @param {Boolean} disabled
|
||||
*/
|
||||
setButtonDisabled(disabled) {
|
||||
if (disabled) {
|
||||
this.button.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
this.button.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the main dropdown HTML element.
|
||||
* @returns {HTMLElement} The element.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* - Update the status of the button when the selected option changes. This will
|
||||
* trigger a "change" event when the status changes.
|
||||
*
|
||||
* @module core/local/dropdown/dialog
|
||||
* @module core/local/dropdown/status
|
||||
* @copyright 2023 Ferran Recio <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -38,7 +38,6 @@ const Selectors = {
|
||||
};
|
||||
|
||||
const Classes = {
|
||||
selectedBg: 'bg-light',
|
||||
selected: 'selected',
|
||||
disabled: 'disabled',
|
||||
hidden: 'd-none',
|
||||
@@ -158,8 +157,10 @@ export class DropdownStatus extends DropdownDialog {
|
||||
* @param {Boolean} checked
|
||||
*/
|
||||
_updateOptionItemChecked(optionItem, checked) {
|
||||
optionItem.classList.toggle(Classes.selectedBg, checked);
|
||||
optionItem.classList.toggle(Classes.selected, checked);
|
||||
const selectedClasses = optionItem.dataset.selectedClasses ?? Classes.selected;
|
||||
for (const selectedClass of selectedClasses.split(' ')) {
|
||||
optionItem.classList.toggle(selectedClass, checked);
|
||||
}
|
||||
if (checked) {
|
||||
optionItem.dataset.selected = checked;
|
||||
} else {
|
||||
|
||||
@@ -80,6 +80,11 @@ class dialog implements named_templatable, renderable {
|
||||
*/
|
||||
protected $extras = [];
|
||||
|
||||
/**
|
||||
* @var bool if the element is disabled.
|
||||
*/
|
||||
protected $disabled = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -190,6 +195,14 @@ class dialog implements named_templatable, renderable {
|
||||
$this->dropdownposition = $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dropdown disabled attribute.
|
||||
* @param bool $disabled the disabled value
|
||||
*/
|
||||
public function set_disabled(bool $disabled) {
|
||||
$this->disabled = $disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template (core/inplace_editable).
|
||||
*
|
||||
@@ -220,6 +233,9 @@ class dialog implements named_templatable, renderable {
|
||||
'dialogclasses' => $this->dropdownwidth,
|
||||
'extras' => $extras,
|
||||
];
|
||||
if ($this->disabled) {
|
||||
$data['disabledbutton'] = true;
|
||||
}
|
||||
// Bootstrap 4 dropdown position still uses left and right literals.
|
||||
$data["position"] = $this->dropdownposition;
|
||||
if (right_to_left()) {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
}} {{#selected}} border bg-primary-light selected {{/selected}}"
|
||||
data-optionnumber="{{optionnumber}}"
|
||||
data-selected="{{selected}}"
|
||||
data-selected-classes = "border bg-primary-light selected"
|
||||
>
|
||||
<div class="option-select-indicator">
|
||||
<span class="{{^selected}} d-none {{/selected}}" data-for="checkedIcon">
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
],
|
||||
"buttonclasses": "extraclasses",
|
||||
"dialogclasses": "extraclasses",
|
||||
"disabledbutton": false,
|
||||
"classes": "extraclasses"
|
||||
}
|
||||
}}
|
||||
@@ -78,6 +79,7 @@
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
data-for="dropdowndialog_button"
|
||||
{{#disabledbutton}} disabled {{/disabledbutton}}
|
||||
>
|
||||
{{$ buttoncontent }}
|
||||
{{{ buttoncontent }}}
|
||||
|
||||
Reference in New Issue
Block a user