bootstrap-modbox
Version:
Native JavaScript wrapper for simple Bootstrap 5 modals. Provides support for alert, confirm, and prompt modals, as well as advanced custom dialogs.
57 lines (56 loc) • 10.5 kB
JavaScript
/*
* bootstrap-modbox
* Native JavaScript wrapper for simple Bootstrap 5 modals. Provides support for alert, confirm, and prompt modals, as well as advanced custom dialogs.
*
* version: 1.7.0
* author: Eric Robertson
* license: MIT
*
* https://erobertson42.github.io/bootstrap-modbox/
*/
class modbox{static version="1.7.0";#options;#modal;#modalEl;#footer;static#bootstrapModal;static#defaultOptions={icon:null,style:"white",titleStyle:null,title:"Information",body:"",message:"",size:null,center:!1,fade:!0,show:!1,relatedTarget:void 0,scrollable:!0,destroyOnClose:!1,defaultButton:!0,swapButtonOrder:!1,justifyButtons:null,showHeaderClose:!0,headerCloseStyle:null,events:{},buttons:[],okButton:{label:"OK",style:"primary"},closeButton:{label:"Close",style:"secondary"},input:{type:"text",class:"",value:"",title:null,placeholder:null,autocomplete:"off",minlength:null,maxlength:null,pattern:null,required:!1,sanitizer:!1},sanitizer:modbox.#sanitizeString};static#defaultButtonOptions={label:"Close",style:"secondary",class:"",outline:!1,size:null,icon:null,title:null,disabled:!1,close:!0,callback:null};static#modalDefaults={alert:{title:"Alert"},info:{style:"info",title:"Information"},success:{style:"success",title:"Success"},warning:{style:"warning",title:"Warning"},danger:{style:"danger",title:"Error"},confirm:{title:"Confirm"},prompt:{title:"Prompt",input:{id:modbox.#getUID("modbox-input-")}}};static#getUID(t="modbox-"){return t+Date.now()+Math.floor(1e4*Math.random())}static#typeof(t){return"object"==typeof t?Object.prototype.toString.call(t).slice(8,-1).toLowerCase():typeof t}static#deepMerge(i,s){const n={...i,...s};return Object.keys(n).forEach(t=>{var o=i[t],e=s[t];"object"===modbox.#typeof(o)&&"object"===modbox.#typeof(e)&&(n[t]=modbox.#deepMerge(o,e))}),n}static#checkUserOptions(t){return"string"==typeof t?{body:t}:t}static#mergeModalOptions(t,o={}){return modbox.#deepMerge(modbox.#deepMerge(modbox.#defaultOptions,modbox.#modalDefaults[t]),modbox.#checkUserOptions(o))}static#sanitizeString(t=""){return t}static#buildPromiseModal(d={},a="alert"){return d={...d,destroyOnClose:!0,defaultButton:!1,buttons:[]},new Promise((s,t)=>{const n=new modbox(d),o=[d.closeButton];if(["confirm","prompt"].includes(a)){let i=()=>s();if("prompt"===a&&"object"===modbox.#typeof(d.input)){let e=!1;(!0===d.input.required||"number"==typeof d.input.minlength||"string"==typeof d.input.pattern&&d.input.pattern.length)&&(d.okButton.close=!1,e=!0),i=()=>{const t=n.modalEl.querySelector(`#${d.input.id}`);if(!0!==e||t.reportValidity()){const o="function"==typeof n.options.input.sanitizer?n.options.input.sanitizer:!0===n.options.input.sanitizer?n.options.sanitizer:modbox.#sanitizeString;s(o(t.value)),n.hide()}}}o.unshift({...d.okButton,callback:function(t,o){"function"==typeof d.okButton.callback&&d.okButton.callback.call(this,t,o),(null!=t.defaultPrevented?t.defaultPrevented:!1===t.returnValue)||i()}})}const[e]=o.map(t=>n.addButton(t));"prompt"===a&&"object"===modbox.#typeof(d.input)&&n.modalEl.querySelector(`#${d.input.id}`).addEventListener("keyup",t=>{"Enter"===t.key&&e.click()}),n.addEvent("hidden",()=>{"alert"===a?s():["confirm","prompt"].includes(a)&&document.activeElement!==e&&t()}),n.show()})}#buildModal(){var t=["primary","secondary","success","danger","dark","body"].includes(this.#options.style),o=this.#options.titleStyle||(t?"white":"dark"),t=`btn-close ${"white"===this.#options.headerCloseStyle||!this.#options.headerCloseStyle&&t?"btn-close-white":""}`;modbox.container.insertAdjacentHTML("beforeend",this.#options.sanitizer(`
<div class="modal ${this.#options.fade?"fade":""}" id="${this.#options.id}" tabindex="-1" aria-labelledby="${this.#options.id}-title" aria-hidden="true">
<div class="modal-dialog ${this.#options.scrollable?"modal-dialog-scrollable":""} ${this.#options.center?"modal-dialog-centered":""} ${this.#options.size?`modal-${this.#options.size}`:""}">
<div class="modal-content">
<div class="modal-header ${this.#options.style?`bg-${this.#options.style}`:""} ${this.#options.title?"":"d-none"}">
<div class="modal-title h5 text-${o}">
${this.#options.icon?`<i class="${this.#options.icon} me-3"></i>`:""}
<span id="${this.#options.id}-title">${this.#options.title}</span>
</div>
<button type="button" class="${t} ${!1===this.#options.showHeaderClose?"d-none":""}" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
${this.#options.body}
</div>
<div class="modal-footer ${this.#options.justifyButtons?`d-flex justify-content-${this.#options.justifyButtons}`:""}"></div>
</div>
</div>
</div>
`.trim())),this.#modalEl=modbox.container.querySelector(`#${this.#options.id}`),this.#footer=this.#modalEl.querySelector(".modal-footer"),this.#addButtons()}#addButtons(){Array.isArray(this.#options.buttons)||(this.#options.buttons=[]),0===this.#options.buttons.length&&!0===this.#options.defaultButton?this.#options.buttons=[modbox.#defaultButtonOptions]:this.#footer.classList.add("d-none"),this.#options.buttons.forEach(t=>this.addButton(t))}#addEvents(){Object.entries(this.#options.events).forEach(([t,o])=>{this.addEvent(t,o)}),!0===this.#options.destroyOnClose&&this.addEvent("hidden",()=>this.destroy())}constructor(t={}){if(!modbox.#bootstrapModal){if("object"!=typeof bootstrap)throw new Error('The "modbox.bootstrapModal" property is undefined. If importing Bootstrap as an ES module, you must also manually set this property. See the modbox README/docs for more info.');modbox.#bootstrapModal=bootstrap.Modal}if(modbox.#defaultOptions={...modbox.#bootstrapModal.Default,...modbox.#defaultOptions},this.#options={...modbox.#defaultOptions,id:modbox.#getUID(),...modbox.#checkUserOptions(t)},"string"!=typeof this.#options.body||!this.#options.body.length){if("string"!=typeof this.#options.message||!this.#options.message.length)throw new Error('The "body" or "message" configuration option is required (string).');this.#options.body=this.#options.message}var o,e;this.#buildModal(),this.#modal=new modbox.#bootstrapModal(this.#modalEl,([{backdrop:o,keyboard:e,focus:t}]=[this.#options],{backdrop:o,keyboard:e,focus:t})),this.#addEvents(),!0===this.#options.show&&this.show(this.#options.relatedTarget)}get options(){return this.#options}get modal(){return this.#modal}get modalEl(){return this.#modalEl}get buttons(){return[...this.#footer.querySelectorAll("button")]}static get bootstrapModal(){return modbox.#bootstrapModal}static set bootstrapModal(t){modbox.#bootstrapModal=t}static get defaultOptions(){return modbox.#defaultOptions}static set defaultOptions(t={}){modbox.#defaultOptions=modbox.#deepMerge(modbox.#defaultOptions,t)}static get defaultButtonOptions(){return modbox.#defaultButtonOptions}static set defaultButtonOptions(t={}){modbox.#defaultButtonOptions={...modbox.#defaultButtonOptions,...t}}static setDefaults(t,o={}){if(!(t="error"===(t=t.trim?.().toLowerCase?.())?"danger":t)||!["alert","info","success","warning","danger","confirm","prompt"].includes(t))throw new Error("Invalid modal type.");o="object"===modbox.#typeof(o)?o:{},modbox.#modalDefaults[t]=modbox.#deepMerge(modbox.#modalDefaults[t],o)}addButton(t={},o=this.#options.swapButtonOrder){this.#footer.classList.remove("d-none");var e=o?"afterbegin":"beforeend";if("string"==typeof t&&t.length){this.#footer.insertAdjacentHTML(e,this.#options.sanitizer(t));var i=this.buttons;return i[o?0:i.length-1]}const s={...modbox.#defaultButtonOptions,id:modbox.#getUID("modbox-btn-"),...t};this.#footer.insertAdjacentHTML(e,this.#options.sanitizer(`
<button
type="button"
class="btn btn-${s.outline?"outline-":""}${s.style} ${s.class} ${s.size?`btn-${s.size}`:""}"
id="${s.id}"
${s.title?`title="${s.title}"`:""}
${s.close?'data-bs-dismiss="modal"':""}
${s.disabled?"disabled":""}
>
${s.icon?`<i class="${s.icon} me-2"></i>`:""}${s.label}
</button>
`.trim()));const n=this.#footer.querySelector(`#${s.id}`);return n&&"function"==typeof s.callback&&n.addEventListener("click",t=>s.callback.call(n,t,this)),n}addEvent(t,o){["show","shown","hide","hidden","hidePrevented"].includes(t)&&"function"==typeof o&&this.#modalEl.addEventListener(`${t}.bs.modal`,t=>o.call(this.#modalEl,t,this))}destroy(){this.dispose(),this.#modalEl.remove()}static get container(){let t=document.querySelector("#modbox-container");if(!t){const o=document.createElement("div");o.id="modbox-container",t=document.body.appendChild(o)}return t}static alert(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("alert",t))}static info(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("info",t))}static success(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("success",t))}static warning(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("warning",t))}static danger(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("danger",t))}static error(t={}){return modbox.danger(t)}static confirm(t={}){return modbox.#buildPromiseModal(modbox.#mergeModalOptions("confirm",t),"confirm")}static prompt(t={}){const o=modbox.#mergeModalOptions("prompt",t);return"regexp"===modbox.#typeof(o.input?.pattern)&&(o.input.pattern=o.input.pattern.source),o.body=`
${o.body?`<p>${o.body}</p>`:""}
${"string"==typeof o.input?o.input:`<input
type="${o.input.type}"
class="form-control ${o.input.class}"
id="${o.input.id}"
value="${o.input.value}"
${o.input.title?`title="${o.input.title}"`:""}
${o.input.placeholder?`placeholder="${o.input.placeholder}"`:""}
${o.input.autocomplete?`autocomplete="${o.input.autocomplete}"`:""}
${"number"==typeof o.input.minlength?`minlength="${o.input.minlength}"`:""}
${"number"==typeof o.input.maxlength?`maxlength="${o.input.maxlength}"`:""}
${"string"==typeof o.input.pattern&&o.input.pattern.length?`pattern="${o.input.pattern}"`:""}
${o.input.required?"required":""}
>`}
`.trim(),modbox.#buildPromiseModal(o,"prompt")}toggle(){this.#modal.toggle()}show(t=this.#options.relatedTarget){this.#modal.show(t)}hide(){this.#modal.hide()}handleUpdate(){this.#modal.handleUpdate()}dispose(){this.#modal.dispose()}static getInstance(t){return modbox.#bootstrapModal.getInstance(t)}static getOrCreateInstance(t){return modbox.#bootstrapModal.getOrCreateInstance(t)}}
//# sourceMappingURL=bootstrap-modbox.min.js.map