lit-toaster 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bryson Ward
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
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"/>
3
+ </div>
4
+
5
+ <br />
6
+
7
+ <div align="center">
8
+ <img src="https://img.shields.io/github/v/tag/brysonbw/lit-toaster?style=flat&color=blue&label=npm" alt="github-tag" />
9
+ <img src="https://img.shields.io/npm/dm/lit-toaster?style=flat&label=npm%20downloads" alt="npm-downloads"/>
10
+ <img src="https://img.shields.io/github/actions/workflow/status/brysonbw/lit-toaster/test.yml?branch=main&style=flat&logo=github&label=CI" alt="ci-test-build-status" />
11
+ </a>
12
+ </div>
13
+ <br />
14
+ <div align="center"><strong>Notifications for Lit Web Components.</strong></div>
15
+ <div align="center">Simple, lightweight, and easy to integrate.</div>
16
+ <br />
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install lit-toaster
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### 1. After installation, add toaster element to template
27
+
28
+ ```html
29
+ <body>
30
+ <my-element></my-element>
31
+ <app-toaster></app-toaster>
32
+ </body>
33
+ ```
34
+
35
+ > Recommend placing `<app-toaster></app-toaster>` below all other elements.
36
+
37
+ ### 2. Import `toast` (emitter) within your Lit Web Component(s) and call `toast.show()`
38
+
39
+ ```typescript
40
+ // my-element.ts
41
+ import { LitElement, css, html, type TemplateResult } from 'lit';
42
+ import { customElement, property } from 'lit/decorators.js';
43
+ import { toast } from 'lit-toaster';
44
+
45
+ @customElement('my-element')
46
+ export class MyElement extends LitElement {
47
+ @property()
48
+ name?: string = 'my-element';
49
+
50
+ render(): TemplateResult {
51
+ return html`<div>
52
+ <p>${this.name} lit toast.</p>
53
+ <button
54
+ type="button"
55
+ @click=${(): void =>
56
+ toast.show("Here's your toast - peace and much love.")}
57
+ ></button>
58
+ </div>`;
59
+ }
60
+
61
+ static styles = css`
62
+ :host {
63
+ color: #1e90ff;
64
+ }
65
+ `;
66
+ }
67
+
68
+ declare global {
69
+ interface HTMLElementTagNameMap {
70
+ 'my-element': MyElement;
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Toaster element properties
76
+
77
+ | Name | Attribute |
78
+ | ------------ | --------- |
79
+ | `queueLimit` | false |
80
+
81
+ ## Documentation
82
+
83
+ For more detailed documentation, please view the [API Reference](docs/api-reference/README.md).
84
+
85
+ ## Contributing
86
+
87
+ If you have suggestions for how this project could be improved, or want to report a bug, feel free to open an issue! We welcome all contributions.
88
+
89
+ Likewise, before contributing please read the [contribution guide](CONTRIBUTING.md).
90
+
91
+ ## Credits
92
+
93
+ Lit Toaster is heavily inspired by [react-hot-toast](https://github.com/timolins/react-hot-toast).
94
+
95
+ ## Motivation
96
+
97
+ I implemented (toast) notifications in a few small side projects using different frontend frameworks. Instead of copying and modifying the same code for [Lit](https://lit.dev/), I decided to publish it as a public package.
98
+
99
+ ## Resources
100
+
101
+ - [Changelog](CHANGELOG.md)
102
+ - [Code of Conduct](CODE_OF_CONDUCT.md)
103
+ - [Contributing](CONTRIBUTING.md)
104
+ - [Security](SECURITY.md)
105
+ - [API Reference](docs/api-reference/README.md)
106
+
107
+ ## License
108
+
109
+ [MIT](LICENSE)
@@ -0,0 +1,54 @@
1
+ import * as lit from 'lit';
2
+ import { LitElement, TemplateResult } from 'lit';
3
+
4
+ type ToastKind = 'success' | 'error' | 'warning' | 'info';
5
+ type ToastPosition = 'top-left' | 'top-right' | 'top-center' | 'bottom-left' | 'bottom-right' | 'bottom-center';
6
+ type Toast = {
7
+ id: string;
8
+ message: string;
9
+ duration: number;
10
+ type: ToastKind;
11
+ position: ToastPosition;
12
+ };
13
+ declare enum ToastEmitterEvent {
14
+ QUEUE_LIMIT_CHANGE = "queue-limit-change",
15
+ TOASTS_CHANGE = "toasts-change"
16
+ }
17
+
18
+ declare class ToastEmitter extends EventTarget {
19
+ private _queueLimit;
20
+ private _toasts;
21
+ get toasts(): Toast[];
22
+ set queueLimit(value: string | number);
23
+ show(message: string, duration?: number, type?: ToastKind, position?: ToastPosition): void;
24
+ remove(t: Toast): void;
25
+ private emitQueueLimitChange;
26
+ private emitToastsChange;
27
+ }
28
+ declare const toast: ToastEmitter;
29
+
30
+ declare class ToasterElement extends LitElement {
31
+ set queueLimit(value: number | undefined);
32
+ get queueLimit(): number | undefined;
33
+ private _toastsList;
34
+ private _queueLimit?;
35
+ connectedCallback(): void;
36
+ disconnectedCallback(): void;
37
+ private get groupedToasts();
38
+ render(): TemplateResult;
39
+ private getToastIcon;
40
+ private dismiss;
41
+ private onQueueLimitChange;
42
+ private onToastsChange;
43
+ static styles: lit.CSSResult;
44
+ }
45
+ declare global {
46
+ interface HTMLElementTagNameMap {
47
+ 'app-toaster': ToasterElement;
48
+ }
49
+ }
50
+
51
+ declare const GUID: () => string;
52
+
53
+ export { GUID, ToastEmitter, ToastEmitterEvent, ToasterElement, toast };
54
+ export type { Toast, ToastKind, ToastPosition };
@@ -0,0 +1,1070 @@
1
+ /*! lit-toaster v0.1.0 Copyright (c) 2025 Bryson Ward and contributors MIT License*/
2
+ import { css, LitElement, html } from 'lit';
3
+
4
+ function __decorate(decorators, target, key, desc) {
5
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
7
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
8
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
9
+ }
10
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
11
+ var e = new Error(message);
12
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
13
+ };
14
+
15
+ const customElement = (tagName) => (classOrTarget, context) => {
16
+ if (context !== undefined) {
17
+ context.addInitializer(() => {
18
+ customElements.define(tagName, classOrTarget);
19
+ });
20
+ }
21
+ else {
22
+ customElements.define(tagName, classOrTarget);
23
+ }
24
+ };
25
+
26
+ const NODE_MODE = false;
27
+ const global$1 = globalThis;
28
+ const supportsAdoptingStyleSheets = global$1.ShadowRoot &&
29
+ (global$1.ShadyCSS === undefined || global$1.ShadyCSS.nativeShadow) &&
30
+ 'adoptedStyleSheets' in Document.prototype &&
31
+ 'replace' in CSSStyleSheet.prototype;
32
+ const constructionToken = Symbol();
33
+ const cssTagCache = new WeakMap();
34
+ class CSSResult {
35
+ constructor(cssText, strings, safeToken) {
36
+ this['_$cssResult$'] = true;
37
+ if (safeToken !== constructionToken) {
38
+ throw new Error('CSSResult is not constructable. Use `unsafeCSS` or `css` instead.');
39
+ }
40
+ this.cssText = cssText;
41
+ this._strings = strings;
42
+ }
43
+ get styleSheet() {
44
+ let styleSheet = this._styleSheet;
45
+ const strings = this._strings;
46
+ if (supportsAdoptingStyleSheets && styleSheet === undefined) {
47
+ const cacheable = strings !== undefined && strings.length === 1;
48
+ if (cacheable) {
49
+ styleSheet = cssTagCache.get(strings);
50
+ }
51
+ if (styleSheet === undefined) {
52
+ (this._styleSheet = styleSheet = new CSSStyleSheet()).replaceSync(this.cssText);
53
+ if (cacheable) {
54
+ cssTagCache.set(strings, styleSheet);
55
+ }
56
+ }
57
+ }
58
+ return styleSheet;
59
+ }
60
+ toString() {
61
+ return this.cssText;
62
+ }
63
+ }
64
+ const unsafeCSS = (value) => new CSSResult(typeof value === 'string' ? value : String(value), undefined, constructionToken);
65
+ const adoptStyles = (renderRoot, styles) => {
66
+ if (supportsAdoptingStyleSheets) {
67
+ renderRoot.adoptedStyleSheets = styles.map((s) => s instanceof CSSStyleSheet ? s : s.styleSheet);
68
+ }
69
+ else {
70
+ for (const s of styles) {
71
+ const style = document.createElement('style');
72
+ const nonce = global$1['litNonce'];
73
+ if (nonce !== undefined) {
74
+ style.setAttribute('nonce', nonce);
75
+ }
76
+ style.textContent = s.cssText;
77
+ renderRoot.appendChild(style);
78
+ }
79
+ }
80
+ };
81
+ const cssResultFromStyleSheet = (sheet) => {
82
+ let cssText = '';
83
+ for (const rule of sheet.cssRules) {
84
+ cssText += rule.cssText;
85
+ }
86
+ return unsafeCSS(cssText);
87
+ };
88
+ const getCompatibleStyle = supportsAdoptingStyleSheets ||
89
+ (NODE_MODE)
90
+ ? (s) => s
91
+ : (s) => s instanceof CSSStyleSheet ? cssResultFromStyleSheet(s) : s;
92
+
93
+ const { is, defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols, getPrototypeOf, } = Object;
94
+ const global = globalThis;
95
+ let issueWarning$1;
96
+ const trustedTypes = global
97
+ .trustedTypes;
98
+ const emptyStringForBooleanAttribute = trustedTypes
99
+ ? trustedTypes.emptyScript
100
+ : '';
101
+ const polyfillSupport = global.reactiveElementPolyfillSupportDevMode
102
+ ;
103
+ {
104
+ global.litIssuedWarnings ??= new Set();
105
+ issueWarning$1 = (code, warning) => {
106
+ warning += ` See https://lit.dev/msg/${code} for more information.`;
107
+ if (!global.litIssuedWarnings.has(warning) &&
108
+ !global.litIssuedWarnings.has(code)) {
109
+ console.warn(warning);
110
+ global.litIssuedWarnings.add(warning);
111
+ }
112
+ };
113
+ queueMicrotask(() => {
114
+ issueWarning$1('dev-mode', `Lit is in dev mode. Not recommended for production!`);
115
+ if (global.ShadyDOM?.inUse && polyfillSupport === undefined) {
116
+ issueWarning$1('polyfill-support-missing', `Shadow DOM is being polyfilled via \`ShadyDOM\` but ` +
117
+ `the \`polyfill-support\` module has not been loaded.`);
118
+ }
119
+ });
120
+ }
121
+ const debugLogEvent = (event) => {
122
+ const shouldEmit = global
123
+ .emitLitDebugLogEvents;
124
+ if (!shouldEmit) {
125
+ return;
126
+ }
127
+ global.dispatchEvent(new CustomEvent('lit-debug', {
128
+ detail: event,
129
+ }));
130
+ }
131
+ ;
132
+ const JSCompiler_renameProperty = (prop, _obj) => prop;
133
+ const defaultConverter = {
134
+ toAttribute(value, type) {
135
+ switch (type) {
136
+ case Boolean:
137
+ value = value ? emptyStringForBooleanAttribute : null;
138
+ break;
139
+ case Object:
140
+ case Array:
141
+ value = value == null ? value : JSON.stringify(value);
142
+ break;
143
+ }
144
+ return value;
145
+ },
146
+ fromAttribute(value, type) {
147
+ let fromValue = value;
148
+ switch (type) {
149
+ case Boolean:
150
+ fromValue = value !== null;
151
+ break;
152
+ case Number:
153
+ fromValue = value === null ? null : Number(value);
154
+ break;
155
+ case Object:
156
+ case Array:
157
+ try {
158
+ fromValue = JSON.parse(value);
159
+ }
160
+ catch (e) {
161
+ fromValue = null;
162
+ }
163
+ break;
164
+ }
165
+ return fromValue;
166
+ },
167
+ };
168
+ const notEqual = (value, old) => !is(value, old);
169
+ const defaultPropertyDeclaration$1 = {
170
+ attribute: true,
171
+ type: String,
172
+ converter: defaultConverter,
173
+ reflect: false,
174
+ useDefault: false,
175
+ hasChanged: notEqual,
176
+ };
177
+ Symbol.metadata ??= Symbol('metadata');
178
+ global.litPropertyMetadata ??= new WeakMap();
179
+ class ReactiveElement
180
+ extends HTMLElement {
181
+ static addInitializer(initializer) {
182
+ this.__prepare();
183
+ (this._initializers ??= []).push(initializer);
184
+ }
185
+ static get observedAttributes() {
186
+ this.finalize();
187
+ return (this.__attributeToPropertyMap && [...this.__attributeToPropertyMap.keys()]);
188
+ }
189
+ static createProperty(name, options = defaultPropertyDeclaration$1) {
190
+ if (options.state) {
191
+ options.attribute = false;
192
+ }
193
+ this.__prepare();
194
+ if (this.prototype.hasOwnProperty(name)) {
195
+ options = Object.create(options);
196
+ options.wrapped = true;
197
+ }
198
+ this.elementProperties.set(name, options);
199
+ if (!options.noAccessor) {
200
+ const key = Symbol.for(`${String(name)} (@property() cache)`)
201
+ ;
202
+ const descriptor = this.getPropertyDescriptor(name, key, options);
203
+ if (descriptor !== undefined) {
204
+ defineProperty(this.prototype, name, descriptor);
205
+ }
206
+ }
207
+ }
208
+ static getPropertyDescriptor(name, key, options) {
209
+ const { get, set } = getOwnPropertyDescriptor(this.prototype, name) ?? {
210
+ get() {
211
+ return this[key];
212
+ },
213
+ set(v) {
214
+ this[key] = v;
215
+ },
216
+ };
217
+ if (get == null) {
218
+ if ('value' in (getOwnPropertyDescriptor(this.prototype, name) ?? {})) {
219
+ throw new Error(`Field ${JSON.stringify(String(name))} on ` +
220
+ `${this.name} was declared as a reactive property ` +
221
+ `but it's actually declared as a value on the prototype. ` +
222
+ `Usually this is due to using @property or @state on a method.`);
223
+ }
224
+ issueWarning$1('reactive-property-without-getter', `Field ${JSON.stringify(String(name))} on ` +
225
+ `${this.name} was declared as a reactive property ` +
226
+ `but it does not have a getter. This will be an error in a ` +
227
+ `future version of Lit.`);
228
+ }
229
+ return {
230
+ get,
231
+ set(value) {
232
+ const oldValue = get?.call(this);
233
+ set?.call(this, value);
234
+ this.requestUpdate(name, oldValue, options);
235
+ },
236
+ configurable: true,
237
+ enumerable: true,
238
+ };
239
+ }
240
+ static getPropertyOptions(name) {
241
+ return this.elementProperties.get(name) ?? defaultPropertyDeclaration$1;
242
+ }
243
+ static __prepare() {
244
+ if (this.hasOwnProperty(JSCompiler_renameProperty('elementProperties'))) {
245
+ return;
246
+ }
247
+ const superCtor = getPrototypeOf(this);
248
+ superCtor.finalize();
249
+ if (superCtor._initializers !== undefined) {
250
+ this._initializers = [...superCtor._initializers];
251
+ }
252
+ this.elementProperties = new Map(superCtor.elementProperties);
253
+ }
254
+ static finalize() {
255
+ if (this.hasOwnProperty(JSCompiler_renameProperty('finalized'))) {
256
+ return;
257
+ }
258
+ this.finalized = true;
259
+ this.__prepare();
260
+ if (this.hasOwnProperty(JSCompiler_renameProperty('properties'))) {
261
+ const props = this.properties;
262
+ const propKeys = [
263
+ ...getOwnPropertyNames(props),
264
+ ...getOwnPropertySymbols(props),
265
+ ];
266
+ for (const p of propKeys) {
267
+ this.createProperty(p, props[p]);
268
+ }
269
+ }
270
+ const metadata = this[Symbol.metadata];
271
+ if (metadata !== null) {
272
+ const properties = litPropertyMetadata.get(metadata);
273
+ if (properties !== undefined) {
274
+ for (const [p, options] of properties) {
275
+ this.elementProperties.set(p, options);
276
+ }
277
+ }
278
+ }
279
+ this.__attributeToPropertyMap = new Map();
280
+ for (const [p, options] of this.elementProperties) {
281
+ const attr = this.__attributeNameForProperty(p, options);
282
+ if (attr !== undefined) {
283
+ this.__attributeToPropertyMap.set(attr, p);
284
+ }
285
+ }
286
+ this.elementStyles = this.finalizeStyles(this.styles);
287
+ {
288
+ if (this.hasOwnProperty('createProperty')) {
289
+ issueWarning$1('no-override-create-property', 'Overriding ReactiveElement.createProperty() is deprecated. ' +
290
+ 'The override will not be called with standard decorators');
291
+ }
292
+ if (this.hasOwnProperty('getPropertyDescriptor')) {
293
+ issueWarning$1('no-override-get-property-descriptor', 'Overriding ReactiveElement.getPropertyDescriptor() is deprecated. ' +
294
+ 'The override will not be called with standard decorators');
295
+ }
296
+ }
297
+ }
298
+ static finalizeStyles(styles) {
299
+ const elementStyles = [];
300
+ if (Array.isArray(styles)) {
301
+ const set = new Set(styles.flat(Infinity).reverse());
302
+ for (const s of set) {
303
+ elementStyles.unshift(getCompatibleStyle(s));
304
+ }
305
+ }
306
+ else if (styles !== undefined) {
307
+ elementStyles.push(getCompatibleStyle(styles));
308
+ }
309
+ return elementStyles;
310
+ }
311
+ static __attributeNameForProperty(name, options) {
312
+ const attribute = options.attribute;
313
+ return attribute === false
314
+ ? undefined
315
+ : typeof attribute === 'string'
316
+ ? attribute
317
+ : typeof name === 'string'
318
+ ? name.toLowerCase()
319
+ : undefined;
320
+ }
321
+ constructor() {
322
+ super();
323
+ this.__instanceProperties = undefined;
324
+ this.isUpdatePending = false;
325
+ this.hasUpdated = false;
326
+ this.__reflectingProperty = null;
327
+ this.__initialize();
328
+ }
329
+ __initialize() {
330
+ this.__updatePromise = new Promise((res) => (this.enableUpdating = res));
331
+ this._$changedProperties = new Map();
332
+ this.__saveInstanceProperties();
333
+ this.requestUpdate();
334
+ this.constructor._initializers?.forEach((i) => i(this));
335
+ }
336
+ addController(controller) {
337
+ (this.__controllers ??= new Set()).add(controller);
338
+ if (this.renderRoot !== undefined && this.isConnected) {
339
+ controller.hostConnected?.();
340
+ }
341
+ }
342
+ removeController(controller) {
343
+ this.__controllers?.delete(controller);
344
+ }
345
+ __saveInstanceProperties() {
346
+ const instanceProperties = new Map();
347
+ const elementProperties = this.constructor
348
+ .elementProperties;
349
+ for (const p of elementProperties.keys()) {
350
+ if (this.hasOwnProperty(p)) {
351
+ instanceProperties.set(p, this[p]);
352
+ delete this[p];
353
+ }
354
+ }
355
+ if (instanceProperties.size > 0) {
356
+ this.__instanceProperties = instanceProperties;
357
+ }
358
+ }
359
+ createRenderRoot() {
360
+ const renderRoot = this.shadowRoot ??
361
+ this.attachShadow(this.constructor.shadowRootOptions);
362
+ adoptStyles(renderRoot, this.constructor.elementStyles);
363
+ return renderRoot;
364
+ }
365
+ connectedCallback() {
366
+ this.renderRoot ??=
367
+ this.createRenderRoot();
368
+ this.enableUpdating(true);
369
+ this.__controllers?.forEach((c) => c.hostConnected?.());
370
+ }
371
+ enableUpdating(_requestedUpdate) { }
372
+ disconnectedCallback() {
373
+ this.__controllers?.forEach((c) => c.hostDisconnected?.());
374
+ }
375
+ attributeChangedCallback(name, _old, value) {
376
+ this._$attributeToProperty(name, value);
377
+ }
378
+ __propertyToAttribute(name, value) {
379
+ const elemProperties = this.constructor.elementProperties;
380
+ const options = elemProperties.get(name);
381
+ const attr = this.constructor.__attributeNameForProperty(name, options);
382
+ if (attr !== undefined && options.reflect === true) {
383
+ const converter = options.converter?.toAttribute !==
384
+ undefined
385
+ ? options.converter
386
+ : defaultConverter;
387
+ const attrValue = converter.toAttribute(value, options.type);
388
+ if (this.constructor.enabledWarnings.includes('migration') &&
389
+ attrValue === undefined) {
390
+ issueWarning$1('undefined-attribute-value', `The attribute value for the ${name} property is ` +
391
+ `undefined on element ${this.localName}. The attribute will be ` +
392
+ `removed, but in the previous version of \`ReactiveElement\`, ` +
393
+ `the attribute would not have changed.`);
394
+ }
395
+ this.__reflectingProperty = name;
396
+ if (attrValue == null) {
397
+ this.removeAttribute(attr);
398
+ }
399
+ else {
400
+ this.setAttribute(attr, attrValue);
401
+ }
402
+ this.__reflectingProperty = null;
403
+ }
404
+ }
405
+ _$attributeToProperty(name, value) {
406
+ const ctor = this.constructor;
407
+ const propName = ctor.__attributeToPropertyMap.get(name);
408
+ if (propName !== undefined && this.__reflectingProperty !== propName) {
409
+ const options = ctor.getPropertyOptions(propName);
410
+ const converter = typeof options.converter === 'function'
411
+ ? { fromAttribute: options.converter }
412
+ : options.converter?.fromAttribute !== undefined
413
+ ? options.converter
414
+ : defaultConverter;
415
+ this.__reflectingProperty = propName;
416
+ const convertedValue = converter.fromAttribute(value, options.type);
417
+ this[propName] =
418
+ convertedValue ??
419
+ this.__defaultValues?.get(propName) ??
420
+ convertedValue;
421
+ this.__reflectingProperty = null;
422
+ }
423
+ }
424
+ requestUpdate(name, oldValue, options) {
425
+ if (name !== undefined) {
426
+ if (name instanceof Event) {
427
+ issueWarning$1(``, `The requestUpdate() method was called with an Event as the property name. This is probably a mistake caused by binding this.requestUpdate as an event listener. Instead bind a function that will call it with no arguments: () => this.requestUpdate()`);
428
+ }
429
+ const ctor = this.constructor;
430
+ const newValue = this[name];
431
+ options ??= ctor.getPropertyOptions(name);
432
+ const changed = (options.hasChanged ?? notEqual)(newValue, oldValue) ||
433
+ (options.useDefault &&
434
+ options.reflect &&
435
+ newValue === this.__defaultValues?.get(name) &&
436
+ !this.hasAttribute(ctor.__attributeNameForProperty(name, options)));
437
+ if (changed) {
438
+ this._$changeProperty(name, oldValue, options);
439
+ }
440
+ else {
441
+ return;
442
+ }
443
+ }
444
+ if (this.isUpdatePending === false) {
445
+ this.__updatePromise = this.__enqueueUpdate();
446
+ }
447
+ }
448
+ _$changeProperty(name, oldValue, { useDefault, reflect, wrapped }, initializeValue) {
449
+ if (useDefault && !(this.__defaultValues ??= new Map()).has(name)) {
450
+ this.__defaultValues.set(name, initializeValue ?? oldValue ?? this[name]);
451
+ if (wrapped !== true || initializeValue !== undefined) {
452
+ return;
453
+ }
454
+ }
455
+ if (!this._$changedProperties.has(name)) {
456
+ if (!this.hasUpdated && !useDefault) {
457
+ oldValue = undefined;
458
+ }
459
+ this._$changedProperties.set(name, oldValue);
460
+ }
461
+ if (reflect === true && this.__reflectingProperty !== name) {
462
+ (this.__reflectingProperties ??= new Set()).add(name);
463
+ }
464
+ }
465
+ async __enqueueUpdate() {
466
+ this.isUpdatePending = true;
467
+ try {
468
+ await this.__updatePromise;
469
+ }
470
+ catch (e) {
471
+ Promise.reject(e);
472
+ }
473
+ const result = this.scheduleUpdate();
474
+ if (result != null) {
475
+ await result;
476
+ }
477
+ return !this.isUpdatePending;
478
+ }
479
+ scheduleUpdate() {
480
+ const result = this.performUpdate();
481
+ if (this.constructor.enabledWarnings.includes('async-perform-update') &&
482
+ typeof result?.then ===
483
+ 'function') {
484
+ issueWarning$1('async-perform-update', `Element ${this.localName} returned a Promise from performUpdate(). ` +
485
+ `This behavior is deprecated and will be removed in a future ` +
486
+ `version of ReactiveElement.`);
487
+ }
488
+ return result;
489
+ }
490
+ performUpdate() {
491
+ if (!this.isUpdatePending) {
492
+ return;
493
+ }
494
+ debugLogEvent?.({ kind: 'update' });
495
+ if (!this.hasUpdated) {
496
+ this.renderRoot ??=
497
+ this.createRenderRoot();
498
+ {
499
+ const ctor = this.constructor;
500
+ const shadowedProperties = [...ctor.elementProperties.keys()].filter((p) => this.hasOwnProperty(p) && p in getPrototypeOf(this));
501
+ if (shadowedProperties.length) {
502
+ throw new Error(`The following properties on element ${this.localName} will not ` +
503
+ `trigger updates as expected because they are set using class ` +
504
+ `fields: ${shadowedProperties.join(', ')}. ` +
505
+ `Native class fields and some compiled output will overwrite ` +
506
+ `accessors used for detecting changes. See ` +
507
+ `https://lit.dev/msg/class-field-shadowing ` +
508
+ `for more information.`);
509
+ }
510
+ }
511
+ if (this.__instanceProperties) {
512
+ for (const [p, value] of this.__instanceProperties) {
513
+ this[p] = value;
514
+ }
515
+ this.__instanceProperties = undefined;
516
+ }
517
+ const elementProperties = this.constructor
518
+ .elementProperties;
519
+ if (elementProperties.size > 0) {
520
+ for (const [p, options] of elementProperties) {
521
+ const { wrapped } = options;
522
+ const value = this[p];
523
+ if (wrapped === true &&
524
+ !this._$changedProperties.has(p) &&
525
+ value !== undefined) {
526
+ this._$changeProperty(p, undefined, options, value);
527
+ }
528
+ }
529
+ }
530
+ }
531
+ let shouldUpdate = false;
532
+ const changedProperties = this._$changedProperties;
533
+ try {
534
+ shouldUpdate = this.shouldUpdate(changedProperties);
535
+ if (shouldUpdate) {
536
+ this.willUpdate(changedProperties);
537
+ this.__controllers?.forEach((c) => c.hostUpdate?.());
538
+ this.update(changedProperties);
539
+ }
540
+ else {
541
+ this.__markUpdated();
542
+ }
543
+ }
544
+ catch (e) {
545
+ shouldUpdate = false;
546
+ this.__markUpdated();
547
+ throw e;
548
+ }
549
+ if (shouldUpdate) {
550
+ this._$didUpdate(changedProperties);
551
+ }
552
+ }
553
+ willUpdate(_changedProperties) { }
554
+ _$didUpdate(changedProperties) {
555
+ this.__controllers?.forEach((c) => c.hostUpdated?.());
556
+ if (!this.hasUpdated) {
557
+ this.hasUpdated = true;
558
+ this.firstUpdated(changedProperties);
559
+ }
560
+ this.updated(changedProperties);
561
+ if (this.isUpdatePending &&
562
+ this.constructor.enabledWarnings.includes('change-in-update')) {
563
+ issueWarning$1('change-in-update', `Element ${this.localName} scheduled an update ` +
564
+ `(generally because a property was set) ` +
565
+ `after an update completed, causing a new update to be scheduled. ` +
566
+ `This is inefficient and should be avoided unless the next update ` +
567
+ `can only be scheduled as a side effect of the previous update.`);
568
+ }
569
+ }
570
+ __markUpdated() {
571
+ this._$changedProperties = new Map();
572
+ this.isUpdatePending = false;
573
+ }
574
+ get updateComplete() {
575
+ return this.getUpdateComplete();
576
+ }
577
+ getUpdateComplete() {
578
+ return this.__updatePromise;
579
+ }
580
+ shouldUpdate(_changedProperties) {
581
+ return true;
582
+ }
583
+ update(_changedProperties) {
584
+ this.__reflectingProperties &&= this.__reflectingProperties.forEach((p) => this.__propertyToAttribute(p, this[p]));
585
+ this.__markUpdated();
586
+ }
587
+ updated(_changedProperties) { }
588
+ firstUpdated(_changedProperties) { }
589
+ }
590
+ ReactiveElement.elementStyles = [];
591
+ ReactiveElement.shadowRootOptions = { mode: 'open' };
592
+ ReactiveElement[JSCompiler_renameProperty('elementProperties')] = new Map();
593
+ ReactiveElement[JSCompiler_renameProperty('finalized')] = new Map();
594
+ polyfillSupport?.({ ReactiveElement });
595
+ {
596
+ ReactiveElement.enabledWarnings = [
597
+ 'change-in-update',
598
+ 'async-perform-update',
599
+ ];
600
+ const ensureOwnWarnings = function (ctor) {
601
+ if (!ctor.hasOwnProperty(JSCompiler_renameProperty('enabledWarnings'))) {
602
+ ctor.enabledWarnings = ctor.enabledWarnings.slice();
603
+ }
604
+ };
605
+ ReactiveElement.enableWarning = function (warning) {
606
+ ensureOwnWarnings(this);
607
+ if (!this.enabledWarnings.includes(warning)) {
608
+ this.enabledWarnings.push(warning);
609
+ }
610
+ };
611
+ ReactiveElement.disableWarning = function (warning) {
612
+ ensureOwnWarnings(this);
613
+ const i = this.enabledWarnings.indexOf(warning);
614
+ if (i >= 0) {
615
+ this.enabledWarnings.splice(i, 1);
616
+ }
617
+ };
618
+ }
619
+ (global.reactiveElementVersions ??= []).push('2.1.1');
620
+ if (global.reactiveElementVersions.length > 1) {
621
+ queueMicrotask(() => {
622
+ issueWarning$1('multiple-versions', `Multiple versions of Lit loaded. Loading multiple versions ` +
623
+ `is not recommended.`);
624
+ });
625
+ }
626
+
627
+ let issueWarning;
628
+ {
629
+ globalThis.litIssuedWarnings ??= new Set();
630
+ issueWarning = (code, warning) => {
631
+ warning += ` See https://lit.dev/msg/${code} for more information.`;
632
+ if (!globalThis.litIssuedWarnings.has(warning) &&
633
+ !globalThis.litIssuedWarnings.has(code)) {
634
+ console.warn(warning);
635
+ globalThis.litIssuedWarnings.add(warning);
636
+ }
637
+ };
638
+ }
639
+ const legacyProperty = (options, proto, name) => {
640
+ const hasOwnProperty = proto.hasOwnProperty(name);
641
+ proto.constructor.createProperty(name, options);
642
+ return hasOwnProperty
643
+ ? Object.getOwnPropertyDescriptor(proto, name)
644
+ : undefined;
645
+ };
646
+ const defaultPropertyDeclaration = {
647
+ attribute: true,
648
+ type: String,
649
+ converter: defaultConverter,
650
+ reflect: false,
651
+ hasChanged: notEqual,
652
+ };
653
+ const standardProperty = (options = defaultPropertyDeclaration, target, context) => {
654
+ const { kind, metadata } = context;
655
+ if (metadata == null) {
656
+ issueWarning('missing-class-metadata', `The class ${target} is missing decorator metadata. This ` +
657
+ `could mean that you're using a compiler that supports decorators ` +
658
+ `but doesn't support decorator metadata, such as TypeScript 5.1. ` +
659
+ `Please update your compiler.`);
660
+ }
661
+ let properties = globalThis.litPropertyMetadata.get(metadata);
662
+ if (properties === undefined) {
663
+ globalThis.litPropertyMetadata.set(metadata, (properties = new Map()));
664
+ }
665
+ if (kind === 'setter') {
666
+ options = Object.create(options);
667
+ options.wrapped = true;
668
+ }
669
+ properties.set(context.name, options);
670
+ if (kind === 'accessor') {
671
+ const { name } = context;
672
+ return {
673
+ set(v) {
674
+ const oldValue = target.get.call(this);
675
+ target.set.call(this, v);
676
+ this.requestUpdate(name, oldValue, options);
677
+ },
678
+ init(v) {
679
+ if (v !== undefined) {
680
+ this._$changeProperty(name, undefined, options, v);
681
+ }
682
+ return v;
683
+ },
684
+ };
685
+ }
686
+ else if (kind === 'setter') {
687
+ const { name } = context;
688
+ return function (value) {
689
+ const oldValue = this[name];
690
+ target.call(this, value);
691
+ this.requestUpdate(name, oldValue, options);
692
+ };
693
+ }
694
+ throw new Error(`Unsupported decorator location: ${kind}`);
695
+ };
696
+ function property(options) {
697
+ return (protoOrTarget, nameOrContext
698
+ ) => {
699
+ return (typeof nameOrContext === 'object'
700
+ ? standardProperty(options, protoOrTarget, nameOrContext)
701
+ : legacyProperty(options, protoOrTarget, nameOrContext));
702
+ };
703
+ }
704
+
705
+ function state(options) {
706
+ return property({
707
+ ...options,
708
+ state: true,
709
+ attribute: false,
710
+ });
711
+ }
712
+
713
+ {
714
+ globalThis.litIssuedWarnings ??= new Set();
715
+ }
716
+
717
+ var ToastEmitterEvent;
718
+ (function (ToastEmitterEvent) {
719
+ ToastEmitterEvent["QUEUE_LIMIT_CHANGE"] = "queue-limit-change";
720
+ ToastEmitterEvent["TOASTS_CHANGE"] = "toasts-change";
721
+ })(ToastEmitterEvent || (ToastEmitterEvent = {}));
722
+
723
+ const DEFAULT_TOASTS_LIMIT = 7;
724
+ const TOAST_TYPES = [
725
+ 'success',
726
+ 'error',
727
+ 'warning',
728
+ 'info',
729
+ ];
730
+
731
+ const GUID = (() => {
732
+ let count = 0;
733
+ return () => {
734
+ return (++count).toString();
735
+ };
736
+ })();
737
+
738
+ class ToastEmitter extends EventTarget {
739
+ constructor() {
740
+ super(...arguments);
741
+ this._queueLimit = DEFAULT_TOASTS_LIMIT;
742
+ this._toasts = [];
743
+ }
744
+ get toasts() {
745
+ return this._toasts;
746
+ }
747
+ set queueLimit(value) {
748
+ let updatedQueueLimit = this._queueLimit;
749
+ if (typeof value === 'number') {
750
+ updatedQueueLimit = value;
751
+ }
752
+ if (typeof value === 'string') {
753
+ const valueToNum = Number(value);
754
+ if (!isNaN(valueToNum)) {
755
+ updatedQueueLimit = valueToNum;
756
+ }
757
+ }
758
+ this._queueLimit = Math.max(0, updatedQueueLimit);
759
+ this.emitQueueLimitChange();
760
+ }
761
+ show(message, duration = 7000, type = 'success', position = 'top-center') {
762
+ if (this._queueLimit > 0 && this._toasts.length + 1 >= this._queueLimit) {
763
+ const existingWarningToast = this._toasts.find((t) => t.type === 'warning' &&
764
+ t.message.toLowerCase().includes('too many notifications'));
765
+ if (!existingWarningToast) {
766
+ const warningToast = {
767
+ id: GUID(),
768
+ message: 'Too many notifications. Please wait a moment and/or close existing ones.',
769
+ duration,
770
+ type: 'warning',
771
+ position: 'bottom-center',
772
+ };
773
+ this._toasts = [...this._toasts, warningToast];
774
+ this.emitToastsChange();
775
+ setTimeout(() => this.remove(warningToast), duration);
776
+ }
777
+ return;
778
+ }
779
+ const newToast = {
780
+ id: GUID(),
781
+ message: typeof message === 'string'
782
+ ? message.trim()
783
+ : `Notification: ${type.charAt(0).toUpperCase() + type.slice(1)}`,
784
+ duration,
785
+ type,
786
+ position,
787
+ };
788
+ this._toasts = [...this._toasts, newToast];
789
+ this.emitToastsChange();
790
+ if (duration > 0) {
791
+ setTimeout(() => this.remove(newToast), duration);
792
+ }
793
+ }
794
+ remove(t) {
795
+ this._toasts = this._toasts.filter((item) => item !== t);
796
+ this.emitToastsChange();
797
+ }
798
+ emitQueueLimitChange() {
799
+ this.dispatchEvent(new CustomEvent(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, {
800
+ detail: this._queueLimit,
801
+ }));
802
+ }
803
+ emitToastsChange() {
804
+ this.dispatchEvent(new CustomEvent(ToastEmitterEvent.TOASTS_CHANGE, {
805
+ detail: this._toasts,
806
+ }));
807
+ }
808
+ }
809
+ const toast = new ToastEmitter();
810
+
811
+ let ToasterElement = class ToasterElement extends LitElement {
812
+ constructor() {
813
+ super(...arguments);
814
+ this._toastsList = [];
815
+ this.onQueueLimitChange = (event) => {
816
+ if (event instanceof CustomEvent) {
817
+ if (event.detail !== undefined && this._queueLimit !== event.detail) {
818
+ this._queueLimit = event.detail;
819
+ this.requestUpdate();
820
+ }
821
+ }
822
+ };
823
+ this.onToastsChange = (event) => {
824
+ if (event instanceof CustomEvent) {
825
+ this._toastsList = event.detail;
826
+ this.requestUpdate();
827
+ }
828
+ };
829
+ }
830
+ set queueLimit(value) {
831
+ this._queueLimit = value;
832
+ if (value !== undefined) {
833
+ toast.queueLimit = value;
834
+ }
835
+ }
836
+ get queueLimit() {
837
+ return this._queueLimit;
838
+ }
839
+ connectedCallback() {
840
+ super.connectedCallback();
841
+ toast.addEventListener(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, this.onQueueLimitChange);
842
+ toast.addEventListener(ToastEmitterEvent.TOASTS_CHANGE, this.onToastsChange);
843
+ }
844
+ disconnectedCallback() {
845
+ super.disconnectedCallback();
846
+ toast.removeEventListener(ToastEmitterEvent.QUEUE_LIMIT_CHANGE, this.onQueueLimitChange);
847
+ toast.removeEventListener(ToastEmitterEvent.TOASTS_CHANGE, this.onToastsChange);
848
+ }
849
+ get groupedToasts() {
850
+ const toastGroups = {};
851
+ for (let i = 0; i < this._toastsList.length; i++) {
852
+ const toast = this._toastsList[i];
853
+ const position = toast.position;
854
+ const bucket = toastGroups[position];
855
+ if (bucket) {
856
+ bucket.push(toast);
857
+ }
858
+ else {
859
+ toastGroups[position] = [toast];
860
+ }
861
+ }
862
+ return toastGroups;
863
+ }
864
+ render() {
865
+ return html `
866
+ ${Object.entries(this.groupedToasts).map(([position, toasts]) => html `
867
+ <div class="toast-container ${position}">
868
+ ${toasts.map((toast) => html `
869
+ <div
870
+ id="toast-${toast.type}-${toast.id}"
871
+ class="toast"
872
+ role="alert"
873
+ >
874
+ <div class="toast-${toast.type} toast-icon">
875
+ ${this.getToastIcon(toast.type)}
876
+ </div>
877
+ <div class="toast-message">${toast.message}</div>
878
+ <button
879
+ @click=${() => this.dismiss(toast)}
880
+ type="button"
881
+ class="toast-close"
882
+ aria-label="Close"
883
+ >
884
+ <span class="sr-only">Close</span>
885
+ X
886
+ </button>
887
+ </div>
888
+ `)}
889
+ </div>
890
+ `)}
891
+ `;
892
+ }
893
+ getToastIcon(type) {
894
+ if (!TOAST_TYPES.includes(type)) {
895
+ type = 'info';
896
+ }
897
+ const icons = {
898
+ success: '✓',
899
+ error: '✗',
900
+ warning: '!',
901
+ info: 'i',
902
+ };
903
+ return icons[type];
904
+ }
905
+ dismiss(t) {
906
+ toast.remove(t);
907
+ }
908
+ };
909
+ ToasterElement.styles = css `
910
+ .toast-container {
911
+ position: fixed;
912
+ z-index: 9999;
913
+ pointer-events: none;
914
+ display: flex;
915
+ flex-direction: column;
916
+ gap: 1rem;
917
+ }
918
+
919
+ .toast {
920
+ pointer-events: auto;
921
+ margin: 0;
922
+ border-radius: 5px;
923
+ display: flex;
924
+ align-items: center;
925
+ gap: 10px;
926
+ background-color: white;
927
+ width: 100%;
928
+ max-width: 20rem;
929
+ padding: 1rem;
930
+ color: #6b7280;
931
+ box-shadow:
932
+ 0 1px 2px #0000000d,
933
+ 0 1px 3px #0000001a;
934
+ }
935
+
936
+ /** Screen ready only */
937
+ .sr-only {
938
+ position: absolute;
939
+ width: 1px;
940
+ height: 1px;
941
+ padding: 0;
942
+ margin: -1px;
943
+ overflow: hidden;
944
+ clip: rect(0, 0, 0, 0);
945
+ white-space: nowrap;
946
+ border: 0;
947
+ }
948
+
949
+ .toast .toast-icon {
950
+ display: inline-flex;
951
+ align-items: center;
952
+ justify-content: center;
953
+ flex-shrink: 0;
954
+ }
955
+
956
+ .toast-message {
957
+ flex: 1;
958
+ margin-right: auto;
959
+ font-size: 0.875rem;
960
+ font-weight: 500;
961
+ word-break: break-word;
962
+ }
963
+
964
+ .toast-close {
965
+ cursor: pointer;
966
+ border: none;
967
+ background-color: transparent;
968
+ margin-inline-start: auto;
969
+ margin-left: -0.375rem;
970
+ margin-right: -0.375rem;
971
+ margin-top: -0.375rem;
972
+ margin-bottom: -0.375rem;
973
+ color: #f87171;
974
+ border-radius: 0.5rem;
975
+ padding: 0.375rem;
976
+ display: inline-flex;
977
+ align-items: center;
978
+ justify-content: center;
979
+ transition: color 0.2s ease-in-out;
980
+ font-weight: bold;
981
+ }
982
+
983
+ .toast-close:hover {
984
+ color: #ef4444;
985
+ }
986
+
987
+ .toast-close:focus {
988
+ outline: none;
989
+ box-shadow: 0 0 0 0.5px #6d6d6dff;
990
+ }
991
+
992
+ .toast-success {
993
+ color: #14b8a6;
994
+ }
995
+
996
+ .toast-info {
997
+ color: #3b82f6;
998
+ }
999
+
1000
+ .toast-warning {
1001
+ color: #eab308;
1002
+ }
1003
+
1004
+ .toast-error {
1005
+ color: #ef4444;
1006
+ }
1007
+
1008
+ .toast-container.top-left {
1009
+ top: 10px;
1010
+ left: 10px;
1011
+ align-items: flex-start;
1012
+ }
1013
+
1014
+ .toast-container.top-center {
1015
+ top: 10px;
1016
+ left: 50%;
1017
+ transform: translateX(-50%);
1018
+ align-items: center;
1019
+ }
1020
+
1021
+ .toast-container.top-right {
1022
+ top: 10px;
1023
+ right: 10px;
1024
+ align-items: flex-end;
1025
+ }
1026
+
1027
+ .toast-container.bottom-left {
1028
+ bottom: 10px;
1029
+ left: 10px;
1030
+ align-items: flex-start;
1031
+ flex-direction: column-reverse;
1032
+ }
1033
+
1034
+ .toast-container.bottom-center {
1035
+ bottom: 10px;
1036
+ left: 50%;
1037
+ transform: translateX(-50%);
1038
+ align-items: center;
1039
+ flex-direction: column-reverse;
1040
+ }
1041
+
1042
+ .toast-container.bottom-right {
1043
+ bottom: 10px;
1044
+ right: 10px;
1045
+ align-items: flex-end;
1046
+ flex-direction: column-reverse;
1047
+ }
1048
+
1049
+ /* Apply dark mode styles if user’s system or browser theme is set to 'dark' */
1050
+ @media (prefers-color-scheme: dark) {
1051
+ .toast {
1052
+ background-color: #333;
1053
+ color: white;
1054
+ }
1055
+ }
1056
+ `;
1057
+ __decorate([
1058
+ property({ type: Number, attribute: false })
1059
+ ], ToasterElement.prototype, "queueLimit", null);
1060
+ __decorate([
1061
+ state()
1062
+ ], ToasterElement.prototype, "_toastsList", void 0);
1063
+ __decorate([
1064
+ state()
1065
+ ], ToasterElement.prototype, "_queueLimit", void 0);
1066
+ ToasterElement = __decorate([
1067
+ customElement('app-toaster')
1068
+ ], ToasterElement);
1069
+
1070
+ export { ToasterElement, toast };
package/package.json ADDED
@@ -0,0 +1,134 @@
1
+ {
2
+ "name": "lit-toaster",
3
+ "version": "0.1.0",
4
+ "engines": {
5
+ "node": ">=20",
6
+ "npm": ">=9.6.3",
7
+ "yarn": "please use npm",
8
+ "pnpm": "please use npm"
9
+ },
10
+ "description": "Notifications for Lit Web Components",
11
+ "type": "module",
12
+ "main": "./dist/lit-toaster.js",
13
+ "module": "./dist/lit-toaster.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build:full": "npm run build && npm run docs",
21
+ "build": "rollup -c",
22
+ "test": "vitest run",
23
+ "coverage": "vitest run --coverage",
24
+ "lint": "eslint . --ext .js",
25
+ "format": "prettier --write .",
26
+ "docs": "typedoc --plugin typedoc-plugin-markdown",
27
+ "release": "release-it",
28
+ "release:dry": "release-it --dry-run --no-npm",
29
+ "release:info": "release-it --release-version",
30
+ "release:changelog:fix": "auto-changelog -p && git add CHANGELOG.md",
31
+ "size": "size-limit --json > size-report.json"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/brysonbw/lit-toaster.git"
36
+ },
37
+ "keywords": [
38
+ "lit",
39
+ "lit web components",
40
+ "toast",
41
+ "toast notifications",
42
+ "notifications",
43
+ "snackbar"
44
+ ],
45
+ "author": "Bryson Ward",
46
+ "license": "MIT",
47
+ "bugs": {
48
+ "url": "https://github.com/brysonbw/lit-toaster/issues"
49
+ },
50
+ "homepage": "https://github.com/brysonbw/lit-toaster",
51
+ "contributors": [
52
+ "Bryson Ward (https://github.com/brysonbw)"
53
+ ],
54
+ "dependencies": {
55
+ "lit": "^3.3.1"
56
+ },
57
+ "devDependencies": {
58
+ "@rollup/plugin-node-resolve": "^16.0.1",
59
+ "@rollup/plugin-typescript": "^12.1.4",
60
+ "@size-limit/file": "^11.2.0",
61
+ "@testing-library/jest-dom": "^6.7.0",
62
+ "@testing-library/user-event": "^14.6.1",
63
+ "@types/eslint-plugin-security": "^3.0.0",
64
+ "@types/jest": "^30.0.0",
65
+ "@vitest/coverage-v8": "^3.2.4",
66
+ "auto-changelog": "^2.5.0",
67
+ "eslint": "^9.33.0",
68
+ "eslint-config-prettier": "^10.1.8",
69
+ "eslint-plugin-lit": "^2.1.1",
70
+ "eslint-plugin-prettier": "^5.5.4",
71
+ "eslint-plugin-security": "^3.0.1",
72
+ "jsdom": "^26.1.0",
73
+ "prettier": "^3.6.2",
74
+ "release-it": "^19.0.4",
75
+ "rollup": "^4.46.2",
76
+ "rollup-plugin-cleanup": "^3.2.1",
77
+ "rollup-plugin-delete": "^3.0.1",
78
+ "rollup-plugin-dts": "^6.2.1",
79
+ "size-limit": "^11.2.0",
80
+ "tslib": "^2.8.1",
81
+ "typedoc": "^0.28.10",
82
+ "typedoc-plugin-markdown": "^4.8.1",
83
+ "typescript": "^5.9.2",
84
+ "typescript-eslint": "^8.39.1",
85
+ "vitest": "^3.2.4"
86
+ },
87
+ "release-it": {
88
+ "git": {
89
+ "commitMessage": "chore(release): v${version}",
90
+ "push": true,
91
+ "commit": true,
92
+ "tag": true,
93
+ "requireCommits": false,
94
+ "requireCleanWorkingDir": false
95
+ },
96
+ "github": {
97
+ "release": true,
98
+ "draft": true
99
+ },
100
+ "npm": {
101
+ "publish": false,
102
+ "ignoreVersion": false
103
+ },
104
+ "hooks": {
105
+ "before:init": [
106
+ "npm run lint",
107
+ "npm run test"
108
+ ],
109
+ "after:bump": "npm run build:full && git add ./dist ./docs/api-reference ./package-lock.json",
110
+ "before:release": "npm run release:changelog:fix",
111
+ "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
112
+ }
113
+ },
114
+ "auto-changelog": {
115
+ "breakingPattern": "Breaking change"
116
+ },
117
+ "typedocOptions": {
118
+ "entryPoints": [
119
+ "src/typedoc.ts"
120
+ ],
121
+ "disableSources": true,
122
+ "out": "docs/api-reference",
123
+ "readme": "none",
124
+ "name": "Lit Toaster API Reference"
125
+ },
126
+ "size-limit": [
127
+ {
128
+ "path": "dist/lit-toaster.js",
129
+ "name": "Browser build (ESM)",
130
+ "limit": "10kB",
131
+ "gzip": true
132
+ }
133
+ ]
134
+ }