├── CNAME ├── .npmignore ├── flashy.d.ts ├── package.json ├── README.md ├── CONTRIBUTING.md └── flashy.js /CNAME: -------------------------------------------------------------------------------- 1 | flashyjs.pablomsj.com -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | index.html 2 | .git 3 | .gitignore 4 | .DS_Store 5 | node_modules/ 6 | *.log 7 | .env 8 | .vscode/ 9 | .idea/ 10 | dist/ 11 | build/ 12 | coverage/ 13 | .nyc_output/ 14 | *.tgz 15 | *.tar.gz 16 | -------------------------------------------------------------------------------- /flashy.d.ts: -------------------------------------------------------------------------------- 1 | export interface FlashyOptions { 2 | type?: "success" | "error" | "warning" | "info" | "default"; 3 | position?: "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"; 4 | duration?: number; 5 | closable?: boolean; 6 | animation?: "slide" | "fade" | "bounce" | "zoom"; 7 | theme?: "light" | "dark"; 8 | icon?: string | null; 9 | onClick?: (() => void) | null; 10 | onClose?: (() => void) | null; 11 | } 12 | 13 | export interface FlashyInstance { 14 | (message: string, options?: FlashyOptions | string): () => void; 15 | success: (message: string, options?: Omit) => () => void; 16 | error: (message: string, options?: Omit) => () => void; 17 | warning: (message: string, options?: Omit) => () => void; 18 | info: (message: string, options?: Omit) => () => void; 19 | closeAll: () => void; 20 | setDefaults: (newDefaults: Partial) => void; 21 | getOptions: () => FlashyOptions; 22 | destroy: () => void; 23 | version: string; 24 | } 25 | 26 | declare const flashy: FlashyInstance; 27 | export default flashy; 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pablotheblink/flashyjs", 3 | "version": "1.0.5", 4 | "description": "Una librería moderna de notificaciones para JavaScript - ligera, elegante y fácil de usar", 5 | "main": "flashy.js", 6 | "module": "flashy.js", 7 | "browser": "flashy.js", 8 | "types": "flashy.d.ts", 9 | "files": [ 10 | "flashy.js", 11 | "flashy.d.ts", 12 | "README.md" 13 | ], 14 | "scripts": { 15 | "test": "echo \"No tests specified\" && exit 0", 16 | "prepublishOnly": "echo \"Preparando para publicar...\"", 17 | "build": "echo \"Build completado\"" 18 | }, 19 | "keywords": [ 20 | "notifications", 21 | "toast", 22 | "alerts", 23 | "messages", 24 | "ui", 25 | "javascript", 26 | "flashy", 27 | "notificaciones", 28 | "frontend" 29 | ], 30 | "author": "Pablo Martínez", 31 | "license": "MIT", 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/pablotheblink/FlashyJS.git" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/pablotheblink/FlashyJS/issues" 38 | }, 39 | "homepage": "https://github.com/pablotheblink/FlashyJS#readme", 40 | "engines": { 41 | "node": ">=12.0.0" 42 | }, 43 | "browserslist": [ 44 | "> 1%", 45 | "last 2 versions", 46 | "not dead" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flashy.js 🔥💥 2 | 3 | ¿Cansado de notificaciones aburridas que parecen de los 90s? ¡Flashy.js llegó para revolucionar tu vida! 😎 4 | 5 | Una librería de notificaciones tan elegante que hasta tu ex se va a poner celosa. 💅✨ 6 | 7 | ## ¿Por qué Flashy.js es tan genial? 🤩 8 | 9 | - 🎨 Múltiples tipos de notificaciones (porque la vida no es solo success o error, ¿verdad?) 10 | - 🎯 6 posiciones diferentes (como un sniper de notificaciones) 11 | - 🎭 4 animaciones que harán que tus usuarios se enamoren 12 | - 🌓 Temas claro y oscuro (para todos los vampiros programadores) 13 | - 📱 Responsive como tú después del café 14 | - ⏱️ Barra de progreso que es más puntual que tu jefe 15 | - 🔄 Más personalizable que tu perfil de dating 16 | - 🎯 Callbacks para cuando quieras ser stalker de tus notificaciones 17 | - 🚀 Sin dependencias (independiente como deberías ser tú) 18 | 19 | ## Instalación 📦 (Más fácil que ligarse a alguien en una app) 20 | 21 | ### NPM (Para los pros) 22 | 23 | ```bash 24 | npm install @pablotheblink/flashyjs 25 | ``` 26 | 27 | ### Yarn (Para los hipsters) 28 | 29 | ```bash 30 | yarn add @pablotheblink/flashyjs 31 | ``` 32 | 33 | ### CDN (Para los que viven al límite) 34 | 35 | ```html 36 | 37 | ``` 38 | 39 | ## Uso Básico 🚀 (Tan simple que tu mascota podría hacerlo) 40 | 41 | ### ES Modules (Moderno y sexy) 42 | 43 | ```javascript 44 | import flashy from "@pablotheblink/flashyjs"; 45 | 46 | flashy("¡Hola Mundo Cruel!"); // Con actitud 😈 47 | ``` 48 | 49 | ### CommonJS (Clásico pero efectivo) 50 | 51 | ```javascript 52 | const flashy = require("@pablotheblink/flashyjs"); 53 | 54 | flashy("¡Saludos terrícolas!"); // Feeling alien 👽 55 | ``` 56 | 57 | ### Script Global (Old school pero con estilo) 58 | 59 | ```html 60 | 61 | 64 | ``` 65 | 66 | ## Tipos de Notificaciones 🎨 (Para todas las personalidades) 67 | 68 | ```javascript 69 | // Notificación básica (para los tímidos) 70 | flashy("Mensaje básico... o no tan básico 🤫"); 71 | 72 | // Notificación de éxito (celebra como si fuera viernes) 73 | flashy.success("¡Lo lograste, crack! 🎉"); 74 | 75 | // Notificación de error (la vida no siempre es color de rosa) 76 | flashy.error("¡Ups! Alguien la regó 💩"); 77 | 78 | // Notificación de advertencia (tu conciencia hablando) 79 | flashy.warning("¡Ojo al piojo! 👁️"); 80 | 81 | // Notificación informativa (el sabelotodo del grupo) 82 | flashy.info("Dato curioso que nadie pidió 🤓"); 83 | ``` 84 | 85 | ## Configuración Avanzada ⚙️ (Para los perfeccionistas obsesivos) 86 | 87 | ```javascript 88 | flashy("Soy una notificación con actitud", { 89 | type: "success", // Porque merezco celebrar 90 | position: "top-right", // La esquina VIP 91 | duration: 4000, // 4 segundos de gloria 92 | closable: true, // Por si te aburres de mí 93 | animation: "slide", // Entrada triunfal 94 | theme: "dark", // Porque soy misterioso 95 | icon: "🔥", // Calentito como mi personalidad 96 | onClick: () => console.log("¡Me tocaste! 😏"), // Interacción picante 97 | onClose: () => console.log("Adiós cruel mundo 😢"), // Drama hasta el final 98 | }); 99 | ``` 100 | 101 | ## Configuración Global 🌍 (Porque eres el jefe de tu código) 102 | 103 | ```javascript 104 | // Establece las reglas del juego 105 | flashy.setDefaults({ 106 | duration: 5000, // 5 segundos de fama 107 | position: "bottom-right", // Mi lugar favorito 108 | theme: "dark", // Lado oscuro activado 109 | }); 110 | 111 | // Espía las opciones actuales (stalker mode) 112 | const options = flashy.getOptions(); 113 | ``` 114 | 115 | ## Métodos de Control 🛠️ (El poder en tus manos) 116 | 117 | ```javascript 118 | // Silencia a todas las notificaciones (dictador mode) 119 | flashy.closeAll(); 120 | 121 | // Destruye todo y desaparece sin dejar rastro (ninja mode) 122 | flashy.destroy(); 123 | ``` 124 | 125 | ## Personalización de Estilos 🎨 (Hazlo tuyo, tigre) 126 | 127 | ```css 128 | .flashy-notification { 129 | /* Aquí va tu creatividad desbordante */ 130 | /* Haz que brillen más que tu futuro */ 131 | } 132 | ``` 133 | 134 | ## Soporte Móvil 📱 (Porque también los smartphones merecen amor) 135 | 136 | Nuestras notificaciones son tan responsivas que se adaptan mejor que tú a los cambios de la vida. 💪 137 | 138 | ## Enlaces Útiles 🔗 (Para los curiosos) 139 | 140 | - **NPM**: https://www.npmjs.com/package/@pablotheblink/flashyjs (Donde vive la magia) 141 | - **CDN**: https://unpkg.com/@pablotheblink/flashyjs@1.0.4/flashy.js (Acceso directo al paraíso) 142 | - **GitHub**: https://github.com/pablotheblink/FlashyJS (El laboratorio secreto) 143 | 144 | ## Licencia 📄 (Lo legal y aburrido) 145 | 146 | MIT License - Básicamente puedes hacer lo que quieras, pero no nos culpes si te vuelves adicto. 147 | 148 | ## El Genio Detrás de la Locura 👨‍💻 149 | 150 | Pablo Martínez - El tipo que pensó que el mundo necesitaba notificaciones más sexys. 151 | 152 | ## Versión Actual 📌 153 | 154 | 1.0.4 - "La que te va a enamorar" 💕 155 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guía de Contribución - Flashy.js 🤝 2 | 3 | ¡Ey, futuro héroe del código! 🦸‍♂️ ¿Así que quieres unirte a la banda de Flashy.js? ¡Perfecto! Este documento es tu mapa del tesoro para no perderte en el camino y convertirte en un/a contribuidor/a de leyenda. 4 | 5 | ## 📋 Tabla de Contenidos 6 | 7 | - [Código de Conducta (o "Cómo no ser un troll")](#código-de-conducta) 8 | - [Cómo Contribuir (Las mil y una formas de brillar)](#cómo-contribuir) 9 | - [Configuración del Entorno de Desarrollo (Preparando tu guarida)](#configuración-del-entorno-de-desarrollo) 10 | - [Proceso de Desarrollo (El arte de la programación ninja)](#proceso-de-desarrollo) 11 | - [Estándares de Código (Porque el caos no mola)](#estándares-de-código) 12 | - [Reportar Errores (Cazando bugs como un pro)](#reportar-errores) 13 | - [Sugerir Nuevas Funcionalidades (¡Suelta tu creatividad!)](#sugerir-nuevas-funcionalidades) 14 | - [Pull Requests (El momento de la verdad)](#pull-requests) 15 | - [Versionado (Números que importan)](#versionado) 16 | - [Licencia (Lo legal, pero en buena onda)](#licencia) 17 | 18 | ## 🤝 Código de Conducta 19 | 20 | ¡Ojo! Aquí no somos salvajes. Este proyecto tiene reglas (como cualquier buen juego). Al participar, prometemos no convertirnos en trolls de internet y mantener la buena vibra. Si alguien se porta mal, no dudes en chivatearlo... digo, reportarlo 😏. 21 | 22 | ### Nuestros Estándares (aka "Cómo ser genial") 23 | 24 | **Comportamientos que nos hacen sonreír:** 25 | 26 | - Hablar bonito (sí, incluso cuando el código no compila) 27 | - Respetar las ideas raras de otros (todas las ideas son raras hasta que funcionan) 28 | - Aceptar críticas sin ponerse dramático/a 29 | - Pensar en el bien común (somos una gran familia disfuncional) 30 | - Ser empático/a (recordar que detrás de cada commit hay un humano con sentimientos) 31 | 32 | ## 🚀 Cómo Contribuir (Las mil maneras de ser útil) 33 | 34 | ¿Quieres formar parte de la magia? Aquí tienes las opciones (elige tu propia aventura): 35 | 36 | ### 1. Reportar Errores 🐛 (El trabajo sucio pero necesario) 37 | 38 | - Usa nuestro [sistema de issues](https://github.com/pablotheblink/FlashyJS/issues) (no, no envíes palomas mensajeras) 39 | - Describe el problema como si se lo contaras a tu abuela 40 | - Incluye pasos para reproducir el error (sí, paso a paso, como una receta) 41 | - Comparte info de tu navegador (Chrome, Firefox, Internet Explorer del 2005... no, eso no) 42 | 43 | ### 2. Sugerir Mejoras 💡 (Sé el visionario que todos necesitamos) 44 | 45 | - Abre un issue para que charlemos de tu idea genial 46 | - Explica por qué el mundo necesita tu feature 47 | - Mantén los pies en la tierra (nada de "que cure el cáncer", por favor) 48 | 49 | ### 3. Escribir Código 💻 (Donde la magia sucede) 50 | 51 | - Arregla bugs (sé el exterminador de bichos) 52 | - Implementa features nuevas (el constructor de sueños) 53 | - Mejora la documentación (el salvador de almas perdidas) 54 | - Escribe tests (sí, lo sé, es aburrido, pero alguien tiene que hacerlo) 55 | 56 | ### 4. Mejorar Documentación 📚 (El héroe no reconocido) 57 | 58 | - Corrige esos typos que nos dan vergüenza ajena 59 | - Explica cosas como si fueras un profesor cool 60 | - Añade ejemplos que no den pereza leer 61 | - Traduce contenido (spreading the love worldwide) 62 | 63 | ## 🛠️ Configuración del Entorno de Desarrollo (Montando tu laboratorio) 64 | 65 | ### Prerrequisitos (Lo que necesitas para no morir en el intento) 66 | 67 | - Node.js (versión 14 o superior, porque vivimos en el siglo XXI) 68 | - npm o yarn (el que prefieras, no juzgamos) 69 | - Git (obvio, ¿no?) 70 | 71 | ### Configuración Inicial (El ritual de iniciación) 72 | 73 | 1. **Fork del repositorio** (Tu primera travesura) 74 | 75 | ```bash 76 | # Haz fork del repositorio en GitHub (botoncito de arriba a la derecha) 77 | # Luego clona tu fork (tu copia personal) 78 | git clone https://github.com/TU_USUARIO/FlashyJS.git 79 | cd FlashyJS 80 | ``` 81 | 82 | 2. **Configurar remotes** (Conectando los cables) 83 | 84 | ```bash 85 | git remote add upstream https://github.com/pablotheblink/FlashyJS.git 86 | git remote -v # Para verificar que no la cagaste 87 | ``` 88 | 89 | 3. **Instalar dependencias** (Alimentando a la bestia) 90 | ```bash 91 | npm install 92 | # o si eres team yarn 93 | yarn install 94 | ``` 95 | 96 | ### Estructura del Proyecto (El mapa del tesoro) 97 | 98 | ``` 99 | FlashyJS/ 100 | ├── flashy.js # El corazón del proyecto (tócalo con cariño) 101 | ├── flashy.d.ts # Para los fans de TypeScript 102 | ├── index.html # Documentación y ejemplos (la cara bonita) 103 | ├── package.json # La configuración sagrada 104 | ├── README.md # El manual de supervivencia 105 | ├── CONTRIBUTING.md # Este documento tan molón 106 | └── .git/ # La magia negra de Git 107 | ``` 108 | 109 | ## 🔄 Proceso de Desarrollo (El arte de no cagarla) 110 | 111 | ### 1. Crear una Rama (Divide y vencerás) 112 | 113 | ```bash 114 | # Asegúrate de estar en main y actualizado (limpieza mental) 115 | git checkout main 116 | git pull upstream main 117 | 118 | # Crea una nueva rama para tu obra maestra 119 | git checkout -b feature/mi-idea-genial 120 | # o para los arregladores de desastres 121 | git checkout -b fix/ese-bug-molesto 122 | ``` 123 | 124 | ### 2. Realizar Cambios (Donde la creatividad fluye) 125 | 126 | - Haz tus cambios sin miedo (pero con cerebro) 127 | - Mantén los cambios enfocados (no hagas un frankenstein) 128 | - Commits frecuentes con mensajes que no den vergüenza 129 | 130 | ### 3. Probar los Cambios (El momento de la verdad) 131 | 132 | ```bash 133 | # Abre index.html en tu navegador favorito 134 | # Prueba todo lo que tocaste (y lo que no también) 135 | # Asegúrate de que funciona en más de un navegador 136 | ``` 137 | 138 | ### 4. Documentar (Para las generaciones futuras) 139 | 140 | - Actualiza la documentación si tocaste algo importante 141 | - Añade ejemplos que no requieran un doctorado para entender 142 | - Comenta el código complejo (tu yo del futuro te lo agradecerá) 143 | 144 | ## 📝 Estándares de Código (Porque el caos no es cool) 145 | 146 | ### JavaScript (El lenguaje de los valientes) 147 | 148 | - **Estilo**: Mantén la consistencia (somos una familia, no un circo) 149 | - **ES6+**: Usa lo moderno (estamos en 2025, no en la edad de piedra) 150 | - **Comentarios**: Explica lo raro (y también lo obvio, por si acaso) 151 | - **Nombres**: Que sean descriptivos, no jeroglíficos 152 | 153 | ```javascript 154 | // ✅ Así mola 155 | function showNotification(message, options = {}) { 156 | const config = { 157 | type: "default", 158 | duration: 4000, 159 | ...options, 160 | }; 161 | // Aquí pasa la magia de mostrar notificaciones 162 | } 163 | 164 | // ❌ Así no, por favor 165 | function show(msg, opts) { 166 | // ¿Qué hace esto? Ni idea... 167 | } 168 | ``` 169 | 170 | ### Compatibilidad (Para que funcione hasta en la nevera) 171 | 172 | - **Navegadores**: Compatibles con navegadores modernos (IE6 no cuenta) 173 | - **Sin dependencias**: No añadas librerías a lo loco sin preguntar 174 | - **Tamaño**: Que sea ligero como una pluma (pero funcional como un martillo) 175 | 176 | ### Documentación (Para no volvernos locos) 177 | 178 | - **JSDoc**: Documenta las funciones importantes 179 | - **TypeScript**: Mantén el `.d.ts` actualizado 180 | - **Ejemplos**: Que sean fáciles de copiar y pegar 181 | 182 | ```javascript 183 | /** 184 | * Muestra una notificación molona en pantalla 185 | * @param {string} message - Lo que quieres decir al mundo 186 | * @param {Object} options - Las opciones para personalizar 187 | * @param {string} options.type - El tipo de notificación (success, error, etc.) 188 | * @param {number} options.duration - Cuánto tiempo vive la notificación 189 | * @returns {Function} Función para cerrar manualmente (por si te arrepientes) 190 | */ 191 | function flashy(message, options = {}) { 192 | // Aquí sucede la magia ✨ 193 | } 194 | ``` 195 | 196 | ## 🐛 Reportar Errores (Conviértete en cazador de bugs) 197 | 198 | ### Antes de Reportar (No seas impulsivo/a) 199 | 200 | 1. **Busca** si alguien ya se quejó del mismo problema 201 | 2. **Verifica** que tienes la última versión (no seas vintage) 202 | 3. **Reproduce** el error en un entorno limpio (no en tu caos personal) 203 | 204 | ### Formato del Reporte (Hazlo bonito) 205 | 206 | ```markdown 207 | **Descripción del Error** 208 | Cuenta la historia del bug como si fuera un crimen que hay que resolver. 209 | 210 | **Pasos para Reproducir** 211 | 212 | 1. Abrir la aplicación 213 | 2. Hacer clic en el botón mágico 214 | 3. Ver cómo todo se va al traste 215 | 216 | **Comportamiento Esperado** 217 | Lo que debería pasar en un mundo perfecto. 218 | 219 | **Capturas de Pantalla** 220 | Una imagen vale más que mil palabras (y mil líneas de código). 221 | 222 | **Información del Entorno:** 223 | 224 | - OS: [ej. macOS, Windows, Linux, TempleOS] 225 | - Navegador: [ej. Chrome, Firefox, Safari, Lynx] 226 | - Versión: [ej. la última, la de antes, la prehistórica] 227 | - Versión de Flashy.js: [ej. 1.0.4] 228 | 229 | **Contexto Adicional** 230 | Todo lo que se te ocurra que pueda ser útil. 231 | ``` 232 | 233 | ## 💡 Sugerir Nuevas Funcionalidades (Sé el visionario) 234 | 235 | ### Proceso de Sugerencia (No seas tímido/a) 236 | 237 | 1. **Verifica** que tu idea revolucionaria no existe ya 238 | 2. **Abre un issue** etiquetado como "enhancement" 239 | 3. **Describe** tu visión como si fueras Steve Jobs 240 | 4. **Discute** con la comunidad (democracia en acción) 241 | 242 | ### Formato de Sugerencia (Vende tu idea) 243 | 244 | ```markdown 245 | **¿Tu solicitud nace de una frustración?** 246 | Cuenta tu drama personal con el código. 247 | 248 | **Describe tu solución soñada** 249 | Pinta el mundo ideal donde tu feature existe. 250 | 251 | **Alternativas que consideraste** 252 | Demuestra que pensaste en más de una opción. 253 | 254 | **Contexto adicional** 255 | Todo lo que haga tu idea más irresistible. 256 | ``` 257 | 258 | ## 📤 Pull Requests (El momento de brillar) 259 | 260 | ### Antes de Enviar (Checklist del perfeccionista) 261 | 262 | - [ ] ¿Tu código no da vergüenza ajena? 263 | - [ ] ¿Lo probaste hasta el cansancio? 264 | - [ ] ¿Actualizaste la documentación? 265 | - [ ] ¿Tu mensaje de commit es decente? 266 | 267 | ### Proceso de PR (La ceremonia) 268 | 269 | 1. **Mantén tu rama actualizada** (Sincronización ninja) 270 | 271 | ```bash 272 | git checkout main 273 | git pull upstream main 274 | git checkout tu-rama-genial 275 | git rebase main # Magia de Git 276 | ``` 277 | 278 | 2. **Push** tus cambios (Momento de verdad) 279 | 280 | ```bash 281 | git push origin tu-rama-genial 282 | ``` 283 | 284 | 3. **Abre el PR** en GitHub 285 | - Título que no sea "fix stuff" 286 | - Descripción que inspire confianza 287 | - Conecta con issues relacionados 288 | 289 | ### Template de PR (Para quedar como un/a pro) 290 | 291 | ```markdown 292 | ## Descripción 293 | 294 | Aquí explicas qué hiciste sin tecnicismos innecesarios. 295 | 296 | ## Tipo de Cambio 297 | 298 | - [ ] Bug fix (arreglé algo roto) 299 | - [ ] Nueva funcionalidad (añadí magia nueva) 300 | - [ ] Breaking change (cambié algo que puede romper cosas) 301 | - [ ] Documentación (mejoré la información) 302 | 303 | ## ¿Cómo lo probaste? 304 | 305 | Describe tus experimentos científicos. 306 | 307 | ## Checklist del Perfeccionista: 308 | 309 | - [ ] Mi código no da pena 310 | - [ ] Me revisé a mí mismo (autocrítica constructiva) 311 | - [ ] Comenté las partes raras 312 | - [ ] Documenté lo que tocaba 313 | - [ ] No rompí nada (que sepa) 314 | 315 | ## Issues Relacionados 316 | 317 | Cierra #(número del issue) 318 | ``` 319 | 320 | ### Revisión de Código (El juicio final) 321 | 322 | - **Paciencia**: Las revisiones toman tiempo (no somos máquinas) 323 | - **Mente abierta**: El feedback es para mejorar, no para joder 324 | - **Acción**: Haz los cambios pedidos sin drama 325 | - **Diálogo**: Si no estás de acuerdo, argumenta con clase 326 | 327 | ## 🏷️ Versionado (Los números que importan) 328 | 329 | Seguimos [Semantic Versioning](https://semver.org/) (porque somos civilizados): 330 | 331 | - **MAJOR** (1.0.0 → 2.0.0): Cambios que rompen todo 332 | - **MINOR** (1.0.0 → 1.1.0): Nuevas funciones que no rompen nada 333 | - **PATCH** (1.0.0 → 1.0.4): Arreglos menores 334 | 335 | ### Changelog (El diario del proyecto) 336 | 337 | Mantenemos un CHANGELOG.md que cuenta: 338 | 339 | - Las novedades brillantes 340 | - Los cambios que rompen cosas 341 | - Lo que va a desaparecer 342 | - Los bugs exterminados 343 | - Las mejoras de seguridad 344 | 345 | ## 📋 Labels de Issues (Organización ninja) 346 | 347 | - `bug`: Algo está jodido 348 | - `enhancement`: Ideas geniales 349 | - `documentation`: Mejoras en la info 350 | - `good first issue`: Para principiantes valientes 351 | - `help wanted`: SOS, necesitamos ayuda 352 | - `question`: Dudas existenciales 353 | 354 | ## 🎯 Roadmap (El plan maestro) 355 | 356 | ### Próximas Versiones (Spoilers del futuro) 357 | 358 | **v1.1.0** 359 | 360 | - [ ] Notificaciones de progreso (porque la espera es dura) 361 | - [ ] Más personalización de estilos (para los creativos) 362 | - [ ] Accesibilidad mejorada (inclusión total) 363 | 364 | **v1.2.0** 365 | 366 | - [ ] Notificaciones persistentes (para los insistentes) 367 | - [ ] Sistema de colas (orden y concierto) 368 | - [ ] Temas adicionales (variedad es vida) 369 | 370 | **v2.0.0** 371 | 372 | - [ ] Reescritura en TypeScript (para los tipos estrictos) 373 | - [ ] Sistema de plugins (extensibilidad máxima) 374 | - [ ] Breaking changes (perdón de antemano) 375 | 376 | ## 🏆 Reconocimientos (Hall of Fame) 377 | 378 | Todos los contribuidores aparecen en: 379 | 380 | - El README.md (fama eterna) 381 | - Las release notes (créditos del episodio) 382 | - La página de documentación (museo virtual) 383 | 384 | ## 📞 Contacto (Para cuando necesites hablar) 385 | 386 | - **Issues**: [GitHub Issues](https://github.com/pablotheblink/FlashyJS/issues) 387 | - **Email**: pablo@ejemplo.com (respondo, lo prometo) 388 | - **Discusiones**: [GitHub Discussions](https://github.com/pablotheblink/FlashyJS/discussions) 389 | 390 | ## 📄 Licencia (Lo legal sin rollo) 391 | 392 | Al contribuir a Flashy.js, aceptas que tus contribuciones tendrán [Licencia MIT](LICENSE) (básicamente, libertad total). 393 | 394 | --- 395 | 396 | ## 🙏 ¡Gracias, héroe/heroína! 397 | 398 | Tu contribución hace que Flashy.js sea más genial cada día. Cada bug reportado, cada línea de código, cada coma corregida... todo suma para hacer este proyecto más épico. 399 | 400 | ¡Nos vemos en el código! 🎉✨ 401 | 402 | P.D.: Si llegaste hasta aquí leyendo, ya eres oficialmente parte de la élite. Welcome to the club! 🎭 403 | -------------------------------------------------------------------------------- /flashy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Flashy.js - Librería de Notificaciones 3 | * @version 1.0.4 4 | * @author Pablo Martínez 5 | * @license MIT 6 | * 7 | * Uso: 8 | * Script global: luego usar window.flashy() 9 | * ES Module: import flashy from './flashy.js' luego usar flashy() 10 | * CommonJS: const flashy = require('./flashy.js') luego usar flashy() 11 | */ 12 | 13 | (function (global, factory) { 14 | typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : ((global = typeof globalThis !== "undefined" ? globalThis : global || self), (global.flashy = factory())); 15 | })(this, function () { 16 | "use strict"; 17 | 18 | const defaults = { 19 | type: "default", 20 | position: "top-right", 21 | duration: 4000, 22 | closable: true, 23 | animation: "slide", 24 | theme: "light", 25 | icon: null, 26 | onClick: null, 27 | onClose: null, 28 | }; 29 | 30 | const styles = ` 31 | .flashy-container { 32 | position: fixed; 33 | z-index: 10000; 34 | pointer-events: none; 35 | } 36 | .flashy-container.top-left { top: 20px; left: 20px; } 37 | .flashy-container.top-center { top: 20px; left: 50%; transform: translateX(-50%); } 38 | .flashy-container.top-right { top: 20px; right: 20px; } 39 | .flashy-container.bottom-left { bottom: 20px; left: 20px; } 40 | .flashy-container.bottom-center { bottom: 20px; left: 50%; transform: translateX(-50%); } 41 | .flashy-container.bottom-right { bottom: 20px; right: 20px; } 42 | .flashy-notification { 43 | display: flex; 44 | align-items: center; 45 | min-width: 300px; 46 | max-width: 500px; 47 | margin: 8px 0; 48 | padding: 16px 20px; 49 | border-radius: 8px; 50 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 51 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 52 | font-size: 14px; 53 | line-height: 1.4; 54 | pointer-events: auto; 55 | cursor: pointer; 56 | border-left: 4px solid; 57 | position: relative; 58 | overflow: hidden; 59 | opacity: 0; 60 | } 61 | .flashy-notification:hover { 62 | transform: translateY(-2px); 63 | box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); 64 | transition: transform 0.3s ease, box-shadow 0.3s ease; 65 | } 66 | .flashy-notification.light { background: #ffffff; color: #333333; } 67 | .flashy-notification.dark { background: #2d3748; color: #e2e8f0; } 68 | .flashy-notification.success { border-left-color: #4CAF50; } 69 | .flashy-notification.error { border-left-color: #f44336; } 70 | .flashy-notification.warning { border-left-color: #ff9800; } 71 | .flashy-notification.info { border-left-color: #2196F3; } 72 | .flashy-notification.default { border-left-color: #607d8b; } 73 | .flashy-notification.success.light { background: #f1f8e9; } 74 | .flashy-notification.error.light { background: #ffebee; } 75 | .flashy-notification.warning.light { background: #fff3e0; } 76 | .flashy-notification.info.light { background: #e3f2fd; } 77 | .flashy-icon { font-size: 18px; margin-right: 12px; flex-shrink: 0; } 78 | .flashy-content { flex: 1; word-wrap: break-word; } 79 | .flashy-close { 80 | background: none; 81 | border: none; 82 | font-size: 18px; 83 | cursor: pointer; 84 | margin-left: 12px; 85 | opacity: 0.7; 86 | transition: opacity 0.2s; 87 | padding: 0; 88 | width: 20px; 89 | height: 20px; 90 | display: flex; 91 | align-items: center; 92 | justify-content: center; 93 | color: inherit; 94 | } 95 | .flashy-close:hover { opacity: 1; } 96 | .flashy-progress { 97 | position: absolute; 98 | bottom: 0; 99 | left: 0; 100 | height: 3px; 101 | background: rgba(0, 0, 0, 0.2); 102 | transition: width linear; 103 | } 104 | @keyframes flashy-slide-in-right { 105 | from { transform: translateX(100%); opacity: 0; } 106 | to { transform: translateX(0); opacity: 1; } 107 | } 108 | @keyframes flashy-slide-in-left { 109 | from { transform: translateX(-100%); opacity: 0; } 110 | to { transform: translateX(0); opacity: 1; } 111 | } 112 | @keyframes flashy-slide-in-top { 113 | from { transform: translateY(-100%); opacity: 0; } 114 | to { transform: translateY(0); opacity: 1; } 115 | } 116 | @keyframes flashy-slide-in-bottom { 117 | from { transform: translateY(100%); opacity: 0; } 118 | to { transform: translateY(0); opacity: 1; } 119 | } 120 | @keyframes flashy-fade-in { 121 | from { opacity: 0; } 122 | to { opacity: 1; } 123 | } 124 | @keyframes flashy-bounce-in { 125 | 0% { transform: scale(0.3); opacity: 0; } 126 | 50% { transform: scale(1.05); opacity: 0.8; } 127 | 70% { transform: scale(0.9); opacity: 0.9; } 128 | 100% { transform: scale(1); opacity: 1; } 129 | } 130 | @keyframes flashy-zoom-in { 131 | 0% { transform: scale(0); opacity: 0; } 132 | 50% { transform: scale(1.1); opacity: 0.8; } 133 | 100% { transform: scale(1); opacity: 1; } 134 | } 135 | @keyframes flashy-exit { 136 | from { opacity: 1; } 137 | to { opacity: 0; } 138 | } 139 | .flashy-notification.animate-slide.top-left { animation: flashy-slide-in-left 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 140 | .flashy-notification.animate-slide.top-center { animation: flashy-slide-in-top 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 141 | .flashy-notification.animate-slide.top-right { animation: flashy-slide-in-right 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 142 | .flashy-notification.animate-slide.bottom-left { animation: flashy-slide-in-left 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 143 | .flashy-notification.animate-slide.bottom-center { animation: flashy-slide-in-bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 144 | .flashy-notification.animate-slide.bottom-right { animation: flashy-slide-in-right 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 145 | .flashy-notification.animate-fade { animation: flashy-fade-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 146 | .flashy-notification.animate-bounce { animation: flashy-bounce-in 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 147 | .flashy-notification.animate-zoom { animation: flashy-zoom-in 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 148 | .flashy-notification.removing { animation: flashy-exit 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards; } 149 | @media (max-width: 768px) { 150 | .flashy-notification { 151 | min-width: 280px; 152 | max-width: calc(100vw - 40px); 153 | margin: 6px 0; 154 | padding: 12px 16px; 155 | font-size: 13px; 156 | } 157 | .flashy-container { 158 | left: 20px !important; 159 | right: 20px !important; 160 | transform: none !important; 161 | } 162 | .flashy-container.top-center, 163 | .flashy-container.bottom-center { 164 | left: 20px !important; 165 | right: 20px !important; 166 | } 167 | } 168 | `; 169 | 170 | const defaultIcons = { 171 | success: "✅", 172 | error: "❌", 173 | warning: "⚠️", 174 | info: "ℹ️", 175 | default: "💬", 176 | }; 177 | 178 | let stylesInjected = false; 179 | const containers = {}; 180 | 181 | function injectStyles() { 182 | if (stylesInjected || typeof document === "undefined") return; 183 | const styleSheet = document.createElement("style"); 184 | styleSheet.id = "flashy-styles"; 185 | styleSheet.textContent = styles; 186 | document.head.appendChild(styleSheet); 187 | stylesInjected = true; 188 | } 189 | 190 | function getContainer(position) { 191 | if (typeof document === "undefined") return null; 192 | if (!containers[position]) { 193 | containers[position] = document.createElement("div"); 194 | containers[position].className = `flashy-container ${position}`; 195 | document.body.appendChild(containers[position]); 196 | } 197 | return containers[position]; 198 | } 199 | 200 | function closeNotification(element, options) { 201 | if (!element || element.classList.contains("removing")) return; 202 | element.classList.add("removing"); 203 | if (options.onClose) { 204 | try { 205 | options.onClose(); 206 | } catch (e) { 207 | console.warn("Flashy: Error en callback onClose:", e); 208 | } 209 | } 210 | setTimeout(() => { 211 | if (element.parentNode) { 212 | element.parentNode.removeChild(element); 213 | } 214 | }, 300); 215 | } 216 | 217 | function flashy(message, options = {}) { 218 | if (typeof document === "undefined") { 219 | console.warn("Flashy: No se puede usar fuera del navegador"); 220 | return () => {}; 221 | } 222 | injectStyles(); 223 | if (!message || typeof message !== "string") { 224 | console.warn("Flashy: El mensaje debe ser una cadena de texto"); 225 | return () => {}; 226 | } 227 | if (typeof options === "string") { 228 | options = { type: options }; 229 | } 230 | const config = { ...defaults, ...options }; 231 | const validTypes = ["success", "error", "warning", "info", "default"]; 232 | const validPositions = ["top-left", "top-center", "top-right", "bottom-left", "bottom-center", "bottom-right"]; 233 | const validAnimations = ["slide", "fade", "bounce", "zoom"]; 234 | const validThemes = ["light", "dark"]; 235 | config.type = validTypes.includes(config.type) ? config.type : "default"; 236 | config.position = validPositions.includes(config.position) ? config.position : "top-right"; 237 | config.animation = validAnimations.includes(config.animation) ? config.animation : "slide"; 238 | config.theme = validThemes.includes(config.theme) ? config.theme : "light"; 239 | config.duration = config.duration < 0 ? 0 : config.duration; 240 | const notification = document.createElement("div"); 241 | notification.className = `flashy-notification ${config.type} ${config.theme} animate-${config.animation} ${config.position}`; 242 | let content = ""; 243 | const icon = config.icon || defaultIcons[config.type] || defaultIcons.default; 244 | if (icon) { 245 | content += `${icon}`; 246 | } 247 | const escapedMessage = message.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); 248 | content += `
${escapedMessage}
`; 249 | if (config.closable) { 250 | content += ``; 251 | } 252 | if (config.duration > 0) { 253 | content += `
`; 254 | } 255 | notification.innerHTML = content; 256 | const container = getContainer(config.position); 257 | if (!container) { 258 | console.warn("Flashy: No se pudo crear el contenedor"); 259 | return () => {}; 260 | } 261 | container.appendChild(notification); 262 | notification.offsetHeight; 263 | if (config.onClick && typeof config.onClick === "function") { 264 | notification.addEventListener("click", (e) => { 265 | if (!e.target.classList.contains("flashy-close")) { 266 | try { 267 | config.onClick(); 268 | } catch (e) { 269 | console.warn("Flashy: Error en callback onClick:", e); 270 | } 271 | } 272 | }); 273 | } 274 | if (config.closable) { 275 | const closeBtn = notification.querySelector(".flashy-close"); 276 | if (closeBtn) { 277 | closeBtn.addEventListener("click", (e) => { 278 | e.stopPropagation(); 279 | closeNotification(notification, config); 280 | }); 281 | } 282 | } 283 | 284 | // Variables para el control del temporizador y hover 285 | let timeoutId = null; 286 | let remainingTime = config.duration; 287 | let startTime = Date.now(); 288 | let isPaused = false; 289 | 290 | if (config.duration > 0) { 291 | const progressBar = notification.querySelector(".flashy-progress"); 292 | if (progressBar) { 293 | progressBar.style.width = "100%"; 294 | progressBar.style.transition = `width ${config.duration}ms linear`; 295 | setTimeout(() => { 296 | progressBar.style.width = "0%"; 297 | }, 10); 298 | } 299 | 300 | // Función para iniciar/reanudar el temporizador 301 | const startTimer = () => { 302 | if (remainingTime <= 0) return; 303 | 304 | startTime = Date.now(); 305 | timeoutId = setTimeout(() => { 306 | closeNotification(notification, config); 307 | }, remainingTime); 308 | }; 309 | 310 | // Función para pausar el temporizador 311 | const pauseTimer = () => { 312 | if (timeoutId) { 313 | clearTimeout(timeoutId); 314 | timeoutId = null; 315 | const elapsed = Date.now() - startTime; 316 | remainingTime = Math.max(0, remainingTime - elapsed); 317 | isPaused = true; 318 | 319 | // Pausar la barra de progreso 320 | if (progressBar) { 321 | progressBar.style.animationPlayState = "paused"; 322 | const currentWidth = parseFloat(getComputedStyle(progressBar).width); 323 | const containerWidth = parseFloat(getComputedStyle(progressBar.parentElement).width); 324 | const percentage = (currentWidth / containerWidth) * 100; 325 | progressBar.style.transition = "none"; 326 | progressBar.style.width = `${percentage}%`; 327 | } 328 | } 329 | }; 330 | 331 | // Función para reanudar el temporizador 332 | const resumeTimer = () => { 333 | if (isPaused && remainingTime > 0) { 334 | isPaused = false; 335 | 336 | // Reanudar la barra de progreso 337 | if (progressBar) { 338 | progressBar.style.transition = `width ${remainingTime}ms linear`; 339 | setTimeout(() => { 340 | progressBar.style.width = "0%"; 341 | }, 10); 342 | } 343 | 344 | startTimer(); 345 | } 346 | }; 347 | 348 | // Event listeners para hover 349 | notification.addEventListener("mouseenter", pauseTimer); 350 | notification.addEventListener("mouseleave", resumeTimer); 351 | 352 | // Iniciar el temporizador 353 | startTimer(); 354 | } 355 | 356 | return () => { 357 | if (timeoutId) { 358 | clearTimeout(timeoutId); 359 | } 360 | closeNotification(notification, config); 361 | }; 362 | } 363 | 364 | flashy.closeAll = function () { 365 | if (typeof document === "undefined") return; 366 | const notifications = document.querySelectorAll(".flashy-notification"); 367 | notifications.forEach((notification) => { 368 | closeNotification(notification, {}); 369 | }); 370 | }; 371 | 372 | flashy.setDefaults = function (newDefaults) { 373 | if (typeof newDefaults === "object" && newDefaults !== null) { 374 | Object.assign(defaults, newDefaults); 375 | } 376 | }; 377 | 378 | flashy.getOptions = function () { 379 | return { ...defaults }; 380 | }; 381 | 382 | flashy.destroy = function () { 383 | if (typeof document === "undefined") return; 384 | flashy.closeAll(); 385 | Object.values(containers).forEach((container) => { 386 | if (container.parentNode) { 387 | container.parentNode.removeChild(container); 388 | } 389 | }); 390 | Object.keys(containers).forEach((key) => { 391 | delete containers[key]; 392 | }); 393 | const styleSheet = document.getElementById("flashy-styles"); 394 | if (styleSheet) { 395 | styleSheet.parentNode.removeChild(styleSheet); 396 | } 397 | stylesInjected = false; 398 | }; 399 | 400 | flashy.success = (message, options = {}) => flashy(message, { ...options, type: "success" }); 401 | flashy.error = (message, options = {}) => flashy(message, { ...options, type: "error" }); 402 | flashy.warning = (message, options = {}) => flashy(message, { ...options, type: "warning" }); 403 | flashy.info = (message, options = {}) => flashy(message, { ...options, type: "info" }); 404 | 405 | flashy.version = "1.0.4"; 406 | 407 | return flashy; 408 | }); 409 | --------------------------------------------------------------------------------