crisp-toast 1.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/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # Crisp Toast 🍞
2
+
3
+ A lightweight, beautiful, and highly customizable vanilla JavaScript toast notification library. Designed for modern web applications with premium aesthetics.
4
+
5
+ ![NPM Version](https://img.shields.io/npm/v/crisp-toast)
6
+ ![License](https://img.shields.io/npm/l/crisp-toast)
7
+
8
+ ## Table of Contents
9
+ - [Features](#features)
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
12
+ - [Usage](#usage)
13
+ - [Toast Types](#toast-types)
14
+ - [Variants](#variants)
15
+ - [Colors](#colors)
16
+ - [Placements](#placements)
17
+ - [Promises](#promises)
18
+ - [API Reference](#api-reference)
19
+ - [License](#license)
20
+
21
+ ## Features
22
+
23
+ - 🚀 **Vanilla JS**: No dependencies, works anywhere.
24
+ - 🎨 **Beautiful Designs**: Support for Solid, Bordered, and Flat variants.
25
+ - 🌈 **Modern Colors**: Built-in palettes (Primary, Success, Danger, etc.) with dark mode support.
26
+ - 🧭 **Precise Placement**: 6 different anchor positions.
27
+ - ⏳ **Progress Bar**: Visual countdown for auto-dismissal.
28
+ - 📦 **Promise Support**: Easy handling of loading states.
29
+ - 🛠️ **Fully Customizable**: Override styles, icons, and behavior.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install crisp-toast
35
+ ```
36
+
37
+ Or via yarn:
38
+
39
+ ```bash
40
+ yarn add crisp-toast
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ Import the library and its styles:
46
+
47
+ ```javascript
48
+ import { toast } from 'crisp-toast';
49
+ import 'crisp-toast/style.css';
50
+
51
+ // Simple toast
52
+ toast('Hello World!');
53
+
54
+ // Success toast
55
+ toast.success('Settings saved!');
56
+ ```
57
+
58
+ ## Usage
59
+
60
+ ### Toast Types
61
+
62
+ Crisp Toast provides specialized methods for common states:
63
+
64
+ ```javascript
65
+ toast('Normal toast');
66
+ toast.success('Task completed!');
67
+ toast.error('Something went wrong.');
68
+ toast.warning('Check your input.');
69
+ toast.info('New update available.');
70
+ toast.loading('Processing...');
71
+ ```
72
+
73
+ ### Variants
74
+
75
+ Choose between three premium variants:
76
+
77
+ ```javascript
78
+ toast.success({
79
+ title: 'Success!',
80
+ variant: 'bordered' // 'solid' | 'bordered' | 'flat' (default: 'bordered')
81
+ });
82
+ ```
83
+
84
+ ### Colors
85
+
86
+ Map toasts to your brand or state:
87
+
88
+ ```javascript
89
+ toast({
90
+ title: 'Custom Color',
91
+ color: 'secondary' // 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger'
92
+ });
93
+ ```
94
+
95
+ ### Placements
96
+
97
+ Control exactly where the toast appears:
98
+
99
+ ```javascript
100
+ toast({
101
+ title: 'Top Center Toast',
102
+ placement: 'top-center'
103
+ // Options: 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right'
104
+ });
105
+ ```
106
+
107
+ ### Promises
108
+
109
+ Handle asynchronous operations with ease:
110
+
111
+ ```javascript
112
+ const savePromise = saveData();
113
+
114
+ toast.promise(savePromise, {
115
+ loading: 'Saving your changes...',
116
+ success: (data) => `Saved successfully: ${data.name}`,
117
+ error: (err) => `Failed to save: ${err.message}`
118
+ });
119
+ ```
120
+
121
+ ## API Reference
122
+
123
+ ### `toast(options | string)`
124
+
125
+ The main function. You can pass a string for a simple message or an options object.
126
+
127
+ #### Options Object
128
+
129
+ | Property | Type | Default | Description |
130
+ | :--- | :--- | :--- | :--- |
131
+ | `title` | `string` | `undefined` | The main message of the toast. |
132
+ | `description`| `string` | `undefined` | Secondary text below the title. |
133
+ | `variant` | `'solid' \| 'bordered' \| 'flat'` | `'bordered'` | The visual style of the toast. |
134
+ | `color` | `ToastColor` | `'default'` | The color theme. |
135
+ | `placement` | `ToastPlacement` | `'bottom-right'` | Position on the screen. |
136
+ | `duration` | `number` | `3000` | Auto-dismissal time in ms. `0` or `Infinity` to keep forever. |
137
+ | `progressBar`| `boolean` | `true` | Whether to show the progress bar. |
138
+ | `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'full'` | `'md'` | Corner rounding. |
139
+ | `icon` | `string \| HTMLElement \| boolean` | `true` | Pass HTML string, element, or `false` to hide. |
140
+ | `endContent` | `HTMLElement \| function` | `undefined` | Custom content at the end of the toast. |
141
+ | `action` | `{ label: string, onClick: function }`| `undefined` | Add a call-to-action button. |
142
+ | `onClose` | `function` | `undefined` | Callback when toast is dismissed. |
143
+ | `customStyle`| `CSSStyleDeclaration` | `{}` | Custom CSS overrides. |
144
+
145
+ ## License
146
+
147
+ MIT © [Ahmad](https://github.com/scriptwithahmad)
@@ -0,0 +1,10 @@
1
+ import { ToastPlacement } from '../types/index';
2
+
3
+ export declare class ToastManager {
4
+ private static containers;
5
+ static getContainer(placement: ToastPlacement): HTMLElement;
6
+ static removeContainer(placement: ToastPlacement): void;
7
+ static addToast(placement: ToastPlacement, element: HTMLElement): void;
8
+ static removeToast(placement: ToastPlacement, element: HTMLElement): void;
9
+ private static updatePositions;
10
+ }
@@ -0,0 +1,15 @@
1
+ import { ToastOptions, ToastState } from '../types/index';
2
+
3
+ export declare class Toast {
4
+ private state;
5
+ private element;
6
+ private timer;
7
+ constructor(options: ToastOptions, type?: ToastState['type']);
8
+ private render;
9
+ private handlePromise;
10
+ update(options: Partial<ToastOptions> & {
11
+ type?: ToastState['type'];
12
+ }): void;
13
+ private buildClasses;
14
+ dismiss(): void;
15
+ }
@@ -0,0 +1,163 @@
1
+ var f = Object.defineProperty;
2
+ var v = (s, t, e) => t in s ? f(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
3
+ var d = (s, t, e) => v(s, typeof t != "symbol" ? t + "" : t, e);
4
+ class m {
5
+ static getContainer(t) {
6
+ if (this.containers.has(t))
7
+ return this.containers.get(t);
8
+ if (typeof document > "u")
9
+ return {};
10
+ const e = document.createElement("div");
11
+ return e.classList.add("ct-container"), e.classList.add(`ct-${t}`), document.body.appendChild(e), this.containers.set(t, e), e;
12
+ }
13
+ static removeContainer(t) {
14
+ const e = this.containers.get(t);
15
+ e && e.childNodes.length === 0 && (e.remove(), this.containers.delete(t));
16
+ }
17
+ static addToast(t, e) {
18
+ this.getContainer(t).prepend(e), setTimeout(() => this.updatePositions(t), 0);
19
+ }
20
+ static removeToast(t, e) {
21
+ e.parentNode && e.parentNode.removeChild(e), this.updatePositions(t), this.removeContainer(t);
22
+ }
23
+ static updatePositions(t) {
24
+ const e = this.containers.get(t);
25
+ if (!e) return;
26
+ const a = Array.from(e.children).filter(
27
+ (o) => o.classList.contains("ct-toast") && !o.classList.contains("ct-leave")
28
+ ), n = t.includes("bottom"), i = 16, h = 0.05;
29
+ a.forEach((o, c) => {
30
+ const p = n ? -(c * i) : c * i, u = 1 - c * h;
31
+ o.style.setProperty("--ct-y", `${p}px`), o.style.setProperty("--ct-scale", `${u}`), o.style.zIndex = `${9999 - c}`, c > 3 ? (o.style.opacity = "0", o.style.pointerEvents = "none") : (o.style.opacity = "1", o.style.pointerEvents = "auto");
32
+ }), Array.from(e.children).filter((o) => o.classList.contains("ct-leave")).forEach((o) => {
33
+ const c = parseFloat(o.style.getPropertyValue("--ct-y") || "0");
34
+ o.style.setProperty("--ct-y", `${c + (n ? 20 : -20)}px`), o.style.opacity = "0";
35
+ });
36
+ }
37
+ }
38
+ d(m, "containers", /* @__PURE__ */ new Map());
39
+ const r = {
40
+ default: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',
41
+ primary: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',
42
+ secondary: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',
43
+ success: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" /></svg>',
44
+ warning: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /></svg>',
45
+ danger: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" /></svg>',
46
+ loading: '<svg class="ct-spinner" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>',
47
+ close: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><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>'
48
+ };
49
+ let g = 0;
50
+ class w {
51
+ constructor(t, e = "normal") {
52
+ d(this, "state");
53
+ d(this, "element", null);
54
+ d(this, "timer", null);
55
+ this.state = {
56
+ ...t,
57
+ type: e,
58
+ id: t.id || `ct-${++g}`,
59
+ createdAt: Date.now(),
60
+ placement: t.placement || "bottom-right",
61
+ duration: t.duration ?? (e === "loading" ? 1 / 0 : 3e3),
62
+ variant: t.variant || "bordered",
63
+ color: t.color || (e === "normal" ? "default" : e === "loading" ? "primary" : e),
64
+ radius: t.radius || "md",
65
+ progressBar: t.progressBar !== !1
66
+ // default to true if they want it
67
+ }, typeof document < "u" && this.render();
68
+ }
69
+ render() {
70
+ this.element = document.createElement("div"), this.element.id = this.state.id, this.element.className = this.buildClasses(), this.state.customStyle && Object.assign(this.element.style, this.state.customStyle);
71
+ const t = document.createElement("div");
72
+ t.className = "ct-icon", this.state.icon !== !1 && (this.state.icon instanceof HTMLElement ? t.appendChild(this.state.icon) : typeof this.state.icon == "string" ? t.innerHTML = this.state.icon : r[this.state.type] || r[this.state.color] ? t.innerHTML = this.state.type === "loading" ? r.loading : r[this.state.color] || r.default : t.innerHTML = r.default, this.element.appendChild(t));
73
+ const e = document.createElement("div");
74
+ if (e.className = "ct-content", this.state.title) {
75
+ const i = document.createElement("div");
76
+ i.className = "ct-title", i.textContent = this.state.title, e.appendChild(i);
77
+ }
78
+ if (this.state.description) {
79
+ const i = document.createElement("div");
80
+ i.className = "ct-description", i.textContent = this.state.description, e.appendChild(i);
81
+ }
82
+ if ((this.state.title || this.state.description) && this.element.appendChild(e), this.state.endContent) {
83
+ const i = document.createElement("div");
84
+ i.className = "ct-end-content", typeof this.state.endContent == "function" ? this.state.endContent(i) : this.state.endContent instanceof HTMLElement ? i.appendChild(this.state.endContent) : typeof this.state.endContent == "string" ? i.innerHTML = this.state.endContent : this.state.endContent, this.element.appendChild(i);
85
+ }
86
+ const a = document.createElement("div");
87
+ if (a.className = "ct-actions", this.state.action) {
88
+ const i = document.createElement("button");
89
+ i.className = "ct-action-btn", i.textContent = this.state.action.label, i.onclick = () => {
90
+ var h;
91
+ (h = this.state.action) == null || h.onClick(), this.dismiss();
92
+ }, a.appendChild(i);
93
+ }
94
+ const n = document.createElement("button");
95
+ if (n.className = "ct-close", n.innerHTML = r.close, n.onclick = () => this.dismiss(), a.appendChild(n), this.element.appendChild(a), this.state.progressBar && this.state.duration > 0 && this.state.duration !== 1 / 0) {
96
+ const i = document.createElement("div");
97
+ i.className = "ct-progress-bar", i.style.animation = `ct-progress ${this.state.duration}ms linear forwards`, this.element.appendChild(i);
98
+ }
99
+ m.addToast(this.state.placement, this.element), this.state.duration > 0 && this.state.duration !== 1 / 0 && (this.timer = window.setTimeout(() => this.dismiss(), this.state.duration)), this.state.promise && this.handlePromise(this.state.promise);
100
+ }
101
+ handlePromise(t) {
102
+ t.then((e) => {
103
+ var n;
104
+ const a = (n = this.state.promiseMessages) != null && n.success ? typeof this.state.promiseMessages.success == "function" ? this.state.promiseMessages.success(e) : this.state.promiseMessages.success : "Success";
105
+ this.update({ type: "success", title: a, color: "success", duration: 3e3 });
106
+ }).catch((e) => {
107
+ var n;
108
+ const a = (n = this.state.promiseMessages) != null && n.error ? typeof this.state.promiseMessages.error == "function" ? this.state.promiseMessages.error(e) : this.state.promiseMessages.error : "Error";
109
+ this.update({ type: "error", title: a, color: "danger", duration: 3e3 });
110
+ });
111
+ }
112
+ update(t) {
113
+ if (!this.element) return;
114
+ this.state = { ...this.state, ...t }, this.element.className = this.buildClasses();
115
+ const e = this.element.querySelector(".ct-icon");
116
+ e && (this.state.icon !== !1 ? this.state.icon instanceof HTMLElement ? (e.innerHTML = "", e.appendChild(this.state.icon)) : typeof this.state.icon == "string" ? e.innerHTML = this.state.icon : e.innerHTML = this.state.type === "loading" ? r.loading : r[this.state.color] || r.default : e.innerHTML = "");
117
+ const a = this.element.querySelector(".ct-content");
118
+ if (a) {
119
+ if (a.innerHTML = "", this.state.title) {
120
+ const n = document.createElement("div");
121
+ n.className = "ct-title", n.textContent = typeof this.state.title == "string" ? this.state.title : "", a.appendChild(n);
122
+ }
123
+ if (this.state.description) {
124
+ const n = document.createElement("div");
125
+ n.className = "ct-description", n.textContent = this.state.description, a.appendChild(n);
126
+ }
127
+ }
128
+ this.timer && clearTimeout(this.timer), this.state.duration > 0 && this.state.duration !== 1 / 0 && (this.timer = window.setTimeout(() => this.dismiss(), this.state.duration));
129
+ }
130
+ buildClasses() {
131
+ return ["ct-toast", `ct-${this.state.variant}`, `ct-color-${this.state.color}`, `ct-radius-${this.state.radius}`].join(" ");
132
+ }
133
+ dismiss() {
134
+ if (!this.element) return;
135
+ this.timer && clearTimeout(this.timer), this.element.classList.add("ct-leave");
136
+ const t = () => {
137
+ this.element && (m.removeToast(this.state.placement, this.element), this.state.onClose && this.state.onClose());
138
+ };
139
+ this.element.addEventListener("animationend", t, { once: !0 }), setTimeout(t, 400);
140
+ }
141
+ }
142
+ const l = (s, t = "normal") => {
143
+ const e = typeof s == "string" ? { title: s } : s;
144
+ return new w(e, t);
145
+ }, M = Object.assign(
146
+ (s) => l(s),
147
+ {
148
+ success: (s) => l(s, "success"),
149
+ error: (s) => l(s, "error"),
150
+ warning: (s) => l(s, "warning"),
151
+ info: (s) => l(s, "info"),
152
+ loading: (s) => l(s, "loading"),
153
+ promise: (s, t, e) => l({
154
+ ...e,
155
+ title: typeof (t == null ? void 0 : t.loading) == "string" ? t.loading : "Loading...",
156
+ promise: s,
157
+ promiseMessages: t
158
+ }, "loading")
159
+ }
160
+ );
161
+ export {
162
+ M as toast
163
+ };
@@ -0,0 +1 @@
1
+ (function(l,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(l=typeof globalThis<"u"?globalThis:l||self,r(l.CrispToast={}))})(this,function(l){"use strict";var w=Object.defineProperty;var C=(l,r,c)=>r in l?w(l,r,{enumerable:!0,configurable:!0,writable:!0,value:c}):l[r]=c;var m=(l,r,c)=>C(l,typeof r!="symbol"?r+"":r,c);class r{static getContainer(t){if(this.containers.has(t))return this.containers.get(t);if(typeof document>"u")return{};const e=document.createElement("div");return e.classList.add("ct-container"),e.classList.add(`ct-${t}`),document.body.appendChild(e),this.containers.set(t,e),e}static removeContainer(t){const e=this.containers.get(t);e&&e.childNodes.length===0&&(e.remove(),this.containers.delete(t))}static addToast(t,e){this.getContainer(t).prepend(e),setTimeout(()=>this.updatePositions(t),0)}static removeToast(t,e){e.parentNode&&e.parentNode.removeChild(e),this.updatePositions(t),this.removeContainer(t)}static updatePositions(t){const e=this.containers.get(t);if(!e)return;const a=Array.from(e.children).filter(o=>o.classList.contains("ct-toast")&&!o.classList.contains("ct-leave")),i=t.includes("bottom"),s=16,p=.05;a.forEach((o,h)=>{const g=i?-(h*s):h*s,y=1-h*p;o.style.setProperty("--ct-y",`${g}px`),o.style.setProperty("--ct-scale",`${y}`),o.style.zIndex=`${9999-h}`,h>3?(o.style.opacity="0",o.style.pointerEvents="none"):(o.style.opacity="1",o.style.pointerEvents="auto")}),Array.from(e.children).filter(o=>o.classList.contains("ct-leave")).forEach(o=>{const h=parseFloat(o.style.getPropertyValue("--ct-y")||"0");o.style.setProperty("--ct-y",`${h+(i?20:-20)}px`),o.style.opacity="0"})}}m(r,"containers",new Map);const c={default:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',primary:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',secondary:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>',success:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" /></svg>',warning:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /></svg>',danger:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" /></svg>',loading:'<svg class="ct-spinner" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>',close:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><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>'};let u=0;class f{constructor(t,e="normal"){m(this,"state");m(this,"element",null);m(this,"timer",null);this.state={...t,type:e,id:t.id||`ct-${++u}`,createdAt:Date.now(),placement:t.placement||"bottom-right",duration:t.duration??(e==="loading"?1/0:3e3),variant:t.variant||"bordered",color:t.color||(e==="normal"?"default":e==="loading"?"primary":e),radius:t.radius||"md",progressBar:t.progressBar!==!1},typeof document<"u"&&this.render()}render(){this.element=document.createElement("div"),this.element.id=this.state.id,this.element.className=this.buildClasses(),this.state.customStyle&&Object.assign(this.element.style,this.state.customStyle);const t=document.createElement("div");t.className="ct-icon",this.state.icon!==!1&&(this.state.icon instanceof HTMLElement?t.appendChild(this.state.icon):typeof this.state.icon=="string"?t.innerHTML=this.state.icon:c[this.state.type]||c[this.state.color]?t.innerHTML=this.state.type==="loading"?c.loading:c[this.state.color]||c.default:t.innerHTML=c.default,this.element.appendChild(t));const e=document.createElement("div");if(e.className="ct-content",this.state.title){const s=document.createElement("div");s.className="ct-title",s.textContent=this.state.title,e.appendChild(s)}if(this.state.description){const s=document.createElement("div");s.className="ct-description",s.textContent=this.state.description,e.appendChild(s)}if((this.state.title||this.state.description)&&this.element.appendChild(e),this.state.endContent){const s=document.createElement("div");s.className="ct-end-content",typeof this.state.endContent=="function"?this.state.endContent(s):this.state.endContent instanceof HTMLElement?s.appendChild(this.state.endContent):typeof this.state.endContent=="string"?s.innerHTML=this.state.endContent:this.state.endContent,this.element.appendChild(s)}const a=document.createElement("div");if(a.className="ct-actions",this.state.action){const s=document.createElement("button");s.className="ct-action-btn",s.textContent=this.state.action.label,s.onclick=()=>{var p;(p=this.state.action)==null||p.onClick(),this.dismiss()},a.appendChild(s)}const i=document.createElement("button");if(i.className="ct-close",i.innerHTML=c.close,i.onclick=()=>this.dismiss(),a.appendChild(i),this.element.appendChild(a),this.state.progressBar&&this.state.duration>0&&this.state.duration!==1/0){const s=document.createElement("div");s.className="ct-progress-bar",s.style.animation=`ct-progress ${this.state.duration}ms linear forwards`,this.element.appendChild(s)}r.addToast(this.state.placement,this.element),this.state.duration>0&&this.state.duration!==1/0&&(this.timer=window.setTimeout(()=>this.dismiss(),this.state.duration)),this.state.promise&&this.handlePromise(this.state.promise)}handlePromise(t){t.then(e=>{var i;const a=(i=this.state.promiseMessages)!=null&&i.success?typeof this.state.promiseMessages.success=="function"?this.state.promiseMessages.success(e):this.state.promiseMessages.success:"Success";this.update({type:"success",title:a,color:"success",duration:3e3})}).catch(e=>{var i;const a=(i=this.state.promiseMessages)!=null&&i.error?typeof this.state.promiseMessages.error=="function"?this.state.promiseMessages.error(e):this.state.promiseMessages.error:"Error";this.update({type:"error",title:a,color:"danger",duration:3e3})})}update(t){if(!this.element)return;this.state={...this.state,...t},this.element.className=this.buildClasses();const e=this.element.querySelector(".ct-icon");e&&(this.state.icon!==!1?this.state.icon instanceof HTMLElement?(e.innerHTML="",e.appendChild(this.state.icon)):typeof this.state.icon=="string"?e.innerHTML=this.state.icon:e.innerHTML=this.state.type==="loading"?c.loading:c[this.state.color]||c.default:e.innerHTML="");const a=this.element.querySelector(".ct-content");if(a){if(a.innerHTML="",this.state.title){const i=document.createElement("div");i.className="ct-title",i.textContent=typeof this.state.title=="string"?this.state.title:"",a.appendChild(i)}if(this.state.description){const i=document.createElement("div");i.className="ct-description",i.textContent=this.state.description,a.appendChild(i)}}this.timer&&clearTimeout(this.timer),this.state.duration>0&&this.state.duration!==1/0&&(this.timer=window.setTimeout(()=>this.dismiss(),this.state.duration))}buildClasses(){return["ct-toast",`ct-${this.state.variant}`,`ct-color-${this.state.color}`,`ct-radius-${this.state.radius}`].join(" ")}dismiss(){if(!this.element)return;this.timer&&clearTimeout(this.timer),this.element.classList.add("ct-leave");const t=()=>{this.element&&(r.removeToast(this.state.placement,this.element),this.state.onClose&&this.state.onClose())};this.element.addEventListener("animationend",t,{once:!0}),setTimeout(t,400)}}const d=(n,t="normal")=>{const e=typeof n=="string"?{title:n}:n;return new f(e,t)},v=Object.assign(n=>d(n),{success:n=>d(n,"success"),error:n=>d(n,"error"),warning:n=>d(n,"warning"),info:n=>d(n,"info"),loading:n=>d(n,"loading"),promise:(n,t,e)=>d({...e,title:typeof(t==null?void 0:t.loading)=="string"?t.loading:"Loading...",promise:n,promiseMessages:t},"loading")});l.toast=v,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,12 @@
1
+ import { ToastOptions } from './types/index';
2
+ import { Toast } from './core/toast';
3
+
4
+ export * from './types/index';
5
+ export declare const toast: ((options: ToastOptions | string) => Toast) & {
6
+ success: (options: ToastOptions | string) => Toast;
7
+ error: (options: ToastOptions | string) => Toast;
8
+ warning: (options: ToastOptions | string) => Toast;
9
+ info: (options: ToastOptions | string) => Toast;
10
+ loading: (options: ToastOptions | string) => Toast;
11
+ promise: <T>(promise: Promise<T>, messages: ToastOptions["promiseMessages"], options?: Omit<ToastOptions, "promise" | "promiseMessages">) => Toast;
12
+ };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ :root{--ct-bg-default: #ffffff;--ct-title-default: #111827;--ct-border-default: #e5e7eb;--ct-icon-default: #6b7280;--ct-bg-primary: #f0f7ff;--ct-title-primary: #1d4ed8;--ct-border-primary: #dbeafe;--ct-icon-primary: #3b82f6;--ct-bg-secondary: #f5f3ff;--ct-title-secondary: #6d28d9;--ct-border-secondary: #ede9fe;--ct-icon-secondary: #8b5cf6;--ct-bg-success: #f0fdf4;--ct-title-success: #15803d;--ct-border-success: #dcfce7;--ct-icon-success: #22c55e;--ct-bg-warning: #fffbeb;--ct-title-warning: #b45309;--ct-border-warning: #fef3c7;--ct-icon-warning: #f59e0b;--ct-bg-danger: #fef2f2;--ct-title-danger: #b91c1c;--ct-border-danger: #fee2e2;--ct-icon-danger: #ef4444;--ct-text-desc: #6b7280;--ct-shadow: 0 4px 6px -1px rgba(0, 0, 0, .05), 0 2px 4px -1px rgba(0, 0, 0, .03);--ct-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ct-radius-none: 0;--ct-radius-sm: 8px;--ct-radius-md: 12px;--ct-radius-lg: 16px;--ct-radius-full: 9999px;--ct-duration: .3s;--ct-easing: cubic-bezier(.2, .8, .2, 1)}.ct-theme-dark{--ct-bg-default: #171717;--ct-title-default: #f9fafb;--ct-border-default: #262626;--ct-icon-default: #737373;--ct-bg-primary: #171c2b;--ct-title-primary: #93c5fd;--ct-border-primary: #1e3a8a;--ct-icon-primary: #3b82f6;--ct-bg-secondary: #1c1926;--ct-title-secondary: #c4b5fd;--ct-border-secondary: #4c1d95;--ct-icon-secondary: #8b5cf6;--ct-bg-success: #141d18;--ct-title-success: #86efac;--ct-border-success: #14532d;--ct-icon-success: #22c55e;--ct-bg-warning: #1c1b14;--ct-title-warning: #fde047;--ct-border-warning: #713f12;--ct-icon-warning: #f59e0b;--ct-bg-danger: #1c1616;--ct-title-danger: #fca5a5;--ct-border-danger: #7f1d1d;--ct-icon-danger: #ef4444;--ct-text-desc: #a3a3a3;--ct-shadow: 0 10px 30px -10px rgba(0, 0, 0, .5)}@media (prefers-color-scheme: dark){:root:not(.ct-theme-light){--ct-bg-default: #171717;--ct-title-default: #f9fafb;--ct-border-default: #262626;--ct-icon-default: #737373;--ct-bg-primary: #171c2b;--ct-title-primary: #93c5fd;--ct-border-primary: #1e3a8a;--ct-icon-primary: #3b82f6;--ct-bg-secondary: #1c1926;--ct-title-secondary: #c4b5fd;--ct-border-secondary: #4c1d95;--ct-icon-secondary: #8b5cf6;--ct-bg-success: #141d18;--ct-title-success: #86efac;--ct-border-success: #14532d;--ct-icon-success: #22c55e;--ct-bg-warning: #1c1b14;--ct-title-warning: #fde047;--ct-border-warning: #713f12;--ct-icon-warning: #f59e0b;--ct-bg-danger: #1c1616;--ct-title-danger: #fca5a5;--ct-border-danger: #7f1d1d;--ct-icon-danger: #ef4444;--ct-text-desc: #a3a3a3}}.ct-container{position:fixed;z-index:9999;display:grid;pointer-events:none}.ct-top-left{top:1.5rem;left:1.5rem;justify-items:start}.ct-top-center{top:1.5rem;left:50%;transform:translate(-50%);justify-items:center}.ct-top-right{top:1.5rem;right:1.5rem;justify-items:end}.ct-bottom-left{bottom:1.5rem;left:1.5rem;justify-items:start;align-items:end}.ct-bottom-center{bottom:1.5rem;left:50%;transform:translate(-50%);justify-items:center;align-items:end}.ct-bottom-right{bottom:1.5rem;right:1.5rem;justify-items:end;align-items:end}.ct-toast{pointer-events:auto;display:flex;align-items:flex-start;gap:1rem;padding:1rem 1.25rem;min-width:340px;max-width:440px;box-shadow:var(--ct-shadow);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:.875rem;line-height:1.25rem;grid-area:1 / 1;will-change:transform,opacity;animation:ct-enter var(--ct-duration) var(--ct-easing) forwards;transition:all var(--ct-duration) var(--ct-easing);border:1px solid transparent;position:relative;overflow:hidden;transform:translateY(var(--ct-y, 0)) scale(var(--ct-scale, 1))}.ct-toast.ct-leave{animation:ct-leave var(--ct-duration) var(--ct-easing) forwards}.ct-radius-none{border-radius:var(--ct-radius-none)}.ct-radius-sm{border-radius:var(--ct-radius-sm)}.ct-radius-md{border-radius:var(--ct-radius-md)}.ct-radius-lg{border-radius:var(--ct-radius-lg)}.ct-radius-full{border-radius:var(--ct-radius-full)}.ct-toast.ct-solid{border-color:transparent;color:#fff}.ct-toast.ct-solid .ct-title{color:#fff!important}.ct-toast.ct-solid .ct-description{color:#fffc!important}.ct-toast.ct-solid .ct-close,.ct-toast.ct-solid .ct-action-btn{color:#fff;opacity:.8}.ct-toast.ct-solid .ct-icon{background:#fff3;border:1px solid rgba(255,255,255,.3)}.ct-toast.ct-solid.ct-color-default{background:var(--ct-icon-default)}.ct-toast.ct-solid.ct-color-primary{background:var(--ct-icon-primary)}.ct-toast.ct-solid.ct-color-secondary{background:var(--ct-icon-secondary)}.ct-toast.ct-solid.ct-color-success{background:var(--ct-icon-success)}.ct-toast.ct-solid.ct-color-warning{background:var(--ct-icon-warning)}.ct-toast.ct-solid.ct-color-danger{background:var(--ct-icon-danger)}.ct-toast.ct-bordered{border-width:2px}.ct-toast.ct-bordered.ct-color-default{background:var(--ct-bg-default);border-color:var(--ct-border-default)}.ct-toast.ct-bordered.ct-color-primary{background:var(--ct-bg-primary);border-color:var(--ct-icon-primary);color:var(--ct-title-primary)}.ct-toast.ct-bordered.ct-color-secondary{background:var(--ct-bg-secondary);border-color:var(--ct-icon-secondary);color:var(--ct-title-secondary)}.ct-toast.ct-bordered.ct-color-success{background:var(--ct-bg-success);border-color:var(--ct-icon-success);color:var(--ct-title-success)}.ct-toast.ct-bordered.ct-color-warning{background:var(--ct-bg-warning);border-color:var(--ct-icon-warning);color:var(--ct-title-warning)}.ct-toast.ct-bordered.ct-color-danger{background:var(--ct-bg-danger);border-color:var(--ct-icon-danger);color:var(--ct-title-danger)}.ct-toast.ct-bordered .ct-title{color:inherit}.ct-toast.ct-bordered .ct-description{color:var(--ct-text-desc)}.ct-toast.ct-flat{box-shadow:none;border-color:transparent;background-image:none!important}.ct-toast.ct-flat:before{content:"";position:absolute;top:-30px;left:-30px;width:90px;height:90px;border-radius:50%;filter:blur(35px);opacity:.35;pointer-events:none;z-index:0}.ct-toast.ct-flat.ct-color-default{background:var(--ct-bg-default)}.ct-toast.ct-flat.ct-color-primary{background:var(--ct-bg-primary);color:var(--ct-title-primary)}.ct-toast.ct-flat.ct-color-secondary{background:var(--ct-bg-secondary);color:var(--ct-title-secondary)}.ct-toast.ct-flat.ct-color-success{background:var(--ct-bg-success);color:var(--ct-title-success)}.ct-toast.ct-flat.ct-color-warning{background:var(--ct-bg-warning);color:var(--ct-title-warning)}.ct-toast.ct-flat.ct-color-danger{background:var(--ct-bg-danger);color:var(--ct-title-danger)}.ct-toast.ct-flat .ct-title{color:inherit}.ct-toast.ct-flat .ct-description{color:var(--ct-text-desc)}.ct-toast.ct-flat.ct-color-primary:before{background:var(--ct-icon-primary)}.ct-toast.ct-flat.ct-color-secondary:before{background:var(--ct-icon-secondary)}.ct-toast.ct-flat.ct-color-success:before{background:var(--ct-icon-success)}.ct-toast.ct-flat.ct-color-warning:before{background:var(--ct-icon-warning)}.ct-toast.ct-flat.ct-color-danger:before{background:var(--ct-icon-danger)}.ct-icon{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:50%;color:#fff;margin-top:.125rem;position:relative;z-index:1}.ct-icon svg{width:.875rem;height:.875rem}.ct-color-default .ct-icon{background:var(--ct-icon-default)}.ct-color-primary .ct-icon{background:var(--ct-icon-primary)}.ct-color-secondary .ct-icon{background:var(--ct-icon-secondary)}.ct-color-success .ct-icon{background:var(--ct-icon-success)}.ct-color-warning .ct-icon{background:var(--ct-icon-warning)}.ct-color-danger .ct-icon{background:var(--ct-icon-danger)}.ct-content{flex-grow:1;display:flex;flex-direction:column;gap:.25rem;position:relative;z-index:1}.ct-end-content{flex-shrink:0;display:flex;align-items:center;justify-content:center;margin-left:auto;padding-left:.5rem;position:relative;z-index:1}.ct-title{font-weight:600;font-size:.9375rem}.ct-color-default .ct-title{color:var(--ct-title-default)}.ct-color-primary .ct-title{color:var(--ct-title-primary)}.ct-color-secondary .ct-title{color:var(--ct-title-secondary)}.ct-color-success .ct-title{color:var(--ct-title-success)}.ct-color-warning .ct-title{color:var(--ct-title-warning)}.ct-color-danger .ct-title{color:var(--ct-title-danger)}.ct-description{font-size:.8125rem;color:var(--ct-text-desc);line-height:1.25rem}.ct-actions{display:flex;align-items:center;gap:.5rem;margin-left:auto;margin-top:.125rem;position:relative;z-index:1}.ct-action-btn{background:transparent;border:none;font-size:.8125rem;font-weight:600;cursor:pointer;padding:.25rem .5rem;border-radius:var(--ct-radius-sm);transition:background .2s,opacity .2s;color:var(--ct-title-default);opacity:.9}.ct-action-btn:hover{background:#8080801a;opacity:1}.ct-close{background:transparent;border:none;cursor:pointer;padding:.25rem;border-radius:4px;opacity:.5;transition:opacity .2s,background .2s;color:var(--ct-text-desc);display:flex;align-items:center;justify-content:center}.ct-close:hover{opacity:1;background:#8080801a}.ct-close svg{width:1rem;height:1rem}.ct-progress-bar{position:absolute;bottom:0;left:0;height:3px;width:100%;transform-origin:left;z-index:2}.ct-color-default .ct-progress-bar{background:var(--ct-icon-default)}.ct-color-primary .ct-progress-bar{background:var(--ct-icon-primary)}.ct-color-secondary .ct-progress-bar{background:var(--ct-icon-secondary)}.ct-color-success .ct-progress-bar{background:var(--ct-icon-success)}.ct-color-warning .ct-progress-bar{background:var(--ct-icon-warning)}.ct-color-danger .ct-progress-bar{background:var(--ct-icon-danger)}@keyframes ct-enter{0%{transform:scale(.9) translateY(20px);opacity:0}to{transform:scale(var(--ct-scale, 1)) translateY(var(--ct-y, 0));opacity:1}}@keyframes ct-leave{0%{transform:scale(var(--ct-scale, 1)) translateY(var(--ct-y, 0));opacity:1}to{transform:scale(.9) translateY(20px);opacity:0}}@keyframes ct-progress{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.ct-spinner{animation:ct-spin 1s linear infinite;color:#fff}@keyframes ct-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}
@@ -0,0 +1,34 @@
1
+ export type ToastVariant = 'solid' | 'bordered' | 'flat';
2
+ export type ToastColor = 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';
3
+ export type ToastPlacement = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
4
+ export interface ToastOptions {
5
+ title?: string;
6
+ description?: string;
7
+ variant?: ToastVariant;
8
+ color?: ToastColor;
9
+ placement?: ToastPlacement;
10
+ duration?: number;
11
+ progressBar?: boolean;
12
+ radius?: 'none' | 'sm' | 'md' | 'lg' | 'full';
13
+ icon?: string | HTMLElement | boolean;
14
+ endContent?: string | HTMLElement | ((container: HTMLElement) => void) | any;
15
+ id?: string;
16
+ customStyle?: Partial<CSSStyleDeclaration>;
17
+ action?: {
18
+ label: string;
19
+ onClick: () => void;
20
+ };
21
+ onClose?: () => void;
22
+ promise?: Promise<any>;
23
+ promiseMessages?: {
24
+ loading: string | HTMLElement;
25
+ success: string | HTMLElement | ((data: any) => string | HTMLElement);
26
+ error: string | HTMLElement | ((err: any) => string | HTMLElement);
27
+ };
28
+ }
29
+ export type ToastType = 'normal' | 'loading' | 'success' | 'error' | 'warning' | 'info';
30
+ export interface ToastState extends ToastOptions {
31
+ type: ToastType;
32
+ createdAt: number;
33
+ element?: HTMLElement;
34
+ }
@@ -0,0 +1,10 @@
1
+ export declare const icons: {
2
+ default: string;
3
+ primary: string;
4
+ secondary: string;
5
+ success: string;
6
+ warning: string;
7
+ danger: string;
8
+ loading: string;
9
+ close: string;
10
+ };
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "crisp-toast",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight, beautiful, and highly customizable vanilla JS toast notification library.",
5
+ "type": "module",
6
+ "main": "./dist/crisp-toast.umd.cjs",
7
+ "module": "./dist/crisp-toast.js",
8
+ "types": "./dist/index.d.ts",
9
+
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/crisp-toast.js",
13
+ "require": "./dist/crisp-toast.umd.cjs",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./style.css": "./dist/style.css"
17
+ },
18
+
19
+ "files": [
20
+ "dist"
21
+ ],
22
+
23
+ "sideEffects": [
24
+ "*.css"
25
+ ],
26
+
27
+ "scripts": {
28
+ "dev": "vite",
29
+ "build": "tsc && vite build",
30
+ "preview": "vite preview"
31
+ },
32
+
33
+ "keywords": [
34
+ "toast",
35
+ "toast-notification",
36
+ "notification",
37
+ "javascript-toast",
38
+ "vanilla-js",
39
+ "alert",
40
+ "toast-library",
41
+ "ui-library",
42
+ "frontend",
43
+ "crisp-toast"
44
+ ],
45
+
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/your-username/crisp-toast.git"
49
+ },
50
+
51
+ "homepage": "https://github.com/your-username/crisp-toast#readme",
52
+
53
+ "bugs": {
54
+ "url": "https://github.com/your-username/crisp-toast/issues"
55
+ },
56
+
57
+ "engines": {
58
+ "node": ">=16"
59
+ },
60
+
61
+ "author": "Ahmad",
62
+ "license": "MIT",
63
+
64
+ "devDependencies": {
65
+ "@types/node": "^25.4.0",
66
+ "typescript": "^5.2.2",
67
+ "vite": "^5.0.0",
68
+ "vite-plugin-dts": "^3.7.0"
69
+ }
70
+ }