8000 GitHub - PabloTheBlink/ScopeJS: Libreria sencilla para crear componentes reactivos en JavaScript vanilla.
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

PabloTheBlink/ScopeJS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

97 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ScopeJS v2.0.4 - El Framework que te va a enamorar 💜

Un framework JavaScript ligero y moderno para desarrollo basado en componentes. Diseñado para ser simple, eficiente y poderoso.

Versión Tamaño Licencia

🚀 Características Principales

  • ⚡ Renderizado Quirúrgico: Solo actualiza lo que realmente ha cambiado
  • 🧩 Componentes Inteligentes: Encapsulación completa con estado reactivo
  • 🔄 Data Binding Automático: Sincronización bidireccional sin configuración
  • 🚪 Modales Elegantes: Sistema de ventanas emergentes con animaciones
  • 🛣️ Router SPA: Navegación fluida para aplicaciones de una sola página
  • 🎨 CSS Scoped: Estilos encapsulados que no interfieren entre sí
  • 📱 Mobile-First: Optimizado para dispositivos móviles
  • 🪶 Ultraligero: Solo ~15KB minificado
  • 🔧 Zero Dependencies: Sin dependencias externas

📦 Instalación Súper Fácil

Método 1: Script Clásico (Copy & Paste)

<!-- En el <head> de tu HTML -->
<script src="https://unpkg.com/@pablotheblink/scopejs@2.0.4/js/ScopeJS.js"></script>

<script>
  // Las funciones están disponibles globalmente
  const MiComponente = ScopeJS.Component({
    controller: class {
      constructor() {
        this.mensaje = "¡Hola Mundo!";
      }
    },
    render() {
      return `<h1>${this.mensaje}</h1>`;
    },
  });

  // Renderizar cuando la página cargue
  window.addEventListener("load", () => {
    const container = document.getElementById("app");
    MiComponente.render(container);
  });
</script>

Método 2: ES6 Modules (Para los cool kids)

<!-- En el <head> de tu HTML -->
<script type="module">
  import { Component, Modal, Router } from "https://unpkg.com/@pablotheblink/scopejs@2.0.4/js/ScopeJS.js";

  const MiComponente = Component({
    controller: class {
      constructor() {
        this.mensaje = "¡Hola Mundo!";
      }
    },
    render() {
      return `<h1>${this.mensaje}</h1>`;
    },
  });

  // Renderizar cuando la página cargue
  window.addEventListener("load", () => {
    const container = document.getElementById("app");
    MiComponente.render(container);
  });
</script>

Método 3: NPM (Para proyectos serios)

npm install @pablotheblink/scopejs
import { Component, Modal, Router } from "@pablotheblink/scopejs";

🧩 Componentes Inteligentes

Los componentes en ScopeJS son como ladrillos LEGO inteligentes que encapsulan todo lo que necesitas:

Ejemplo Básico: Contador Interactivo

import { Component } from "@pablotheblink/scopejs";

const Counter = Component({
  // Controlador con lógica
  controller: class {
    constructor() {
      this.count = 0;
      this.step = 1;
    }

    increment() {
      this.count += this.step;
      this.apply(); // Re-renderizar
    }

    decrement() {
      this.count -= this.step;
      this.apply();
    }

    reset() {
      this.count = 0;
      this.apply();
    }
  },

  // Template HTML
  render() {
    return `
            <div class="counter-widget">
                <h3>Contador: ${this.count}</h3>
                <div class="controls">
                    <button >- ${this.step}</button>
                    <button >+ ${this.step}</button>
                </div>
                <button >Reset</button>
            </div>
        `;
  },

  // Estilos CSS scoped
  style: `
        padding: 1rem;
        border: 2px solid #9333ea;
        border-radius: 0.5rem;
        text-align: center;
        background: white;
    `,

  // Tag personalizado (opcional)
  tagName: "my-counter",
});

// Usar el componente
const container = document.getElementById("app");
Counter.render(container);

Ejemplo Avanzado: Lista de Tareas

const TodoList = Component({
  controller: class {
    constructor() {
      this.todos = [];
      this.newTodo = "";
      this.filter = "all";
    }

    addTodo() {
      if (this.newTodo.trim()) {
        this.todos.push({
          id: Date.now(),
          text: this.newTodo,
          completed: false,
        });
        this.newTodo = "";
        this.apply();
      }
    }

    toggleTodo(id) {
      const todo = this.todos.find((t) => t.id == id);
      if (todo) {
        todo.completed = !todo.completed;
        this.apply();
      }
    }

    deleteTodo(id) {
      this.todos = this.todos.filter((t) => t.id != id);
      this.apply();
    }

    get filteredTodos() {
      if (this.filter === "completed") {
        return this.todos.filter((t) => t.completed);
      }
      if (this.filter === "active") {
        return this.todos.filter((t) => !t.completed);
      }
      return this.todos;
    }
  },

  render() {
    return `
            <div class="todo-app">
                <h3>Lista de Tareas</h3>
                
                <div class="add-todo">
                    <input type="text" model="newTodo" 
                           placeholder="Nueva tarea..."
                           >
                    <button >Agregar</button>
                </div>
                
                <div class="filters">
                    <button  
                            class="${this.filter === "all" ? "active" : ""}">
                        Todas
                    </button>
                    <button  
                            class="${this.filter === "active" ? "active" : ""}">
                        Activas
                    </button>
                    <button  
                            class="${this.filter === "completed" ? "active" : ""}">
                        Completadas
                    </button>
                </div>
                
                <ul class="todo-list">
                    ${this.filteredTodos
                      .map(
                        (todo) => `
                        <li class="todo-item ${todo.completed ? "completed" : ""}">
                            <input type="checkbox" 
                                   ${todo.completed ? "checked" : ""} 
                                   pl-s1">${todo.id})">
                            <span>${todo.text}</span>
                            <button pl-s1">${todo.id})">❌</button>
                        </li>
                    `
                      )
                      .join("")}
                </ul>
            </div>
        `;
  },

  style: `
        padding: 1rem;
        border: 2px solid #9333ea;
        border-radius: 0.5rem;
        background: white;
        
        .active { background: #9333ea; color: white; }
        .completed { text-decoration: line-through; opacity: 0.6; }
    `,
});

🚪 Modales con Estilo

Crea ventanas emergentes que no molestan, sino que encantan:

import { Modal } from "@pablotheblink/scopejs";

function openUserModal() {
  Modal({
    controller: class {
      constructor() {
        this.message = "¡Hola desde el modal!";
        this.userName = "";
      }

      saveUser() {
        if (this.userName.trim()) {
          alert(`Usuario guardado: ${this.userName}`);
          this.close();
        }
      }
    },

    render() {
      return `
                <div class="p-6">
                    <h3 class="text-xl font-bold mb-4">${this.message}</h3>
                    <div class="mb-4">
                        <label class="block text-sm font-medium mb-2">
                            Nombre de usuario:
                        </label>
                        <input type="text" model="userName" 
                               class="w-full px-3 py-2 border rounded"
                               placeholder="Escribe tu nombre">
                    </div>
                    <div class="flex gap-2">
                        <button  
                                class="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700">
                            Guardar
                        </button>
                        <button  
                                class="bg-gray-300 text-gray-700 px-4 py-2 rounded hover:bg-gray-400">
                            Cancelar
                        </button>
                    </div>
                </div>
            `;
    },

    hideWhenClickOverlay: true, // Cerrar al hacer clic fuera
  });
}

🛣️ Router para SPAs

Sistema de navegación que funciona como debe:

import { Router, Component } from "@pablotheblink/scopejs";

// Definir las vistas/componentes
const HomePage = Component({
  controller: class {
    constructor() {
      this.title = "Página de Inicio";
      this.message = "¡Bienvenido a ScopeJS!";
    }
  },

  render() {
    return `
            <div class="p-6 text-center">
                <h1 class="text-3xl font-bold text-purple-800 mb-4">
                    ${this.title}
                </h1>
                <p class="text-gray-600">${this.message}</p>
                <a href="/usuario/123" class="text-purple-600 hover:underline">
                    Ver perfil de usuario
                </a>
            </div>
        `;
  },
});

const UserProfile = Component({
  controller: class {
    constructor() {
      this.userId = null;
      this.userName = "";
    }

    init(params) {
      this.userId = params.id;
      this.userName = `Usuario ${params.id}`;
      this.apply();
    }
  },

  render() {
    return `
            <div class="p-6">
                <h1 class="text-2xl font-bold text-purple-800 mb-4">
                    Perfil de ${this.userName}
                </h1>
                <p class="text-gray-600 mb-4">ID: ${this.userId}</p>
                <button  
                        class="bg-purple-600 text-white px-4 py-2 rounded">
                    Volver
                </button>
            </div>
        `;
  },
});

// Configurar el router
const AppRouter = Router([
  {
    path: "/",
    component: HomePage,
    alias: "inicio",
  },
  {
    path: "/usuario/:id",
    component: UserProfile,
    alias: "perfil-usuario",
    middleware: (params, next) => {
      // Validar que el ID sea un número
      if (isNaN(params.id)) {
        alert("ID de usuario inválido");
        return false;
      }
      next();
    },
  },
]);

// Renderizar el router en el DOM
const container = document.getElementById("app");
AppRouter.render(container);

// Navegación programática
AppRouter.navigate("/usuario/123");

// Escuchar cambios de ruta
AppRouter.listen((route, params) => {
  console.log("Navegando a:", route, "Parámetros:", params);
});

⚡ Renderizado Quirúrgico

ScopeJS no es de esos frameworks pesados que actualizan toda la página. Nosotros somos más listos: solo tocamos lo que realmente ha cambiado.

Cómo Funciona

  • Precisión ninja: Si cambias un texto, solo se actualiza ese texto
  • Eficiencia máxima: Si cambias una clase CSS, solo se modifica esa clase
  • Resultado: Animaciones fluidas y experiencia de usuario que enamora

Ejemplo de Actualización Eficiente

const Counter = Component({
  controller: class {
    constructor() {
      this.count = 0;
    }

    increment() {
      this.count++; // Solo este valor cambia
      this.apply(); // Solo se actualiza el número en pantalla
    }
  },

  render() {
    return `
            <div>
                <h1>Mi App Genial</h1>
                <p>Contador: ${this.count}</p>
                <button >+</button>
            </div>
        `;
  },
});

🔄 Data Binding Automático

Con el atributo model, los datos se sincronizan automáticamente:

const FormExample = Component({
  controller: class {
    constructor() {
      this.user = {
        name: "",
        email: "",
        age: "",
      };
    }

    onSubmit(e) {
      e.preventDefault();
      console.log("Usuario:", this.user);
    }
  },

  render() {
    return `
            <form >
                <input type="text" model="user.name" placeholder="Nombre">
                <input type="email" model="user.email" placeholder="Email">
                <input type="number" model="user.age" placeholder="Edad">
                
                <p>Hola ${this.user.name}! 👋</p>
                
                <button type="submit">Enviar</button>
            </form>
        `;
  },
});

🎯 Eventos Inteligentes

Manejo de eventos que funciona como esperas:

const EventExample = Component({
  controller: class {
    constructor() {
      this.message = "";
    }

    // Eventos sin parámetros (recibe el evento)
    onClick(event) {
      console.log(event.target);
    }

    // Eventos con parámetros
    onButtonClick(id, action, event) {
      console.log(id, action, event);
      this.message = `Botón ${id} - Acción: ${action}`;
      this.apply();
    }
  },

  render() {
    return `
            <div>
                <button >Clic Simple</button>
                <button >Clic con Parámetros</button>
                <p>${this.message}</p>
            </div>
        `;
  },
});

📚 API Reference

Component API

Propiedad Tipo Descripción
controller Class Clase controladora del componente
render Function Función que retorna el HTML del componente
style String CSS scoped para el componente
tagName String Nombre del elemento personalizado
postRender Function Callback ejecutado después del render
title String Título de la página
meta Array Meta tags para SEO

Modal API

Propiedad Tipo Descripción
controller Class Controlador del modal
render Function Función de renderizado
hideWhenClickOverlay Boolean Cerrar al hacer clic fuera
className String Clase CSS adicional
referrer Element Elemento de referencia para posición

Router API

Método Parámetros Descripción
navigate path, body Navegar a una ruta específica
render container Renderizar el router en un contenedor
listen callback Escuchar cambios de ruta
unlisten uuid Remover listener de cambios

Utilidades

Función Parámetros Descripción
enableDebugger boolean Activar/desactivar modo debug
apply - Re-renderizar componente
close ...args Cerrar modal (disponible en modales)

🎨 Funciones Adicionales

Animaciones Fade In

<img fadeIn src="mi-imagen-genial.jpg" />
<!-- Aparece suavemente cuando entra en el viewport -->

Carga Diferida de Imágenes (Lazy Loading)

<img lazy src="imagen-pesada.jpg" />
<!-- Solo se carga cuando realmente se necesita -->

Transiciones de Vista (View Transitions)

<!-- Page1 -->
<img id="image_1" src="foto1.jpg" />

<!-- Page2 -->
<img id="image_1" src="foto2.jpg" />
<!-- Transición automática entre páginas usando el mismo ID -->

🏗️ Estructura HTML Básica

<!DOCTYPE html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Mi App con ScopeJS</title>

    <!-- Cargar ScopeJS -->
    <script src="https://unpkg.com/@pablotheblink/scopejs@2.0.4/js/ScopeJS.js"></script>
  </head>
  <body>
    <div id="app">
      <!-- Tu aplicación se renderizará aquí -->
    </div>

    <script>
      // Tu código de la aplicación aquí
      const App = ScopeJS.Component({
        controller: class {
          constructor() {
            this.titulo = "Mi Primera App ScopeJS";
            this.contador = 0;
          }

          incrementar() {
            this.contador++;
            this.apply();
          }
        },

        render() {
          return `
          <div style="text-align: center; padding: 2rem;">
            <h1>${this.titulo}</h1>
            <p>Contador: ${this.contador}</p>
            <button >
              Incrementar
            </button>
          </div>
        `;
        },
      });

      // Inicializar cuando la página cargue
      window.addEventListener("load", () => {
        const container = document.getElementById("app");
        App.render(container);
      });
    </script>
  </body>
</html>

💡 Consejos de Rendimiento

  1. Usar apply() solo cuando sea necesario - Solo después de cambios de estado
  2. Componentes pequeños y enfocados - Un componente = una responsabilidad
  3. Aprovechar lazy loading para imágenes - <img lazy src="...">
  4. Usar fadeIn para animaciones - <div fadeIn>...</div>
  5. Estilos scoped por componente - Evita conflictos CSS
  6. Evitar manipulación DOM directa - Usar el sistema reactivo
  7. Validar datos antes de renderizar - Especialmente arrays/objetos nulos

❌ Errores Comunes a Evitar

  1. No llamar apply() después de cambiar el estado
  2. Manipular DOM directamente en lugar de usar el sistema reactivo
  3. No limpiar listeners en onDestroy
  4. Componentes demasiado grandes - Mejor dividir en subcomponentes
  5. No validar datos antes de renderizar
  6. Usar innerHTML directamente - Mejor usar el sistema de templates
  7. No gestionar errores en operaciones asíncronas

✅ Casos de Uso Ideales

  • SPAs pequeñas y medianas con Router
  • Dashboards interactivos con widgets reutilizables
  • Formularios complejos con validación
  • Aplicaciones CRUD básicas
  • Prototipos rápidos y MVPs
  • Landing pages dinámicas
  • Componentes web independientes
  • Aplicaciones de gestión simples

❌ Cuándo NO usar ScopeJS

  • Aplicaciones muy grandes (mejor React/Vue/Angular)
  • Necesidades de SSR críticas (server-side rendering)
  • Ecosistemas muy específicos (ej: React Native)
  • Equipos grandes que necesitan tooling avanzado
  • Aplicaciones con estado muy complejo (state management)
  • Necesidades de testing avanzado (unit testing frameworks)

📊 Compatibilidad

  • Chrome 60+
  • Firefox 55+
  • Safari 11+
  • Edge 79+
  • Navegadores móviles modernos
  • Internet Explorer (porque ya es hora de dejarlo ir)

Licencia 📄 (Lo legal y aburrido)

MIT License - Básicamente puedes hacer lo que quieras, pero no nos culpes si te vuelves adicto.

El Genio Detrás de la Locura 👨‍💻

Pablo Martínez - El tipo que pensó que el mundo necesitaba notificaciones más sexys.


ScopeJS v2.0.4 - El framework JavaScript que no te va a dar dolores de cabeza 😎

⬆ Volver arriba

About

Libreria sencilla para crear componentes reactivos en JavaScript vanilla.

Resources

License

Stars

Watchers

Forks

Packages

No packages published
0