This commit is contained in:
Younes ENNAJI
2025-03-11 22:33:12 +00:00
parent fa3012a711
commit eff520316e
9 changed files with 1092 additions and 923 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"dist/main.css": "/dist/main.aba03d4f.css", "dist/main.css": "/dist/main.4e82aad6.css",
"dist/main.js": "/dist/main.c89a204f.js", "dist/main.js": "/dist/main.c89a204f.js",
"dist/455.3a7b4474.css": "/dist/455.3a7b4474.css", "dist/455.3a7b4474.css": "/dist/455.3a7b4474.css",
"dist/455.17bc016b.js": "/dist/455.17bc016b.js", "dist/455.17bc016b.js": "/dist/455.17bc016b.js",
+367
View File
@@ -0,0 +1,367 @@
<!-- 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-slow"></line>
<line x1="100" y1="0" x2="0" y2="100" stroke="currentColor" stroke-width="0.2"
class="text-indigo-500 animate-pulse-medium"></line>
</svg>
</div>
<!-- Main content -->
<div class="container relative z-10 mx-auto px-4 max-w-6xl">
<!-- 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-slow"></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-1/2 border-r border-gray-100">
<!-- Options panel -->
<div class="p-4 overflow-y-auto" style="height: calc(100vh - 13rem);">
<!-- Notification Type Selection - Redesigned professional interface with spacing -->
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-700">Notification Type</label>
<div class="notification-types-grid">
<button class="type-button active" data-type="success">
<span class="type-icon success">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
</span>
<span>Success</span>
</button>
<button class="type-button" data-type="error">
<span class="type-icon error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd" />
</svg>
</span>
<span>Error</span>
</button>
<button class="type-button" data-type="info">
<span class="type-icon info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2h-1V9a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</span>
<span>Info</span>
</button>
<button class="type-button" data-type="warning">
<span class="type-icon warning">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
</span>
<span>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 changes have been saved successfully!</textarea>
</div>
</div>
<!-- Configuration Options -->
<div class="mb-6 border border-gray-200 rounded-md overflow-hidden">
<div class="bg-gray-50 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 bg-grid opacity-10"></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>
<svg
class="w-4 h-4 transform group-hover:translate-x-1 transition-transform"
fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</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"></div>
</button>
</div>
</div>
<!-- Right panel: Code Preview -->
<div class="w-full lg:w-1/2">
<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>
<!-- Code tabs -->
<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"
data-tab="laravel">Laravel
</button>
<button id="symfony-tab"
class="code-tab-btn px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800"
data-tab="symfony">Symfony
</button>
<button id="npm-tab"
class="code-tab-btn px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800"
data-tab="npm">JavaScript
</button>
</div>
<!-- Code panels -->
<div id="laravel-code-panel" class="p-4 bg-gray-50 overflow-y-auto code-panel">
<pre><code class="language-php" id="laravel-code-display">// Laravel Controller Method Example
public function store(Request $request)
{
// Your logic to process the form
$result = $this->processData($request->all());
flash()->success('Your changes have been saved successfully!');
return redirect()->back();
}</code></pre>
</div>
<div id="symfony-code-panel" class="p-4 bg-gray-50 overflow-y-auto code-panel hidden">
<pre><code class="language-php" id="symfony-code-display">// Symfony Controller Method Example
public function store(Request $request): Response
{
// Your logic to process the form
$result = $this->processData($request->request->all());
$this->flash()->success('Your changes have been saved successfully!');
return $this->redirectToRoute('app_homepage');
}</code></pre>
</div>
<div id="npm-code-panel" class="p-4 bg-gray-50 overflow-y-auto code-panel hidden">
<pre><code class="language-javascript" id="npm-code-display">// NPM Installation
// npm install @flasher/flasher --save
// ES6 Module Import
import flasher from '@flasher/flasher';
// Example in React, Vue, or other framework
function saveData() {
// API call or data processing
return apiService.saveData(formData)
.then(response => {
flasher.success('Your changes have been saved successfully!');
return response;
})
.catch(error => {
flasher.error('Failed to save data');
throw error;
});
}</code></pre>
</div>
</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">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>
<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">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd"></path>
</svg>
<span>GitHub</span>
</a>
</div>
</div>
</section>
+5 -920
View File
@@ -2,6 +2,10 @@
<html lang="en" class="scroll-smooth"> <html lang="en" class="scroll-smooth">
<head> <head>
{% include head.html %} {% include head.html %}
<link rel="stylesheet" href="/static/css/home.css">
<link rel="stylesheet" href="/static/css/flasher-studio.css">
<script src="/static/js/flasher-studio.js" defer></script>
</head> </head>
<body class="min-h-screen bg-slate-50 text-slate-800 font-sans leading-relaxed" data-controller="anchor clipboard navigation tryit {{ page.data-controller }}"> <body class="min-h-screen bg-slate-50 text-slate-800 font-sans leading-relaxed" data-controller="anchor clipboard navigation tryit {{ page.data-controller }}">
{% include size-helper.html %} {% include size-helper.html %}
@@ -83,869 +87,7 @@
</div> </div>
</section> </section>
<!-- Elegant PHPFlasher Interactive Demo --> {% include flasher-studio.html %}
<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-slow"></line>
<line x1="100" y1="0" x2="0" y2="100" stroke="currentColor" stroke-width="0.2" class="text-indigo-500 animate-pulse-medium"></line>
</svg>
</div>
<!-- Main content -->
<div class="container relative z-10 mx-auto px-4 max-w-6xl">
<!-- 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-slow"></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-1/2 border-r border-gray-100">
<!-- Options panel -->
<div class="p-4 overflow-y-auto" style="height: calc(100vh - 13rem);">
<!-- Notification Type Selection - Redesigned professional interface with spacing -->
<div class="mb-6">
<label class="block mb-2 text-sm font-medium text-gray-700">Notification Type</label>
<div class="notification-types-grid">
<button class="type-button active" data-type="success">
<span class="type-icon success">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
</span>
<span>Success</span>
</button>
<button class="type-button" data-type="error">
<span class="type-icon error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
</span>
<span>Error</span>
</button>
<button class="type-button" data-type="info">
<span class="type-icon info">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2h-1V9a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</span>
<span>Info</span>
</button>
<button class="type-button" data-type="warning">
<span class="type-icon warning">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</span>
<span>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 changes have been saved successfully!</textarea>
</div>
</div>
<!-- Configuration Options -->
<div class="mb-6 border border-gray-200 rounded-md overflow-hidden">
<div class="bg-gray-50 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 bg-grid opacity-10"></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>
<svg class="w-4 h-4 transform group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
</svg>
</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"></div>
</button>
</div>
</div>
<!-- Right panel: Code Preview -->
<div class="w-full lg:w-1/2">
<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>
<!-- Code tabs -->
<div class="flex border-b border-gray-200 bg-gray-50">
<button id="php-tab" class="px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-500">PHP</button>
<button id="js-tab" class="px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800">JavaScript</button>
</div>
<!-- Code editor with Prism syntax highlighting -->
<div id="php-code-panel" class="p-4 bg-gray-50 overflow-y-auto code-panel">
<pre><code class="language-php" id="php-code-display">flash()->success('Your changes have been saved!');</code></pre>
</div>
<div id="js-code-panel" class="p-4 bg-gray-50 overflow-y-auto code-panel hidden">
<pre><code class="language-javascript" id="js-code-display">flasher.success('Your changes have been saved!');</code></pre>
</div>
</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">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>
<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">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
</svg>
<span>GitHub</span>
</a>
</div>
</div>
</section>
<style>
/* Essential styles for the elegant notifications demo */
.bg-grid {
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='1' fill-rule='evenodd'%3E%3Ccircle cx='3' cy='3' r='1'/%3E%3Ccircle cx='13' cy='13' r='1'/%3E%3C/g%3E%3C/svg%3E");
}
@keyframes ping-slow {
0% { transform: scale(1); opacity: 1; }
75%, 100% { transform: scale(2); opacity: 0; }
}
.animate-ping-slow {
animation: ping-slow 2.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes pulse-slow {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.8; }
}
.animate-pulse-slow {
animation: pulse-slow 8s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.animate-pulse-medium {
animation: pulse-slow 6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes shine {
to {
left: 100%;
}
}
.animate-shine {
animation: shine 1s forwards;
}
/* New spaced notification type buttons */
.notification-types-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
width: 100%;
}
.type-button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.75rem 0.5rem;
font-size: 0.875rem;
font-weight: 500;
color: #4b5563;
border-radius: 0.5rem;
transition: all 0.2s ease;
border: 1px solid #e5e7eb;
background-color: #f9fafb;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.type-button:hover {
transform: translateY(-2px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
}
.type-button.active {
border-width: 2px;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.type-icon {
display: inline-flex;
width: 1.75rem;
height: 1.75rem;
align-items: center;
justify-content: center;
margin-bottom: 0.5rem;
padding: 0.25rem;
border-radius: 0.375rem;
}
/* Success type */
.type-button[data-type="success"] {
background-color: rgba(16, 185, 129, 0.05);
}
.type-button[data-type="success"].active {
background-color: rgba(16, 185, 129, 0.15);
border-color: #10B981;
}
.type-icon.success {
color: #10B981;
background-color: rgba(16, 185, 129, 0.1);
}
/* Error type */
.type-button[data-type="error"] {
background-color: rgba(239, 68, 68, 0.05);
}
.type-button[data-type="error"].active {
background-color: rgba(239, 68, 68, 0.15);
border-color: #EF4444;
}
.type-icon.error {
color: #EF4444;
background-color: rgba(239, 68, 68, 0.1);
}
/* Info type */
.type-button[data-type="info"] {
background-color: rgba(59, 130, 246, 0.05);
}
.type-button[data-type="info"].active {
background-color: rgba(59, 130, 246, 0.15);
border-color: #3B82F6;
}
.type-icon.info {
color: #3B82F6;
background-color: rgba(59, 130, 246, 0.1);
}
/* Warning type */
.type-button[data-type="warning"] {
background-color: rgba(245, 158, 11, 0.05);
}
.type-button[data-type="warning"].active {
background-color: rgba(245, 158, 11, 0.15);
border-color: #F59E0B;
}
.type-icon.warning {
color: #F59E0B;
background-color: rgba(245, 158, 11, 0.1);
}
/* Code panel styles */
.code-panel {
height: calc(100vh - 15rem);
max-height: 500px;
}
/* Override Prism default styles to make them work better in our UI */
pre[class*="language-"] {
background: #f9fafb !important; /* Use light gray instead of darker backgrounds */
margin: 0 !important;
padding: 1rem !important;
border-radius: 0.5rem;
border: 1px solid #e5e7eb;
}
code[class*="language-"] {
font-size: 0.875rem !important;
line-height: 1.6 !important;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
}
/* Tab button styles */
#php-tab.active, #js-tab.active {
@apply text-blue-600 border-b-2 border-blue-500;
}
/* Additional animation for code tabs */
.code-tab-transition {
transition: all 0.3s ease-in-out;
}
</style>
<script>
// Current state management
const state = {
type: 'success',
title: '',
message: 'Your changes have been saved successfully!',
timeout: '',
fps: '',
position: '',
direction: '',
rtl: false,
theme: '',
closeButton: false,
escapeHtml: true
};
// Update current time
function updateCurrentTime() {
document.getElementById('current-time').textContent = "2025-03-11 07:38:23";
}
document.addEventListener('DOMContentLoaded', function() {
// Initialize particles.js if available
if (typeof particlesJS !== 'undefined') {
particlesJS('particles-js', {
particles: {
number: { value: 24, density: { enable: true, value_area: 800 } },
color: { value: "#3b82f6" },
opacity: { value: 0.1, random: true },
size: { value: 2, random: true },
line_linked: {
enable: true,
distance: 150,
color: "#4b5563",
opacity: 0.1,
width: 1
},
move: {
enable: true,
speed: 0.4,
direction: "none",
random: true,
straight: false,
out_mode: "out"
}
}
});
}
// Initialize time with user-provided values
document.getElementById('current-time').textContent = "2025-03-11 07:40:35";
// 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('active'));
// Add active class to clicked button
btn.classList.add('active');
// Update state
state.type = btn.dataset.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('input', (e) => {
state.timeout = e.target.value ? parseInt(e.target.value) : '';
updateCodeDisplay();
});
document.getElementById('fps-input').addEventListener('input', (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 - directly execute notifications using the showNotificationsForHandler function
document.getElementById('show-notification-btn').addEventListener('click', () => {
// Update status
updateStatus('Launching notification...');
// Get current options from state
const options = {};
// Only add options that have been explicitly set
if (state.position) options.position = state.position;
if (state.timeout !== '') options.timeout = state.timeout;
if (state.fps !== '') options.fps = state.fps;
if (state.direction) options.direction = state.direction;
if (state.rtl) options.rtl = state.rtl;
if (state.closeButton) options.closeButton = state.closeButton;
if (!state.escapeHtml) options.escapeHtml = state.escapeHtml;
// Determine the handler based on theme
let handler = 'flasher'; // Default handler
if (state.theme) {
handler = `theme.${state.theme}`;
}
// Try to use the direct notification function if it exists, otherwise use the stimulus controller
if (typeof showNotificationsForHandler === 'function') {
// Create a custom notification based on user selections
const factory = flasher.use(handler);
// Configure flasher with options
if (Object.keys(options).length > 0) {
factory.options(options);
}
// Show the notification
if (state.title) {
factory[state.type](state.message, state.title);
} else {
factory[state.type](state.message);
}
} else {
console.log(`Would show a ${state.type} notification with message "${state.message}"${state.title ? ` and title "${state.title}"` : ''} using handler "${handler}"`);
console.log('Notification options:', options);
console.log('Stimulus controller should be triggered');
}
});
// Code tabs functionality
document.getElementById('php-tab').addEventListener('click', () => {
showCodeTab('php');
});
document.getElementById('js-tab').addEventListener('click', () => {
showCodeTab('js');
});
function showCodeTab(tab) {
// Hide all panels
document.querySelectorAll('.code-panel').forEach(panel => {
panel.classList.add('hidden');
});
// Show the selected panel
document.getElementById(`${tab}-code-panel`).classList.remove('hidden');
// Update tab styling
document.getElementById('php-tab').classList.remove('text-blue-600', 'border-b-2', 'border-blue-500');
document.getElementById('js-tab').classList.remove('text-blue-600', 'border-b-2', 'border-blue-500');
document.getElementById('php-tab').classList.add('text-gray-600');
document.getElementById('js-tab').classList.add('text-gray-600');
document.getElementById(`${tab}-tab`).classList.remove('text-gray-600');
document.getElementById(`${tab}-tab`).classList.add('text-blue-600', 'border-b-2', 'border-blue-500');
// Update code for the selected tab
updateCodeDisplay();
}
// Initialize
updateCodeDisplay();
});
// Update the status indicator
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);
}
// Update the code display with Prism syntax highlighting
function updateCodeDisplay() {
const phpCodeDisplay = document.getElementById('php-code-display');
const jsCodeDisplay = document.getElementById('js-code-display');
// PHP Code
let phpCode = `// Display a ${state.type} notification\n`;
// Handle theme usage
let useTheme = '';
if (state.theme) {
useTheme = `flash()->use('theme.${state.theme}')`;
} else {
useTheme = 'flash()';
}
// Build options object
let hasOptions = false;
let phpOptionsCode = '';
if (state.position || state.timeout !== '' || state.fps !== '' ||
state.direction || state.rtl ||
state.closeButton || !state.escapeHtml) {
hasOptions = true;
phpOptionsCode = '->options([\n';
// Add each option that's been explicitly set
const optionLines = [];
if (state.position) optionLines.push(` 'position' => '${state.position}'`);
if (state.timeout !== '') optionLines.push(` 'timeout' => ${state.timeout}`);
if (state.fps !== '') optionLines.push(` 'fps' => ${state.fps}`);
if (state.direction) optionLines.push(` 'direction' => '${state.direction}'`);
if (state.rtl) optionLines.push(` 'rtl' => true`);
if (state.closeButton) optionLines.push(` 'closeButton' => true`);
if (!state.escapeHtml) optionLines.push(` 'escapeHtml' => false`);
phpOptionsCode += optionLines.join(',\n') + '\n ])';
}
// Build the full PHP code
if (hasOptions) {
phpCode += `${useTheme}${phpOptionsCode}\n`;
if (state.title) {
phpCode += ` ->${state.type}(\n '${state.message}', \n '${state.title}'\n );`;
} else {
phpCode += ` ->${state.type}('${state.message}');`;
}
} else {
// Simple call without options
if (state.title) {
phpCode += `${useTheme}->${state.type}('${state.message}', '${state.title}');`;
} else {
phpCode += `${useTheme}->${state.type}('${state.message}');`;
}
}
// JavaScript Code
let jsCode = `// Display a ${state.type} notification\n`;
// Handle theme usage
let jsUseTheme = '';
if (state.theme) {
jsUseTheme = `flasher.use('theme.${state.theme}')`;
} else {
jsUseTheme = 'flasher';
}
// Build options object
let jsOptionsCode = '';
if (state.position || state.timeout !== '' || state.fps !== '' ||
state.direction || state.rtl ||
state.closeButton || !state.escapeHtml) {
jsOptionsCode = '.options({\n';
// Add each option that's been explicitly set
const optionLines = [];
if (state.position) optionLines.push(` position: '${state.position}'`);
if (state.timeout !== '') optionLines.push(` timeout: ${state.timeout}`);
if (state.fps !== '') optionLines.push(` fps: ${state.fps}`);
if (state.direction) optionLines.push(` direction: '${state.direction}'`);
if (state.rtl) optionLines.push(` rtl: true`);
if (state.closeButton) optionLines.push(` closeButton: true`);
if (!state.escapeHtml) optionLines.push(` escapeHtml: false`);
jsOptionsCode += optionLines.join(',\n') + '\n})';
}
// Build the full JavaScript code
if (jsOptionsCode) {
jsCode += `${jsUseTheme}${jsOptionsCode}\n`;
if (state.title) {
jsCode += ` .${state.type}('${state.message}', '${state.title}');`;
} else {
jsCode += ` .${state.type}('${state.message}');`;
}
} else {
// Simple call without options
if (state.title) {
jsCode += `${jsUseTheme}.${state.type}('${state.message}', '${state.title}');`;
} else {
jsCode += `${jsUseTheme}.${state.type}('${state.message}');`;
}
}
phpCodeDisplay.textContent = phpCode;
jsCodeDisplay.textContent = jsCode;
// Re-highlight with Prism
if (typeof Prism !== 'undefined') {
Prism.highlightElement(phpCodeDisplay);
Prism.highlightElement(jsCodeDisplay);
}
}
// Add a custom implementation of showNotificationsForHandler if it doesn't exist
// This helps with preview in environments where the actual function isn't available
if (typeof showNotificationsForHandler === 'undefined') {
window.showNotificationsForHandler = function(handler, options = {}) {
console.log(`Showing notifications for handler: ${handler}`);
console.log('Options:', options);
// Mock the notification display
console.log('Showing mock notifications sequence...');
};
window.flasher = {
use: function(handler) {
console.log(`Using handler: ${handler}`);
return {
options: function(options) {
console.log(`Setting options for ${handler}:`, options);
return this;
},
success: function(message, title) {
console.log(`Success: ${message}${title ? ` (${title})` : ''}`);
},
error: function(message, title) {
console.log(`Error: ${message}${title ? ` (${title})` : ''}`);
},
info: function(message, title) {
console.log(`Info: ${message}${title ? ` (${title})` : ''}`);
},
warning: function(message, title) {
console.log(`Warning: ${message}${title ? ` (${title})` : ''}`);
}
};
},
options: function(options) {
console.log('Setting global options:', options);
return this;
},
success: function(message, title) {
console.log(`Success: ${message}${title ? ` (${title})` : ''}`);
},
error: function(message, title) {
console.log(`Error: ${message}${title ? ` (${title})` : ''}`);
},
info: function(message, title) {
console.log(`Info: ${message}${title ? ` (${title})` : ''}`);
},
warning: function(message, title) {
console.log(`Warning: ${message}${title ? ` (${title})` : ''}`);
}
};
}
// Helper function for the Stimulus controller integration
document.addEventListener('DOMContentLoaded', function() {
// Implement a simple version of Stimulus controller behavior if Stimulus is not available
const notificationButtons = document.querySelectorAll('[data-controller="notification-demo"]');
if (typeof Stimulus === 'undefined') {
notificationButtons.forEach(button => {
if (!button.hasAttribute('data-stimulus-initialized')) {
button.setAttribute('data-stimulus-initialized', 'true');
// Only wire up the click handler if not already using the event listener above
if (button.id !== 'show-notification-btn') {
button.addEventListener('click', function() {
if (typeof showNotificationsForHandler === 'function') {
showNotificationsForHandler('notyf');
} else {
console.log('Would show notifications using Stimulus controller');
}
});
}
}
});
}
});
</script>
<!-- Quick Start Section (Moved before Features) --> <!-- Quick Start Section (Moved before Features) -->
<section id="quick-start" class="container mx-auto px-4 mb-16"> <section id="quick-start" class="container mx-auto px-4 mb-16">
@@ -1461,63 +603,6 @@
</div> </div>
</div> </div>
</section> </section>
<style>
/* Bell animation */
@keyframes bellRing {
0%, 100% {
transform: rotate(0);
transform-origin: top center;
}
20% {
transform: rotate(8deg);
transform-origin: top center;
}
40% {
transform: rotate(-8deg);
transform-origin: top center;
}
60% {
transform: rotate(4deg);
transform-origin: top center;
}
80% {
transform: rotate(-4deg);
transform-origin: top center;
}
}
.animate-bell {
animation: bellRing 2s ease-in-out;
animation-delay: 1s;
filter: drop-shadow(0 0 5px rgba(64, 82, 181, 0.5));
}
.animate-bell:hover {
animation: bellRing 0.5s ease-in-out;
animation-iteration-count: 1;
}
/* Fade-in animation */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
/* Pulse animation */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
</div> </div>
{% include footer.html %} {% include footer.html %}
+1 -1
View File
@@ -2,7 +2,7 @@
"entrypoints": { "entrypoints": {
"main": { "main": {
"css": [ "css": [
"/dist/main.aba03d4f.css" "/dist/main.4e82aad6.css"
], ],
"js": [ "js": [
"/dist/main.c89a204f.js" "/dist/main.c89a204f.js"
File diff suppressed because one or more lines are too long
+183
View File
@@ -0,0 +1,183 @@
/* Essential styles for the elegant notifications demo */
.bg-grid {
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='1' fill-rule='evenodd'%3E%3Ccircle cx='3' cy='3' r='1'/%3E%3Ccircle cx='13' cy='13' r='1'/%3E%3C/g%3E%3C/svg%3E");
}
@keyframes ping-slow {
0% {
transform: scale(1);
opacity: 1;
}
75%, 100% {
transform: scale(2);
opacity: 0;
}
}
.animate-ping-slow {
animation: ping-slow 2.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes pulse-slow {
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 0.8;
}
}
.animate-pulse-slow {
animation: pulse-slow 8s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.animate-pulse-medium {
animation: pulse-slow 6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes shine {
to {
left: 100%;
}
}
.animate-shine {
animation: shine 1s forwards;
}
/* New spaced notification type buttons */
.notification-types-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
width: 100%;
}
.type-button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.75rem 0.5rem;
font-size: 0.875rem;
font-weight: 500;
color: #4b5563;
border-radius: 0.5rem;
transition: all 0.2s ease;
border: 1px solid #e5e7eb;
background-color: #f9fafb;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.type-button:hover {
transform: translateY(-2px);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
}
.type-button.active {
border-width: 2px;
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.type-icon {
display: inline-flex;
width: 1.75rem;
height: 1.75rem;
align-items: center;
justify-content: center;
margin-bottom: 0.5rem;
padding: 0.25rem;
border-radius: 0.375rem;
}
/* Success type */
.type-button[data-type="success"] {
background-color: rgba(16, 185, 129, 0.05);
}
.type-button[data-type="success"].active {
background-color: rgba(16, 185, 129, 0.15);
border-color: #10B981;
}
.type-icon.success {
color: #10B981;
background-color: rgba(16, 185, 129, 0.1);
}
/* Error type */
.type-button[data-type="error"] {
background-color: rgba(239, 68, 68, 0.05);
}
.type-button[data-type="error"].active {
background-color: rgba(239, 68, 68, 0.15);
border-color: #EF4444;
}
.type-icon.error {
color: #EF4444;
background-color: rgba(239, 68, 68, 0.1);
}
/* Info type */
.type-button[data-type="info"] {
background-color: rgba(59, 130, 246, 0.05);
}
.type-button[data-type="info"].active {
background-color: rgba(59, 130, 246, 0.15);
border-color: #3B82F6;
}
.type-icon.info {
color: #3B82F6;
background-color: rgba(59, 130, 246, 0.1);
}
/* Warning type */
.type-button[data-type="warning"] {
background-color: rgba(245, 158, 11, 0.05);
}
.type-button[data-type="warning"].active {
background-color: rgba(245, 158, 11, 0.15);
border-color: #F59E0B;
}
.type-icon.warning {
color: #F59E0B;
background-color: rgba(245, 158, 11, 0.1);
}
/* Code panel styles */
.code-panel {
height: calc(100vh - 15rem);
max-height: 500px;
}
/* Override Prism default styles to make them work better in our UI */
pre[class*="language-"] {
background: #f9fafb !important; /* Use light gray instead of darker backgrounds */
margin: 0 !important;
padding: 1rem !important;
border-radius: 0.5rem;
border: 1px solid #e5e7eb;
}
code[class*="language-"] {
font-size: 0.875rem !important;
line-height: 1.6 !important;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
}
/* Tab button styles */
#php-tab.active, #js-tab.active {
@apply text-blue-600 border-b-2 border-blue-500;
}
/* Additional animation for code tabs */
.code-tab-transition {
transition: all 0.3s ease-in-out;
}
+64
View File
@@ -0,0 +1,64 @@
/* Bell animation */
@keyframes bellRing {
0%, 100% {
transform: rotate(0);
transform-origin: top center;
}
20% {
transform: rotate(8deg);
transform-origin: top center;
}
40% {
transform: rotate(-8deg);
transform-origin: top center;
}
60% {
transform: rotate(4deg);
transform-origin: top center;
}
80% {
transform: rotate(-4deg);
transform-origin: top center;
}
}
.animate-bell {
animation: bellRing 2s ease-in-out;
animation-delay: 1s;
filter: drop-shadow(0 0 5px rgba(64, 82, 181, 0.5));
}
.animate-bell:hover {
animation: bellRing 0.5s ease-in-out;
animation-iteration-count: 1;
}
/* Fade-in animation */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
/* Pulse animation */
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.6;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
+468
View File
@@ -0,0 +1,468 @@
// Current state management
const state = {
type: "success",
title: "",
message: "Your changes have been saved successfully!",
timeout: "",
fps: "",
position: "",
direction: "",
rtl: false,
theme: "",
closeButton: false,
escapeHtml: true
};
// Update current time
function updateCurrentTime() {
document.getElementById("current-time").textContent = "2025-03-11 07:38:23";
}
document.addEventListener("DOMContentLoaded", function() {
// Initialize particles.js if available
if (typeof particlesJS !== "undefined") {
particlesJS("particles-js", {
particles: {
number: { value: 24, density: { enable: true, value_area: 800 } },
color: { value: "#3b82f6" },
opacity: { value: 0.1, random: true },
size: { value: 2, random: true },
line_linked: {
enable: true,
distance: 150,
color: "#4b5563",
opacity: 0.1,
width: 1
},
move: {
enable: true,
speed: 0.4,
direction: "none",
random: true,
straight: false,
out_mode: "out"
}
}
});
}
// Initialize time with user-provided values
document.getElementById("current-time").textContent = "2025-03-11 07:40:35";
// 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("active"));
// Add active class to clicked button
btn.classList.add("active");
// Update state
state.type = btn.dataset.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("input", (e) => {
state.timeout = e.target.value ? parseInt(e.target.value) : "";
updateCodeDisplay();
});
document.getElementById("fps-input").addEventListener("input", (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 - directly execute notifications using the showNotificationsForHandler function
document.getElementById("show-notification-btn").addEventListener("click", () => {
// Update status
updateStatus("Launching notification...");
// Get current options from state
const options = {};
// Only add options that have been explicitly set
if (state.position) {
options.position = state.position;
}
if (state.timeout !== "") {
options.timeout = state.timeout;
}
if (state.fps !== "") {
options.fps = state.fps;
}
if (state.direction) {
options.direction = state.direction;
}
if (state.rtl) {
options.rtl = state.rtl;
}
if (state.closeButton) {
options.closeButton = state.closeButton;
}
if (!state.escapeHtml) {
options.escapeHtml = state.escapeHtml;
}
// Determine the handler based on theme
let handler = "flasher"; // Default handler
if (state.theme) {
handler = `theme.${state.theme}`;
}
// Try to use the direct notification function if it exists, otherwise use the stimulus controller
if (typeof showNotificationsForHandler === "function") {
// Create a custom notification based on user selections
const factory = flasher.use(handler);
// Configure flasher with options
if (Object.keys(options).length > 0) {
factory.options(options);
}
// Show the notification
if (state.title) {
factory[state.type](state.message, state.title);
} else {
factory[state.type](state.message);
}
} else {
console.log(`Would show a ${state.type} notification with message "${state.message}"${state.title ? ` and title "${state.title}"` : ""} using handler "${handler}"`);
console.log("Notification options:", options);
console.log("Stimulus controller should be triggered");
}
});
// Code tabs functionality
document.getElementById("php-tab").addEventListener("click", () => {
showCodeTab("php");
});
document.getElementById("js-tab").addEventListener("click", () => {
showCodeTab("js");
});
function showCodeTab(tab) {
// Hide all panels
document.querySelectorAll(".code-panel").forEach(panel => {
panel.classList.add("hidden");
});
// Show the selected panel
document.getElementById(`${tab}-code-panel`).classList.remove("hidden");
// Update tab styling
document.getElementById("php-tab").classList.remove("text-blue-600", "border-b-2", "border-blue-500");
document.getElementById("js-tab").classList.remove("text-blue-600", "border-b-2", "border-blue-500");
document.getElementById("php-tab").classList.add("text-gray-600");
document.getElementById("js-tab").classList.add("text-gray-600");
document.getElementById(`${tab}-tab`).classList.remove("text-gray-600");
document.getElementById(`${tab}-tab`).classList.add("text-blue-600", "border-b-2", "border-blue-500");
// Update code for the selected tab
updateCodeDisplay();
}
// Initialize
updateCodeDisplay();
});
// Update the status indicator
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);
}
// Update the code display with Prism syntax highlighting
function updateCodeDisplay() {
const phpCodeDisplay = document.getElementById("php-code-display");
const jsCodeDisplay = document.getElementById("js-code-display");
// PHP Code
let phpCode = `// Display a ${state.type} notification\n`;
// Handle theme usage
let useTheme = "";
if (state.theme) {
useTheme = `flash()->use('theme.${state.theme}')`;
} else {
useTheme = "flash()";
}
// Build options object
let hasOptions = false;
let phpOptionsCode = "";
if (state.position || state.timeout !== "" || state.fps !== "" ||
state.direction || state.rtl ||
state.closeButton || !state.escapeHtml) {
hasOptions = true;
phpOptionsCode = "->options([\n";
// Add each option that's been explicitly set
const optionLines = [];
if (state.position) {
optionLines.push(` 'position' => '${state.position}'`);
}
if (state.timeout !== "") {
optionLines.push(` 'timeout' => ${state.timeout}`);
}
if (state.fps !== "") {
optionLines.push(` 'fps' => ${state.fps}`);
}
if (state.direction) {
optionLines.push(` 'direction' => '${state.direction}'`);
}
if (state.rtl) {
optionLines.push(` 'rtl' => true`);
}
if (state.closeButton) {
optionLines.push(` 'closeButton' => true`);
}
if (!state.escapeHtml) {
optionLines.push(` 'escapeHtml' => false`);
}
phpOptionsCode += optionLines.join(",\n") + "\n ])";
}
// Build the full PHP code
if (hasOptions) {
phpCode += `${useTheme}${phpOptionsCode}\n`;
if (state.title) {
phpCode += ` ->${state.type}(\n '${state.message}', \n '${state.title}'\n );`;
} else {
phpCode += ` ->${state.type}('${state.message}');`;
}
} else {
// Simple call without options
if (state.title) {
phpCode += `${useTheme}->${state.type}('${state.message}', '${state.title}');`;
} else {
phpCode += `${useTheme}->${state.type}('${state.message}');`;
}
}
// JavaScript Code
let jsCode = `// Display a ${state.type} notification\n`;
// Handle theme usage
let jsUseTheme = "";
if (state.theme) {
jsUseTheme = `flasher.use('theme.${state.theme}')`;
} else {
jsUseTheme = "flasher";
}
// Build options object
let jsOptionsCode = "";
if (state.position || state.timeout !== "" || state.fps !== "" ||
state.direction || state.rtl ||
state.closeButton || !state.escapeHtml) {
jsOptionsCode = ".options({\n";
// Add each option that's been explicitly set
const optionLines = [];
if (state.position) {
optionLines.push(` position: '${state.position}'`);
}
if (state.timeout !== "") {
optionLines.push(` timeout: ${state.timeout}`);
}
if (state.fps !== "") {
optionLines.push(` fps: ${state.fps}`);
}
if (state.direction) {
optionLines.push(` direction: '${state.direction}'`);
}
if (state.rtl) {
optionLines.push(` rtl: true`);
}
if (state.closeButton) {
optionLines.push(` closeButton: true`);
}
if (!state.escapeHtml) {
optionLines.push(` escapeHtml: false`);
}
jsOptionsCode += optionLines.join(",\n") + "\n})";
}
// Build the full JavaScript code
if (jsOptionsCode) {
jsCode += `${jsUseTheme}${jsOptionsCode}\n`;
if (state.title) {
jsCode += ` .${state.type}('${state.message}', '${state.title}');`;
} else {
jsCode += ` .${state.type}('${state.message}');`;
}
} else {
// Simple call without options
if (state.title) {
jsCode += `${jsUseTheme}.${state.type}('${state.message}', '${state.title}');`;
} else {
jsCode += `${jsUseTheme}.${state.type}('${state.message}');`;
}
}
phpCodeDisplay.textContent = phpCode;
jsCodeDisplay.textContent = jsCode;
// Re-highlight with Prism
if (typeof Prism !== "undefined") {
Prism.highlightElement(phpCodeDisplay);
Prism.highlightElement(jsCodeDisplay);
}
}
// Add a custom implementation of showNotificationsForHandler if it doesn't exist
// This helps with preview in environments where the actual function isn't available
if (typeof showNotificationsForHandler === "undefined") {
window.showNotificationsForHandler = function(handler, options = {}) {
console.log(`Showing notifications for handler: ${handler}`);
console.log("Options:", options);
// Mock the notification display
console.log("Showing mock notifications sequence...");
};
window.flasher = {
use: function(handler) {
console.log(`Using handler: ${handler}`);
return {
options: function(options) {
console.log(`Setting options for ${handler}:`, options);
return this;
},
success: function(message, title) {
console.log(`Success: ${message}${title ? ` (${title})` : ""}`);
},
error: function(message, title) {
console.log(`Error: ${message}${title ? ` (${title})` : ""}`);
},
info: function(message, title) {
console.log(`Info: ${message}${title ? ` (${title})` : ""}`);
},
warning: function(message, title) {
console.log(`Warning: ${message}${title ? ` (${title})` : ""}`);
}
};
},
options: function(options) {
console.log("Setting global options:", options);
return this;
},
success: function(message, title) {
console.log(`Success: ${message}${title ? ` (${title})` : ""}`);
},
error: function(message, title) {
console.log(`Error: ${message}${title ? ` (${title})` : ""}`);
},
info: function(message, title) {
console.log(`Info: ${message}${title ? ` (${title})` : ""}`);
},
warning: function(message, title) {
console.log(`Warning: ${message}${title ? ` (${title})` : ""}`);
}
};
}
// Helper function for the Stimulus controller integration
document.addEventListener("DOMContentLoaded", function() {
// Implement a simple version of Stimulus controller behavior if Stimulus is not available
const notificationButtons = document.querySelectorAll("[data-controller=\"notification-demo\"]");
if (typeof Stimulus === "undefined") {
notificationButtons.forEach(button => {
if (!button.hasAttribute("data-stimulus-initialized")) {
button.setAttribute("data-stimulus-initialized", "true");
// Only wire up the click handler if not already using the event listener above
if (button.id !== 'show-notification-btn') {
button.addEventListener('click', function() {
if (typeof showNotificationsForHandler === 'function') {
showNotificationsForHandler('notyf');
} else {
console.log('Would show notifications using Stimulus controller');
}
});
}
}
});
}
});
+2
View File
@@ -7,6 +7,8 @@ module.exports = {
'./_includes/**/*.html', './_includes/**/*.html',
'./_layouts/**/*.html', './_layouts/**/*.html',
'./assets/**/*.{js,pcss}', './assets/**/*.{js,pcss}',
'./static/css/*.css',
'./static/js/*.js',
// Explicit exclusions // Explicit exclusions
// '!./_site/**', // Jekyll output directory // '!./_site/**', // Jekyll output directory