diff --git a/docs/V5/_content/BlazorStrap/blazorstrap.js b/docs/V5/_content/BlazorStrap/blazorstrap.js index 90b7d567..6aeba30c 100644 --- a/docs/V5/_content/BlazorStrap/blazorstrap.js +++ b/docs/V5/_content/BlazorStrap/blazorstrap.js @@ -17,8 +17,50 @@ if (!Element.prototype.closest) { return null; }; } - window.blazorStrap = { + ToastTimer: function(element, time, timeRemaining, rendered) + { + if(rendered === false) + { + element.classList.add("showing"); + } + if(time === 0) + { + + setTimeout(function (){ + element.classList.remove("showing"); + },100) + } + if(time !== 0) { + + const dflex = element.querySelector(".d-flex"); + const wrapper = document.createElement("div"); + wrapper.className = "w-100 p-0 m-0 position-relative border-bottom-1 border-dark"; + wrapper.style.top = "-1px"; + const timeEl = document.createElement("div") + wrapper.appendChild(timeEl); + element.insertBefore(wrapper, dflex); + timeEl.classList.add("bg-dark"); + timeEl.style.height = "4px"; + timeEl.style.opacity = ".4"; + if (timeRemaining === 0) { + timeEl.style.width = "0"; + timeEl.style["transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + timeEl.style["-webkit-transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + } else { + timeRemaining = time-timeRemaining; + console.log(((timeRemaining / time) * 100)); + timeEl.style.width = ((timeRemaining / time) * 100) + "%"; + timeEl.style["transition"] = "linear" + (time - timeRemaining) / 1000 + "s"; + timeEl.style["-webkit-transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + } + + setTimeout(function () { + element.classList.remove("showing"); + timeEl.style.width = "100%"; + }, 100); + } + }, EventHandlers: [], AddEvent: async function (id, name, type, isDocument = false, ignoreChildren = false, filter = "") { return new Promise(function (resolve) { diff --git a/docs/V5/_content/BlazorStrap/blazorstrap.min.js b/docs/V5/_content/BlazorStrap/blazorstrap.min.js index fec7e276..5d13ec99 100644 --- a/docs/V5/_content/BlazorStrap/blazorstrap.min.js +++ b/docs/V5/_content/BlazorStrap/blazorstrap.min.js @@ -1 +1 @@ -var link;if(!Element.prototype.matches){Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector}if(!Element.prototype.closest){Element.prototype.closest=function(s){var el=this;do{if(Element.prototype.matches.call(el,s))return el;el=el.parentElement||el.parentNode}while(el!==null&&el.nodeType===1);return null}}window.blazorStrap={EventHandlers:[],AddEvent:async function(id,name,type,isDocument=false,ignoreChildren=false,filter=""){return new Promise(function(resolve){let element;if(isDocument)element=document;else element=document.querySelector("[data-blazorstrap='"+id+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(blazorStrap.EventHandlers[id][name]===undefined){blazorStrap.EventHandlers[id][name]={}}blazorStrap.EventHandlers[id][name][type]={Callback:function(event){if(type==="transitionend"&&id!==event.target.getAttribute("data-blazorstrap"))return;if(name==="documentDropdown"){let parent=document.querySelector("[data-blazorstrap='"+id+"']");if(parent!==null){if(parent.contains(event.target)){if(event.target.closest(".dropdown-toggle")!==null){return}}}}if(ignoreChildren){let parent=document.querySelector("[data-blazorstrap='"+id+"']");if(parent!==null){if(parent.contains(event.target))return}}if(filter===""){DotNet.invokeMethodAsync("BlazorStrap","EventCallback",id,name,type,element.classList,blazorStrap.GetEvents(event))}else if(element.getElementsByClassName(filter)){DotNet.invokeMethodAsync("BlazorStrap","EventCallback",id,name,type,element.classList,blazorStrap.GetEvents(event))}}};element.addEventListener(type,blazorStrap.EventHandlers[id][name][type].Callback,false);resolve()})},RemoveEvent:async function(id,name,type,isDocument=false){return new Promise(function(resolve){let element;if(isDocument)element=document;else element=document.querySelector("[data-blazorstrap='"+id+"']");if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(name!=="null"&&type!=="null"){if(blazorStrap.EventHandlers[id][name]===undefined)return resolve();if(blazorStrap.EventHandlers[id][name][type]===undefined)return resolve();if(blazorStrap.EventHandlers[id][name][type]!==undefined&&blazorStrap.EventHandlers[id][name][type]!==null){if(element!==undefined&&element!==null){element.removeEventListener(type,blazorStrap.EventHandlers[id][name][type].Callback,false)}delete blazorStrap.EventHandlers[id][name][type]}if(Object.keys(blazorStrap.EventHandlers[id][name]).length===0){delete blazorStrap.EventHandlers[id][name]}}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}resolve()})},TransitionDidNotStart:async function(element,delay=200){return new Promise(function(resolve){let handler=function(event){resolve(false);clearTimeout(timeout)};const timeout=setTimeout(function(){resolve(true);element.removeEventListener("transitionstart",handler,{once:true})},delay);element.addEventListener("transitionstart",handler,{once:true})}).then(data=>data)},RemovePopover:async function(element,id){return new Promise(function(resolve,r){if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(blazorStrap.EventHandlers[id]["Popover"]!==undefined){blazorStrap.EventHandlers[id].Popover.destroy();delete blazorStrap.EventHandlers[id].Popover;resolve()}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}})},AddPopover:async function(element,dotNetObjectReference,position,target,offset="none"){function ToPixels(value){if(value.indexOf("rem")!==-1){value=value.substring(0,value.indexOf("rem"));return value*parseFloat(getComputedStyle(document.documentElement).fontSize)}else if(value.indexOf("em")!==-1){value=value.substring(0,value.indexOf("em"));return value*parseFloat(getComputedStyle(element).fontSize)}return value}return new Promise(function(resolve){const id=element.getAttribute("data-blazorstrap");target=document.querySelector("[data-blazorstrap='"+target+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(offset!=="none"){offset=offset.split(",");blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position,modifiers:[{name:"offset",options:{offset:[parseInt(ToPixels(offset[0])),parseInt(ToPixels(offset[1]))]}}]})}else{blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position})}resolve(true)})},UpdatePopover:async function(element){const id=element.getAttribute("data-blazorstrap");return new Promise(function(resolve){setTimeout(async function(){await blazorStrap.EventHandlers[id].Popover.update();return resolve(element.style.cssText)},10)})},UpdatePopoverArrow:async function(element,dotNetObjectReference,position,tooltip){const id=element.getAttribute("data-blazorstrap");let arrow;return new Promise(function(resolve){if(tooltip===false)arrow=element.querySelector(".popover-arrow");else arrow=element.querySelector(".tooltip-arrow");position=position.replace("start","");position=position.replace("end","");switch(position){case"top":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"bottom":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"left":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break;case"right":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break}resolve()})},PeakHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){element.style.visibility="hidden";element.style.position="absolute";element.style.display="block";setTimeout(function(){element.style.display="";element.style.visibility="";element.style.position=""},1);resolve(element.offsetHeight)})},GetHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){resolve(element.offsetHeight)})},GetInnerHeight:async function(){return new Promise(function(resolve){resolve(window.innerHeight)})},SetBodyStyle:async function(style,value){return new Promise(function(resolve){document.body.style[style]=value;resolve()})},GetScrollBarWidth:async function(){return new Promise(function(resolve){resolve(window.innerWidth-document.documentElement.clientWidth)})},AddBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.add(className);resolve()})},RemoveBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.remove(className);resolve()})},SetStyle:async function(element,style,value,delay=0){if(element===null||element===undefined)return;element.style[style]=value;return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetStyle:async function(element){if(element===null||element===undefined)return;return element.style.cssText},AddAttribute:async function(element,name,value){if(element===null||element===undefined)return;return new Promise(function(resolve){element.setAttribute(name,value);resolve()})},RemoveAttribute:async function(element,name){if(element===null||element===undefined)return;return new Promise(function(resolve){element.removeAttribute(name);resolve()})},AddClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.add(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},CleanupCarousel:async function(showEl,hideEl){return new Promise(function(resolve){hideEl.classList.remove("carousel-item-end");hideEl.classList.remove("carousel-item-prev");hideEl.classList.remove("carousel-item-next");hideEl.classList.remove("carousel-item-start");showEl.classList.remove("carousel-item-end");showEl.classList.remove("carousel-item-prev");showEl.classList.remove("carousel-item-next");showEl.classList.remove("carousel-item-start");resolve()})},AnimateCarousel:async function(id,showEl,hideEl,back){await blazorStrap.CleanupCarousel(showEl,hideEl);let callback=function(){DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,"bsCarousel","transitionend")};return new Promise(function(resolve){if(back){showEl.classList.add("carousel-item-prev");setTimeout(async function(){showEl.classList.add("carousel-item-end");hideEl.classList.add("carousel-item-end");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}else{showEl.classList.add("carousel-item-next");setTimeout(async function(){showEl.classList.add("carousel-item-start");hideEl.classList.add("carousel-item-start");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}})},AnimateCollapse:async function(element,shown,id,name,type){if(shown){let cleanup=function(){element.style["height"]="";element.classList.remove("collapsing");element.classList.add("collapse");element.classList.add("show");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.PeakHeight(element);element.classList.remove("collapse");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]=height+"px";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}else{let cleanup=function(){element.classList.remove("collapsing");element.classList.add("collapse");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.GetHeight(element);element.style["height"]=height+"px";element.classList.remove("collapse");element.classList.remove("show");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]="";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}},RemoveClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.remove(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetEvents:function(event){return{key:event.key,target:{classList:event.target.classList,targetId:event.target.getAttribute("data-blazorstrap-target"),childrenId:blazorStrap.GetChildrenIds(event.target),dataId:event.target.getAttribute("data-blazorstrap")}}},GetChildrenIds:function(target){const children=target.querySelectorAll("[data-blazorstrap]");let result=[];for(i=0;idata)},RemovePopover:async function(element,id){return new Promise(function(resolve,r){if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(blazorStrap.EventHandlers[id]["Popover"]!==undefined){blazorStrap.EventHandlers[id].Popover.destroy();delete blazorStrap.EventHandlers[id].Popover;resolve()}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}})},AddPopover:async function(element,dotNetObjectReference,position,target,offset="none"){function ToPixels(value){if(value.indexOf("rem")!==-1){value=value.substring(0,value.indexOf("rem"));return value*parseFloat(getComputedStyle(document.documentElement).fontSize)}else if(value.indexOf("em")!==-1){value=value.substring(0,value.indexOf("em"));return value*parseFloat(getComputedStyle(element).fontSize)}return value}return new Promise(function(resolve){const id=element.getAttribute("data-blazorstrap");target=document.querySelector("[data-blazorstrap='"+target+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(offset!=="none"){offset=offset.split(",");blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position,modifiers:[{name:"offset",options:{offset:[parseInt(ToPixels(offset[0])),parseInt(ToPixels(offset[1]))]}}]})}else{blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position})}resolve(true)})},UpdatePopover:async function(element){const id=element.getAttribute("data-blazorstrap");return new Promise(function(resolve){setTimeout(async function(){await blazorStrap.EventHandlers[id].Popover.update();return resolve(element.style.cssText)},10)})},UpdatePopoverArrow:async function(element,dotNetObjectReference,position,tooltip){const id=element.getAttribute("data-blazorstrap");let arrow;return new Promise(function(resolve){if(tooltip===false)arrow=element.querySelector(".popover-arrow");else arrow=element.querySelector(".tooltip-arrow");position=position.replace("start","");position=position.replace("end","");switch(position){case"top":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"bottom":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"left":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break;case"right":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break}resolve()})},PeakHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){element.style.visibility="hidden";element.style.position="absolute";element.style.display="block";setTimeout(function(){element.style.display="";element.style.visibility="";element.style.position=""},1);resolve(element.offsetHeight)})},GetHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){resolve(element.offsetHeight)})},GetInnerHeight:async function(){return new Promise(function(resolve){resolve(window.innerHeight)})},SetBodyStyle:async function(style,value){return new Promise(function(resolve){document.body.style[style]=value;resolve()})},GetScrollBarWidth:async function(){return new Promise(function(resolve){resolve(window.innerWidth-document.documentElement.clientWidth)})},AddBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.add(className);resolve()})},RemoveBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.remove(className);resolve()})},SetStyle:async function(element,style,value,delay=0){if(element===null||element===undefined)return;element.style[style]=value;return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetStyle:async function(element){if(element===null||element===undefined)return;return element.style.cssText},AddAttribute:async function(element,name,value){if(element===null||element===undefined)return;return new Promise(function(resolve){element.setAttribute(name,value);resolve()})},RemoveAttribute:async function(element,name){if(element===null||element===undefined)return;return new Promise(function(resolve){element.removeAttribute(name);resolve()})},AddClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.add(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},CleanupCarousel:async function(showEl,hideEl){return new Promise(function(resolve){hideEl.classList.remove("carousel-item-end");hideEl.classList.remove("carousel-item-prev");hideEl.classList.remove("carousel-item-next");hideEl.classList.remove("carousel-item-start");showEl.classList.remove("carousel-item-end");showEl.classList.remove("carousel-item-prev");showEl.classList.remove("carousel-item-next");showEl.classList.remove("carousel-item-start");resolve()})},AnimateCarousel:async function(id,showEl,hideEl,back){await blazorStrap.CleanupCarousel(showEl,hideEl);let callback=function(){DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,"bsCarousel","transitionend")};return new Promise(function(resolve){if(back){showEl.classList.add("carousel-item-prev");setTimeout(async function(){showEl.classList.add("carousel-item-end");hideEl.classList.add("carousel-item-end");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}else{showEl.classList.add("carousel-item-next");setTimeout(async function(){showEl.classList.add("carousel-item-start");hideEl.classList.add("carousel-item-start");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}})},AnimateCollapse:async function(element,shown,id,name,type){if(shown){let cleanup=function(){element.style["height"]="";element.classList.remove("collapsing");element.classList.add("collapse");element.classList.add("show");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.PeakHeight(element);element.classList.remove("collapse");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]=height+"px";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}else{let cleanup=function(){element.classList.remove("collapsing");element.classList.add("collapse");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.GetHeight(element);element.style["height"]=height+"px";element.classList.remove("collapse");element.classList.remove("show");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]="";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}},RemoveClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.remove(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetEvents:function(event){return{key:event.key,target:{classList:event.target.classList,targetId:event.target.getAttribute("data-blazorstrap-target"),childrenId:blazorStrap.GetChildrenIds(event.target),dataId:event.target.getAttribute("data-blazorstrap")}}},GetChildrenIds:function(target){const children=target.querySelectorAll("[data-blazorstrap]");let result=[];for(i=0;i +
BlazorStarp
Hello, world!
\ No newline at end of file diff --git a/docs/V5/docs/Samples/Components/Toast/Toast2.md b/docs/V5/docs/Samples/Components/Toast/Toast2.md new file mode 100644 index 00000000..28d6e33c --- /dev/null +++ b/docs/V5/docs/Samples/Components/Toast/Toast2.md @@ -0,0 +1,3 @@ + + Hello, world! + \ No newline at end of file diff --git a/docs/V5/docs/Samples/Components/Toast/Toast3.md b/docs/V5/docs/Samples/Components/Toast/Toast3.md new file mode 100644 index 00000000..c71e632f --- /dev/null +++ b/docs/V5/docs/Samples/Components/Toast/Toast3.md @@ -0,0 +1,43 @@ +@inject IBlazorStrap _blazorStrap +
+ +
+ +
+ Toast Placement + + @foreach (var item in Enum.GetNames(typeof(Toast)).ToList()) + { + + } + + Background Color + + @foreach (var item in Enum.GetNames(typeof(BSColor)).ToList()) + { + + } + +
+
+ Close Time in milliseconds + + Show +
+@code +{ + private Toast ToastPlacement { get; set; } + private BSColor Color { get; set; } + private int Time { get; set; } = 0; + private int i = 0; + private void Show() + { + _blazorStrap.Toaster.Add("Live Example " + @i, "Live Example Text", o => + { + o.Color = Color; + o.CloseAfter = Time; + o.Toast = ToastPlacement; + }); + ++i; + } +} \ No newline at end of file diff --git a/docs/V5/docs/Samples/Forms/FormControls/FormControls1.md b/docs/V5/docs/Samples/Forms/FormControls/FormControls1.md index ab85be92..ba41c18a 100644 --- a/docs/V5/docs/Samples/Forms/FormControls/FormControls1.md +++ b/docs/V5/docs/Samples/Forms/FormControls/FormControls1.md @@ -2,7 +2,7 @@ Email address -
> +
Example Textarea
\ No newline at end of file diff --git a/docs/V5/docs/Samples/Forms/Validation/ValidationMain.md b/docs/V5/docs/Samples/Forms/Validation/ValidationMain.md index bee03d2c..e8bba27d 100644 --- a/docs/V5/docs/Samples/Forms/Validation/ValidationMain.md +++ b/docs/V5/docs/Samples/Forms/Validation/ValidationMain.md @@ -1,5 +1,5 @@ @using System.ComponentModel.DataAnnotations - + @_message @@ -32,10 +32,9 @@ Submit - Reset + Reset - @code { private EmployeeModal Modal { get; set; } = new EmployeeModal(); private string _message = ""; @@ -70,4 +69,8 @@ public bool? HasPendingPhoto { get; set; } public string? PhotoName { get; set; } } + public void Reset () + { + Modal = new EmployeeModal(); + } } \ No newline at end of file diff --git a/docs/V5/docs/Static/Components/Toast.md b/docs/V5/docs/Static/Components/Toast.md index 3641c3eb..61c3714b 100644 --- a/docs/V5/docs/Static/Components/Toast.md +++ b/docs/V5/docs/Static/Components/Toast.md @@ -22,3 +22,11 @@ See [shared](layout/shared) for additional parameters ### Example {{sample=Components/Toast/Toast1}} + +### Without header +{{sample=Components/Toast/Toast2}} + +### Toaster Example + should be placed before you `@Body` in your layout. Exact placement depends on your requirements for where you want the toasts to show up. + +{{sample=Components/Toast/Toast3}} diff --git a/docs/V5/docs/Static/Forms/Formcontrols.md b/docs/V5/docs/Static/Forms/Formcontrols.md index aaf09b83..1836c1e7 100644 --- a/docs/V5/docs/Static/Forms/Formcontrols.md +++ b/docs/V5/docs/Static/Forms/Formcontrols.md @@ -14,7 +14,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Example diff --git a/docs/V5/docs/Static/Forms/Inputgroup.md b/docs/V5/docs/Static/Forms/Inputgroup.md index e595f04d..23fe1465 100644 --- a/docs/V5/docs/Static/Forms/Inputgroup.md +++ b/docs/V5/docs/Static/Forms/Inputgroup.md @@ -3,5 +3,6 @@ Only one example provided see https://getbootstrap.com/docs/5.1/forms/input-grou See [shared](layout/shared) for additional parameters ### Example with `BS` Class Helper +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` {{sample=Forms/InputGroup/InputGroup1}} diff --git a/docs/V5/docs/Static/Forms/Range.md b/docs/V5/docs/Static/Forms/Range.md index fb72687a..7327f735 100644 --- a/docs/V5/docs/Static/Forms/Range.md +++ b/docs/V5/docs/Static/Forms/Range.md @@ -14,7 +14,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Overview diff --git a/docs/V5/docs/Static/Forms/Select.md b/docs/V5/docs/Static/Forms/Select.md index 6024e4b0..30a4ec63 100644 --- a/docs/V5/docs/Static/Forms/Select.md +++ b/docs/V5/docs/Static/Forms/Select.md @@ -13,7 +13,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Example diff --git a/src/BlazorStrap-Docs/ObjectExtensions.cs b/src/BlazorStrap-Docs/ObjectExtensions.cs new file mode 100644 index 00000000..14f9bb16 --- /dev/null +++ b/src/BlazorStrap-Docs/ObjectExtensions.cs @@ -0,0 +1,10 @@ +namespace BlazorStrap_Docs +{ + public static class ObjectExtensions + { + public static T? Clone(this T source) + { + return System.Text.Json.JsonSerializer.Deserialize(System.Text.Json.JsonSerializer.Serialize(source)); + } + } +} \ No newline at end of file diff --git a/src/BlazorStrap-Docs/Samples/Components/Toast/Toast1.razor b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast1.razor index 745abc1c..e3040abf 100644 --- a/src/BlazorStrap-Docs/Samples/Components/Toast/Toast1.razor +++ b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast1.razor @@ -1,4 +1,4 @@ - +
BlazorStarp
Hello, world!
\ No newline at end of file diff --git a/src/BlazorStrap-Docs/Samples/Components/Toast/Toast2.razor b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast2.razor new file mode 100644 index 00000000..5646e0c0 --- /dev/null +++ b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast2.razor @@ -0,0 +1,3 @@ + + Hello, world! + \ No newline at end of file diff --git a/src/BlazorStrap-Docs/Samples/Components/Toast/Toast3.razor b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast3.razor new file mode 100644 index 00000000..c7024b2d --- /dev/null +++ b/src/BlazorStrap-Docs/Samples/Components/Toast/Toast3.razor @@ -0,0 +1,43 @@ +@inject IBlazorStrap _blazorStrap +
+ +
+ +
+ Toast Placement + + @foreach (var item in Enum.GetNames(typeof(Toast)).ToList()) + { + + } + + Background Color + + @foreach (var item in Enum.GetNames(typeof(BSColor)).ToList()) + { + + } + +
+
+ Close Time in milliseconds + + Show +
+@code +{ + private Toast ToastPlacement { get; set; } + private BSColor Color { get; set; } + private int Time { get; set; } = 0; + private int i = 0; + private void Show() + { + _blazorStrap.Toaster.Add("Live Example " + @i, "Live Example Text", o => + { + o.Color = Color; + o.CloseAfter = Time; + o.Toast = ToastPlacement; + }); + ++i; + } +} \ No newline at end of file diff --git a/src/BlazorStrap-Docs/Samples/Forms/FormControls/FormControls1.razor b/src/BlazorStrap-Docs/Samples/Forms/FormControls/FormControls1.razor index 34ed85aa..de9aee88 100644 --- a/src/BlazorStrap-Docs/Samples/Forms/FormControls/FormControls1.razor +++ b/src/BlazorStrap-Docs/Samples/Forms/FormControls/FormControls1.razor @@ -2,7 +2,7 @@ Email address
-
> +
Example Textarea
\ No newline at end of file diff --git a/src/BlazorStrap-Docs/Samples/Forms/Validation/ValidationMain.razor b/src/BlazorStrap-Docs/Samples/Forms/Validation/ValidationMain.razor index a17296bc..45def6c6 100644 --- a/src/BlazorStrap-Docs/Samples/Forms/Validation/ValidationMain.razor +++ b/src/BlazorStrap-Docs/Samples/Forms/Validation/ValidationMain.razor @@ -1,5 +1,5 @@ @using System.ComponentModel.DataAnnotations - + @_message @@ -32,10 +32,9 @@ Submit - Reset + Reset - @code { private EmployeeModal Modal { get; set; } = new EmployeeModal(); private string _message = ""; @@ -70,4 +69,8 @@ public bool? HasPendingPhoto { get; set; } public string? PhotoName { get; set; } } + public void Reset () + { + Modal = new EmployeeModal(); + } } \ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast1.md b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast1.md index 745abc1c..e3040abf 100644 --- a/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast1.md +++ b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast1.md @@ -1,4 +1,4 @@ - +
BlazorStarp
Hello, world!
\ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast2.md b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast2.md new file mode 100644 index 00000000..5646e0c0 --- /dev/null +++ b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast2.md @@ -0,0 +1,3 @@ + + Hello, world! + \ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast3.md b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast3.md new file mode 100644 index 00000000..c7024b2d --- /dev/null +++ b/src/BlazorStrap-Docs/wwwroot/Samples/Components/Toast/Toast3.md @@ -0,0 +1,43 @@ +@inject IBlazorStrap _blazorStrap +
+ +
+ +
+ Toast Placement + + @foreach (var item in Enum.GetNames(typeof(Toast)).ToList()) + { + + } + + Background Color + + @foreach (var item in Enum.GetNames(typeof(BSColor)).ToList()) + { + + } + +
+
+ Close Time in milliseconds + + Show +
+@code +{ + private Toast ToastPlacement { get; set; } + private BSColor Color { get; set; } + private int Time { get; set; } = 0; + private int i = 0; + private void Show() + { + _blazorStrap.Toaster.Add("Live Example " + @i, "Live Example Text", o => + { + o.Color = Color; + o.CloseAfter = Time; + o.Toast = ToastPlacement; + }); + ++i; + } +} \ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Samples/Forms/FormControls/FormControls1.md b/src/BlazorStrap-Docs/wwwroot/Samples/Forms/FormControls/FormControls1.md index 34ed85aa..de9aee88 100644 --- a/src/BlazorStrap-Docs/wwwroot/Samples/Forms/FormControls/FormControls1.md +++ b/src/BlazorStrap-Docs/wwwroot/Samples/Forms/FormControls/FormControls1.md @@ -2,7 +2,7 @@ Email address
-
> +
Example Textarea
\ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Samples/Forms/Validation/ValidationMain.md b/src/BlazorStrap-Docs/wwwroot/Samples/Forms/Validation/ValidationMain.md index a17296bc..45def6c6 100644 --- a/src/BlazorStrap-Docs/wwwroot/Samples/Forms/Validation/ValidationMain.md +++ b/src/BlazorStrap-Docs/wwwroot/Samples/Forms/Validation/ValidationMain.md @@ -1,5 +1,5 @@ @using System.ComponentModel.DataAnnotations - + @_message @@ -32,10 +32,9 @@ Submit - Reset + Reset - @code { private EmployeeModal Modal { get; set; } = new EmployeeModal(); private string _message = ""; @@ -70,4 +69,8 @@ public bool? HasPendingPhoto { get; set; } public string? PhotoName { get; set; } } + public void Reset () + { + Modal = new EmployeeModal(); + } } \ No newline at end of file diff --git a/src/BlazorStrap-Docs/wwwroot/Static/Components/Toast.md b/src/BlazorStrap-Docs/wwwroot/Static/Components/Toast.md index 1f5fce36..3ac33c25 100644 --- a/src/BlazorStrap-Docs/wwwroot/Static/Components/Toast.md +++ b/src/BlazorStrap-Docs/wwwroot/Static/Components/Toast.md @@ -22,3 +22,11 @@ See [shared](layout/shared) for additional parameters ### Example {{sample=Components/Toast/Toast1}} + +### Without header +{{sample=Components/Toast/Toast2}} + +### Toaster Example + should be placed before you `@Body` in your layout. Exact placement depends on your requirements for where you want the toasts to show up. + +{{sample=Components/Toast/Toast3}} diff --git a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Formcontrols.md b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Formcontrols.md index 2a949f9b..effabc9e 100644 --- a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Formcontrols.md +++ b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Formcontrols.md @@ -14,7 +14,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Example diff --git a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Inputgroup.md b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Inputgroup.md index 22776bc5..065da879 100644 --- a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Inputgroup.md +++ b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Inputgroup.md @@ -3,5 +3,6 @@ Only one example provided see https://getbootstrap.com/docs/5.1/forms/input-grou See [shared](layout/shared) for additional parameters ### Example with `BS` Class Helper +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` {{sample=Forms/InputGroup/InputGroup1}} diff --git a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Range.md b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Range.md index 6a377032..a95d4e4d 100644 --- a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Range.md +++ b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Range.md @@ -14,7 +14,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Overview diff --git a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Select.md b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Select.md index 4e91d1ce..3cc211c1 100644 --- a/src/BlazorStrap-Docs/wwwroot/Static/Forms/Select.md +++ b/src/BlazorStrap-Docs/wwwroot/Static/Forms/Select.md @@ -13,7 +13,7 @@ See [shared](forms/shared) for additional parameters | NoColorClass | bool | true/false | Removes `.form-control-color` | ::: -`@("value")` is not required it's a line declaration of a string to make the demo work +`Value="@("X")"` is so the demo has a type and compile with adding code blocks. You will want to use `@bind-Value="YourRealVar""` ### Example diff --git a/src/BlazorStrap/Components/BSToaster.cs b/src/BlazorStrap/Components/BSToaster.cs new file mode 100644 index 00000000..623139ab --- /dev/null +++ b/src/BlazorStrap/Components/BSToaster.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; + +namespace BlazorStrap +{ + public class BSToaster : ComponentBase, IDisposable + { + // ReSharper disable once NullableWarningSuppressionIsUsed + [Inject] private IBlazorStrap BlazorStrap { get; set; } = default!; + + protected override void OnInitialized() + { + BlazorStrap.Toaster.OnChange += OnChange; + } + + private void OnChange() + { + StateHasChanged(); + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.Clear(); + var lastPlacment = Toast.Default; + var building = false; + var i = 0; + foreach (var toast in BlazorStrap.Toaster.Children.OrderBy(q => q.Placement).ThenBy(q => q.Created)) + { + if (toast.Placement != Toast.Default) + { + building = true; + // Close and open new wrapper when changes + if (lastPlacment != toast.Placement && i != 0) + { + builder.CloseElement(); + builder.OpenElement(i, "div"); + builder.AddAttribute(i + 1, "class", GetClass(toast.Placement)); + } + + //Open first wrapper + if (i == 0) + { + builder.OpenElement(i, "div"); + builder.AddAttribute(i + 1, "class", GetClass(toast.Placement)); + } + + builder.OpenElement(i + 2, "div"); + builder.SetKey(toast.Id); + builder.AddContent(i + 4, GetFragment(toast)); + builder.CloseElement(); + i = i + 5; + if (lastPlacment != toast.Placement) + lastPlacment = toast.Placement; + } + } + + if (building) + { + builder.CloseElement(); + } + } + + public void Dispose() + { + BlazorStrap.Toaster.OnChange -= OnChange; + } + + internal RenderFragment GetFragment(Toasts Toast) + { + var header = new RenderFragment(childBuilder => + { + childBuilder.AddContent(0, new MarkupString(Toast.HeaderText ?? "")); + }); + var content = new RenderFragment(childBuilder => + { + childBuilder.AddContent(0, new MarkupString(Toast.ContentText ?? "")); + }); + + return builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, "ButtonClass", Toast.Options.ButtonClass); + builder.AddAttribute(2, "CloseAfter", Toast.Options.CloseAfter); + builder.AddAttribute(3, "Color", Toast.Options.Color); + if (!string.IsNullOrEmpty(Toast.HeaderText)) + { + builder.AddAttribute(4, "Header", header); + } + builder.AddAttribute(5, "Content", content); + builder.AddAttribute(6, "ContentClass", Toast.Options.ContentClass); + builder.AddAttribute(7, "HeaderClass", Toast.Options.HeaderClass); + builder.AddAttribute(8, "TimeOut", + EventCallback.Factory.Create(this, BlazorStrap.Toaster.OnChange)); + builder.AddAttribute(9, "ToasterId", Toast.Id); + builder.CloseComponent(); + }; + } + + public string GetClass(Toast pos) + { + return pos switch + { + Toast.TopLeft => "position-absolute top-0 start-0", + Toast.TopCenter => "position-absolute top-0 start-50 translate-middle-x", + Toast.TopRight => "position-absolute top-0 end-0", + Toast.MiddleLeft => "position-absolute top-50 start-0 translate-middle-y", + Toast.MiddleCenter => "position-absolute top-50 start-50 translate-middle", + Toast.MiddleRight => "position-absolute top-50 end-0 translate-middle-y", + Toast.BottomLeft => "position-absolute bottom-0 start-0", + Toast.BottomCenter => "position-absolute bottom-0 start-50 translate-middle-x", + _ => "position-absolute bottom-0 end-0" + }; + } + } +} \ No newline at end of file diff --git a/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor b/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor index 1566ff5f..fff2ae7d 100644 --- a/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor +++ b/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor @@ -1,24 +1,30 @@ @namespace BlazorStrap @inherits BlazorStrapBase -@if (Header == null || Content == null) +@if (Content == null) { - - Both Header and Content RenderFragment are required. - + + Both Header and Content RenderFragment are required. + - return; +return; } -
+
@if (Header != null) {
@Header - +
} -
- @Content +
+
+ @Content +
+ @if (Header == null) + { + + }
-
+
\ No newline at end of file diff --git a/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor.cs b/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor.cs index eb4fa4f3..e6f21290 100644 --- a/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor.cs +++ b/src/BlazorStrap/Components/BootstrapComponents/BSToast.razor.cs @@ -1,48 +1,132 @@ +using System.Timers; using BlazorComponentUtilities; +using BlazorStrap.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; +using Microsoft.JSInterop; namespace BlazorStrap { - public partial class BSToast : BlazorStrapBase + public partial class BSToast : BlazorStrapBase, IDisposable { + + private int CloseAfter { get; set; } = 0; + private int TimeRemaining { get; set; } = 0; + private bool _showTimer = false; + private ElementReference MyRef { get; set; } + [Parameter] public Guid? ToasterId { get; set; } = null; + [Parameter] public bool IsBackgroundInRoot { get; set; } [Parameter] public string? ButtonClass { get; set; } - [Parameter] public BSColor Color { get; set; } = BSColor.Default; [Parameter] public RenderFragment? Content { get; set; } + [Parameter] public Toast Toast { get; set; } = Toast.Default; [Parameter] public string? ContentClass { get; set; } [Parameter] public RenderFragment? Header { get; set; } [Parameter] public string? HeaderClass { get; set; } [Parameter] public EventCallback OnClick { get; set; } private string? ClassBuilder => new CssBuilder("toast") + .AddClass("align-items-center",Header == null ) + .AddClass($"bg-{Color.NameToLower()}", Color != BSColor.Default && IsBackgroundInRoot) + .AddClass("position-absolute top-0 start-0", Toast == Toast.TopLeft) + .AddClass("position-absolute top-0 start-50 translate-middle-x", Toast == Toast.TopCenter) + .AddClass("position-absolute top-0 end-0", Toast == Toast.TopRight) + .AddClass("position-absolute top-50 start-0 translate-middle-y", Toast == Toast.MiddleLeft) + .AddClass("position-absolute top-50 start-50 translate-middle", Toast == Toast.MiddleCenter) + .AddClass("position-absolute top-50 end-0 translate-middle-y", Toast == Toast.MiddleRight) + .AddClass("position-absolute bottom-0 start-0", Toast == Toast.BottomLeft) + .AddClass("position-absolute bottom-0 start-50 translate-middle-x", Toast == Toast.BottomCenter) + .AddClass("position-absolute bottom-0 end-0", Toast == Toast.BottomRight) .AddClass("show", Shown) .AddClass("fade") - .AddClass($"bg-{Color.NameToLower()}", Color != BSColor.Default) + .AddClass($"bg-{Color.NameToLower()}", Color != BSColor.Default && (IsBackgroundInRoot || Header == null)) + .AddClass("text-white", Color != BSColor.Warning && Color != BSColor.Default && (IsBackgroundInRoot || Header == null)) .AddClass(LayoutClass, !string.IsNullOrEmpty(LayoutClass)) .AddClass(Class, !string.IsNullOrEmpty(Class)) .Build().ToNullString(); + private string? ButtonClassBuilder => new CssBuilder() + .AddClass("btn-close-white", Color != BSColor.Warning && Color != BSColor.Default) + .AddClass(ButtonClass).Build().ToNullString(); + private string? ContentClassBuilder => new CssBuilder("toast-body") - .AddClass(ContentClass) + .AddClass(ContentClass) .Build().ToNullString(); private string? HeaderClassBuilder => new CssBuilder("toast-header") + .AddClass($"bg-{Color.NameToLower()}", Color != BSColor.Default && !IsBackgroundInRoot) + .AddClass("text-white", Color != BSColor.Warning && Color != BSColor.Default && Color != BSColor.Light) + .AddClass("text-dark", Color == BSColor.Warning && Color != BSColor.Default && Color == BSColor.Light) .AddClass(HeaderClass) .Build().ToNullString(); + protected override bool ShouldRender() + { + return false; + } + private bool Shown { get; set; } = true; public void Toggle() { Shown = !Shown; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + var self = BlazorStrap.Toaster.Children.FirstOrDefault(q => q.Id == ToasterId); + if(self != null) + { + + if (self.Timer == null) return; + self.Timer.Elapsed += TimerOnElapsed; + CloseAfter = self.CloseAfter; + if (self?.Timer?.Enabled == false && CloseAfter != 0) + { + await Js.InvokeVoidAsync("blazorStrap.ToastTimer", MyRef, CloseAfter,0,self.Rendered); + self.Timer.Start(); + } + else + { + await Js.InvokeVoidAsync("blazorStrap.ToastTimer", MyRef, CloseAfter, self.Timer.TimeLeft,self.Rendered ); + } + self.Rendered = true; + } + } + } + + private async void TimerOnElapsed(object sender, ElapsedEventArgs e) + { + // Delay for animation .15 seconds = 150 ms move this to transition end later + await Js.InvokeVoidAsync("blazorStrap.AddClass", MyRef, "showing"); + await Task.Delay(150); + if(BlazorStrap.Toaster.Children.FirstOrDefault(q => q.Id == ToasterId) != null ) + BlazorStrap.Toaster.RemoveChild(ToasterId); + + } private async Task ClickEvent() { + // Delay for animation .15 seconds = 150 ms move this to transition end later + await Js.InvokeVoidAsync("blazorStrap.AddClass", MyRef, "showing"); + await Task.Delay(150); + if(BlazorStrap.Toaster.Children.FirstOrDefault(q => q.Id == ToasterId) != null ) + BlazorStrap.Toaster.RemoveChild(ToasterId); + if (!OnClick.HasDelegate) Toggle(); await OnClick.InvokeAsync(); } + + public void Dispose() + { + var self = BlazorStrap.Toaster.Children.FirstOrDefault(q => q.Id == ToasterId); + if (self != null && self.Timer != null) + { + self.Timer.Elapsed += TimerOnElapsed; + } + } } } \ No newline at end of file diff --git a/src/BlazorStrap/Components/Forms/BSForm.cs b/src/BlazorStrap/Components/Forms/BSForm.cs index 9a75c756..6a54afdd 100644 --- a/src/BlazorStrap/Components/Forms/BSForm.cs +++ b/src/BlazorStrap/Components/Forms/BSForm.cs @@ -28,6 +28,7 @@ public class BSForm : BlazorStrapBase [Parameter] public TValue? Model { get; set; } [Parameter] public EventCallback OnInvalidSubmit { get; set; } [Parameter] public EventCallback OnSubmit { get; set; } + [Parameter] public EventCallback OnReset { get; set; } [Parameter] public EventCallback OnValidSubmit { get; set; } // [Parameter] public bool ValidateOnInit { get; set; } @@ -68,7 +69,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.OpenElement(0, "form"); builder.AddAttribute(1, "class", ClassBuilder); builder.AddMultipleAttributes(2, Attributes); - builder.AddContent(0, ChildContent); + builder.AddContent(3, ChildContent); builder.CloseElement(); return; } @@ -96,7 +97,6 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) { formBuilder.AddAttribute(3, "Model", Model); } - formBuilder.AddAttribute(4, "OnSubmit", OnSubmit); formBuilder.AddAttribute(5, "OnValidSubmit", OnValidSubmit); formBuilder.AddAttribute(6, "OnInvalidSubmit", OnInvalidSubmit); diff --git a/src/BlazorStrap/Enums/Toast.cs b/src/BlazorStrap/Enums/Toast.cs new file mode 100644 index 00000000..eaa671dc --- /dev/null +++ b/src/BlazorStrap/Enums/Toast.cs @@ -0,0 +1,16 @@ +namespace BlazorStrap +{ + public enum Toast + { + Default, + TopLeft, + TopCenter, + TopRight, + BottomLeft, + BottomCenter, + BottomRight, + MiddleLeft, + MiddleCenter, + MiddleRight + } +} \ No newline at end of file diff --git a/src/BlazorStrap/Extensions/DoubleExtensions.cs b/src/BlazorStrap/Extensions/DoubleExtensions.cs new file mode 100644 index 00000000..5ad5b517 --- /dev/null +++ b/src/BlazorStrap/Extensions/DoubleExtensions.cs @@ -0,0 +1,12 @@ +namespace BlazorStrap +{ + public static class DoubleExtensions + { + public static double RemoveNegative(this double value) + { + if (value < 0) + return 0; + return value; + } + } +} \ No newline at end of file diff --git a/src/BlazorStrap/Extensions/ServiceCollectionExtensions.cs b/src/BlazorStrap/Extensions/ServiceCollectionExtensions.cs index ae584179..9c0d9689 100644 --- a/src/BlazorStrap/Extensions/ServiceCollectionExtensions.cs +++ b/src/BlazorStrap/Extensions/ServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddBlazorStrap(this IServiceCollection serviceCollection) { serviceCollection.AddScoped(); - serviceCollection.AddTransient(); + serviceCollection.AddScoped(); serviceCollection.AddScoped(); return serviceCollection; } diff --git a/src/BlazorStrap/Extensions/TimerPlus.cs b/src/BlazorStrap/Extensions/TimerPlus.cs new file mode 100644 index 00000000..a8db50a6 --- /dev/null +++ b/src/BlazorStrap/Extensions/TimerPlus.cs @@ -0,0 +1,30 @@ +namespace BlazorStrap +{ + public class TimerPlus : System.Timers.Timer + { + private DateTime m_dueTime; + + public TimerPlus() => Elapsed += this.ElapsedAction; + + protected new void Dispose() + { + Elapsed -= this.ElapsedAction; + base.Dispose(); + } + + public double TimeLeft => (m_dueTime - DateTime.Now).TotalMilliseconds.RemoveNegative(); + public new void Start() + { + m_dueTime = DateTime.Now.AddMilliseconds(Interval); + base.Start(); + } + + private void ElapsedAction(object sender, System.Timers.ElapsedEventArgs e) + { + if (AutoReset) + m_dueTime = DateTime.Now.AddMilliseconds(Interval); + } + + + } +} \ No newline at end of file diff --git a/src/BlazorStrap/IBlazorStrap.cs b/src/BlazorStrap/IBlazorStrap.cs index 121a6601..a83cc98d 100644 --- a/src/BlazorStrap/IBlazorStrap.cs +++ b/src/BlazorStrap/IBlazorStrap.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace BlazorStrap { public interface IBlazorStrap { + Toaster Toaster { get; } Theme CurrentTheme { get; } Task SetBootstrapCss(); Task SetBootstrapCss(string version); diff --git a/src/BlazorStrap/Service/BlazorStrapCore.cs b/src/BlazorStrap/Service/BlazorStrapCore.cs index 315f9f47..49655b79 100644 --- a/src/BlazorStrap/Service/BlazorStrapCore.cs +++ b/src/BlazorStrap/Service/BlazorStrapCore.cs @@ -4,8 +4,11 @@ namespace BlazorStrap.Service { public class BlazorStrapCore : IBlazorStrap { + + private readonly BlazorStrapInterop _blazorStrapInterop; //private readonly CurrentTheme _currentTheme; + public Toaster Toaster { get;} = new Toaster(); public Theme CurrentTheme { get; internal set; } = Theme.Bootstrap; public BlazorStrapCore(BlazorStrapInterop blazorStrapInterop) diff --git a/src/BlazorStrap/Service/Toaster.cs b/src/BlazorStrap/Service/Toaster.cs new file mode 100644 index 00000000..289da454 --- /dev/null +++ b/src/BlazorStrap/Service/Toaster.cs @@ -0,0 +1,99 @@ +using Microsoft.AspNetCore.Components; + +namespace BlazorStrap +{ + public class Toaster : ComponentBase + { + public Action? OnChange; + internal List Children { get; } = new List(); + + public void Add(string? header, string? content) + { + AddChild(header,content, o => + { + o.Color = BSColor.Primary; + }); + } + public void Add(string? content) + { + AddChild(null,content, o => + { + o.Color = BSColor.Primary; + }); + } + public void Add(string? content, Action? options) + { + AddChild(null,content, options); + } + + public void Add(string? header, string? content, Action? options) + { + AddChild(header, content, options); + } + private void AddChild(string? headerText, string? contentText, Action? options) + { + var opts = new Options(); + options?.Invoke(opts); + if (opts.Toast == Toast.Default) + opts.Toast = Toast.TopRight; + + var newChild = new Toasts + { + Id = Guid.NewGuid(), + CloseAfter = opts.CloseAfter, + Timer = new TimerPlus(), + Options = new Options() + { + ButtonClass = opts.ButtonClass, + CloseAfter = opts.CloseAfter, + Color = opts.Color, + ContentClass = opts.ContentClass, + HeaderClass = opts.HeaderClass, + Toast = opts.Toast + }, + Placement = opts.Toast, + HeaderText = headerText, + ContentText = contentText + }; + if (opts.CloseAfter != 0) + { + newChild.Timer.Interval = opts.CloseAfter; + } + + Children.Add(newChild); + OnChange?.Invoke(); + } + internal void RemoveChild(Guid? id) + { + var toast = Children.FirstOrDefault(q => q.Id == id); + toast?.Timer?.Stop(); + toast?.Timer?.Dispose(); + if (toast != null) + Children.Remove(toast); + + OnChange?.Invoke(); + } + } + public class Options + { + public string? ButtonClass { get; set; } + public int CloseAfter { get; set; } = 30; + public BSColor Color { get; set; } = BSColor.Default; + public Toast Toast { get; set; } = Toast.Default; + public string? ContentClass { get; set; } + public string? HeaderClass { get; set; } + } + internal class Toasts + { + public Guid? Id { get; set; } + public bool Rendered { get; set; } + public int CloseAfter { get; set; } + public TimerPlus? Timer { get; set; } + public Options? Options { get; set; } + public string? HeaderText { get; set; } + public string? ContentText { get; set; } + internal int TimeRemaining { get; set; } = 0; + public DateTime Created { get; } = DateTime.Now; + public Toast Placement { get; set; } = Toast.Default; + } +} \ No newline at end of file diff --git a/src/BlazorStrap/Utilities/BlazorInputBase.cs b/src/BlazorStrap/Utilities/BlazorInputBase.cs index c101a1b4..c8ff9eeb 100644 --- a/src/BlazorStrap/Utilities/BlazorInputBase.cs +++ b/src/BlazorStrap/Utilities/BlazorInputBase.cs @@ -175,17 +175,15 @@ public override Task SetParametersAsync(ParameterView parameters) { // This is the first run // Could put this logic in OnInit, but its nice to avoid forcing people who override OnInit to call base.OnInit() - - if (ValueExpression == null ) - { - throw new InvalidOperationException($"{GetType()} requires a value for the 'ValueExpression' " + - $"parameter. Normally this is provided automatically when using 'bind-Value'."); - } - - FieldIdentifier = FieldIdentifier.Create(ValueExpression); - + if (CascadedEditContext != null) { + if (ValueExpression == null ) + { + throw new InvalidOperationException($"{GetType()} requires a value for the 'ValueExpression' " + + $"parameter. Normally this is provided automatically when using 'bind-Value'."); + } + FieldIdentifier = FieldIdentifier.Create(ValueExpression); EditContext = CascadedEditContext; EditContext.OnValidationStateChanged += _validationStateChangedHandler; } diff --git a/src/BlazorStrap/wwwroot/blazorstrap.js b/src/BlazorStrap/wwwroot/blazorstrap.js index 4822252f..2786a21d 100644 --- a/src/BlazorStrap/wwwroot/blazorstrap.js +++ b/src/BlazorStrap/wwwroot/blazorstrap.js @@ -17,8 +17,50 @@ if (!Element.prototype.closest) { return null; }; } - window.blazorStrap = { + ToastTimer: function(element, time, timeRemaining, rendered) + { + if(rendered === false) + { + element.classList.add("showing"); + } + if(time === 0) + { + + setTimeout(function (){ + element.classList.remove("showing"); + },100) + } + if(time !== 0) { + + const dflex = element.querySelector(".d-flex"); + const wrapper = document.createElement("div"); + wrapper.className = "w-100 p-0 m-0 position-relative border-bottom-1 border-dark"; + wrapper.style.top = "-1px"; + const timeEl = document.createElement("div") + wrapper.appendChild(timeEl); + element.insertBefore(wrapper, dflex); + timeEl.classList.add("bg-dark"); + timeEl.style.height = "4px"; + timeEl.style.opacity = ".4"; + if (timeRemaining === 0) { + timeEl.style.width = "0"; + timeEl.style["transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + timeEl.style["-webkit-transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + } else { + timeRemaining = time-timeRemaining; + console.log(((timeRemaining / time) * 100)); + timeEl.style.width = ((timeRemaining / time) * 100) + "%"; + timeEl.style["transition"] = "linear" + (time - timeRemaining) / 1000 + "s"; + timeEl.style["-webkit-transition"] = "linear " + (time - timeRemaining) / 1000 + "s"; + } + + setTimeout(function () { + element.classList.remove("showing"); + timeEl.style.width = "100%"; + }, 100); + } + }, EventHandlers: [], AddEvent: async function (id, name, type, isDocument = false, ignoreChildren = false, filter = "") { return new Promise(function (resolve) { diff --git a/src/BlazorStrap/wwwroot/blazorstrap.min.js b/src/BlazorStrap/wwwroot/blazorstrap.min.js index fec7e276..5d13ec99 100644 --- a/src/BlazorStrap/wwwroot/blazorstrap.min.js +++ b/src/BlazorStrap/wwwroot/blazorstrap.min.js @@ -1 +1 @@ -var link;if(!Element.prototype.matches){Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector}if(!Element.prototype.closest){Element.prototype.closest=function(s){var el=this;do{if(Element.prototype.matches.call(el,s))return el;el=el.parentElement||el.parentNode}while(el!==null&&el.nodeType===1);return null}}window.blazorStrap={EventHandlers:[],AddEvent:async function(id,name,type,isDocument=false,ignoreChildren=false,filter=""){return new Promise(function(resolve){let element;if(isDocument)element=document;else element=document.querySelector("[data-blazorstrap='"+id+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(blazorStrap.EventHandlers[id][name]===undefined){blazorStrap.EventHandlers[id][name]={}}blazorStrap.EventHandlers[id][name][type]={Callback:function(event){if(type==="transitionend"&&id!==event.target.getAttribute("data-blazorstrap"))return;if(name==="documentDropdown"){let parent=document.querySelector("[data-blazorstrap='"+id+"']");if(parent!==null){if(parent.contains(event.target)){if(event.target.closest(".dropdown-toggle")!==null){return}}}}if(ignoreChildren){let parent=document.querySelector("[data-blazorstrap='"+id+"']");if(parent!==null){if(parent.contains(event.target))return}}if(filter===""){DotNet.invokeMethodAsync("BlazorStrap","EventCallback",id,name,type,element.classList,blazorStrap.GetEvents(event))}else if(element.getElementsByClassName(filter)){DotNet.invokeMethodAsync("BlazorStrap","EventCallback",id,name,type,element.classList,blazorStrap.GetEvents(event))}}};element.addEventListener(type,blazorStrap.EventHandlers[id][name][type].Callback,false);resolve()})},RemoveEvent:async function(id,name,type,isDocument=false){return new Promise(function(resolve){let element;if(isDocument)element=document;else element=document.querySelector("[data-blazorstrap='"+id+"']");if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(name!=="null"&&type!=="null"){if(blazorStrap.EventHandlers[id][name]===undefined)return resolve();if(blazorStrap.EventHandlers[id][name][type]===undefined)return resolve();if(blazorStrap.EventHandlers[id][name][type]!==undefined&&blazorStrap.EventHandlers[id][name][type]!==null){if(element!==undefined&&element!==null){element.removeEventListener(type,blazorStrap.EventHandlers[id][name][type].Callback,false)}delete blazorStrap.EventHandlers[id][name][type]}if(Object.keys(blazorStrap.EventHandlers[id][name]).length===0){delete blazorStrap.EventHandlers[id][name]}}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}resolve()})},TransitionDidNotStart:async function(element,delay=200){return new Promise(function(resolve){let handler=function(event){resolve(false);clearTimeout(timeout)};const timeout=setTimeout(function(){resolve(true);element.removeEventListener("transitionstart",handler,{once:true})},delay);element.addEventListener("transitionstart",handler,{once:true})}).then(data=>data)},RemovePopover:async function(element,id){return new Promise(function(resolve,r){if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(blazorStrap.EventHandlers[id]["Popover"]!==undefined){blazorStrap.EventHandlers[id].Popover.destroy();delete blazorStrap.EventHandlers[id].Popover;resolve()}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}})},AddPopover:async function(element,dotNetObjectReference,position,target,offset="none"){function ToPixels(value){if(value.indexOf("rem")!==-1){value=value.substring(0,value.indexOf("rem"));return value*parseFloat(getComputedStyle(document.documentElement).fontSize)}else if(value.indexOf("em")!==-1){value=value.substring(0,value.indexOf("em"));return value*parseFloat(getComputedStyle(element).fontSize)}return value}return new Promise(function(resolve){const id=element.getAttribute("data-blazorstrap");target=document.querySelector("[data-blazorstrap='"+target+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(offset!=="none"){offset=offset.split(",");blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position,modifiers:[{name:"offset",options:{offset:[parseInt(ToPixels(offset[0])),parseInt(ToPixels(offset[1]))]}}]})}else{blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position})}resolve(true)})},UpdatePopover:async function(element){const id=element.getAttribute("data-blazorstrap");return new Promise(function(resolve){setTimeout(async function(){await blazorStrap.EventHandlers[id].Popover.update();return resolve(element.style.cssText)},10)})},UpdatePopoverArrow:async function(element,dotNetObjectReference,position,tooltip){const id=element.getAttribute("data-blazorstrap");let arrow;return new Promise(function(resolve){if(tooltip===false)arrow=element.querySelector(".popover-arrow");else arrow=element.querySelector(".tooltip-arrow");position=position.replace("start","");position=position.replace("end","");switch(position){case"top":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"bottom":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"left":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break;case"right":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break}resolve()})},PeakHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){element.style.visibility="hidden";element.style.position="absolute";element.style.display="block";setTimeout(function(){element.style.display="";element.style.visibility="";element.style.position=""},1);resolve(element.offsetHeight)})},GetHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){resolve(element.offsetHeight)})},GetInnerHeight:async function(){return new Promise(function(resolve){resolve(window.innerHeight)})},SetBodyStyle:async function(style,value){return new Promise(function(resolve){document.body.style[style]=value;resolve()})},GetScrollBarWidth:async function(){return new Promise(function(resolve){resolve(window.innerWidth-document.documentElement.clientWidth)})},AddBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.add(className);resolve()})},RemoveBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.remove(className);resolve()})},SetStyle:async function(element,style,value,delay=0){if(element===null||element===undefined)return;element.style[style]=value;return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetStyle:async function(element){if(element===null||element===undefined)return;return element.style.cssText},AddAttribute:async function(element,name,value){if(element===null||element===undefined)return;return new Promise(function(resolve){element.setAttribute(name,value);resolve()})},RemoveAttribute:async function(element,name){if(element===null||element===undefined)return;return new Promise(function(resolve){element.removeAttribute(name);resolve()})},AddClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.add(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},CleanupCarousel:async function(showEl,hideEl){return new Promise(function(resolve){hideEl.classList.remove("carousel-item-end");hideEl.classList.remove("carousel-item-prev");hideEl.classList.remove("carousel-item-next");hideEl.classList.remove("carousel-item-start");showEl.classList.remove("carousel-item-end");showEl.classList.remove("carousel-item-prev");showEl.classList.remove("carousel-item-next");showEl.classList.remove("carousel-item-start");resolve()})},AnimateCarousel:async function(id,showEl,hideEl,back){await blazorStrap.CleanupCarousel(showEl,hideEl);let callback=function(){DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,"bsCarousel","transitionend")};return new Promise(function(resolve){if(back){showEl.classList.add("carousel-item-prev");setTimeout(async function(){showEl.classList.add("carousel-item-end");hideEl.classList.add("carousel-item-end");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}else{showEl.classList.add("carousel-item-next");setTimeout(async function(){showEl.classList.add("carousel-item-start");hideEl.classList.add("carousel-item-start");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}})},AnimateCollapse:async function(element,shown,id,name,type){if(shown){let cleanup=function(){element.style["height"]="";element.classList.remove("collapsing");element.classList.add("collapse");element.classList.add("show");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.PeakHeight(element);element.classList.remove("collapse");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]=height+"px";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}else{let cleanup=function(){element.classList.remove("collapsing");element.classList.add("collapse");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.GetHeight(element);element.style["height"]=height+"px";element.classList.remove("collapse");element.classList.remove("show");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]="";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}},RemoveClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.remove(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetEvents:function(event){return{key:event.key,target:{classList:event.target.classList,targetId:event.target.getAttribute("data-blazorstrap-target"),childrenId:blazorStrap.GetChildrenIds(event.target),dataId:event.target.getAttribute("data-blazorstrap")}}},GetChildrenIds:function(target){const children=target.querySelectorAll("[data-blazorstrap]");let result=[];for(i=0;idata)},RemovePopover:async function(element,id){return new Promise(function(resolve,r){if(blazorStrap.EventHandlers[id]===undefined)return resolve();if(blazorStrap.EventHandlers[id]["Popover"]!==undefined){blazorStrap.EventHandlers[id].Popover.destroy();delete blazorStrap.EventHandlers[id].Popover;resolve()}if(Object.keys(blazorStrap.EventHandlers[id]).length===0){delete blazorStrap.EventHandlers[id]}})},AddPopover:async function(element,dotNetObjectReference,position,target,offset="none"){function ToPixels(value){if(value.indexOf("rem")!==-1){value=value.substring(0,value.indexOf("rem"));return value*parseFloat(getComputedStyle(document.documentElement).fontSize)}else if(value.indexOf("em")!==-1){value=value.substring(0,value.indexOf("em"));return value*parseFloat(getComputedStyle(element).fontSize)}return value}return new Promise(function(resolve){const id=element.getAttribute("data-blazorstrap");target=document.querySelector("[data-blazorstrap='"+target+"']");if(blazorStrap.EventHandlers[id]===undefined){blazorStrap.EventHandlers[id]={}}if(offset!=="none"){offset=offset.split(",");blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position,modifiers:[{name:"offset",options:{offset:[parseInt(ToPixels(offset[0])),parseInt(ToPixels(offset[1]))]}}]})}else{blazorStrap.EventHandlers[id]["Popover"]=Popper.createPopper(target,element,{placement:position})}resolve(true)})},UpdatePopover:async function(element){const id=element.getAttribute("data-blazorstrap");return new Promise(function(resolve){setTimeout(async function(){await blazorStrap.EventHandlers[id].Popover.update();return resolve(element.style.cssText)},10)})},UpdatePopoverArrow:async function(element,dotNetObjectReference,position,tooltip){const id=element.getAttribute("data-blazorstrap");let arrow;return new Promise(function(resolve){if(tooltip===false)arrow=element.querySelector(".popover-arrow");else arrow=element.querySelector(".tooltip-arrow");position=position.replace("start","");position=position.replace("end","");switch(position){case"top":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"bottom":arrow.style.transform="translate("+(element.offsetWidth/2-arrow.offsetWidth/2)+"px, 0px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetHeight]}}]});break;case"left":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break;case"right":arrow.style.transform="translate(0px, "+(element.offsetHeight/2-arrow.offsetHeight/2)+"px)";if(!tooltip)blazorStrap.EventHandlers[id].Popover.setOptions({modifiers:[{name:"offset",options:{offset:[0,arrow.offsetWidth]}}]});break}resolve()})},PeakHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){element.style.visibility="hidden";element.style.position="absolute";element.style.display="block";setTimeout(function(){element.style.display="";element.style.visibility="";element.style.position=""},1);resolve(element.offsetHeight)})},GetHeight:async function(element){if(element===null||element===undefined)return;return new Promise(function(resolve){resolve(element.offsetHeight)})},GetInnerHeight:async function(){return new Promise(function(resolve){resolve(window.innerHeight)})},SetBodyStyle:async function(style,value){return new Promise(function(resolve){document.body.style[style]=value;resolve()})},GetScrollBarWidth:async function(){return new Promise(function(resolve){resolve(window.innerWidth-document.documentElement.clientWidth)})},AddBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.add(className);resolve()})},RemoveBodyClass:async function(className){return new Promise(function(resolve){document.body.classList.remove(className);resolve()})},SetStyle:async function(element,style,value,delay=0){if(element===null||element===undefined)return;element.style[style]=value;return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetStyle:async function(element){if(element===null||element===undefined)return;return element.style.cssText},AddAttribute:async function(element,name,value){if(element===null||element===undefined)return;return new Promise(function(resolve){element.setAttribute(name,value);resolve()})},RemoveAttribute:async function(element,name){if(element===null||element===undefined)return;return new Promise(function(resolve){element.removeAttribute(name);resolve()})},AddClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.add(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},CleanupCarousel:async function(showEl,hideEl){return new Promise(function(resolve){hideEl.classList.remove("carousel-item-end");hideEl.classList.remove("carousel-item-prev");hideEl.classList.remove("carousel-item-next");hideEl.classList.remove("carousel-item-start");showEl.classList.remove("carousel-item-end");showEl.classList.remove("carousel-item-prev");showEl.classList.remove("carousel-item-next");showEl.classList.remove("carousel-item-start");resolve()})},AnimateCarousel:async function(id,showEl,hideEl,back){await blazorStrap.CleanupCarousel(showEl,hideEl);let callback=function(){DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,"bsCarousel","transitionend")};return new Promise(function(resolve){if(back){showEl.classList.add("carousel-item-prev");setTimeout(async function(){showEl.classList.add("carousel-item-end");hideEl.classList.add("carousel-item-end");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}else{showEl.classList.add("carousel-item-next");setTimeout(async function(){showEl.classList.add("carousel-item-start");hideEl.classList.add("carousel-item-start");hideEl.addEventListener("transitionend",callback,{once:true});resolve(await blazorStrap.TransitionDidNotStart(showEl))},10)}})},AnimateCollapse:async function(element,shown,id,name,type){if(shown){let cleanup=function(){element.style["height"]="";element.classList.remove("collapsing");element.classList.add("collapse");element.classList.add("show");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.PeakHeight(element);element.classList.remove("collapse");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]=height+"px";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}else{let cleanup=function(){element.classList.remove("collapsing");element.classList.add("collapse");DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)};let height=await blazorStrap.GetHeight(element);element.style["height"]=height+"px";element.classList.remove("collapse");element.classList.remove("show");element.classList.add("collapsing");element.addEventListener("transitionend",cleanup,{once:true});setTimeout(async function(){element.style["height"]="";if(await blazorStrap.TransitionDidNotStart(element,50)){cleanup();element.removeEventListener("transitionend",cleanup,{once:true});DotNet.invokeMethodAsync("BlazorStrap","EventFallback",id,name,type)}},10)}},RemoveClass:async function(element,className,delay=0){if(element===null||element===undefined)return;element.classList.remove(className);return new Promise(function(resolve){if(delay!==0)setTimeout(()=>resolve(),delay);else resolve()})},GetEvents:function(event){return{key:event.key,target:{classList:event.target.classList,targetId:event.target.getAttribute("data-blazorstrap-target"),childrenId:blazorStrap.GetChildrenIds(event.target),dataId:event.target.getAttribute("data-blazorstrap")}}},GetChildrenIds:function(target){const children=target.querySelectorAll("[data-blazorstrap]");let result=[];for(i=0;i