Compare commits

...

11 Commits

Author SHA1 Message Date
Younes ENNAJI 50ffa722a5 Add tests for OctaneListener and WorkerListener 2026-03-02 03:04:35 +00:00
Younes ENNAJI 93fc3c00e8 Add worker listeners for Octane and Symfony, update FallbackSession 2026-03-02 02:55:50 +00:00
Younes ENNAJI e783943933 Fix notification cleanup for Turbo and Livewire navigation 2026-03-02 02:42:38 +00:00
Younes ENNAJI f3d72c88c9 Fix array_merge error and update exception handling 2026-03-02 02:35:27 +00:00
Younes ENNAJI 63b9083782 Update README and documentation pages 2026-03-02 02:34:48 +00:00
Younes ENNAJI 1f7d884c4d Update README and JavaScript documentation 2026-03-02 02:01:00 +00:00
Younes ENNAJI 88f7546b50 Update documentation for installation and libraries 2026-03-02 01:29:04 +00:00
Younes ENNAJI cbd753371b Upgrade Symfony demo to v8.0, update dependencies 2026-03-02 00:40:18 +00:00
Younes ENNAJI 04d1e42fa3 Refine code block rendering and features 2026-03-01 23:27:02 +00:00
Younes ENNAJI 67b24f24bb Refactor demo notification display and asset loading 2026-03-01 23:21:50 +00:00
Younes ENNAJI b136cfcf6e Revamp Flasher demo application 2026-03-01 22:47:28 +00:00
110 changed files with 22482 additions and 10974 deletions
+3
View File
@@ -16,6 +16,9 @@
/taskfile.yml
/phpstan.neon
/demo/laravel/node_modules/
/demo/symfony/node_modules/
/npm-debug.log
/tests/Symfony/Fixtures/project/public/vendor/
+7
View File
@@ -2,6 +2,13 @@
## [Unreleased](https://github.com/php-flasher/php-flasher/compare/v2.1.4...2.x)
* feature [Laravel] Improve Laravel Octane support by resetting FallbackSession static storage between requests to prevent notification leakage
* feature [Symfony] Add FrankenPHP/Swoole/RoadRunner support with WorkerListener that implements ResetInterface and is tagged with kernel.reset
* feature [Symfony] Add reset() method to FallbackSession for long-running process support
* feature [Flasher] Add Hotwire/Turbo Drive support with turbo:before-cache event listener to clean up notifications before page caching
* fix [Flasher] Fix potential runtime error in Envelope::toArray() when no PresentableStampInterface stamps exist
* fix [Flasher] Use more specific \Random\RandomException in IdStamp instead of broad \Exception
* fix [Flasher] Update Livewire navigation cleanup to use correct .fl-wrapper selector instead of unused .fl-no-cache class
* feature [Flasher] Add event dispatching system for all notification adapters and themes with Livewire integration:
- [Toastr] Dispatch events: `flasher:toastr:click`, `flasher:toastr:close`, `flasher:toastr:show`, `flasher:toastr:hidden`
- [Noty] Dispatch events: `flasher:noty:click`, `flasher:noty:close`, `flasher:noty:show`, `flasher:noty:hover`
+10 -15
View File
@@ -59,7 +59,7 @@ flash()->success('Welcome aboard! Your account is ready.');
|---|:---:|:---:|
| **Zero JavaScript** | Write PHP only, frontend handled automatically | Requires manual JS setup |
| **Auto Asset Injection** | CSS/JS injected automatically | Manual script tags needed |
| **26 Built-in Themes** | Amazon, iOS, Slack, Material & more | Limited or no themes |
| **17 Built-in Themes** | Amazon, iOS, Slack, Material & more | Limited or no themes |
| **4 Notification Libraries** | Toastr, SweetAlert, Noty, Notyf | Single library only |
| **Livewire Integration** | Full event system support | Limited or none |
| **RTL Support** | Built-in right-to-left | Often missing |
@@ -94,9 +94,9 @@ flash()->success('Profile updated!', [
---
## 26 Beautiful Themes
## 17 Beautiful Themes
PHPFlasher includes **26 professionally designed themes** ready to use:
PHPFlasher includes **17 professionally designed themes** ready to use:
```php
flash()->success('Welcome!', ['theme' => 'amazon']);
@@ -111,12 +111,10 @@ flash()->success('Welcome!', ['theme' => 'material']);
| Theme | Style |
|-------|-------|
| `flasher` | Default clean design |
| `amazon` | Amazon-inspired |
| `bootstrap` | Bootstrap style |
| `amazon` | Amazon-inspired e-commerce |
| `ios` | Apple iOS notifications |
| `slack` | Slack messaging style |
| `material` | Google Material Design |
| `tailwind` | Tailwind CSS inspired |
| `google` | Google notifications |
| `facebook` | Facebook style |
| `minimal` | Ultra-clean minimal |
@@ -129,10 +127,6 @@ flash()->success('Welcome!', ['theme' => 'material']);
| `onyx` | Dark mode sleek |
| `ruby` | Bold ruby accents |
| `sapphire` | Elegant blue style |
| `shadow` | Soft shadow effects |
| `spectrum` | Colorful spectrum |
| `sunset` | Warm sunset colors |
| `zen` | Calm minimal design |
[**See all themes with live demos →**](https://php-flasher.io/themes)
@@ -296,17 +290,18 @@ flasher:
For complete documentation, visit **[php-flasher.io](https://php-flasher.io)**
- [Installation Guide](https://php-flasher.io/installation)
- [Usage & Examples](https://php-flasher.io/usage)
- [Themes Gallery](https://php-flasher.io/themes)
- [Laravel Integration](https://php-flasher.io/laravel)
- [Symfony Integration](https://php-flasher.io/symfony)
- [Livewire Integration](https://php-flasher.io/livewire)
- [Configuration Reference](https://php-flasher.io/configuration)
- [API Reference](https://php-flasher.io/api)
- [Inertia.js Integration](https://php-flasher.io/inertia)
- [Themes Gallery](https://php-flasher.io/themes)
- [JavaScript Usage](https://php-flasher.io/javascript)
---
## Contributing
Contributions are welcome! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
Contributions are welcome! Please feel free to submit a [Pull Request](https://github.com/php-flasher/php-flasher/pulls).
## Contributors
@@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
final class DemoController extends Controller
{
public function home(): View
{
return view('home');
}
public function types(): View
{
return view('types');
}
public function themes(): View
{
return view('themes');
}
public function adapters(): View
{
return view('adapters');
}
public function positions(): View
{
return view('positions');
}
public function examples(): View
{
return view('examples');
}
public function playground(): View
{
return view('playground');
}
public function livewire(): View
{
return view('livewire');
}
public function notify(Request $request): JsonResponse
{
$type = $request->input('type', 'success');
$message = $request->input('message', 'Notification message');
$title = $request->input('title');
$theme = $request->input('theme');
$position = $request->input('position', 'top-right');
$timeout = (int) $request->input('timeout', 5000);
$flasher = flash();
// Apply theme if specified
if ($theme && $theme !== 'flasher') {
$flasher = $flasher->use("theme.{$theme}");
}
$options = [
'position' => $position,
'timeout' => $timeout,
];
if ($title) {
$options['title'] = $title;
}
$flasher->{$type}($message, $options);
return response()->json(['success' => true]);
}
public function runExample(Request $request, string $scenario): JsonResponse
{
match ($scenario) {
'registration' => $this->registrationExample(),
'login_failed' => $this->loginFailedExample(),
'validation' => $this->validationExample(),
'shopping_cart' => $this->shoppingCartExample(),
'file_upload' => $this->fileUploadExample(),
'settings' => $this->settingsExample(),
'payment_success' => $this->paymentSuccessExample(),
'payment_failed' => $this->paymentFailedExample(),
'delete_confirm' => $this->deleteConfirmExample(),
'session_expiring' => $this->sessionExpiringExample(),
default => flash()->info('Example not found'),
};
return response()->json(['success' => true]);
}
private function registrationExample(): void
{
flash()->success('Welcome! Your account has been created.');
flash()->info('Please check your email to verify your account.');
}
private function loginFailedExample(): void
{
flash()->error('Invalid email or password.');
flash()->info('Forgot your password? Click here to reset.');
}
private function validationExample(): void
{
flash()->error('The email field is required.');
flash()->error('Password must be at least 8 characters.');
flash()->error('Please accept the terms and conditions.');
}
private function shoppingCartExample(): void
{
flash()->success('iPhone 15 Pro added to cart!');
flash()->warning('Only 2 items left in stock!');
flash()->info('Add $20 more for free shipping!');
}
private function fileUploadExample(): void
{
flash()->success('document.pdf uploaded successfully!');
flash()->info('File size: 2.4 MB');
}
private function settingsExample(): void
{
flash()->success('Settings saved successfully!');
flash()->info('Some changes may require a page refresh.');
}
private function paymentSuccessExample(): void
{
flash()->success('Payment of $149.99 confirmed!');
flash()->info('Order #12345 - Receipt sent to your email.');
}
private function paymentFailedExample(): void
{
flash()->error('Payment declined by your bank.');
flash()->warning('Please try a different payment method.');
flash()->info('Your cart has been saved.');
}
private function deleteConfirmExample(): void
{
flash()->warning('Are you sure? This action cannot be undone.');
flash()->info('Click confirm to delete or cancel to keep the item.');
}
private function sessionExpiringExample(): void
{
flash()->warning('Your session will expire in 5 minutes.');
flash()->info('Click anywhere to stay logged in.');
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Livewire;
use Livewire\Component;
class ContactForm extends Component
{
public string $name = '';
public string $email = '';
public string $message = '';
protected array $rules = [
'name' => 'required|min:2',
'email' => 'required|email',
'message' => 'required|min:10',
];
public function submit(): void
{
$this->validate();
// Simulate form processing
flash()->success('Message sent successfully!');
flash()->info('We will respond within 24 hours.');
$this->reset(['name', 'email', 'message']);
}
public function render()
{
return view('livewire.contact-form');
}
}
+13 -7
View File
@@ -1,25 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 1;
public int $count = 0;
public function increment()
public function increment(): void
{
flash()->success('increment');
$this->count++;
flash()->success("Count increased to {$this->count}!");
}
public function decrement()
public function decrement(): void
{
flash()->info('decrement');
$this->count--;
flash()->warning("Count decreased to {$this->count}");
}
public function reset(): void
{
$this->count = 0;
flash()->info('Counter has been reset.');
}
public function render()
+41
View File
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Livewire;
use Livewire\Component;
class DeleteItem extends Component
{
protected $listeners = [
'sweetalert:confirmed' => 'onConfirmed',
'sweetalert:denied' => 'onDenied',
];
public function confirmDelete(): void
{
sweetalert()
->showDenyButton()
->showCancelButton()
->confirmButtonText('Yes, delete it!')
->denyButtonText('Keep it')
->warning('Are you sure you want to delete this item?');
}
public function onConfirmed(array $payload): void
{
// Simulate deletion
flash()->success('Item has been deleted successfully!');
}
public function onDenied(array $payload): void
{
flash()->info('The item was kept safe.');
}
public function render()
{
return view('livewire.delete-item');
}
}
+14 -14
View File
@@ -15,26 +15,26 @@
],
"require": {
"php": "^8.2",
"inertiajs/inertia-laravel": "^2.0.1",
"laravel/framework": "^12.3",
"laravel/tinker": "^2.10.1",
"livewire/livewire": "^3.6.2",
"inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10",
"livewire/livewire": "^3.6",
"php-flasher/php-flasher": "@dev",
"spatie/laravel-csp": "^2.10.3",
"spatie/laravel-ray": "^1.40.2"
"spatie/laravel-csp": "^2.10",
"spatie/laravel-ray": "^1.40"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15.2",
"fakerphp/faker": "^1.24.1",
"barryvdh/laravel-debugbar": "^3.15",
"fakerphp/faker": "^1.24",
"larastan/larastan": "^3.2",
"laravel/pint": "^1.21.2",
"laravel/pint": "^1.21",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6.12",
"nunomaduro/collision": "^8.7.0",
"pestphp/pest": "^3.7.4",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.7",
"pestphp/pest": "^3.7",
"pestphp/pest-plugin-laravel": "^3.1",
"spatie/laravel-ignition": "^2.9.1",
"spatie/ray": "^1.41.6"
"spatie/laravel-ignition": "^2.9",
"spatie/ray": "^1.41"
},
"autoload": {
"psr-4": {
+1891 -1403
View File
File diff suppressed because it is too large Load Diff
+1371
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
/* PHPFlasher Laravel Demo - Custom Styles */
/* Any custom styles can be added here */
/* Note: Main styling is handled via Tailwind CSS v4 CDN in the layout */
+10
View File
@@ -1 +1,11 @@
/**
* PHPFlasher Laravel Demo
*
* Main JavaScript entry point.
* Note: PHPFlasher and other libraries are loaded via CDN in the layout.
*/
import './bootstrap';
// Custom demo functionality can be added here
console.log('PHPFlasher Laravel Demo loaded');
@@ -0,0 +1,354 @@
@extends('layouts.app')
@section('title', 'Adapters')
@section('content')
<div class="mb-8">
<h1 class="section-title">Notification Adapters</h1>
<p class="section-subtitle">PHPFlasher supports multiple notification libraries. Choose your favorite!</p>
</div>
<div class="space-y-8">
{{-- Flasher (Default) --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-indigo-100 rounded-xl">
<svg class="w-8 h-8 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Flasher</h2>
<p class="text-gray-500">Default adapter with 17+ themes</p>
</div>
</div>
<p class="text-gray-600 mb-4">The default PHPFlasher adapter with beautiful built-in themes. No additional JavaScript libraries required.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'This is the default Flasher notification!', adapter: 'flasher'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Error notification with Flasher', adapter: 'flasher'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Warning notification with Flasher', adapter: 'flasher'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Info notification with Flasher', adapter: 'flasher'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->success('Operation completed!');</code></pre>
</div>
</div>
</div>
</div>
</div>
{{-- Toastr --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-sky-500 to-blue-500"></div>
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-sky-100 rounded-xl">
<svg class="w-8 h-8 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Toastr</h2>
<p class="text-gray-500">Simple, elegant toast notifications</p>
</div>
</div>
<p class="text-gray-600 mb-4">Classic toast notifications with smooth animations. One of the most popular notification libraries.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Toastr success notification!', adapter: 'toastr'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Toastr error notification!', adapter: 'toastr'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Toastr warning notification!', adapter: 'toastr'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Toastr info notification!', adapter: 'toastr'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-toastr-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">toastr()->success('Data saved successfully!');
// With options
toastr()
->closeButton()
->progressBar()
->positionClass('toast-top-right')
->success('Profile updated!');</code></pre>
</div>
</div>
</div>
</div>
</div>
{{-- SweetAlert --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-pink-500 to-rose-500"></div>
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-pink-100 rounded-xl">
<svg class="w-8 h-8 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">SweetAlert 2</h2>
<p class="text-gray-500">Beautiful, responsive, customizable alerts</p>
</div>
</div>
<p class="text-gray-600 mb-4">Feature-rich modal dialogs with confirmations, inputs, and beautiful animations. Perfect for important user interactions.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'SweetAlert success!', title: 'Success', adapter: 'sweetalert'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'SweetAlert error message', title: 'Error', adapter: 'sweetalert'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Are you sure you want to proceed?', title: 'Warning', adapter: 'sweetalert', options: {showCancelButton: true}})"
class="btn btn-warning">Confirm Dialog</button>
<button onclick="showNotification({type: 'info', message: 'Here is some information', title: 'Info', adapter: 'sweetalert'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-sweetalert-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">sweetalert()->success('Great job!', 'Success');
// Confirmation dialog
sweetalert()
->showDenyButton()
->showCancelButton()
->confirmButtonText('Save')
->denyButtonText("Don't save")
->warning('Do you want to save the changes?');</code></pre>
</div>
</div>
</div>
</div>
</div>
{{-- Noty --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500"></div>
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-8 h-8 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Noty</h2>
<p class="text-gray-500">Dependency-free notification library</p>
</div>
</div>
<p class="text-gray-600 mb-4">A flexible, dependency-free notification library with multiple layouts and themes.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Noty success notification!', adapter: 'noty'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Noty error notification!', adapter: 'noty'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Noty warning notification!', adapter: 'noty'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Noty info notification!', adapter: 'noty'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-noty-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">noty()->success('Data saved!');
// With layout options
noty()
->layout('topCenter')
->timeout(3000)
->progressBar()
->info('Processing your request...');</code></pre>
</div>
</div>
</div>
</div>
</div>
{{-- Notyf --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-amber-500 to-orange-500"></div>
<div class="card-body">
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-amber-100 rounded-xl">
<svg class="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Notyf</h2>
<p class="text-gray-500">Minimalist, responsive notifications</p>
</div>
</div>
<p class="text-gray-600 mb-4">A minimalist JavaScript library for toast notifications. Tiny footprint with smooth animations.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Notyf success notification!', adapter: 'notyf'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Notyf error notification!', adapter: 'notyf'})"
class="btn btn-danger">Error</button>
</div>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-notyf-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">notyf()->success('Minimal and clean!');
// With options
notyf()
->position('x', 'right')
->position('y', 'top')
->dismissible(true)
->ripple(true)
->success('File uploaded successfully!');</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- Comparison Table --}}
<div class="card mt-8">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Adapter Comparison</h2>
</div>
<div class="card-body overflow-x-auto">
<table class="w-full text-left">
<thead>
<tr class="border-b border-gray-200">
<th class="py-3 px-4 font-semibold text-gray-700">Feature</th>
<th class="py-3 px-4 font-semibold text-gray-700">Flasher</th>
<th class="py-3 px-4 font-semibold text-gray-700">Toastr</th>
<th class="py-3 px-4 font-semibold text-gray-700">SweetAlert</th>
<th class="py-3 px-4 font-semibold text-gray-700">Noty</th>
<th class="py-3 px-4 font-semibold text-gray-700">Notyf</th>
</tr>
</thead>
<tbody class="text-gray-600">
<tr class="border-b border-gray-100">
<td class="py-3 px-4">Toast Notifications</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-3 px-4">Modal Dialogs</td>
<td class="py-3 px-4 text-gray-400">No</td>
<td class="py-3 px-4 text-gray-400">No</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-gray-400">No</td>
<td class="py-3 px-4 text-gray-400">No</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-3 px-4">Confirmations</td>
<td class="py-3 px-4 text-gray-400">No</td>
<td class="py-3 px-4 text-gray-400">No</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-gray-400">No</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-3 px-4">Progress Bar</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-emerald-600">Yes</td>
<td class="py-3 px-4 text-gray-400">No</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-3 px-4">Built-in Themes</td>
<td class="py-3 px-4 text-emerald-600">17+</td>
<td class="py-3 px-4 text-emerald-600">4</td>
<td class="py-3 px-4 text-emerald-600">1</td>
<td class="py-3 px-4 text-emerald-600">5</td>
<td class="py-3 px-4 text-emerald-600">1</td>
</tr>
<tr>
<td class="py-3 px-4">Dependencies</td>
<td class="py-3 px-4">None</td>
<td class="py-3 px-4">jQuery</td>
<td class="py-3 px-4">None</td>
<td class="py-3 px-4">None</td>
<td class="py-3 px-4">None</td>
</tr>
</tbody>
</table>
</div>
</div>
@endsection
@@ -0,0 +1,349 @@
@extends('layouts.app')
@section('title', 'Real-World Examples')
@section('content')
<div class="mb-8">
<h1 class="section-title">Real-World Examples</h1>
<p class="section-subtitle">See how PHPFlasher handles common application scenarios. Click to run each example.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
{{-- User Registration --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-green-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">User Registration</h3>
<p class="text-sm text-gray-500">Account creation flow</p>
</div>
</div>
<p class="text-gray-600 mb-4">Simulates a successful user registration with welcome message and email verification notice.</p>
<button onclick="runExample('registration')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Welcome! Your account has been created.');
flash()->info('Please check your email to verify your account.');</code></pre>
</div>
</div>
</div>
{{-- Login Flow --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-rose-500 to-red-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-rose-100 rounded-xl">
<svg class="w-6 h-6 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Login Failed</h3>
<p class="text-sm text-gray-500">Authentication error</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows how to display login failure messages with helpful guidance.</p>
<button onclick="runExample('login_failed')" class="btn btn-danger w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->error('Invalid email or password.');
flash()->info('Forgot your password? Click here to reset.');</code></pre>
</div>
</div>
</div>
{{-- Form Validation --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-amber-500 to-yellow-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-amber-100 rounded-xl">
<svg class="w-6 h-6 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Form Validation</h3>
<p class="text-sm text-gray-500">Multiple field errors</p>
</div>
</div>
<p class="text-gray-600 mb-4">Demonstrates displaying multiple validation errors from a form submission.</p>
<button onclick="runExample('validation')" class="btn btn-warning w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->error('The email field is required.');
flash()->error('Password must be at least 8 characters.');
flash()->error('Please accept the terms and conditions.');</code></pre>
</div>
</div>
</div>
{{-- Shopping Cart --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-indigo-100 rounded-xl">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Shopping Cart</h3>
<p class="text-sm text-gray-500">E-commerce interactions</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows notifications for adding items to cart with stock warnings and promotions.</p>
<button onclick="runExample('shopping_cart')" class="btn btn-primary w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('iPhone 15 Pro added to cart!');
flash()->warning('Only 2 items left in stock!');
flash()->info('Add $20 more for free shipping!');</code></pre>
</div>
</div>
</div>
{{-- File Upload --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-sky-500 to-cyan-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-sky-100 rounded-xl">
<svg class="w-6 h-6 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">File Upload</h3>
<p class="text-sm text-gray-500">Upload progress and status</p>
</div>
</div>
<p class="text-gray-600 mb-4">Demonstrates file upload progress with success and additional info.</p>
<button onclick="runExample('file_upload')" class="btn btn-info w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('document.pdf uploaded successfully!');
flash()->info('File size: 2.4 MB');</code></pre>
</div>
</div>
</div>
{{-- Settings Saved --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-teal-500 to-green-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-teal-100 rounded-xl">
<svg class="w-6 h-6 text-teal-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Settings Saved</h3>
<p class="text-sm text-gray-500">Preferences updated</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows settings save confirmation with additional context.</p>
<button onclick="runExample('settings')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Settings saved successfully!');
flash()->info('Some changes may require a page refresh.');</code></pre>
</div>
</div>
</div>
{{-- Payment Success --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-green-500 to-emerald-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-green-100 rounded-xl">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Payment Success</h3>
<p class="text-sm text-gray-500">Transaction completed</p>
</div>
</div>
<p class="text-gray-600 mb-4">Payment confirmation with order details and receipt notification.</p>
<button onclick="runExample('payment_success')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Payment of $149.99 confirmed!');
flash()->info('Order #12345 - Receipt sent to your email.');</code></pre>
</div>
</div>
</div>
{{-- Payment Failed --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-red-500 to-rose-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-red-100 rounded-xl">
<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Payment Failed</h3>
<p class="text-sm text-gray-500">Transaction declined</p>
</div>
</div>
<p class="text-gray-600 mb-4">Payment failure with helpful guidance for resolving the issue.</p>
<button onclick="runExample('payment_failed')" class="btn btn-danger w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->error('Payment declined by your bank.');
flash()->warning('Please try a different payment method.');
flash()->info('Your cart has been saved.');</code></pre>
</div>
</div>
</div>
{{-- Delete Confirmation --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-pink-500 to-rose-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-pink-100 rounded-xl">
<svg class="w-6 h-6 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Delete Confirmation</h3>
<p class="text-sm text-gray-500">SweetAlert dialog</p>
</div>
</div>
<p class="text-gray-600 mb-4">Uses SweetAlert for a confirmation dialog before deleting.</p>
<button onclick="runExample('delete_confirm')" class="btn btn-danger w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">sweetalert()
->showCancelButton()
->confirmButtonText('Yes, delete it!')
->cancelButtonText('Cancel')
->warning('Are you sure? This cannot be undone.');</code></pre>
</div>
</div>
</div>
{{-- Session Expiring --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-orange-500 to-amber-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-orange-100 rounded-xl">
<svg class="w-6 h-6 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Session Expiring</h3>
<p class="text-sm text-gray-500">Timeout warning</p>
</div>
</div>
<p class="text-gray-600 mb-4">Alerts user when their session is about to expire.</p>
<button onclick="runExample('session_expiring')" class="btn btn-warning w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->warning('Your session will expire in 5 minutes.');
flash()->info('Click anywhere to stay logged in.');</code></pre>
</div>
</div>
</div>
</div>
@endsection
+197
View File
@@ -0,0 +1,197 @@
@extends('layouts.app')
@section('title', 'Home')
@section('content')
{{-- Hero Section --}}
<div class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
Beautiful Flash Notifications
</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto mb-8">
PHPFlasher makes it easy to add elegant notifications to your Laravel application.
Try the quick demos below!
</p>
{{-- Quick Demo Buttons --}}
<div class="flex flex-wrap justify-center gap-3 mb-8">
<button onclick="showNotification({type: 'success', message: 'Operation completed successfully!', title: 'Success'})"
class="btn btn-success">
<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="M5 13l4 4L19 7"></path>
</svg>
Success
</button>
<button onclick="showNotification({type: 'error', message: 'Something went wrong. Please try again.', title: 'Error'})"
class="btn btn-danger">
<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="M6 18L18 6M6 6l12 12"></path>
</svg>
Error
</button>
<button onclick="showNotification({type: 'warning', message: 'Please review your input before continuing.', title: 'Warning'})"
class="btn btn-warning">
<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 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
Warning
</button>
<button onclick="showNotification({type: 'info', message: 'Here is some useful information for you.', title: 'Info'})"
class="btn btn-info">
<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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Info
</button>
</div>
</div>
{{-- Features Grid --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
{{-- Feature 1: Types --}}
<a href="{{ route('types') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-emerald-100 rounded-lg group-hover:bg-emerald-200 transition-colors">
<svg class="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Types</h3>
</div>
<p class="text-gray-600">Success, error, warning, and info notifications for every use case.</p>
</div>
</a>
{{-- Feature 2: Themes --}}
<a href="{{ route('themes') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-purple-500 to-pink-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-purple-100 rounded-lg group-hover:bg-purple-200 transition-colors">
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">17+ Themes</h3>
</div>
<p class="text-gray-600">Material, iOS, Slack, Amazon, and many more beautiful themes.</p>
</div>
</a>
{{-- Feature 3: Adapters --}}
<a href="{{ route('adapters') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-blue-500 to-cyan-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Multiple Adapters</h3>
</div>
<p class="text-gray-600">Toastr, SweetAlert, Noty, and Notyf adapters included.</p>
</div>
</a>
{{-- Feature 4: Positions --}}
<a href="{{ route('positions') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-amber-500 to-orange-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-amber-100 rounded-lg group-hover:bg-amber-200 transition-colors">
<svg class="w-6 h-6 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Flexible Positions</h3>
</div>
<p class="text-gray-600">Place notifications anywhere on the screen.</p>
</div>
</a>
{{-- Feature 5: Examples --}}
<a href="{{ route('examples') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-rose-500 to-red-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-rose-100 rounded-lg group-hover:bg-rose-200 transition-colors">
<svg class="w-6 h-6 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Real Examples</h3>
</div>
<p class="text-gray-600">User registration, shopping cart, payments, and more.</p>
</div>
</a>
{{-- Feature 6: Playground --}}
<a href="{{ route('playground') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-violet-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-indigo-100 rounded-lg group-hover:bg-indigo-200 transition-colors">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Interactive Playground</h3>
</div>
<p class="text-gray-600">Build and customize notifications in real-time.</p>
</div>
</a>
</div>
{{-- Quick Start Code --}}
<div class="card mb-12">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Quick Start</h2>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Get started with PHPFlasher in seconds. Just install and use!</p>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-laravel</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">// In your controller
flash()->success('Profile updated successfully!');
// With options
flash()->success('Welcome back!', [
'position' => 'top-right',
'timeout' => 5000,
]);
// Using themes
flash()->use('theme.material')->info('New feature available!');</code></pre>
</div>
</div>
</div>
{{-- CTA Section --}}
<div class="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-2xl p-8 text-center text-white">
<h2 class="text-2xl md:text-3xl font-bold mb-4">Ready to try PHPFlasher?</h2>
<p class="text-indigo-100 mb-6 max-w-xl mx-auto">
Explore the interactive playground to customize notifications and see the generated code.
</p>
<a href="{{ route('playground') }}" class="inline-flex items-center px-6 py-3 bg-white text-indigo-600 font-semibold rounded-lg hover:bg-indigo-50 transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Open Playground
</a>
</div>
@endsection
@@ -0,0 +1,297 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', 'PHPFlasher Demo') - Laravel</title>
{{-- Tailwind CSS v4 CDN --}}
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
{{-- Prism.js for syntax highlighting --}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css">
{{-- Prism.js code block fixes --}}
<style>
.code-block pre {
margin: 0 !important;
border-radius: 0 !important;
background: #2d2d2d !important;
padding: 1rem !important;
overflow-x: auto;
}
.code-block code[class*="language-"] {
font-size: 0.875rem;
line-height: 1.6;
background: transparent !important;
}
</style>
{{-- Custom Tailwind components --}}
<style type="text/tailwindcss">
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 inline-flex items-center justify-center gap-2 cursor-pointer;
}
.btn-primary {
@apply bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500/50;
}
.btn-success {
@apply bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500/50;
}
.btn-danger {
@apply bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500/50;
}
.btn-warning {
@apply bg-amber-500 text-white hover:bg-amber-600 focus:ring-amber-400/50;
}
.btn-info {
@apply bg-sky-500 text-white hover:bg-sky-600 focus:ring-sky-400/50;
}
.btn-outline {
@apply border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-indigo-500/50;
}
.btn-sm {
@apply px-3 py-1.5 text-sm;
}
.card {
@apply bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300;
}
.card-header {
@apply px-6 py-4 border-b border-gray-100;
}
.card-body {
@apply p-6;
}
.code-block {
@apply rounded-lg overflow-hidden shadow-lg my-4;
}
.code-header {
@apply bg-gray-800 text-gray-300 px-4 py-2 flex justify-between items-center text-sm font-mono;
}
.section-title {
@apply text-2xl font-bold text-gray-800 mb-2;
}
.section-subtitle {
@apply text-gray-600 mb-6;
}
.nav-link {
@apply px-3 py-2 rounded-lg text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 transition-colors font-medium;
}
.nav-link-active {
@apply text-indigo-600 bg-indigo-50;
}
}
</style>
@stack('styles')
{{-- Preload theme CSS for playground --}}
<link rel="stylesheet" href="/vendor/flasher/flasher.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/flasher/flasher.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/material/material.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/ios/ios.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/slack/slack.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/amazon/amazon.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/google/google.min.css">
{{-- Load main flasher script first, then themes --}}
<script src="/vendor/flasher/flasher.min.js"></script>
<script src="/vendor/flasher/themes/flasher/flasher.min.js"></script>
<script src="/vendor/flasher/themes/material/material.min.js"></script>
<script src="/vendor/flasher/themes/ios/ios.min.js"></script>
<script src="/vendor/flasher/themes/slack/slack.min.js"></script>
<script src="/vendor/flasher/themes/amazon/amazon.min.js"></script>
<script src="/vendor/flasher/themes/google/google.min.js"></script>
{{-- Render server-side notifications --}}
@flasher_render
</head>
<body class="bg-gray-50 antialiased text-gray-900 min-h-screen flex flex-col">
{{-- Header --}}
<header class="bg-white border-b border-gray-200 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
{{-- Logo --}}
<a href="{{ route('home') }}" class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-indigo-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-xl font-bold text-gray-900">PHPFlasher</span>
<span class="hidden sm:inline-block px-2 py-0.5 text-xs font-medium bg-red-100 text-red-600 rounded-full">Laravel</span>
</a>
{{-- Desktop Navigation --}}
<nav class="hidden md:flex items-center space-x-1">
<a href="{{ route('home') }}" class="nav-link @if(request()->routeIs('home')) nav-link-active @endif">Home</a>
<a href="{{ route('types') }}" class="nav-link @if(request()->routeIs('types')) nav-link-active @endif">Types</a>
<a href="{{ route('themes') }}" class="nav-link @if(request()->routeIs('themes')) nav-link-active @endif">Themes</a>
<a href="{{ route('adapters') }}" class="nav-link @if(request()->routeIs('adapters')) nav-link-active @endif">Adapters</a>
<a href="{{ route('positions') }}" class="nav-link @if(request()->routeIs('positions')) nav-link-active @endif">Positions</a>
<a href="{{ route('examples') }}" class="nav-link @if(request()->routeIs('examples')) nav-link-active @endif">Examples</a>
<a href="{{ route('playground') }}" class="nav-link @if(request()->routeIs('playground')) nav-link-active @endif">Playground</a>
<a href="{{ route('livewire') }}" class="nav-link @if(request()->routeIs('livewire')) nav-link-active @endif">Livewire</a>
</nav>
{{-- External Links --}}
<div class="hidden md:flex items-center space-x-3">
<a href="https://github.com/php-flasher/php-flasher" target="_blank" class="text-gray-500 hover:text-gray-700">
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
</a>
<a href="https://php-flasher.io" target="_blank" class="btn btn-primary btn-sm">
Documentation
</a>
</div>
{{-- Mobile menu button --}}
<button id="mobile-menu-btn" class="md:hidden p-2 rounded-lg text-gray-500 hover:bg-gray-100">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
{{-- Mobile Navigation --}}
<div id="mobile-menu" class="hidden md:hidden border-t border-gray-200 bg-white">
<div class="px-4 py-3 space-y-1">
<a href="{{ route('home') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('home')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Home</a>
<a href="{{ route('types') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('types')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Types</a>
<a href="{{ route('themes') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('themes')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Themes</a>
<a href="{{ route('adapters') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('adapters')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Adapters</a>
<a href="{{ route('positions') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('positions')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Positions</a>
<a href="{{ route('examples') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('examples')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Examples</a>
<a href="{{ route('playground') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('playground')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Playground</a>
<a href="{{ route('livewire') }}" class="block px-3 py-2 rounded-lg @if(request()->routeIs('livewire')) bg-indigo-50 text-indigo-600 @else text-gray-600 hover:bg-gray-50 @endif">Livewire</a>
</div>
</div>
</header>
{{-- Main Content --}}
<main class="flex-1">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
@yield('content')
</div>
</main>
{{-- Footer --}}
<footer class="bg-white border-t border-gray-200 mt-auto">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<div class="flex items-center space-x-2 text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span>PHPFlasher Demo</span>
</div>
<div class="flex items-center space-x-6 text-sm text-gray-500">
<a href="https://php-flasher.io" target="_blank" class="hover:text-indigo-600">Documentation</a>
<a href="https://github.com/php-flasher/php-flasher" target="_blank" class="hover:text-indigo-600">GitHub</a>
<span>Made with ❤️ by <a href="https://github.com/yoeunes" target="_blank" class="hover:text-indigo-600">Younes</a></span>
</div>
</div>
</div>
</footer>
{{-- Scripts --}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup-templating.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-php.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
<script>
// Mobile menu toggle
document.getElementById('mobile-menu-btn')?.addEventListener('click', function() {
document.getElementById('mobile-menu')?.classList.toggle('hidden');
});
// CSRF token for AJAX requests
window.csrfToken = '{{ csrf_token() }}';
// Helper function for notifications using PHPFlasher's JavaScript API
window.showNotification = function(options) {
const type = options.type || 'success';
const message = options.message || 'Notification message';
const title = options.title || null;
const position = options.position || 'top-right';
const timeout = options.timeout || 5000;
// Use the theme if specified
let plugin = 'flasher';
if (options.theme && options.theme !== 'flasher') {
plugin = 'theme.' + options.theme;
}
// Build notification options
const notificationOptions = {
position: position,
timeout: timeout,
};
// Call PHPFlasher's JavaScript API
flasher.use(plugin).flash(type, message, title, notificationOptions);
};
// Helper function for running examples using client-side notifications
window.runExample = function(scenario) {
const examples = {
registration: [
{ type: 'success', message: 'Welcome! Your account has been created.' },
{ type: 'info', message: 'Please check your email to verify your account.' }
],
login_failed: [
{ type: 'error', message: 'Invalid email or password.' },
{ type: 'info', message: 'Forgot your password? Click here to reset.' }
],
validation: [
{ type: 'error', message: 'The email field is required.' },
{ type: 'error', message: 'Password must be at least 8 characters.' },
{ type: 'error', message: 'Please accept the terms and conditions.' }
],
shopping_cart: [
{ type: 'success', message: 'iPhone 15 Pro added to cart!' },
{ type: 'warning', message: 'Only 2 items left in stock!' },
{ type: 'info', message: 'Add $20 more for free shipping!' }
],
file_upload: [
{ type: 'success', message: 'document.pdf uploaded successfully!' },
{ type: 'info', message: 'File size: 2.4 MB' }
],
settings: [
{ type: 'success', message: 'Settings saved successfully!' },
{ type: 'info', message: 'Some changes may require a page refresh.' }
],
payment_success: [
{ type: 'success', message: 'Payment of $149.99 confirmed!' },
{ type: 'info', message: 'Order #12345 - Receipt sent to your email.' }
],
payment_failed: [
{ type: 'error', message: 'Payment declined by your bank.' },
{ type: 'warning', message: 'Please try a different payment method.' },
{ type: 'info', message: 'Your cart has been saved.' }
],
delete_confirm: [
{ type: 'warning', message: 'Are you sure? This action cannot be undone.' },
{ type: 'info', message: 'Click confirm to delete or cancel to keep the item.' }
],
session_expiring: [
{ type: 'warning', message: 'Your session will expire in 5 minutes.' },
{ type: 'info', message: 'Click anywhere to stay logged in.' }
]
};
const notifications = examples[scenario] || [{ type: 'info', message: 'Example not found' }];
notifications.forEach((notification, index) => {
setTimeout(() => {
flasher.flash(notification.type, notification.message);
}, index * 300);
});
};
</script>
@stack('scripts')
</body>
</html>
@@ -0,0 +1,220 @@
@extends('layouts.app')
@section('title', 'Livewire Integration')
@section('content')
<div class="mb-8">
<h1 class="section-title">Livewire Integration</h1>
<p class="section-subtitle">PHPFlasher works seamlessly with Laravel Livewire. See live examples below.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{{-- Counter Component --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-6">
<div class="p-3 bg-indigo-100 rounded-xl">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Counter Component</h3>
<p class="text-sm text-gray-500">Simple state management</p>
</div>
</div>
@livewire('counter')
<div class="code-block mt-6">
<div class="code-header">
<span>app/Livewire/Counter.php</span>
</div>
<pre class="!m-0"><code class="language-php">class Counter extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
flash()->success("Count increased to {$this->count}!");
}
public function decrement(): void
{
$this->count--;
flash()->warning("Count decreased to {$this->count}");
}
public function reset(): void
{
$this->count = 0;
flash()->info('Counter has been reset.');
}
}</code></pre>
</div>
</div>
</div>
{{-- Contact Form Component --}}
<div class="card">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-6">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Contact Form</h3>
<p class="text-sm text-gray-500">Form validation example</p>
</div>
</div>
@livewire('contact-form')
<div class="code-block mt-6">
<div class="code-header">
<span>app/Livewire/ContactForm.php</span>
</div>
<pre class="!m-0"><code class="language-php">class ContactForm extends Component
{
public string $name = '';
public string $email = '';
public string $message = '';
protected $rules = [
'name' => 'required|min:2',
'email' => 'required|email',
'message' => 'required|min:10',
];
public function submit(): void
{
$this->validate();
// Process form...
flash()->success('Message sent successfully!');
flash()->info('We will respond within 24 hours.');
$this->reset(['name', 'email', 'message']);
}
}</code></pre>
</div>
</div>
</div>
</div>
{{-- SweetAlert Events --}}
<div class="card mt-8">
<div class="h-2 bg-gradient-to-r from-pink-500 to-rose-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-6">
<div class="p-3 bg-pink-100 rounded-xl">
<svg class="w-6 h-6 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">SweetAlert Confirmations</h3>
<p class="text-sm text-gray-500">Handle dialog responses in Livewire</p>
</div>
</div>
@livewire('delete-item')
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6">
<div class="code-block">
<div class="code-header">
<span>Component</span>
</div>
<pre class="!m-0"><code class="language-php">class DeleteItem extends Component
{
protected $listeners = [
'sweetalert:confirmed' => 'onConfirmed',
'sweetalert:denied' => 'onDenied',
];
public function confirmDelete(): void
{
sweetalert()
->showDenyButton()
->showCancelButton()
->confirmButtonText('Yes, delete!')
->denyButtonText('Keep it')
->warning('Delete this item?');
}
public function onConfirmed(array $payload): void
{
// Delete the item...
flash()->success('Item deleted!');
}
public function onDenied(array $payload): void
{
flash()->info('Item was kept.');
}
}</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Blade Template</span>
</div>
<pre class="!m-0"><code class="language-php">&lt;div&gt;
&lt;button wire:click="confirmDelete"
class="btn btn-danger"&gt;
Delete Item
&lt;/button&gt;
&lt;/div&gt;</code></pre>
</div>
</div>
</div>
</div>
{{-- Installation Guide --}}
<div class="card mt-8">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Livewire Setup</h2>
</div>
<div class="card-body">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-800 mb-3">1. Install PHPFlasher</h3>
<div class="code-block">
<div class="code-header"><span>Terminal</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-laravel</code></pre>
</div>
</div>
<div>
<h3 class="font-semibold text-gray-800 mb-3">2. Use in Components</h3>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">public function save()
{
// Your logic...
flash()->success('Saved!');
}</code></pre>
</div>
</div>
</div>
<div class="mt-6 p-4 bg-indigo-50 rounded-lg">
<div class="flex items-start space-x-3">
<svg class="w-6 h-6 text-indigo-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<h4 class="font-semibold text-indigo-800">Automatic Livewire Support</h4>
<p class="text-indigo-700 text-sm mt-1">PHPFlasher automatically detects Livewire requests and handles them appropriately. No additional configuration needed!</p>
</div>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,41 @@
<form wire:submit="submit" class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Name</label>
<input type="text" id="name" wire:model="name"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 @error('name') border-rose-500 @enderror"
placeholder="Your name">
@error('name')
<p class="mt-1 text-sm text-rose-500">{{ $message }}</p>
@enderror
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">Email</label>
<input type="email" id="email" wire:model="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 @error('email') border-rose-500 @enderror"
placeholder="your@email.com">
@error('email')
<p class="mt-1 text-sm text-rose-500">{{ $message }}</p>
@enderror
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700 mb-1">Message</label>
<textarea id="message" wire:model="message" rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 @error('message') border-rose-500 @enderror"
placeholder="Your message..."></textarea>
@error('message')
<p class="mt-1 text-sm text-rose-500">{{ $message }}</p>
@enderror
</div>
<button type="submit"
class="w-full px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg transition-colors flex items-center justify-center space-x-2">
<svg wire:loading wire:target="submit" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span wire:loading.remove wire:target="submit">Send Message</span>
<span wire:loading wire:target="submit">Sending...</span>
</button>
</form>
@@ -1,7 +1,22 @@
<div>
<h1>{{ $count }}</h1>
<div class="flex items-center justify-center space-x-4">
<button wire:click="decrement"
class="w-12 h-12 bg-rose-500 hover:bg-rose-600 text-white rounded-full flex items-center justify-center text-2xl font-bold transition-colors">
-
</button>
<button wire:click="increment">+</button>
<div class="w-24 h-24 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-2xl flex items-center justify-center">
<span class="text-4xl font-bold text-white">{{ $count }}</span>
</div>
<button wire:click="decrement">-</button>
<button wire:click="increment"
class="w-12 h-12 bg-emerald-500 hover:bg-emerald-600 text-white rounded-full flex items-center justify-center text-2xl font-bold transition-colors">
+
</button>
</div>
<div class="flex justify-center mt-4">
<button wire:click="reset"
class="px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg text-sm font-medium transition-colors">
Reset
</button>
</div>
@@ -0,0 +1,21 @@
<div class="flex flex-col items-center space-y-4">
<div class="flex items-center space-x-4">
<div class="w-16 h-16 bg-gray-100 rounded-lg flex items-center justify-center">
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
<div>
<h4 class="font-semibold text-gray-800">Important Document.pdf</h4>
<p class="text-sm text-gray-500">2.4 MB - Modified today</p>
</div>
</div>
<button wire:click="confirmDelete"
class="px-6 py-2 bg-rose-600 hover:bg-rose-700 text-white font-medium rounded-lg transition-colors flex items-center space-x-2">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
<span>Delete File</span>
</button>
</div>
@@ -0,0 +1,390 @@
@extends('layouts.app')
@section('title', 'Playground')
@section('content')
<div class="mb-8">
<h1 class="section-title">Interactive Playground</h1>
<p class="section-subtitle">Build and customize notifications in real-time. See the generated PHP code instantly.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{{-- Configuration Panel --}}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Configuration</h2>
</div>
<div class="card-body space-y-6">
{{-- Type --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Notification Type</label>
<div class="grid grid-cols-4 gap-2">
<button onclick="setType('success')" id="type-success" class="type-btn px-3 py-2 rounded-lg border-2 border-emerald-500 bg-emerald-50 text-emerald-700 font-medium text-sm">
Success
</button>
<button onclick="setType('error')" id="type-error" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-rose-500 hover:bg-rose-50 hover:text-rose-700">
Error
</button>
<button onclick="setType('warning')" id="type-warning" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-amber-500 hover:bg-amber-50 hover:text-amber-700">
Warning
</button>
<button onclick="setType('info')" id="type-info" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-sky-500 hover:bg-sky-50 hover:text-sky-700">
Info
</button>
</div>
</div>
{{-- Title --}}
<div>
<label for="title" class="block text-sm font-medium text-gray-700 mb-2">Title (optional)</label>
<input type="text" id="title" placeholder="Enter a title..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
oninput="updateCode()">
</div>
{{-- Message --}}
<div>
<label for="message" class="block text-sm font-medium text-gray-700 mb-2">Message</label>
<textarea id="message" rows="2" placeholder="Enter your notification message..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
oninput="updateCode()">Operation completed successfully!</textarea>
</div>
{{-- Position --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Position</label>
<select id="position" onchange="updateCode()"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="top-right" selected>Top Right (default)</option>
<option value="top-left">Top Left</option>
<option value="top-center">Top Center</option>
<option value="bottom-right">Bottom Right</option>
<option value="bottom-left">Bottom Left</option>
<option value="bottom-center">Bottom Center</option>
<option value="center">Center</option>
</select>
</div>
{{-- Theme --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Theme</label>
<select id="theme" onchange="updateCode()"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="">Default (Flasher)</option>
<option value="flasher">Flasher</option>
<option value="material">Material</option>
<option value="ios">iOS</option>
<option value="slack">Slack</option>
<option value="amazon">Amazon</option>
<option value="google">Google</option>
<option value="facebook">Facebook</option>
<option value="minimal">Minimal</option>
<option value="neon">Neon</option>
<option value="emerald">Emerald</option>
<option value="sapphire">Sapphire</option>
<option value="ruby">Ruby</option>
<option value="amber">Amber</option>
<option value="jade">Jade</option>
<option value="onyx">Onyx</option>
<option value="crystal">Crystal</option>
<option value="aurora">Aurora</option>
</select>
</div>
{{-- Adapter --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Adapter</label>
<select id="adapter" onchange="updateCode()"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="flasher" selected>Flasher (default)</option>
<option value="toastr">Toastr</option>
<option value="sweetalert">SweetAlert</option>
<option value="noty">Noty</option>
<option value="notyf">Notyf</option>
</select>
</div>
{{-- Timeout --}}
<div>
<label for="timeout" class="block text-sm font-medium text-gray-700 mb-2">
Timeout: <span id="timeout-value">5000</span>ms
</label>
<input type="range" id="timeout" min="1000" max="10000" step="500" value="5000"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
oninput="document.getElementById('timeout-value').textContent = this.value; updateCode()">
</div>
{{-- Show Button --}}
<button onclick="showPlaygroundNotification()" class="btn btn-primary w-full">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Show Notification
</button>
</div>
</div>
{{-- Code Preview --}}
<div class="space-y-6">
<div class="card">
<div class="card-header flex justify-between items-center">
<h2 class="text-xl font-bold text-gray-800">Generated Code</h2>
<button onclick="copyCode()" class="btn btn-sm btn-outline">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
Copy
</button>
</div>
<div class="card-body p-0">
<div class="code-block !my-0 !rounded-none">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php" id="generated-code">flash()->success('Operation completed successfully!');</code></pre>
</div>
</div>
</div>
{{-- Preview Card --}}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Preview</h2>
</div>
<div class="card-body">
<div class="bg-gray-100 rounded-lg p-6 min-h-[200px] flex items-center justify-center">
<div id="preview-notification" class="max-w-sm w-full">
{{-- Preview will be rendered here --}}
<div class="bg-emerald-500 text-white p-4 rounded-lg shadow-lg">
<div class="flex items-start space-x-3">
<svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<p id="preview-title" class="font-semibold hidden"></p>
<p id="preview-message" class="text-sm opacity-90">Operation completed successfully!</p>
</div>
</div>
</div>
</div>
</div>
<p class="text-sm text-gray-500 mt-3 text-center">This is a static preview. Click "Show Notification" to see the real notification.</p>
</div>
</div>
{{-- Quick Examples --}}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Quick Examples</h2>
</div>
<div class="card-body">
<div class="space-y-2">
<button onclick="loadExample('simple')" class="btn btn-outline w-full justify-start">
Simple Notification
</button>
<button onclick="loadExample('with_title')" class="btn btn-outline w-full justify-start">
With Title
</button>
<button onclick="loadExample('material_theme')" class="btn btn-outline w-full justify-start">
Material Theme
</button>
<button onclick="loadExample('toastr_adapter')" class="btn btn-outline w-full justify-start">
Toastr Adapter
</button>
<button onclick="loadExample('bottom_center')" class="btn btn-outline w-full justify-start">
Bottom Center Position
</button>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
let currentType = 'success';
const typeColors = {
success: { border: 'border-emerald-500', bg: 'bg-emerald-50', text: 'text-emerald-700', preview: 'bg-emerald-500' },
error: { border: 'border-rose-500', bg: 'bg-rose-50', text: 'text-rose-700', preview: 'bg-rose-500' },
warning: { border: 'border-amber-500', bg: 'bg-amber-50', text: 'text-amber-700', preview: 'bg-amber-500' },
info: { border: 'border-sky-500', bg: 'bg-sky-50', text: 'text-sky-700', preview: 'bg-sky-500' }
};
function setType(type) {
currentType = type;
// Update button styles
document.querySelectorAll('.type-btn').forEach(btn => {
btn.className = 'type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm';
});
const activeBtn = document.getElementById(`type-${type}`);
const colors = typeColors[type];
activeBtn.className = `type-btn px-3 py-2 rounded-lg border-2 ${colors.border} ${colors.bg} ${colors.text} font-medium text-sm`;
// Update preview
updatePreview();
updateCode();
}
function updatePreview() {
const previewDiv = document.querySelector('#preview-notification > div');
const colors = typeColors[currentType];
previewDiv.className = `${colors.preview} text-white p-4 rounded-lg shadow-lg`;
const title = document.getElementById('title').value;
const message = document.getElementById('message').value;
document.getElementById('preview-title').textContent = title;
document.getElementById('preview-title').classList.toggle('hidden', !title);
document.getElementById('preview-message').textContent = message || 'Your notification message...';
}
function updateCode() {
const type = currentType;
const title = document.getElementById('title').value;
const message = document.getElementById('message').value || 'Your message here';
const position = document.getElementById('position').value;
const theme = document.getElementById('theme').value;
const adapter = document.getElementById('adapter').value;
const timeout = document.getElementById('timeout').value;
let code = '';
const hasOptions = position !== 'top-right' || timeout !== '5000';
// Build the code
if (theme) {
code = `flash()->use('theme.${theme}')`;
} else if (adapter !== 'flasher') {
code = `${adapter}()`;
} else {
code = 'flash()';
}
// Add options if needed
if (hasOptions) {
const options = [];
if (position !== 'top-right') {
options.push(`'position' => '${position}'`);
}
if (timeout !== '5000') {
options.push(`'timeout' => ${timeout}`);
}
if (title) {
code += `\n ->${type}('${message}', '${title}', [${options.join(', ')}]);`;
} else {
code += `\n ->${type}('${message}', [${options.join(', ')}]);`;
}
} else {
if (title) {
code += `->${type}('${message}', '${title}');`;
} else {
code += `->${type}('${message}');`;
}
}
document.getElementById('generated-code').textContent = code;
Prism.highlightElement(document.getElementById('generated-code'));
updatePreview();
}
function showPlaygroundNotification() {
const options = {
type: currentType,
message: document.getElementById('message').value || 'Your message here',
title: document.getElementById('title').value || undefined,
position: document.getElementById('position').value,
theme: document.getElementById('theme').value || undefined,
adapter: document.getElementById('adapter').value,
timeout: parseInt(document.getElementById('timeout').value)
};
showNotification(options);
}
function copyCode() {
const code = document.getElementById('generated-code').textContent;
navigator.clipboard.writeText(code).then(() => {
// Show success feedback
const btn = event.target.closest('button');
const originalText = btn.innerHTML;
btn.innerHTML = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg> Copied!';
setTimeout(() => {
btn.innerHTML = originalText;
}, 2000);
});
}
function loadExample(example) {
const examples = {
simple: {
type: 'success',
title: '',
message: 'Data saved successfully!',
position: 'top-right',
theme: '',
adapter: 'flasher',
timeout: 5000
},
with_title: {
type: 'info',
title: 'New Update',
message: 'Version 2.0 is now available!',
position: 'top-right',
theme: '',
adapter: 'flasher',
timeout: 5000
},
material_theme: {
type: 'success',
title: '',
message: 'Material design notification!',
position: 'top-right',
theme: 'material',
adapter: 'flasher',
timeout: 5000
},
toastr_adapter: {
type: 'warning',
title: 'Warning',
message: 'Please review your changes',
position: 'top-right',
theme: '',
adapter: 'toastr',
timeout: 5000
},
bottom_center: {
type: 'error',
title: '',
message: 'Something went wrong!',
position: 'bottom-center',
theme: '',
adapter: 'flasher',
timeout: 5000
}
};
const ex = examples[example];
if (ex) {
setType(ex.type);
document.getElementById('title').value = ex.title;
document.getElementById('message').value = ex.message;
document.getElementById('position').value = ex.position;
document.getElementById('theme').value = ex.theme;
document.getElementById('adapter').value = ex.adapter;
document.getElementById('timeout').value = ex.timeout;
document.getElementById('timeout-value').textContent = ex.timeout;
updateCode();
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateCode();
});
</script>
@endpush
@@ -0,0 +1,211 @@
@extends('layouts.app')
@section('title', 'Positions')
@section('content')
<div class="mb-8">
<h1 class="section-title">Notification Positions</h1>
<p class="section-subtitle">Place your notifications anywhere on the screen. Click any position to see it in action.</p>
</div>
{{-- Interactive Position Grid --}}
<div class="card mb-8">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Click a Position</h2>
</div>
<div class="card-body">
<div class="bg-gray-100 rounded-xl p-4 relative" style="min-height: 400px;">
{{-- Visual Browser Frame --}}
<div class="bg-white rounded-lg shadow-lg overflow-hidden h-full" style="min-height: 380px;">
{{-- Browser Header --}}
<div class="bg-gray-200 px-4 py-2 flex items-center space-x-2">
<div class="w-3 h-3 bg-red-400 rounded-full"></div>
<div class="w-3 h-3 bg-yellow-400 rounded-full"></div>
<div class="w-3 h-3 bg-green-400 rounded-full"></div>
<div class="flex-1 ml-4">
<div class="bg-white rounded px-3 py-1 text-sm text-gray-500 max-w-md">your-app.com</div>
</div>
</div>
{{-- Position Grid --}}
<div class="grid grid-cols-3 grid-rows-3 gap-2 p-4" style="min-height: 340px;">
{{-- Top Left --}}
<button onclick="showNotification({type: 'info', message: 'Top Left notification', position: 'top-left'})"
class="flex items-start justify-start p-4 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition-colors group">
<div class="bg-indigo-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-left
</div>
</button>
{{-- Top Center --}}
<button onclick="showNotification({type: 'info', message: 'Top Center notification', position: 'top-center'})"
class="flex items-start justify-center p-4 bg-purple-50 hover:bg-purple-100 rounded-lg transition-colors group">
<div class="bg-purple-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-center
</div>
</button>
{{-- Top Right --}}
<button onclick="showNotification({type: 'info', message: 'Top Right notification', position: 'top-right'})"
class="flex items-start justify-end p-4 bg-pink-50 hover:bg-pink-100 rounded-lg transition-colors group">
<div class="bg-pink-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-right
</div>
</button>
{{-- Center Left --}}
<button onclick="showNotification({type: 'info', message: 'Center Left notification', position: 'center-left'})"
class="flex items-center justify-start p-4 bg-sky-50 hover:bg-sky-100 rounded-lg transition-colors group">
<div class="bg-sky-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center-left
</div>
</button>
{{-- Center --}}
<button onclick="showNotification({type: 'info', message: 'Center notification', position: 'center'})"
class="flex items-center justify-center p-4 bg-amber-50 hover:bg-amber-100 rounded-lg transition-colors group">
<div class="bg-amber-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center
</div>
</button>
{{-- Center Right --}}
<button onclick="showNotification({type: 'info', message: 'Center Right notification', position: 'center-right'})"
class="flex items-center justify-end p-4 bg-emerald-50 hover:bg-emerald-100 rounded-lg transition-colors group">
<div class="bg-emerald-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center-right
</div>
</button>
{{-- Bottom Left --}}
<button onclick="showNotification({type: 'info', message: 'Bottom Left notification', position: 'bottom-left'})"
class="flex items-end justify-start p-4 bg-rose-50 hover:bg-rose-100 rounded-lg transition-colors group">
<div class="bg-rose-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-left
</div>
</button>
{{-- Bottom Center --}}
<button onclick="showNotification({type: 'info', message: 'Bottom Center notification', position: 'bottom-center'})"
class="flex items-end justify-center p-4 bg-teal-50 hover:bg-teal-100 rounded-lg transition-colors group">
<div class="bg-teal-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-center
</div>
</button>
{{-- Bottom Right --}}
<button onclick="showNotification({type: 'info', message: 'Bottom Right notification', position: 'bottom-right'})"
class="flex items-end justify-end p-4 bg-orange-50 hover:bg-orange-100 rounded-lg transition-colors group">
<div class="bg-orange-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-right
</div>
</button>
</div>
</div>
</div>
</div>
</div>
{{-- Quick Buttons --}}
<div class="card mb-8">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Quick Position Tests</h2>
</div>
<div class="card-body">
<div class="flex flex-wrap gap-3">
<button onclick="showNotification({type: 'success', message: 'Top Right (Default)', position: 'top-right'})"
class="btn btn-success">Top Right</button>
<button onclick="showNotification({type: 'error', message: 'Top Left notification', position: 'top-left'})"
class="btn btn-danger">Top Left</button>
<button onclick="showNotification({type: 'warning', message: 'Bottom Right notification', position: 'bottom-right'})"
class="btn btn-warning">Bottom Right</button>
<button onclick="showNotification({type: 'info', message: 'Bottom Left notification', position: 'bottom-left'})"
class="btn btn-info">Bottom Left</button>
<button onclick="showNotification({type: 'success', message: 'Top Center notification', position: 'top-center'})"
class="btn btn-outline">Top Center</button>
<button onclick="showNotification({type: 'info', message: 'Bottom Center notification', position: 'bottom-center'})"
class="btn btn-outline">Bottom Center</button>
</div>
</div>
</div>
{{-- Code Examples --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="card">
<div class="card-header">
<h3 class="text-lg font-bold text-gray-800">Inline Position</h3>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Set the position directly when creating a notification.</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()
->option('position', 'top-left')
->success('Profile updated!');
// Or use the fluent method
flash()
->position('bottom-right')
->info('New message received!');</code></pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="text-lg font-bold text-gray-800">Global Configuration</h3>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Set a default position for all notifications in your config file.</p>
<div class="code-block">
<div class="code-header">
<span>config/flasher.php</span>
</div>
<pre class="!m-0"><code class="language-php">return [
'default' => 'flasher',
'options' => [
'position' => 'bottom-right',
],
];</code></pre>
</div>
</div>
</div>
</div>
{{-- Position Reference --}}
<div class="card mt-6">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Available Positions</h2>
</div>
<div class="card-body">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-semibold text-gray-800 mb-2">Top Positions</h4>
<ul class="space-y-1 text-gray-600 text-sm">
<li><code class="bg-gray-200 px-2 py-0.5 rounded">top-left</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">top-center</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">top-right</code> <span class="text-emerald-600">(default)</span></li>
</ul>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-semibold text-gray-800 mb-2">Center Positions</h4>
<ul class="space-y-1 text-gray-600 text-sm">
<li><code class="bg-gray-200 px-2 py-0.5 rounded">center-left</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">center</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">center-right</code></li>
</ul>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<h4 class="font-semibold text-gray-800 mb-2">Bottom Positions</h4>
<ul class="space-y-1 text-gray-600 text-sm">
<li><code class="bg-gray-200 px-2 py-0.5 rounded">bottom-left</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">bottom-center</code></li>
<li><code class="bg-gray-200 px-2 py-0.5 rounded">bottom-right</code></li>
</ul>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,75 @@
@extends('layouts.app')
@section('title', 'Themes')
@section('content')
<div class="mb-8">
<h1 class="section-title">Theme Gallery</h1>
<p class="section-subtitle">Choose from 17+ beautiful themes to match your application's design.</p>
</div>
{{-- Theme Grid --}}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mb-12">
@php
$themes = [
['name' => 'flasher', 'label' => 'Flasher', 'desc' => 'Default theme', 'gradient' => 'from-indigo-500 to-indigo-600'],
['name' => 'material', 'label' => 'Material', 'desc' => 'Google Material', 'gradient' => 'from-blue-500 to-blue-600'],
['name' => 'ios', 'label' => 'iOS', 'desc' => 'Apple style', 'gradient' => 'from-slate-600 to-slate-700'],
['name' => 'slack', 'label' => 'Slack', 'desc' => 'Slack messaging', 'gradient' => 'from-purple-500 to-purple-600'],
['name' => 'amazon', 'label' => 'Amazon', 'desc' => 'Amazon alerts', 'gradient' => 'from-orange-500 to-orange-600'],
['name' => 'google', 'label' => 'Google', 'desc' => 'Google notifications', 'gradient' => 'from-red-500 to-yellow-500'],
['name' => 'facebook', 'label' => 'Facebook', 'desc' => 'Facebook style', 'gradient' => 'from-blue-600 to-blue-700'],
['name' => 'minimal', 'label' => 'Minimal', 'desc' => 'Ultra clean', 'gradient' => 'from-gray-400 to-gray-500'],
['name' => 'neon', 'label' => 'Neon', 'desc' => 'Bright & bold', 'gradient' => 'from-pink-500 to-rose-500'],
['name' => 'emerald', 'label' => 'Emerald', 'desc' => 'Green palette', 'gradient' => 'from-emerald-500 to-green-600'],
['name' => 'sapphire', 'label' => 'Sapphire', 'desc' => 'Blue elegance', 'gradient' => 'from-blue-500 to-indigo-600'],
['name' => 'ruby', 'label' => 'Ruby', 'desc' => 'Red accents', 'gradient' => 'from-red-500 to-rose-600'],
['name' => 'amber', 'label' => 'Amber', 'desc' => 'Warm tones', 'gradient' => 'from-amber-400 to-orange-500'],
['name' => 'jade', 'label' => 'Jade', 'desc' => 'Soft green', 'gradient' => 'from-teal-500 to-green-600'],
['name' => 'onyx', 'label' => 'Onyx', 'desc' => 'Dark mode', 'gradient' => 'from-slate-700 to-slate-900'],
['name' => 'crystal', 'label' => 'Crystal', 'desc' => 'Transparent', 'gradient' => 'from-cyan-400 to-blue-500'],
['name' => 'aurora', 'label' => 'Aurora', 'desc' => 'Gradient effects', 'gradient' => 'from-green-400 via-blue-500 to-purple-500'],
];
@endphp
@foreach($themes as $theme)
<div class="card group cursor-pointer" onclick="showNotification({type: 'success', message: 'This is the {{ $theme['label'] }} theme!', title: '{{ $theme['label'] }} Theme', theme: '{{ $theme['name'] }}'})">
<div class="h-20 bg-gradient-to-br {{ $theme['gradient'] }} flex items-center justify-center">
<span class="text-white text-2xl font-bold">{{ substr($theme['label'], 0, 1) }}</span>
</div>
<div class="p-4">
<h3 class="font-semibold text-gray-800">{{ $theme['label'] }}</h3>
<p class="text-sm text-gray-500">{{ $theme['desc'] }}</p>
</div>
</div>
@endforeach
</div>
{{-- Usage Example --}}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Using Themes</h2>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Apply themes to your notifications with the <code class="bg-gray-100 px-2 py-1 rounded">use()</code> method.</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">// Use a specific theme
flash()->use('theme.material')->success('Material design notification!');
// Different themes for different messages
flash()->use('theme.ios')->info('iOS style notification');
flash()->use('theme.slack')->warning('Slack style warning');
flash()->use('theme.amazon')->success('Amazon style success');
// Set default theme in config/flasher.php
return [
'default' => 'theme.material',
];</code></pre>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,200 @@
@extends('layouts.app')
@section('title', 'Notification Types')
@section('content')
<div class="mb-8">
<h1 class="section-title">Notification Types</h1>
<p class="section-subtitle">PHPFlasher supports four notification types for different scenarios.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{{-- Success --}}
<div class="card">
<div class="h-2 bg-emerald-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-8 h-8 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Success</h2>
<p class="text-gray-500">Positive feedback</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use success notifications for completed actions, confirmations, and positive feedback.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Your profile has been updated successfully!'})"
class="btn btn-success w-full justify-start">
Profile Updated
</button>
<button onclick="showNotification({type: 'success', message: 'Your account has been created. Welcome aboard!', title: 'Account Created'})"
class="btn btn-success w-full justify-start">
Account Created
</button>
<button onclick="showNotification({type: 'success', message: 'Payment of $99.00 confirmed. Receipt sent to your email.', title: 'Payment Successful'})"
class="btn btn-success w-full justify-start">
Payment Confirmed
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->success('Profile updated successfully!');
// With title
flash()->success('Welcome aboard!', 'Account Created');</code></pre>
</div>
</div>
</div>
{{-- Error --}}
<div class="card">
<div class="h-2 bg-rose-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-rose-100 rounded-xl">
<svg class="w-8 h-8 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Error</h2>
<p class="text-gray-500">Failures and issues</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use error notifications for validation failures, server errors, and problems.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'error', message: 'Invalid email or password. Please try again.'})"
class="btn btn-danger w-full justify-start">
Login Failed
</button>
<button onclick="showNotification({type: 'error', message: 'Your card was declined. Please try another payment method.', title: 'Payment Failed'})"
class="btn btn-danger w-full justify-start">
Payment Declined
</button>
<button onclick="showNotification({type: 'error', message: 'Unable to connect to server. Please check your connection.', title: 'Connection Error'})"
class="btn btn-danger w-full justify-start">
Server Error
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->error('Invalid credentials.');
// With title
flash()->error('Please try again.', 'Payment Failed');</code></pre>
</div>
</div>
</div>
{{-- Warning --}}
<div class="card">
<div class="h-2 bg-amber-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-amber-100 rounded-xl">
<svg class="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Warning</h2>
<p class="text-gray-500">Caution alerts</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use warning notifications to alert users about potential issues or important notices.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'warning', message: 'Your session will expire in 5 minutes.'})"
class="btn btn-warning w-full justify-start">
Session Expiring
</button>
<button onclick="showNotification({type: 'warning', message: 'You have unsaved changes that will be lost.', title: 'Unsaved Changes'})"
class="btn btn-warning w-full justify-start">
Unsaved Changes
</button>
<button onclick="showNotification({type: 'warning', message: 'Only 2 items left in stock!', title: 'Low Stock'})"
class="btn btn-warning w-full justify-start">
Low Stock Alert
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->warning('Session expiring soon.');
// With title
flash()->warning('Only 2 left!', 'Low Stock');</code></pre>
</div>
</div>
</div>
{{-- Info --}}
<div class="card">
<div class="h-2 bg-sky-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-sky-100 rounded-xl">
<svg class="w-8 h-8 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Info</h2>
<p class="text-gray-500">Neutral information</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use info notifications for tips, updates, and general information.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'info', message: 'A new version is available. Click here to update.'})"
class="btn btn-info w-full justify-start">
Update Available
</button>
<button onclick="showNotification({type: 'info', message: 'Tip: You can use keyboard shortcuts for faster navigation.', title: 'Pro Tip'})"
class="btn btn-info w-full justify-start">
Helpful Tip
</button>
<button onclick="showNotification({type: 'info', message: 'Your report is being generated and will be ready shortly.'})"
class="btn btn-info w-full justify-start">
Processing Status
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->info('New version available!');
// With title
flash()->info('Use shortcuts!', 'Pro Tip');</code></pre>
</div>
</div>
</div>
</div>
@endsection
+13 -20
View File
@@ -2,26 +2,19 @@
declare(strict_types=1);
use App\Entity\Book;
use App\Http\Controllers\DemoController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
sweetalert()->timerProgressBar()->success('Your account has been successfully created!');
noty()->layout('topCenter')->success('Welcome back, John Doe!');
notyf()->ripple(false)->warning('Your subscription is about to expire in 3 days.');
toastr()->positionClass('toast-bottom-left')->error('Payment failed. Please try again.');
flash()->use('flasher')->success('Your profile has been updated successfully.');
flash()->created(new Book('The Great Gatsby'));
flash()->saved(new Book('1984'));
session()->flash('success', 'Your settings have been saved.');
return view('welcome');
})->name('app_home');
// Main pages
Route::get('/', [DemoController::class, 'home'])->name('home');
Route::get('/types', [DemoController::class, 'types'])->name('types');
Route::get('/themes', [DemoController::class, 'themes'])->name('themes');
Route::get('/adapters', [DemoController::class, 'adapters'])->name('adapters');
Route::get('/positions', [DemoController::class, 'positions'])->name('positions');
Route::get('/examples', [DemoController::class, 'examples'])->name('examples');
Route::get('/playground', [DemoController::class, 'playground'])->name('playground');
Route::get('/livewire', [DemoController::class, 'livewire'])->name('livewire');
Route::get('/redirect', function () {
session()->flash('success', 'You have been redirected successfully.');
return redirect('/destination');
});
Route::get('/destination', function () {
return view('welcome');
});
// AJAX endpoints
Route::post('/notify', [DemoController::class, 'notify'])->name('notify');
Route::post('/example/{scenario}', [DemoController::class, 'runExample'])->name('example.run');
@@ -0,0 +1,7 @@
{
"success": false,
"errors": "npm warn exec The following package was not found and will be installed: vite@7.3.1\nfailed to load config from /Users/yoeunes/projects/flasher/php-flasher/demo/laravel/vite.config.js\nerror during build:\nError [ERR_MODULE_NOT_FOUND]: Cannot find package 'vite' imported from /Users/yoeunes/projects/flasher/php-flasher/node_modules/.vite-temp/vite.config.js.timestamp-1772405569113-b016e3f78b86a8.mjs\n at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)\n at packageResolve (node:internal/modules/esm/resolve:767:81)\n at moduleResolve (node:internal/modules/esm/resolve:853:18)\n at defaultResolve (node:internal/modules/esm/resolve:983:11)\n at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:799:12)\n at #cachedDefaultResolve (node:internal/modules/esm/loader:723:25)\n at ModuleLoader.resolve (node:internal/modules/esm/loader:706:38)\n at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)\n at #link (node:internal/modules/esm/module_job:170:49)\n",
"timestamp": "2026-03-01 22:52:49",
"vite_ruby": "3.9.3",
"digest": "f670d25672cb5f6c5badc00b4d6aca0d4d0f25ae"
}
+8 -1
View File
@@ -5,11 +5,18 @@
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/public/vendor/flasherg
/public/vendor/flasher
/var/
/vendor/
###< symfony/framework-bundle ###
###> symfony/webpack-encore-bundle ###
/node_modules/
/public/build/
npm-debug.log
yarn-error.log
###< symfony/webpack-encore-bundle ###
###> phpstan/phpstan ###
phpstan.neon
###< phpstan/phpstan ###
+12
View File
@@ -0,0 +1,12 @@
/**
* PHPFlasher Symfony Demo
*
* Main JavaScript entry point.
* Note: PHPFlasher and other libraries are loaded via CDN in the layout.
*/
// Import styles
import './styles/app.css';
// Custom demo functionality can be added here
console.log('PHPFlasher Symfony Demo loaded');
+4
View File
@@ -0,0 +1,4 @@
/* PHPFlasher Symfony Demo - Custom Styles */
/* Any custom styles can be added here */
/* Note: Main styling is handled via Tailwind CSS v4 CDN in the layout */
+92 -21
View File
@@ -6,7 +6,70 @@
"repositories": [
{
"type": "path",
"url": "../..",
"url": "../../src/Prime",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Symfony",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Noty/Prime",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Noty/Symfony",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Notyf/Prime",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Notyf/Symfony",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/SweetAlert/Prime",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/SweetAlert/Symfony",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Toastr/Prime",
"options": {
"symlink": true
}
},
{
"type": "path",
"url": "../../src/Toastr/Symfony",
"options": {
"symlink": true
}
@@ -16,27 +79,35 @@
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"nelmio/security-bundle": "^3.4.2",
"php-flasher/php-flasher": "@dev",
"phpstan/phpstan-symfony": "^2.0.2",
"symfony/console": "7.2.*",
"symfony/dotenv": "7.2.*",
"symfony/flex": "^2.4.7",
"symfony/framework-bundle": "7.2.*",
"symfony/monolog-bundle": "^3.10",
"symfony/notifier": "7.2.*",
"symfony/runtime": "7.2.*",
"symfony/translation": "7.2.*",
"symfony/twig-bundle": "7.2.*",
"symfony/yaml": "7.2.*",
"twig/extra-bundle": "^2.12|^3.20",
"twig/twig": "^2.12|^3.20"
"nelmio/security-bundle": "^3.4",
"php-flasher/flasher": "@dev",
"php-flasher/flasher-symfony": "@dev",
"php-flasher/flasher-noty": "@dev",
"php-flasher/flasher-noty-symfony": "@dev",
"php-flasher/flasher-notyf": "@dev",
"php-flasher/flasher-notyf-symfony": "@dev",
"php-flasher/flasher-sweetalert": "@dev",
"php-flasher/flasher-sweetalert-symfony": "@dev",
"php-flasher/flasher-toastr": "@dev",
"php-flasher/flasher-toastr-symfony": "@dev",
"phpstan/phpstan-symfony": "^2.0",
"symfony/console": "8.0.*",
"symfony/dotenv": "8.0.*",
"symfony/flex": "^2.4",
"symfony/framework-bundle": "8.0.*",
"symfony/monolog-bundle": "^4.0",
"symfony/runtime": "8.0.*",
"symfony/translation": "8.0.*",
"symfony/twig-bundle": "8.0.*",
"symfony/yaml": "8.0.*",
"twig/extra-bundle": "^3.20",
"twig/twig": "^3.20"
},
"require-dev": {
"spatie/ray": "^1.41.5",
"symfony/maker-bundle": "^1.62.1",
"symfony/stopwatch": "7.2.*",
"symfony/web-profiler-bundle": "7.2.*"
"spatie/ray": "^1.41",
"symfony/maker-bundle": "^1.62",
"symfony/stopwatch": "8.0.*",
"symfony/web-profiler-bundle": "8.0.*"
},
"config": {
"allow-plugins": {
@@ -85,7 +156,7 @@
"extra": {
"symfony": {
"allow-contrib": false,
"require": "7.2.*",
"require": "8.0.*",
"docker": false
}
}
+4619 -7841
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -10,8 +10,6 @@ return [
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
// Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
];
-3
View File
@@ -1,3 +0,0 @@
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
@@ -1,12 +0,0 @@
framework:
notifier:
chatter_transports:
texter_transports:
channel_policy:
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
urgent: ['email']
high: ['email']
medium: ['email']
low: ['email']
admin_recipients:
- { email: admin@example.com }
@@ -0,0 +1,3 @@
framework:
property_info:
with_constructor_extractor: true
@@ -1,5 +0,0 @@
twig_component:
anonymous_template_directory: 'components/'
defaults:
# Namespace & directory for components
App\Twig\Components\: 'components/'
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
resource: '@FrameworkBundle/Resources/config/routing/errors.php'
prefix: /_error
+2 -2
View File
@@ -1,8 +1,8 @@
when@dev:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
resource: '@WebProfilerBundle/Resources/config/routing/wdt.php'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
resource: '@WebProfilerBundle/Resources/config/routing/profiler.php'
prefix: /_profiler
+8245
View File
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
{
"private": true,
"scripts": {
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production"
},
"devDependencies": {
"@symfony/webpack-encore": "^4.0.0",
"webpack": "^5.0.0",
"webpack-cli": "^5.0.0",
"webpack-notifier": "^1.15.0"
}
}
@@ -0,0 +1 @@
"use strict";(self.webpackChunk=self.webpackChunk||[]).push([[524],{28:function(){console.log("PHPFlasher Symfony Demo loaded")}},function(e){var n;n=28,e(e.s=n)}]);
@@ -0,0 +1,13 @@
{
"entrypoints": {
"app": {
"js": [
"/build/runtime.ccfad4f9.js",
"/build/app.f62ef15b.js"
],
"css": [
"/build/app.31d6cfe0.css"
]
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"build/app.css": "/build/app.31d6cfe0.css",
"build/app.js": "/build/app.f62ef15b.js",
"build/runtime.js": "/build/runtime.ccfad4f9.js"
}
+1 -1
View File
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("noty")):"function"==typeof define&&define.amd?define(["@flasher/flasher","noty"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).Noty=t(e.flasher,e.Noty)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,o){let i,n,r,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,n=l.message,r=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,n=l.message,r=l.title,delete l.message,delete l.title):(i=e,n=t,null==s?(r=void 0,l=o||{}):"string"==typeof s?(r=s,l=o||{}):"object"==typeof s&&(l=Object.assign({},s),"title"in l?(r=l.title,delete l.title):r=void 0,o&&"object"==typeof o&&(l=Object.assign(Object.assign({},l),o)))),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");null==r&&(r=i.charAt(0).toUpperCase()+i.slice(1));const a={type:i,message:n,title:r,options:l,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([a])}}const o=new class extends s{constructor(){super(...arguments),this.defaultOptions={timeout:1e4}}renderEnvelopes(e){(null==e?void 0:e.length)&&e.forEach((e=>{try{const s=Object.assign({text:e.message,type:e.type},this.defaultOptions);e.options&&Object.assign(s,e.options);const o=new t(s);o.show();const i=o.layoutDom;i&&"object"==typeof i.dataset&&(i.dataset.turboTemporary="")}catch(t){console.error("PHPFlasher Noty: Error rendering notification",t,e)}}))}renderOptions(e){e&&(Object.assign(this.defaultOptions,e),t.overrideDefaults(this.defaultOptions))}};return e.addPlugin("noty",o),o}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("noty")):"function"==typeof define&&define.amd?define(["@flasher/flasher","noty"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).Noty=t(e.flasher,e.Noty)}(this,(function(e,t){"use strict";class o{success(e,t,o){this.flash("success",e,t,o)}error(e,t,o){this.flash("error",e,t,o)}info(e,t,o){this.flash("info",e,t,o)}warning(e,t,o){this.flash("warning",e,t,o)}flash(e,t,o,s){let n,i,l,a={};if("object"==typeof e?(a=Object.assign({},e),n=a.type,i=a.message,l=a.title,delete a.type,delete a.message,delete a.title):"object"==typeof t?(a=Object.assign({},t),n=e,i=a.message,l=a.title,delete a.message,delete a.title):(n=e,i=t,null==o?(l=void 0,a=s||{}):"string"==typeof o?(l=o,a=s||{}):"object"==typeof o&&(a=Object.assign({},o),"title"in a?(l=a.title,delete a.title):l=void 0,s&&"object"==typeof s&&(a=Object.assign(Object.assign({},a),s)))),!n)throw new Error("Type is required for notifications");if(null==i)throw new Error("Message is required for notifications");null==l&&(l=n.charAt(0).toUpperCase()+n.slice(1));const r={type:n,message:i,title:l,options:a,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([r])}}const s=new class extends o{constructor(){super(...arguments),this.defaultOptions={timeout:1e4}}renderEnvelopes(e){(null==e?void 0:e.length)&&e.forEach((e=>{var o,s,n,i;try{const l=Object.assign({text:e.message,type:e.type},this.defaultOptions);e.options&&Object.assign(l,e.options);const a={onShow:null===(o=l.callbacks)||void 0===o?void 0:o.onShow,onClick:null===(s=l.callbacks)||void 0===s?void 0:s.onClick,onClose:null===(n=l.callbacks)||void 0===n?void 0:n.onClose,onHover:null===(i=l.callbacks)||void 0===i?void 0:i.onHover};l.callbacks=Object.assign(Object.assign({},l.callbacks),{onShow:()=>{var t;this.dispatchEvent("flasher:noty:show",e),null===(t=a.onShow)||void 0===t||t.call(a)},onClick:()=>{var t;this.dispatchEvent("flasher:noty:click",e),null===(t=a.onClick)||void 0===t||t.call(a)},onClose:()=>{var t;this.dispatchEvent("flasher:noty:close",e),null===(t=a.onClose)||void 0===t||t.call(a)},onHover:()=>{var t;this.dispatchEvent("flasher:noty:hover",e),null===(t=a.onHover)||void 0===t||t.call(a)}});const r=new t(l);r.show();const c=r.layoutDom;c&&"object"==typeof c.dataset&&(c.dataset.turboTemporary="")}catch(t){console.error("PHPFlasher Noty: Error rendering notification",t,e)}}))}dispatchEvent(e,t){window.dispatchEvent(new CustomEvent(e,{detail:{envelope:t}}))}renderOptions(e){e&&(Object.assign(this.defaultOptions,e),t.overrideDefaults(this.defaultOptions))}};return e.addPlugin("noty",s),s}));
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("toastr")):"function"==typeof define&&define.amd?define(["@flasher/flasher","toastr"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).toastr=t(e.flasher,e.toastr)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,r){let o,i,n,a={};if("object"==typeof e?(a=Object.assign({},e),o=a.type,i=a.message,n=a.title,delete a.type,delete a.message,delete a.title):"object"==typeof t?(a=Object.assign({},t),o=e,i=a.message,n=a.title,delete a.message,delete a.title):(o=e,i=t,null==s?(n=void 0,a=r||{}):"string"==typeof s?(n=s,a=r||{}):"object"==typeof s&&(a=Object.assign({},s),"title"in a?(n=a.title,delete a.title):n=void 0,r&&"object"==typeof r&&(a=Object.assign(Object.assign({},a),r)))),!o)throw new Error("Type is required for notifications");if(null==i)throw new Error("Message is required for notifications");null==n&&(n=o.charAt(0).toUpperCase()+o.slice(1));const l={type:o,message:i,title:n,options:a,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([l])}}const r=new class extends s{renderEnvelopes(e){(null==e?void 0:e.length)&&this.isDependencyAvailable()&&e.forEach((e=>{try{const{message:s,title:r,type:o,options:i}=e,n=t[o](s,r,i);if(n&&n.parent)try{const e=n.parent();e&&"function"==typeof e.attr&&e.attr("data-turbo-temporary","")}catch(e){console.error("PHPFlasher Toastr: Error setting Turbo compatibility",e)}}catch(t){console.error("PHPFlasher Toastr: Error rendering notification",t,e)}}))}renderOptions(e){if(this.isDependencyAvailable())try{t.options=Object.assign({timeOut:e.timeOut||1e4,progressBar:e.progressBar||!0},e)}catch(e){console.error("PHPFlasher Toastr: Error applying options",e)}}isDependencyAvailable(){return!(!window.jQuery&&!window.$)||(console.error("PHPFlasher Toastr: jQuery is required but not loaded. Make sure jQuery is loaded before using Toastr."),!1)}};return e.addPlugin("toastr",r),r}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("toastr")):"function"==typeof define&&define.amd?define(["@flasher/flasher","toastr"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).toastr=t(e.flasher,e.toastr)}(this,(function(e,t){"use strict";class s{success(e,t,s){this.flash("success",e,t,s)}error(e,t,s){this.flash("error",e,t,s)}info(e,t,s){this.flash("info",e,t,s)}warning(e,t,s){this.flash("warning",e,t,s)}flash(e,t,s,o){let i,r,n,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,r=l.message,n=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,r=l.message,n=l.title,delete l.message,delete l.title):(i=e,r=t,null==s?(n=void 0,l=o||{}):"string"==typeof s?(n=s,l=o||{}):"object"==typeof s&&(l=Object.assign({},s),"title"in l?(n=l.title,delete l.title):n=void 0,o&&"object"==typeof o&&(l=Object.assign(Object.assign({},l),o)))),!i)throw new Error("Type is required for notifications");if(null==r)throw new Error("Message is required for notifications");null==n&&(n=i.charAt(0).toUpperCase()+i.slice(1));const a={type:i,message:r,title:n,options:l,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([a])}}const o=new class extends s{renderEnvelopes(e){(null==e?void 0:e.length)&&this.isDependencyAvailable()&&e.forEach((e=>{try{const{message:s,title:o,type:i,options:r}=e,n=Object.assign(Object.assign({},r),{onShown:()=>{var t;this.dispatchEvent("flasher:toastr:show",e),null===(t=null==r?void 0:r.onShown)||void 0===t||t.call(r)},onclick:()=>{var t;this.dispatchEvent("flasher:toastr:click",e),null===(t=null==r?void 0:r.onclick)||void 0===t||t.call(r)},onCloseClick:()=>{var t;this.dispatchEvent("flasher:toastr:close",e),null===(t=null==r?void 0:r.onCloseClick)||void 0===t||t.call(r)},onHidden:()=>{var t;this.dispatchEvent("flasher:toastr:hidden",e),null===(t=null==r?void 0:r.onHidden)||void 0===t||t.call(r)}}),l=t[i](s,o,n);if(l&&l.parent)try{const e=l.parent();e&&"function"==typeof e.attr&&e.attr("data-turbo-temporary","")}catch(e){console.error("PHPFlasher Toastr: Error setting Turbo compatibility",e)}}catch(t){console.error("PHPFlasher Toastr: Error rendering notification",t,e)}}))}dispatchEvent(e,t){window.dispatchEvent(new CustomEvent(e,{detail:{envelope:t}}))}renderOptions(e){if(this.isDependencyAvailable())try{t.options=Object.assign({timeOut:e.timeOut||1e4,progressBar:e.progressBar||!0},e)}catch(e){console.error("PHPFlasher Toastr: Error applying options",e)}}isDependencyAvailable(){return!(!window.jQuery&&!window.$)||(console.error("PHPFlasher Toastr: jQuery is required but not loaded. Make sure jQuery is loaded before using Toastr."),!1)}};return e.addPlugin("toastr",o),o}));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+24 -23
View File
@@ -1,48 +1,49 @@
{
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=2dfca2b3c2159f2ff676a62ad338f092",
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=689710919ac658a64bacd98998f04534",
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=aa16aaa1cf4481da81e2392eed0a5804",
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=0d92a4c6a11d829ad5232f66b08ffe82",
"/vendor/flasher/themes/amazon/amazon.min.css": "/vendor/flasher/themes/amazon/amazon.min.css?id=9ee70f25ed683a1a06c96832e53ae372",
"/vendor/flasher/themes/amazon/amazon.min.js": "/vendor/flasher/themes/amazon/amazon.min.js?id=2e8cbc1a93524a122b8f62f072f2ff71",
"/vendor/flasher/themes/amazon/amazon.min.js": "/vendor/flasher/themes/amazon/amazon.min.js?id=818ae079cef66072237504a6292015fe",
"/vendor/flasher/themes/sapphire/sapphire.min.css": "/vendor/flasher/themes/sapphire/sapphire.min.css?id=f7b4efc7b645ee792dacd4438fdc4ea3",
"/vendor/flasher/themes/sapphire/sapphire.min.js": "/vendor/flasher/themes/sapphire/sapphire.min.js?id=aeaeda5a1eadd052f0c466fdf6c6f7b3",
"/vendor/flasher/themes/google/google.min.js": "/vendor/flasher/themes/google/google.min.js?id=812cddda9e58967cd46fb1f5a385b8ec",
"/vendor/flasher/themes/sapphire/sapphire.min.js": "/vendor/flasher/themes/sapphire/sapphire.min.js?id=6b9266d751da3dff9201fdd70d86c42a",
"/vendor/flasher/themes/google/google.min.js": "/vendor/flasher/themes/google/google.min.js?id=ce9d22b65c40f5537bd3dca88029bfa8",
"/vendor/flasher/themes/google/google.min.css": "/vendor/flasher/themes/google/google.min.css?id=b5f06fb2a4795ce361e94113d3b91822",
"/vendor/flasher/themes/flasher/flasher.min.js": "/vendor/flasher/themes/flasher/flasher.min.js?id=7b56b75dafcf90e2fd3cf243f6e17ede",
"/vendor/flasher/themes/flasher/flasher.min.css": "/vendor/flasher/themes/flasher/flasher.min.css?id=a806450943da43e7876dd3ddf78c47bc",
"/vendor/flasher/themes/flasher/flasher.min.js": "/vendor/flasher/themes/flasher/flasher.min.js?id=369290828dc5b9dc18b4e768b7855e2b",
"/vendor/flasher/themes/flasher/flasher.min.css": "/vendor/flasher/themes/flasher/flasher.min.css?id=afe8be6f2c545ee55168ccc601d60a27",
"/vendor/flasher/themes/aurora/aurora.min.css": "/vendor/flasher/themes/aurora/aurora.min.css?id=c147c3dce846ce387a2360fb7c5ef317",
"/vendor/flasher/themes/aurora/aurora.min.js": "/vendor/flasher/themes/aurora/aurora.min.js?id=16817691e8c76e7a27ea1be3a9d3e288",
"/vendor/flasher/themes/aurora/aurora.min.js": "/vendor/flasher/themes/aurora/aurora.min.js?id=7215e6d20cb0a7291004907beaf3b8c3",
"/vendor/flasher/themes/crystal/crystal.min.css": "/vendor/flasher/themes/crystal/crystal.min.css?id=22d1ac60ba71b99d33f06d8860542bea",
"/vendor/flasher/themes/crystal/crystal.min.js": "/vendor/flasher/themes/crystal/crystal.min.js?id=b98d586c16df24f556bd509d128d4206",
"/vendor/flasher/themes/crystal/crystal.min.js": "/vendor/flasher/themes/crystal/crystal.min.js?id=d0b16d7675876adb142ce7215ed5aa59",
"/vendor/flasher/themes/shared/shared.min.js": "/vendor/flasher/themes/shared/shared.min.js?id=6099d7536c1f8046d778d61dfa40004c",
"/vendor/flasher/themes/minimal/minimal.min.css": "/vendor/flasher/themes/minimal/minimal.min.css?id=ece6c8d8425a37426f2953e7d771cd34",
"/vendor/flasher/themes/minimal/minimal.min.js": "/vendor/flasher/themes/minimal/minimal.min.js?id=1497c70c9875d728c439136c92fbca08",
"/vendor/flasher/themes/ios/ios.min.js": "/vendor/flasher/themes/ios/ios.min.js?id=7149c00236bda6f7aecfd7f0ce18a1f4",
"/vendor/flasher/themes/minimal/minimal.min.js": "/vendor/flasher/themes/minimal/minimal.min.js?id=0a94dfe76fb9b0a2dbf60a3661b06bfb",
"/vendor/flasher/themes/ios/ios.min.js": "/vendor/flasher/themes/ios/ios.min.js?id=4fc66d5cef6a7d30c5e94c4ababe21ea",
"/vendor/flasher/themes/ios/ios.min.css": "/vendor/flasher/themes/ios/ios.min.css?id=9680ce2ce92ac435c99a40e599d2e775",
"/vendor/flasher/themes/onyx/onyx.min.css": "/vendor/flasher/themes/onyx/onyx.min.css?id=eff8e7447767c39298a9c3565a917d79",
"/vendor/flasher/themes/onyx/onyx.min.js": "/vendor/flasher/themes/onyx/onyx.min.js?id=2c8dc59c4abefd370535eb102d4e6711",
"/vendor/flasher/themes/amber/amber.min.js": "/vendor/flasher/themes/amber/amber.min.js?id=95b57e413d5cb896b47ad9158dd4ef07",
"/vendor/flasher/themes/onyx/onyx.min.js": "/vendor/flasher/themes/onyx/onyx.min.js?id=e15080237480b663ad2ab817e254fa9f",
"/vendor/flasher/themes/amber/amber.min.js": "/vendor/flasher/themes/amber/amber.min.js?id=a5147d1108e4752a0ebb492b0e26ed81",
"/vendor/flasher/themes/amber/amber.min.css": "/vendor/flasher/themes/amber/amber.min.css?id=20aa374b7ede682a6a0eb00c19a0013c",
"/vendor/flasher/themes/facebook/facebook.min.js": "/vendor/flasher/themes/facebook/facebook.min.js?id=7ce0a41e3886db70a56b6e9e49bdda91",
"/vendor/flasher/themes/facebook/facebook.min.js": "/vendor/flasher/themes/facebook/facebook.min.js?id=5a82f0ed6b06975fedabd8f0f8eb3eed",
"/vendor/flasher/themes/facebook/facebook.min.css": "/vendor/flasher/themes/facebook/facebook.min.css?id=edfbad59ded6831073a0d08a799757fd",
"/vendor/flasher/themes/neon/neon.min.css": "/vendor/flasher/themes/neon/neon.min.css?id=38716f3f934f720ac34fb6617d5dc674",
"/vendor/flasher/themes/neon/neon.min.js": "/vendor/flasher/themes/neon/neon.min.js?id=01987a2d265f63b2e92fe5f3e4b61496",
"/vendor/flasher/themes/slack/slack.min.js": "/vendor/flasher/themes/slack/slack.min.js?id=6b1ade06f3eb351e9eaef76d402918fb",
"/vendor/flasher/themes/neon/neon.min.js": "/vendor/flasher/themes/neon/neon.min.js?id=3188b54684a7193acffa63d2305468a0",
"/vendor/flasher/themes/slack/slack.min.js": "/vendor/flasher/themes/slack/slack.min.js?id=cd54c7e2570e9784e03d6772e9ae4e8e",
"/vendor/flasher/themes/slack/slack.min.css": "/vendor/flasher/themes/slack/slack.min.css?id=4fa1fe1bdca7c256f225e9f4c2e6d779",
"/vendor/flasher/themes/jade/jade.min.js": "/vendor/flasher/themes/jade/jade.min.js?id=0fbcab9da8649325269c934da21006cc",
"/vendor/flasher/themes/jade/jade.min.js": "/vendor/flasher/themes/jade/jade.min.js?id=664998188449eebf74b7c1924f1a129d",
"/vendor/flasher/themes/jade/jade.min.css": "/vendor/flasher/themes/jade/jade.min.css?id=b140ea3056bc6eaa860c891bedf16818",
"/vendor/flasher/themes/material/material.min.js": "/vendor/flasher/themes/material/material.min.js?id=0360045692aaa32af12dc9739374fdae",
"/vendor/flasher/themes/material/material.min.js": "/vendor/flasher/themes/material/material.min.js?id=b5b83b804f0ce8135899f86453741128",
"/vendor/flasher/themes/material/material.min.css": "/vendor/flasher/themes/material/material.min.css?id=f0327e5645332c10d957fff6af1220fc",
"/vendor/flasher/themes/ruby/ruby.min.css": "/vendor/flasher/themes/ruby/ruby.min.css?id=faffa15686ab77a5f8988d1b8cc54650",
"/vendor/flasher/themes/ruby/ruby.min.js": "/vendor/flasher/themes/ruby/ruby.min.js?id=7c8e19091329bd9242408f8d5b9890e7",
"/vendor/flasher/themes/ruby/ruby.min.js": "/vendor/flasher/themes/ruby/ruby.min.js?id=5d0262a53a5d9bb8ab0eadaceadfc7b8",
"/vendor/flasher/themes/emerald/emerald.min.css": "/vendor/flasher/themes/emerald/emerald.min.css?id=4e346793d433693b3fdfd9de46d05590",
"/vendor/flasher/themes/emerald/emerald.min.js": "/vendor/flasher/themes/emerald/emerald.min.js?id=815113a3e5023948b5f700f5d62253dd",
"/vendor/flasher/themes/emerald/emerald.min.js": "/vendor/flasher/themes/emerald/emerald.min.js?id=317b24bd9eee938ebd9c3221417fd27c",
"/vendor/flasher/mint.css": "/vendor/flasher/mint.css?id=348f135fff639305dde0005c647c1d20",
"/vendor/flasher/flasher-noty.min.js": "/vendor/flasher/flasher-noty.min.js?id=4a788f294fa818764ee4d3e1cf10a330",
"/vendor/flasher/flasher-noty.min.js": "/vendor/flasher/flasher-noty.min.js?id=6a5a2c112c3192328d30b740397f84ed",
"/vendor/flasher/noty.css": "/vendor/flasher/noty.css?id=bf51111a785e04cc8c86a7786e855484",
"/vendor/flasher/noty.min.js": "/vendor/flasher/noty.min.js?id=840a31ddb720ff391cfc386c009d3422",
"/vendor/flasher/flasher-notyf.min.js": "/vendor/flasher/flasher-notyf.min.js?id=d6c390269f18e78bc9c74a98fc6c3c29",
"/vendor/flasher/flasher-notyf.min.js": "/vendor/flasher/flasher-notyf.min.js?id=deabfa48c70419ebd3b2b28fcfe7b8c5",
"/vendor/flasher/flasher-notyf.min.css": "/vendor/flasher/flasher-notyf.min.css?id=a68d410be6df5aaa1dd8ed3a2798d390",
"/vendor/flasher/toastr.min.css": "/vendor/flasher/toastr.min.css?id=f284028c678041d687c6f1be6968f68a",
"/vendor/flasher/flasher-toastr.min.js": "/vendor/flasher/flasher-toastr.min.js?id=1ba7dae06e6524905228794c1db14290",
"/vendor/flasher/flasher-toastr.min.js": "/vendor/flasher/flasher-toastr.min.js?id=8285b44e43242b12946afdf304df9e83",
"/vendor/flasher/toastr.min.js": "/vendor/flasher/toastr.min.js?id=8ee1218b09fb02d43fcf0b84e30637ad",
"/vendor/flasher/jquery.min.js": "/vendor/flasher/jquery.min.js?id=2c872dbe60f4ba70fb85356113d8b35e",
"/vendor/flasher/sweetalert2.min.js": "/vendor/flasher/sweetalert2.min.js?id=bba3afcfe7dcc339fa25fad49842c2bf",
@@ -1 +1 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("amazon",{render:e=>{const{type:n,message:t}=e,r="error"===n||"warning"===n;return`\n <div class="fl-amazon fl-${n}" role="${r?"alert":"status"}" aria-live="${r?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-amazon-alert">\n <div class="fl-alert-content">\n <div class="fl-icon-container">\n ${(()=>{switch(n){case"success":return'<svg viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>\n </svg>';case"error":return'<svg viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>\n </svg>';case"warning":return'<svg viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>\n </svg>';case"info":return'<svg viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>\n </svg>'}return""})()}\n </div>\n <div class="fl-text-content">\n <div class="fl-alert-title">${(()=>{switch(n){case"success":return"Success!";case"error":return"Problem";case"warning":return"Warning";case"info":return"Information";default:return"Alert"}})()}</div>\n <div class="fl-alert-message">${t}</div>\n </div>\n </div>\n <div class="fl-alert-actions">\n <button class="fl-close" aria-label="Close notification">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>\n </svg>\n </button>\n </div>\n </div>\n </div>`}})}));
!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],e):e((n="undefined"!=typeof globalThis?globalThis:n||self).flasher)}(this,(function(n){"use strict";const e={sm:16,md:20,lg:24},s={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function r(n,r={}){const{size:i="md",className:t=""}=r,a="number"==typeof i?i:e[i],o=s[n];if(!o)return"";return`<svg${t?` class="${t}"`:""} viewBox="0 0 24 24" width="${a}" height="${a}" aria-hidden="true"><path fill="currentColor" d="${o}"/></svg>`}const i="fl-close",t=n=>`fl-${n}`,a=n=>`fl-${n}`,o={success:"Success",error:"Error",warning:"Warning",info:"Information"},l={success:"Success!",error:"Problem",warning:"Warning",info:"Information"},c={render:n=>{const{type:e,message:s}=n,c=l[e]||o[e]||"Alert";return`\n <div class="${a("amazon")} ${t(e)}" ${function(n){const e=function(n){const e="error"===n||"warning"===n;return{role:e?"alert":"status",ariaLive:e?"assertive":"polite",ariaAtomic:"true"}}(n);return`role="${e.role}" aria-live="${e.ariaLive}" aria-atomic="${e.ariaAtomic}"`}(e)}>\n <div class="fl-amazon-alert">\n <div class="fl-alert-content">\n <div class="fl-icon-container">\n ${function(n,e={}){return"success"===n||"error"===n||"warning"===n||"info"===n?r(n,e):""}(e,{size:"lg"})}\n </div>\n <div class="fl-text-content">\n <div class="fl-alert-title">${c}</div>\n <div class="fl-alert-message">${s}</div>\n </div>\n </div>\n <div class="fl-alert-actions">\n <button class="${i}" ${function(n){return`aria-label="Close ${n} message"`}(e)}>\n ${function(n={}){return r("close",Object.assign({size:"sm"},n))}()}\n </button>\n </div>\n </div>\n </div>`}};n.addTheme("amazon",c)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("amber",{render:e=>{const{type:s,message:i}=e,l="error"===s||"warning"===s;return`\n <div class="fl-amber fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-icon"></div>\n <div class="fl-text">\n <div class="fl-message">${i}</div>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s="fl-content",i="fl-message",n="fl-text",a="fl-icon",r="fl-close",t="fl-progress-bar",l="fl-progress",o=e=>`fl-${e}`,f=e=>`fl-${e}`,c={render:e=>{const{type:c,message:d}=e;return`\n <div class="${f("amber")} ${o(c)}" ${function(e){const s=function(e){const s="error"===e||"warning"===e;return{role:s?"alert":"status",ariaLive:s?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${s.role}" aria-live="${s.ariaLive}" aria-atomic="${s.ariaAtomic}"`}(c)}>\n <div class="${s}">\n <div class="${a}"></div>\n <div class="${n}">\n <div class="${i}">${d}</div>\n </div>\n <button class="${r}" ${function(e){return`aria-label="Close ${e} message"`}(c)}>×</button>\n </div>\n <div class="${t}">\n <div class="${l}"></div>\n </div>\n </div>`}};e.addTheme("amber",c)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("aurora",{render:e=>{const{type:s,message:a}=e,r="error"===s||"warning"===s;return`\n <div class="fl-aurora fl-${s}" role="${r?"alert":"status"}" aria-live="${r?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-message">${a}</div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const r="fl-content",s="fl-message",a="fl-close",n="fl-progress-bar",i="fl-progress",t=e=>`fl-${e}`,o=e=>`fl-${e}`,l={render:e=>{const{type:l,message:f}=e;return`\n <div class="${o("aurora")} ${t(l)}" ${function(e){const r=function(e){const r="error"===e||"warning"===e;return{role:r?"alert":"status",ariaLive:r?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${r.role}" aria-live="${r.ariaLive}" aria-atomic="${r.ariaAtomic}"`}(l)}>\n <div class="${r}">\n <div class="${s}">${f}</div>\n <button class="${a}" ${function(e){return`aria-label="Close ${e} message"`}(l)}>×</button>\n </div>\n <div class="${n}">\n <div class="${i}"></div>\n </div>\n </div>`}};e.addTheme("aurora",l)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("crystal",{render:e=>{const{type:s,message:l}=e,a="error"===s||"warning"===s;return`\n <div class="fl-crystal fl-${s}" role="${a?"alert":"status"}" aria-live="${a?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-text">\n <p class="fl-message">${l}</p>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s="fl-content",n="fl-message",r="fl-text",t="fl-close",a="fl-progress-bar",i="fl-progress",l=e=>`fl-${e}`,o=e=>`fl-${e}`,f={render:e=>{const{type:f,message:c}=e;return`\n <div class="${o("crystal")} ${l(f)}" ${function(e){const s=function(e){const s="error"===e||"warning"===e;return{role:s?"alert":"status",ariaLive:s?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${s.role}" aria-live="${s.ariaLive}" aria-atomic="${s.ariaAtomic}"`}(f)}>\n <div class="${s}">\n <div class="${r}">\n <p class="${n}">${c}</p>\n </div>\n <button class="${t}" ${function(e){return`aria-label="Close ${e} message"`}(f)}>×</button>\n </div>\n <div class="${a}">\n <div class="${i}"></div>\n </div>\n </div>`}};e.addTheme("crystal",f)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("emerald",{render:e=>{const{type:s,message:a}=e,l="error"===s||"warning"===s;return`\n <div class="fl-emerald fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-message">${a}</div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n="fl-content",t="fl-message",a="fl-close",i=e=>`fl-${e}`,r=e=>`fl-${e}`,s={render:e=>{const{type:s,message:l}=e;return`\n <div class="${r("emerald")} ${i(s)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(s)}>\n <div class="${n}">\n <div class="${t}">${l}</div>\n <button class="${a}" ${function(e){return`aria-label="Close ${e} message"`}(s)}>×</button>\n </div>\n </div>`}};e.addTheme("emerald",s)}));
@@ -1 +1 @@
!function(n,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((n="undefined"!=typeof globalThis?globalThis:n||self).flasher)}(this,(function(n){"use strict";const i={render:n=>{const{type:i,message:e}=n,s="error"===i||"warning"===i,t=s?"alert":"status",l=s?"assertive":"polite",o=(new Date).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"});return`\n <div class="fl-facebook fl-${i}" role="${t}" aria-live="${l}" aria-atomic="true">\n <div class="fl-fb-notification">\n <div class="fl-icon-container">\n ${(()=>{switch(i){case"success":return'<div class="fl-fb-icon fl-fb-icon-success">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z"/>\n </svg>\n </div>';case"error":return'<div class="fl-fb-icon fl-fb-icon-error">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.5c-.69 0-1.25.56-1.25 1.25S11.31 13 12 13s1.25-.56 1.25-1.25S12.69 10.5 12 10.5zM12 9c.552 0 1-.448 1-1V7c0-.552-.448-1-1-1s-1 .448-1 1v1c0 .552.448 1 1 1z"/>\n </svg>\n </div>';case"warning":return'<div class="fl-fb-icon fl-fb-icon-warning">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12.865 3.00017L22.3912 19.5002C22.6674 19.9785 22.5035 20.5901 22.0252 20.8662C21.8732 20.954 21.7008 21.0002 21.5252 21.0002H2.47266C1.92037 21.0002 1.47266 20.5525 1.47266 20.0002C1.47266 19.8246 1.51886 19.6522 1.60663 19.5002L11.1329 3.00017C11.409 2.52187 12.0206 2.358 12.4989 2.63414C12.651 2.72192 12.7772 2.84815 12.865 3.00017ZM11 16.0002V18.0002H13V16.0002H11ZM11 8.00017V14.0002H13V8.00017H11Z"/>\n </svg>\n </div>';case"info":return'<div class="fl-fb-icon fl-fb-icon-info">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 100-16 8 8 0 000 16zm-1-5h2v-2h-2v2zm0-4h2V7h-2v4z"/>\n </svg>\n </div>'}return""})()}\n </div>\n <div class="fl-content">\n <div class="fl-message">\n ${e}\n </div>\n <div class="fl-meta">\n <span class="fl-time">${o}</span>\n </div>\n </div>\n <div class="fl-actions">\n <button class="fl-button fl-close" aria-label="Close ${i} message">\n <div class="fl-button-icon">\n <svg viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>\n </svg>\n </div>\n </button>\n </div>\n </div>\n </div>`}};n.addTheme("facebook",i)}));
!function(n,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((n="undefined"!=typeof globalThis?globalThis:n||self).flasher)}(this,(function(n){"use strict";const i={sm:16,md:20,lg:24},e={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function s(n={}){return function(n,s={}){const{size:t="md",className:o=""}=s,r="number"==typeof t?t:i[t];return`<svg${o?` class="${o}"`:""} viewBox="0 0 24 24" width="${r}" height="${r}" aria-hidden="true"><path fill="currentColor" d="${e[n]}"/></svg>`}("close",Object.assign({size:"sm"},n))}const t="fl-content",o="fl-message",r="fl-actions",l="fl-close",c=n=>`fl-${n}`,a=n=>`fl-${n}`;const f={render:n=>{const{type:i,message:e}=n;return`\n <div class="${a("facebook")} ${c(i)}" ${function(n){const i=function(n){const i="error"===n||"warning"===n;return{role:i?"alert":"status",ariaLive:i?"assertive":"polite",ariaAtomic:"true"}}(n);return`role="${i.role}" aria-live="${i.ariaLive}" aria-atomic="${i.ariaAtomic}"`}(i)}>\n <div class="fl-fb-notification">\n <div class="fl-icon-container">\n ${(()=>{switch(i){case"success":return'<div class="fl-fb-icon fl-fb-icon-success">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z"/>\n </svg>\n </div>';case"error":return'<div class="fl-fb-icon fl-fb-icon-error">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-11.5c-.69 0-1.25.56-1.25 1.25S11.31 13 12 13s1.25-.56 1.25-1.25S12.69 10.5 12 10.5zM12 9c.552 0 1-.448 1-1V7c0-.552-.448-1-1-1s-1 .448-1 1v1c0 .552.448 1 1 1z"/>\n </svg>\n </div>';case"warning":return'<div class="fl-fb-icon fl-fb-icon-warning">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12.865 3.00017L22.3912 19.5002C22.6674 19.9785 22.5035 20.5901 22.0252 20.8662C21.8732 20.954 21.7008 21.0002 21.5252 21.0002H2.47266C1.92037 21.0002 1.47266 20.5525 1.47266 20.0002C1.47266 19.8246 1.51886 19.6522 1.60663 19.5002L11.1329 3.00017C11.409 2.52187 12.0206 2.358 12.4989 2.63414C12.651 2.72192 12.7772 2.84815 12.865 3.00017ZM11 16.0002V18.0002H13V16.0002H11ZM11 8.00017V14.0002H13V8.00017H11Z"/>\n </svg>\n </div>';case"info":return'<div class="fl-fb-icon fl-fb-icon-info">\n <svg viewBox="0 0 24 24" width="16" height="16">\n <path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 100-16 8 8 0 000 16zm-1-5h2v-2h-2v2zm0-4h2V7h-2v4z"/>\n </svg>\n </div>'}return""})()}\n </div>\n <div class="${t}">\n <div class="${o}">\n ${e}\n </div>\n <div class="fl-meta">\n <span class="fl-time">${(new Date).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})}</span>\n </div>\n </div>\n <div class="${r}">\n <button class="fl-button ${l}" ${function(n){return`aria-label="Close ${n} message"`}(i)}>\n <div class="fl-button-icon">\n ${s({size:"md"})}\n </div>\n </button>\n </div>\n </div>\n </div>`}};n.addTheme("facebook",f)}));
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("flasher",{render:e=>{const{type:s,title:n,message:a}=e,l="error"===s||"warning"===s,t=l?"alert":"status",r=l?"assertive":"polite",i=n||s.charAt(0).toUpperCase()+s.slice(1);return`\n <div class="fl-flasher fl-${s}" role="${t}" aria-live="${r}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-icon"></div>\n <div>\n <strong class="fl-title">${i}</strong>\n <span class="fl-message">${a}</span>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">&times;</button>\n </div>\n <span class="fl-progress-bar">\n <span class="fl-progress"></span>\n </span>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n="fl-content",s="fl-message",r="fl-title",a="fl-icon",t="fl-close",i="fl-progress-bar",o="fl-progress",l=e=>`fl-${e}`,f=e=>`fl-${e}`,c={success:"Success",error:"Error",warning:"Warning",info:"Information"};const u={render:e=>{const{type:u,title:d,message:$}=e,p=function(e,n){return e||c[n]||function(e){return e.charAt(0).toUpperCase()+e.slice(1)}(n)}(d,u);return`\n <div class="${f("flasher")} ${l(u)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(u)}>\n <div class="${n}">\n <div class="${a}"></div>\n <div>\n <strong class="${r}">${p}</strong>\n <span class="${s}">${$}</span>\n </div>\n <button class="${t}" ${function(e){return`aria-label="Close ${e} message"`}(u)}>&times;</button>\n </div>\n <span class="${i}">\n <span class="${o}"></span>\n </span>\n </div>`}};e.addTheme("flasher",u)}));
@@ -1 +1 @@
!function(s,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],e):e((s="undefined"!=typeof globalThis?globalThis:s||self).flasher)}(this,(function(s){"use strict";s.addTheme("google",{render:s=>{const{type:e,message:n,title:l}=s,i="error"===e||"warning"===e,t=l?`<div class="fl-title">${l}</div>`:"";return`\n <div class="fl-google fl-${e}" role="${i?"alert":"status"}" aria-live="${i?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-md-card">\n <div class="fl-content">\n <div class="fl-icon-wrapper">\n ${(()=>{switch(e){case"success":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>\n </svg>';case"error":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>\n </svg>';case"warning":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z"/>\n </svg>';case"info":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>\n </svg>'}return""})()}\n </div>\n <div class="fl-text-content">\n ${t}\n <div class="fl-message">${n}</div>\n </div>\n </div>\n <div class="fl-actions">\n <button class="fl-action-button fl-close" aria-label="Close ${e} message">\n DISMISS\n </button>\n </div>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n={sm:16,md:20,lg:24},s={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function i(e,i={}){return"success"===e||"error"===e||"warning"===e||"info"===e?function(e,i={}){const{size:t="md",className:l=""}=i,r="number"==typeof t?t:n[t],a=s[e];return a?`<svg${l?` class="${l}"`:""} viewBox="0 0 24 24" width="${r}" height="${r}" aria-hidden="true"><path fill="currentColor" d="${a}"/></svg>`:""}(e,i):""}const t="fl-content",l="fl-message",r="fl-title",a="fl-icon-wrapper",o="fl-actions",c="fl-close",f="fl-progress-bar",d="fl-progress",v=e=>`fl-${e}`,u=e=>`fl-${e}`,$="DISMISS",h={render:e=>{const{type:n,message:s,title:h}=e,m=h?`<div class="${r}">${h}</div>`:"";return`\n <div class="${u("google")} ${v(n)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(n)}>\n <div class="fl-md-card">\n <div class="${t}">\n <div class="${a}">\n ${i(n,{size:"lg",className:"fl-icon-svg"})}\n </div>\n <div class="fl-text-content">\n ${m}\n <div class="${l}">${s}</div>\n </div>\n </div>\n <div class="${o}">\n <button class="fl-action-button ${c}" ${function(e){return`aria-label="Close ${e} message"`}(n)}>\n ${$}\n </button>\n </div>\n </div>\n <div class="${f}">\n <div class="${d}"></div>\n </div>\n </div>`}};e.addTheme("google",h)}));
+1 -1
View File
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s={render:e=>{const{type:s,message:i,title:n}=e,l="error"===s||"warning"===s,t=l?"alert":"status",a=l?"assertive":"polite",r=(new Date).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}),o=n||"PHPFlasher";return`\n <div class="fl-ios fl-${s}" role="${t}" aria-live="${a}" aria-atomic="true">\n <div class="fl-ios-notification">\n <div class="fl-header">\n <div class="fl-app-icon">\n ${(()=>{switch(s){case"success":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>\n </svg>';case"error":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>\n </svg>';case"warning":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/>\n </svg>';case"info":return'<svg class="fl-icon-svg" viewBox="0 0 24 24" width="20" height="20">\n <path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>\n </svg>'}return""})()}\n </div>\n <div class="fl-app-info">\n <div class="fl-app-name">${o}</div>\n <div class="fl-time">${r}</div>\n </div>\n </div>\n <div class="fl-content">\n <div class="fl-message">${i}</div>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">\n <span aria-hidden="true">×</span>\n </button>\n </div>\n </div>`}};e.addTheme("ios",s)}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n={sm:16,md:20,lg:24},i={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function s(e,s={}){return"success"===e||"error"===e||"warning"===e||"info"===e?function(e,s={}){const{size:a="md",className:t=""}=s,r="number"==typeof a?a:n[a],l=i[e];return l?`<svg${t?` class="${t}"`:""} viewBox="0 0 24 24" width="${r}" height="${r}" aria-hidden="true"><path fill="currentColor" d="${l}"/></svg>`:""}(e,s):""}const a="fl-content",t="fl-message",r="fl-close",l=e=>`fl-${e}`,o=e=>`fl-${e}`;const c={render:e=>{const{type:n,message:i,title:c}=e,d=c||"PHPFlasher";return`\n <div class="${o("ios")} ${l(n)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(n)}>\n <div class="fl-ios-notification">\n <div class="fl-header">\n <div class="fl-app-icon">\n ${s(n,{size:"md",className:"fl-icon-svg"})}\n </div>\n <div class="fl-app-info">\n <div class="fl-app-name">${d}</div>\n <div class="fl-time">${(new Date).toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})}</div>\n </div>\n </div>\n <div class="${a}">\n <div class="${t}">${i}</div>\n </div>\n <button class="${r}" ${function(e){return`aria-label="Close ${e} message"`}(n)}>\n <span aria-hidden="true">×</span>\n </button>\n </div>\n </div>`}};e.addTheme("ios",c)}));
+1 -1
View File
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("jade",{render:e=>{const{type:s,message:a}=e,l="error"===s||"warning"===s;return`\n <div class="fl-jade fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-message">${a}</div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s="fl-content",n="fl-message",i="fl-close",a="fl-progress-bar",r="fl-progress",t=e=>`fl-${e}`,l=e=>`fl-${e}`,o={render:e=>{const{type:o,message:f}=e;return`\n <div class="${l("jade")} ${t(o)}" ${function(e){const s=function(e){const s="error"===e||"warning"===e;return{role:s?"alert":"status",ariaLive:s?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${s.role}" aria-live="${s.ariaLive}" aria-atomic="${s.ariaAtomic}"`}(o)}>\n <div class="${s}">\n <div class="${n}">${f}</div>\n <button class="${i}" ${function(e){return`aria-label="Close ${e} message"`}(o)}>×</button>\n </div>\n <div class="${a}">\n <div class="${r}"></div>\n </div>\n </div>`}};e.addTheme("jade",o)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("material",{render:e=>{const{type:s,message:n}=e,a="error"===s||"warning"===s;return`\n <div class="fl-material fl-${s}" role="${a?"alert":"status"}" aria-live="${a?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-md-card">\n <div class="fl-content">\n <div class="fl-text-content">\n <div class="fl-message">${n}</div>\n </div>\n </div>\n <div class="fl-actions">\n <button class="fl-action-button fl-close" aria-label="Close ${s} message">\n DISMISS\n </button>\n </div>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n="fl-content",i="fl-message",s="fl-actions",a="fl-close",t="fl-progress-bar",r="fl-progress",l=e=>`fl-${e}`,o=e=>`fl-${e}`,f="DISMISS",c={render:e=>{const{type:c,message:d}=e;return`\n <div class="${o("material")} ${l(c)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(c)}>\n <div class="fl-md-card">\n <div class="${n}">\n <div class="fl-text-content">\n <div class="${i}">${d}</div>\n </div>\n </div>\n <div class="${s}">\n <button class="fl-action-button ${a}" ${function(e){return`aria-label="Close ${e} message"`}(c)}>\n ${f}\n </button>\n </div>\n </div>\n <div class="${t}">\n <div class="${r}"></div>\n </div>\n </div>`}};e.addTheme("material",c)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("minimal",{render:e=>{const{type:s,message:i}=e,l="error"===s||"warning"===s;return`\n <div class="fl-minimal fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-message">${i}</div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const i="fl-content",n="fl-message",s="fl-close",a="fl-progress-bar",r="fl-progress",t=e=>`fl-${e}`,l=e=>`fl-${e}`,o={render:e=>{const{type:o,message:f}=e;return`\n <div class="${l("minimal")} ${t(o)}" ${function(e){const i=function(e){const i="error"===e||"warning"===e;return{role:i?"alert":"status",ariaLive:i?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${i.role}" aria-live="${i.ariaLive}" aria-atomic="${i.ariaAtomic}"`}(o)}>\n <div class="${i}">\n <div class="${n}">${f}</div>\n <button class="${s}" ${function(e){return`aria-label="Close ${e} message"`}(o)}>×</button>\n </div>\n <div class="${a}">\n <div class="${r}"></div>\n </div>\n </div>`}};e.addTheme("minimal",o)}));
+1 -1
View File
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("neon",{render:e=>{const{type:s,message:n}=e,l="error"===s||"warning"===s;return`\n <div class="fl-neon fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-message">${n}</div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n="fl-content",s="fl-message",i="fl-close",r="fl-progress-bar",a="fl-progress",t=e=>`fl-${e}`,o=e=>`fl-${e}`,l={render:e=>{const{type:l,message:f}=e;return`\n <div class="${o("neon")} ${t(l)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(l)}>\n <div class="${n}">\n <div class="${s}">${f}</div>\n <button class="${i}" ${function(e){return`aria-label="Close ${e} message"`}(l)}>×</button>\n </div>\n <div class="${r}">\n <div class="${a}"></div>\n </div>\n </div>`}};e.addTheme("neon",l)}));
+1 -1
View File
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("onyx",{render:e=>{const{type:s,message:n}=e,l="error"===s||"warning"===s;return`\n <div class="fl-onyx fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <div class="fl-text">\n <div class="fl-message">${n}</div>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const n="fl-content",s="fl-message",i="fl-text",r="fl-close",t="fl-progress-bar",a="fl-progress",l=e=>`fl-${e}`,o=e=>`fl-${e}`,f={render:e=>{const{type:f,message:c}=e;return`\n <div class="${o("onyx")} ${l(f)}" ${function(e){const n=function(e){const n="error"===e||"warning"===e;return{role:n?"alert":"status",ariaLive:n?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${n.role}" aria-live="${n.ariaLive}" aria-atomic="${n.ariaAtomic}"`}(f)}>\n <div class="${n}">\n <div class="${i}">\n <div class="${s}">${c}</div>\n </div>\n <button class="${r}" ${function(e){return`aria-label="Close ${e} message"`}(f)}>×</button>\n </div>\n <div class="${t}">\n <div class="${a}"></div>\n </div>\n </div>`}};e.addTheme("onyx",f)}));
+1 -1
View File
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("ruby",{render:e=>{const{type:s,message:i}=e,l="error"===s||"warning"===s;return`\n <div class="fl-ruby fl-${s}" role="${l?"alert":"status"}" aria-live="${l?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-shine"></div>\n <div class="fl-content">\n <div class="fl-icon-circle">\n <div class="fl-icon"></div>\n </div>\n <div class="fl-text">\n <div class="fl-message">${i}</div>\n </div>\n <button class="fl-close" aria-label="Close ${s} message">×</button>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],i):i((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const i="fl-content",s="fl-message",n="fl-text",r="fl-icon",a="fl-close",l="fl-progress-bar",t="fl-progress",o=e=>`fl-${e}`,f=e=>`fl-${e}`,c={render:e=>{const{type:c,message:d}=e;return`\n <div class="${f("ruby")} ${o(c)}" ${function(e){const i=function(e){const i="error"===e||"warning"===e;return{role:i?"alert":"status",ariaLive:i?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${i.role}" aria-live="${i.ariaLive}" aria-atomic="${i.ariaAtomic}"`}(c)}>\n <div class="fl-shine"></div>\n <div class="${i}">\n <div class="fl-icon-circle">\n <div class="${r}"></div>\n </div>\n <div class="${n}">\n <div class="${s}">${d}</div>\n </div>\n <button class="${a}" ${function(e){return`aria-label="Close ${e} message"`}(c)}>×</button>\n </div>\n <div class="${l}">\n <div class="${t}"></div>\n </div>\n </div>`}};e.addTheme("ruby",c)}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("sapphire",{render:e=>{const{type:s,message:a}=e,i="error"===s||"warning"===s;return`\n <div class="fl-sapphire fl-${s}" role="${i?"alert":"status"}" aria-live="${i?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-content">\n <span class="fl-message">${a}</span>\n </div>\n <div class="fl-progress-bar">\n <div class="fl-progress"></div>\n </div>\n </div>`}})}));
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s="fl-content",i="fl-message",r="fl-progress-bar",n="fl-progress",a=e=>`fl-${e}`,t=e=>`fl-${e}`,o={render:e=>{const{type:o,message:l}=e;return`\n <div class="${t("sapphire")} ${a(o)}" ${function(e){const s=function(e){const s="error"===e||"warning"===e;return{role:s?"alert":"status",ariaLive:s?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${s.role}" aria-live="${s.ariaLive}" aria-atomic="${s.ariaAtomic}"`}(o)}>\n <div class="${s}">\n <span class="${i}">${l}</span>\n </div>\n <div class="${r}">\n <div class="${n}"></div>\n </div>\n </div>`}};e.addTheme("sapphire",o)}));
@@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).theme=e.theme||{},e.theme.shared={}))}(this,(function(e){"use strict";const t={sm:16,md:20,lg:24},r={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function n(e,n={}){const{size:s="md",className:o=""}=n,i="number"==typeof s?s:t[s],l=r[e];if(!l)return"";return`<svg${o?` class="${o}"`:""} viewBox="0 0 24 24" width="${i}" height="${i}" aria-hidden="true"><path fill="currentColor" d="${l}"/></svg>`}function s(e){const t="error"===e||"warning"===e;return{role:t?"alert":"status",ariaLive:t?"assertive":"polite",ariaAtomic:"true"}}const o={success:"Success",error:"Error",warning:"Warning",info:"Information"};function i(e){return e.charAt(0).toUpperCase()+e.slice(1)}e.CLASS_NAMES={container:"fl-container",wrapper:"fl-wrapper",content:"fl-content",message:"fl-message",title:"fl-title",text:"fl-text",icon:"fl-icon",iconWrapper:"fl-icon-wrapper",actions:"fl-actions",close:"fl-close",progressBar:"fl-progress-bar",progress:"fl-progress",show:"fl-show",sticky:"fl-sticky",rtl:"fl-rtl",type:e=>`fl-${e}`,theme:e=>`fl-${e}`},e.DEFAULT_TEXT={dismissButton:"DISMISS",closeLabel:e=>`Close ${e} message`},e.DEFAULT_TITLES=o,e.capitalizeType=i,e.getA11yAttributes=s,e.getA11yString=function(e){const t=s(e);return`role="${t.role}" aria-live="${t.ariaLive}" aria-atomic="${t.ariaAtomic}"`},e.getCloseButtonA11y=function(e){return`aria-label="Close ${e} message"`},e.getCloseIcon=function(e={}){return n("close",Object.assign({size:"sm"},e))},e.getIcon=n,e.getTitle=function(e,t){return e||o[t]||i(t)},e.getTypeIcon=function(e,t={}){return"success"===e||"error"===e||"warning"===e||"info"===e?n(e,t):""}}));
@@ -1 +1 @@
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";e.addTheme("slack",{render:e=>{const{type:s,message:i}=e,n="error"===s||"warning"===s;return`\n <div class="fl-slack fl-${s}" role="${n?"alert":"status"}" aria-live="${n?"assertive":"polite"}" aria-atomic="true">\n <div class="fl-slack-message">\n <div class="fl-avatar">\n ${(()=>{switch(s){case"success":return'<div class="fl-type-icon fl-success-icon">✓</div>';case"error":return'<div class="fl-type-icon fl-error-icon">✕</div>';case"warning":return'<div class="fl-type-icon fl-warning-icon">!</div>';case"info":return'<div class="fl-type-icon fl-info-icon">i</div>'}return""})()}\n </div>\n <div class="fl-message-content">\n <div class="fl-message-text">${i}</div>\n </div>\n <div class="fl-actions">\n <button class="fl-close" aria-label="Close ${s} message">\n <svg viewBox="0 0 20 20" width="16" height="16">\n <path fill="currentColor" d="M10 8.586L6.707 5.293a1 1 0 00-1.414 1.414L8.586 10l-3.293 3.293a1 1 0 101.414 1.414L10 11.414l3.293 3.293a1 1 0 001.414-1.414L11.414 10l3.293-3.293a1 1 0 00-1.414-1.414L10 8.586z"/>\n </svg>\n </button>\n </div>\n </div>\n </div>`}})}));
!function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(require("@flasher/flasher")):"function"==typeof define&&define.amd?define(["@flasher/flasher"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).flasher)}(this,(function(e){"use strict";const s={sm:16,md:20,lg:24},n={success:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",error:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z",warning:"M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z",info:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z",close:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"};function i(e={}){return function(e,i={}){const{size:a="md",className:t=""}=i,r="number"==typeof a?a:s[a];return`<svg${t?` class="${t}"`:""} viewBox="0 0 24 24" width="${r}" height="${r}" aria-hidden="true"><path fill="currentColor" d="${n[e]}"/></svg>`}("close",Object.assign({size:"sm"},e))}const a="fl-actions",t="fl-close",r=e=>`fl-${e}`,l=e=>`fl-${e}`,o={success:"✓",error:"✕",warning:"!",info:"i"},c={render:e=>{const{type:s,message:n}=e,c=o[s]||"";return`\n <div class="${l("slack")} ${r(s)}" ${function(e){const s=function(e){const s="error"===e||"warning"===e;return{role:s?"alert":"status",ariaLive:s?"assertive":"polite",ariaAtomic:"true"}}(e);return`role="${s.role}" aria-live="${s.ariaLive}" aria-atomic="${s.ariaAtomic}"`}(s)}>\n <div class="fl-slack-message">\n <div class="fl-avatar">\n <div class="fl-type-icon fl-${s}-icon">${c}</div>\n </div>\n <div class="fl-message-content">\n <div class="fl-message-text">${n}</div>\n </div>\n <div class="${a}">\n <button class="${t}" ${function(e){return`aria-label="Close ${e} message"`}(s)}>\n ${i()}\n </button>\n </div>\n </div>\n </div>`}};e.addTheme("slack",c)}));
@@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class DemoController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function home(): Response
{
return $this->render('home.html.twig');
}
#[Route('/types', name: 'app_types')]
public function types(): Response
{
return $this->render('types.html.twig');
}
#[Route('/themes', name: 'app_themes')]
public function themes(): Response
{
return $this->render('themes.html.twig');
}
#[Route('/adapters', name: 'app_adapters')]
public function adapters(): Response
{
return $this->render('adapters.html.twig');
}
#[Route('/positions', name: 'app_positions')]
public function positions(): Response
{
return $this->render('positions.html.twig');
}
#[Route('/examples', name: 'app_examples')]
public function examples(): Response
{
return $this->render('examples.html.twig');
}
#[Route('/playground', name: 'app_playground')]
public function playground(): Response
{
return $this->render('playground.html.twig');
}
#[Route('/notify', name: 'app_notify', methods: ['POST'])]
public function notify(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true);
$type = $data['type'] ?? 'success';
$message = $data['message'] ?? 'Notification message';
$title = $data['title'] ?? null;
$theme = $data['theme'] ?? null;
$position = $data['position'] ?? 'top-right';
$timeout = (int) ($data['timeout'] ?? 5000);
$flasher = flash();
// Apply theme if specified
if ($theme && 'flasher' !== $theme) {
$flasher = $flasher->use("theme.{$theme}");
}
$options = [
'position' => $position,
'timeout' => $timeout,
];
if ($title) {
$options['title'] = $title;
}
$flasher->{$type}($message, $options);
return new JsonResponse(['success' => true]);
}
#[Route('/example/{scenario}', name: 'app_example', methods: ['POST'])]
public function runExample(string $scenario): JsonResponse
{
match ($scenario) {
'registration' => $this->registrationExample(),
'login_failed' => $this->loginFailedExample(),
'validation' => $this->validationExample(),
'shopping_cart' => $this->shoppingCartExample(),
'file_upload' => $this->fileUploadExample(),
'settings' => $this->settingsExample(),
'payment_success' => $this->paymentSuccessExample(),
'payment_failed' => $this->paymentFailedExample(),
'delete_confirm' => $this->deleteConfirmExample(),
'session_expiring' => $this->sessionExpiringExample(),
default => flash()->info('Example not found'),
};
return new JsonResponse(['success' => true]);
}
private function registrationExample(): void
{
flash()->success('Welcome! Your account has been created.');
flash()->info('Please check your email to verify your account.');
}
private function loginFailedExample(): void
{
flash()->error('Invalid email or password.');
flash()->info('Forgot your password? Click here to reset.');
}
private function validationExample(): void
{
flash()->error('The email field is required.');
flash()->error('Password must be at least 8 characters.');
flash()->error('Please accept the terms and conditions.');
}
private function shoppingCartExample(): void
{
flash()->success('iPhone 15 Pro added to cart!');
flash()->warning('Only 2 items left in stock!');
flash()->info('Add $20 more for free shipping!');
}
private function fileUploadExample(): void
{
flash()->success('document.pdf uploaded successfully!');
flash()->info('File size: 2.4 MB');
}
private function settingsExample(): void
{
flash()->success('Settings saved successfully!');
flash()->info('Some changes may require a page refresh.');
}
private function paymentSuccessExample(): void
{
flash()->success('Payment of $149.99 confirmed!');
flash()->info('Order #12345 - Receipt sent to your email.');
}
private function paymentFailedExample(): void
{
flash()->error('Payment declined by your bank.');
flash()->warning('Please try a different payment method.');
flash()->info('Your cart has been saved.');
}
private function deleteConfirmExample(): void
{
flash()->warning('Are you sure? This action cannot be undone.');
flash()->info('Click confirm to delete or cancel to keep the item.');
}
private function sessionExpiringExample(): void
{
flash()->warning('Your session will expire in 5 minutes.');
flash()->info('Click anywhere to stay logged in.');
}
}
@@ -1,147 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ExamplesController extends AbstractController
{
#[Route('/examples', name: 'app_examples')]
public function index(): Response
{
flash()->info('Browse through our examples to see PHPFlasher in action!');
return $this->render('examples/index.html.twig');
}
#[Route('/examples/form', name: 'app_form_example')]
public function formExample(Request $request): Response
{
if ($request->isMethod('POST')) {
$name = $request->request->get('name');
$email = $request->request->get('email');
// Simulate validation
$errors = [];
if (empty($name)) {
$errors['name'] = 'Name is required';
}
if (empty($email)) {
$errors['email'] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Invalid email format';
}
// Display appropriate notifications
if (empty($errors)) {
flash()->success('Form submitted successfully! Thank you, ' . $name);
} else {
foreach ($errors as $field => $message) {
flash()->error($message);
}
}
}
return $this->render('examples/form.html.twig');
}
#[Route('/examples/ajax', name: 'app_ajax_example')]
public function ajaxExample(): Response
{
return $this->render('examples/ajax.html.twig');
}
#[Route('/examples/ajax/process', name: 'app_ajax_process', methods: ['POST'])]
public function ajaxProcess(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$action = $request->request->get('action');
$success = mt_rand(0, 10) > 2; // 80% success rate for demo
if ($success) {
switch ($action) {
case 'save':
flash()->success('Data saved successfully!');
break;
case 'update':
flash()->success('Record updated successfully!');
break;
case 'delete':
flash()->info('Item has been deleted.');
break;
default:
flash()->success('Operation completed successfully!');
}
return new JsonResponse(['success' => true]);
} else {
// Simulate errors
flash()->error('An error occurred while processing your request. Please try again.');
return new JsonResponse(['success' => false], 500);
}
}
#[Route('/examples/real-world', name: 'app_real_world')]
public function realWorldExamples(): Response
{
return $this->render('examples/real-world.html.twig');
}
#[Route('/examples/real-world/user-registration', name: 'app_example_user_registration')]
public function userRegistration(Request $request): Response
{
if ($request->isMethod('POST')) {
// Simulate user registration
$username = $request->request->get('username');
if (!empty($username)) {
flash()->option('timeout', 5000)
->option('showProgressbar', true)
->success('Registration successful! Welcome, ' . $username . '!');
// Send additional notification about email verification
flash()->option('timeout', 8000)
->info('A verification email has been sent to your inbox.');
} else {
flash()->error('Username is required.');
}
}
return $this->render('examples/user-registration.html.twig');
}
#[Route('/examples/real-world/shopping-cart', name: 'app_example_shopping_cart')]
public function shoppingCart(): Response
{
return $this->render('examples/shopping-cart.html.twig');
}
#[Route('/examples/real-world/add-to-cart', name: 'app_add_to_cart', methods: ['POST'])]
public function addToCart(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$productId = $request->request->get('product_id');
$productName = $request->request->get('product_name', 'Product');
// Simulate adding to cart
flash()->option('position', 'bottom-right')
->option('showCloseButton', true)
->success($productName . ' added to your cart!');
return new JsonResponse(['success' => true]);
}
}
@@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class FeaturesController extends AbstractController
{
#[Route('/features', name: 'app_features')]
public function index(): Response
{
// Demonstrate a few different notification types
flash()->success('Explore the features of PHPFlasher!');
flash()->option('timeout', 8000)
->info('Click on the links to see each feature in action.');
return $this->render('features/index.html.twig');
}
#[Route('/features/types', name: 'app_types')]
public function types(): Response
{
// Demonstrate different notification types
flash()->success('This is a success notification!');
flash()->info('This is an info notification!');
flash()->warning('This is a warning notification!');
flash()->error('This is an error notification!');
return $this->render('features/types.html.twig');
}
#[Route('/features/positions', name: 'app_positions')]
public function positions(): Response
{
// Notifications with different positions
flash()->option('position', 'top-right')->success('This notification appears in the top-right corner!');
return $this->render('features/positions.html.twig');
}
#[Route('/features/options', name: 'app_options')]
public function options(): Response
{
// Notification with custom options
flash()->option('timeout', 8000)
->option('position', 'bottom-center')
->option('showProgressbar', true)
->success('This notification has custom options!');
return $this->render('features/options.html.twig');
}
#[Route('/features/plugins', name: 'app_plugins')]
public function plugins(): Response
{
// Plugin examples
flash()->addSuccess('This shows how PHPFlasher can be extended with plugins!');
return $this->render('features/plugins.html.twig');
}
#[Route('/features/presets', name: 'app_presets')]
public function presets(): Response
{
// Example of using presets
flash()->preset('profile.updated')
->success('Your profile has been updated successfully!');
flash()->preset('item.deleted')
->info('The item has been deleted.');
return $this->render('features/presets.html.twig');
}
}
@@ -1,82 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HomeController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function index(): Response
{
// Welcome notification
flash()->option('timeout', 10000)
->info('Welcome to the PHPFlasher Demo! 👋 Explore different features using the navigation.');
return $this->render('home/index.html.twig');
}
#[Route('/installation', name: 'app_installation')]
public function installation(): Response
{
return $this->render('home/installation.html.twig');
}
#[Route('/basic-usage', name: 'app_basic_usage')]
public function basicUsage(): Response
{
// Basic success notification
flash()->success('This is a success notification!');
return $this->render('home/basic-usage.html.twig');
}
#[Route('/playground', name: 'app_playground')]
public function playground(): Response
{
return $this->render('home/playground.html.twig');
}
#[Route('/theme-builder', name: 'app_theme_builder')]
public function themeBuilder(): Response
{
return $this->render('home/theme-builder.html.twig');
}
#[Route('/show-notification', name: 'app_show_notification', methods: ['POST'])]
public function showNotification(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
return new JsonResponse(['error' => 'AJAX requests only'], 400);
}
$data = json_decode($request->getContent(), true);
$type = $data['type'] ?? 'info';
$message = $data['message'] ?? 'This is a notification!';
// Create notification based on type
switch ($type) {
case 'success':
flash()->success($message);
break;
case 'error':
flash()->error($message);
break;
case 'warning':
flash()->warning($message);
break;
case 'info':
default:
flash()->info($message);
break;
}
return new JsonResponse(['status' => 'success']);
}
}
@@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class IntegrationController extends AbstractController
{
#[Route('/integration/api', name: 'app_integration_api')]
public function apiIntegration(): Response
{
// Example of how PHPFlasher can work with API responses
flash()->option('position', 'bottom-center')
->success('API request processed successfully!');
return $this->render('integration/api.html.twig');
}
#[Route('/integration/translation', name: 'app_integration_translation')]
public function translationIntegration(): Response
{
// Example using translation integration
flash()->option('translate', true)
->success('messages.success.operation_completed');
return $this->render('integration/translation.html.twig');
}
#[Route('/integration/symfony-flashbag', name: 'app_integration_flashbag')]
public function symfonyFlashbag(): Response
{
// Add message to Symfony flash bag to demonstrate integration
$this->addFlash('success', 'This message was added using Symfony\'s addFlash method!');
$this->addFlash('error', 'This error was added using Symfony\'s addFlash method!');
$this->addFlash('warning', 'This warning was added using Symfony\'s addFlash method!');
return $this->render('integration/flashbag.html.twig');
}
#[Route('/integration/custom-templates', name: 'app_integration_templates')]
public function customTemplates(): Response
{
// Example using custom templates
flash()->option('template', 'custom_template')
->success('This notification uses a custom template!');
return $this->render('integration/custom-templates.html.twig');
}
}
@@ -1,71 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ThemesController extends AbstractController
{
#[Route('/themes', name: 'app_themes')]
public function index(): Response
{
// Show default theme notification
flash()->success('This is a notification with the default theme!');
return $this->render('themes/index.html.twig');
}
#[Route('/themes/flasher', name: 'app_theme_flasher')]
public function flasherTheme(): Response
{
// Using the Flasher theme
flash()->option('theme', 'flasher')
->success('This notification uses the Flasher theme!');
return $this->render('themes/flasher.html.twig');
}
#[Route('/themes/crystal', name: 'app_theme_crystal')]
public function crystalTheme(): Response
{
// Using the Crystal theme
flash()->option('theme', 'crystal')
->success('This notification uses the Crystal theme!');
return $this->render('themes/crystal.html.twig');
}
#[Route('/themes/emerald', name: 'app_theme_emerald')]
public function emeraldTheme(): Response
{
// Using the Emerald theme
flash()->option('theme', 'emerald')
->success('This notification uses the Emerald theme!');
return $this->render('themes/emerald.html.twig');
}
#[Route('/themes/sapphire', name: 'app_theme_sapphire')]
public function sapphireTheme(): Response
{
// Using the Sapphire theme
flash()->option('theme', 'sapphire')
->success('This notification uses the Sapphire theme!');
return $this->render('themes/sapphire.html.twig');
}
#[Route('/themes/amazon', name: 'app_theme_amazon')]
public function amazonTheme(): Response
{
// Using the Amazon theme
flash()->option('theme', 'amazon')
->success('This notification uses the Amazon theme!');
return $this->render('themes/amazon.html.twig');
}
}
+20 -5
View File
@@ -11,6 +11,21 @@
"config/packages/nelmio_security.yaml"
]
},
"php-flasher/flasher-noty-symfony": {
"version": "2.x-dev"
},
"php-flasher/flasher-notyf-symfony": {
"version": "2.x-dev"
},
"php-flasher/flasher-sweetalert-symfony": {
"version": "2.x-dev"
},
"php-flasher/flasher-symfony": {
"version": "2.x-dev"
},
"php-flasher/flasher-toastr-symfony": {
"version": "2.x-dev"
},
"phpstan/phpstan": {
"version": "1.10",
"recipe": {
@@ -99,16 +114,16 @@
"config/packages/monolog.yaml"
]
},
"symfony/notifier": {
"version": "7.0",
"symfony/property-info": {
"version": "7.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.0",
"ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc"
"version": "7.3",
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
},
"files": [
"config/packages/notifier.yaml"
"config/packages/property_info.yaml"
]
},
"symfony/routing": {
+243
View File
@@ -0,0 +1,243 @@
{% extends 'base.html.twig' %}
{% block title %}Adapters{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Notification Adapters</h1>
<p class="section-subtitle">PHPFlasher supports multiple notification libraries. Choose your favorite!</p>
</div>
<div class="space-y-8">
{# Flasher (Default) #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-indigo-100 rounded-xl">
<svg class="w-8 h-8 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Flasher</h2>
<p class="text-gray-500">Default adapter with 17+ themes</p>
</div>
</div>
<p class="text-gray-600 mb-4">The default PHPFlasher adapter with beautiful built-in themes. No additional JavaScript libraries required.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'This is the default Flasher notification!', adapter: 'flasher'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Error notification with Flasher', adapter: 'flasher'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Warning notification with Flasher', adapter: 'flasher'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Info notification with Flasher', adapter: 'flasher'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header"><span>Installation</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Usage</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Operation completed!');</code></pre>
</div>
</div>
</div>
{# Toastr #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-sky-500 to-blue-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-sky-100 rounded-xl">
<svg class="w-8 h-8 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Toastr</h2>
<p class="text-gray-500">Simple, elegant toast notifications</p>
</div>
</div>
<p class="text-gray-600 mb-4">Classic toast notifications with smooth animations. One of the most popular notification libraries.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Toastr success notification!', adapter: 'toastr'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Toastr error notification!', adapter: 'toastr'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Toastr warning notification!', adapter: 'toastr'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Toastr info notification!', adapter: 'toastr'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header"><span>Installation</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-toastr-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Usage</span></div>
<pre class="!m-0"><code class="language-php">toastr()->success('Data saved successfully!');
// With options
toastr()
->closeButton()
->progressBar()
->positionClass('toast-top-right')
->success('Profile updated!');</code></pre>
</div>
</div>
</div>
{# SweetAlert #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-pink-500 to-rose-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-pink-100 rounded-xl">
<svg class="w-8 h-8 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">SweetAlert 2</h2>
<p class="text-gray-500">Beautiful, responsive, customizable alerts</p>
</div>
</div>
<p class="text-gray-600 mb-4">Feature-rich modal dialogs with confirmations, inputs, and beautiful animations. Perfect for important user interactions.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'SweetAlert success!', title: 'Success', adapter: 'sweetalert'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'SweetAlert error message', title: 'Error', adapter: 'sweetalert'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Are you sure you want to proceed?', title: 'Warning', adapter: 'sweetalert'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Here is some information', title: 'Info', adapter: 'sweetalert'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header"><span>Installation</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-sweetalert-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Usage</span></div>
<pre class="!m-0"><code class="language-php">sweetalert()->success('Great job!', 'Success');
// Confirmation dialog
sweetalert()
->showDenyButton()
->showCancelButton()
->confirmButtonText('Save')
->denyButtonText("Don't save")
->warning('Do you want to save the changes?');</code></pre>
</div>
</div>
</div>
{# Noty #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-8 h-8 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Noty</h2>
<p class="text-gray-500">Dependency-free notification library</p>
</div>
</div>
<p class="text-gray-600 mb-4">A flexible, dependency-free notification library with multiple layouts and themes.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Noty success notification!', adapter: 'noty'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Noty error notification!', adapter: 'noty'})"
class="btn btn-danger">Error</button>
<button onclick="showNotification({type: 'warning', message: 'Noty warning notification!', adapter: 'noty'})"
class="btn btn-warning">Warning</button>
<button onclick="showNotification({type: 'info', message: 'Noty info notification!', adapter: 'noty'})"
class="btn btn-info">Info</button>
</div>
<div class="code-block">
<div class="code-header"><span>Installation</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-noty-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Usage</span></div>
<pre class="!m-0"><code class="language-php">noty()->success('Data saved!');
// With layout options
noty()
->layout('topCenter')
->timeout(3000)
->progressBar()
->info('Processing your request...');</code></pre>
</div>
</div>
</div>
{# Notyf #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-amber-500 to-orange-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-amber-100 rounded-xl">
<svg class="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Notyf</h2>
<p class="text-gray-500">Minimalist, responsive notifications</p>
</div>
</div>
<p class="text-gray-600 mb-4">A minimalist JavaScript library for toast notifications. Tiny footprint with smooth animations.</p>
<div class="flex flex-wrap gap-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Notyf success notification!', adapter: 'notyf'})"
class="btn btn-success">Success</button>
<button onclick="showNotification({type: 'error', message: 'Notyf error notification!', adapter: 'notyf'})"
class="btn btn-danger">Error</button>
</div>
<div class="code-block">
<div class="code-header"><span>Installation</span></div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-notyf-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Usage</span></div>
<pre class="!m-0"><code class="language-php">notyf()->success('Minimal and clean!');
// With options
notyf()
->position('x', 'right')
->position('y', 'top')
->dismissible(true)
->ripple(true)
->success('File uploaded successfully!');</code></pre>
</div>
</div>
</div>
</div>
{% endblock %}
+219 -159
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}PHPFlasher Demo{% endblock %}</title>
<title>{% block title %}PHPFlasher Demo{% endblock %} - Symfony</title>
{# Tailwind CSS v4 CDN #}
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
@@ -12,218 +12,278 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css">
{# Custom styles using Tailwind #}
<script type="text/tailwindcss">
{# Prism.js code block fixes #}
<style>
.code-block pre {
margin: 0 !important;
border-radius: 0 !important;
background: #2d2d2d !important;
padding: 1rem !important;
overflow-x: auto;
}
.code-block code[class*="language-"] {
font-size: 0.875rem;
line-height: 1.6;
background: transparent !important;
}
</style>
{# Custom Tailwind components #}
<style type="text/tailwindcss">
@layer components {
.btn {
@apply px-4 py-2 rounded-md font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-opacity-50;
@apply px-4 py-2 rounded-lg font-medium transition-all duration-200 focus:outline-none focus:ring-2 inline-flex items-center justify-center gap-2 cursor-pointer;
}
.btn-primary {
@apply bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500;
@apply bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500/50;
}
.btn-success {
@apply bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500;
@apply bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500/50;
}
.btn-danger {
@apply bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500;
@apply bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500/50;
}
.btn-warning {
@apply bg-amber-500 text-white hover:bg-amber-600 focus:ring-amber-400;
@apply bg-amber-500 text-white hover:bg-amber-600 focus:ring-amber-400/50;
}
.btn-info {
@apply bg-sky-500 text-white hover:bg-sky-600 focus:ring-sky-400;
@apply bg-sky-500 text-white hover:bg-sky-600 focus:ring-sky-400/50;
}
.btn-outline {
@apply border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-indigo-500;
@apply border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-indigo-500/50;
}
.btn-sm {
@apply px-3 py-1.5 text-sm;
}
.card {
@apply bg-white rounded-xl shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300;
}
.card-header {
@apply px-6 py-4 border-b border-gray-100;
}
.card-body {
@apply p-6;
}
.code-block {
@apply rounded-lg overflow-hidden shadow-lg my-6;
@apply rounded-lg overflow-hidden shadow-lg my-4;
}
.code-header {
@apply bg-gray-800 text-gray-200 px-4 py-2 flex justify-between items-center;
@apply bg-gray-800 text-gray-300 px-4 py-2 flex justify-between items-center text-sm font-mono;
}
.theme-card {
@apply rounded-lg overflow-hidden bg-white shadow-md hover:shadow-lg transition duration-300;
.section-title {
@apply text-2xl font-bold text-gray-800 mb-2;
}
.theme-preview {
@apply h-32 flex items-center justify-center;
.section-subtitle {
@apply text-gray-600 mb-6;
}
.nav-link {
@apply px-3 py-2 rounded-lg text-gray-600 hover:text-indigo-600 hover:bg-indigo-50 transition-colors font-medium;
}
.nav-link-active {
@apply text-indigo-600 bg-indigo-50;
}
}
</script>
</style>
{% block stylesheets %}{% endblock %}
{# Preload theme CSS for playground #}
<link rel="stylesheet" href="/vendor/flasher/flasher.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/flasher/flasher.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/material/material.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/ios/ios.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/slack/slack.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/amazon/amazon.min.css">
<link rel="stylesheet" href="/vendor/flasher/themes/google/google.min.css">
{# Load main flasher script first, then themes #}
<script src="/vendor/flasher/flasher.min.js"></script>
<script src="/vendor/flasher/themes/flasher/flasher.min.js"></script>
<script src="/vendor/flasher/themes/material/material.min.js"></script>
<script src="/vendor/flasher/themes/ios/ios.min.js"></script>
<script src="/vendor/flasher/themes/slack/slack.min.js"></script>
<script src="/vendor/flasher/themes/amazon/amazon.min.js"></script>
<script src="/vendor/flasher/themes/google/google.min.js"></script>
{# Render server-side notifications #}
{{ flasher_render() }}
</head>
<body class="bg-gray-50 antialiased text-gray-900 min-h-screen flex flex-col">
<header class="bg-indigo-600 text-white shadow-md">
{# Header #}
<header class="bg-white border-b border-gray-200 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center py-4">
<div class="flex items-center space-x-3">
<div class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="ml-2 text-2xl font-bold">PHPFlasher</span>
</div>
<span class="hidden md:inline text-indigo-200">Beautiful Notifications for Your PHP Applications</span>
</div>
<div class="hidden md:flex space-x-4">
<a href="https://github.com/php-flasher/flasher" class="text-indigo-100 hover:text-white transition flex items-center" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
GitHub
<div class="flex justify-between items-center h-16">
{# Logo #}
<a href="{{ path('app_home') }}" class="flex items-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-indigo-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-xl font-bold text-gray-900">PHPFlasher</span>
<span class="hidden sm:inline-block px-2 py-0.5 text-xs font-medium bg-emerald-100 text-emerald-600 rounded-full">Symfony</span>
</a>
{# Desktop Navigation #}
<nav class="hidden md:flex items-center space-x-1">
<a href="{{ path('app_home') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_home' %}nav-link-active{% endif %}">Home</a>
<a href="{{ path('app_types') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_types' %}nav-link-active{% endif %}">Types</a>
<a href="{{ path('app_themes') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_themes' %}nav-link-active{% endif %}">Themes</a>
<a href="{{ path('app_adapters') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_adapters' %}nav-link-active{% endif %}">Adapters</a>
<a href="{{ path('app_positions') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_positions' %}nav-link-active{% endif %}">Positions</a>
<a href="{{ path('app_examples') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_examples' %}nav-link-active{% endif %}">Examples</a>
<a href="{{ path('app_playground') }}" class="nav-link {% if app.request.attributes.get('_route') == 'app_playground' %}nav-link-active{% endif %}">Playground</a>
</nav>
{# External Links #}
<div class="hidden md:flex items-center space-x-3">
<a href="https://github.com/php-flasher/php-flasher" target="_blank" class="text-gray-500 hover:text-gray-700">
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
</a>
<a href="https://php-flasher.io" class="text-indigo-100 hover:text-white transition flex items-center" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
</svg>
<a href="https://php-flasher.io" target="_blank" class="btn btn-primary btn-sm">
Documentation
</a>
</div>
<button id="mobile-menu-button" class="md:hidden text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
{# Mobile menu button #}
<button id="mobile-menu-btn" class="md:hidden p-2 rounded-lg text-gray-500 hover:bg-gray-100">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
{# Mobile Navigation #}
<div id="mobile-menu" class="hidden md:hidden border-t border-gray-200 bg-white">
<div class="px-4 py-3 space-y-1">
<a href="{{ path('app_home') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_home' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Home</a>
<a href="{{ path('app_types') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_types' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Types</a>
<a href="{{ path('app_themes') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_themes' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Themes</a>
<a href="{{ path('app_adapters') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_adapters' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Adapters</a>
<a href="{{ path('app_positions') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_positions' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Positions</a>
<a href="{{ path('app_examples') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_examples' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Examples</a>
<a href="{{ path('app_playground') }}" class="block px-3 py-2 rounded-lg {% if app.request.attributes.get('_route') == 'app_playground' %}bg-indigo-50 text-indigo-600{% else %}text-gray-600 hover:bg-gray-50{% endif %}">Playground</a>
</div>
</div>
</header>
<div id="mobile-menu" class="hidden md:hidden bg-indigo-700 text-white">
<div class="px-4 pt-2 pb-4 space-y-3">
<a href="{{ path('app_home') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Home</a>
<a href="{{ path('app_features') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Features</a>
<a href="{{ path('app_themes') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Themes</a>
<a href="{{ path('app_examples') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Examples</a>
<a href="{{ path('app_playground') }}" class="block py-2 hover:bg-indigo-600 rounded px-2">Playground</a>
<a href="https://github.com/php-flasher/flasher" class="block py-2 hover:bg-indigo-600 rounded px-2">GitHub</a>
<a href="https://php-flasher.io" class="block py-2 hover:bg-indigo-600 rounded px-2">Documentation</a>
{# Main Content #}
<main class="flex-1">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{% block content %}{% endblock %}
</div>
</div>
<div class="flex flex-1">
<aside class="hidden md:block bg-white w-64 border-r border-gray-200 shadow-sm">
<div class="p-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-800">Navigation</h2>
<p class="text-sm text-gray-500 mt-1">Explore PHPFlasher</p>
</div>
<nav class="py-4">
<div class="px-6 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Getting Started
</div>
<a href="{{ path('app_home') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_home' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Home
</a>
<a href="{{ path('app_installation') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_installation' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Installation
</a>
<a href="{{ path('app_basic_usage') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_basic_usage' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Basic Usage
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Features
</div>
<a href="{{ path('app_types') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_types' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Notification Types
</a>
<a href="{{ path('app_positions') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_positions' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Positions
</a>
<a href="{{ path('app_themes') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_themes' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Themes
</a>
<a href="{{ path('app_options') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_options' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Options & Configuration
</a>
<a href="{{ path('app_plugins') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_plugins' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Plugins
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Examples
</div>
<a href="{{ path('app_form_example') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_form_example' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Form Validation
</a>
<a href="{{ path('app_ajax_example') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_ajax_example' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
AJAX Requests
</a>
<a href="{{ path('app_presets') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_presets' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Presets
</a>
<a href="{{ path('app_real_world') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_real_world' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Real-World Examples
</a>
<div class="px-6 py-2 mt-4 text-xs font-semibold text-gray-500 uppercase tracking-wider">
Interactive
</div>
<a href="{{ path('app_playground') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_playground' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Interactive Playground
</a>
<a href="{{ path('app_theme_builder') }}" class="block px-6 py-2 text-gray-700 hover:bg-indigo-50 hover:text-indigo-700 {% if app.request.get('_route') == 'app_theme_builder' %}bg-indigo-50 text-indigo-700 border-r-4 border-indigo-500{% endif %}">
Theme Builder
</a>
</nav>
</aside>
<main class="flex-1 overflow-x-hidden">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-6">
<h1 class="text-3xl font-bold text-gray-900">{% block page_title %}Welcome to PHPFlasher{% endblock %}</h1>
<p class="mt-2 text-lg text-gray-600">{% block page_subtitle %}A powerful notification system for PHP applications{% endblock %}</p>
</div>
{% block content %}{% endblock %}
</div>
</main>
</div>
</main>
{# Footer #}
<footer class="bg-white border-t border-gray-200 mt-auto">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
<div class="text-center md:text-left">
<p class="text-gray-500">
&copy; {{ "now"|date("Y") }} PHPFlasher. Made with ❤️ by <a href="https://github.com/yoeunes" class="text-indigo-600 hover:text-indigo-500">Younes ENNAJI</a>
</p>
<div class="flex items-center space-x-2 text-gray-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span>PHPFlasher Demo</span>
</div>
<div class="flex space-x-6">
<a href="https://github.com/php-flasher/flasher" class="text-gray-500 hover:text-gray-700">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" 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>
</a>
<a href="https://php-flasher.io" class="text-gray-500 hover:text-gray-700">
<span class="sr-only">Website</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</a>
<div class="flex items-center space-x-6 text-sm text-gray-500">
<a href="https://php-flasher.io" target="_blank" class="hover:text-indigo-600">Documentation</a>
<a href="https://github.com/php-flasher/php-flasher" target="_blank" class="hover:text-indigo-600">GitHub</a>
<span>Made with ❤️ by <a href="https://github.com/yoeunes" target="_blank" class="hover:text-indigo-600">Younes</a></span>
</div>
</div>
</div>
</footer>
{# Prism.js for syntax highlighting #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
{# Scripts #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup-templating.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-php.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
{# Mobile menu toggle script #}
<script>
document.addEventListener('DOMContentLoaded', function() {
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenuButton && mobileMenu) {
mobileMenuButton.addEventListener('click', function() {
mobileMenu.classList.toggle('hidden');
});
}
// Mobile menu toggle
document.getElementById('mobile-menu-btn')?.addEventListener('click', function() {
document.getElementById('mobile-menu')?.classList.toggle('hidden');
});
// Helper function for notifications using PHPFlasher's JavaScript API
window.showNotification = function(options) {
const type = options.type || 'success';
const message = options.message || 'Notification message';
const title = options.title || null;
const position = options.position || 'top-right';
const timeout = options.timeout || 5000;
// Use the theme if specified
let plugin = 'flasher';
if (options.theme && options.theme !== 'flasher') {
plugin = 'theme.' + options.theme;
}
// Build notification options
const notificationOptions = {
position: position,
timeout: timeout,
};
// Call PHPFlasher's JavaScript API
flasher.use(plugin).flash(type, message, title, notificationOptions);
};
// Helper function for running examples using client-side notifications
window.runExample = function(scenario) {
const examples = {
registration: [
{ type: 'success', message: 'Welcome! Your account has been created.' },
{ type: 'info', message: 'Please check your email to verify your account.' }
],
login_failed: [
{ type: 'error', message: 'Invalid email or password.' },
{ type: 'info', message: 'Forgot your password? Click here to reset.' }
],
validation: [
{ type: 'error', message: 'The email field is required.' },
{ type: 'error', message: 'Password must be at least 8 characters.' },
{ type: 'error', message: 'Please accept the terms and conditions.' }
],
shopping_cart: [
{ type: 'success', message: 'iPhone 15 Pro added to cart!' },
{ type: 'warning', message: 'Only 2 items left in stock!' },
{ type: 'info', message: 'Add $20 more for free shipping!' }
],
file_upload: [
{ type: 'success', message: 'document.pdf uploaded successfully!' },
{ type: 'info', message: 'File size: 2.4 MB' }
],
settings: [
{ type: 'success', message: 'Settings saved successfully!' },
{ type: 'info', message: 'Some changes may require a page refresh.' }
],
payment_success: [
{ type: 'success', message: 'Payment of $149.99 confirmed!' },
{ type: 'info', message: 'Order #12345 - Receipt sent to your email.' }
],
payment_failed: [
{ type: 'error', message: 'Payment declined by your bank.' },
{ type: 'warning', message: 'Please try a different payment method.' },
{ type: 'info', message: 'Your cart has been saved.' }
],
delete_confirm: [
{ type: 'warning', message: 'Are you sure? This action cannot be undone.' },
{ type: 'info', message: 'Click confirm to delete or cancel to keep the item.' }
],
session_expiring: [
{ type: 'warning', message: 'Your session will expire in 5 minutes.' },
{ type: 'info', message: 'Click anywhere to stay logged in.' }
]
};
const notifications = examples[scenario] || [{ type: 'info', message: 'Example not found' }];
notifications.forEach((notification, index) => {
setTimeout(() => {
flasher.flash(notification.type, notification.message);
}, index * 300);
});
};
</script>
{% block javascripts %}{% endblock %}
+215
View File
@@ -0,0 +1,215 @@
{% extends 'base.html.twig' %}
{% block title %}Real-World Examples{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Real-World Examples</h1>
<p class="section-subtitle">See how PHPFlasher handles common application scenarios. Click to run each example.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
{# User Registration #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-green-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">User Registration</h3>
<p class="text-sm text-gray-500">Account creation flow</p>
</div>
</div>
<p class="text-gray-600 mb-4">Simulates a successful user registration with welcome message and email verification notice.</p>
<button onclick="runExample('registration')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Welcome! Your account has been created.');
flash()->info('Please check your email to verify your account.');</code></pre>
</div>
</div>
</div>
{# Login Flow #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-rose-500 to-red-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-rose-100 rounded-xl">
<svg class="w-6 h-6 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Login Failed</h3>
<p class="text-sm text-gray-500">Authentication error</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows how to display login failure messages with helpful guidance.</p>
<button onclick="runExample('login_failed')" class="btn btn-danger w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->error('Invalid email or password.');
flash()->info('Forgot your password? Click here to reset.');</code></pre>
</div>
</div>
</div>
{# Shopping Cart #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-purple-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-indigo-100 rounded-xl">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Shopping Cart</h3>
<p class="text-sm text-gray-500">E-commerce interactions</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows notifications for adding items to cart with stock warnings and promotions.</p>
<button onclick="runExample('shopping_cart')" class="btn btn-primary w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('iPhone 15 Pro added to cart!');
flash()->warning('Only 2 items left in stock!');
flash()->info('Add $20 more for free shipping!');</code></pre>
</div>
</div>
</div>
{# Payment Success #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-green-500 to-emerald-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-green-100 rounded-xl">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Payment Success</h3>
<p class="text-sm text-gray-500">Transaction completed</p>
</div>
</div>
<p class="text-gray-600 mb-4">Payment confirmation with order details and receipt notification.</p>
<button onclick="runExample('payment_success')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Payment of $149.99 confirmed!');
flash()->info('Order #12345 - Receipt sent to your email.');</code></pre>
</div>
</div>
</div>
{# Delete Confirmation #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-pink-500 to-rose-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-pink-100 rounded-xl">
<svg class="w-6 h-6 text-pink-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Delete Confirmation</h3>
<p class="text-sm text-gray-500">SweetAlert dialog</p>
</div>
</div>
<p class="text-gray-600 mb-4">Uses SweetAlert for a confirmation dialog before deleting.</p>
<button onclick="runExample('delete_confirm')" class="btn btn-danger w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">sweetalert()
->showCancelButton()
->confirmButtonText('Yes, delete it!')
->cancelButtonText('Cancel')
->warning('Are you sure? This cannot be undone.');</code></pre>
</div>
</div>
</div>
{# Settings Saved #}
<div class="card">
<div class="h-2 bg-gradient-to-r from-teal-500 to-green-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-4">
<div class="p-3 bg-teal-100 rounded-xl">
<svg class="w-6 h-6 text-teal-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
</div>
<div>
<h3 class="text-lg font-bold text-gray-800">Settings Saved</h3>
<p class="text-sm text-gray-500">Preferences updated</p>
</div>
</div>
<p class="text-gray-600 mb-4">Shows settings save confirmation with additional context.</p>
<button onclick="runExample('settings')" class="btn btn-success w-full mb-4">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
</svg>
Run Example
</button>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()->success('Settings saved successfully!');
flash()->info('Some changes may require a page refresh.');</code></pre>
</div>
</div>
</div>
</div>
{% endblock %}
@@ -1,232 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Notification Positions{% endblock %}
{% block page_title %}Notification Positions{% endblock %}
{% block page_subtitle %}Control where your notifications appear on the screen{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Positions</h2>
<p class="text-gray-600 mb-6">
PHPFlasher allows you to position your notifications in different areas of the screen.
You can choose from 9 different positions to best fit your design and user experience needs.
</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 left-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-left</div>
<div class="absolute top-2 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 left-1/2 transform -translate-x-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-center</div>
<div class="absolute top-2 left-1/2 transform -translate-x-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-2 right-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">top-right</div>
<div class="absolute top-2 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="top-right">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 left-2 transform -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center-left</div>
<div class="absolute top-1/2 left-2 transform -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center</div>
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute top-1/2 right-2 transform -translate-y-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">center-right</div>
<div class="absolute top-1/2 right-2 transform -translate-y-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="center-right">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 left-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-left</div>
<div class="absolute bottom-10 left-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-left">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 left-1/2 transform -translate-x-1/2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-center</div>
<div class="absolute bottom-10 left-1/2 transform -translate-x-1/2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-center">
Show Notification
</button>
</div>
<div class="relative h-72 bg-gray-100 rounded-lg overflow-hidden border border-gray-200">
<div class="absolute bottom-2 right-2 bg-indigo-100 text-indigo-800 px-3 py-1 rounded-lg text-sm">bottom-right</div>
<div class="absolute bottom-10 right-2 bg-white shadow-md p-3 rounded w-3/4">
<div class="h-2 w-12 bg-indigo-500 mb-2 rounded"></div>
<div class="h-2 w-20 bg-gray-300 mb-2 rounded"></div>
<div class="h-2 w-16 bg-gray-300 rounded"></div>
</div>
<button class="absolute bottom-4 left-1/2 transform -translate-x-1/2 btn btn-primary notification-position" data-position="bottom-right">
Show Notification
</button>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Setting Positions in Code</h2>
<p class="text-gray-600 mb-4">
You can set the position of notifications in your PHP code using the <code>option()</code> method:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Top-right position (default)
flash()->success('Your changes have been saved.');
// Bottom-left position
flash()->option('position', 'bottom-left')
->success('Your changes have been saved.');
// Top-center position
flash()->option('position', 'top-center')
->info('You have 3 unread messages.');
// Center position (middle of the screen)
flash()->option('position', 'center')
->warning('Your session will expire in 1 minute.');</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Global Position Configuration</h2>
<p class="text-gray-600 mb-4">
You can set a default position for all notifications in your configuration file:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
options:
position: 'top-right' # Default position for all notifications</code></pre>
</div>
<p class="text-gray-600 mb-4">
Or you can set specific positions for different themes:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
themes:
amazon:
options:
position: 'bottom-right'
sapphire:
options:
position: 'top-center'</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_themes') }}" class="btn btn-primary">
Explore Themes
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const positionButtons = document.querySelectorAll('.notification-position');
positionButtons.forEach(button => {
button.addEventListener('click', function() {
const position = this.getAttribute('data-position');
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: 'success',
message: 'This notification is at the ' + position + ' position!',
options: {
position: position
}
})
});
});
});
});
</script>
{% endblock %}
@@ -1,220 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Notification Types{% endblock %}
{% block page_title %}Notification Types{% endblock %}
{% block page_subtitle %}Display different notification types to communicate various messages{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Notification Types</h2>
<p class="text-gray-600 mb-6">
PHPFlasher provides four primary notification types to communicate different types of messages to your users.
Each type has a distinct visual style to clearly convey the nature of the message.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-emerald-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 class="text-lg font-semibold">Success</h3>
</div>
<p class="text-gray-600 mb-3">
Used for positive feedback when an action has been completed successfully.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->success('Your profile has been updated successfully!');</code></pre>
</div>
<button class="btn btn-success w-full notification-demo" data-type="success" data-message="This is a success notification!">
Show Success Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-rose-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-rose-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h3 class="text-lg font-semibold">Error</h3>
</div>
<p class="text-gray-600 mb-3">
Used to alert users about errors or problems that need attention.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->error('An error occurred while processing your request.');</code></pre>
</div>
<button class="btn btn-danger w-full notification-demo" data-type="error" data-message="This is an error notification!">
Show Error Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-amber-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<h3 class="text-lg font-semibold">Warning</h3>
</div>
<p class="text-gray-600 mb-3">
Used to inform users about potential issues or actions they should be aware of.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->warning('Your subscription will expire in 3 days.');</code></pre>
</div>
<button class="btn btn-warning w-full notification-demo" data-type="warning" data-message="This is a warning notification!">
Show Warning Notification
</button>
</div>
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
<div class="flex items-center mb-3">
<div class="bg-sky-100 p-2 rounded-md mr-3">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-sky-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h3 class="text-lg font-semibold">Info</h3>
</div>
<p class="text-gray-600 mb-3">
Used to provide neutral information or updates to users.
</p>
<div class="code-block mb-3">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->info('You have 5 unread messages.');</code></pre>
</div>
<button class="btn btn-info w-full notification-demo" data-type="info" data-message="This is an info notification!">
Show Info Notification
</button>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Usage in Controllers</h2>
<p class="text-gray-600 mb-4">
Here's how you can use notification types in your Symfony controllers:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php"><?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ExampleController extends AbstractController
{
#[Route('/process-form', name: 'app_process_form')]
public function processForm(): Response
{
// Simulate form processing
$success = true;
if ($success) {
// Show success notification
flash()->success('Form submitted successfully!');
} else {
// Show error notification
flash()->error('An error occurred while processing the form.');
}
// Show info notification
flash()->info('You will receive a confirmation email shortly.');
// Show warning notification
flash()->warning('Please verify your email within 24 hours.');
return $this->redirectToRoute('app_home');
}
}</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Advanced Usage</h2>
<p class="text-gray-600 mb-4">
You can combine notification types with other options to create more customized notifications:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Success notification with custom timeout
flash()->option('timeout', 8000)
->success('Changes saved successfully!');
// Warning notification with custom position
flash()->option('position', 'bottom-center')
->warning('Your session will expire soon.');
// Error notification with progress bar
flash()->option('showProgressbar', true)
->error('Unable to connect to the server.');</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_options') }}" class="btn btn-primary">
Learn More About Options
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const demoButtons = document.querySelectorAll('.notification-demo');
demoButtons.forEach(button => {
button.addEventListener('click', function() {
const type = this.getAttribute('data-type');
const message = this.getAttribute('data-message') || 'This is a notification!';
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: type,
message: message
})
});
});
});
});
</script>
{% endblock %}
+197
View File
@@ -0,0 +1,197 @@
{% extends 'base.html.twig' %}
{% block title %}Home{% endblock %}
{% block content %}
{# Hero Section #}
<div class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
Beautiful Flash Notifications
</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto mb-8">
PHPFlasher makes it easy to add elegant notifications to your Symfony application.
Try the quick demos below!
</p>
{# Quick Demo Buttons #}
<div class="flex flex-wrap justify-center gap-3 mb-8">
<button onclick="showNotification({type: 'success', message: 'Operation completed successfully!', title: 'Success'})"
class="btn btn-success">
<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="M5 13l4 4L19 7"></path>
</svg>
Success
</button>
<button onclick="showNotification({type: 'error', message: 'Something went wrong. Please try again.', title: 'Error'})"
class="btn btn-danger">
<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="M6 18L18 6M6 6l12 12"></path>
</svg>
Error
</button>
<button onclick="showNotification({type: 'warning', message: 'Please review your input before continuing.', title: 'Warning'})"
class="btn btn-warning">
<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 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
Warning
</button>
<button onclick="showNotification({type: 'info', message: 'Here is some useful information for you.', title: 'Info'})"
class="btn btn-info">
<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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Info
</button>
</div>
</div>
{# Features Grid #}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
{# Feature 1: Types #}
<a href="{{ path('app_types') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-emerald-100 rounded-lg group-hover:bg-emerald-200 transition-colors">
<svg class="w-6 h-6 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Types</h3>
</div>
<p class="text-gray-600">Success, error, warning, and info notifications for every use case.</p>
</div>
</a>
{# Feature 2: Themes #}
<a href="{{ path('app_themes') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-purple-500 to-pink-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-purple-100 rounded-lg group-hover:bg-purple-200 transition-colors">
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">17+ Themes</h3>
</div>
<p class="text-gray-600">Material, iOS, Slack, Amazon, and many more beautiful themes.</p>
</div>
</a>
{# Feature 3: Adapters #}
<a href="{{ path('app_adapters') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-blue-500 to-cyan-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-blue-100 rounded-lg group-hover:bg-blue-200 transition-colors">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Multiple Adapters</h3>
</div>
<p class="text-gray-600">Toastr, SweetAlert, Noty, and Notyf adapters included.</p>
</div>
</a>
{# Feature 4: Positions #}
<a href="{{ path('app_positions') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-amber-500 to-orange-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-amber-100 rounded-lg group-hover:bg-amber-200 transition-colors">
<svg class="w-6 h-6 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Flexible Positions</h3>
</div>
<p class="text-gray-600">Place notifications anywhere on the screen.</p>
</div>
</a>
{# Feature 5: Examples #}
<a href="{{ path('app_examples') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-rose-500 to-red-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-rose-100 rounded-lg group-hover:bg-rose-200 transition-colors">
<svg class="w-6 h-6 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Real Examples</h3>
</div>
<p class="text-gray-600">User registration, shopping cart, payments, and more.</p>
</div>
</a>
{# Feature 6: Playground #}
<a href="{{ path('app_playground') }}" class="card group">
<div class="h-2 bg-gradient-to-r from-indigo-500 to-violet-500"></div>
<div class="card-body">
<div class="flex items-center space-x-3 mb-3">
<div class="p-2 bg-indigo-100 rounded-lg group-hover:bg-indigo-200 transition-colors">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Interactive Playground</h3>
</div>
<p class="text-gray-600">Build and customize notifications in real-time.</p>
</div>
</a>
</div>
{# Quick Start Code #}
<div class="card mb-12">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Quick Start</h2>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Get started with PHPFlasher in seconds. Just install and use!</p>
<div class="code-block">
<div class="code-header">
<span>Installation</span>
</div>
<pre class="!m-0"><code class="language-bash">composer require php-flasher/flasher-symfony</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Usage</span>
</div>
<pre class="!m-0"><code class="language-php">// In your controller
flash()->success('Profile updated successfully!');
// With options
flash()->success('Welcome back!', [
'position' => 'top-right',
'timeout' => 5000,
]);
// Using themes
flash()->use('theme.material')->info('New feature available!');</code></pre>
</div>
</div>
</div>
{# CTA Section #}
<div class="bg-gradient-to-r from-indigo-600 to-purple-600 rounded-2xl p-8 text-center text-white">
<h2 class="text-2xl md:text-3xl font-bold mb-4">Ready to try PHPFlasher?</h2>
<p class="text-indigo-100 mb-6 max-w-xl mx-auto">
Explore the interactive playground to customize notifications and see the generated code.
</p>
<a href="{{ path('app_playground') }}" class="inline-flex items-center px-6 py-3 bg-white text-indigo-600 font-semibold rounded-lg hover:bg-indigo-50 transition-colors">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Open Playground
</a>
</div>
{% endblock %}
@@ -1,97 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher Features{% endblock %}
{% block page_title %}PHPFlasher Features{% endblock %}
{% block page_subtitle %}Explore the powerful features of PHPFlasher{% endblock %}
{% block content %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-indigo-600 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-indigo-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Types</h3>
</div>
<p class="text-gray-600">Display success, error, warning, and info notifications to communicate different messages to your users.</p>
<div class="mt-4">
<a href="{{ path('app_types') }}" class="text-indigo-600 hover:text-indigo-800 font-medium">See Notification Types →</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-emerald-600 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-emerald-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Notification Positions</h3>
</div>
<p class="text-gray-600">Place notifications in different positions around the screen to best suit your UI design.</p>
<div class="mt-4">
<a href="{{ path('app_positions') }}" class="text-emerald-600 hover:text-emerald-800 font-medium">Explore Positions →</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition duration-300">
<div class="bg-amber-500 h-2"></div>
<div class="p-6">
<div class="flex items-center space-x-3 mb-3">
<div class="bg-amber-100 p-2 rounded-md">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Configuration Options</h3>
</div>
<p class="text-gray-600">Customize timeouts, animations, progressbars, and more with PHPFlasher's flexible configuration.</p>
<div class="mt-4">
<a href="{{ path('app_options') }}" class="text-amber-600 hover:text-amber-800 font-medium">View Options →</a>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-bold mb-4 text-gray-800">Plugins & Extensions</h3>
<p class="text-gray-600 mb-4">PHPFlasher can be extended with plugins to add more functionality.</p>
<div class="flex justify-end">
<a href="{{ path('app_plugins') }}" class="btn btn-primary">Explore Plugins</a>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-bold mb-4 text-gray-800">Notification Presets</h3>
<p class="text-gray-600 mb-4">Create reusable notification configurations for consistent messaging.</p>
<div class="flex justify-end">
<a href="{{ path('app_presets') }}" class="btn btn-primary">View Presets</a>
</div>
</div>
</div>
</div>
<div class="bg-indigo-50 rounded-lg p-8 mb-12">
<div class="text-center">
<h2 class="text-2xl font-bold text-indigo-800 mb-4">Try Out the Interactive Playground</h2>
<p class="text-indigo-600 mb-6 max-w-2xl mx-auto">Want to experiment with different notification settings? Use our interactive playground to test PHPFlasher's features in real-time.</p>
<div class="flex justify-center">
<a href="{{ path('app_playground') }}" class="btn btn-primary">Go to Playground</a>
</div>
</div>
</div>
{% endblock %}
+266
View File
@@ -0,0 +1,266 @@
{% extends 'base.html.twig' %}
{% block title %}Playground{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Interactive Playground</h1>
<p class="section-subtitle">Build and customize notifications in real-time. See the generated PHP code instantly.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{# Configuration Panel #}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Configuration</h2>
</div>
<div class="card-body space-y-6">
{# Type #}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Notification Type</label>
<div class="grid grid-cols-4 gap-2">
<button onclick="setType('success')" id="type-success" class="type-btn px-3 py-2 rounded-lg border-2 border-emerald-500 bg-emerald-50 text-emerald-700 font-medium text-sm">
Success
</button>
<button onclick="setType('error')" id="type-error" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-rose-500 hover:bg-rose-50 hover:text-rose-700">
Error
</button>
<button onclick="setType('warning')" id="type-warning" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-amber-500 hover:bg-amber-50 hover:text-amber-700">
Warning
</button>
<button onclick="setType('info')" id="type-info" class="type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm hover:border-sky-500 hover:bg-sky-50 hover:text-sky-700">
Info
</button>
</div>
</div>
{# Title #}
<div>
<label for="title" class="block text-sm font-medium text-gray-700 mb-2">Title (optional)</label>
<input type="text" id="title" placeholder="Enter a title..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
oninput="updateCode()">
</div>
{# Message #}
<div>
<label for="message" class="block text-sm font-medium text-gray-700 mb-2">Message</label>
<textarea id="message" rows="2" placeholder="Enter your notification message..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
oninput="updateCode()">Operation completed successfully!</textarea>
</div>
{# Position #}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Position</label>
<select id="position" onchange="updateCode()"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="top-right" selected>Top Right (default)</option>
<option value="top-left">Top Left</option>
<option value="top-center">Top Center</option>
<option value="bottom-right">Bottom Right</option>
<option value="bottom-left">Bottom Left</option>
<option value="bottom-center">Bottom Center</option>
<option value="center">Center</option>
</select>
</div>
{# Theme #}
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Theme</label>
<select id="theme" onchange="updateCode()"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="">Default (Flasher)</option>
<option value="flasher">Flasher</option>
<option value="material">Material</option>
<option value="ios">iOS</option>
<option value="slack">Slack</option>
<option value="amazon">Amazon</option>
<option value="google">Google</option>
</select>
</div>
{# Timeout #}
<div>
<label for="timeout" class="block text-sm font-medium text-gray-700 mb-2">
Timeout: <span id="timeout-value">5000</span>ms
</label>
<input type="range" id="timeout" min="1000" max="10000" step="500" value="5000"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
oninput="document.getElementById('timeout-value').textContent = this.value; updateCode()">
</div>
{# Show Button #}
<button onclick="showPlaygroundNotification()" class="btn btn-primary w-full">
<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="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Show Notification
</button>
</div>
</div>
{# Code Preview #}
<div class="space-y-6">
<div class="card">
<div class="card-header flex justify-between items-center">
<h2 class="text-xl font-bold text-gray-800">Generated Code</h2>
<button onclick="copyCode()" class="btn btn-sm btn-outline">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
</svg>
Copy
</button>
</div>
<div class="card-body p-0">
<div class="code-block !my-0 !rounded-none">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php" id="generated-code">flash()->success('Operation completed successfully!');</code></pre>
</div>
</div>
</div>
{# Preview Card #}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Preview</h2>
</div>
<div class="card-body">
<div class="bg-gray-100 rounded-lg p-6 min-h-[200px] flex items-center justify-center">
<div id="preview-notification" class="max-w-sm w-full">
<div class="bg-emerald-500 text-white p-4 rounded-lg shadow-lg">
<div class="flex items-start space-x-3">
<svg class="w-6 h-6 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div>
<p id="preview-title" class="font-semibold hidden"></p>
<p id="preview-message" class="text-sm opacity-90">Operation completed successfully!</p>
</div>
</div>
</div>
</div>
</div>
<p class="text-sm text-gray-500 mt-3 text-center">This is a static preview. Click "Show Notification" to see the real notification.</p>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
let currentType = 'success';
const typeColors = {
success: { border: 'border-emerald-500', bg: 'bg-emerald-50', text: 'text-emerald-700', preview: 'bg-emerald-500' },
error: { border: 'border-rose-500', bg: 'bg-rose-50', text: 'text-rose-700', preview: 'bg-rose-500' },
warning: { border: 'border-amber-500', bg: 'bg-amber-50', text: 'text-amber-700', preview: 'bg-amber-500' },
info: { border: 'border-sky-500', bg: 'bg-sky-50', text: 'text-sky-700', preview: 'bg-sky-500' }
};
function setType(type) {
currentType = type;
document.querySelectorAll('.type-btn').forEach(btn => {
btn.className = 'type-btn px-3 py-2 rounded-lg border-2 border-gray-200 bg-white text-gray-600 font-medium text-sm';
});
const activeBtn = document.getElementById(`type-${type}`);
const colors = typeColors[type];
activeBtn.className = `type-btn px-3 py-2 rounded-lg border-2 ${colors.border} ${colors.bg} ${colors.text} font-medium text-sm`;
updatePreview();
updateCode();
}
function updatePreview() {
const previewDiv = document.querySelector('#preview-notification > div');
const colors = typeColors[currentType];
previewDiv.className = `${colors.preview} text-white p-4 rounded-lg shadow-lg`;
const title = document.getElementById('title').value;
const message = document.getElementById('message').value;
document.getElementById('preview-title').textContent = title;
document.getElementById('preview-title').classList.toggle('hidden', !title);
document.getElementById('preview-message').textContent = message || 'Your notification message...';
}
function updateCode() {
const type = currentType;
const title = document.getElementById('title').value;
const message = document.getElementById('message').value || 'Your message here';
const position = document.getElementById('position').value;
const theme = document.getElementById('theme').value;
const timeout = document.getElementById('timeout').value;
let code = '';
const hasOptions = position !== 'top-right' || timeout !== '5000';
if (theme) {
code = `flash()->use('theme.${theme}')`;
} else {
code = 'flash()';
}
if (hasOptions) {
const options = [];
if (position !== 'top-right') {
options.push(`'position' => '${position}'`);
}
if (timeout !== '5000') {
options.push(`'timeout' => ${timeout}`);
}
if (title) {
code += `\n ->${type}('${message}', '${title}', [${options.join(', ')}]);`;
} else {
code += `\n ->${type}('${message}', [${options.join(', ')}]);`;
}
} else {
if (title) {
code += `->${type}('${message}', '${title}');`;
} else {
code += `->${type}('${message}');`;
}
}
document.getElementById('generated-code').textContent = code;
Prism.highlightElement(document.getElementById('generated-code'));
updatePreview();
}
function showPlaygroundNotification() {
const options = {
type: currentType,
message: document.getElementById('message').value || 'Your message here',
title: document.getElementById('title').value || undefined,
position: document.getElementById('position').value,
theme: document.getElementById('theme').value || undefined,
timeout: parseInt(document.getElementById('timeout').value)
};
showNotification(options);
}
function copyCode() {
const code = document.getElementById('generated-code').textContent;
navigator.clipboard.writeText(code).then(() => {
const btn = event.target.closest('button');
const originalText = btn.innerHTML;
btn.innerHTML = '<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg> Copied!';
setTimeout(() => {
btn.innerHTML = originalText;
}, 2000);
});
}
document.addEventListener('DOMContentLoaded', function() {
updateCode();
});
</script>
{% endblock %}
+147
View File
@@ -0,0 +1,147 @@
{% extends 'base.html.twig' %}
{% block title %}Positions{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Notification Positions</h1>
<p class="section-subtitle">Place your notifications anywhere on the screen. Click any position to see it in action.</p>
</div>
{# Interactive Position Grid #}
<div class="card mb-8">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Click a Position</h2>
</div>
<div class="card-body">
<div class="bg-gray-100 rounded-xl p-4 relative" style="min-height: 400px;">
{# Visual Browser Frame #}
<div class="bg-white rounded-lg shadow-lg overflow-hidden h-full" style="min-height: 380px;">
{# Browser Header #}
<div class="bg-gray-200 px-4 py-2 flex items-center space-x-2">
<div class="w-3 h-3 bg-red-400 rounded-full"></div>
<div class="w-3 h-3 bg-yellow-400 rounded-full"></div>
<div class="w-3 h-3 bg-green-400 rounded-full"></div>
<div class="flex-1 ml-4">
<div class="bg-white rounded px-3 py-1 text-sm text-gray-500 max-w-md">your-app.com</div>
</div>
</div>
{# Position Grid #}
<div class="grid grid-cols-3 grid-rows-3 gap-2 p-4" style="min-height: 340px;">
{# Top Left #}
<button onclick="showNotification({type: 'info', message: 'Top Left notification', position: 'top-left'})"
class="flex items-start justify-start p-4 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition-colors group">
<div class="bg-indigo-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-left
</div>
</button>
{# Top Center #}
<button onclick="showNotification({type: 'info', message: 'Top Center notification', position: 'top-center'})"
class="flex items-start justify-center p-4 bg-purple-50 hover:bg-purple-100 rounded-lg transition-colors group">
<div class="bg-purple-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-center
</div>
</button>
{# Top Right #}
<button onclick="showNotification({type: 'info', message: 'Top Right notification', position: 'top-right'})"
class="flex items-start justify-end p-4 bg-pink-50 hover:bg-pink-100 rounded-lg transition-colors group">
<div class="bg-pink-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
top-right
</div>
</button>
{# Center Left #}
<button onclick="showNotification({type: 'info', message: 'Center Left notification', position: 'center-left'})"
class="flex items-center justify-start p-4 bg-sky-50 hover:bg-sky-100 rounded-lg transition-colors group">
<div class="bg-sky-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center-left
</div>
</button>
{# Center #}
<button onclick="showNotification({type: 'info', message: 'Center notification', position: 'center'})"
class="flex items-center justify-center p-4 bg-amber-50 hover:bg-amber-100 rounded-lg transition-colors group">
<div class="bg-amber-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center
</div>
</button>
{# Center Right #}
<button onclick="showNotification({type: 'info', message: 'Center Right notification', position: 'center-right'})"
class="flex items-center justify-end p-4 bg-emerald-50 hover:bg-emerald-100 rounded-lg transition-colors group">
<div class="bg-emerald-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
center-right
</div>
</button>
{# Bottom Left #}
<button onclick="showNotification({type: 'info', message: 'Bottom Left notification', position: 'bottom-left'})"
class="flex items-end justify-start p-4 bg-rose-50 hover:bg-rose-100 rounded-lg transition-colors group">
<div class="bg-rose-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-left
</div>
</button>
{# Bottom Center #}
<button onclick="showNotification({type: 'info', message: 'Bottom Center notification', position: 'bottom-center'})"
class="flex items-end justify-center p-4 bg-teal-50 hover:bg-teal-100 rounded-lg transition-colors group">
<div class="bg-teal-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-center
</div>
</button>
{# Bottom Right #}
<button onclick="showNotification({type: 'info', message: 'Bottom Right notification', position: 'bottom-right'})"
class="flex items-end justify-end p-4 bg-orange-50 hover:bg-orange-100 rounded-lg transition-colors group">
<div class="bg-orange-500 text-white px-3 py-2 rounded-lg text-sm font-medium group-hover:scale-105 transition-transform">
bottom-right
</div>
</button>
</div>
</div>
</div>
</div>
</div>
{# Code Examples #}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="card">
<div class="card-header">
<h3 class="text-lg font-bold text-gray-800">Inline Position</h3>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Set the position directly when creating a notification.</p>
<div class="code-block">
<div class="code-header"><span>PHP</span></div>
<pre class="!m-0"><code class="language-php">flash()
->option('position', 'top-left')
->success('Profile updated!');
// Or use the fluent method
flash()
->position('bottom-right')
->info('New message received!');</code></pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3 class="text-lg font-bold text-gray-800">Global Configuration</h3>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Set a default position for all notifications in your config file.</p>
<div class="code-block">
<div class="code-header"><span>config/packages/flasher.yaml</span></div>
<pre class="!m-0"><code class="language-yaml">flasher:
default: flasher
options:
position: bottom-right</code></pre>
</div>
</div>
</div>
</div>
{% endblock %}
+72
View File
@@ -0,0 +1,72 @@
{% extends 'base.html.twig' %}
{% block title %}Themes{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Theme Gallery</h1>
<p class="section-subtitle">Choose from 17+ beautiful themes to match your application's design.</p>
</div>
{# Theme Grid #}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mb-12">
{% set themes = [
{name: 'flasher', label: 'Flasher', desc: 'Default theme', gradient: 'from-indigo-500 to-indigo-600'},
{name: 'material', label: 'Material', desc: 'Google Material', gradient: 'from-blue-500 to-blue-600'},
{name: 'ios', label: 'iOS', desc: 'Apple style', gradient: 'from-slate-600 to-slate-700'},
{name: 'slack', label: 'Slack', desc: 'Slack messaging', gradient: 'from-purple-500 to-purple-600'},
{name: 'amazon', label: 'Amazon', desc: 'Amazon alerts', gradient: 'from-orange-500 to-orange-600'},
{name: 'google', label: 'Google', desc: 'Google notifications', gradient: 'from-red-500 to-yellow-500'},
{name: 'facebook', label: 'Facebook', desc: 'Facebook style', gradient: 'from-blue-600 to-blue-700'},
{name: 'minimal', label: 'Minimal', desc: 'Ultra clean', gradient: 'from-gray-400 to-gray-500'},
{name: 'neon', label: 'Neon', desc: 'Bright & bold', gradient: 'from-pink-500 to-rose-500'},
{name: 'emerald', label: 'Emerald', desc: 'Green palette', gradient: 'from-emerald-500 to-green-600'},
{name: 'sapphire', label: 'Sapphire', desc: 'Blue elegance', gradient: 'from-blue-500 to-indigo-600'},
{name: 'ruby', label: 'Ruby', desc: 'Red accents', gradient: 'from-red-500 to-rose-600'},
{name: 'amber', label: 'Amber', desc: 'Warm tones', gradient: 'from-amber-400 to-orange-500'},
{name: 'jade', label: 'Jade', desc: 'Soft green', gradient: 'from-teal-500 to-green-600'},
{name: 'onyx', label: 'Onyx', desc: 'Dark mode', gradient: 'from-slate-700 to-slate-900'},
{name: 'crystal', label: 'Crystal', desc: 'Transparent', gradient: 'from-cyan-400 to-blue-500'},
{name: 'aurora', label: 'Aurora', desc: 'Gradient effects', gradient: 'from-green-400 via-blue-500 to-purple-500'}
] %}
{% for theme in themes %}
<div class="card group cursor-pointer" onclick="showNotification({type: 'success', message: 'This is the {{ theme.label }} theme!', title: '{{ theme.label }} Theme', theme: '{{ theme.name }}'})">
<div class="h-20 bg-gradient-to-br {{ theme.gradient }} flex items-center justify-center">
<span class="text-white text-2xl font-bold">{{ theme.label|slice(0, 1) }}</span>
</div>
<div class="p-4">
<h3 class="font-semibold text-gray-800">{{ theme.label }}</h3>
<p class="text-sm text-gray-500">{{ theme.desc }}</p>
</div>
</div>
{% endfor %}
</div>
{# Usage Example #}
<div class="card">
<div class="card-header">
<h2 class="text-xl font-bold text-gray-800">Using Themes</h2>
</div>
<div class="card-body">
<p class="text-gray-600 mb-4">Apply themes to your notifications with the <code class="bg-gray-100 px-2 py-1 rounded">use()</code> method.</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">// Use a specific theme
flash()->use('theme.material')->success('Material design notification!');
// Different themes for different messages
flash()->use('theme.ios')->info('iOS style notification');
flash()->use('theme.slack')->warning('Slack style warning');
flash()->use('theme.amazon')->success('Amazon style success');
// Set default theme in config/packages/flasher.yaml
flasher:
default: theme.material</code></pre>
</div>
</div>
</div>
{% endblock %}
@@ -1,302 +0,0 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher - Themes{% endblock %}
{% block page_title %}PHPFlasher Themes{% endblock %}
{% block page_subtitle %}Beautiful notification themes to match your application's design{% endblock %}
{% block content %}
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Available Themes</h2>
<p class="text-gray-600 mb-6">
PHPFlasher comes with various themes to help you match the notification style to your application's design.
Each theme provides a distinct visual appearance while maintaining the same functionality.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="theme-card">
<div class="bg-indigo-600 h-2"></div>
<div class="theme-preview bg-indigo-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-500" 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>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Flasher Default Theme</p>
<p class="text-xs text-gray-500">Clean and minimal design</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Flasher (Default)</h3>
<p class="text-gray-600 text-sm mb-4">The default theme with a clean, minimal design that works well in most applications.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="flasher">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-emerald-600 h-2"></div>
<div class="theme-preview bg-emerald-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4 border-l-4 border-emerald-500">
<div class="flex">
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Emerald Theme</p>
<p class="text-xs text-gray-500">Elegant left-border style</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Emerald</h3>
<p class="text-gray-600 text-sm mb-4">A sleek theme with colored left borders and subtle design elements.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="emerald">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-blue-600 h-2"></div>
<div class="theme-preview bg-blue-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0 bg-blue-100 rounded-full p-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-blue-500" 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>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Sapphire Theme</p>
<p class="text-xs text-gray-500">Modern with rounded icons</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Sapphire</h3>
<p class="text-gray-600 text-sm mb-4">A modern theme with rounded icons and smooth animations.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="sapphire">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-purple-600 h-2"></div>
<div class="theme-preview bg-purple-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4 bg-opacity-80 backdrop-filter backdrop-blur-sm">
<div class="flex">
<div class="flex-shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-purple-500" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Crystal Theme</p>
<p class="text-xs text-gray-500">Transparent with blur effects</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Crystal</h3>
<p class="text-gray-600 text-sm mb-4">A modern glass-like theme with transparency and blur effects.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="crystal">
Preview Theme
</button>
</div>
</div>
<div class="theme-card">
<div class="bg-amber-500 h-2"></div>
<div class="theme-preview bg-amber-50 flex items-center justify-center p-4">
<div class="w-full bg-white rounded-lg shadow-md p-4">
<div class="flex">
<div class="flex-shrink-0 bg-amber-100 p-1 rounded">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-amber-500" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 3a1 1 0 00-1.447-.894L8.763 6H5a3 3 0 000 6h.28l1.771 5.316A1 1 0 008 18h1a1 1 0 001-1v-4.382l6.553 3.276A1 1 0 0018 15V3z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Amazon Theme</p>
<p class="text-xs text-gray-500">Inspired by Amazon's notification style</p>
</div>
</div>
</div>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold mb-2">Amazon</h3>
<p class="text-gray-600 text-sm mb-4">Inspired by Amazon's notification style with distinctive icons.</p>
<button class="w-full btn btn-primary theme-demo" data-theme="amazon">
Preview Theme
</button>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Using Themes in Your Code</h2>
<p class="text-gray-600 mb-4">
You can specify which theme to use for each notification using the <code>option()</code> method:
</p>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">// Using the default theme
flash()->success('Your profile has been updated successfully!');
// Using the Emerald theme
flash()->option('theme', 'emerald')
->success('Your profile has been updated successfully!');
// Using the Sapphire theme with additional options
flash()->option('theme', 'sapphire')
->option('position', 'bottom-right')
->option('timeout', 5000)
->info('You have 3 unread messages.');
// Using the Amazon theme
flash()->option('theme', 'amazon')
->warning('Your subscription will expire in 3 days.');</code></pre>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Theme Configuration</h2>
<p class="text-gray-600 mb-4">
You can configure themes globally in your configuration file:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
# Set the default theme for all notifications
default: theme.amazon
themes:
amazon:
scripts:
- '/vendor/flasher/themes/amazon/amazon.min.js'
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/amazon/amazon.min.css'
options:
position: 'bottom-right'
timeout: 6000
emerald:
scripts:
- '/vendor/flasher/themes/emerald/emerald.min.js'
styles:
- '/vendor/flasher/flasher.min.css'
- '/vendor/flasher/themes/emerald/emerald.min.css'
options:
position: 'top-right'
timeout: 5000</code></pre>
</div>
<p class="text-gray-600 mb-4">
Each theme can have its own default options, scripts, and styles. This allows you to create a consistent notification experience throughout your application.
</p>
</div>
</div>
<div class="bg-white rounded-lg shadow-md overflow-hidden">
<div class="p-6">
<h2 class="text-xl font-bold mb-4">Custom Theme Templates</h2>
<p class="text-gray-600 mb-4">
Need something more customized? PHPFlasher allows you to create your own themes:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>YAML</span>
</div>
<pre><code class="language-yaml"># config/packages/flasher.yaml
flasher:
themes:
custom_theme:
scripts:
- '/assets/js/custom-theme.js'
styles:
- '/assets/css/custom-theme.css'
options:
position: 'top-center'
timeout: 7000
# Add any custom options your theme supports</code></pre>
</div>
<p class="text-gray-600 mb-4">
Then you can use your custom theme in your code:
</p>
<div class="code-block mb-6">
<div class="code-header">
<span>PHP</span>
</div>
<pre><code class="language-php">flash()->option('theme', 'custom_theme')
->success('This uses your custom theme!');</code></pre>
</div>
<div class="flex justify-center">
<a href="{{ path('app_theme_builder') }}" class="btn btn-primary">
Try the Theme Builder
</a>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const themeButtons = document.querySelectorAll('.theme-demo');
themeButtons.forEach(button => {
button.addEventListener('click', function() {
const theme = this.getAttribute('data-theme');
const messages = {
'flasher': 'This notification uses the Flasher theme!',
'emerald': 'This notification uses the Emerald theme!',
'sapphire': 'This notification uses the Sapphire theme!',
'crystal': 'This notification uses the Crystal theme!',
'amazon': 'This notification uses the Amazon theme!'
};
// Send AJAX request to show notification
fetch('{{ path('app_show_notification') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
type: 'success',
message: messages[theme] || 'Theme preview',
options: {
theme: theme
}
})
});
});
});
});
</script>
{% endblock %}
+200
View File
@@ -0,0 +1,200 @@
{% extends 'base.html.twig' %}
{% block title %}Notification Types{% endblock %}
{% block content %}
<div class="mb-8">
<h1 class="section-title">Notification Types</h1>
<p class="section-subtitle">PHPFlasher supports four notification types for different scenarios.</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
{# Success #}
<div class="card">
<div class="h-2 bg-emerald-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-emerald-100 rounded-xl">
<svg class="w-8 h-8 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Success</h2>
<p class="text-gray-500">Positive feedback</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use success notifications for completed actions, confirmations, and positive feedback.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'success', message: 'Your profile has been updated successfully!'})"
class="btn btn-success w-full justify-start">
Profile Updated
</button>
<button onclick="showNotification({type: 'success', message: 'Your account has been created. Welcome aboard!', title: 'Account Created'})"
class="btn btn-success w-full justify-start">
Account Created
</button>
<button onclick="showNotification({type: 'success', message: 'Payment of $99.00 confirmed. Receipt sent to your email.', title: 'Payment Successful'})"
class="btn btn-success w-full justify-start">
Payment Confirmed
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->success('Profile updated successfully!');
// With title
flash()->success('Welcome aboard!', 'Account Created');</code></pre>
</div>
</div>
</div>
{# Error #}
<div class="card">
<div class="h-2 bg-rose-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-rose-100 rounded-xl">
<svg class="w-8 h-8 text-rose-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Error</h2>
<p class="text-gray-500">Failures and issues</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use error notifications for validation failures, server errors, and problems.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'error', message: 'Invalid email or password. Please try again.'})"
class="btn btn-danger w-full justify-start">
Login Failed
</button>
<button onclick="showNotification({type: 'error', message: 'Your card was declined. Please try another payment method.', title: 'Payment Failed'})"
class="btn btn-danger w-full justify-start">
Payment Declined
</button>
<button onclick="showNotification({type: 'error', message: 'Unable to connect to server. Please check your connection.', title: 'Connection Error'})"
class="btn btn-danger w-full justify-start">
Server Error
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->error('Invalid credentials.');
// With title
flash()->error('Please try again.', 'Payment Failed');</code></pre>
</div>
</div>
</div>
{# Warning #}
<div class="card">
<div class="h-2 bg-amber-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-amber-100 rounded-xl">
<svg class="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Warning</h2>
<p class="text-gray-500">Caution alerts</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use warning notifications to alert users about potential issues or important notices.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'warning', message: 'Your session will expire in 5 minutes.'})"
class="btn btn-warning w-full justify-start">
Session Expiring
</button>
<button onclick="showNotification({type: 'warning', message: 'You have unsaved changes that will be lost.', title: 'Unsaved Changes'})"
class="btn btn-warning w-full justify-start">
Unsaved Changes
</button>
<button onclick="showNotification({type: 'warning', message: 'Only 2 items left in stock!', title: 'Low Stock'})"
class="btn btn-warning w-full justify-start">
Low Stock Alert
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->warning('Session expiring soon.');
// With title
flash()->warning('Only 2 left!', 'Low Stock');</code></pre>
</div>
</div>
</div>
{# Info #}
<div class="card">
<div class="h-2 bg-sky-500"></div>
<div class="card-body">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="p-3 bg-sky-100 rounded-xl">
<svg class="w-8 h-8 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div>
<h2 class="text-2xl font-bold text-gray-800">Info</h2>
<p class="text-gray-500">Neutral information</p>
</div>
</div>
</div>
<p class="text-gray-600 mb-4">Use info notifications for tips, updates, and general information.</p>
<div class="space-y-2 mb-6">
<button onclick="showNotification({type: 'info', message: 'A new version is available. Click here to update.'})"
class="btn btn-info w-full justify-start">
Update Available
</button>
<button onclick="showNotification({type: 'info', message: 'Tip: You can use keyboard shortcuts for faster navigation.', title: 'Pro Tip'})"
class="btn btn-info w-full justify-start">
Helpful Tip
</button>
<button onclick="showNotification({type: 'info', message: 'Your report is being generated and will be ready shortly.'})"
class="btn btn-info w-full justify-start">
Processing Status
</button>
</div>
<div class="code-block">
<div class="code-header">
<span>PHP</span>
</div>
<pre class="!m-0"><code class="language-php">flash()->info('New version available!');
// With title
flash()->info('Use shortcuts!', 'Pro Tip');</code></pre>
</div>
</div>
</div>
</div>
{% endblock %}
+39
View File
@@ -0,0 +1,39 @@
const Encore = require('@symfony/webpack-encore');
// Manually configure the runtime environment if not already configured by the "encore" command.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
/*
* ENTRY CONFIG
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
;
module.exports = Encore.getWebpackConfig();
+287 -4
View File
@@ -1,12 +1,20 @@
---
permalink: /installation/
title: Installation
description: Install PHPFlasher in your project easily. Its modular, so you can install only the parts you need. Just include the library in your composer.json file and run composer install to get started.
description: Install PHPFlasher in your project easily. It's modular, so you can install only the parts you need. Just include the library in your composer.json file and run composer install to get started.
---
**<span class="text-indigo-900">PHP<span class="text-indigo-500">Flasher</span></span>** is modular and consists of multiple libraries,
allowing users to install and use only the specific components they need for their project.
## <i class="fa-duotone fa-list-radio"></i> Requirements
- **PHP 8.2** or higher
- **Composer**
- **Laravel 11+** or **Symfony 7+**
---
## <i class="fa-duotone fa-list-radio"></i> Installation
**<span class="text-indigo-900">PHP<span class="text-indigo-500">Flasher</span></span>** can be installed using composer :
@@ -22,6 +30,8 @@ After installation, you need to run another command to set up the necessary asse
php artisan flasher:install
```
This command publishes the required JavaScript and CSS files to your `public/vendor/flasher` directory. These assets are automatically injected into your HTML responses.
<br />
**<i class="fa-brands fa-symfony text-black fa-xl"></i> Symfony**:
@@ -35,6 +45,8 @@ After installation, you need to run another command to set up the necessary asse
php bin/console flasher:install
```
This command publishes the required JavaScript and CSS files to your `public/vendor/flasher` directory. These assets are automatically injected into your HTML responses.
---
## <i class="fa-duotone fa-list-radio"></i> Usage
@@ -161,10 +173,10 @@ flash()->flash('{{ type }}', '{{ message }}');
| param | description |
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$type` | Notification type : <span class="text-white bg-green-600 px-2 py-1 rounded">success</span>, <span class="text-white bg-red-600 px-2 py-1 rounded">error</span>, <span class="text-white bg-yellow-600 px-2 py-1 rounded">warning</span>, <span class="text-white bg-blue-600 px-2 py-1 rounded">info</span> ....etc |
| `$type` | Notification type : <span class="text-white bg-green-600 px-2 py-1 rounded">success</span>, <span class="text-white bg-red-600 px-2 py-1 rounded">error</span>, <span class="text-white bg-yellow-600 px-2 py-1 rounded">warning</span>, <span class="text-white bg-blue-600 px-2 py-1 rounded">info</span> etc. |
| `$message` | The body of the message you want to deliver to your user. This may contain HTML. If you add links, be sure to add the appropriate classes for the framework you are using. |
| `$title` | The notification title, Can also include HTML |
| `$options` | Custom options for javascript libraries (toastr, noty, notyf ...etc) | |
| `$options` | Custom options for javascript libraries (toastr, noty, notyf etc.) | |
---
@@ -202,7 +214,7 @@ flash()
| param | description |
|------------|--------------------------------------------------------------------------------------|
| `$options` | Custom options to be passed to the javascript libraries (toastr, noty, notyf ...etc) |
| `$options` | Custom options to be passed to the javascript libraries (toastr, noty, notyf etc.) |
| `$merge` | Merge options if you call the options method multiple times |
---
@@ -390,3 +402,274 @@ return [
'Congratulations!' => 'تهانينا',
];
```
---
<p id="method-created"><a href="#method-created" class="anchor"><i class="fa-duotone fa-link"></i> Resource Operations</a></p>
PHPFlasher provides convenient methods for common CRUD operations. These methods automatically generate appropriate success messages with the resource name.
```php
flash()->created(string|object|null $resource = null): Envelope
flash()->updated(string|object|null $resource = null): Envelope
flash()->saved(string|object|null $resource = null): Envelope
flash()->deleted(string|object|null $resource = null): Envelope
```
{% assign id = '#/ usage created' %}
{% assign type = 'success' %}
{% assign message = 'The resource was created' %}
{% assign options = '{}' %}
{% include example.html %}
```php
{{ id }}
// Basic usage - displays "The resource was created"
flash()->created();
// With a string - displays "The user was created"
flash()->created('user');
// With an object - automatically resolves the class name
flash()->created($user); // displays "The User was created"
```
{% assign id = '#/ usage updated' %}
{% assign message = 'The post was updated' %}
{% include example.html %}
```php
{{ id }}
flash()->updated('post');
```
{% assign id = '#/ usage deleted' %}
{% assign message = 'The comment was deleted' %}
{% include example.html %}
```php
{{ id }}
flash()->deleted('comment');
```
| param | description |
|-------------|--------------------------------------------------------------------------------------------|
| `$resource` | A string name, an object (class name will be extracted), or null (defaults to "resource") |
> If you pass an object that implements a `getFlashIdentifier()` method, that value will be used as the resource name.
---
<p id="method-when"><a href="#method-when" class="anchor"><i class="fa-duotone fa-link"></i> Conditional Notifications</a></p>
Use `when()` and `unless()` to conditionally display notifications without wrapping your code in if statements.
```php
flash()->when(bool|\Closure $condition): static
flash()->unless(bool|\Closure $condition): static
```
{% assign id = '#/ usage when' %}
{% assign type = 'success' %}
{% assign message = 'Profile updated successfully!' %}
{% assign options = '{}' %}
{% include example.html %}
```php
{{ id }}
// Only show notification if condition is true
flash()
->when($user->isVerified())
->success('Profile updated successfully!');
// Using a closure for more complex conditions
flash()
->when(fn() => $order->total() > 100)
->success('You qualify for free shipping!');
```
{% assign id = '#/ usage unless' %}
{% assign type = 'warning' %}
{% assign message = 'Please verify your email address.' %}
{% include example.html %}
```php
{{ id }}
// Show notification unless condition is true
flash()
->unless($user->hasVerifiedEmail())
->warning('Please verify your email address.');
```
| param | description |
|--------------|----------------------------------------------------------------|
| `$condition` | A boolean or a Closure that returns a boolean |
---
<p id="method-keep"><a href="#method-keep" class="anchor"><i class="fa-duotone fa-link"></i> keep</a></p>
The `keep()` method increments the notification's hop count by 1, allowing it to persist through an additional request. This is useful when you need a notification to survive one more redirect.
```php
flash()->keep(): static
```
```php
// Notification will persist through one additional redirect
flash()
->keep()
->success('This message will survive one more redirect.');
```
| behavior | description |
|-------------|----------------------------------------------------------------|
| Default | Notifications are shown once and then removed |
| With keep() | Notification persists through one additional request |
> The `keep()` method is a shorthand for incrementing the current hops by 1. For more control, use the `hops()` method directly.
---
<p id="method-delay"><a href="#method-delay" class="anchor"><i class="fa-duotone fa-link"></i> delay</a></p>
The `delay()` method sets a delay (in milliseconds) before the notification is displayed. This is useful for staggered notifications or giving users time to focus on content before showing a message.
```php
flash()->delay(int $delay): static
```
{% assign id = '#/ usage delay' %}
{% assign type = 'info' %}
{% assign message = 'Check out our new features!' %}
{% assign options = '{"delay": 2000}' %}
{% include example.html %}
```php
{{ id }}
// Notification appears after 2 seconds
flash()
->delay(2000) // 2000 milliseconds = 2 seconds
->info('Check out our new features!');
```
| param | description |
|----------|--------------------------------------------------|
| `$delay` | Delay in milliseconds before showing the notification |
---
<p id="method-preset"><a href="#method-preset" class="anchor"><i class="fa-duotone fa-link"></i> preset</a></p>
Presets allow you to define reusable notification configurations in your config file, then reference them by name in your code.
```php
flash()->preset(string $preset, array $parameters = []): Envelope
```
First, define presets in your configuration file:
**<i class="fa-brands fa-laravel text-red-900 fa-xl"></i> Laravel** - `config/flasher.php`:
```php
return [
'presets' => [
'entity_saved' => [
'type' => 'success',
'title' => 'Saved',
'message' => 'The :entity has been saved successfully.',
],
'welcome' => [
'type' => 'info',
'message' => 'Welcome back, :name!',
'options' => [
'timeout' => 3000,
],
],
],
];
```
**<i class="fa-brands fa-symfony text-black fa-xl"></i> Symfony** - `config/packages/flasher.yaml`:
```yaml
flasher:
presets:
entity_saved:
type: success
title: Saved
message: 'The :entity has been saved successfully.'
welcome:
type: info
message: 'Welcome back, :name!'
options:
timeout: 3000
```
Then use the preset in your code:
```php
// Use a preset with parameter substitution
flash()->preset('entity_saved', [':entity' => 'product']);
// Another example
flash()->preset('welcome', [':name' => $user->name]);
```
| param | description |
|---------------|------------------------------------------------------------|
| `$preset` | The name of the preset as defined in your configuration |
| `$parameters` | Key-value pairs for placeholder substitution in the message |
---
<p id="long-running-processes"><a href="#long-running-processes" class="anchor"><i class="fa-duotone fa-link"></i> Long-Running Processes</a></p>
PHPFlasher fully supports long-running PHP processes like **Laravel Octane**, **FrankenPHP**, **Swoole**, and **RoadRunner**. The library automatically resets its internal state between requests to prevent notification leakage.
### Laravel Octane
PHPFlasher automatically integrates with Laravel Octane. No additional configuration is required. The library listens for the `RequestReceived` event and resets all internal state including:
- Notification logger (tracked notifications)
- Fallback session storage (used when session is not started)
```php
// This works seamlessly with Octane - no special handling needed
flash()->success('Welcome back!');
```
### Symfony with FrankenPHP / Swoole / RoadRunner
PHPFlasher uses Symfony's `kernel.reset` mechanism to automatically reset state between requests. The following services are registered with the `kernel.reset` tag:
- `flasher.notification_logger_listener` - Resets the notification tracking
- `flasher.worker_listener` - Resets fallback session storage
No additional configuration is required. Just install PHPFlasher as usual and it will work correctly in worker mode.
```php
// This works seamlessly in worker mode - no special handling needed
flash()->success('Operation completed!');
```
### Hotwire / Turbo Drive
PHPFlasher includes built-in support for Hotwire Turbo Drive. The library:
1. Marks notification containers with `data-turbo-temporary` to prevent caching
2. Listens for `turbo:before-cache` events to clean up notifications before page caching
3. Properly renders notifications after Turbo Drive navigation
```php
// Notifications work seamlessly with Turbo Drive navigation
flash()->success('Profile updated successfully!');
```
> No additional JavaScript configuration is required. PHPFlasher handles Turbo Drive integration automatically.
+4 -4
View File
@@ -26,7 +26,7 @@ To pull in the <strong><span class="text-indigo-900">PHP<span class="text-indigo
</span>
```html
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@1.2.4/dist/flasher.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@2.2.0/dist/flasher.min.js"></script>
```
---
@@ -55,7 +55,7 @@ flasher.info("Welcome back");
or if you are using a cdn like this:
```html
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@1.2.4/dist/flasher.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@2.2.0/dist/flasher.min.js"></script>
<script>
flasher.error("Oops! Something went wrong!");
flasher.warning("Are you sure you want to proceed ?");
@@ -72,8 +72,8 @@ First grab the CDN for any js library adapter supported by <strong><span class="
and then call the `create()` method on flasher object :
```html
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@1.2.4/dist/flasher.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher-toastr@1.2.4/dist/flasher-toastr.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher@2.2.0/dist/flasher.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@flasher/flasher-toastr@2.2.0/dist/flasher-toastr.min.js"></script>
<script>
const factory = flasher.use('toastr');
factory.error("Oops! Something went wrong!");
+5 -5
View File
@@ -89,7 +89,7 @@ flasher:
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `noty` adapter.
---
@@ -182,7 +182,7 @@ For more information on Noty options and usage, please refer to the original doc
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `noty` adapter.
---
@@ -317,7 +317,7 @@ noty()
```php
#/ noty theme relax
// don't the load the theme css file: https://github.com/needim/noty/blob/master/lib/themes/relax.css
// don't forget to load the theme css file: https://github.com/needim/noty/blob/master/lib/themes/relax.css
noty()
->theme('relax')
@@ -609,8 +609,8 @@ noty()
<p id="method-sounds"><a href="#method-sounds" class="anchor"><i class="fa-duotone fa-link"></i> sounds</a></p>
`sources` : Array of audio sources e.g 'some.wav' <br />
`volume` : nteger value between 0-1 e.g 0.5 <br />
`sources` : Array of audio sources e.g. 'some.wav' <br />
`volume` : Integer value between 0-1 e.g. 0.5 <br />
`conditions` : There are two conditions for now: 'docVisible' & 'docHidden'. You can use one of them or both. <br />
```php
+1 -1
View File
@@ -94,7 +94,7 @@ Then, before returning a view or redirecting, call the `success()` method and pa
<p id="method-success"><a href="#method-success" class="anchor"><i class="fa-duotone fa-link"></i> success</a></p>
{% assign id = '#/ noty' %}
{% assign id = '#/ notyf' %}
{% assign type = 'success' %}
{% assign message = site.data.messages[type] | sample %}
{% assign options = '{}' %}
+3 -3
View File
@@ -87,7 +87,7 @@ flasher:
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `sweetalert` adapter.
---
@@ -96,7 +96,7 @@ Then, before returning a view or redirecting, call the `success()` method and pa
<p id="method-success"><a href="#method-success" class="anchor"><i class="fa-duotone fa-link"></i> success</a></p>
{% assign id = '#/ noty' %}
{% assign id = '#/ sweetalert' %}
{% assign type = 'success' %}
{% assign message = site.data.messages[type] | sample %}
{% assign options = '{}' %}
@@ -180,7 +180,7 @@ For more information on Sweetalert2 alert options and usage, please refer to th
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `sweetalert` adapter.
---
+4 -4
View File
@@ -72,7 +72,7 @@ php bin/console flasher:install
flasher:
plugins:
noty:
toastr:
scripts:
- '/vendor/flasher/jquery.min.js'
- '/vendor/flasher/toastr.min.js'
@@ -90,7 +90,7 @@ flasher:
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `toastr` adapter.
---
@@ -99,7 +99,7 @@ Then, before returning a view or redirecting, call the `success()` method and pa
<p id="method-success"><a href="#method-success" class="anchor"><i class="fa-duotone fa-link"></i> success</a></p>
{% assign id = '#/ noty' %}
{% assign id = '#/ toastr' %}
{% assign type = 'success' %}
{% assign message = site.data.messages[type] | sample %}
{% assign options = '{}' %}
@@ -183,7 +183,7 @@ For more information on Toastr options and usage, please refer to the original d
---
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `notyf` adapter.
> The methods described in the **[Usage](/installation/#-usage)** section can also be used with the `toastr` adapter.
---
+26
View File
@@ -74,3 +74,29 @@ html_structure: |
</div>
</div>
---
## Usage
To use the Amazon theme, specify it in your flash notification:
```php
flash()->use('theme.amazon')->success('Your order has been placed successfully!');
flash()->use('theme.amazon')->error('Unable to process your payment.');
flash()->use('theme.amazon')->warning('This item is almost out of stock.');
flash()->use('theme.amazon')->info('Free shipping on orders over $25.');
```
Or set it as the default theme in your configuration:
**Laravel** - `config/flasher.php`:
```php
return [
'default' => 'theme.amazon',
];
```
**Symfony** - `config/packages/flasher.yaml`:
```yaml
flasher:
default: theme.amazon
```
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Flasher\Laravel\EventListener;
use Flasher\Laravel\Storage\FallbackSession;
use Flasher\Prime\EventDispatcher\EventListener\NotificationLoggerListener;
use Laravel\Octane\Events\RequestReceived;
@@ -11,8 +12,13 @@ final readonly class OctaneListener
{
public function __invoke(RequestReceived $event): void
{
// Reset the notification logger to prevent state leakage between requests
/** @var NotificationLoggerListener $listener */
$listener = $event->sandbox->make('flasher.notification_logger_listener');
$listener->reset();
// Reset the fallback session static storage to prevent notification leakage
// when session is not started (e.g., during API requests)
FallbackSession::reset();
}
}
+1 -1
View File
@@ -175,7 +175,7 @@ final class Envelope implements NotificationInterface
return [
...$this->notification->toArray(),
'metadata' => array_merge(...$stamps),
'metadata' => $stamps ? array_merge(...$stamps) : [],
];
}
@@ -122,6 +122,9 @@ final class HtmlPresenter implements PresenterInterface
const addRenderListener = () => {
if (1 === document.querySelectorAll('script.flasher-js').length) {
document.addEventListener('flasher:render', render);
document.addEventListener('turbo:before-cache', () => {
document.querySelectorAll('.fl-wrapper').forEach(el => el.remove());
});
}
{$livewireListener}
@@ -145,7 +148,7 @@ final class HtmlPresenter implements PresenterInterface
return <<<JAVASCRIPT
document.addEventListener('livewire:navigating', () => {
document.querySelectorAll('.fl-no-cache').forEach(el => el.remove());
document.querySelectorAll('.fl-wrapper').forEach(el => el.remove());
});
JAVASCRIPT;
}

Some files were not shown because too many files have changed in this diff Show More