+ ${closeButton}
+ ${titleHtml}
+
${message}
+
`;
+
+ // 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 () {
+ abp.notify.success = function (message, title, options) {
+ new AbpToastService().success(message, title, options);
+ };
+
+ abp.notify.info = function (message, title, options) {
+ new AbpToastService().info(message, title, options);
+ };
+
+ abp.notify.warn = function (message, title, options) {
+ new AbpToastService().warning(message, title, options);
+ };
+
+ abp.notify.error = function (message, title, options) {
+ new AbpToastService().error(message, title, options);
+ };
+})();
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
deleted file mode 100644
index 37a7c40166..0000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
+++ /dev/null
@@ -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);
- };
-
-})();
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BlobStoring.Bunny/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.Bunny/FodyWeavers.xml
new file mode 100644
index 0000000000..1715698ccd
--- /dev/null
+++ b/framework/src/Volo.Abp.BlobStoring.Bunny/FodyWeavers.xml
@@ -0,0 +1,3 @@
+