v2.0.4
ScopeJS es ese amigo desarrollador que siempre quisiste tener: confiable, ligero y que nunca te complica la vida. Perfecto para cuando quieres crear algo genial sin dramas.
No necesitas ser un ninja del desarrollo. Con ScopeJS, en 2 minutos ya estás creando maravillas 🎯
Para los que prefieren lo sencillo: copia, pega y a volar 🚀
<!-- 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>
Para los que les gusta presumir con ES6 modules y bundlers 😎
<!-- 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>
Directo desde la nube, más rápido que tu café matutino:
Para los proyectos serios que merecen lo mejor:
Aquí tienes un ejemplo completo de cómo estructurar tu página HTML:
<!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>
<!-- Opción 1: Script normal -->
<script src="js/ScopeJS.js"></script>
<!-- Opción 2: ES6 Module -->
<!-- <script type="module" src="js/app.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 onclick="incrementar()">
Incrementar
</button>
</div>
`;
}
});
// Inicializar cuando la página cargue
window.addEventListener('load', () => {
const container = document.getElementById('app');
App.render(container);
});
</script>
</body>
</html>
Crea piezas de código que funcionan como un reloj suizo: precisas, elegantes y reutilizables. Porque la vida es muy corta para escribir código repetitivo 🕰️
Los componentes en ScopeJS son como ladrillos LEGO inteligentes que encapsulan todo lo que necesitas:
Botones, tarjetas, formularios... Todo lo que necesitas para impresionar.
Gráficos, métricas y paneles que hacen que los datos cobren vida.
Layouts complejos que se comunican entre sí como por arte de magia.
import { Component } from './js/ScopeJS.js';
// Definir el componente
const Counter = Component({
// Lógica del controlador
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 onclick="decrement()">-</button>
<button onclick="increment()">+</button>
</div>
<button onclick="reset()">Reset</button>
</div>
`;
},
// Estilos CSS scoped
style: `
padding: 1rem;
border: 2px solid #9333ea;
border-radius: 0.5rem;
text-align: center;
background: white;
`,
// Tag personalizado
tagName: 'my-counter'
});
// Usar el componente
const container = document.getElementById('app');
Counter.render(container);
import { Component } from './js/ScopeJS.js';
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 onclick="addTodo()">Agregar</button>
</div>
<div class="filters">
<button onclick="filter='all'; apply()"
class="${this.filter === 'all' ? 'active' : ''}">
Todas
</button>
<button onclick="filter='active'; apply()"
class="${this.filter === 'active' ? 'active' : ''}">
Activas
</button>
<button onclick="filter='completed'; apply()"
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' : ''}
onclick="toggleTodo(${todo.id})">
<span>${todo.text}</span>
<button onclick="deleteTodo(${todo.id})">❌</button>
</li>
`).join('')}
</ul>
</div>
`;
},
style: `
padding: 1rem;
border: 2px solid #9333ea;
border-radius: 0.5rem;
background: white;
`,
tagName: 'todo-list'
});
ScopeJS no es de esos frameworks pesados que actualizan toda la página cada vez que cambias una coma. Nosotros somos más listos: solo tocamos lo que realmente ha cambiado. Como un cirujano del DOM.
Precisión ninja: Si cambias un texto, solo se actualiza ese texto. Nada más.
Eficiencia máxima: Si cambias una clase CSS, solo se modifica esa clase. Punto.
✨ Resultado: Animaciones fluidas, formularios que no se resetean y una experiencia de usuario que enamora
Con el atributo model
los datos se sincronizan solos. ¡Magia pura!
Manejo de eventos que funciona como esperas, con parámetros y todo
Estilos que se portan bien y no molestan al resto
Re-renderizado eficiente solo cuando hace falta. Ni más, ni menos
Crea ventanas emergentes que no molestan, sino que encantan. Porque un modal feo es como una cita a ciegas mal planificada 💫
import { Modal } from './js/ScopeJS.js';
// Abrir modal simple
function openModal() {
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 onclick="saveUser()"
class="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700">
Guardar
</button>
<button onclick="close()"
class="bg-gray-300 text-gray-700 px-4 py-2 rounded hover:bg-gray-400">
Cancelar
</button>
</div>
</div>
`;
},
hideWhenClickOverlay: true
});
}
Haz clic para ver el modal en acción
Transiciones suaves que hacen que cada modal sea un pequeño espectáculo
Control total sobre todo: posición, comportamiento, estilo... Tú mandas
Se adaptan a cualquier pantalla como un camaleón digital
Sistema de navegación para SPAs que funciona como debe: sin dramas, sin bugs raros y con todas las funciones que necesitas. El router que siempre quisiste 🗺️
import { Router, Component } from './js/ScopeJS.js';
// 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 onclick="history.back()"
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: "/inicio",
component: HomePage,
alias: "home"
},
{
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();
}
},
{
path: "/productos",
alias: "catalogo-productos",
component: Component({
render() {
return `
<div class="p-6">
<h1 class="text-2xl font-bold text-purple-800 mb-4">
Catálogo de Productos
</h1>
<div class="grid grid-cols-2 gap-4">
<div class="border p-4 rounded">Producto 1</div>
<div class="border p-4 rounded">Producto 2</div>
</div>
</div>
`;
}
})
}
]);
// Renderizar el router en el DOM
const container = document.getElementById('app');
AppRouter.render(container);
// Navegación programática
function navigateToUser(userId) {
AppRouter.navigate(`/usuario/${userId}`);
}
function navigateToProducts() {
AppRouter.navigate('/productos');
}
// Escuchar cambios de ruta
AppRouter.listen((route, params) => {
console.log('Navegando a:', route, 'Parámetros:', params);
});
Selecciona una ruta para ver la navegación
Parámetros en las rutas que funcionan de verdad
Control de acceso y validación sin complicaciones
Navegación fluida sin recargas molestas
Cambios de vista que enamoran
Todo lo que necesitas saber para dominar ScopeJS como un ninja del código. Aquí está la documentación que realmente vas a leer 📚
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 |
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 |
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 |
Función | Parámetros | Descripción |
---|---|---|
enableDebugger | boolean | Activar/desactivar modo debug |
apply | - | Re-renderizar componente |
close | ...args | Cerrar modal (disponible en modales) |
Tu sandbox personal para experimentar con ScopeJS. Aquí puedes romper cosas, crear maravillas y aprender sin miedo. ¡Es tu momento de brillar! ⚡
¡Vamos! Escribe algo genial y verás cómo cobra vida aquí ✨
this.apply()
después de cambiar datosmodel
es tu mejor amigo para formulariosonclick="miMetodo()"
Desarrollador Full Stack con un poquito de locura 🤪 por crear herramientas que hagan que los usuarios digan "¡WOW!" Creé ScopeJS porque estaba cansado de frameworks pesados que convierten proyectos simples en monstruos complicados 🐉
Si este proyecto te ha robado el corazón ❤️ (y algunos cafés ☕), considera invitarme a una pizza 🍕 para seguir creando cositas geniales
Tu apoyo me ayuda a seguir desarrollando herramientas útiles y gratuitas para la comunidad