Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
portableapps
/
theme
/
default
/
vendor
:
themestrap.plugin.alert.js
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
// Alert (((themestrap = {}, $) => { const instanceName = '__pluginAlert'; // Injected stylesheet — keyed to STYLE_ID so it runs only once per page const STYLE_ID = 'ts-alert-styles'; // CSS_TEXT is injected lazily by injectStyles() on first build (see below), // so loading this script never adds CSS to pages that don't use the plugin. const CSS_TEXT = `/* Themestrap — PluginAlert */ .alert-ts-primary { --alert-ts-bg : var(--primary-rgba-10); --alert-ts-border : var(--primary); --alert-ts-icon : var(--primary); --alert-ts-title : var(--primary-300); --alert-ts-text : var(--primary-200); --alert-ts-action : var(--primary-200); --alert-ts-action-hover : var(--primary-300); --alert-ts-close : var(--primary--200); --alert-ts-close-hover : var(--primary-300); } .alert-ts-secondary { --alert-ts-bg : var(--secondary-rgba-10); --alert-ts-border : var(--secondary); --alert-ts-icon : var(--secondary); --alert-ts-title : var(--secondary-300); --alert-ts-text : var(--secondary-200); --alert-ts-action : var(--secondary-200); --alert-ts-action-hover : var(--secondary-300); --alert-ts-close : var(--secondary--200); --alert-ts-close-hover : var(--secondary-300); } .alert-ts-tertiary { --alert-ts-bg : var(--tertiary-rgba-10); --alert-ts-border : var(--tertiary); --alert-ts-icon : var(--tertiary); --alert-ts-title : var(--tertiary-300); --alert-ts-text : var(--tertiary-200); --alert-ts-action : var(--tertiary-200); --alert-ts-action-hover : var(--tertiary-300); --alert-ts-close : var(--tertiary--200); --alert-ts-close-hover : var(--tertiary-300); } .alert-ts-quaternary { --alert-ts-bg : var(--quaternary-rgba-10); --alert-ts-border : var(--quaternary); --alert-ts-icon : var(--quaternary); --alert-ts-title : var(--quaternary-300); --alert-ts-text : var(--quaternary-200); --alert-ts-action : var(--quaternary-200); --alert-ts-action-hover : var(--quaternary-300); --alert-ts-close : var(--quaternary--200); --alert-ts-close-hover : var(--quaternary-300); } .alert-ts-light { --alert-ts-bg : var(--light-100); --alert-ts-border : var(--grey-800); --alert-ts-icon : var(--light-300); --alert-ts-title : var(--dark-300); --alert-ts-text : var(--dark-200); --alert-ts-action : var(--dark-200); --alert-ts-action-hover : var(--dark-300); --alert-ts-close : var(--dark--200); --alert-ts-close-hover : var(--dark-300); } .alert-ts-dark { --alert-ts-bg : var(--dark--300); --alert-ts-border : var(--dark-300); --alert-ts-icon : var(--dark-300); --alert-ts-title : var(--light-300); --alert-ts-text : var(--light-200); --alert-ts-action : var(--light-200); --alert-ts-action-hover : var(--light-300); --alert-ts-close : var(--light--200); --alert-ts-close-hover : var(--light-300); } .alert-ts-info { --alert-ts-bg : #eff6ff; --alert-ts-border : #3b82f6; --alert-ts-icon : #3b82f6; --alert-ts-title : #1e40af; --alert-ts-text : #1d4ed8; --alert-ts-action : #1d4ed8; --alert-ts-action-hover : #1e40af; --alert-ts-close : #93c5fd; --alert-ts-close-hover : #1d4ed8; } .alert-ts-success { --alert-ts-bg : #f0fdf4; --alert-ts-border : #22c55e; --alert-ts-icon : #22c55e; --alert-ts-title : #14532d; --alert-ts-text : #15803d; --alert-ts-action : #15803d; --alert-ts-action-hover : #14532d; --alert-ts-close : #86efac; --alert-ts-close-hover : #15803d; } .alert-ts-warning { --alert-ts-bg : #fffbeb; --alert-ts-border : #f59e0b; --alert-ts-icon : #f59e0b; --alert-ts-title : #78350f; --alert-ts-text : #92400e; --alert-ts-action : #92400e; --alert-ts-action-hover : #78350f; --alert-ts-close : #fcd34d; --alert-ts-close-hover : #92400e; } .alert-ts-danger { --alert-ts-bg : #fef2f2; --alert-ts-border : #ef4444; --alert-ts-icon : #ef4444; --alert-ts-title : #7f1d1d; --alert-ts-text : #991b1b; --alert-ts-action : #991b1b; --alert-ts-action-hover : #7f1d1d; --alert-ts-close : #fca5a5; --alert-ts-close-hover : #991b1b; } .alert-ts-neutral { --alert-ts-bg : #f9fafb; --alert-ts-border : #6b7280; --alert-ts-icon : #6b7280; --alert-ts-title : #111827; --alert-ts-text : #374151; --alert-ts-action : #374151; --alert-ts-action-hover : #111827; --alert-ts-close : #d1d5db; --alert-ts-close-hover : #374151; } /* Base alert-ts layout */ .alert.alert-ts { display : flex; align-items : flex-start; gap : 0.75rem; padding : 1rem 1rem 1rem 1.25rem; border-radius : 0.5rem; border-width : 0; border-left : 4px solid var(--alert-ts-border); background-color : var(--alert-ts-bg); position : relative; overflow : hidden; /* clips countdown bar */ } .alert.alert-ts.alert-sm { padding: 5px 10px; font-size: 0.9em; } .alert.alert-ts.alert-lg { padding: 20px; font-size: 1.2em; } /* Remove Bootstrap's default coloured border/bg — our tokens take over */ .alert.alert-ts[class*="alert-primary"], .alert.alert-ts[class*="alert-secondary"], .alert.alert-ts[class*="alert-success"], .alert.alert-ts[class*="alert-danger"], .alert.alert-ts[class*="alert-warning"], .alert.alert-ts[class*="alert-info"], .alert.alert-ts[class*="alert-light"], .alert.alert-ts[class*="alert-dark"] { background-color : var(--alert-ts-bg); border-color : transparent; border-left-color: var(--alert-ts-border); color : var(--alert-ts-text); } /* Icon */ .alert-ts-icon { flex-shrink : 0; width : 1.25rem; /* 20 px */ height : 1.25rem; color : var(--alert-ts-icon); margin-top : 1px; /* optical alignment with first line of text */ } .alert-ts-icon svg { display : block; width : 100%; height : 100%; } /* Body */ .alert-ts-body { flex : 1 1 auto; min-width: 0; } .alert-ts-title { font-size : 0.975rem; /* 14 px */ font-weight : 600; line-height : 1.4; color : var(--alert-ts-title); margin : 0 0 0.25rem; } .alert-ts-text { font-size : 0.975rem; line-height : 1.5; color : var(--alert-ts-text); } .alert-ts-text p:last-child { margin-bottom: 0; } /* Action buttons */ .alert-ts-actions { display : flex; flex-wrap : wrap; gap : 0.5rem; margin-top : 0.75rem; } .alert-ts-action { display : inline-flex; align-items : center; padding : 0.375rem 0.75rem; font-size : 0.875rem; font-weight : 500; line-height : 1; border-radius : 0.375rem; border : 1.5px solid transparent; cursor : pointer; transition : background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease; text-decoration : none; white-space : nowrap; } /* Primary action — filled */ .alert-ts-action-primary { background-color : var(--alert-ts-action); border-color : var(--alert-ts-action); color : #fff; } .alert-ts-action-primary:hover, .alert-ts-action-primary:focus-visible { background-color : var(--alert-ts-action-hover); border-color : var(--alert-ts-action-hover); color : #fff; } /* Secondary action — ghost / outlined */ .alert-ts-action-secondary { background-color : transparent; border-color : var(--alert-ts-action); color : var(--alert-ts-action); } .alert-ts-action-secondary:hover, .alert-ts-action-secondary:focus-visible { background-color : var(--alert-ts-action); color : #fff; } /* Link-style action — no border, just tinted text */ .alert-ts-action-link { background : none; border-color: transparent; color : var(--alert-ts-action); padding-left : 0; padding-right: 0; } .alert-ts-action-link:hover, .alert-ts-action-link:focus-visible { color : var(--alert-ts-action-hover); text-decoration : underline; } .alert-ts-action:focus-visible { outline : 2px solid var(--alert-ts-border); outline-offset : 2px; } /* Dismiss (×) button */ .alert-ts-close { flex-shrink : 0; display : inline-flex; align-items : center; justify-content : center; width : 1.5rem; height : 1.5rem; padding : 0; margin-left : auto; background : none; border : none; border-radius : 0.375rem; color : var(--alert-ts-close); cursor : pointer; transition : color 0.15s ease, background-color 0.15s ease; } .alert-ts-close svg { display : block; width : 1.25rem; height : 1.25rem; } .alert-ts-close:hover { color : var(--alert-ts-close-hover); background-color : color-mix(in srgb, var(--alert-ts-border) 12%, transparent); } .alert-ts-close:focus-visible { outline : 2px solid var(--alert-ts-border); outline-offset : 2px; color : var(--alert-ts-close-hover); } /* Countdown progress bar */ .alert-ts-countdown { position : absolute; bottom : 0; left : 0; width : 100%; height : 3px; background-color : var(--alert-ts-border); opacity : 0.45; transform : scaleX(1); transform-origin : left center; transition-property : transform; transition-timing-function: linear; /* transitionDuration is set dynamically by JS */ } /* Toast stack (programmatic / create()) */ .alert-toast-stack { position : fixed; top : 1.25rem; right : 1.25rem; z-index : 1090; display : flex; flex-direction : column; gap : 0.625rem; width : clamp(280px, 90vw, 400px); pointer-events: none; } .alert-toast-stack > .alert-ts { pointer-events : auto; box-shadow : 0 4px 6px -1px rgba(0,0,0,.08), 0 2px 4px -2px rgba(0,0,0,.06); } /* Slide-in entrance for toasts */ .alert-ts-toast { animation: alertTsSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) both; } @keyframes alertTsSlideIn { from { opacity : 0; transform : translateX(2rem); } to { opacity : 1; transform : translateX(0); } } /* Dark-mode palette */ html.dark .alert-ts-info { --alert-ts-bg : #172554; --alert-ts-border : #3b82f6; --alert-ts-icon : #60a5fa; --alert-ts-title : #bfdbfe; --alert-ts-text : #93c5fd; --alert-ts-action : #93c5fd; --alert-ts-action-hover : #bfdbfe; --alert-ts-close : #3b82f6; --alert-ts-close-hover : #bfdbfe; } html.dark .alert-ts-success { --alert-ts-bg : #052e16; --alert-ts-border : #22c55e; --alert-ts-icon : #4ade80; --alert-ts-title : #bbf7d0; --alert-ts-text : #86efac; --alert-ts-action : #86efac; --alert-ts-action-hover : #bbf7d0; --alert-ts-close : #22c55e; --alert-ts-close-hover : #bbf7d0; } html.dark .alert-ts-warning { --alert-ts-bg : #27170a; --alert-ts-border : #f59e0b; --alert-ts-icon : #fbbf24; --alert-ts-title : #fde68a; --alert-ts-text : #fcd34d; --alert-ts-action : #fcd34d; --alert-ts-action-hover : #fde68a; --alert-ts-close : #f59e0b; --alert-ts-close-hover : #fde68a; } html.dark .alert-ts-danger { --alert-ts-bg : #2d0a0a; --alert-ts-border : #ef4444; --alert-ts-icon : #f87171; --alert-ts-title : #fecaca; --alert-ts-text : #fca5a5; --alert-ts-action : #fca5a5; --alert-ts-action-hover : #fecaca; --alert-ts-close : #ef4444; --alert-ts-close-hover : #fecaca; } html.dark .alert-ts-neutral { --alert-ts-bg : #111827; --alert-ts-border : #6b7280; --alert-ts-icon : #9ca3af; --alert-ts-title : #f9fafb; --alert-ts-text : #d1d5db; --alert-ts-action : #d1d5db; --alert-ts-action-hover : #f9fafb; --alert-ts-close : #6b7280; --alert-ts-close-hover : #f9fafb; } .alert.alert-ts { box-shadow: inset 0 0 0 1px rgba(255,255,255,.06); } /* Reduced-motion overrides */ @media (prefers-reduced-motion: reduce) { .alert-ts-toast { animation: none; } .alert-ts-countdown { transition: none; } }`; // Inject the stylesheet only when the plugin is actually used (called // from build()), so loading the script never adds CSS to unused pages. function injectStyles() { if (document.getElementById(STYLE_ID)) return; const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = CSS_TEXT; (document.head || document.documentElement).appendChild(style); } const ICONS = { info: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" fill-rule="evenodd" d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2s10 4.477 10 10m-10 5.75a.75.75 0 0 0 .75-.75v-6a.75.75 0 0 0-1.5 0v6c0 .414.336.75.75.75M12 7a1 1 0 1 1 0 2a1 1 0 0 1 0-2" clip-rule="evenodd" /> </svg>`, success: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" fill-rule="evenodd" d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2s10 4.477 10 10m-5.97-3.03a.75.75 0 0 1 0 1.06l-5 5a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 1 1 1.06-1.06l1.47 1.47l2.235-2.235L14.97 8.97a.75.75 0 0 1 1.06 0" clip-rule="evenodd" /> </svg>`, warning: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" fill-rule="evenodd" d="M5.312 10.762C8.23 5.587 9.689 3 12 3s3.77 2.587 6.688 7.762l.364.644c2.425 4.3 3.638 6.45 2.542 8.022S17.786 21 12.364 21h-.728c-5.422 0-8.134 0-9.23-1.572s.117-3.722 2.542-8.022zM12 7.25a.75.75 0 0 1 .75.75v5a.75.75 0 0 1-1.5 0V8a.75.75 0 0 1 .75-.75M12 17a1 1 0 1 0 0-2a1 1 0 0 0 0 2" clip-rule="evenodd" /> </svg>`, danger: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" fill-rule="evenodd" d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2s10 4.477 10 10M8.97 8.97a.75.75 0 0 1 1.06 0L12 10.94l1.97-1.97a.75.75 0 0 1 1.06 1.06L13.06 12l1.97 1.97a.75.75 0 0 1-1.06 1.06L12 13.06l-1.97 1.97a.75.75 0 0 1-1.06-1.06L10.94 12l-1.97-1.97a.75.75 0 0 1 0-1.06" clip-rule="evenodd" /> </svg>`, neutral: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"> <path fill="currentColor" fill-rule="evenodd" d="M9.25 18.709c0-.42.336-.76.75-.76h4c.414 0 .75.34.75.76s-.336.76-.75.76h-4a.755.755 0 0 1-.75-.76m.667 2.532c0-.42.335-.76.75-.76h2.666c.415 0 .75.34.75.76a.754.754 0 0 1-.75.759h-2.666a.755.755 0 0 1-.75-.76" clip-rule="evenodd" /> <path fill="currentColor" d="m7.41 13.828l1.105 1.053c.31.295.485.707.485 1.137c0 .647.518 1.172 1.157 1.172h3.686c.639 0 1.157-.525 1.157-1.172c0-.43.176-.842.485-1.137l1.104-1.053c1.542-1.48 2.402-3.425 2.41-5.446L19 8.297C19 4.842 15.866 2 12 2S5 4.842 5 8.297v.085c.009 2.021.87 3.966 2.41 5.446" /> </svg>` }; //Bootstrap class ? plugin type const BS_TYPE_MAP = { 'alert-default' : 'neutral', 'alert-primary' : 'primary', 'alert-secondary' : 'secondary', 'alert-tertiary' : 'tertiary', 'alert-quaternary' : 'quaternary', 'alert-info' : 'info', 'alert-success' : 'success', 'alert-warning' : 'warning', 'alert-danger' : 'danger', 'alert-secondary' : 'secondary', 'alert-light' : 'light', 'alert-dark' : 'dark' }; // Close-button SVG const CLOSE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"/> </svg>`; class PluginAlert { constructor($el, opts) { return this.initialize($el, opts); } initialize($el, opts) { if ($el.data(instanceName)) { return this; } this.$el = $el; this.$countdown = null; this._timer = null; this._startTime = null; this._remaining = null; this._initialHTML = $el.html(); // saved before build mutates the DOM this .setData() .setOptions(opts) .build() .events(); return this; } setData() { this.$el.data(instanceName, this); return this; } setOptions(opts) { const pluginOptions = themestrap.fn.getOptions(this.$el.data('plugin-options')); this.options = $.extend(true, {}, PluginAlert.defaults, pluginOptions, opts, { wrapper: this.$el }); return this; } build() { // Inject component CSS lazily - only when an instance is built. injectStyles(); const self = this; const o = self.options; // Resolve type from Bootstrap classes when not explicitly provided if (!o.type) { o.type = self._detectType(); } // Apply skin classes self.$el .addClass(`alert-ts alert-ts-${o.type}`) .attr('role', 'alert'); // Restructure markup self._buildStructure(); // Countdown progress bar if (o.showCountdown && o.autoDismiss) { self.$el.css('position', 'relative'); self.$countdown = $('<div>', { class: 'alert-ts-countdown' }) .appendTo(self.$el); requestAnimationFrame(() => { requestAnimationFrame(() => { if (self.$countdown) { self.$countdown.css({ transitionDuration : `${o.delay}ms`, transform : 'scaleX(0)' }); } }); }); } // Auto-dismiss timer if (o.autoDismiss) { self._remaining = o.delay; self._startTimer(); } return this; } _detectType() { let type = 'info'; $.each(BS_TYPE_MAP, (cls, mapped) => { if (this.$el.hasClass(cls)) { type = mapped; return false; // break $.each } }); return type; } /** * Rearranges the alert's inner HTML * Works for HTML-authored alerts and create()-generated ones. */ _buildStructure() { const self = this; const o = self.options; // Detach any existing Bootstrap close button const $bsClose = self.$el.find('.btn-close').detach(); // Capture remaining content as the message (only when no explicit message option) const messageHTML = (o.message != null) ? o.message : self.$el.html().trim(); self.$el.empty(); // Icon if (o.showIcon) { $('<div>', { class: 'alert-ts-icon', 'aria-hidden': 'true' }) .html(ICONS[o.type] || ICONS.info) .appendTo(self.$el); } // Body const $body = $('<div>', { class: 'alert-ts-body' }).appendTo(self.$el); if (o.title) { $('<p>', { class: 'alert-ts-title' }).text(o.title).appendTo($body); } if (messageHTML) { $('<div>', { class: 'alert-ts-text' }).html(messageHTML).appendTo($body); } // Action links / buttons if (o.actions && o.actions.length) { const $actions = $('<div>', { class: 'alert-ts-actions' }); o.actions.forEach(action => { $('<button>', { type : 'button', class : `alert-ts-action alert-ts-action-${action.variant || 'primary'} ${action.class || ''}`.trim(), text : action.label, 'data-action': action.key || '' }).appendTo($actions); }); $actions.appendTo($body); } // Dismiss button if (o.dismissible) { $('<button>', { type : 'button', class : 'alert-ts-close', 'aria-label': 'Dismiss alert' }).html(CLOSE_SVG).appendTo(self.$el); } else if ($bsClose.length) { // Preserve original Bootstrap close button when present $bsClose.addClass('alert-ts-close').appendTo(self.$el); } } events() { const self = this; const o = self.options; // Dismiss on close-button click self.$el.on('click.pluginalert', '.alert-ts-close, .btn-close', e => { e.preventDefault(); e.stopPropagation(); self.dismiss(); }); // Action button clicks — fire a namespaced jQuery event and optional callback self.$el.on('click.pluginalert', '.alert-ts-action', function() { const key = $(this).data('action'); self.$el.trigger('action.alert', [key, self]); if ($.isFunction(o.onAction)) { o.onAction.call(self, key); } }); // Hover-pause if (o.pauseOnHover && o.autoDismiss) { self.$el .on('mouseenter.pluginalert', () => self._pauseTimer()) .on('mouseleave.pluginalert', () => self._resumeTimer()); } return this; } _startTimer() { this._startTime = Date.now(); this._timer = setTimeout(() => this.dismiss(), this._remaining); return this; } _pauseTimer() { if (!this._timer) return this; clearTimeout(this._timer); this._timer = null; this._remaining -= Date.now() - this._startTime; if (this.$countdown) { const ratio = Math.max(0, this._remaining / this.options.delay); this.$countdown.css({ transitionDuration : '0ms', transform : `scaleX(${ratio})` }); } return this; } _resumeTimer() { if (this._timer) return this; this._startTimer(); if (this.$countdown) { requestAnimationFrame(() => { requestAnimationFrame(() => { if (this.$countdown) { this.$countdown.css({ transitionDuration : `${this._remaining}ms`, transform : 'scaleX(0)' }); } }); }); } return this; } /** * Animate the alert out, then remove or hide it. * Fires close.bs.alert and closed.bs.alert to preserve Bootstrap compatibility. */ dismiss() { const self = this; const o = self.options; clearTimeout(self._timer); self._timer = null; self.$el.trigger('close.bs.alert'); if (o.animation === 'slide') { self.$el.css({ overflow : 'hidden', maxHeight : `${self.$el.outerHeight(true)}px`, transition : `max-height ${o.animationDuration}ms ease, opacity ${o.animationDuration}ms ease, margin ${o.animationDuration}ms ease, padding ${o.animationDuration}ms ease` }); setTimeout(() => { self.$el.css({ maxHeight : '0px', opacity : 0, marginTop : 0, marginBottom : 0, paddingTop : 0, paddingBottom : 0 }); setTimeout(() => self._afterDismiss(), o.animationDuration); }, 20); } else { self.$el.css({ transition : `opacity ${o.animationDuration}ms ease`, opacity : 0 }); setTimeout(() => self._afterDismiss(), o.animationDuration); } return this; } _afterDismiss() { const self = this; self.$el.trigger('closed.bs.alert'); if ($.isFunction(self.options.onDismiss)) { self.options.onDismiss.call(self); } if (self.options.remove) { self.$el.remove(); } else { self.$el.css('display', 'none'); } } destroy() { const self = this; clearTimeout(self._timer); self._timer = null; self.$el.off('.pluginalert'); self.$el.html(self._initialHTML); self.$el.removeClass((_, cls) => cls.split(' ').filter(c => c.startsWith('alert-ts')).join(' ') ); self.$el.css({ position : '', overflow : '', maxHeight : '', opacity : '', transition : '', marginTop : '', marginBottom : '', paddingTop : '', paddingBottom : '' }); self.$countdown = null; self.$el.removeData(instanceName); return this; } } PluginAlert.defaults = { // Type: 'info' | 'success' | 'warning' | 'danger' | 'neutral' // Auto-detected from Bootstrap classes when null. type : null, // Layout / content showIcon : false, // inject icon matching the type dismissible : false, // render dismiss button title : null, // bold heading above the message message : null, // message HTML (used by create(); HTML alerts auto-detect) actions : [], // [{label, key, variant, class}] // Behaviour autoDismiss : false, delay : 5000, // ms before auto-dismiss pauseOnHover : true, // Animation animation : 'fade', // 'slide' | 'fade' animationDuration : 400, showCountdown : true, // shrinking bar along the bottom edge // Lifecycle callbacks onDismiss : null, // fn() — called after the alert is removed onAction : null, // fn(key) — called when an action button is clicked remove : true // true: remove from DOM; false: display:none }; /** * PluginAlert.create(containerSelector, opts) * * Programmatically create and inject a new alert. If no container is given, * alerts stack inside a shared `.alert-toast-stack` (auto-created on <body>). * * @param {string|jQuery} container Target selector / jQuery object (optional). * @param {object} opts Any PluginAlert options. `message` is expected. * @returns {PluginAlert} The newly-created plugin instance. * * Examples: * * // Toast with a title and two action buttons * themestrap.PluginAlert.create({ * type : 'warning', * title : 'Unsaved changes', * message : 'You have unsaved changes. Do you want to save them?', * delay : 8000, * actions : [ * { label: 'Save', key: 'save', variant: 'primary' }, * { label: 'Discard', key: 'discard', variant: 'secondary' } * ], * onAction(key) { * if (key === 'save') saveChanges(); * this.dismiss(); * } * }); * * // Inject into a specific container * themestrap.PluginAlert.create('#page-notifications', { * type : 'success', * message : 'Profile updated successfully.' * }); */ PluginAlert.create = function(container, opts) { // Allow omitting container argument if (container && typeof container === 'object' && !container.jquery) { opts = container; container = null; } const o = $.extend(true, {}, PluginAlert.defaults, opts); // Resolve / create container let $container = container ? $(container) : null; if (!$container || !$container.length) { $container = $('.alert-toast-stack'); if (!$container.length) { $container = $('<div>', { class: 'alert-toast-stack' }).appendTo('body'); } } // Build a minimal Bootstrap-compatible alert element const $alert = $('<div>', { class : `alert alert-ts alert-ts-${o.type || 'info'} alert-ts-toast`, role : 'alert' }).appendTo($container); return new PluginAlert($alert, o); }; $.extend(themestrap, { PluginAlert }); $.fn.themestrapPluginAlert = function(opts) { return this.each(function() { const $this = $(this); const instance = $this.data(instanceName); if (typeof opts === 'string') { if (instance && $.isFunction(instance[opts])) { instance[opts](); } else if (opts !== 'dismiss' && opts !== 'destroy') { console.warn(`[PluginAlert] Unknown command: "${opts}"`); } return; } if (!instance) { new PluginAlert($this, opts); } }); }; })).apply(this, [window.themestrap, jQuery]);