ypsilon-event-handler
Version:
A production-ready event handling system for web applications with memory leak prevention, and method chaining support
357 lines (311 loc) âĸ 12.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Methods Object Pattern - YpsilonEventHandler</title>
<meta name="description" content="Vue.js-like methods object pattern for clean handler organization">
<link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" type="text/css" href="../assets/main.css">
<style>
body {
font-family: system-ui, sans-serif;
margin: 0 auto;
padding: 2rem 0;
max-width: 800px;
background: #f5f5f5;
}
.demo-section {
margin: 2rem 0;
padding: 1.5rem;
border: 1px solid #e0e0e0;
border-radius: 6px;
background: #fafafa;
}
.demo-section h3 {
margin-top: 0;
color: #2c3e50;
border-bottom: 2px solid #27ae60;
padding-bottom: 0.5rem;
}
.test-button {
margin: 0.5rem;
padding: 0.7rem 1.2rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s;
background: #27ae60;
color: white;
}
.test-button:hover {
background: #229954;
transform: translateY(-2px);
}
.test-button:disabled {
background: #95a5a6 ;
cursor: not-allowed ;
transform: none ;
opacity: 0.6;
}
.code-block {
background: #2c3e50;
color: #ecf0f1;
padding: 1rem;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
margin: 1rem 0;
overflow-x: auto;
}
.output-log {
background: #34495e;
color: #ecf0f1;
padding: 1rem;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
max-height: 200px;
overflow-y: auto;
}
.highlight {
background: #f39c12;
padding: 2px 4px;
border-radius: 3px;
color: #2c3e50;
}
.benefits {
background: #e8f5e8;
border-left: 4px solid #27ae60;
padding: 1rem;
margin: 1rem 0;
}
.clear-btn {
background: #6c757d;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
float: right;
margin-bottom: 1rem;
}
.clear-btn:hover {
background: #5a6268;
}
.logs {
padding: 0;
position: sticky;
bottom: 0;
box-shadow: 0 2px 10px rgba(69, 3, 175, 0.6);
}
.logs h3 {
margin: 0;
padding: .5rem;
border: 0 none;
}
.logs .clear-btn {
position: absolute;
top: 5px;
right: 5px;
}
</style>
</head>
<body>
<header class="demo-container">
<h1><a href="./index.html" title="Back to index" style="text-decoration: none;">ÂĢ</a> Methods Object Pattern</h1>
<p>Vue.js-inspired methods object pattern for clean handler organization and better code separation.</p>
<div class="benefits">
<strong>đ¯ Benefits:</strong>
<ul>
<li><strong>Clean Separation:</strong> Handlers separate from class logic</li>
<li><strong>Modular Design:</strong> Easy to import/export handler collections</li>
<li><strong>Team Collaboration:</strong> Different developers can work on handlers vs. class setup</li>
<li><strong>Testing:</strong> Handlers can be tested independently</li>
<li><strong>Plugin System:</strong> Third-party handlers without class inheritance</li>
</ul>
</div>
</header>
<!-- Basic Example -->
<div class="demo-section">
<h3>đ Basic Usage</h3>
<p>Define handlers in a separate object and pass to constructor:</p>
<pre class="code-block">// 1. Define handlers object
const myHandlers = {
handleSave: (event, target) => {
console.log('Saving data...');
},
handleDelete: (event, target) => {
console.log('Deleting item...');
}
};
// 2. Pass to constructor
new YpsilonEventHandler({
body: ['click']
}, {}, {
<span class="highlight">methods: myHandlers</span>
});</pre>
<button class="test-button" data-action="handleSave">đž Save</button>
<button class="test-button" data-action="handleDelete">đī¸ Delete</button>
</div>
<!-- Advanced Example -->
<div class="demo-section">
<h3>đ§ Advanced Patterns</h3>
<p>Complex handlers with state management and async operations:</p>
<button class="test-button" data-action="handleFormSubmit">đ¤ Submit Form</button>
<button class="test-button" data-action="handleAsyncOperation">âŗ Async Operation</button>
<button class="test-button" data-action="handleDataValidation">â
Validate Data</button>
</div>
<!-- Module Integration -->
<div class="demo-section">
<h3>đ Module Integration</h3>
<p>Import handlers from separate modules for better organization:</p>
<pre class="code-block">// userHandlers.js
export const userHandlers = {
handleLogin: (event, target) => { /* login logic */ },
handleLogout: (event, target) => { /* logout logic */ }
};
// dataHandlers.js
export const dataHandlers = {
handleLoad: (event, target) => { /* load logic */ },
handleSave: (event, target) => { /* save logic */ }
};
// main.js
import { userHandlers } from './userHandlers.js';
import { dataHandlers } from './dataHandlers.js';
const allHandlers = { ...userHandlers, ...dataHandlers };
new YpsilonEventHandler({...}, {}, {
<span class="highlight">methods: allHandlers</span>
});</pre>
<button class="test-button" data-action="handleLogin">đ Login</button>
<button class="test-button" data-action="handleLogout">đĒ Logout</button>
<button class="test-button" data-action="handleLoad">đĨ Load Data</button>
</div>
<!-- Event Log -->
<div class="demo-section logs">
<h3>đ Event Log</h3>
<button class="clear-btn" onclick="clearLog()">Clear Log</button>
<div class="output-log" id="event-log">Handler events will appear here...</div>
</div>
<!-- Navigation -->
<div class="nav-container">
<nav class="main-nav">
<div>
<a href="../index.html" class="btn-primary">Start</a>
<a href="../basic-example.html" class="btn-secondary">Basic Example</a>
<a href="../reactive-y.html" class="btn-danger">Reactive Demo</a>
<a href="../single-listener-multiple-actions.html" class="btn-purple">Single Listener</a>
<a href="../spa.html" class="btn-success">SPA Demo</a>
<a href="../ai-reviews.html" class="btn-warning">AI Reviews</a>
<a href="https://github.com/eypsilon/YpsilonEventHandler" class="btn-dark">GitHub</a>
</div>
</nav>
</div>
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@1.5.0/ypsilon-event-handler.min.js"></script>
<script>
// Methods object with various handler patterns
const methodsHandlers = {
// Basic handlers
handleSave: (event, target) => {
logEvent('đž Save', 'Data saved successfully');
},
handleDelete: (event, target) => {
logEvent('đī¸ Delete', 'Item deleted');
},
// Advanced handlers
handleFormSubmit: async (event, target) => {
target.disabled = true;
target.textContent = 'đ¤ Validating...';
logEvent('đ¤ Form Submit', 'Validating form data...');
// Simulate form validation
setTimeout(() => {
logEvent('đ¤ Form Submit', 'Form submitted successfully');
target.disabled = false;
target.textContent = 'đ¤ Submit Form';
}, 1000);
},
handleAsyncOperation: async (event, target) => {
target.disabled = true;
target.textContent = 'âŗ Processing...';
logEvent('âŗ Async', 'Starting async operation...');
// Simulate async work
try {
await new Promise(resolve => setTimeout(resolve, 1500));
logEvent('âŗ Async', 'Async operation completed');
} catch (error) {
logEvent('âŗ Async', 'Async operation failed');
} finally {
target.disabled = false;
target.textContent = 'âŗ Async Operation';
}
},
handleDataValidation: (event, target) => {
const isValid = Math.random() > 0.5;
logEvent('â
Validation', isValid ? 'Data is valid' : 'Data validation failed');
},
// Module-style handlers
handleLogin: (event, target) => {
target.disabled = true;
target.textContent = 'đ Authenticating...';
logEvent('đ Login', 'User authentication started');
setTimeout(() => {
logEvent('đ Login', 'User logged in successfully');
target.disabled = false;
target.textContent = 'đ Login';
}, 800);
},
handleLogout: (event, target) => {
logEvent('đĒ Logout', 'User logged out');
},
handleLoad: (event, target) => {
target.disabled = true;
target.textContent = 'đĨ Loading...';
logEvent('đĨ Load', 'Loading data from server...');
setTimeout(() => {
logEvent('đĨ Load', 'Data loaded successfully');
target.disabled = false;
target.textContent = 'đĨ Load Data';
}, 1200);
}
};
// Handler class using methods object
class MethodsObjectDemo extends YpsilonEventHandler {
constructor() {
super({
body: ['click']
}, {}, {
methods: methodsHandlers // Pass methods object
});
}
handleClick(event, target) {
const action = target.dataset.action;
if (action && this.resolveHandler(action, 'click')) {
const handler = this.resolveHandler(action, 'click');
handler(event, target);
}
}
}
// Utility functions
function logEvent(source, message) {
const log = document.getElementById('event-log');
const timestamp = new Date().toLocaleTimeString();
const entry = `[${timestamp}] ${source}: ${message}`;
if (log.textContent === 'Handler events will appear here...') {
log.innerHTML = entry;
} else {
log.innerHTML += '<br>' + entry;
}
log.scrollTop = log.scrollHeight;
}
function clearLog() {
document.getElementById('event-log').textContent = 'Handler events will appear here...';
}
// Initialize the handler
const handler = new MethodsObjectDemo();
// Log initialization
logEvent('đ System', 'Methods Object Pattern initialized');
logEvent('âšī¸ Info', 'Click buttons to test different handler patterns');
</script>
</body>
</html>