lit-toaster 0.1.2 → 0.2.1

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/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img alt="react-hot-toast - Try it out" src="https://res.cloudinary.com/ddlhtsgmp/image/upload/w_300,h_300,c_fill,r_10/v1755055178/lit-toaster-logo-full.png"/>
2
+ <img alt="lit toaster logo" src="https://res.cloudinary.com/ddlhtsgmp/image/upload/w_300,h_300,c_fill,r_10/v1755055178/lit-toaster-logo-full.png"/>
3
3
  </div>
4
4
 
5
5
  <br />
@@ -82,9 +82,9 @@ declare global {
82
82
 
83
83
  ## Toaster element properties
84
84
 
85
- | Name | Attribute |
86
- | ------------ | --------- |
87
- | `queueLimit` | false |
85
+ | Name | Attribute |
86
+ | ------------- | --------- |
87
+ | `toastsLimit` | false |
88
88
 
89
89
  ## Documentation
90
90
 
package/dist/index.d.ts CHANGED
@@ -3,42 +3,44 @@ import { LitElement, TemplateResult } from 'lit';
3
3
 
4
4
  type ToastKind = 'success' | 'error' | 'warning' | 'info';
5
5
  type ToastPosition = 'top-left' | 'top-right' | 'top-center' | 'bottom-left' | 'bottom-right' | 'bottom-center';
6
+ type ToastState = 'enter' | 'leave';
6
7
  type Toast = {
7
8
  id: string;
8
9
  message: string;
9
10
  duration: number;
10
11
  type: ToastKind;
11
12
  position: ToastPosition;
13
+ state: ToastState;
12
14
  };
13
15
  declare enum ToastEmitterEvent {
14
- QUEUE_LIMIT_CHANGE = "queue-limit-change",
16
+ TOASTS_LIMIT_CHANGE = "toasts-limit-change",
15
17
  TOASTS_CHANGE = "toasts-change"
16
18
  }
17
19
 
18
20
  declare class ToastEmitter extends EventTarget {
19
- private _queueLimit;
21
+ private _toastsLimit;
20
22
  private _toasts;
21
23
  get toasts(): Toast[];
22
- set queueLimit(value: string | number);
24
+ set toastsLimit(value: string | number);
23
25
  show(message: string, duration?: number, type?: ToastKind, position?: ToastPosition): void;
24
- remove(t: Toast): void;
25
- private emitQueueLimitChange;
26
+ remove(toast: Toast): void;
27
+ private emitToastsLimitChange;
26
28
  private emitToastsChange;
27
29
  }
28
30
  declare const toast: ToastEmitter;
29
31
 
30
32
  declare class ToasterElement extends LitElement {
31
- set queueLimit(value: number | undefined);
32
- get queueLimit(): number | undefined;
33
+ set toastsLimit(value: number | undefined);
34
+ get toastsLimit(): number | undefined;
33
35
  private _toastsList;
34
- private _queueLimit?;
36
+ private _toastsLimit?;
35
37
  connectedCallback(): void;
36
38
  disconnectedCallback(): void;
37
39
  private get groupedToasts();
38
40
  render(): TemplateResult;
39
41
  private getToastIcon;
40
42
  private dismiss;
41
- private onQueueLimitChange;
43
+ private onToastsLimitChange;
42
44
  private onToastsChange;
43
45
  static styles: lit.CSSResult;
44
46
  }
@@ -49,4 +51,4 @@ declare global {
49
51
  }
50
52
 
51
53
  export { ToastEmitterEvent, ToasterElement, toast };
52
- export type { Toast, ToastKind, ToastPosition };
54
+ export type { Toast, ToastKind, ToastPosition, ToastState };
@@ -1,4 +1,4 @@
1
- /*! lit-toaster v0.1.2 Copyright (c) 2025 Bryson Ward and contributors MIT License*/
1
+ /*! lit-toaster v0.2.1 Copyright (c) 2025 Bryson Ward and contributors MIT License*/
2
2
  import { css, LitElement, html } from 'lit';
3
3
  import { property, state, customElement } from 'lit/decorators.js';
4
4
 
@@ -15,7 +15,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
15
15
 
16
16
  var ToastEmitterEvent;
17
17
  (function (ToastEmitterEvent) {
18
- ToastEmitterEvent["QUEUE_LIMIT_CHANGE"] = "queue-limit-change";
18
+ ToastEmitterEvent["TOASTS_LIMIT_CHANGE"] = "toasts-limit-change";
19
19
  ToastEmitterEvent["TOASTS_CHANGE"] = "toasts-change";
20
20
  })(ToastEmitterEvent || (ToastEmitterEvent = {}));
21
21
 
@@ -26,6 +26,7 @@ const TOAST_TYPES = [
26
26
  'warning',
27
27
  'info',
28
28
  ];
29
+ const TOAST_ANIMATION_DURATION = 200;
29
30
 
30
31
  const GUID = (() => {
31
32
  let count = 0;
@@ -37,28 +38,28 @@ const GUID = (() => {
37
38
  class ToastEmitter extends EventTarget {
38
39
  constructor() {
39
40
  super(...arguments);
40
- this._queueLimit = DEFAULT_TOASTS_LIMIT;
41
+ this._toastsLimit = DEFAULT_TOASTS_LIMIT;
41
42
  this._toasts = [];
42
43
  }
43
44
  get toasts() {
44
45
  return this._toasts;
45
46
  }
46
- set queueLimit(value) {
47
- let updatedQueueLimit = this._queueLimit;
47
+ set toastsLimit(value) {
48
+ let updatedToastsLimit = this._toastsLimit;
48
49
  if (typeof value === 'number') {
49
- updatedQueueLimit = value;
50
+ updatedToastsLimit = value;
50
51
  }
51
52
  if (typeof value === 'string') {
52
53
  const valueToNum = Number(value);
53
54
  if (!isNaN(valueToNum)) {
54
- updatedQueueLimit = valueToNum;
55
+ updatedToastsLimit = valueToNum;
55
56
  }
56
57
  }
57
- this._queueLimit = Math.max(0, updatedQueueLimit);
58
- this.emitQueueLimitChange();
58
+ this._toastsLimit = Math.max(0, updatedToastsLimit);
59
+ this.emitToastsLimitChange();
59
60
  }
60
61
  show(message, duration = 7000, type = 'success', position = 'top-center') {
61
- if (this._queueLimit > 0 && this._toasts.length + 1 >= this._queueLimit) {
62
+ if (this._toastsLimit > 0 && this._toasts.length + 1 >= this._toastsLimit) {
62
63
  const existingWarningToast = this._toasts.find((t) => t.type === 'warning' &&
63
64
  t.message.toLowerCase().includes('too many notifications'));
64
65
  if (!existingWarningToast) {
@@ -68,8 +69,9 @@ class ToastEmitter extends EventTarget {
68
69
  duration,
69
70
  type: 'warning',
70
71
  position: 'bottom-center',
72
+ state: 'enter',
71
73
  };
72
- this._toasts = [...this._toasts, warningToast];
74
+ this._toasts = [warningToast, ...this._toasts];
73
75
  this.emitToastsChange();
74
76
  setTimeout(() => this.remove(warningToast), duration);
75
77
  }
@@ -83,20 +85,28 @@ class ToastEmitter extends EventTarget {
83
85
  duration,
84
86
  type,
85
87
  position,
88
+ state: 'enter',
86
89
  };
87
- this._toasts = [...this._toasts, newToast];
90
+ this._toasts = [newToast, ...this._toasts];
88
91
  this.emitToastsChange();
89
92
  if (duration > 0) {
90
93
  setTimeout(() => this.remove(newToast), duration);
91
94
  }
92
95
  }
93
- remove(t) {
94
- this._toasts = this._toasts.filter((item) => item !== t);
96
+ remove(toast) {
97
+ const index = this._toasts.indexOf(toast);
98
+ if (index === -1)
99
+ return;
100
+ this._toasts[index].state = 'leave';
95
101
  this.emitToastsChange();
96
- }
97
- emitQueueLimitChange() {
98
- this.dispatchEvent(new CustomEvent(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, {
99
- detail: this._queueLimit,
102
+ setTimeout(() => {
103
+ this._toasts = this._toasts.filter((item) => item !== toast);
104
+ this.emitToastsChange();
105
+ }, TOAST_ANIMATION_DURATION);
106
+ }
107
+ emitToastsLimitChange() {
108
+ this.dispatchEvent(new CustomEvent(ToastEmitterEvent.TOASTS_LIMIT_CHANGE, {
109
+ detail: this._toastsLimit,
100
110
  }));
101
111
  }
102
112
  emitToastsChange() {
@@ -111,10 +121,10 @@ let ToasterElement = class ToasterElement extends LitElement {
111
121
  constructor() {
112
122
  super(...arguments);
113
123
  this._toastsList = [];
114
- this.onQueueLimitChange = (event) => {
124
+ this.onToastsLimitChange = (event) => {
115
125
  if (event instanceof CustomEvent) {
116
- if (event.detail !== undefined && this._queueLimit !== event.detail) {
117
- this._queueLimit = event.detail;
126
+ if (event.detail !== undefined && this._toastsLimit !== event.detail) {
127
+ this._toastsLimit = event.detail;
118
128
  this.requestUpdate();
119
129
  }
120
130
  }
@@ -126,23 +136,23 @@ let ToasterElement = class ToasterElement extends LitElement {
126
136
  }
127
137
  };
128
138
  }
129
- set queueLimit(value) {
130
- this._queueLimit = value;
139
+ set toastsLimit(value) {
140
+ this._toastsLimit = value;
131
141
  if (value !== undefined) {
132
- toast.queueLimit = value;
142
+ toast.toastsLimit = value;
133
143
  }
134
144
  }
135
- get queueLimit() {
136
- return this._queueLimit;
145
+ get toastsLimit() {
146
+ return this._toastsLimit;
137
147
  }
138
148
  connectedCallback() {
139
149
  super.connectedCallback();
140
- toast.addEventListener(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, this.onQueueLimitChange);
150
+ toast.addEventListener(ToastEmitterEvent.TOASTS_LIMIT_CHANGE, this.onToastsLimitChange);
141
151
  toast.addEventListener(ToastEmitterEvent.TOASTS_CHANGE, this.onToastsChange);
142
152
  }
143
153
  disconnectedCallback() {
144
154
  super.disconnectedCallback();
145
- toast.removeEventListener(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, this.onQueueLimitChange);
155
+ toast.removeEventListener(ToastEmitterEvent.TOASTS_LIMIT_CHANGE, this.onToastsLimitChange);
146
156
  toast.removeEventListener(ToastEmitterEvent.TOASTS_CHANGE, this.onToastsChange);
147
157
  }
148
158
  get groupedToasts() {
@@ -167,7 +177,7 @@ let ToasterElement = class ToasterElement extends LitElement {
167
177
  ${toasts.map((toast) => html `
168
178
  <div
169
179
  id="toast-${toast.type}-${toast.id}"
170
- class="toast"
180
+ class="toast ${toast.state}"
171
181
  role="alert"
172
182
  >
173
183
  <div class="toast-${toast.type} toast-icon">
@@ -232,7 +242,7 @@ ToasterElement.styles = css `
232
242
  0 1px 3px #0000001a;
233
243
  }
234
244
 
235
- /** Screen ready only */
245
+ /** Screen reader only */
236
246
  .sr-only {
237
247
  position: absolute;
238
248
  width: 1px;
@@ -345,6 +355,39 @@ ToasterElement.styles = css `
345
355
  flex-direction: column-reverse;
346
356
  }
347
357
 
358
+ .toast.enter {
359
+ animation: onToastEnter 250ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
360
+ }
361
+
362
+ .toast.leave {
363
+ animation: onToastLeave 200ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
364
+ }
365
+
366
+ @keyframes onToastEnter {
367
+ from {
368
+ opacity: 0;
369
+ transform: translateY(12px);
370
+ }
371
+ to {
372
+ opacity: 1;
373
+ transform: translateY(0);
374
+ }
375
+ }
376
+
377
+ @keyframes onToastLeave {
378
+ 0% {
379
+ opacity: 1;
380
+ transform: translateY(0) scaleY(1);
381
+ }
382
+ 50% {
383
+ transform: translateY(4px) scaleY(0.95);
384
+ }
385
+ 100% {
386
+ opacity: 0;
387
+ transform: translateY(24px) scaleY(0.8);
388
+ }
389
+ }
390
+
348
391
  /* Apply dark mode styles if user’s system or browser theme is set to 'dark' */
349
392
  @media (prefers-color-scheme: dark) {
350
393
  .toast {
@@ -355,13 +398,13 @@ ToasterElement.styles = css `
355
398
  `;
356
399
  __decorate([
357
400
  property({ type: Number, attribute: false })
358
- ], ToasterElement.prototype, "queueLimit", null);
401
+ ], ToasterElement.prototype, "toastsLimit", null);
359
402
  __decorate([
360
403
  state()
361
404
  ], ToasterElement.prototype, "_toastsList", void 0);
362
405
  __decorate([
363
406
  state()
364
- ], ToasterElement.prototype, "_queueLimit", void 0);
407
+ ], ToasterElement.prototype, "_toastsLimit", void 0);
365
408
  ToasterElement = __decorate([
366
409
  customElement('app-toaster')
367
410
  ], ToasterElement);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lit-toaster",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "engines": {
5
5
  "node": ">=20",
6
6
  "npm": ">=9.6.3",
@@ -61,13 +61,13 @@
61
61
  "@rollup/plugin-node-resolve": "^16.0.1",
62
62
  "@rollup/plugin-typescript": "^12.1.4",
63
63
  "@size-limit/file": "^11.2.0",
64
- "@testing-library/jest-dom": "^6.7.0",
64
+ "@testing-library/jest-dom": "^6.8.0",
65
65
  "@testing-library/user-event": "^14.6.1",
66
66
  "@types/eslint-plugin-security": "^3.0.0",
67
67
  "@types/jest": "^30.0.0",
68
68
  "@vitest/coverage-v8": "^3.2.4",
69
69
  "auto-changelog": "^2.5.0",
70
- "eslint": "^9.33.0",
70
+ "eslint": "^9.35.0",
71
71
  "eslint-config-prettier": "^10.1.8",
72
72
  "eslint-plugin-lit": "^2.1.1",
73
73
  "eslint-plugin-prettier": "^5.5.4",
@@ -75,15 +75,15 @@
75
75
  "jsdom": "^26.1.0",
76
76
  "prettier": "^3.6.2",
77
77
  "release-it": "^19.0.4",
78
- "rollup": "^4.46.2",
78
+ "rollup": "^4.50.1",
79
79
  "rollup-plugin-cleanup": "^3.2.1",
80
80
  "rollup-plugin-delete": "^3.0.1",
81
- "rollup-plugin-dts": "^6.2.1",
81
+ "rollup-plugin-dts": "^6.2.3",
82
82
  "size-limit": "^11.2.0",
83
- "typedoc": "^0.28.10",
83
+ "typedoc": "^0.28.12",
84
84
  "typedoc-plugin-markdown": "^4.8.1",
85
85
  "typescript": "^5.9.2",
86
- "typescript-eslint": "^8.39.1",
86
+ "typescript-eslint": "^8.43.0",
87
87
  "vitest": "^3.2.4"
88
88
  },
89
89
  "release-it": {