update symfony and laravel demos

This commit is contained in:
Younes ENNAJI
2025-03-28 02:06:37 +00:00
parent 93ca18a67b
commit 12b0842cdd
14 changed files with 1542 additions and 120 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ 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],
// 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],
+1 -1
View File
@@ -1,7 +1,7 @@
flasher:
# Default notification library (e.g., 'flasher', 'toastr', 'noty', etc.)
# themes: flasher, crystal, emerald, sapphire
default: theme.amazon
default: theme.minimal
# Path to the main JavaScript file of PHPFlasher
main_script: '/vendor/flasher/flasher.min.js'
@@ -1,33 +1,33 @@
nelmio_security:
# prevents framing of the entire site
clickjacking:
paths:
'^/.*': DENY
# disables content type sniffing for script resources
content_type:
nosniff: true
# forces Microsoft's XSS-Protection with
# its block mode
xss_protection:
enabled: true
mode_block: true
# Send a full URL in the `Referer` header when performing a same-origin request,
# only send the origin of the document to secure destination (HTTPS->HTTPS),
# and send no header to a less secure destination (HTTPS->HTTP).
# If `strict-origin-when-cross-origin` is not supported, use `no-referrer` policy,
# no referrer information is sent along with requests.
referrer_policy:
enabled: true
policies:
- 'no-referrer'
- 'strict-origin-when-cross-origin'
csp:
enabled: true
enforce:
script-src:
- 'self'
# nelmio_security:
# # prevents framing of the entire site
# clickjacking:
# paths:
# '^/.*': DENY
#
# # disables content type sniffing for script resources
# content_type:
# nosniff: true
#
# # forces Microsoft's XSS-Protection with
# # its block mode
# xss_protection:
# enabled: true
# mode_block: true
#
# # Send a full URL in the `Referer` header when performing a same-origin request,
# # only send the origin of the document to secure destination (HTTPS->HTTPS),
# # and send no header to a less secure destination (HTTPS->HTTP).
# # If `strict-origin-when-cross-origin` is not supported, use `no-referrer` policy,
# # no referrer information is sent along with requests.
# referrer_policy:
# enabled: true
# policies:
# - 'no-referrer'
# - 'strict-origin-when-cross-origin'
#
# csp:
# enabled: true
#
# enforce:
# script-src:
# - 'self'
@@ -0,0 +1,22 @@
document.addEventListener('DOMContentLoaded', function() {
const demoForm = document.getElementById('notification-demo-form');
if (demoForm) {
demoForm.addEventListener('submit', function(e) {
e.preventDefault();
const type = document.getElementById('notification-type').value;
const title = document.getElementById('notification-title').value;
const message = document.getElementById('notification-message').value;
const position = document.getElementById('notification-position').value;
const timeout = document.getElementById('notification-timeout').value;
// Use the PHPFlasher JavaScript API to show the notification
window.flasher[type](message, {
title: title,
position: position,
timeout: parseInt(timeout, 10)
});
});
}
});
@@ -0,0 +1,147 @@
<?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]);
}
}
@@ -0,0 +1,78 @@
<?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');
}
}
+63 -70
View File
@@ -5,6 +5,8 @@ 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;
@@ -13,77 +15,68 @@ class HomeController extends AbstractController
#[Route('/', name: 'app_home')]
public function index(): Response
{
$themes = [
// 'flasher',
// 'amber',
// 'sapphire',
// 'crystal',
// 'emerald',
// 'ruby',
// 'onyx',
// 'jade',
// 'aurora',
// 'neon',
// 'minimal',
// 'material',
// 'google',
// 'ios',
// 'slack',
// 'facebook',
// 'amazon',
];
$positions = [
// 'top-left',
// 'top-right',
// 'bottom-left',
// 'bottom-right',
// 'top-center',
// 'bottom-center',
// 'center-left',
// 'center-right',
// 'center-center',
];
$messages = [
'info' => 'Welcome back!',
'warning' => 'Are you sure you want to proceed?',
'error' => 'Oops! Something went wrong!',
'success' => 'Data has been saved successfully!',
];
// foreach ($themes as $index => $theme) {
foreach ($messages as $type => $message) {
// $position = $positions[$index % \count($positions)];
// $message = \sprintf('%s: %s', $theme, $message);
flash()
// ->use("theme.$theme")
// ->option('position', $position)
->$type($message);
}
// }
$plugins = [
// 'noty',
// 'notyf',
// 'sweetalert',
// 'toastr',
];
foreach ($plugins as $plugin) {
foreach ($messages as $type => $message) {
flash()
->use($plugin)
->$type($message);
}
}
// 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']);
}
}
@@ -0,0 +1,53 @@
<?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');
}
}
@@ -0,0 +1,71 @@
<?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');
}
}
+227 -13
View File
@@ -1,17 +1,231 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}PHPFlasher Demo{% endblock %}</title>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{# Tailwind CSS v4 CDN #}
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
{% block stylesheets %}{% endblock %}
{% block javascripts %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
</body>
{# 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">
{# Custom styles using Tailwind #}
<script 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;
}
.btn-primary {
@apply bg-indigo-600 text-white hover:bg-indigo-700 focus:ring-indigo-500;
}
.btn-success {
@apply bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500;
}
.btn-danger {
@apply bg-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500;
}
.btn-warning {
@apply bg-amber-500 text-white hover:bg-amber-600 focus:ring-amber-400;
}
.btn-info {
@apply bg-sky-500 text-white hover:bg-sky-600 focus:ring-sky-400;
}
.btn-outline {
@apply border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-indigo-500;
}
.code-block {
@apply rounded-lg overflow-hidden shadow-lg my-6;
}
.code-header {
@apply bg-gray-800 text-gray-200 px-4 py-2 flex justify-between items-center;
}
.theme-card {
@apply rounded-lg overflow-hidden bg-white shadow-md hover:shadow-lg transition duration-300;
}
.theme-preview {
@apply h-32 flex items-center justify-center;
}
}
</script>
{% block stylesheets %}{% endblock %}
</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">
<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
</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>
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">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</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>
</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>
<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>
<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>
</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>
<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');
});
}
});
</script>
{% block javascripts %}{% endblock %}
</body>
</html>
@@ -0,0 +1,232 @@
{% 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 %}
@@ -0,0 +1,220 @@
{% 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 %}
+92 -2
View File
@@ -1,7 +1,97 @@
{% extends 'base.html.twig' %}
{% block title %}PHPFlasher{% endblock %}
{% block title %}PHPFlasher Features{% endblock %}
{% block body %}
{% 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 %}
@@ -0,0 +1,302 @@
{% 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 %}