From 0e79aa275b34359e11de987bf92765d968d49138 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 Jan 2024 20:57:41 +0000 Subject: [PATCH] Added support for movable modals --- bsdialog5.js | 132 +++++++++++++++++++++++++++++------------------ bsdialog5.min.js | 4 +- 2 files changed, 84 insertions(+), 52 deletions(-) diff --git a/bsdialog5.js b/bsdialog5.js index c03d5af..bf04f5d 100644 --- a/bsdialog5.js +++ b/bsdialog5.js @@ -1,6 +1,6 @@ /** * BSDialog5 - * @version v0.2.1.068 (2023/11/14 2028) + * @version v0.2.2.003 (2024/01/22 2037) */ class BSDialog05 { @@ -8,48 +8,6 @@ class BSDialog05 { } - get Options() { - return { - ShowModal: { - ID: null, - Title: "", - Message: "", - URL: null, - Size: "md", - Colour: "secondary", - ShowFooter: true, - EasyClose: true - }, - ShowPrompt: { - Type: "button", - Title: "", - Message: "", - Size: "md", - EasyClose: true, - Buttons: [ - { Label: "Yes", Value: "Yes", Colour: "primary" }, - { Label: "No", Value: "No", Colour: "secondary" }, - { Label: "Cancel", Value: "Cancel", Colour: "secondary" } - ], - Textbox: { - Label: "", - LabelSize: 4, - Placeholder: "", - Value: "", - BoxSize: 8 - } - }, - UpdateModal: { - ID: null, - Title: null, - Body: null, - URL: null, - Footer: null, - Size: null - } - }; - } - get #prefix() { return "bsdia5_"; } @@ -71,9 +29,19 @@ class BSDialog05 { async Show(options) { const a = this; - const _options = Object.assign(a.Options.ShowModal, options); + const _options = Object.assign({ + ID: null, + Title: "", + Message: "", + URL: null, + Size: "md", + Colour: "secondary", + ShowFooter: true, + EasyClose: true, + IsMovable: true + }, options); - a.#addModal(_options.ID, _options.Title, _options.Size, _options.ShowFooter, _options.Colour); + a.#addModal(_options.ID, _options.Title, _options.Size, _options.ShowFooter, _options.IsMovable, _options.Colour); (new bootstrap.Modal(document.getElementById(a.#prefix + _options.ID), { backdrop: _options.EasyClose @@ -89,7 +57,26 @@ class BSDialog05 { async Prompt(options) { const a = this; const id = "prompt" + Math.floor(Math.random() * 10000) + 1000; - const _options = Object.assign(a.Options.ShowPrompt, options); + const _options = Object.assign({ + Type: "button", + Title: "", + Message: "", + Size: "md", + EasyClose: true, + IsMovable: true, + Buttons: [ + { Label: "Yes", Value: "Yes", Colour: "primary" }, + { Label: "No", Value: "No", Colour: "secondary" }, + { Label: "Cancel", Value: "Cancel", Colour: "secondary" } + ], + Textbox: { + Label: "", + LabelSize: 4, + Placeholder: "", + Value: "", + BoxSize: 8 + } + }, options); switch (_options.Type) { case "textbox": @@ -133,7 +120,14 @@ class BSDialog05 { async Update(options) { const a = this; - let _options = Object.assign(a.Options.UpdateModal, options); + let _options = Object.assign({ + ID: null, + Title: null, + Body: null, + URL: null, + Footer: null, + Size: null + }, options); const modal = a.Find(_options.ID); if (modal === null) { @@ -206,7 +200,7 @@ class BSDialog05 { } - #addModal(id, title, size, showFooter, closeColour) { + #addModal(id, title, size, showFooter, isMovable, closeColour) { const a = this; // don't allow duplicates @@ -262,6 +256,42 @@ class BSDialog05 { a.#toggleSize(id); }); + + if (isMovable) { + const panel = modal.Modal.querySelectorAll(".modal-dialog")[0]; + let isMovable = false; + let currentPos = { X: 0, Y: 0 }; + let movePos = { X: 0, Y: 0 }; + + modal.Title.addEventListener("mousedown", function(e) { + if ((e.buttons == 1) && !isMovable) { + movePos = { X: e.clientX, Y: e.clientY }; + currentPos = { + X: (a.#isNullOrWhitespace(panel.style.left) ? 0 : parseInt(panel.style.left.replace("px", ""))), + Y: (a.#isNullOrWhitespace(panel.style.top) ? 0 : parseInt(panel.style.top.replace("px", ""))) + }; + isMovable = true; + + modal.Title.style.cursor = "move"; + + e.preventDefault(); + } + }); + + document.addEventListener("mouseup", function(e) { + if (isMovable) { + isMovable = false; + modal.Title.style.cursor = "default"; + } + }); + + document.addEventListener("mousemove", function(e) { + if ((e.buttons == 1) && isMovable) { + panel.style.left = ((e.clientX - movePos.X) + currentPos.X) + 'px'; + panel.style.top = ((e.clientY - movePos.Y) + currentPos.Y) + 'px'; + } + }); + } } async #showButtonPrompt(id, options) { @@ -273,7 +303,8 @@ class BSDialog05 { Title: options.Title, Message: options.Message, Size: options.Size, - EasyClose: options.EasyClose + EasyClose: options.EasyClose, + IsMovable: options.IsMovable }); let html = ''; @@ -325,7 +356,8 @@ class BSDialog05 { Title: options.Title, Message: options.Message, Size: options.Size, - EasyClose: options.EasyClose + EasyClose: options.EasyClose, + IsMovable: options.IsMovable }); let body = ''; diff --git a/bsdialog5.min.js b/bsdialog5.min.js index 42b2bdc..cbe6bd0 100644 --- a/bsdialog5.min.js +++ b/bsdialog5.min.js @@ -1,5 +1,5 @@ /** * BSDialog5 - * @version v0.2.1.068 (2023/11/14 2028) + * @version v0.2.2.003 (2024/01/22 2037) */ -class BSDialog05{constructor(){}get Options(){return{ShowModal:{ID:null,Title:"",Message:"",URL:null,Size:"md",Colour:"secondary",ShowFooter:!0,EasyClose:!0},ShowPrompt:{Type:"button",Title:"",Message:"",Size:"md",EasyClose:!0,Buttons:[{Label:"Yes",Value:"Yes",Colour:"primary"},{Label:"No",Value:"No",Colour:"secondary"},{Label:"Cancel",Value:"Cancel",Colour:"secondary"}],Textbox:{Label:"",LabelSize:4,Placeholder:"",Value:"",BoxSize:8}},UpdateModal:{ID:null,Title:null,Body:null,URL:null,Footer:null,Size:null}}}get#e(){return"bsdia5_"}get#t(){return document.getElementsByTagName("body")[0]}get#o(){return["modal-sm","modal-md","modal-lg","modal-xl","modal-xxl"]}async Show(e){const t=this,o=Object.assign(t.Options.ShowModal,e);t.#l(o.ID,o.Title,o.Size,o.ShowFooter,o.Colour),new bootstrap.Modal(document.getElementById(t.#e+o.ID),{backdrop:o.EasyClose}).show(),t.#a(o.URL)?await t.UpdateBodyRemote(o.ID,o.URL):await t.Update({ID:o.ID,Body:o.Message})}async Prompt(e){const t=this,o="prompt"+Math.floor(1e4*Math.random())+1e3,l=Object.assign(t.Options.ShowPrompt,e);return"textbox"===l.Type?await t.#s(o,l):await t.#i(o,l)}async Clear(){document.querySelectorAll(".modal").forEach((function(e){const t=bootstrap.Modal.getInstance(e);t&&t.hide()}))}async Close(e){const t=this;e.startsWith(t.#e)&&(e=e.substr(t.#e.length));const o=document.getElementById(t.#e+e),l=bootstrap.Modal.getInstance(o);l&&l.hide(),o&&o.parentNode.removeChild(o)}async Update(e){const t=this;let o=Object.assign(t.Options.UpdateModal,e);const l=t.Find(o.ID);null!==l&&(this.#n(o.Title)||(l.Title.innerHTML=o.Title),this.#n(o.Body)?this.#a(o.URL)&&await t.UpdateBodyRemote(o.ID,o.URL):t.#r(l.Body,o.Body),this.#n(o.Footer)||t.#r(l.Footer,o.Footer),this.#n(o.Size)||t.#d(o.ID,"modal-"+o.Size))}async UpdateBodyRemote(e,t){const o=this;o.Exists(e)&&await fetch(t,{cache:"no-cache",credentials:"same-origin"}).then((e=>e.text())).then((t=>{o.Update({ID:e,Body:t})})).catch((t=>{o.Update({ID:e,Body:"Error: "+t})}))}Exists(e){return null!==this.Find(e)}Find(e){const t=this.#t.querySelectorAll("#"+this.#e+e+".modal");return t?t.length<=0?null:{Title:t[0].querySelectorAll(".modal-title")[0],Header:t[0].querySelectorAll(".modal-header")[0],Body:t[0].querySelectorAll(".modal-body")[0],Footer:t[0].querySelectorAll(".modal-footer")[0],Close:t[0].querySelectorAll("[data-bs-dismiss='modal']"),Modal:t[0]}:null}#l(e,t,o,l,a){const s=this;let i=s.Find(e);if(null!==i)return;let n="";n+='",s.#c(s.#t,n),i=s.Find(e),null!==i&&(document.getElementById(s.#e+e).addEventListener("hidden.bs.modal",(function(t){s.Close(e)})),i.Title.addEventListener("dblclick",(function(t){t.stopPropagation(),t.preventDefault(),s.#u(e)})))}async#i(e,t){const o=this;return await new Promise((async(l,a)=>{await o.Show({ID:e,Title:t.Title,Message:t.Message,Size:t.Size,EasyClose:t.EasyClose});let s="";t.Buttons.forEach((function(e){s+='"})),o.Update({ID:e,Footer:s});const i=o.Find(e);i.Footer.querySelectorAll("button").forEach((function(t){t.addEventListener("click",(function(a){a.stopPropagation(),a.preventDefault();const s=t.getAttribute("data-prompt-value");o.Close(e),l(s)}))})),i.Close.forEach((function(t){t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault(),o.Close(e),l("")}))}))}))}async#s(e,t){const o=this;return await new Promise((async(l,a)=>{t.Buttons=[{Label:"OK",Value:"",Colour:"primary"},{Label:"Cancel",Value:"",Colour:"secondary"}],await o.Show({ID:e,Title:t.Title,Message:t.Message,Size:t.Size,EasyClose:t.EasyClose});let s="";o.#n(t.Message)||(s+="

"+t.Message+"

"),s+='
',o.#n(t.Textbox.Label)||t.Textbox.LabelSize<=0?(s+='
',s+='',s+="
"):(s+='",s+='
',s+='',s+="
"),s+="
";let i="";t.Buttons.forEach((function(e){i+='"})),o.Update({ID:e,Body:s,Footer:i});const n=o.Find(e),r=n.Footer.querySelectorAll("button");r[0].addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault();const a=n.Body.querySelectorAll("input")[0].value;await o.Close(e),l(a)})),r[1].addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault(),await o.Close(e),l("")})),n.Close.forEach((async function(t){t.addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault(),await o.Close(e),l("")}))}))}))}#c(e,t){let o=document.createElement("template");o.innerHTML=t,o=o.content.firstChild,e.appendChild(o)}#r(e,t){jQuery?jQuery(e).html(t):e.innerHTML=t}#n(e){return void 0===e||(null==e||(0==e||e.trim().length<=0))}#a(e){return!this.#n(e)&&!!(e.startsWith("http://")||e.startsWith("https://")||e.startsWith("/"))}#d(e,t){const o=this,l=o.Find(e);if(null!==l){for(let e=0;et.#o.length-1&&(l=0),o.Modal.classList.add(t.#o[l])}}var BSDialog5=new BSDialog05; \ No newline at end of file +class BSDialog05{constructor(){}get#e(){return"bsdia5_"}get#t(){return document.getElementsByTagName("body")[0]}get#l(){return["modal-sm","modal-md","modal-lg","modal-xl","modal-xxl"]}async Show(e){const t=this,l=Object.assign({ID:null,Title:"",Message:"",URL:null,Size:"md",Colour:"secondary",ShowFooter:!0,EasyClose:!0,IsMovable:!0},e);t.#o(l.ID,l.Title,l.Size,l.ShowFooter,l.IsMovable,l.Colour),new bootstrap.Modal(document.getElementById(t.#e+l.ID),{backdrop:l.EasyClose}).show(),t.#s(l.URL)?await t.UpdateBodyRemote(l.ID,l.URL):await t.Update({ID:l.ID,Body:l.Message})}async Prompt(e){const t=this,l="prompt"+Math.floor(1e4*Math.random())+1e3,o=Object.assign({Type:"button",Title:"",Message:"",Size:"md",EasyClose:!0,IsMovable:!0,Buttons:[{Label:"Yes",Value:"Yes",Colour:"primary"},{Label:"No",Value:"No",Colour:"secondary"},{Label:"Cancel",Value:"Cancel",Colour:"secondary"}],Textbox:{Label:"",LabelSize:4,Placeholder:"",Value:"",BoxSize:8}},e);return"textbox"===o.Type?await t.#a(l,o):await t.#i(l,o)}async Clear(){document.querySelectorAll(".modal").forEach((function(e){const t=bootstrap.Modal.getInstance(e);t&&t.hide()}))}async Close(e){const t=this;e.startsWith(t.#e)&&(e=e.substr(t.#e.length));const l=document.getElementById(t.#e+e),o=bootstrap.Modal.getInstance(l);o&&o.hide(),l&&l.parentNode.removeChild(l)}async Update(e){const t=this;let l=Object.assign({ID:null,Title:null,Body:null,URL:null,Footer:null,Size:null},e);const o=t.Find(l.ID);null!==o&&(this.#n(l.Title)||(o.Title.innerHTML=l.Title),this.#n(l.Body)?this.#s(l.URL)&&await t.UpdateBodyRemote(l.ID,l.URL):t.#r(o.Body,l.Body),this.#n(l.Footer)||t.#r(o.Footer,l.Footer),this.#n(l.Size)||t.#d(l.ID,"modal-"+l.Size))}async UpdateBodyRemote(e,t){const l=this;l.Exists(e)&&await fetch(t,{cache:"no-cache",credentials:"same-origin"}).then((e=>e.text())).then((t=>{l.Update({ID:e,Body:t})})).catch((t=>{l.Update({ID:e,Body:"Error: "+t})}))}Exists(e){return null!==this.Find(e)}Find(e){const t=this.#t.querySelectorAll("#"+this.#e+e+".modal");return t?t.length<=0?null:{Title:t[0].querySelectorAll(".modal-title")[0],Header:t[0].querySelectorAll(".modal-header")[0],Body:t[0].querySelectorAll(".modal-body")[0],Footer:t[0].querySelectorAll(".modal-footer")[0],Close:t[0].querySelectorAll("[data-bs-dismiss='modal']"),Modal:t[0]}:null}#o(e,t,l,o,s,a){const i=this;let n=i.Find(e);if(null!==n)return;let r="";if(r+='",i.#c(i.#t,r),n=i.Find(e),null!==n&&(document.getElementById(i.#e+e).addEventListener("hidden.bs.modal",(function(t){i.Close(e)})),n.Title.addEventListener("dblclick",(function(t){t.stopPropagation(),t.preventDefault(),i.#u(e)})),s)){const e=n.Modal.querySelectorAll(".modal-dialog")[0];let t=!1,l={X:0,Y:0},o={X:0,Y:0};n.Title.addEventListener("mousedown",(function(s){1!=s.buttons||t||(o={X:s.clientX,Y:s.clientY},l={X:i.#n(e.style.left)?0:parseInt(e.style.left.replace("px","")),Y:i.#n(e.style.top)?0:parseInt(e.style.top.replace("px",""))},t=!0,n.Title.style.cursor="move",s.preventDefault())})),document.addEventListener("mouseup",(function(e){t&&(t=!1,n.Title.style.cursor="default")})),document.addEventListener("mousemove",(function(s){1==s.buttons&&t&&(e.style.left=s.clientX-o.X+l.X+"px",e.style.top=s.clientY-o.Y+l.Y+"px")}))}}async#i(e,t){const l=this;return await new Promise((async(o,s)=>{await l.Show({ID:e,Title:t.Title,Message:t.Message,Size:t.Size,EasyClose:t.EasyClose,IsMovable:t.IsMovable});let a="";t.Buttons.forEach((function(e){a+='"})),l.Update({ID:e,Footer:a});const i=l.Find(e);i.Footer.querySelectorAll("button").forEach((function(t){t.addEventListener("click",(function(s){s.stopPropagation(),s.preventDefault();const a=t.getAttribute("data-prompt-value");l.Close(e),o(a)}))})),i.Close.forEach((function(t){t.addEventListener("click",(function(t){t.stopPropagation(),t.preventDefault(),l.Close(e),o("")}))}))}))}async#a(e,t){const l=this;return await new Promise((async(o,s)=>{t.Buttons=[{Label:"OK",Value:"",Colour:"primary"},{Label:"Cancel",Value:"",Colour:"secondary"}],await l.Show({ID:e,Title:t.Title,Message:t.Message,Size:t.Size,EasyClose:t.EasyClose,IsMovable:t.IsMovable});let a="";l.#n(t.Message)||(a+="

"+t.Message+"

"),a+='
',l.#n(t.Textbox.Label)||t.Textbox.LabelSize<=0?(a+='
',a+='',a+="
"):(a+='",a+='
',a+='',a+="
"),a+="
";let i="";t.Buttons.forEach((function(e){i+='"})),l.Update({ID:e,Body:a,Footer:i});const n=l.Find(e),r=n.Footer.querySelectorAll("button");r[0].addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault();const s=n.Body.querySelectorAll("input")[0].value;await l.Close(e),o(s)})),r[1].addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault(),await l.Close(e),o("")})),n.Close.forEach((async function(t){t.addEventListener("click",(async function(t){t.stopPropagation(),t.preventDefault(),await l.Close(e),o("")}))}))}))}#c(e,t){let l=document.createElement("template");l.innerHTML=t,l=l.content.firstChild,e.appendChild(l)}#r(e,t){jQuery?jQuery(e).html(t):e.innerHTML=t}#n(e){return void 0===e||(null==e||(0==e||e.trim().length<=0))}#s(e){return!this.#n(e)&&!!(e.startsWith("http://")||e.startsWith("https://")||e.startsWith("/"))}#d(e,t){const l=this,o=l.Find(e);if(null!==o){for(let e=0;et.#l.length-1&&(o=0),l.Modal.classList.add(t.#l[o])}}var BSDialog5=new BSDialog05; \ No newline at end of file