minisnackbar 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +33 -19
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/controller.d.ts +27 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/index.d.ts +3 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/minisnackbar.cjs +406 -357
- package/dist/minisnackbar.cjs.map +1 -1
- package/dist/minisnackbar.esm.js +372 -317
- package/dist/minisnackbar.esm.js.map +1 -1
- package/dist/minisnackbar.js +420 -367
- package/dist/minisnackbar.js.map +1 -1
- package/dist/minisnackbar.min.js +11 -10
- package/dist/minisnackbar.min.js.map +1 -1
- package/dist/renderer.d.ts +18 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/styles.d.ts +4 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validation.d.ts +6 -0
- package/dist/validation.d.ts.map +1 -0
- package/docs.md +514 -0
- package/package.json +64 -63
- package/dist/minisnackbar.min.cjs +0 -12
- package/dist/minisnackbar.min.cjs.map +0 -1
package/dist/minisnackbar.esm.js
CHANGED
|
@@ -1,370 +1,425 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
// Snackbar class
|
|
12
|
-
class Snackbar {
|
|
13
|
-
static init(options = {}) {
|
|
14
|
-
if (this._initialized)
|
|
15
|
-
return;
|
|
16
|
-
if (typeof document === 'undefined' || !document.body) {
|
|
17
|
-
console.error('Snackbar: DOM is not available');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
if (options.transitionDuration && typeof options.transitionDuration === 'number') {
|
|
21
|
-
this._transitionDuration = options.transitionDuration;
|
|
22
|
-
}
|
|
23
|
-
if (document.getElementById('mini-snackbar')) {
|
|
24
|
-
this._initialized = true;
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
if (!document.getElementById('mini-snackbar-styles')) {
|
|
28
|
-
const style = document.createElement('style');
|
|
29
|
-
style.id = 'mini-snackbar-styles';
|
|
30
|
-
style.textContent = `
|
|
31
|
-
.mini-snackbar {
|
|
32
|
-
/* Positioning */
|
|
33
|
-
position: fixed;
|
|
34
|
-
z-index: 1000;
|
|
35
|
-
left: 50%;
|
|
36
|
-
bottom: 30px;
|
|
37
|
-
transform: translateX(-50%) translateY(100%);
|
|
38
|
-
|
|
39
|
-
/* Sizing */
|
|
40
|
-
min-width: 250px;
|
|
41
|
-
max-width: 90%;
|
|
42
|
-
|
|
43
|
-
/* Layout */
|
|
44
|
-
display: flex;
|
|
45
|
-
align-items: center;
|
|
46
|
-
justify-content: space-between;
|
|
47
|
-
gap: 8px;
|
|
48
|
-
padding: 0.875rem 1rem;
|
|
49
|
-
|
|
50
|
-
/* Visibility */
|
|
51
|
-
visibility: hidden;
|
|
52
|
-
|
|
53
|
-
/* Theming */
|
|
54
|
-
background-color: var(--mini-snackbar-bg, var(--md-sys-color-inverse-surface, rgba(255, 255, 255, 1)));
|
|
55
|
-
color: var(--mini-snackbar-text, var(--md-sys-color-inverse-on-surface, rgba(27, 27, 27, 1)));
|
|
56
|
-
border: var(--mini-snackbar-border, none);
|
|
57
|
-
font-family: var(--mini-snackbar-font-family, inherit);
|
|
58
|
-
font-size: 0.875rem;
|
|
59
|
-
text-align: left;
|
|
60
|
-
border-radius: var(--mini-snackbar-radius, 1rem);
|
|
61
|
-
box-shadow: var(--mini-snackbar-shadow, 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12));
|
|
62
|
-
|
|
63
|
-
/* Animation */
|
|
64
|
-
transition: var(--mini-snackbar-transition, transform ${this._transitionDuration}ms ease-in-out);
|
|
65
|
-
}
|
|
1
|
+
const SNACKBAR_ID = 'mini-snackbar';
|
|
2
|
+
const STYLES_ID = 'mini-snackbar-styles';
|
|
3
|
+
const SNACKBAR_CLASS = 'mini-snackbar';
|
|
4
|
+
const SNACKBAR_VISIBLE_CLASS = 'show';
|
|
5
|
+
const SNACKBAR_TEXT_CLASS = 'mini-snackbar-text';
|
|
6
|
+
const SNACKBAR_ACTION_CLASS = 'mini-snackbar-action';
|
|
7
|
+
const DEFAULT_DURATION = 3000;
|
|
8
|
+
const DEFAULT_TRANSITION_DURATION = 250;
|
|
9
|
+
const QUEUE_GAP_DURATION = 200;
|
|
66
10
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
11
|
+
function createSnackbarStyles(transitionDuration) {
|
|
12
|
+
return `
|
|
13
|
+
.mini-snackbar {
|
|
14
|
+
position: fixed;
|
|
15
|
+
z-index: 1000;
|
|
16
|
+
left: 50%;
|
|
17
|
+
bottom: 30px;
|
|
18
|
+
transform: translateX(-50%) translateY(100%);
|
|
19
|
+
min-width: 250px;
|
|
20
|
+
max-width: 90%;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
gap: 8px;
|
|
25
|
+
padding: 0.875rem 1rem;
|
|
26
|
+
visibility: hidden;
|
|
27
|
+
background-color: var(--mini-snackbar-bg, var(--md-sys-color-inverse-surface, rgba(255, 255, 255, 1)));
|
|
28
|
+
color: var(--mini-snackbar-text, var(--md-sys-color-inverse-on-surface, rgba(27, 27, 27, 1)));
|
|
29
|
+
border: var(--mini-snackbar-border, none);
|
|
30
|
+
font-family: var(--mini-snackbar-font-family, inherit);
|
|
31
|
+
font-size: 0.875rem;
|
|
32
|
+
text-align: left;
|
|
33
|
+
border-radius: var(--mini-snackbar-radius, 1rem);
|
|
34
|
+
box-shadow: var(--mini-snackbar-shadow, 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12));
|
|
35
|
+
transition: var(--mini-snackbar-transition, transform ${transitionDuration}ms ease-in-out);
|
|
36
|
+
}
|
|
71
37
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
margin: -0.5rem -0.5rem -0.5rem 0;
|
|
77
|
-
}
|
|
38
|
+
.mini-snackbar.show {
|
|
39
|
+
visibility: visible;
|
|
40
|
+
transform: translateX(-50%) translateY(0);
|
|
41
|
+
}
|
|
78
42
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
margin: -0.5rem -0.5rem -0.5rem 0;
|
|
85
|
-
border: none;
|
|
86
|
-
background: var(--mini-snackbar-btn-bg, transparent);
|
|
87
|
-
font-size: inherit;
|
|
88
|
-
font-family: inherit;
|
|
89
|
-
font-weight: 500;
|
|
90
|
-
letter-spacing: 0.0892857143em;
|
|
91
|
-
text-transform: uppercase;
|
|
92
|
-
color: var(--mini-snackbar-btn-text, inherit);
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
user-select: none;
|
|
95
|
-
border-radius: var(--mini-snackbar-btn-radius, 1rem);
|
|
96
|
-
transition: opacity 0.2s ease;
|
|
97
|
-
}
|
|
43
|
+
.mini-snackbar .mini-snackbar-action {
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
padding: 0.5rem 1rem;
|
|
46
|
+
margin: -0.5rem -0.5rem -0.5rem 0;
|
|
47
|
+
}
|
|
98
48
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
49
|
+
.mini-snackbar button.mini-snackbar-action {
|
|
50
|
+
border: none;
|
|
51
|
+
background: var(--mini-snackbar-btn-bg, transparent);
|
|
52
|
+
font-size: inherit;
|
|
53
|
+
font-family: inherit;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
letter-spacing: 0.0892857143em;
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
color: var(--mini-snackbar-btn-text, inherit);
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
user-select: none;
|
|
60
|
+
border-radius: var(--mini-snackbar-btn-radius, 1rem);
|
|
61
|
+
transition: opacity 0.2s ease;
|
|
62
|
+
}
|
|
105
63
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
64
|
+
.mini-snackbar button.mini-snackbar-action:hover {
|
|
65
|
+
opacity: var(--mini-snackbar-btn-hover-opacity, 0.8);
|
|
66
|
+
outline: var(--mini-snackbar-btn-hover-outline, 2px solid var(--mini-snackbar-btn-text, inherit));
|
|
67
|
+
outline-offset: var(--mini-snackbar-btn-outline-offset, 2px);
|
|
68
|
+
background-color: var(--mini-snackbar-btn-hover-bg, transparent);
|
|
69
|
+
}
|
|
110
70
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
}
|
|
71
|
+
.mini-snackbar button.mini-snackbar-action:focus {
|
|
72
|
+
outline: var(--mini-snackbar-btn-focus-outline, 2px solid var(--mini-snackbar-btn-text, inherit));
|
|
73
|
+
outline-offset: var(--mini-snackbar-btn-outline-offset, 2px);
|
|
74
|
+
}
|
|
117
75
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
76
|
+
@media (max-width: 600px) {
|
|
77
|
+
.mini-snackbar {
|
|
78
|
+
bottom: 90px;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
.mini-snackbar {
|
|
84
|
+
transition: opacity 0.15s ease;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
function installSnackbarStyles(document, transitionDuration) {
|
|
90
|
+
const existingStyles = document.getElementById(STYLES_ID);
|
|
91
|
+
if (existingStyles)
|
|
92
|
+
return;
|
|
93
|
+
const styles = document.createElement('style');
|
|
94
|
+
styles.id = STYLES_ID;
|
|
95
|
+
styles.textContent = createSnackbarStyles(transitionDuration);
|
|
96
|
+
document.head.appendChild(styles);
|
|
97
|
+
}
|
|
98
|
+
function removeSnackbarStyles(document) {
|
|
99
|
+
document.getElementById(STYLES_ID)?.remove();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class SnackbarRenderer {
|
|
103
|
+
constructor(document) {
|
|
104
|
+
this.actionButton = null;
|
|
105
|
+
this.actionHandler = null;
|
|
106
|
+
this.document = document;
|
|
107
|
+
}
|
|
108
|
+
ensureRoot(transitionDuration) {
|
|
109
|
+
if (!this.document.body)
|
|
110
|
+
return false;
|
|
111
|
+
installSnackbarStyles(this.document, transitionDuration);
|
|
112
|
+
if (this.getRoot())
|
|
113
|
+
return true;
|
|
114
|
+
const snackbar = this.document.createElement('div');
|
|
115
|
+
snackbar.id = SNACKBAR_ID;
|
|
116
|
+
snackbar.className = SNACKBAR_CLASS;
|
|
130
117
|
snackbar.setAttribute('role', 'alert');
|
|
131
118
|
snackbar.setAttribute('aria-live', 'assertive');
|
|
132
119
|
snackbar.setAttribute('aria-atomic', 'true');
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
snackbar.appendChild(
|
|
136
|
-
document.body.appendChild(snackbar);
|
|
137
|
-
|
|
120
|
+
const text = this.document.createElement('span');
|
|
121
|
+
text.className = SNACKBAR_TEXT_CLASS;
|
|
122
|
+
snackbar.appendChild(text);
|
|
123
|
+
this.document.body.appendChild(snackbar);
|
|
124
|
+
return true;
|
|
138
125
|
}
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
142
|
-
|
|
143
|
-
if (snackbar)
|
|
144
|
-
snackbar.remove();
|
|
145
|
-
const styles = document.getElementById('mini-snackbar-styles');
|
|
146
|
-
if (styles)
|
|
147
|
-
styles.remove();
|
|
148
|
-
this._state = 'idle';
|
|
149
|
-
this._isShowing = false;
|
|
150
|
-
this._currentActionHandler = null;
|
|
151
|
-
this._currentTimeout = null;
|
|
152
|
-
this._initialized = false;
|
|
126
|
+
destroy() {
|
|
127
|
+
this.cleanupAction();
|
|
128
|
+
this.getRoot()?.remove();
|
|
129
|
+
removeSnackbarStyles(this.document);
|
|
153
130
|
}
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
if (
|
|
157
|
-
|
|
131
|
+
setMessage(message) {
|
|
132
|
+
const text = this.getRoot()?.querySelector(`.${SNACKBAR_TEXT_CLASS}`);
|
|
133
|
+
if (text)
|
|
134
|
+
text.textContent = message;
|
|
135
|
+
}
|
|
136
|
+
setAction(action, onClick) {
|
|
137
|
+
this.cleanupAction();
|
|
138
|
+
if (!action)
|
|
139
|
+
return;
|
|
140
|
+
const actionButton = this.createActionButton();
|
|
141
|
+
actionButton.classList.add(SNACKBAR_ACTION_CLASS);
|
|
142
|
+
actionButton.textContent = action.text;
|
|
143
|
+
this.actionHandler = onClick;
|
|
144
|
+
actionButton.addEventListener('click', this.actionHandler);
|
|
145
|
+
this.actionButton = actionButton;
|
|
146
|
+
this.getRoot()?.appendChild(actionButton);
|
|
147
|
+
}
|
|
148
|
+
show() {
|
|
149
|
+
this.getRoot()?.classList.add(SNACKBAR_VISIBLE_CLASS);
|
|
150
|
+
}
|
|
151
|
+
hide() {
|
|
152
|
+
this.getRoot()?.classList.remove(SNACKBAR_VISIBLE_CLASS);
|
|
153
|
+
}
|
|
154
|
+
cleanupAction() {
|
|
155
|
+
if (this.actionButton && this.actionHandler) {
|
|
156
|
+
this.actionButton.removeEventListener('click', this.actionHandler);
|
|
157
|
+
}
|
|
158
|
+
this.actionButton?.remove();
|
|
159
|
+
this.actionButton = null;
|
|
160
|
+
this.actionHandler = null;
|
|
161
|
+
}
|
|
162
|
+
getTransitionDuration(fallback) {
|
|
163
|
+
const snackbar = this.getRoot();
|
|
164
|
+
const view = this.document.defaultView;
|
|
165
|
+
if (!snackbar || !view)
|
|
166
|
+
return fallback;
|
|
158
167
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
if (duration && duration !== '0s') {
|
|
162
|
-
const value = parseFloat(duration);
|
|
163
|
-
return duration.includes('ms') ? value : value * 1000;
|
|
164
|
-
}
|
|
168
|
+
const duration = view.getComputedStyle(snackbar).transitionDuration;
|
|
169
|
+
return parseTransitionDuration(duration, fallback);
|
|
165
170
|
}
|
|
166
|
-
catch (
|
|
167
|
-
console.warn('Snackbar: Could not read transition duration from CSS',
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.warn('Snackbar: Could not read transition duration from CSS', error);
|
|
173
|
+
return fallback;
|
|
168
174
|
}
|
|
169
|
-
return this._transitionDuration;
|
|
170
175
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
176
|
+
getRoot() {
|
|
177
|
+
return this.document.getElementById(SNACKBAR_ID);
|
|
178
|
+
}
|
|
179
|
+
createActionButton() {
|
|
180
|
+
const registry = this.document.defaultView?.customElements;
|
|
181
|
+
if (registry?.get('md-text-button')) {
|
|
182
|
+
return this.document.createElement('md-text-button');
|
|
183
|
+
}
|
|
184
|
+
const button = this.document.createElement('button');
|
|
185
|
+
button.type = 'button';
|
|
186
|
+
return button;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function parseTransitionDuration(duration, fallback) {
|
|
190
|
+
if (!duration || duration === '0s')
|
|
191
|
+
return fallback;
|
|
192
|
+
const firstDuration = duration.split(',')[0]?.trim();
|
|
193
|
+
if (!firstDuration)
|
|
194
|
+
return fallback;
|
|
195
|
+
const value = Number.parseFloat(firstDuration);
|
|
196
|
+
if (!Number.isFinite(value))
|
|
197
|
+
return fallback;
|
|
198
|
+
return firstDuration.endsWith('ms') ? value : value * 1000;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const isValidDuration = (duration) => typeof duration === 'number' && Number.isFinite(duration) && duration > 0;
|
|
202
|
+
function normalizeItem(message, action = null, duration = DEFAULT_DURATION, warn = console.warn) {
|
|
203
|
+
if (typeof message !== 'string' || message.trim() === '') {
|
|
204
|
+
warn('Snackbar: Message must be a non-empty string');
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
if (action !== null &&
|
|
208
|
+
(typeof action !== 'object' ||
|
|
209
|
+
typeof action.text !== 'string' ||
|
|
210
|
+
typeof action.handler !== 'function')) {
|
|
211
|
+
warn('Snackbar: Action must be an object with "text" (string) and "handler" (function) properties');
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
if (!isValidDuration(duration)) {
|
|
215
|
+
warn('Snackbar: Duration must be a positive number');
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
message,
|
|
220
|
+
action: action,
|
|
221
|
+
duration
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function normalizeTransitionDuration(duration, warn = console.warn) {
|
|
225
|
+
if (duration === undefined)
|
|
226
|
+
return null;
|
|
227
|
+
if (typeof duration !== 'number' || !Number.isFinite(duration) || duration < 0) {
|
|
228
|
+
warn('Snackbar: transitionDuration must be a non-negative number');
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return duration;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
class SnackbarController {
|
|
235
|
+
constructor() {
|
|
236
|
+
this.queue = [];
|
|
237
|
+
this.state = 'idle';
|
|
238
|
+
this.renderer = null;
|
|
239
|
+
this.displayTimer = null;
|
|
240
|
+
this.transitionTimer = null;
|
|
241
|
+
this.queueGapTimer = null;
|
|
242
|
+
this.transitionDuration = DEFAULT_TRANSITION_DURATION;
|
|
243
|
+
this.initialized = false;
|
|
244
|
+
}
|
|
245
|
+
init(options = {}) {
|
|
246
|
+
if (this.initialized)
|
|
174
247
|
return;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
console.warn('Snackbar: Message must be a non-empty string');
|
|
248
|
+
if (typeof document === 'undefined') {
|
|
249
|
+
console.error('Snackbar: DOM is not available');
|
|
178
250
|
return;
|
|
179
251
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
252
|
+
const transitionDuration = normalizeTransitionDuration(options.transitionDuration);
|
|
253
|
+
if (transitionDuration !== null)
|
|
254
|
+
this.transitionDuration = transitionDuration;
|
|
255
|
+
this.renderer = new SnackbarRenderer(document);
|
|
256
|
+
if (!this.renderer.ensureRoot(this.transitionDuration)) {
|
|
257
|
+
console.error('Snackbar: DOM is not available');
|
|
258
|
+
this.renderer = null;
|
|
185
259
|
return;
|
|
186
260
|
}
|
|
187
|
-
|
|
188
|
-
|
|
261
|
+
this.initialized = true;
|
|
262
|
+
}
|
|
263
|
+
destroy() {
|
|
264
|
+
this.clearTimers();
|
|
265
|
+
this.clearQueue();
|
|
266
|
+
this.renderer?.destroy();
|
|
267
|
+
this.renderer = null;
|
|
268
|
+
this.state = 'idle';
|
|
269
|
+
this.transitionDuration = DEFAULT_TRANSITION_DURATION;
|
|
270
|
+
this.initialized = false;
|
|
271
|
+
}
|
|
272
|
+
add(message, action = null, duration) {
|
|
273
|
+
if (!this.ensureInitialized())
|
|
189
274
|
return;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
275
|
+
const item = normalizeItem(message, action, duration);
|
|
276
|
+
if (!item)
|
|
277
|
+
return;
|
|
278
|
+
this.queue.push(item);
|
|
279
|
+
if (this.state === 'idle')
|
|
193
280
|
this.showNext();
|
|
194
281
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (!snackbar)
|
|
282
|
+
show(message, action = null, duration) {
|
|
283
|
+
if (!this.ensureInitialized())
|
|
198
284
|
return;
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
actionButton.removeEventListener('click', this._currentActionHandler);
|
|
202
|
-
this._currentActionHandler = null;
|
|
203
|
-
actionButton.remove();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
static _showSnackbar(message, action, duration, onHide = null) {
|
|
207
|
-
const snackbar = document.getElementById('mini-snackbar');
|
|
208
|
-
if (!snackbar) {
|
|
209
|
-
console.error('Snackbar: Snackbar element not found. Ensure init() has been called.');
|
|
285
|
+
const item = normalizeItem(message, action, duration);
|
|
286
|
+
if (!item)
|
|
210
287
|
return;
|
|
211
|
-
|
|
212
|
-
this.
|
|
213
|
-
|
|
214
|
-
const snackbarText = snackbar.querySelector('.mini-snackbar-text');
|
|
215
|
-
if (snackbarText) {
|
|
216
|
-
snackbarText.textContent = message;
|
|
217
|
-
}
|
|
218
|
-
if (action) {
|
|
219
|
-
const actionButton = document.createElement('md-text-button');
|
|
220
|
-
actionButton.classList.add('mini-snackbar-action');
|
|
221
|
-
// Fallback for when Material Components are not available
|
|
222
|
-
if (customElements.get('md-text-button') === undefined) {
|
|
223
|
-
actionButton.setAttribute('data-fallback', '');
|
|
224
|
-
}
|
|
225
|
-
actionButton.textContent = action.text;
|
|
226
|
-
this._currentActionHandler = () => {
|
|
227
|
-
action.handler();
|
|
228
|
-
this._hideSnackbar(onHide);
|
|
229
|
-
};
|
|
230
|
-
actionButton.addEventListener('click', this._currentActionHandler);
|
|
231
|
-
snackbar.appendChild(actionButton);
|
|
232
|
-
}
|
|
233
|
-
snackbar.classList.add('show');
|
|
234
|
-
this._currentTimeout = setTimeout(() => {
|
|
235
|
-
this._hideSnackbar(onHide);
|
|
236
|
-
}, duration);
|
|
237
|
-
}
|
|
238
|
-
static _hideSnackbar(onHide = null) {
|
|
239
|
-
if (this._currentTimeout) {
|
|
240
|
-
clearTimeout(this._currentTimeout);
|
|
241
|
-
this._currentTimeout = null;
|
|
242
|
-
}
|
|
243
|
-
this._state = 'transitioning';
|
|
244
|
-
const snackbar = document.getElementById('mini-snackbar');
|
|
245
|
-
if (snackbar) {
|
|
246
|
-
snackbar.classList.remove('show');
|
|
247
|
-
}
|
|
248
|
-
this._cleanupAction();
|
|
249
|
-
// Wait for CSS transition to complete
|
|
250
|
-
const transitionDuration = this.getTransitionDuration();
|
|
251
|
-
setTimeout(() => {
|
|
252
|
-
this._isShowing = false;
|
|
253
|
-
this._state = 'idle';
|
|
254
|
-
if (onHide)
|
|
255
|
-
onHide();
|
|
256
|
-
}, transitionDuration);
|
|
257
|
-
}
|
|
258
|
-
static show(message, action = null, duration = 3000) {
|
|
259
|
-
if (!this._initialized) {
|
|
260
|
-
console.warn('Snackbar: Not initialized. Call Snackbar.init() first.');
|
|
288
|
+
this.clearQueue();
|
|
289
|
+
if (this.state === 'idle') {
|
|
290
|
+
this.showItem(item);
|
|
261
291
|
return;
|
|
262
292
|
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
293
|
+
if (this.state === 'transitioning') {
|
|
294
|
+
this.clearTimer('transitionTimer');
|
|
295
|
+
this.state = 'idle';
|
|
296
|
+
this.showItem(item);
|
|
265
297
|
return;
|
|
266
298
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
this.hideActiveItem(() => this.showItem(item));
|
|
300
|
+
}
|
|
301
|
+
clearQueue() {
|
|
302
|
+
this.queue = [];
|
|
303
|
+
this.clearTimer('queueGapTimer');
|
|
304
|
+
}
|
|
305
|
+
hideCurrent() {
|
|
306
|
+
if (this.state === 'showing') {
|
|
307
|
+
this.hideActiveItem(() => this.finishCurrentItem());
|
|
273
308
|
}
|
|
274
|
-
|
|
275
|
-
|
|
309
|
+
}
|
|
310
|
+
isInitialized() {
|
|
311
|
+
return this.initialized;
|
|
312
|
+
}
|
|
313
|
+
getTransitionDuration() {
|
|
314
|
+
return this.renderer?.getTransitionDuration(this.transitionDuration) ?? this.transitionDuration;
|
|
315
|
+
}
|
|
316
|
+
showNext() {
|
|
317
|
+
if (this.state !== 'idle')
|
|
276
318
|
return;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (this._state === 'transitioning') {
|
|
280
|
-
this.add(message, action, duration);
|
|
319
|
+
const item = this.queue.shift();
|
|
320
|
+
if (!item)
|
|
281
321
|
return;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
322
|
+
this.showItem(item);
|
|
323
|
+
}
|
|
324
|
+
showItem(item) {
|
|
325
|
+
if (!this.renderer)
|
|
326
|
+
return;
|
|
327
|
+
this.clearTimers();
|
|
328
|
+
this.state = 'showing';
|
|
329
|
+
this.renderer.setMessage(item.message);
|
|
330
|
+
this.renderer.setAction(item.action, () => {
|
|
331
|
+
try {
|
|
332
|
+
item.action?.handler();
|
|
289
333
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
snackbar.classList.remove('show');
|
|
334
|
+
finally {
|
|
335
|
+
this.hideActiveItem(() => this.finishCurrentItem());
|
|
293
336
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
this._showSnackbar(message, action, duration);
|
|
300
|
-
}, transitionDuration);
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
this._showSnackbar(message, action, duration);
|
|
304
|
-
}
|
|
337
|
+
});
|
|
338
|
+
this.renderer.show();
|
|
339
|
+
this.displayTimer = setTimeout(() => {
|
|
340
|
+
this.hideActiveItem(() => this.finishCurrentItem());
|
|
341
|
+
}, item.duration);
|
|
305
342
|
}
|
|
306
|
-
|
|
307
|
-
if (this.
|
|
308
|
-
this._isShowing = false;
|
|
309
|
-
this._state = 'idle';
|
|
343
|
+
hideActiveItem(afterHidden) {
|
|
344
|
+
if (!this.renderer || this.state === 'transitioning')
|
|
310
345
|
return;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
346
|
+
this.clearTimer('displayTimer');
|
|
347
|
+
this.state = 'transitioning';
|
|
348
|
+
this.renderer.hide();
|
|
349
|
+
this.renderer.cleanupAction();
|
|
350
|
+
this.transitionTimer = setTimeout(() => {
|
|
351
|
+
this.transitionTimer = null;
|
|
352
|
+
this.state = 'idle';
|
|
353
|
+
afterHidden?.();
|
|
354
|
+
}, this.getTransitionDuration());
|
|
319
355
|
}
|
|
320
|
-
|
|
321
|
-
this.
|
|
356
|
+
finishCurrentItem() {
|
|
357
|
+
if (this.state !== 'idle')
|
|
358
|
+
return;
|
|
359
|
+
if (this.queue.length === 0)
|
|
360
|
+
return;
|
|
361
|
+
this.clearTimer('queueGapTimer');
|
|
362
|
+
this.queueGapTimer = setTimeout(() => {
|
|
363
|
+
this.queueGapTimer = null;
|
|
364
|
+
this.showNext();
|
|
365
|
+
}, QUEUE_GAP_DURATION);
|
|
322
366
|
}
|
|
323
|
-
|
|
324
|
-
if (this.
|
|
325
|
-
|
|
326
|
-
|
|
367
|
+
ensureInitialized() {
|
|
368
|
+
if (this.initialized)
|
|
369
|
+
return true;
|
|
370
|
+
console.warn('Snackbar: Not initialized. Call Snackbar.init() first.');
|
|
371
|
+
return false;
|
|
327
372
|
}
|
|
328
|
-
|
|
329
|
-
|
|
373
|
+
clearTimers() {
|
|
374
|
+
this.clearTimer('displayTimer');
|
|
375
|
+
this.clearTimer('transitionTimer');
|
|
376
|
+
this.clearTimer('queueGapTimer');
|
|
330
377
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
378
|
+
clearTimer(timer) {
|
|
379
|
+
const timeout = this[timer];
|
|
380
|
+
if (timeout)
|
|
381
|
+
clearTimeout(timeout);
|
|
382
|
+
this[timer] = null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* MiniSnackbar - A simple vanilla JavaScript snackbar/toast library
|
|
388
|
+
*
|
|
389
|
+
* @version 3.0.0
|
|
390
|
+
* @author Shanto Islam <shantoislamdev@gmail.com>
|
|
391
|
+
* @license MIT
|
|
392
|
+
* @description A lightweight, zero-dependency snackbar library with Material Design integration
|
|
393
|
+
* @repository https://github.com/shantoislamdev/minisnackbar
|
|
394
|
+
* @homepage https://github.com/shantoislamdev/minisnackbar#readme
|
|
395
|
+
*/
|
|
396
|
+
const controller = new SnackbarController();
|
|
397
|
+
class Snackbar {
|
|
398
|
+
static init(options = {}) {
|
|
399
|
+
controller.init(options);
|
|
334
400
|
}
|
|
335
|
-
static
|
|
336
|
-
|
|
401
|
+
static destroy() {
|
|
402
|
+
controller.destroy();
|
|
337
403
|
}
|
|
338
|
-
static
|
|
339
|
-
return
|
|
404
|
+
static getTransitionDuration() {
|
|
405
|
+
return controller.getTransitionDuration();
|
|
340
406
|
}
|
|
341
|
-
static
|
|
342
|
-
|
|
407
|
+
static add(message, action = null, duration) {
|
|
408
|
+
controller.add(message, action, duration);
|
|
343
409
|
}
|
|
344
|
-
static
|
|
345
|
-
|
|
410
|
+
static show(message, action = null, duration) {
|
|
411
|
+
controller.show(message, action, duration);
|
|
346
412
|
}
|
|
347
|
-
static
|
|
348
|
-
|
|
413
|
+
static clearQueue() {
|
|
414
|
+
controller.clearQueue();
|
|
349
415
|
}
|
|
350
|
-
static
|
|
351
|
-
|
|
416
|
+
static hideCurrent() {
|
|
417
|
+
controller.hideCurrent();
|
|
352
418
|
}
|
|
353
|
-
static
|
|
354
|
-
|
|
419
|
+
static isInitialized() {
|
|
420
|
+
return controller.isInitialized();
|
|
355
421
|
}
|
|
356
422
|
}
|
|
357
|
-
Snackbar._queue = [];
|
|
358
|
-
Snackbar._isShowing = false;
|
|
359
|
-
Snackbar._currentTimeout = null;
|
|
360
|
-
Snackbar._state = 'idle';
|
|
361
|
-
Snackbar._currentActionHandler = null;
|
|
362
|
-
Snackbar._transitionDuration = 250;
|
|
363
|
-
Snackbar._initialized = false;
|
|
364
|
-
// Make available globally in browser for UMD builds
|
|
365
|
-
if (typeof window !== 'undefined') {
|
|
366
|
-
window.Snackbar = Snackbar;
|
|
367
|
-
}
|
|
368
423
|
|
|
369
424
|
export { Snackbar, Snackbar as default };
|
|
370
425
|
//# sourceMappingURL=minisnackbar.esm.js.map
|