DotvvmChildEventCallback

Add/wire-up Toastr

6/28/2023 9:03:11 AM

Details

.gitignore 1(+1 -0)

diff --git a/.gitignore b/.gitignore
index 2e829e4..36d1b80 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
 src/Web/wwwroot/css/bootstrap/
 src/Web/wwwroot/css/font-awesome/
 src/Web/wwwroot/js/jquery/
+src/Web/wwwroot/js/toastr/
 
 # User-specific files
 *.rsuser
diff --git a/src/Web/DotvvmStartup.cs b/src/Web/DotvvmStartup.cs
index e424b1e..5f7933a 100644
--- a/src/Web/DotvvmStartup.cs
+++ b/src/Web/DotvvmStartup.cs
@@ -52,6 +52,18 @@ namespace Web
             {
                 Location = new UrlResourceLocation("~/wwwroot/css/site.css")
             });
+
+            config.Resources.Register("Toastr", new ScriptResource
+            {
+                Dependencies = new[] { "jquery" },
+                Location = new UrlResourceLocation("~/wwwroot/js/toastr/toastr.min.js")
+            });
+
+            config.Resources.Register("ToastrCustom", new ScriptResource
+            {
+                Dependencies = new[] { "Toastr" },
+                Location = new UrlResourceLocation("~/wwwroot/js/toastr/toastr.options.js")
+            });
         }
 
         private void RegisterJavascript(DotvvmConfiguration config)
diff --git a/src/Web/libman.json b/src/Web/libman.json
index 273a705..ea7e84b 100644
--- a/src/Web/libman.json
+++ b/src/Web/libman.json
@@ -7,12 +7,16 @@
       "destination": "wwwroot/css/bootstrap"
     },
     {
+      "library": "font-awesome@4.7.0",
+      "destination": "wwwroot/css/font-awesome/"
+    },
+    {
       "library": "jquery@3.7.0",
       "destination": "wwwroot/js/jquery/"
     },
     {
-      "library": "font-awesome@4.7.0",
-      "destination": "wwwroot/css/font-awesome/"
+      "library": "toastr.js@2.1.4",
+      "destination": "wwwroot/js/toastr"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/src/Web/ViewModels/DefaultViewModel.cs b/src/Web/ViewModels/DefaultViewModel.cs
index 6dd200e..16cc237 100644
--- a/src/Web/ViewModels/DefaultViewModel.cs
+++ b/src/Web/ViewModels/DefaultViewModel.cs
@@ -20,9 +20,19 @@ namespace Web.ViewModels
                 "Seventh",
                 "Eighth",
                 "Ninth",
-            }, 2);
+            }, 1);
             Wizard.ShowNavigation();
         }
+
+        public void OnNextClick()
+        {
+            Wizard.OnNextClick();
+        }
+
+        public void OnPreviousClick()
+        {
+            Wizard.OnPreviousClick();
+        }
     }
 
     public class WizardNavigationViewModel
diff --git a/src/Web/ViewModels/SiteViewModel.cs b/src/Web/ViewModels/SiteViewModel.cs
index a1d8eaf..a1d1afd 100644
--- a/src/Web/ViewModels/SiteViewModel.cs
+++ b/src/Web/ViewModels/SiteViewModel.cs
@@ -1,8 +1,69 @@
 namespace Web.ViewModels
 {
     using DotVVM.Framework.ViewModel;
+    using System.Web;
+    using System;
 
     public class SiteViewModel : DotvvmViewModelBase
     {
+        #region Toastr Wrappers 
+        public void ToastrError(string message, bool isSticky = false, string title = "")
+        {
+            ShowToast(ToastType.Error, message, title, isSticky);
+        }
+
+        public void ToastrInformation(string message, bool isSticky = false, string title = "")
+        {
+            ShowToast(ToastType.Info, message, title, isSticky);
+        }
+
+        public void ToastrSuccess(string message, bool isSticky = false, string title = "")
+        {
+            ShowToast(ToastType.Success, message, title, isSticky);
+        }
+
+        public void ToastrWarning(string message, bool isSticky = false, string title = "")
+        {
+            ShowToast(ToastType.Warning, message, title, isSticky);
+        }
+        #endregion
+
+        #region Private Methods
+        private void ShowToast(ToastType type, string message, string title, bool isSticky)
+        {
+            if (String.IsNullOrWhiteSpace(message))
+                return;
+
+            var encodedMessage = HttpUtility.HtmlEncode(message);
+            var encodedTitle = HttpUtility.HtmlEncode(title);
+
+
+            string javascript;
+            switch (type)
+            {
+                case ToastType.Error:
+                    javascript = $"toastr.error('{encodedMessage}', '{encodedTitle}')";
+                    break;
+                case ToastType.Info:
+                    javascript = $"toastr.info('{encodedMessage}', '{encodedTitle}')";
+                    break;
+                case ToastType.Success:
+                    javascript = $"toastr.success('{encodedMessage}', '{encodedTitle}')";
+                    break;
+                case ToastType.Warning:
+                    javascript = $"toastr.warning('{encodedMessage}', '{encodedTitle}')";
+                    break;
+                default:
+                    javascript = $"toastr.info('{encodedMessage}', '{encodedTitle}')";
+                    break;
+            }
+
+            Context.ResourceManager.AddStartupScript(isSticky
+                ? "Toast.setSticky()"
+                : "Toast.setStandard()");
+
+            Context.ResourceManager.AddStartupScript(javascript);
+        }
+        #endregion
     }
 }
diff --git a/src/Web/ViewModels/ToastType.cs b/src/Web/ViewModels/ToastType.cs
new file mode 100644
index 0000000..81d14df
--- /dev/null
+++ b/src/Web/ViewModels/ToastType.cs
@@ -0,0 +1,10 @@
+namespace Web.ViewModels
+{
+    public enum ToastType
+    {
+        Error,
+        Info,
+        Success,
+        Warning
+    }
+}
diff --git a/src/Web/Views/Default.dothtml b/src/Web/Views/Default.dothtml
index 9cc2393..bacb2be 100644
--- a/src/Web/Views/Default.dothtml
+++ b/src/Web/Views/Default.dothtml
@@ -6,7 +6,7 @@
         <div class="container mt-3" IncludeInPage="{value: NavigationVisible}">
             <div class="row">
                 <div class="col-md-2">
-                    <bs:Button ButtonTagName="button" Click="{command:  OnPreviousClick()}" Enabled="{value: PreviousButtonEnabled}" IncludeInPage="{value: PreviousButtonVisible}" Type="Light">
+                    <bs:Button ButtonTagName="button" Click="{command:  _root.OnPreviousClick()}" Enabled="{value: PreviousButtonEnabled}" IncludeInPage="{value: PreviousButtonVisible}" Type="Light">
                         Previous Button
                     </bs:Button>
                 </div>
@@ -16,7 +16,7 @@
                     {{value: Percentage.ToString("#.##")}}% Complete
                 </div>
                 <div class="col-md-2">
-                    <bs:Button ButtonTagName="button" Click="{command: OnNextClick()}" Enabled="{value: NextButtonEnabled}" IncludeInPage="{value: NextButtonVisible}" IsSubmitButton="true" Type="Primary">
+                    <bs:Button ButtonTagName="button" Click="{command: _root.OnNextClick()}" Enabled="{value: NextButtonEnabled}" IncludeInPage="{value: NextButtonVisible}" IsSubmitButton="true" Type="Primary">
                         Next Button
                     </bs:Button>
                 </div>
diff --git a/src/Web/Views/Site.dotmaster b/src/Web/Views/Site.dotmaster
index e90dbd7..01f350b 100644
--- a/src/Web/Views/Site.dotmaster
+++ b/src/Web/Views/Site.dotmaster
@@ -13,10 +13,10 @@
 <body Visible="{value: _page.EvaluatingOnClient}">
     <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
         <a class="navbar-brand" href="#">Demonstration</a>
-        <bs:Button aria-controls="navBarToggle" aria-expanded="false" aria-label="Toggle navigation" 
+        <bs:Button aria-controls="navBarToggle" aria-expanded="false" aria-label="Toggle navigation"
                    ButtonTagName="button"
-                   class="navbar-toggler"  
-                   data-toggle="collapse" data-target="#navBarToggle" 
+                   class="navbar-toggler"
+                   data-toggle="collapse" data-target="#navBarToggle"
                    Type="Link">
             <span class="navbar-toggler-icon"></span>
         </bs:Button>
@@ -26,8 +26,8 @@
                     <span class="nav-link">
                         Home <span class="sr-only">(current)</span>
                     </span>
-                </li>                
-            </ul>            
+                </li>
+            </ul>
         </div>
     </nav>
     <main role="main" class="container">
@@ -35,6 +35,7 @@
             <dot:ContentPlaceHolder ID="Body" />
         </div>
     </main>
+    <dot:RequiredResource Name="ToastrCustom" />
     <dot:ContentPlaceHolder ID="Scripts" />
 </body>
 </html>
diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj
index 92e8f93..d254708 100644
--- a/src/Web/Web.csproj
+++ b/src/Web/Web.csproj
@@ -197,9 +197,12 @@
     <Compile Include="DotVvmStartup.cs" />
     <Compile Include="ViewModels\DefaultViewModel.cs" />
     <Compile Include="ViewModels\SiteViewModel.cs" />
+    <Compile Include="ViewModels\ToastType.cs" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="favicon.ico" />
+    <Content Include="wwwroot\js\toastr\font-awesome-icons.css" />
+    <Content Include="wwwroot\js\toastr\toastr.options.js" />
     <Content Include="Web.config" />
     <Content Include="libman.json" />
     <Content Include="wwwroot\css\bootstrap\css\bootstrap-grid.css" />
@@ -221,6 +224,9 @@
     <Content Include="wwwroot\js\jquery\jquery.min.js" />
     <Content Include="wwwroot\js\jquery\jquery.slim.js" />
     <Content Include="wwwroot\js\jquery\jquery.slim.min.js" />
+    <Content Include="wwwroot\js\toastr\toastr.css" />
+    <Content Include="wwwroot\js\toastr\toastr.min.css" />
+    <Content Include="wwwroot\js\toastr\toastr.min.js" />
     <None Include="packages.config" />
     <Content Include="Views\Site.dotmaster" />
     <Content Include="Views\Default.dothtml" />
@@ -338,12 +344,14 @@
     <Content Include="wwwroot\css\font-awesome\fonts\fontawesome-webfont.woff" />
     <Content Include="wwwroot\css\font-awesome\fonts\fontawesome-webfont.woff2" />
     <Content Include="wwwroot\css\font-awesome\fonts\FontAwesome.otf" />
+    <Content Include="wwwroot\js\toastr\toastr.js.map" />
   </ItemGroup>
   <ItemGroup>
     <Analyzer Include="..\packages\DotVVM.4.1.7\analyzers\dotnet\cs\DotVVM.Analyzers.CodeFixes.dll" />
     <Analyzer Include="..\packages\DotVVM.4.1.7\analyzers\dotnet\cs\DotVVM.Analyzers.dll" />
   </ItemGroup>
   <ItemGroup>
+    <Folder Include="Models\" />
     <Folder Include="wwwroot\css\scss\" />
   </ItemGroup>
   <PropertyGroup>
diff --git a/src/Web/wwwroot/js/toastr/font-awesome-icons.css b/src/Web/wwwroot/js/toastr/font-awesome-icons.css
new file mode 100644
index 0000000..0f40d0a
--- /dev/null
+++ b/src/Web/wwwroot/js/toastr/font-awesome-icons.css
@@ -0,0 +1,30 @@
+#toast-container > .toast {
+    background-image: none !important;
+}
+
+    #toast-container > .toast:before {
+        position: fixed;
+        font-family: FontAwesome;
+        font-size: 24px;
+        line-height: 18px;
+        float: left;
+        color: #fff;
+        padding-right: 0.5em;
+        margin: auto 0.5em auto -1.5em;
+    }
+
+#toast-container > .toast-error:before {
+    content: "\f05e"; /*fa-ban*/
+}
+
+#toast-container > .toast-info:before {
+    content: "\f129"; /*fa-info*/
+}
+
+#toast-container > .toast-success:before {
+    content: "\f00c"; /*fa-check*/
+}
+
+#toast-container > .toast-warning:before {
+    content: "\f071"; /*fa-exclamation-triangle*/
+}
diff --git a/src/Web/wwwroot/js/toastr/toastr.options.js b/src/Web/wwwroot/js/toastr/toastr.options.js
new file mode 100644
index 0000000..205d1ea
--- /dev/null
+++ b/src/Web/wwwroot/js/toastr/toastr.options.js
@@ -0,0 +1,34 @@
+let Toast = (function (toastr) {
+    toastr.options.closeButton = false;
+    toastr.options.debug = false;
+    toastr.options.extendedTimeOut = 1000;
+    toastr.options.hideEasing = 'linear';
+    toastr.options.hideDuration = 1000;
+    toastr.options.hideMethod = 'fadeOut';
+    toastr.options.newestOnTop = false;
+    toastr.options.onclick = null;
+    toastr.options.positionClass = 'toast-bottom-right';
+    toastr.options.preventDuplicates = false;
+    toastr.options.progressBar = false;
+    toastr.options.showDuration = 300;
+    toastr.options.showEasing = 'swing';
+    toastr.options.showMethod = 'fadeIn';
+    toastr.options.timeOut = 5000;
+
+    function standard() {
+        toastr.options.closeButton = false;
+        toastr.options.extendedTimeOut = 1000;
+        toastr.options.timeOut = 5000;
+    };
+
+    function sticky() {
+        toastr.options.closeButton = true;
+        toastr.options.extendedTimeOut = 0;
+        toastr.options.timeOut = 0;
+    };
+
+    return {
+        setStandard: standard,
+        setSticky: sticky
+    };
+})(window.toastr);