Browse Source

Replace toastr with a new implementation.

pull/21940/head
maliming 1 year ago
parent
commit
44e4299f6b
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 33
      docs/en/framework/ui/mvc-razor-pages/javascript-api/notify.md
  2. BIN
      docs/en/images/js-notify-success.png
  3. 15
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Toastr/ToastrScriptBundleContributor.cs
  4. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Toastr/ToastrStyleBundleContributor.cs
  5. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  6. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs
  7. 139
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.css
  8. 216
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.js
  9. 34
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
  10. 3
      npm/packs/aspnetcore.mvc.ui.theme.shared/package.json

33
docs/en/framework/ui/mvc-razor-pages/javascript-api/notify.md

@ -1,6 +1,6 @@
# ASP.NET Core MVC / Razor Pages UI: JavaScript Notify API
Notify API is used to show toast style, auto disappearing UI notifications to the end user. It is implemented by the [Toastr](https://github.com/CodeSeven/toastr) library by default.
Notify API is used to show toast style, auto disappearing UI notifications to the end user.
## Quick Example
@ -26,20 +26,23 @@ There are four types of pre-defined notifications;
* `abp.notify.warn(...)`
* `abp.notify.error(...)`
All of the methods above gets the following parameters;
All of the methods above accept the following parameters:
* `message`: A message (`string`) to show to the user.
* `title`: An optional title (`string`).
* `options`: Additional options to be passed to the underlying library, to the Toastr by default.
## Toastr Configuration
The notification API is implemented by the [Toastr](https://github.com/CodeSeven/toastr) library by default. You can see its own configuration options.
**Example: Show toast messages on the top right of the page**
````js
toastr.options.positionClass = 'toast-top-right';
````
> ABP sets this option to `toast-bottom-right` by default. You can override it just as shown above.
* `options`: Additional options to customize the notification. Available options:
* `life`: Display duration in milliseconds (default: `5000`)
* `sticky`: Keep toast visible until manually closed (default: `false`)
* `closable`: Show close button (default: `true`)
* `tapToDismiss`: Click anywhere on toast to dismiss (default: `false`)
* `containerKey`: Key for multiple container support (optional)
* `iconClass`: Custom icon class (optional)
* `position`: Position configuration (optional)
* `top`: Distance from top (default: `'auto'`)
* `right`: Distance from right (default: `'30px'`)
* `bottom`: Distance from bottom (default: `'30px'`)
* `left`: Distance from left (default: `'auto'`)
## Global Configuration
`AbpToastService.setDefaultOptions` method can be used to set default options for all notifications. This method should be called before any notification is shown.

BIN
docs/en/images/js-notify-success.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 77 KiB

15
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Toastr/ToastrScriptBundleContributor.cs

@ -1,15 +0,0 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
[DependsOn(typeof(JQueryScriptContributor))]
public class ToastrScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/toastr/toastr.min.js");
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Toastr/ToastrStyleBundleContributor.cs

@ -1,12 +0,0 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
public class ToastrStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/toastr/toastr.min.css");
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -12,7 +12,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SweetAlert2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Timeago;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
@ -26,7 +25,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
typeof(Select2ScriptContributor),
typeof(DatatablesNetBs5ScriptContributor),
typeof(Sweetalert2ScriptContributor),
typeof(ToastrScriptBundleContributor),
typeof(MalihuCustomScrollbarPluginScriptBundleContributor),
typeof(LuxonScriptContributor),
typeof(TimeagoScriptContributor),
@ -48,7 +46,7 @@ public class SharedThemeGlobalScriptContributor : BundleContributor
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/authentication-state/authentication-state-listener.js"
});

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs

@ -7,7 +7,6 @@ using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs5;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.FontAwesome;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Toastr;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
@ -16,7 +15,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
typeof(CoreStyleContributor),
typeof(BootstrapStyleContributor),
typeof(FontAwesomeStyleContributor),
typeof(ToastrStyleBundleContributor),
typeof(Select2StyleContributor),
typeof(MalihuCustomScrollbarPluginStyleBundleContributor),
typeof(DatatablesNetBs5StyleContributor),
@ -30,7 +28,8 @@ public class SharedThemeGlobalStyleContributor : BundleContributor
context.Files.AddRange(new BundleFile[]
{
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-styles.css"
"/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-styles.css",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.css",
});
}
}

139
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.css

@ -0,0 +1,139 @@
.abp-toast-container {
position: fixed;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
min-width: 350px;
min-height: 80px;
z-index: 1900;
right: 30px;
bottom: 30px;
}
.abp-toast {
display: grid;
grid-template-columns: 35px 1fr;
gap: 5px;
margin: 5px 0;
padding: 7px;
width: 350px;
user-select: none;
z-index: 9999;
color: #fff;
border-radius: 8px;
font-size: 14px;
box-shadow: 0 0 20px 0 rgba(76, 87, 125, 0.02);
animation: toastIn 0.3s ease-in-out;
}
.abp-toast-success {
border: 2px solid #4fbf67;
background-color: #4fbf67;
}
.abp-toast-error {
border: 2px solid #c00d49;
background-color: #c00d49;
}
.abp-toast-info {
border: 2px solid #438aa7;
background-color: #438aa7;
}
.abp-toast-warning {
border: 2px solid #ff9f38;
background-color: #ff9f38;
}
.abp-toast-icon {
display: flex;
align-items: center;
justify-content: center;
}
.abp-toast-icon .icon {
font-size: 32px;
}
.abp-toast-content {
position: relative;
display: flex;
align-self: center;
flex-direction: column;
word-break: break-word;
padding-bottom: 2px;
}
.abp-toast-close-button {
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0px 5px 0 0;
width: 25px;
height: 100%;
border: none;
border-radius: 50%;
background: transparent;
color: inherit;
cursor: pointer;
}
.abp-toast-close-button:focus {
outline: none;
}
.abp-toast-title {
margin: 0;
padding: 0;
font-size: 1rem;
font-weight: 600;
}
.abp-toast-message {
margin: 0;
padding: 0;
max-width: 240px;
}
@keyframes toastIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes toastOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
.toast-removing {
animation: toastOut 0.3s ease-in-out forwards;
}
@media only screen and (max-width: 768px) {
.abp-toast-container {
min-width: 100%;
right: 0;
}
.abp-toast {
width: 95%;
}
}

216
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toast/abp-toast.js

@ -0,0 +1,216 @@
function AbpToastService(globalOptions) {
// Static default configuration for all instances
if (!AbpToastService.defaultOptions) {
AbpToastService.defaultOptions = {
closable: true,
sticky: false,
life: 5000,
tapToDismiss: false,
containerKey: undefined,
iconClass: undefined,
position: {
top: 'auto',
right: '30px',
bottom: '30px',
left: 'auto'
}
};
}
// Find existing container or create new one
const containerId = globalOptions?.containerKey ?
`toast-container-${globalOptions.containerKey}` :
'toast-container';
this.container = document.getElementById(containerId);
if (!this.container) {
this.container = document.createElement('div');
this.container.id = containerId;
this.container.className = 'abp-toast-container';
document.body.appendChild(this.container);
}
this.toasts = [];
this.lastId = 0;
// Merge user provided global options with defaults
this.globalOptions = this.extend({}, AbpToastService.defaultOptions, globalOptions);
this.updateContainerPosition();
}
// Deep merge objects
AbpToastService.prototype.extend = function(target, ...sources) {
sources.forEach(source => {
for (const key in source) {
if (source[key] && typeof source[key] === 'object') {
target[key] = this.extend(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
});
return target;
};
// Update toast container position based on global options
AbpToastService.prototype.updateContainerPosition = function() {
const { position } = this.globalOptions;
Object.assign(this.container.style, position);
};
// Update global options
AbpToastService.prototype.setGlobalOptions = function(options) {
this.globalOptions = this.extend({}, this.globalOptions, options);
this.updateContainerPosition();
};
// Get icon class based on severity
AbpToastService.prototype.getIconClass = function(severity, options) {
// Use custom icon class if provided
if (options.iconClass) {
return options.iconClass;
}
const icons = {
success: 'bi-check',
info: 'bi-info-circle',
warning: 'bi-exclamation-triangle',
error: 'bi-shield-exclamation'
};
return icons[severity] || 'bi-exclamation-triangle';
};
// Create toast DOM element
AbpToastService.prototype.createToastElement = function(message, title, severity, options) {
const toast = document.createElement('div');
toast.className = `abp-toast abp-toast-${severity}`;
const closeButton = options.closable !== false ?
`<button class="abp-toast-close-button">
<i class="bi bi-x fs-4" aria-hidden="true"></i>
</button>` : '';
const titleHtml = title ? `<div class="abp-toast-title">${title}</div>` : '';
toast.innerHTML = `
<div class="abp-toast-icon">
<i class="bi ${this.getIconClass(severity, options)} icon" aria-hidden="true"></i>
</div>
<div class="abp-toast-content">
${closeButton}
${titleHtml}
<p class="abp-toast-message">${message}</p>
</div>`;
// Add event listeners
const closeButtonElement = toast.querySelector('.abp-toast-close-button');
if (closeButtonElement) {
closeButtonElement.addEventListener('click', () => this.remove(toast));
}
if (options.tapToDismiss) {
toast.addEventListener('click', () => this.remove(toast));
}
return toast;
};
// Show a toast with given options
AbpToastService.prototype.show = function(message, title, severity = 'neutral', options = {}) {
const mergedOptions = this.extend({}, this.globalOptions, options);
const id = ++this.lastId;
const toast = this.createToastElement(message, title, severity, mergedOptions);
// Set data attributes for non-object options
Object.entries(mergedOptions)
.filter(([_, value]) => typeof value !== 'object')
.forEach(([key, value]) => toast.dataset[key] = value);
toast.dataset.id = id;
this.container.appendChild(toast);
this.toasts.push(toast);
// Auto remove if not sticky
if (!mergedOptions.sticky) {
setTimeout(() => this.remove(toast), mergedOptions.life);
}
return id;
};
// Remove a toast with animation
AbpToastService.prototype.remove = function(toastElement) {
toastElement.classList.add('toast-removing');
setTimeout(() => {
if (toastElement.parentNode === this.container) {
this.container.removeChild(toastElement);
}
this.toasts = this.toasts.filter(t => t !== toastElement);
}, 300); // Match animation duration
};
// Convenience methods for different severities
AbpToastService.prototype.success = function(message, title, options) {
return this.show(message, title, 'success', options);
};
AbpToastService.prototype.error = function(message, title, options) {
return this.show(message, title, 'error', options);
};
AbpToastService.prototype.info = function(message, title, options) {
return this.show(message, title, 'info', options);
};
AbpToastService.prototype.warning = function(message, title, options) {
return this.show(message, title, 'warning', options);
};
// Clear all toasts
AbpToastService.prototype.clear = function(containerKey) {
if (containerKey) {
this.toasts = this.toasts.filter(toast => {
const shouldRemove = toast.dataset.containerKey === containerKey;
if (shouldRemove) {
this.remove(toast);
}
return !shouldRemove;
});
} else {
this.toasts.forEach(toast => this.remove(toast));
}
};
// Static method to set default options for all instances
AbpToastService.setDefaultOptions = function(options) {
AbpToastService.defaultOptions = this.prototype.extend({}, AbpToastService.defaultOptions, options);
};
var abp = abp || {};
(function () {
var toast;
function getToast() {
if (!toast) {
toast = new AbpToastService();
}
return toast;
}
abp.notify.success = function (message, title, options) {
getToast().success(message, title, options);
};
abp.notify.info = function (message, title, options) {
getToast().info(message, title, options);
};
abp.notify.warn = function (message, title, options) {
getToast().warning(message, title, options);
};
abp.notify.error = function (message, title, options) {
getToast().error(message, title, options);
};
})();

34
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js

@ -1,34 +0,0 @@
var abp = abp || {};
(function () {
if (!toastr) {
return;
}
/* DEFAULTS *************************************************/
toastr.options.positionClass = 'toast-bottom-right';
/* NOTIFICATION *********************************************/
var showNotification = function (type, message, title, options) {
toastr[type](message, title, options);
};
abp.notify.success = function (message, title, options) {
showNotification('success', message, title, options);
};
abp.notify.info = function (message, title, options) {
showNotification('info', message, title, options);
};
abp.notify.warn = function (message, title, options) {
showNotification('warning', message, title, options);
};
abp.notify.error = function (message, title, options) {
showNotification('error', message, title, options);
};
})();

3
npm/packs/aspnetcore.mvc.ui.theme.shared/package.json

@ -24,8 +24,7 @@
"@abp/moment": "~9.1.0-rc.1",
"@abp/select2": "~9.1.0-rc.1",
"@abp/sweetalert2": "~9.1.0-rc.1",
"@abp/timeago": "~9.1.0-rc.1",
"@abp/toastr": "~9.1.0-rc.1"
"@abp/timeago": "~9.1.0-rc.1"
},
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431",
"homepage": "https://abp.io",

Loading…
Cancel
Save