whisp-toast 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/README.md +194 -0
- package/dist/index.cjs +225 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +128 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# whisp-toast
|
|
2
|
+
|
|
3
|
+
Toasts ligeros para React / Next.js. Cero dependencias, API global — llamá `whisp.success('mensaje')` desde cualquier parte de tu código, sin hooks ni refs ni providers.
|
|
4
|
+
|
|
5
|
+
- 🪶 Sin dependencias externas
|
|
6
|
+
- 🌐 API global — funciona fuera de componentes React (handlers, fetch, utils)
|
|
7
|
+
- 🎨 Colores personalizables por CSS variables
|
|
8
|
+
- 🖼️ Íconos personalizables, por tipo o por toast individual
|
|
9
|
+
- 📍 6 posiciones en pantalla
|
|
10
|
+
- 🔒 Tipado con TypeScript
|
|
11
|
+
|
|
12
|
+
## Instalación
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install whisp-toast
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Uso básico
|
|
19
|
+
|
|
20
|
+
**1. Montá el componente `Whisper`** una sola vez, en tu layout raíz:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// app/layout.tsx
|
|
24
|
+
import { Whisper } from 'whisp-toast'
|
|
25
|
+
|
|
26
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
27
|
+
return (
|
|
28
|
+
<html lang="es">
|
|
29
|
+
<body>
|
|
30
|
+
{children}
|
|
31
|
+
<Whisper position="bottom-right" />
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**2. Llamá `whisp` desde cualquier componente cliente, handler, o función async:**
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
'use client'
|
|
42
|
+
import { whisp } from 'whisp-toast'
|
|
43
|
+
|
|
44
|
+
export default function Formulario() {
|
|
45
|
+
const handleSubmit = async () => {
|
|
46
|
+
try {
|
|
47
|
+
await fetch('/api/guardar', { method: 'POST' })
|
|
48
|
+
whisp.success('¡Guardado con éxito!')
|
|
49
|
+
} catch {
|
|
50
|
+
whisp.error('Algo salió mal')
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return <button onClick={handleSubmit}>Guardar</button>
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
No hace falta ningún import de estilos por separado — los estilos ya vienen incluidos al importar el componente.
|
|
59
|
+
|
|
60
|
+
## API
|
|
61
|
+
|
|
62
|
+
### `whisp(message, options?)`
|
|
63
|
+
|
|
64
|
+
Dispara un toast neutro (sin ícono por defecto).
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
whisp('Mensaje simple')
|
|
68
|
+
whisp('Con duración custom', { duration: 5000 })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Variantes por tipo
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
whisp.success('¡Guardado con éxito!')
|
|
75
|
+
whisp.error('Algo salió mal')
|
|
76
|
+
whisp.info('Tenés una notificación nueva')
|
|
77
|
+
whisp.alert('¡Atención urgente!')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Opciones
|
|
81
|
+
|
|
82
|
+
Cada función acepta un segundo argumento, que puede ser un número (duración en ms, para mantener compatibilidad) o un objeto de opciones:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
whisp.success('Listo', 5000) // duración directa
|
|
86
|
+
|
|
87
|
+
whisp.success('Listo', {
|
|
88
|
+
duration: 5000, // ms antes de cerrarse. 0 = no se cierra solo. Default: 3000
|
|
89
|
+
icon: '🎉', // ícono custom para este toast puntual
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Todos los toasts se pueden cerrar antes de tiempo haciendo click sobre ellos.
|
|
94
|
+
|
|
95
|
+
## Componente `<Whisper />`
|
|
96
|
+
|
|
97
|
+
Se monta una sola vez en el layout raíz de tu app.
|
|
98
|
+
|
|
99
|
+
| Prop | Valores | Default |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `position` | `'top-center'` \| `'top-left'` \| `'top-right'` \|`'bottom-right'` \| `'bottom-left'`\| `'bottom-center'` | `'top-center'` |
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<Whisper position="top-left" />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Personalizar íconos
|
|
108
|
+
|
|
109
|
+
### Ícono por defecto de un tipo
|
|
110
|
+
|
|
111
|
+
Cada tipo (`success`, `error`, `info`, `alert`) trae un ícono SVG incluido, sin dependencias externas.
|
|
112
|
+
|
|
113
|
+
### Ícono en un toast puntual
|
|
114
|
+
|
|
115
|
+
Sobrescribe el ícono por defecto solo para esa llamada. Acepta cualquier `ReactNode`: emoji, string, SVG propio, o un componente de ícono de tu librería favorita.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
whisp('Subiendo archivo...', { icon: '⏳' })
|
|
119
|
+
|
|
120
|
+
whisp.success('¡Lanzado!', {
|
|
121
|
+
icon: (
|
|
122
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
123
|
+
<path d="M18 6 7 17l-5-5" />
|
|
124
|
+
</svg>
|
|
125
|
+
),
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Sin ícono, aunque el tipo tenga uno por defecto
|
|
129
|
+
whisp.success('Sin ícono', { icon: null })
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Personalizar colores
|
|
133
|
+
|
|
134
|
+
whisp-toast expone CSS variables con valores por defecto (dark, estilo Sonner). Sobrescribilas donde quieras: `globals.css`, un `<style>` inline, o dentro de un selector específico.
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
:root {
|
|
138
|
+
--whisp-bg: #ffffff;
|
|
139
|
+
--whisp-text: #111111;
|
|
140
|
+
--whisp-border: rgba(0, 0, 0, 0.1);
|
|
141
|
+
--whisp-radius: 12px;
|
|
142
|
+
|
|
143
|
+
--whisp-success-bg: #10b981;
|
|
144
|
+
--whisp-success-color: #052e16;
|
|
145
|
+
|
|
146
|
+
--whisp-error-bg: #dc2626;
|
|
147
|
+
--whisp-error-color: #450a0a;
|
|
148
|
+
|
|
149
|
+
--whisp-info-bg: #6366f1;
|
|
150
|
+
--whisp-info-color: #1e1b4b;
|
|
151
|
+
|
|
152
|
+
--whisp-alert-bg: #eab308;
|
|
153
|
+
--whisp-alert-color: #451a03;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Si no definís nada, se usan los valores por defecto — cero configuración obligatoria.
|
|
158
|
+
|
|
159
|
+
### Tabla de variables
|
|
160
|
+
|
|
161
|
+
| Variable | Controla | Default |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| `--whisp-bg` | Fondo de la card | `#1a1a1a` |
|
|
164
|
+
| `--whisp-text` | Color del texto | `#f5f5f5` |
|
|
165
|
+
| `--whisp-border` | Borde de la card | `rgba(255,255,255,0.08)` |
|
|
166
|
+
| `--whisp-radius` | Radio de esquinas | `10px` |
|
|
167
|
+
| `--whisp-success-bg` / `--whisp-success-color` | Círculo del ícono de éxito | `#22c55e` / `#052e16` |
|
|
168
|
+
| `--whisp-error-bg` / `--whisp-error-color` | Círculo del ícono de error | `#ef4444` / `#450a0a` |
|
|
169
|
+
| `--whisp-info-bg` / `--whisp-info-color` | Círculo del ícono de info | `#3b82f6` / `#172554` |
|
|
170
|
+
| `--whisp-alert-bg` / `--whisp-alert-color` | Círculo del ícono de alerta | `#f59e0b` / `#451a03` |
|
|
171
|
+
|
|
172
|
+
### Ejemplo: dark/light mode automático
|
|
173
|
+
|
|
174
|
+
```css
|
|
175
|
+
@media (prefers-color-scheme: light) {
|
|
176
|
+
:root {
|
|
177
|
+
--whisp-bg: #ffffff;
|
|
178
|
+
--whisp-text: #111111;
|
|
179
|
+
--whisp-border: rgba(0, 0, 0, 0.08);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## TypeScript
|
|
185
|
+
|
|
186
|
+
Todos los tipos están exportados:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import type { WhispType, WhispItem, WhispOptions, WhisperProps } from 'whisp-toast'
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Licencia
|
|
193
|
+
|
|
194
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defProps = Object.defineProperties;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __export = (target, all) => {
|
|
25
|
+
for (var name in all)
|
|
26
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
|
|
38
|
+
// src/index.ts
|
|
39
|
+
var index_exports = {};
|
|
40
|
+
__export(index_exports, {
|
|
41
|
+
Whisper: () => Whisper,
|
|
42
|
+
whisp: () => whisp,
|
|
43
|
+
whispStore: () => whispStore
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
|
|
47
|
+
// src/store.ts
|
|
48
|
+
var whisps = [];
|
|
49
|
+
var listeners = [];
|
|
50
|
+
var idCounter = 0;
|
|
51
|
+
function emit() {
|
|
52
|
+
listeners.forEach((listener) => listener(whisps));
|
|
53
|
+
}
|
|
54
|
+
function normalizeOptions(options) {
|
|
55
|
+
if (typeof options === "number") return { duration: options };
|
|
56
|
+
return options != null ? options : {};
|
|
57
|
+
}
|
|
58
|
+
function addWhisp(message, type = "default", options) {
|
|
59
|
+
const { duration = 3e3, icon: icon2 } = normalizeOptions(options);
|
|
60
|
+
const id = idCounter++;
|
|
61
|
+
whisps = [{ id, message, type, icon: icon2 }];
|
|
62
|
+
emit();
|
|
63
|
+
if (duration > 0) {
|
|
64
|
+
setTimeout(() => removeWhisp(id), duration);
|
|
65
|
+
}
|
|
66
|
+
return id;
|
|
67
|
+
}
|
|
68
|
+
function removeWhisp(id) {
|
|
69
|
+
const whisp2 = whisps.find((w) => w.id === id);
|
|
70
|
+
if (!whisp2 || whisp2.closing) return;
|
|
71
|
+
whisps = whisps.map(
|
|
72
|
+
(w) => w.id === id ? __spreadProps(__spreadValues({}, w), { closing: true }) : w
|
|
73
|
+
);
|
|
74
|
+
emit();
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
whisps = whisps.filter((w) => w.id !== id);
|
|
77
|
+
emit();
|
|
78
|
+
}, 250);
|
|
79
|
+
}
|
|
80
|
+
function subscribe(listener) {
|
|
81
|
+
listeners.push(listener);
|
|
82
|
+
listener(whisps);
|
|
83
|
+
return () => {
|
|
84
|
+
listeners = listeners.filter((l) => l !== listener);
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
var whisp = Object.assign(
|
|
88
|
+
(message, options) => addWhisp(message, "default", options),
|
|
89
|
+
{
|
|
90
|
+
success: (message, options) => addWhisp(message, "success", options),
|
|
91
|
+
error: (message, options) => addWhisp(message, "error", options),
|
|
92
|
+
info: (message, options) => addWhisp(message, "info", options),
|
|
93
|
+
alert: (message, options) => addWhisp(message, "alert", options)
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
var whispStore = { subscribe, removeWhisp };
|
|
97
|
+
|
|
98
|
+
// src/Whisper.tsx
|
|
99
|
+
var import_react = require("react");
|
|
100
|
+
|
|
101
|
+
// #style-inject:#style-inject
|
|
102
|
+
function styleInject(css, { insertAt } = {}) {
|
|
103
|
+
if (!css || typeof document === "undefined") return;
|
|
104
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
105
|
+
const style = document.createElement("style");
|
|
106
|
+
style.type = "text/css";
|
|
107
|
+
if (insertAt === "top") {
|
|
108
|
+
if (head.firstChild) {
|
|
109
|
+
head.insertBefore(style, head.firstChild);
|
|
110
|
+
} else {
|
|
111
|
+
head.appendChild(style);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
head.appendChild(style);
|
|
115
|
+
}
|
|
116
|
+
if (style.styleSheet) {
|
|
117
|
+
style.styleSheet.cssText = css;
|
|
118
|
+
} else {
|
|
119
|
+
style.appendChild(document.createTextNode(css));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/styles.css
|
|
124
|
+
styleInject(".whisp-container {\n position: fixed;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n z-index: 9999;\n pointer-events: none;\n}\n.whisp-container[data-vertical=top] {\n top: 1.5rem;\n}\n.whisp-container[data-vertical=bottom] {\n bottom: 1.5rem;\n flex-direction: column-reverse;\n}\n.whisp-container[data-horizontal=right] {\n right: 1.5rem;\n align-items: flex-end;\n}\n.whisp-container[data-horizontal=left] {\n left: 1.5rem;\n align-items: flex-start;\n}\n.whisp-container[data-horizontal=center] {\n left: 50%;\n transform: translateX(-50%);\n align-items: center;\n}\n.whisp-card {\n pointer-events: auto;\n display: flex;\n align-items: center;\n gap: 0.6rem;\n min-width: 280px;\n max-width: 356px;\n padding: 0.85rem 1rem;\n background: var(--whisp-bg, #1a1a1a);\n color: var(--whisp-text, #f5f5f5);\n border: 1px solid var(--whisp-border, rgba(255, 255, 255, 0.08));\n border-radius: var(--whisp-radius, 10px);\n font-size: 0.85rem;\n line-height: 1.3;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);\n cursor: pointer;\n animation: whisp-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\n}\n.whisp-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n font-size: 0.7rem;\n flex-shrink: 0;\n}\n.whisp-success .whisp-icon {\n background: var(--whisp-success-bg, #22c55e);\n color: var(--whisp-success-color, #052e16);\n}\n.whisp-error .whisp-icon {\n background: var(--whisp-error-bg, #ef4444);\n color: var(--whisp-error-color, #450a0a);\n}\n.whisp-info .whisp-icon {\n background: var(--whisp-info-bg, #3b82f6);\n color: var(--whisp-info-color, #172554);\n}\n.whisp-alert .whisp-icon {\n background: var(--whisp-alert-bg, #f59e0b);\n color: var(--whisp-alert-color, #451a03);\n}\n.whisp-card.whisp-out {\n animation: whisp-out 0.25s ease forwards;\n}\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\n animation-name: whisp-out-bottom;\n}\n@keyframes whisp-in {\n from {\n transform: translateY(16px) scale(0.95);\n opacity: 0;\n }\n to {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n}\n@keyframes whisp-out {\n from {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n to {\n transform: translateY(-16px) scale(0.95);\n opacity: 0;\n }\n}\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\n animation-name: whisp-out-bottom;\n}\n@keyframes whisp-out-bottom {\n from {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n to {\n transform: translateY(16px) scale(0.95);\n opacity: 0;\n }\n}\n");
|
|
125
|
+
|
|
126
|
+
// src/icons/icons.tsx
|
|
127
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
128
|
+
var icon = {
|
|
129
|
+
success: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
130
|
+
"svg",
|
|
131
|
+
{
|
|
132
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
133
|
+
width: "16",
|
|
134
|
+
height: "16",
|
|
135
|
+
viewBox: "0 0 24 24",
|
|
136
|
+
fill: "none",
|
|
137
|
+
stroke: "currentColor",
|
|
138
|
+
strokeWidth: "2",
|
|
139
|
+
strokeLinecap: "round",
|
|
140
|
+
strokeLinejoin: "round",
|
|
141
|
+
children: [
|
|
142
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m9 12 2 2 4-4" })
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
error: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m15 9-6 6" }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m9 9 6 6" })
|
|
151
|
+
] }),
|
|
152
|
+
info: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 16v-4" }),
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 8h.01" })
|
|
156
|
+
] }),
|
|
157
|
+
alert: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
158
|
+
"svg",
|
|
159
|
+
{
|
|
160
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
161
|
+
width: "16",
|
|
162
|
+
height: "16",
|
|
163
|
+
viewBox: "0 0 24 24",
|
|
164
|
+
fill: "none",
|
|
165
|
+
stroke: "currentColor",
|
|
166
|
+
strokeWidth: "2",
|
|
167
|
+
strokeLinecap: "round",
|
|
168
|
+
strokeLinejoin: "round",
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/Whisper.tsx
|
|
179
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
180
|
+
var defaultIcons = {
|
|
181
|
+
success: icon.success,
|
|
182
|
+
error: icon.error,
|
|
183
|
+
info: icon.info,
|
|
184
|
+
alert: icon.alert,
|
|
185
|
+
default: null
|
|
186
|
+
};
|
|
187
|
+
function Whisper({ position = "top-center" }) {
|
|
188
|
+
const [whisps2, setWhisps] = (0, import_react.useState)([]);
|
|
189
|
+
(0, import_react.useEffect)(() => {
|
|
190
|
+
const unsubscribe = whispStore.subscribe(setWhisps);
|
|
191
|
+
return unsubscribe;
|
|
192
|
+
}, []);
|
|
193
|
+
const [vertical, horizontal] = position.split("-");
|
|
194
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
195
|
+
"div",
|
|
196
|
+
{
|
|
197
|
+
className: "whisp-container",
|
|
198
|
+
"data-vertical": vertical,
|
|
199
|
+
"data-horizontal": horizontal,
|
|
200
|
+
children: whisps2.map((w) => {
|
|
201
|
+
var _a;
|
|
202
|
+
const iconToShow = (_a = w.icon) != null ? _a : defaultIcons[w.type];
|
|
203
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
className: `whisp-card whisp-${w.type} ${w.closing ? "whisp-out" : ""}`,
|
|
207
|
+
onClick: () => whispStore.removeWhisp(w.id),
|
|
208
|
+
children: [
|
|
209
|
+
iconToShow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "whisp-icon", children: iconToShow }),
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "whisp-message", children: w.message })
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
w.id
|
|
214
|
+
);
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
220
|
+
0 && (module.exports = {
|
|
221
|
+
Whisper,
|
|
222
|
+
whisp,
|
|
223
|
+
whispStore
|
|
224
|
+
});
|
|
225
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/Whisper.tsx","#style-inject:#style-inject","../src/styles.css","../src/icons/icons.tsx"],"sourcesContent":["export { whisp, whispStore } from './store'\nexport type { WhispItem, WhispType, WhispOptions } from './store'\nexport { Whisper } from './Whisper'\nexport type { WhisperProps } from './Whisper'\n","import type { ReactNode } from 'react'\n\nexport type WhispType = 'success' | 'error' | 'info' | 'alert' | 'default'\n\nexport interface WhispItem {\n id: number\n message: string\n type: WhispType\n icon?: ReactNode\n closing?: boolean\n}\n\nexport interface WhispOptions {\n duration?: number\n icon?: ReactNode\n}\n\ntype Listener = (whisps: WhispItem[]) => void\n\nlet whisps: WhispItem[] = []\nlet listeners: Listener[] = []\nlet idCounter = 0\n\nfunction emit() {\n listeners.forEach((listener) => listener(whisps))\n}\n\nfunction normalizeOptions(options?: number | WhispOptions): WhispOptions {\n if (typeof options === 'number') return { duration: options }\n return options ?? {}\n}\n\nfunction addWhisp(message: string, type: WhispType = 'default', options?: number | WhispOptions) {\n const { duration = 3000, icon } = normalizeOptions(options)\n const id = idCounter++\n whisps = [{ id, message, type, icon }]\n emit()\n\n if (duration > 0) {\n setTimeout(() => removeWhisp(id), duration)\n }\n\n return id\n}\n\nfunction removeWhisp(id: number) {\n const whisp = whisps.find(w => w.id === id)\n\n if (!whisp || whisp.closing) return\n\n whisps = whisps.map(w =>\n w.id === id ? { ...w, closing: true } : w\n )\n\n emit()\n\n setTimeout(() => {\n whisps = whisps.filter(w => w.id !== id)\n emit()\n }, 250)\n}\n\nfunction subscribe(listener: Listener) {\n listeners.push(listener)\n listener(whisps)\n return () => {\n listeners = listeners.filter((l) => l !== listener)\n }\n}\n\nexport const whisp = Object.assign(\n (message: string, options?: number | WhispOptions) => addWhisp(message, 'default', options),\n {\n success: (message: string, options?: number | WhispOptions) => addWhisp(message, 'success', options),\n error: (message: string, options?: number | WhispOptions) => addWhisp(message, 'error', options),\n info: (message: string, options?: number | WhispOptions) => addWhisp(message, 'info', options),\n alert: (message: string, options?: number | WhispOptions) => addWhisp(message, 'alert', options),\n }\n)\n\nexport const whispStore = { subscribe, removeWhisp }","'use client'\n\nimport { ReactNode, useEffect, useState } from 'react'\nimport { whispStore, WhispItem, WhispType } from './store'\nimport './styles.css'\nimport { icon } from './icons/icons'\n\nconst defaultIcons: Record<WhispType, ReactNode> = {\n success: icon.success,\n error: icon.error,\n info: icon.info,\n alert: icon.alert,\n default: null,\n}\n\nexport interface WhisperProps {\n position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center'\n}\n\nexport function Whisper({ position = 'top-center' }: WhisperProps) {\n const [whisps, setWhisps] = useState<WhispItem[]>([])\n\n useEffect(() => {\n const unsubscribe = whispStore.subscribe(setWhisps)\n return unsubscribe\n }, [])\n\n const [vertical, horizontal] = position.split('-')\n\n return (\n <div\n className=\"whisp-container\"\n data-vertical={vertical}\n data-horizontal={horizontal}\n >\n {whisps.map((w) => {\n const iconToShow = w.icon ?? defaultIcons[w.type]\n\n return (\n <div\n key={w.id}\n className={`whisp-card whisp-${w.type} ${w.closing ? 'whisp-out' : ''}`}\n onClick={() => whispStore.removeWhisp(w.id)}\n >\n {iconToShow && <span className=\"whisp-icon\">{iconToShow}</span>}\n <span className=\"whisp-message\">{w.message}</span>\n </div>\n )\n })}\n </div>\n )\n}","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".whisp-container {\\n position: fixed;\\n display: flex;\\n flex-direction: column;\\n gap: 0.5rem;\\n z-index: 9999;\\n pointer-events: none;\\n}\\n.whisp-container[data-vertical=top] {\\n top: 1.5rem;\\n}\\n.whisp-container[data-vertical=bottom] {\\n bottom: 1.5rem;\\n flex-direction: column-reverse;\\n}\\n.whisp-container[data-horizontal=right] {\\n right: 1.5rem;\\n align-items: flex-end;\\n}\\n.whisp-container[data-horizontal=left] {\\n left: 1.5rem;\\n align-items: flex-start;\\n}\\n.whisp-container[data-horizontal=center] {\\n left: 50%;\\n transform: translateX(-50%);\\n align-items: center;\\n}\\n.whisp-card {\\n pointer-events: auto;\\n display: flex;\\n align-items: center;\\n gap: 0.6rem;\\n min-width: 280px;\\n max-width: 356px;\\n padding: 0.85rem 1rem;\\n background: var(--whisp-bg, #1a1a1a);\\n color: var(--whisp-text, #f5f5f5);\\n border: 1px solid var(--whisp-border, rgba(255, 255, 255, 0.08));\\n border-radius: var(--whisp-radius, 10px);\\n font-size: 0.85rem;\\n line-height: 1.3;\\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);\\n cursor: pointer;\\n animation: whisp-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\\n}\\n.whisp-icon {\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n width: 18px;\\n height: 18px;\\n border-radius: 50%;\\n font-size: 0.7rem;\\n flex-shrink: 0;\\n}\\n.whisp-success .whisp-icon {\\n background: var(--whisp-success-bg, #22c55e);\\n color: var(--whisp-success-color, #052e16);\\n}\\n.whisp-error .whisp-icon {\\n background: var(--whisp-error-bg, #ef4444);\\n color: var(--whisp-error-color, #450a0a);\\n}\\n.whisp-info .whisp-icon {\\n background: var(--whisp-info-bg, #3b82f6);\\n color: var(--whisp-info-color, #172554);\\n}\\n.whisp-alert .whisp-icon {\\n background: var(--whisp-alert-bg, #f59e0b);\\n color: var(--whisp-alert-color, #451a03);\\n}\\n.whisp-card.whisp-out {\\n animation: whisp-out 0.25s ease forwards;\\n}\\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\\n animation-name: whisp-out-bottom;\\n}\\n@keyframes whisp-in {\\n from {\\n transform: translateY(16px) scale(0.95);\\n opacity: 0;\\n }\\n to {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n}\\n@keyframes whisp-out {\\n from {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n to {\\n transform: translateY(-16px) scale(0.95);\\n opacity: 0;\\n }\\n}\\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\\n animation-name: whisp-out-bottom;\\n}\\n@keyframes whisp-out-bottom {\\n from {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n to {\\n transform: translateY(16px) scale(0.95);\\n opacity: 0;\\n }\\n}\\n\")","export const icon = {\n success: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n ),\n error: (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m15 9-6 6\" />\n <path d=\"m9 9 6 6\" />\n </svg>\n ),\n info: (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n ),\n alert: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\" />\n </svg>\n ),\n}"],"mappings":"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","names":["icon","whisp","import_jsx_runtime","whisps"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type WhispType = 'success' | 'error' | 'info' | 'alert' | 'default';
|
|
5
|
+
interface WhispItem {
|
|
6
|
+
id: number;
|
|
7
|
+
message: string;
|
|
8
|
+
type: WhispType;
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
closing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface WhispOptions {
|
|
13
|
+
duration?: number;
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
type Listener = (whisps: WhispItem[]) => void;
|
|
17
|
+
declare function removeWhisp(id: number): void;
|
|
18
|
+
declare function subscribe(listener: Listener): () => void;
|
|
19
|
+
declare const whisp: ((message: string, options?: number | WhispOptions) => number) & {
|
|
20
|
+
success: (message: string, options?: number | WhispOptions) => number;
|
|
21
|
+
error: (message: string, options?: number | WhispOptions) => number;
|
|
22
|
+
info: (message: string, options?: number | WhispOptions) => number;
|
|
23
|
+
alert: (message: string, options?: number | WhispOptions) => number;
|
|
24
|
+
};
|
|
25
|
+
declare const whispStore: {
|
|
26
|
+
subscribe: typeof subscribe;
|
|
27
|
+
removeWhisp: typeof removeWhisp;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
interface WhisperProps {
|
|
31
|
+
position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
|
|
32
|
+
}
|
|
33
|
+
declare function Whisper({ position }: WhisperProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
export { type WhispItem, type WhispOptions, type WhispType, Whisper, type WhisperProps, whisp, whispStore };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type WhispType = 'success' | 'error' | 'info' | 'alert' | 'default';
|
|
5
|
+
interface WhispItem {
|
|
6
|
+
id: number;
|
|
7
|
+
message: string;
|
|
8
|
+
type: WhispType;
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
closing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface WhispOptions {
|
|
13
|
+
duration?: number;
|
|
14
|
+
icon?: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
type Listener = (whisps: WhispItem[]) => void;
|
|
17
|
+
declare function removeWhisp(id: number): void;
|
|
18
|
+
declare function subscribe(listener: Listener): () => void;
|
|
19
|
+
declare const whisp: ((message: string, options?: number | WhispOptions) => number) & {
|
|
20
|
+
success: (message: string, options?: number | WhispOptions) => number;
|
|
21
|
+
error: (message: string, options?: number | WhispOptions) => number;
|
|
22
|
+
info: (message: string, options?: number | WhispOptions) => number;
|
|
23
|
+
alert: (message: string, options?: number | WhispOptions) => number;
|
|
24
|
+
};
|
|
25
|
+
declare const whispStore: {
|
|
26
|
+
subscribe: typeof subscribe;
|
|
27
|
+
removeWhisp: typeof removeWhisp;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
interface WhisperProps {
|
|
31
|
+
position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
|
|
32
|
+
}
|
|
33
|
+
declare function Whisper({ position }: WhisperProps): react.JSX.Element;
|
|
34
|
+
|
|
35
|
+
export { type WhispItem, type WhispOptions, type WhispType, Whisper, type WhisperProps, whisp, whispStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
|
|
22
|
+
// src/store.ts
|
|
23
|
+
var whisps = [];
|
|
24
|
+
var listeners = [];
|
|
25
|
+
var idCounter = 0;
|
|
26
|
+
function emit() {
|
|
27
|
+
listeners.forEach((listener) => listener(whisps));
|
|
28
|
+
}
|
|
29
|
+
function normalizeOptions(options) {
|
|
30
|
+
if (typeof options === "number") return { duration: options };
|
|
31
|
+
return options != null ? options : {};
|
|
32
|
+
}
|
|
33
|
+
function addWhisp(message, type = "default", options) {
|
|
34
|
+
const { duration = 3e3, icon: icon2 } = normalizeOptions(options);
|
|
35
|
+
const id = idCounter++;
|
|
36
|
+
whisps = [{ id, message, type, icon: icon2 }];
|
|
37
|
+
emit();
|
|
38
|
+
if (duration > 0) {
|
|
39
|
+
setTimeout(() => removeWhisp(id), duration);
|
|
40
|
+
}
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
function removeWhisp(id) {
|
|
44
|
+
const whisp2 = whisps.find((w) => w.id === id);
|
|
45
|
+
if (!whisp2 || whisp2.closing) return;
|
|
46
|
+
whisps = whisps.map(
|
|
47
|
+
(w) => w.id === id ? __spreadProps(__spreadValues({}, w), { closing: true }) : w
|
|
48
|
+
);
|
|
49
|
+
emit();
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
whisps = whisps.filter((w) => w.id !== id);
|
|
52
|
+
emit();
|
|
53
|
+
}, 250);
|
|
54
|
+
}
|
|
55
|
+
function subscribe(listener) {
|
|
56
|
+
listeners.push(listener);
|
|
57
|
+
listener(whisps);
|
|
58
|
+
return () => {
|
|
59
|
+
listeners = listeners.filter((l) => l !== listener);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
var whisp = Object.assign(
|
|
63
|
+
(message, options) => addWhisp(message, "default", options),
|
|
64
|
+
{
|
|
65
|
+
success: (message, options) => addWhisp(message, "success", options),
|
|
66
|
+
error: (message, options) => addWhisp(message, "error", options),
|
|
67
|
+
info: (message, options) => addWhisp(message, "info", options),
|
|
68
|
+
alert: (message, options) => addWhisp(message, "alert", options)
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
var whispStore = { subscribe, removeWhisp };
|
|
72
|
+
|
|
73
|
+
// src/Whisper.tsx
|
|
74
|
+
import { useEffect, useState } from "react";
|
|
75
|
+
|
|
76
|
+
// #style-inject:#style-inject
|
|
77
|
+
function styleInject(css, { insertAt } = {}) {
|
|
78
|
+
if (!css || typeof document === "undefined") return;
|
|
79
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
80
|
+
const style = document.createElement("style");
|
|
81
|
+
style.type = "text/css";
|
|
82
|
+
if (insertAt === "top") {
|
|
83
|
+
if (head.firstChild) {
|
|
84
|
+
head.insertBefore(style, head.firstChild);
|
|
85
|
+
} else {
|
|
86
|
+
head.appendChild(style);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
head.appendChild(style);
|
|
90
|
+
}
|
|
91
|
+
if (style.styleSheet) {
|
|
92
|
+
style.styleSheet.cssText = css;
|
|
93
|
+
} else {
|
|
94
|
+
style.appendChild(document.createTextNode(css));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/styles.css
|
|
99
|
+
styleInject(".whisp-container {\n position: fixed;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n z-index: 9999;\n pointer-events: none;\n}\n.whisp-container[data-vertical=top] {\n top: 1.5rem;\n}\n.whisp-container[data-vertical=bottom] {\n bottom: 1.5rem;\n flex-direction: column-reverse;\n}\n.whisp-container[data-horizontal=right] {\n right: 1.5rem;\n align-items: flex-end;\n}\n.whisp-container[data-horizontal=left] {\n left: 1.5rem;\n align-items: flex-start;\n}\n.whisp-container[data-horizontal=center] {\n left: 50%;\n transform: translateX(-50%);\n align-items: center;\n}\n.whisp-card {\n pointer-events: auto;\n display: flex;\n align-items: center;\n gap: 0.6rem;\n min-width: 280px;\n max-width: 356px;\n padding: 0.85rem 1rem;\n background: var(--whisp-bg, #1a1a1a);\n color: var(--whisp-text, #f5f5f5);\n border: 1px solid var(--whisp-border, rgba(255, 255, 255, 0.08));\n border-radius: var(--whisp-radius, 10px);\n font-size: 0.85rem;\n line-height: 1.3;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);\n cursor: pointer;\n animation: whisp-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\n}\n.whisp-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n font-size: 0.7rem;\n flex-shrink: 0;\n}\n.whisp-success .whisp-icon {\n background: var(--whisp-success-bg, #22c55e);\n color: var(--whisp-success-color, #052e16);\n}\n.whisp-error .whisp-icon {\n background: var(--whisp-error-bg, #ef4444);\n color: var(--whisp-error-color, #450a0a);\n}\n.whisp-info .whisp-icon {\n background: var(--whisp-info-bg, #3b82f6);\n color: var(--whisp-info-color, #172554);\n}\n.whisp-alert .whisp-icon {\n background: var(--whisp-alert-bg, #f59e0b);\n color: var(--whisp-alert-color, #451a03);\n}\n.whisp-card.whisp-out {\n animation: whisp-out 0.25s ease forwards;\n}\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\n animation-name: whisp-out-bottom;\n}\n@keyframes whisp-in {\n from {\n transform: translateY(16px) scale(0.95);\n opacity: 0;\n }\n to {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n}\n@keyframes whisp-out {\n from {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n to {\n transform: translateY(-16px) scale(0.95);\n opacity: 0;\n }\n}\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\n animation-name: whisp-out-bottom;\n}\n@keyframes whisp-out-bottom {\n from {\n transform: translateY(0) scale(1);\n opacity: 1;\n }\n to {\n transform: translateY(16px) scale(0.95);\n opacity: 0;\n }\n}\n");
|
|
100
|
+
|
|
101
|
+
// src/icons/icons.tsx
|
|
102
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
103
|
+
var icon = {
|
|
104
|
+
success: /* @__PURE__ */ jsxs(
|
|
105
|
+
"svg",
|
|
106
|
+
{
|
|
107
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
108
|
+
width: "16",
|
|
109
|
+
height: "16",
|
|
110
|
+
viewBox: "0 0 24 24",
|
|
111
|
+
fill: "none",
|
|
112
|
+
stroke: "currentColor",
|
|
113
|
+
strokeWidth: "2",
|
|
114
|
+
strokeLinecap: "round",
|
|
115
|
+
strokeLinejoin: "round",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
118
|
+
/* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
error: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
123
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
124
|
+
/* @__PURE__ */ jsx("path", { d: "m15 9-6 6" }),
|
|
125
|
+
/* @__PURE__ */ jsx("path", { d: "m9 9 6 6" })
|
|
126
|
+
] }),
|
|
127
|
+
info: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
128
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
129
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
130
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
131
|
+
] }),
|
|
132
|
+
alert: /* @__PURE__ */ jsxs(
|
|
133
|
+
"svg",
|
|
134
|
+
{
|
|
135
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
136
|
+
width: "16",
|
|
137
|
+
height: "16",
|
|
138
|
+
viewBox: "0 0 24 24",
|
|
139
|
+
fill: "none",
|
|
140
|
+
stroke: "currentColor",
|
|
141
|
+
strokeWidth: "2",
|
|
142
|
+
strokeLinecap: "round",
|
|
143
|
+
strokeLinejoin: "round",
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
146
|
+
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "8", y2: "12" }),
|
|
147
|
+
/* @__PURE__ */ jsx("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/Whisper.tsx
|
|
154
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
155
|
+
var defaultIcons = {
|
|
156
|
+
success: icon.success,
|
|
157
|
+
error: icon.error,
|
|
158
|
+
info: icon.info,
|
|
159
|
+
alert: icon.alert,
|
|
160
|
+
default: null
|
|
161
|
+
};
|
|
162
|
+
function Whisper({ position = "top-center" }) {
|
|
163
|
+
const [whisps2, setWhisps] = useState([]);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
const unsubscribe = whispStore.subscribe(setWhisps);
|
|
166
|
+
return unsubscribe;
|
|
167
|
+
}, []);
|
|
168
|
+
const [vertical, horizontal] = position.split("-");
|
|
169
|
+
return /* @__PURE__ */ jsx2(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
className: "whisp-container",
|
|
173
|
+
"data-vertical": vertical,
|
|
174
|
+
"data-horizontal": horizontal,
|
|
175
|
+
children: whisps2.map((w) => {
|
|
176
|
+
var _a;
|
|
177
|
+
const iconToShow = (_a = w.icon) != null ? _a : defaultIcons[w.type];
|
|
178
|
+
return /* @__PURE__ */ jsxs2(
|
|
179
|
+
"div",
|
|
180
|
+
{
|
|
181
|
+
className: `whisp-card whisp-${w.type} ${w.closing ? "whisp-out" : ""}`,
|
|
182
|
+
onClick: () => whispStore.removeWhisp(w.id),
|
|
183
|
+
children: [
|
|
184
|
+
iconToShow && /* @__PURE__ */ jsx2("span", { className: "whisp-icon", children: iconToShow }),
|
|
185
|
+
/* @__PURE__ */ jsx2("span", { className: "whisp-message", children: w.message })
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
w.id
|
|
189
|
+
);
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
export {
|
|
195
|
+
Whisper,
|
|
196
|
+
whisp,
|
|
197
|
+
whispStore
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/Whisper.tsx","#style-inject:#style-inject","../src/styles.css","../src/icons/icons.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nexport type WhispType = 'success' | 'error' | 'info' | 'alert' | 'default'\n\nexport interface WhispItem {\n id: number\n message: string\n type: WhispType\n icon?: ReactNode\n closing?: boolean\n}\n\nexport interface WhispOptions {\n duration?: number\n icon?: ReactNode\n}\n\ntype Listener = (whisps: WhispItem[]) => void\n\nlet whisps: WhispItem[] = []\nlet listeners: Listener[] = []\nlet idCounter = 0\n\nfunction emit() {\n listeners.forEach((listener) => listener(whisps))\n}\n\nfunction normalizeOptions(options?: number | WhispOptions): WhispOptions {\n if (typeof options === 'number') return { duration: options }\n return options ?? {}\n}\n\nfunction addWhisp(message: string, type: WhispType = 'default', options?: number | WhispOptions) {\n const { duration = 3000, icon } = normalizeOptions(options)\n const id = idCounter++\n whisps = [{ id, message, type, icon }]\n emit()\n\n if (duration > 0) {\n setTimeout(() => removeWhisp(id), duration)\n }\n\n return id\n}\n\nfunction removeWhisp(id: number) {\n const whisp = whisps.find(w => w.id === id)\n\n if (!whisp || whisp.closing) return\n\n whisps = whisps.map(w =>\n w.id === id ? { ...w, closing: true } : w\n )\n\n emit()\n\n setTimeout(() => {\n whisps = whisps.filter(w => w.id !== id)\n emit()\n }, 250)\n}\n\nfunction subscribe(listener: Listener) {\n listeners.push(listener)\n listener(whisps)\n return () => {\n listeners = listeners.filter((l) => l !== listener)\n }\n}\n\nexport const whisp = Object.assign(\n (message: string, options?: number | WhispOptions) => addWhisp(message, 'default', options),\n {\n success: (message: string, options?: number | WhispOptions) => addWhisp(message, 'success', options),\n error: (message: string, options?: number | WhispOptions) => addWhisp(message, 'error', options),\n info: (message: string, options?: number | WhispOptions) => addWhisp(message, 'info', options),\n alert: (message: string, options?: number | WhispOptions) => addWhisp(message, 'alert', options),\n }\n)\n\nexport const whispStore = { subscribe, removeWhisp }","'use client'\n\nimport { ReactNode, useEffect, useState } from 'react'\nimport { whispStore, WhispItem, WhispType } from './store'\nimport './styles.css'\nimport { icon } from './icons/icons'\n\nconst defaultIcons: Record<WhispType, ReactNode> = {\n success: icon.success,\n error: icon.error,\n info: icon.info,\n alert: icon.alert,\n default: null,\n}\n\nexport interface WhisperProps {\n position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center'\n}\n\nexport function Whisper({ position = 'top-center' }: WhisperProps) {\n const [whisps, setWhisps] = useState<WhispItem[]>([])\n\n useEffect(() => {\n const unsubscribe = whispStore.subscribe(setWhisps)\n return unsubscribe\n }, [])\n\n const [vertical, horizontal] = position.split('-')\n\n return (\n <div\n className=\"whisp-container\"\n data-vertical={vertical}\n data-horizontal={horizontal}\n >\n {whisps.map((w) => {\n const iconToShow = w.icon ?? defaultIcons[w.type]\n\n return (\n <div\n key={w.id}\n className={`whisp-card whisp-${w.type} ${w.closing ? 'whisp-out' : ''}`}\n onClick={() => whispStore.removeWhisp(w.id)}\n >\n {iconToShow && <span className=\"whisp-icon\">{iconToShow}</span>}\n <span className=\"whisp-message\">{w.message}</span>\n </div>\n )\n })}\n </div>\n )\n}","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';styleInject(\".whisp-container {\\n position: fixed;\\n display: flex;\\n flex-direction: column;\\n gap: 0.5rem;\\n z-index: 9999;\\n pointer-events: none;\\n}\\n.whisp-container[data-vertical=top] {\\n top: 1.5rem;\\n}\\n.whisp-container[data-vertical=bottom] {\\n bottom: 1.5rem;\\n flex-direction: column-reverse;\\n}\\n.whisp-container[data-horizontal=right] {\\n right: 1.5rem;\\n align-items: flex-end;\\n}\\n.whisp-container[data-horizontal=left] {\\n left: 1.5rem;\\n align-items: flex-start;\\n}\\n.whisp-container[data-horizontal=center] {\\n left: 50%;\\n transform: translateX(-50%);\\n align-items: center;\\n}\\n.whisp-card {\\n pointer-events: auto;\\n display: flex;\\n align-items: center;\\n gap: 0.6rem;\\n min-width: 280px;\\n max-width: 356px;\\n padding: 0.85rem 1rem;\\n background: var(--whisp-bg, #1a1a1a);\\n color: var(--whisp-text, #f5f5f5);\\n border: 1px solid var(--whisp-border, rgba(255, 255, 255, 0.08));\\n border-radius: var(--whisp-radius, 10px);\\n font-size: 0.85rem;\\n line-height: 1.3;\\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);\\n cursor: pointer;\\n animation: whisp-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;\\n}\\n.whisp-icon {\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n width: 18px;\\n height: 18px;\\n border-radius: 50%;\\n font-size: 0.7rem;\\n flex-shrink: 0;\\n}\\n.whisp-success .whisp-icon {\\n background: var(--whisp-success-bg, #22c55e);\\n color: var(--whisp-success-color, #052e16);\\n}\\n.whisp-error .whisp-icon {\\n background: var(--whisp-error-bg, #ef4444);\\n color: var(--whisp-error-color, #450a0a);\\n}\\n.whisp-info .whisp-icon {\\n background: var(--whisp-info-bg, #3b82f6);\\n color: var(--whisp-info-color, #172554);\\n}\\n.whisp-alert .whisp-icon {\\n background: var(--whisp-alert-bg, #f59e0b);\\n color: var(--whisp-alert-color, #451a03);\\n}\\n.whisp-card.whisp-out {\\n animation: whisp-out 0.25s ease forwards;\\n}\\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\\n animation-name: whisp-out-bottom;\\n}\\n@keyframes whisp-in {\\n from {\\n transform: translateY(16px) scale(0.95);\\n opacity: 0;\\n }\\n to {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n}\\n@keyframes whisp-out {\\n from {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n to {\\n transform: translateY(-16px) scale(0.95);\\n opacity: 0;\\n }\\n}\\n.whisp-container[data-vertical=bottom] .whisp-card.whisp-out {\\n animation-name: whisp-out-bottom;\\n}\\n@keyframes whisp-out-bottom {\\n from {\\n transform: translateY(0) scale(1);\\n opacity: 1;\\n }\\n to {\\n transform: translateY(16px) scale(0.95);\\n opacity: 0;\\n }\\n}\\n\")","export const icon = {\n success: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </svg>\n ),\n error: (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m15 9-6 6\" />\n <path d=\"m9 9 6 6\" />\n </svg>\n ),\n info: (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n ),\n alert: (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\" />\n </svg>\n ),\n}"],"mappings":"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","names":["icon","whisp","jsx","jsxs","whisps"]}
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
.whisp-container {
|
|
2
|
+
position: fixed;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: 0.5rem;
|
|
6
|
+
z-index: 9999;
|
|
7
|
+
pointer-events: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.whisp-container[data-vertical='top'] {
|
|
11
|
+
top: 1.5rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.whisp-container[data-vertical='bottom'] {
|
|
15
|
+
bottom: 1.5rem;
|
|
16
|
+
flex-direction: column-reverse;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.whisp-container[data-horizontal='right'] {
|
|
20
|
+
right: 1.5rem;
|
|
21
|
+
align-items: flex-end;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.whisp-container[data-horizontal='left'] {
|
|
25
|
+
left: 1.5rem;
|
|
26
|
+
align-items: flex-start;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.whisp-container[data-horizontal='center'] {
|
|
30
|
+
left: 50%;
|
|
31
|
+
transform: translateX(-50%);
|
|
32
|
+
align-items: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.whisp-card {
|
|
36
|
+
pointer-events: auto;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 0.6rem;
|
|
40
|
+
min-width: 280px;
|
|
41
|
+
max-width: 356px;
|
|
42
|
+
padding: 0.85rem 1rem;
|
|
43
|
+
background: var(--whisp-bg, #1a1a1a);
|
|
44
|
+
color: var(--whisp-text, #f5f5f5);
|
|
45
|
+
border: 1px solid var(--whisp-border, rgba(255, 255, 255, 0.08));
|
|
46
|
+
border-radius: var(--whisp-radius, 10px);
|
|
47
|
+
font-size: 0.85rem;
|
|
48
|
+
line-height: 1.3;
|
|
49
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
animation: whisp-in 0.3s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.whisp-icon {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
width: 18px;
|
|
59
|
+
height: 18px;
|
|
60
|
+
border-radius: 50%;
|
|
61
|
+
font-size: 0.7rem;
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.whisp-success .whisp-icon {
|
|
66
|
+
background: var(--whisp-success-bg, #22c55e);
|
|
67
|
+
color: var(--whisp-success-color, #052e16);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.whisp-error .whisp-icon {
|
|
71
|
+
background: var(--whisp-error-bg, #ef4444);
|
|
72
|
+
color: var(--whisp-error-color, #450a0a);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.whisp-info .whisp-icon {
|
|
76
|
+
background: var(--whisp-info-bg, #3b82f6);
|
|
77
|
+
color: var(--whisp-info-color, #172554);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.whisp-alert .whisp-icon {
|
|
81
|
+
background: var(--whisp-alert-bg, #f59e0b);
|
|
82
|
+
color: var(--whisp-alert-color, #451a03);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.whisp-card.whisp-out {
|
|
86
|
+
animation: whisp-out 0.25s ease forwards;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.whisp-container[data-vertical='bottom'] .whisp-card.whisp-out {
|
|
90
|
+
animation-name: whisp-out-bottom;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@keyframes whisp-in {
|
|
94
|
+
from {
|
|
95
|
+
transform: translateY(16px) scale(0.95);
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
to {
|
|
99
|
+
transform: translateY(0) scale(1);
|
|
100
|
+
opacity: 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@keyframes whisp-out {
|
|
105
|
+
from {
|
|
106
|
+
transform: translateY(0) scale(1);
|
|
107
|
+
opacity: 1;
|
|
108
|
+
}
|
|
109
|
+
to {
|
|
110
|
+
transform: translateY(-16px) scale(0.95);
|
|
111
|
+
opacity: 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.whisp-container[data-vertical='bottom'] .whisp-card.whisp-out {
|
|
116
|
+
animation-name: whisp-out-bottom;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@keyframes whisp-out-bottom {
|
|
120
|
+
from {
|
|
121
|
+
transform: translateY(0) scale(1);
|
|
122
|
+
opacity: 1;
|
|
123
|
+
}
|
|
124
|
+
to {
|
|
125
|
+
transform: translateY(16px) scale(0.95);
|
|
126
|
+
opacity: 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "whisp-toast",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Librería ligera de toasts para React/Next.js, sin dependencias, con API global estilo toast.success().",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"react",
|
|
26
|
+
"nextjs",
|
|
27
|
+
"toast",
|
|
28
|
+
"notifications",
|
|
29
|
+
"notification",
|
|
30
|
+
"ui"
|
|
31
|
+
],
|
|
32
|
+
"author": "Alex Aperador Salcedo",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=18.0.0",
|
|
36
|
+
"react-dom": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/react": "^18.3.0",
|
|
40
|
+
"@types/react-dom": "^18.3.0",
|
|
41
|
+
"react": "^18.3.0",
|
|
42
|
+
"react-dom": "^18.3.0",
|
|
43
|
+
"tsup": "^8.0.0",
|
|
44
|
+
"typescript": "^5.5.0"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/alexaperador/whisp-toast#readme",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/alexaperador/whisp-toast.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/alexaperador/whisp-toast/issues"
|
|
53
|
+
}
|
|
54
|
+
}
|