Files
php-flasher/docs/_includes/flasher-studio.html
T
Younes ENNAJI 68ae6dc9cb Wip
2025-03-11 22:47:45 +00:00

805 lines
39 KiB
HTML

<!-- Elegant PHPFlasher Interactive Demo -->
<section class="min-h-screen relative overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100 py-8">
<!-- Dynamic background elements -->
<div class="absolute inset-0 z-0">
<div class="absolute top-0 right-0 w-1/3 h-1/3 bg-gradient-to-br from-blue-500/5 to-indigo-500/5 rounded-full blur-3xl"></div>
<div class="absolute bottom-0 left-0 w-1/3 h-1/3 bg-gradient-to-tr from-emerald-500/5 to-blue-500/5 rounded-full blur-3xl"></div>
<!-- Animated particles -->
<div id="particles-js" class="absolute inset-0 opacity-60"></div>
<!-- Animated lines -->
<svg class="absolute inset-0 w-full h-full z-0 opacity-10" viewBox="0 0 100 100" preserveAspectRatio="none">
<line x1="0" y1="0" x2="100" y2="100" stroke="currentColor" stroke-width="0.2" class="text-blue-500 animate-[pulse_8s_cubic-bezier(0.4,0,0.6,1)_infinite]"></line>
<line x1="100" y1="0" x2="0" y2="100" stroke="currentColor" stroke-width="0.2" class="text-indigo-500 animate-[pulse_6s_cubic-bezier(0.4,0,0.6,1)_infinite]"></line>
</svg>
</div>
<!-- Main content -->
<div class="container relative z-10 mx-auto px-4 max-w-7xl">
<!-- Elegant header -->
<header class="mb-8 text-center relative">
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-24 h-1 bg-gradient-to-r from-blue-500/0 via-blue-500 to-blue-500/0"></div>
<h1 class="mt-6 text-3xl font-light tracking-tight">
PHPFlasher <span class="font-semibold text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-indigo-600">Interactive</span> Studio
</h1>
<p class="mt-2 text-gray-500 max-w-2xl mx-auto">Design your perfect notification, customize options, and see it in action</p>
</header>
<!-- Main interactive area -->
<div class="rounded-xl shadow-lg bg-white overflow-hidden backdrop-blur-sm border border-gray-100">
<!-- Toolbar -->
<div class="bg-gradient-to-r from-gray-50 to-gray-100 border-b border-gray-200 px-6 py-2 flex items-center justify-between">
<div class="flex items-center space-x-2">
<div class="w-3 h-3 rounded-full bg-red-400"></div>
<div class="w-3 h-3 rounded-full bg-yellow-400"></div>
<div class="w-3 h-3 rounded-full bg-green-400"></div>
</div>
<div class="text-sm text-gray-500 font-medium">PHPFlasher Studio • Build ID: FL-20250311</div>
<div id="status-indicator" class="flex items-center space-x-2">
<span id="status-text" class="text-xs text-emerald-600">Ready</span>
<div class="w-2 h-2 bg-emerald-500 rounded-full animate-[ping_2.5s_cubic-bezier(0,0,0.2,1)_infinite]"></div>
</div>
</div>
<!-- Main content area with tabs -->
<div class="flex flex-col lg:flex-row">
<!-- Left panel: Options -->
<div class="w-full lg:w-2/5 border-r border-gray-100">
<!-- Options panel -->
<div class="p-4 overflow-y-auto" style="height: calc(100vh - 13rem); max-height: 700px;">
<!-- Notification Type Selection -->
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-700">Notification Type</label>
<div class="grid grid-cols-4 gap-2">
<button class="type-button flex flex-col items-center justify-center p-3 rounded-lg bg-green-50 border border-green-200 hover:bg-green-100 transition-colors active:scale-95 transform border-2 border-green-500" data-type="success">
<span class="flex items-center justify-center w-8 h-8 rounded-md bg-green-100 text-green-600 mb-2">
<i class="fas fa-check text-lg"></i>
</span>
<span class="text-xs font-medium text-gray-700">Success</span>
</button>
<button class="type-button flex flex-col items-center justify-center p-3 rounded-lg bg-red-50 border border-red-200 hover:bg-red-100 transition-colors active:scale-95 transform" data-type="error">
<span class="flex items-center justify-center w-8 h-8 rounded-md bg-red-100 text-red-600 mb-2">
<i class="fas fa-times text-lg"></i>
</span>
<span class="text-xs font-medium text-gray-700">Error</span>
</button>
<button class="type-button flex flex-col items-center justify-center p-3 rounded-lg bg-blue-50 border border-blue-200 hover:bg-blue-100 transition-colors active:scale-95 transform" data-type="info">
<span class="flex items-center justify-center w-8 h-8 rounded-md bg-blue-100 text-blue-600 mb-2">
<i class="fas fa-info-circle text-lg"></i>
</span>
<span class="text-xs font-medium text-gray-700">Info</span>
</button>
<button class="type-button flex flex-col items-center justify-center p-3 rounded-lg bg-amber-50 border border-amber-200 hover:bg-amber-100 transition-colors active:scale-95 transform" data-type="warning">
<span class="flex items-center justify-center w-8 h-8 rounded-md bg-amber-100 text-amber-600 mb-2">
<i class="fas fa-exclamation-triangle text-lg"></i>
</span>
<span class="text-xs font-medium text-gray-700">Warning</span>
</button>
</div>
</div>
<!-- Content Section -->
<div class="mb-6 space-y-4">
<div>
<div class="flex items-center justify-between mb-2">
<label for="title-input" class="block text-sm font-medium text-gray-700">Title</label>
<span class="text-xs text-gray-400">Optional</span>
</div>
<input type="text" id="title-input" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 transition-shadow" placeholder="Enter notification title" value="">
</div>
<div>
<label for="message-input" class="block mb-2 text-sm font-medium text-gray-700">Message</label>
<textarea id="message-input" rows="3" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 transition-shadow resize-none" placeholder="Enter your notification message">Your product has been created successfully!</textarea>
</div>
</div>
<!-- Configuration Options -->
<div class="mb-6 border border-gray-200 rounded-md overflow-hidden shadow-sm">
<div class="bg-gradient-to-r from-gray-50 to-gray-100 px-3 py-2 flex items-center">
<h3 class="font-medium text-gray-700">Configuration Options</h3>
</div>
<div class="px-3 py-3 border-t border-gray-200">
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label for="position-select" class="block mb-1 text-sm font-medium text-gray-700">Position</label>
<select id="position-select" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Default</option>
<option value="top-right">top-right</option>
<option value="top-left">top-left</option>
<option value="bottom-right">bottom-right</option>
<option value="bottom-left">bottom-left</option>
<option value="center-top">center-top</option>
<option value="center-bottom">center-bottom</option>
</select>
</div>
<div>
<label for="timeout-input" class="block mb-1 text-sm font-medium text-gray-700">Timeout (ms)</label>
<select id="timeout-input" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Default</option>
<option value="10000">10 seconds</option>
<option value="5000">5 seconds</option>
<option value="3000">3 seconds</option>
<option value="60000">1 minute</option>
</select>
</div>
</div>
<div class="grid grid-cols-2 gap-3 mb-4">
<div>
<label for="theme-select" class="block mb-1 text-sm font-medium text-gray-700">Theme</label>
<select id="theme-select" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Default</option>
<option value="amazon">amazon</option>
<option value="amber">amber</option>
<option value="aurora">aurora</option>
<option value="crystal">crystal</option>
<option value="emerald">emerald</option>
<option value="facebook">facebook</option>
<option value="flasher">flasher</option>
<option value="google">google</option>
<option value="ios">ios</option>
<option value="jade">jade</option>
<option value="material">material</option>
<option value="minimal">minimal</option>
<option value="neon">neon</option>
<option value="onyx">onyx</option>
<option value="ruby">ruby</option>
<option value="sapphire">sapphire</option>
<option value="slack">slack</option>
</select>
</div>
<div>
<label for="direction-select" class="block mb-1 text-sm font-medium text-gray-700">Direction</label>
<select id="direction-select" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Default</option>
<option value="top">top</option>
<option value="bottom">bottom</option>
</select>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label for="fps-input" class="block mb-1 text-sm font-medium text-gray-700">FPS</label>
<select id="fps-input" class="w-full px-3 py-2 bg-gray-50 border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Default</option>
<option value="30">30</option>
<option value="60">60</option>
<option value="120">120</option>
</select>
</div>
<div class="flex items-end">
<div class="flex items-center h-10">
<input type="checkbox" id="rtl-check" class="w-4 h-4 text-blue-600 rounded focus:ring-blue-500">
<label for="rtl-check" class="ml-2 text-sm text-gray-700">RTL Layout</label>
</div>
</div>
</div>
<div class="mt-4 grid grid-cols-2 gap-3">
<div class="flex items-center">
<input type="checkbox" id="close-btn-check" class="w-4 h-4 text-blue-600 rounded focus:ring-blue-500">
<label for="close-btn-check" class="ml-2 text-sm text-gray-700">Close Button</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="escape-html-check" class="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" checked>
<label for="escape-html-check" class="ml-2 text-sm text-gray-700">Escape HTML</label>
</div>
</div>
</div>
</div>
<!-- Launch button -->
<button id="show-notification-btn" class="w-full group relative overflow-hidden rounded-md"
data-controller="notification-demo">
<div class="absolute inset-0 bg-gradient-to-r from-blue-600 to-indigo-600 group-hover:from-blue-700 group-hover:to-indigo-700 transition-all duration-300"></div>
<div class="absolute inset-0 opacity-10 bg-[url('data:image/svg+xml,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20viewBox%3D%220%200%2020%2020%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23ffffff%22%20fill-opacity%3D%221%22%20fill-rule%3D%22evenodd%22%3E%3Ccircle%20cx%3D%223%22%20cy%3D%223%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%2213%22%20cy%3D%2213%22%20r%3D%221%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E')]"></div>
<div class="relative px-5 py-3 flex items-center justify-center text-white font-medium">
<span class="flex items-center space-x-2">
<span>Launch Notification</span>
<i class="fas fa-arrow-right transform group-hover:translate-x-1 transition-transform ml-2"></i>
</span>
</div>
<div class="absolute top-0 -inset-full h-full w-1/2 block transform -skew-x-12 bg-gradient-to-r from-transparent to-white opacity-20 group-hover:animate-[shine_1s_forwards]"></div>
</button>
</div>
</div>
<!-- Right panel: Code Preview (wider) -->
<div class="w-full lg:w-3/5">
<div class="border-b border-gray-200 px-6 py-2 flex items-center justify-between bg-gray-50">
<div class="text-sm font-medium text-gray-700">Generated Code</div>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-500">Last updated: 2025-03-11 22:43:43</span>
</div>
</div>
<!-- Code tabs with FontAwesome icons -->
<div class="flex border-b border-gray-200 bg-gray-50 overflow-x-auto">
<button id="laravel-tab" class="code-tab-btn px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-500 flex items-center space-x-1.5" data-tab="laravel">
<i class="fab fa-laravel text-red-500"></i>
<span>Laravel</span>
</button>
<button id="symfony-tab" class="code-tab-btn px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800 flex items-center space-x-1.5" data-tab="symfony">
<i class="fab fa-symfony text-black"></i>
<span>Symfony</span>
</button>
<button id="js-tab" class="code-tab-btn px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800 flex items-center space-x-1.5" data-tab="js">
<i class="fab fa-js text-yellow-400"></i>
<span>JavaScript</span>
</button>
</div>
<!-- Laravel Code Panel -->
<div id="laravel-code-panel" class="p-4 bg-gray-50 overflow-y-auto" style="height: calc(100vh - 15rem); max-height: 700px;">
<pre class="text-left text-sm"><code class="language-php">
<span class="text-gray-500 font-semibold">// app/Http/Controllers/ProductController.php</span>
<span class="text-blue-700">namespace</span> App\Http\Controllers;
<span class="text-blue-700">use</span> App\Models\Product;
<span class="text-blue-700">use</span> App\Http\Requests\StoreProductRequest;
<span class="text-blue-700">use</span> Illuminate\Http\Request;
<span class="text-blue-700">class</span> <span class="text-green-700 font-bold">ProductController</span> <span class="text-blue-700">extends</span> Controller
{
<span class="text-blue-700">public function</span> <span class="text-yellow-600">store</span>(StoreProductRequest $request)
{
<span class="text-gray-400">// Create new product</span>
$product = Product::create($request->validated());
<span class="bg-green-100 p-1 rounded-md shadow-sm font-bold">flash()->success('Your product has been created successfully!');</span>
<span class="text-blue-700">return</span> redirect()->route('products.index');
}
}</code></pre>
</div>
<!-- Symfony Code Panel -->
<div id="symfony-code-panel" class="p-4 bg-gray-50 overflow-y-auto hidden" style="height: calc(100vh - 15rem); max-height: 700px;">
<pre class="text-left text-sm"><code class="language-php">
<span class="text-gray-500 font-semibold">// src/Controller/ProductController.php</span>
<span class="text-blue-700">namespace</span> App\Controller;
<span class="text-blue-700">use</span> App\Entity\Product;
<span class="text-blue-700">use</span> App\Form\ProductType;
<span class="text-blue-700">use</span> Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
<span class="text-blue-700">use</span> Symfony\Component\HttpFoundation\Request;
<span class="text-blue-700">use</span> Symfony\Component\HttpFoundation\Response;
<span class="text-blue-700">use</span> Symfony\Component\Routing\Attribute\Route;
<span class="text-blue-700">class</span> <span class="text-green-700 font-bold">ProductController</span> <span class="text-blue-700">extends</span> AbstractController
{
#[Route('/products/new', name: 'app_product_new', methods: ['GET', 'POST'])]
<span class="text-blue-700">public function</span> <span class="text-yellow-600">new</span>(Request $request): Response
{
$product = <span class="text-blue-700">new</span> Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
<span class="text-blue-700">if</span> ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->container->get('doctrine')->getManager();
$entityManager->persist($product);
$entityManager->flush();
<span class="bg-green-100 p-1 rounded-md shadow-sm font-bold">$this->flash()->success('Your product has been created successfully!');</span>
<span class="text-blue-700">return</span> $this->redirectToRoute('app_product_index');
}
<span class="text-blue-700">return</span> $this->render('product/new.html.twig', [
'product' => $product,
'form' => $form,
]);
}
}</code></pre>
</div>
<!-- JavaScript Code Panel -->
<div id="js-code-panel" class="p-4 bg-gray-50 overflow-y-auto hidden" style="height: calc(100vh - 15rem); max-height: 700px;">
<pre class="text-left text-sm"><code class="language-javascript">
<span class="text-gray-500 font-semibold">// product-service.js</span>
<span class="text-blue-700">import</span> flasher <span class="text-blue-700">from</span> '@flasher/flasher';
<span class="text-blue-700">async function</span> <span class="text-yellow-600">createProduct</span>(productData) {
<span class="text-blue-700">try</span> {
<span class="text-blue-700">const</span> response = <span class="text-blue-700">await</span> fetch('/api/products', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(productData)
});
<span class="text-blue-700">const</span> result = <span class="text-blue-700">await</span> response.json();
<span class="text-blue-700">if</span> (!response.ok) {
<span class="text-blue-700">throw new</span> Error(result.message || 'Failed to create product');
}
<span class="bg-green-100 p-1 rounded-md shadow-sm font-bold">flasher.success('Your product has been created successfully!');</span>
<span class="text-blue-700">return</span> result;
} <span class="text-blue-700">catch</span> (error) {
flasher.error('Error: ' + error.message);
<span class="text-blue-700">throw</span> error;
}
}
<span class="text-gray-500">// Usage example</span>
document.getElementById('productForm').addEventListener('submit', <span class="text-blue-700">async</span> (event) => {
event.preventDefault();
<span class="text-blue-700">const</span> formData = <span class="text-blue-700">new</span> FormData(event.target);
<span class="text-blue-700">const</span> productData = Object.fromEntries(formData.entries());
<span class="text-blue-700">try</span> {
<span class="text-blue-700">const</span> product = <span class="text-blue-700">await</span> createProduct(productData);
window.location.href = '/products';
} <span class="text-blue-700">catch</span> (error) {
console.error('Failed to create product:', error);
}
});</code></pre>
</div>
</div>
</div>
<!-- Additional interactive elements for demos -->
<div class="px-6 py-3 bg-gray-50 border-t border-gray-200 flex items-center justify-between">
<div class="text-sm text-gray-500">Current time: <span id="current-time" class="font-mono">2025-03-11 22:43:43</span></div>
<div class="flex space-x-4">
<a href="https://phpflasher.com/docs" class="text-xs text-gray-600 hover:text-blue-600 transition-colors flex items-center">
<i class="fas fa-book mr-1.5 text-xs"></i>
Documentation
</a>
<a href="https://github.com/php-flasher/php-flasher" class="text-xs text-gray-600 hover:text-blue-600 transition-colors flex items-center">
<i class="fab fa-github mr-1.5 text-xs"></i>
GitHub
</a>
</div>
</div>
</div>
<!-- Code preview actions -->
<div class="flex justify-center mt-4 space-x-4">
<a href="https://phpflasher.com/docs" class="flex items-center space-x-2 text-gray-500 hover:text-gray-700 transition-colors">
<i class="fas fa-book text-lg"></i>
<span>Documentation</span>
</a>
<a href="https://github.com/php-flasher/php-flasher" class="flex items-center space-x-2 text-gray-500 hover:text-gray-700 transition-colors">
<i class="fab fa-github text-lg"></i>
<span>GitHub</span>
</a>
</div>
</div>
</section>
<script>
// Current state management
const state = {
type: 'success',
title: '',
message: 'Your product has been created successfully!',
timeout: '',
fps: '',
position: '',
direction: '',
rtl: false,
theme: '',
closeButton: false,
escapeHtml: true
};
document.addEventListener('DOMContentLoaded', function() {
// Update current time
document.getElementById('current-time').textContent = "2025-03-11 22:43:43";
// Initialize type buttons
document.querySelectorAll('.type-button').forEach(btn => {
btn.addEventListener('click', () => {
// Remove active class from all type buttons
document.querySelectorAll('.type-button').forEach(b => {
b.classList.remove('border-2');
b.classList.remove('border-green-500', 'border-red-500', 'border-blue-500', 'border-amber-500');
});
// Add active class to clicked button
btn.classList.add('border-2');
// Add appropriate color based on type
const type = btn.dataset.type;
switch(type) {
case 'success':
btn.classList.add('border-green-500');
break;
case 'error':
btn.classList.add('border-red-500');
break;
case 'info':
btn.classList.add('border-blue-500');
break;
case 'warning':
btn.classList.add('border-amber-500');
break;
}
// Update state
state.type = type;
// Update code display
updateCodeDisplay();
// Update status
updateStatus(`Type changed to: ${state.type}`);
});
});
// Input event listeners
document.getElementById('title-input').addEventListener('input', (e) => {
state.title = e.target.value.trim();
updateCodeDisplay();
});
document.getElementById('message-input').addEventListener('input', (e) => {
state.message = e.target.value;
updateCodeDisplay();
});
// Select event listeners
document.getElementById('position-select').addEventListener('change', (e) => {
state.position = e.target.value;
updateCodeDisplay();
});
document.getElementById('timeout-input').addEventListener('change', (e) => {
state.timeout = e.target.value ? parseInt(e.target.value) : '';
updateCodeDisplay();
});
document.getElementById('fps-input').addEventListener('change', (e) => {
state.fps = e.target.value ? parseInt(e.target.value) : '';
updateCodeDisplay();
});
document.getElementById('theme-select').addEventListener('change', (e) => {
state.theme = e.target.value;
updateCodeDisplay();
});
document.getElementById('direction-select').addEventListener('change', (e) => {
state.direction = e.target.value;
updateCodeDisplay();
});
// Checkbox event listeners
document.getElementById('rtl-check').addEventListener('change', (e) => {
state.rtl = e.target.checked;
updateCodeDisplay();
});
document.getElementById('close-btn-check').addEventListener('change', (e) => {
state.closeButton = e.target.checked;
updateCodeDisplay();
});
document.getElementById('escape-html-check').addEventListener('change', (e) => {
state.escapeHtml = e.target.checked;
updateCodeDisplay();
});
// Show notification button
document.getElementById('show-notification-btn').addEventListener('click', () => {
updateStatus('Launching notification...');
showNotification();
});
// Tab buttons functionality
document.querySelectorAll('.code-tab-btn').forEach(button => {
button.addEventListener('click', () => {
const tab = button.getAttribute('data-tab');
showCodeTab(tab);
});
});
// Initialize with Laravel tab selected
showCodeTab('laravel');
});
function updateStatus(message) {
const statusText = document.getElementById('status-text');
statusText.textContent = message;
// Temporarily change color
statusText.classList.remove('text-emerald-600');
statusText.classList.add('text-blue-600');
// Add animation to the dot
const dot = document.querySelector('#status-indicator div');
dot.classList.remove('bg-emerald-500');
dot.classList.add('bg-blue-500');
// Reset after 2 seconds
setTimeout(() => {
statusText.classList.remove('text-blue-600');
statusText.classList.add('text-emerald-600');
statusText.textContent = 'Ready';
dot.classList.remove('bg-blue-500');
dot.classList.add('bg-emerald-500');
}, 2000);
}
function showCodeTab(tab) {
// Hide all panels
document.querySelectorAll('#laravel-code-panel, #symfony-code-panel, #js-code-panel').forEach(panel => {
panel.classList.add('hidden');
});
// Show the selected panel
document.getElementById(`${tab}-code-panel`).classList.remove('hidden');
// Update tab styling
document.querySelectorAll('.code-tab-btn').forEach(btn => {
btn.classList.remove('text-blue-600', 'border-b-2', 'border-blue-500');
btn.classList.add('text-gray-600');
});
// Highlight active tab
document.querySelector(`[data-tab="${tab}"]`).classList.remove('text-gray-600');
document.querySelector(`[data-tab="${tab}"]`).classList.add('text-blue-600', 'border-b-2', 'border-blue-500');
}
// Show notification function
function showNotification() {
// Create container for notifications if it doesn't exist
let container = document.getElementById('flasher-notifications');
if (!container) {
container = document.createElement('div');
container.id = 'flasher-notifications';
container.className = 'fixed z-50';
document.body.appendChild(container);
}
// Position based on settings
positionContainer(container, state.position || 'top-right');
// Create notification element
const notification = document.createElement('div');
notification.className = `transform transition-all duration-300 flex overflow-hidden rounded-lg shadow-md mb-3 max-w-xs w-full`;
// Style based on type
const styles = getNotificationStyles(state.type);
// Build notification HTML
let notificationHTML = `
<div class="flex-shrink-0 ${styles.iconBg} flex items-center justify-center rounded-l-lg px-3">
<i class="${styles.icon} text-white text-lg"></i>
</div>
<div class="p-3 flex-1 ${styles.bg} relative overflow-hidden">
${state.title ? `<h4 class="font-medium ${styles.titleColor} mb-1">${state.title}</h4>` : ''}
<p class="${styles.textColor} text-sm">${state.message}</p>
${state.closeButton ? `<button class="absolute top-2 right-2 text-gray-400 hover:text-gray-600 transition-colors">
<i class="fas fa-times"></i>
</button>` : ''}
<div class="absolute bottom-0 left-0 right-0 h-1 ${styles.progressBg} origin-left animate-[progress_${state.timeout || 5000}ms_linear_forwards]"></div>
</div>
`;
notification.innerHTML = notificationHTML;
// Add entrance animation classes based on position
addEntranceAnimation(notification, state.position || 'top-right');
// Add to container
container.appendChild(notification);
// Close button functionality if enabled
if (state.closeButton) {
const closeButton = notification.querySelector('button');
closeButton.addEventListener('click', () => {
removeNotification(notification, state.position || 'top-right');
});
}
// Auto-remove after timeout
const timeout = state.timeout !== '' ? state.timeout : 5000;
if (timeout > 0) {
setTimeout(() => {
removeNotification(notification, state.position || 'top-right');
}, timeout);
}
}
function positionContainer(container, position) {
// Reset positioning
container.className = 'fixed z-50';
// Set position
switch(position) {
case 'top-right':
container.classList.add('top-4', 'right-4');
break;
case 'top-left':
container.classList.add('top-4', 'left-4');
break;
case 'bottom-right':
container.classList.add('bottom-4', 'right-4');
break;
case 'bottom-left':
container.classList.add('bottom-4', 'left-4');
break;
case 'center-top':
container.classList.add('top-4', 'left-1/2', '-translate-x-1/2');
break;
case 'center-bottom':
container.classList.add('bottom-4', 'left-1/2', '-translate-x-1/2');
break;
default:
container.classList.add('top-4', 'right-4');
}
}
function getNotificationStyles(type) {
switch(type) {
case 'success':
return {
bg: 'bg-green-50',
iconBg: 'bg-green-500',
titleColor: 'text-green-800',
textColor: 'text-green-700',
progressBg: 'bg-green-500',
icon: 'fas fa-check'
};
case 'error':
return {
bg: 'bg-red-50',
iconBg: 'bg-red-500',
titleColor: 'text-red-800',
textColor: 'text-red-700',
progressBg: 'bg-red-500',
icon: 'fas fa-times'
};
case 'info':
return {
bg: 'bg-blue-50',
iconBg: 'bg-blue-500',
titleColor: 'text-blue-800',
textColor: 'text-blue-700',
progressBg: 'bg-blue-500',
icon: 'fas fa-info-circle'
};
case 'warning':
return {
bg: 'bg-amber-50',
iconBg: 'bg-amber-500',
titleColor: 'text-amber-800',
textColor: 'text-amber-700',
progressBg: 'bg-amber-500',
icon: 'fas fa-exclamation-triangle'
};
default:
return getNotificationStyles('success');
}
}
function addEntranceAnimation(notification, position) {
if (position.includes('right')) {
notification.classList.add('translate-x-full');
} else if (position.includes('left')) {
notification.classList.add('-translate-x-full');
} else if (position.includes('top')) {
notification.classList.add('-translate-y-full');
} else if (position.includes('bottom')) {
notification.classList.add('translate-y-full');
}
// Trigger animation after a brief delay
setTimeout(() => {
notification.classList.remove(
'translate-x-full',
'-translate-x-full',
'translate-y-full',
'-translate-y-full'
);
}, 10);
}
function removeNotification(notification, position) {
// Add exit animation
if (position.includes('right')) {
notification.classList.add('translate-x-full');
} else if (position.includes('left')) {
notification.classList.add('-translate-x-full');
} else if (position.includes('top')) {
notification.classList.add('-translate-y-full');
} else if (position.includes('bottom')) {
notification.classList.add('translate-y-full');
}
notification.classList.add('opacity-0');
// Remove after animation completes
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}
// Define keyframes at the document level once
if (!document.getElementById('flasher-keyframes')) {
const styleSheet = document.createElement('style');
styleSheet.id = 'flasher-keyframes';
styleSheet.textContent = `
@keyframes shine {
to {
left: 100%;
}
}
@keyframes progress {
0% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
`;
document.head.appendChild(styleSheet);
}
// Update code display function - this updates the highlighted line in each example
function updateCodeDisplay() {
// Get currently active tab
const activeTab = document.querySelector('.code-tab-btn.text-blue-600').getAttribute('data-tab');
// Update notification message and type in the highlighted lines
const highlightLines = document.querySelectorAll('.bg-green-100');
highlightLines.forEach(line => {
switch(activeTab) {
case 'laravel':
line.textContent = `flash()->${state.type}('${state.message}'${state.title ? ", '" + state.title + "'" : ''});`;
break;
case 'symfony':
line.textContent = `$this->flash()->${state.type}('${state.message}'${state.title ? ", '" + state.title + "'" : ''});`;
break;
case 'js':
line.textContent = `flasher.${state.type}('${state.message}'${state.title ? ", '" + state.title + "'" : ''});`;
break;
}
});
}
// Update current time
function updateCurrentTime() {
document.getElementById('current-time').textContent = "2025-03-11 22:45:58";
}
// Call it once on load
updateCurrentTime();
</script>
<style>
/* Utility classes for animation - using Tailwind's @keyframes syntax */
@keyframes shine {
to {
left: 100%;
}
}
@keyframes progress {
0% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
@keyframes ping {
0% { transform: scale(1); opacity: 1; }
75%, 100% { transform: scale(2); opacity: 0; }
}
</style>