Compare commits

...

390 Commits

Author SHA1 Message Date
Younes ENNAJI 58123f259c Release v2.5.1 2026-03-28 02:08:46 +01:00
Younes ENNAJI d427132437 Upgrade Laravel demo to Laravel 13 for JSON session testing
- Update PHP requirement to ^8.3
- Upgrade laravel/framework to ^13.0 and laravel/tinker to ^3.0
- Upgrade pestphp/pest to ^4.0 and pest-plugin-laravel to ^4.0
- Update spatie/laravel-csp to ^3.23 and barryvdh/laravel-debugbar to ^4.1
- Add session serialization config option (defaults to 'json')
- Add FlasherSessionTest to verify flasher works with JSON sessions
2026-03-28 02:05:14 +01:00
Younes ENNAJI f372dcf70e Fix Laravel 13 JSON session serialization compatibility 2026-03-28 01:52:00 +01:00
Younes ENNAJI c4f1b059a3 upgrade dependencies 2026-03-28 01:50:03 +01:00
Younes ENNAJI df4b85c0fd Add v2.5.0 release preparation tests and update CHANGELOG 2026-03-07 19:15:58 +00:00
Younes ENNAJI e0766c1198 Add v2.5.0 release preparation tests and update CHANGELOG 2026-03-07 18:31:05 +00:00
Younes ENNAJI e4337b0b63 Fix SweetAlertBuilder::question() bug and improve code quality 2026-03-07 17:50:43 +00:00
Younes ENNAJI bd8169619e Refactor code and update tests for improved DX 2026-03-02 05:19:34 +00:00
Younes ENNAJI 0d25c72743 Add IDE autocompletion support and Type helper methods 2026-03-02 05:13:28 +00:00
Younes ENNAJI d4abef58eb Standardize exception message format and add exception tests 2026-03-02 04:06:51 +00:00
Younes ENNAJI e126813835 Fix FlasherPlugin::normalizeAlias() return type and add tests 2026-03-02 04:02:08 +00:00
Younes ENNAJI 5612d4d705 Fix ResponseManager filter criteria handling and update tests 2026-03-02 03:49:35 +00:00
Younes ENNAJI 9454b2d155 Fix return type and parameter name inconsistencies in Notification classes 2026-03-02 03:46:43 +00:00
Younes ENNAJI d39159cf90 Fix FilterEvent type consistency and remove redundant callable check 2026-03-02 03:42:27 +00:00
Younes ENNAJI 76d63c03ce Reset CSP handler in worker listeners and add related tests 2026-03-02 03:33:16 +00:00
Younes ENNAJI 286fe5143e Update ContentSecurityPolicy handling and ResponseExtension 2026-03-02 03:27:04 +00:00
Younes ENNAJI f399bc912d Update NotificationFactoryLocator and CHANGELOG 2026-03-02 03:23:45 +00:00
Younes ENNAJI 98336b98bf Update filter criteria and CHANGELOG 2026-03-02 03:20:33 +00:00
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
Younes ENNAJI fd65254d63 docs: move Try All Themes section to top of themes page 2026-03-01 22:17:40 +00:00
Younes ENNAJI 33ac9013d5 build assets 2026-03-01 22:16:54 +00:00
Younes ENNAJI 1cc6a7c537 docs: remove hero sections from playground and themes pages 2026-03-01 22:15:18 +00:00
Younes ENNAJI dfe9a12fe1 docs: remove old themes.md (replaced by themes/index.html) 2026-03-01 22:11:08 +00:00
Younes ENNAJI 8074bb1f90 docs: add themes gallery page at /themes/
Features:
- Hero section with animated gradient background
- Themes organized by category (Brand-Inspired, Gemstone, Minimal)
- Visual cards with gradient previews for each theme
- Interactive "Try All Themes" section with demo buttons
- Usage code examples for setting default or per-notification themes
- CTA linking to playground
- Added "All Themes" link to navigation menu
2026-03-01 22:11:00 +00:00
Younes ENNAJI 03942aa634 build assets 2026-03-01 22:04:32 +00:00
Younes ENNAJI 6abae0bdde docs: add interactive playground page
Features:
- Hero section with animated background elements
- Embedded flasher-studio interactive component
- Quick theme preview buttons (12 popular themes)
- Pro tips section for better user experience
- CTA section linking to Laravel/Symfony guides
- Added playground to navigation menu
2026-03-01 22:02:13 +00:00
Younes ENNAJI 05c15399ac docs: restore Palestine support banner 2026-03-01 21:57:14 +00:00
Younes ENNAJI 1bd85021d9 docs: redesign root README for better first impression
Key improvements:
- Strong tagline: "One line of PHP. Beautiful notifications. Zero JavaScript"
- Quick Start at the top (installation in 1 command)
- "Why PHPFlasher?" comparison table showing advantages
- Livewire integration section (previously missing)
- Collapsible themes list for better scannability
- Cleaner visual hierarchy with horizontal rules
- Prominent links to docs, playground, and bug reports
- Simplified contributors section using contrib.rocks
- Clear calls to action for starring and sharing
2026-03-01 21:54:51 +00:00
Younes ENNAJI 359e6de361 docs: expand adapter READMEs with comprehensive examples
Each adapter README now includes:
- Features section highlighting key capabilities
- Installation instructions for Laravel and Symfony
- Multiple Quick Start code examples
- Configuration options table
- Livewire integration with events
- Global configuration examples
2026-03-01 21:50:11 +00:00
Younes ENNAJI e417105f7a refactor(themes): create shared utilities and design tokens
This refactoring improves code quality and reduces duplication:

## New Files
- `themes/shared/icons.ts` - Centralized SVG icons (getIcon, getCloseIcon)
- `themes/shared/accessibility.ts` - A11y helpers (getA11yString, getCloseButtonA11y)
- `themes/shared/constants.ts` - Standard class names and default titles

## Design Tokens Added (index.scss)
- Spacing scale: --fl-spacing-xs through --fl-spacing-2xl
- Typography scale: --fl-font-size-xs through --fl-font-size-xl
- Close button sizes: --fl-close-sm, --fl-close-md, --fl-close-lg
- Border radius: --fl-radius-sm through --fl-radius-full
- Shadow tokens: --fl-shadow-sm through --fl-shadow-lg
- Animation: --fl-duration-*, --fl-easing-*, --fl-slide-*

## Mixins Updated
- Added `close-button-sized($size)` with sm/md/lg support
- Added `close-button-circular($size)` variant
- Added `close-button-text` for text-style buttons
- Updated existing mixins to use design tokens

## All 17 Themes Refactored
Each theme now uses shared utilities instead of duplicating code:
- Icon code: 20+ lines → 1 function call
- Accessibility: 3 lines → 1 function call
- Class names: via CLASS_NAMES constants

Backwards compatible - no breaking changes.
2026-03-01 21:34:50 +00:00
Younes ENNAJI abd70c1d4b Update main JavaScript bundle 2026-03-01 21:13:14 +00:00
Younes ENNAJI 9acddbda52 docs: update CHANGELOG and Livewire docs with event system
Add documentation for the event dispatching system:
- Update CHANGELOG.md with unreleased features
- Add Toastr events section (click, close, show, hidden)
- Add Noty events section (click, close, show, hover)
- Add Notyf events section (click, dismiss)
- Add Theme events section (generic and theme-specific click)
- Include code examples for Livewire event listeners
2026-03-01 21:11:13 +00:00
Younes ENNAJI 7d6e9b46b8 add event dispatching system for Livewire integration
Implement consistent event dispatching across Noty, Notyf, Toastr adapters and
themes, following the existing SweetAlert pattern. This enables Livewire
integration for all notification types.

JavaScript Events:
- Noty: flasher:noty:show, flasher:noty:click, flasher:noty:close, flasher:noty:hover
- Notyf: flasher:notyf:click, flasher:notyf:dismiss
- Toastr: flasher:toastr:show, flasher:toastr:click, flasher:toastr:close, flasher:toastr:hidden
- Themes: flasher:theme:click (generic), flasher:theme:{name}:click (specific)

PHP Livewire Listeners:
- LivewireListener for each adapter (Noty, Notyf, Toastr)
- ThemeLivewireListener for theme click events
- Registered in service providers when Livewire is bound

This allows Livewire users to listen for notification events and react
accordingly (e.g., noty:click, theme:flasher:click).
2026-03-01 21:05:10 +00:00
Younes ENNAJI f1051e1d7f fix: resolve PHPStan errors and add Vitest to lint script
- Remove unnecessary null coalescing in HtmlPresenter (mainScript is never null)
- Add comprehensive ignore rules for test-related PHPStan warnings
- Add Vitest test runner to bin/lint script

All quality checks now pass:
- Rector
- PHP-CS-Fixer
- PHPStan (level max)
- Composer validation
- PHPLint
- PHPUnit (1281 tests)
- Vitest (219 tests)
2026-03-01 20:24:33 +00:00
Younes ENNAJI 87da42fdea Refine code style and tests 2026-03-01 20:16:44 +00:00
Younes ENNAJI 4d9cda22cf build assets 2026-03-01 20:16:00 +00:00
Younes ENNAJI c58f3c7b40 upgrade dependencies 2026-03-01 20:15:46 +00:00
Younes ENNAJI 47eb66e874 fix: update tests for new validation requirements
Update tests that were using invalid values for HopsStamp (0 is now
invalid, must be >= 1) and update expected output format for
mainScript (now uses json_encode which produces double quotes).
2026-03-01 20:13:57 +00:00
Younes ENNAJI 30de24f054 fix: add null checks and error handling to docs JS controllers
clipboard_controller.js:
- Check if clipboard API is available before using
- Handle clipboard write promise rejection
- Show error icon on failure

prev-next_controller.js:
- Guard against missing navigation element
- Guard against missing span element in links

anchor_controller.js:
- Guard against missing ul element
2026-03-01 20:11:07 +00:00
Younes ENNAJI 162ea87330 fix: prevent memory leaks and handle errors in FlasherPlugin
- Remove DOMContentLoaded listener after it fires to prevent memory leak
- Clean up timer interval and event listeners when notification is removed
- Add null check in stringToHTML to throw clear error for invalid templates
- Store cleanup function on notification element for proper disposal

Added tests for memory leak prevention and error handling.
2026-03-01 20:10:06 +00:00
Younes ENNAJI 8cda9d1eb1 fix: rename shadowed variable in Request::getType()
The parameter \$type was being reassigned to the session value, which
shadows the original parameter and causes confusion for static analysis
tools and maintainers.

Renamed the session value variable to \$value for clarity.
2026-03-01 20:08:28 +00:00
Younes ENNAJI 6d314dbc07 fix: add validation to HopsStamp and DelayStamp
HopsStamp:
- Validate hops amount must be >= 1 (positive integer)
- Negative or zero hops don't make logical sense

DelayStamp:
- Validate delay must be >= 0 (non-negative integer)
- Negative delays don't make logical sense

Both now throw InvalidArgumentException for invalid values, making
configuration errors fail fast with clear messages.
2026-03-01 20:07:54 +00:00
Younes ENNAJI ad5c0f56dd fix: validate limit criteria must be positive integer
Previously, negative limits caused array_slice() to return unexpected
results (all except last N elements) and zero returned an empty array
without clear intent.

Now throws InvalidArgumentException for values < 1, making invalid
configurations fail fast with a clear error message.
2026-03-01 20:06:53 +00:00
Younes ENNAJI fd36c2ec0c fix: validate alias attribute in PresenterCompilerPass
Add validation to ensure services tagged with 'flasher.presenter' have
the required 'alias' attribute. Previously, accessing $attributes['alias']
without checking would throw an "Undefined array key" error.

Now throws a clear InvalidArgumentException with a helpful message.
2026-03-01 20:01:12 +00:00
Younes ENNAJI 5202c86107 fix: handle invalid JSON gracefully in FlasherComponent
The json_decode() with JSON_THROW_ON_ERROR throws an exception before
the ?: operator can provide a fallback value. This caused page crashes
when invalid JSON was passed to the Blade component attributes.

Now using a helper method with try-catch to safely decode JSON and
return an empty array on failure, preventing page crashes.
2026-03-01 20:00:16 +00:00
Younes ENNAJI 9e7bb17faa fix: make OctaneListener invokable for Laravel event dispatcher
Laravel's event dispatcher expects listener classes to be invokable
(have __invoke method) when registered as a class name string.

The OctaneListener was using a handle() method which caused the
listener to never be invoked, resulting in notification state leaking
between Octane requests.

Renamed handle() to __invoke() to fix the issue.
2026-03-01 19:59:25 +00:00
Younes ENNAJI 83dc9e49dc fix: handle null public directory gracefully in InstallCommand
The getPublicDir() method can return null, but the code was directly
concatenating it with a string, which could cause unexpected behavior.

Now the command properly checks for null and returns a failure with
a clear error message instead of proceeding with an invalid path.
2026-03-01 19:57:50 +00:00
Younes ENNAJI 1d81de581b fix: escape nonce and mainScript to prevent XSS vulnerabilities
The HtmlPresenter was interpolating user-controlled values directly into
HTML attributes and JavaScript code without proper escaping, creating
XSS vulnerabilities.

Changes:
- Escape nonce with htmlspecialchars() for HTML attribute context
- Escape nonce with json_encode() for JavaScript string context
- Escape mainScript with json_encode() for JavaScript string context

Added tests to verify XSS payloads are properly escaped.
2026-03-01 19:55:49 +00:00
Younes ENNAJI 2ebdbecda6 fix: correct inverted Livewire registration condition
The condition in registerLivewire() was inverted, causing the Livewire
listener to never be registered when Livewire was actually available.

Before: returned early when Livewire class exists AND is NOT bound
After: returns early when Livewire class does NOT exist OR is NOT bound

This fixes Livewire notifications not being displayed in Livewire components.
2026-03-01 19:53:39 +00:00
Younes ENNAJI 851e0a00ed improve Translator test coverage to 100% 2026-02-26 06:13:13 +00:00
Younes ENNAJI d9b0b6998e add tests for FlasherDataCollector 2026-02-26 06:04:11 +00:00
Younes ENNAJI ed992d78f6 add tests for FlasherAssert and Constraint classes 2026-02-25 20:45:32 +00:00
Younes ENNAJI 0612a3bb61 exclude .phpstorm.meta.php from coverage 2026-02-25 20:35:27 +00:00
Younes ENNAJI c9a61ba69c add tests for helper functions 2026-02-25 20:29:51 +00:00
Younes ENNAJI f9746f607b add Laravel Facade tests 2026-02-25 20:22:38 +00:00
Younes ENNAJI 18c2233baa add tests for remaining Criteria classes 2026-02-25 20:01:29 +00:00
Younes ENNAJI 08c242b45a fix test to properly suppress expected console error 2026-02-25 19:50:01 +00:00
Younes ENNAJI cd53ceb139 improve test coverage for edge cases 2026-02-25 19:31:21 +00:00
Younes ENNAJI 549c36eeee improve test coverage for PHPUnit and Vitest 2026-02-25 19:16:53 +00:00
Younes ENNAJI ea0dccc961 add test coverage for PHPUnit and Vitest 2026-02-25 15:58:13 +00:00
Younes ENNAJI d33de77835 add vitest for JS/TS testing with comprehensive test coverage 2026-02-25 15:52:21 +00:00
Younes ENNAJI 62848e0fd1 update composer.lock 2026-02-25 11:37:47 +00:00
Younes ENNAJI c5059adac7 fix PHPStan errors in ThemeTemplatesTest 2026-02-25 11:34:09 +00:00
Younes ENNAJI 4145b870dd fix PHPStan errors in SessionBag 2026-02-25 11:34:01 +00:00
Younes ENNAJI 18a31f578b use PHP 8.2 for lint task 2026-02-25 11:33:40 +00:00
Younes ENNAJI f20bdebda0 add bootstrap and tailwindcss theme templates for Laravel 2026-02-25 11:20:29 +00:00
Younes ENNAJI f9807e91e2 add FallbackSession for stateless requests in Laravel 2026-02-25 11:15:34 +00:00
Younes ENNAJI e35339dca9 fix typo in FilterCriteria error message 2026-02-25 11:00:32 +00:00
Younes ENNAJI f9bef40ae6 fix misleading error messages in HopsCriteria and DelayCriteria 2026-02-25 10:43:52 +00:00
Younes ENNAJI 670e40dc97 fix StampsCriteria comparing values instead of keys 2026-02-25 10:43:44 +00:00
Younes ENNAJI 2b0e736d28 fix multi-field sorting in OrderByCriteria 2026-02-25 10:43:36 +00:00
Younes ENNAJI b79902779e add support for Laravel v13 2026-02-25 10:26:04 +00:00
Younes ENNAJI 8779de6c62 upgrade dependencies 2026-02-25 10:18:29 +00:00
Younes ENNAJI c074feea05 Apply clean design to Inertia docs and remove Additional Features section
- Removed emerald-colored icons from table of contents navigation
- Removed gradient backgrounds from section headers
- Simplified version requirement cards
- Updated all callout boxes to use slate-50 background
- Updated tabs to use indigo colors instead of emerald
- Removed Additional Features section from Advanced section
2026-01-25 06:32:49 +01:00
Younes ENNAJI 033d650497 Redesign Laravel, Symfony, and Livewire documentation pages
- Simplify table of contents navigation (remove colored icons)
- Remove gradient backgrounds from section headers
- Simplify version requirement cards (remove gradients and accent lines)
- Update callout boxes to use consistent slate-50 background
- Simplify notification type cards (remove gradient backgrounds)
- Use uniform slate/indigo color scheme throughout
2026-01-25 06:23:15 +01:00
Younes ENNAJI 913f7b9d17 Updates assets due to code changes 2026-01-25 06:20:13 +01:00
Younes ENNAJI 9eb6dcf8fa Redesign Symfony page with clean, professional styling
- Simplify table of contents navigation (remove colored icons)
- Remove gradient backgrounds from section headers
- Simplify version requirement cards (remove gradients and accent lines)
- Update callout boxes to use consistent slate-50 background
- Simplify notification type cards (remove gradient backgrounds)
- Use uniform slate/indigo color scheme throughout
- Fix malformed HTML in code blocks
- Update notification example to match callout style
2026-01-25 06:14:41 +01:00
Younes ENNAJI 06f75b21eb Fix jQuery dependency for Toastr library
Import jQuery and make it globally available before Toastr is loaded.
This prevents console errors about jQuery not being found.
2026-01-25 06:03:39 +01:00
Younes ENNAJI 0fe2e3e38e Change Themes badge from 'soon' to 'NEW' 2026-01-25 05:57:42 +01:00
Younes ENNAJI 3ee5ba2909 Change footer 'Created By' to 'Author' 2026-01-25 05:56:06 +01:00
Younes ENNAJI ca1a38d282 Simplify footer design with clean slate color scheme
- Remove gradient background and decorative SVG wave
- Use consistent slate-900/400/500 color palette
- Remove animated elements and decorative bars
- Simplify social links and documentation navigation
- Reduce author image size for cleaner layout
2026-01-25 05:54:42 +01:00
Younes ENNAJI 917b2c8f06 Simplify color scheme in Features and Libraries sections
- Replace vibrant gradient cards with clean white cards
- Use consistent indigo/slate color palette throughout
- Remove multicolor gradients for a more professional look
- All icons now use indigo-600 on indigo-50 backgrounds
- Stats section now uses uniform indigo color
- CTA button simplified to single indigo color
2026-01-25 05:51:18 +01:00
Younes ENNAJI 80cf92af09 Redesign "Choose Your Preferred Library" section with modern compact cards
- Simplify card design with centered layout
- Remove long descriptions in favor of concise taglines
- Use gradient icon badges with rounded squares
- Improve CTA button with gradient styling
- Reduce vertical space while maintaining visual appeal
2026-01-25 05:47:35 +01:00
Younes ENNAJI 82d0bc5a26 Redesign "Why Choose PHPFlasher?" section with modern gradient cards
- Replace white cards with vibrant gradient backgrounds
- Simplify content with concise descriptions
- Add decorative circles and backdrop blur effects
- Add stats section with key metrics (6+ frameworks, 5+ libraries, 10+ themes, 100% TypeScript)
- Improve visual hierarchy and spacing
2026-01-25 05:45:29 +01:00
Younes ENNAJI 32055b1e87 Update build artifacts 2026-01-25 05:43:02 +01:00
Younes ENNAJI 9c07b5a72e Increase PHPFlasher Studio height and remove v3.0 label 2026-01-25 05:41:16 +01:00
Younes ENNAJI fdfd8e0b50 Remove Notification Studio title and Copy button to save space 2026-01-25 05:39:27 +01:00
Younes ENNAJI 18712ba51e Remove Custom Icon, Reset All, and Animation Toggle from Notification Studio
- Remove Custom Icon section (not yet implemented in PHPFlasher)
- Remove Reset All button
- Remove Animation Toggle button
- Clean up related JavaScript code
- Reduce vertical scroll by simplifying UI
2026-01-25 05:37:01 +01:00
Younes ENNAJI abc8cf2569 Enhance Notification Studio with major improvements
- Add Copy Code button with visual feedback
- Add Reset All button to clear options
- Add Quick Presets for common use cases (User Created, Payment Failed, Login Success, etc.)
- Add Animation Toggle to enable/disable typing animations
- Add Icon Selection with 12 common icons
- Add 3 new adapter tabs: Noty, Toastr, and SweetAlert
- Add Code Comparison modal showing Before vs After PHPFlasher
- Improve mobile responsiveness with better layout for small screens
- Add custom scrollbar styling for code panels
- Update version to 3.0
2026-01-25 05:31:31 +01:00
Younes ENNAJI e1e3f01dce Updates generated CSS and manifest files 2026-01-25 05:28:57 +01:00
Younes ENNAJI 5aa8500683 Add jekyll-paginate gem to Gemfile and config 2026-01-25 05:25:49 +01:00
Younes ENNAJI cea67fe813 Add jekyll-paginate plugin to fix deprecation warning 2026-01-25 05:24:42 +01:00
Younes ENNAJI 3a47e100a0 Fix Liquid syntax errors in blog post by escaping Blade template syntax 2026-01-25 05:23:41 +01:00
Younes ENNAJI 93c5e987fc Remove Browser Support and Need assistance sections from theme layout 2026-01-25 05:22:02 +01:00
Younes ENNAJI 151c4f7778 Remove Footer CTA sections from Livewire and Inertia pages 2026-01-25 05:21:02 +01:00
Younes ENNAJI a5849a615b Remove Footer CTA section from Laravel page 2026-01-25 05:19:45 +01:00
Younes ENNAJI 1801e187ac Remove Footer CTA section from Symfony page 2026-01-25 05:18:40 +01:00
Younes ENNAJI 6eede92db6 Update LinkedIn username from younes--ennaji to yoeunes 2026-01-25 05:16:22 +01:00
Younes ENNAJI 0e04125627 Remove Testimonials and Browser Support sections from homepage 2026-01-25 05:11:25 +01:00
Younes ENNAJI aeca5b5ee7 Remove Community and CTA sections from homepage 2026-01-25 05:10:23 +01:00
Younes ENNAJI 1b2f45f964 Fix code indentation in homepage examples 2026-01-25 05:09:04 +01:00
Younes ENNAJI e70d40ef04 Simplify GitHub CTA message in documentation 2026-01-25 05:06:36 +01:00
Younes ENNAJI 0b9cfe80d5 Remove Blog links and v1.x documentation from navigation
- Hide Blog section from sidebar menu and header navigation
- Remove v1.x version link, keeping only current v2.x docs
2026-01-25 05:04:47 +01:00
Younes ENNAJI 767939a38c Update docs build artifacts after code simplification
Regenerate documentation build files with updated asset hashes following the recent code cleanup and simplification work.
2026-01-25 05:00:25 +01:00
Younes ENNAJI d7234c64a2 Simplify verbose PHPDoc class descriptions across 76 files
Remove descriptive class/interface-level documentation that duplicates
what the class/interface names already convey, while keeping all
type annotations (@param, @return, @var, @throws, @phpstan-, @mixin,
@template, @internal).

Files modified:
- Symfony integration (18 files): Attribute, Translation, Template,
  Component, Storage, Support, Http, Profiler, Command, Twig,
  EventListener, FlasherSymfonyBundle
- Laravel integration (13 files): ServiceProvider, Storage, Middleware,
  Http, Facade, Template, Translation, Command, Component, EventListener
- Prime/EventDispatcher (17 files): Events and EventListeners
- Prime/Storage (11 files): Filter/Criteria and Bag interfaces
- Prime/Stamp (4 files): Interface and stamp classes
- Prime/Factory (5 files): Factory interfaces and implementations
- Prime/Notification (2 files): Envelope and Notification
- Prime/Support/Traits (1 file): ForwardsCalls
2026-01-16 01:46:44 +01:00
Younes ENNAJI 194eab473e Simplify verbose documentation in translation language files
Remove verbose class-level and method-level documentation from:
- 8 translation language files (Arabic, Chinese, English, French, German, Portuguese, Russian, Spanish)
- FilterFactory class

Kept only essential type annotations (@return, @param, @throws, @var).

Also regenerated CSS build artifacts after previous SCSS changes.
2026-01-16 01:31:15 +01:00
Younes ENNAJI 521746f813 Simplify remaining comments in global.d.ts and SCSS theme files
- Remove file headers and verbose documentation from 2 global.d.ts files
- Simplify 12 SCSS theme files by removing multi-line documentation blocks
- Remove verbose inline comments that just repeat what the code does
- Keep helpful inline comments for non-obvious code

Files modified:
- src/Prime/Resources/assets/global.d.ts
- src/Toastr/Prime/Resources/assets/global.d.ts
- src/Prime/Resources/assets/themes/aurora/aurora.scss
- src/Prime/Resources/assets/themes/facebook/facebook.scss
- src/Prime/Resources/assets/themes/google/google.scss
- src/Prime/Resources/assets/themes/ios/ios.scss
- src/Prime/Resources/assets/themes/jade/jade.scss
- src/Prime/Resources/assets/themes/material/material.scss
- src/Prime/Resources/assets/themes/minimal/minimal.scss
- src/Prime/Resources/assets/themes/neon/neon.scss
- src/Prime/Resources/assets/themes/onyx/onyx.scss
- src/Prime/Resources/assets/themes/ruby/ruby.scss
- src/Prime/Resources/assets/themes/sapphire/sapphire.scss
- src/Prime/Resources/assets/themes/slack/slack.scss
2026-01-16 01:28:29 +01:00
Younes ENNAJI 34d780b0b7 Fix SCSS syntax errors and TypeScript build error
- Fix 15 theme SCSS files with orphaned comment closing markers
- Add type assertion to notyf.ts for private property access
- Regenerate build artifacts after fixes

The SCSS files had broken syntax from previous comment removal where
orphaned '*/' markers were left behind, causing SCSS parsing errors.
2026-01-16 00:52:20 +01:00
Younes ENNAJI 61f991f379 Simplify JSDoc and SCSS comments in theme files and entry points
Remove verbose JSDoc and SCSS comments from:
- Theme TypeScript files (17 amazon, amber, aurora, crystal, emerald, facebook, flasher, google, ios, jade, material, minimal, neon, onyx, ruby, sapphire, slack)
- Theme SCSS files (21 theme stylesheets including icons, progress, wrapper, and all theme variants)
- Theme index.ts registration files (17 files)
- Entry point and export files (index.ts, exports.ts)
- Adapter index.ts files (Noty, Notyf, SweetAlert, Toastr)
- Remaining plugin files (toastr.ts, notyf.ts)

Kept type annotations and removed only descriptive comments, examples,
and file headers following Symfony's concise documentation style.
2026-01-16 00:40:14 +01:00
Younes ENNAJI cc9fa57c4a Simplify JSDoc comments in plugin TypeScript files
Removes verbose documentation from:
- plugin.ts (AbstractPlugin base class)
- noty.ts (Noty plugin implementation)
- sweetalert.ts (SweetAlert plugin implementation)

Keeps essential type annotations.
2026-01-16 00:28:35 +01:00
Younes ENNAJI 613c73d7b6 Simplify JSDoc and comments in TypeScript and SCSS files
Removes verbose documentation and examples from:
- flasher.ts (main orchestration class)
- flasher-plugin.ts (default plugin implementation)
- types.ts (type definitions)
- flasher.scss (default theme styles)
- mixins.scss (reusable SASS mixins)

Keeps essential type annotations and parameter documentation.
2026-01-16 00:27:42 +01:00
Younes ENNAJI 9239063159 Simplify PHPDoc comments in Stamp classes and PhpStorm meta files
Removes verbose documentation and comments from:
- Stamp classes (PriorityStamp, HopsStamp, DelayStamp, etc.)
- .phpstorm.meta.php files (Prime, Noty, Notyf, SweetAlert, Toastr)
- Factory and EventDispatcher classes
2026-01-16 00:23:16 +01:00
Younes ENNAJI 2e61a0539f Simplify PHPDoc comments in core Prime classes
Removes verbose documentation while preserving type annotations
for array parameters and PHPStan type definitions.
2026-01-16 00:20:39 +01:00
Younes ENNAJI 548044bd7f Simplify PHPDoc comments in Notification builder classes
Removes verbose documentation while preserving type annotations
for array parameters.
2026-01-16 00:17:52 +01:00
Younes ENNAJI 3f920f2c01 Simplify PHPDoc comments in Plugin, HTTP, Stamp, and Test constraint classes
Removes verbose documentation while preserving type annotations.
2026-01-16 00:07:42 +01:00
Younes ENNAJI 65e9125539 Simplify PHPDoc comments in helper functions and translation files
This commit simplifies PHPDoc comments in helper functions and Laravel
translation files by removing verbose documentation and keeping only
essential type annotations.

Changes include:
- Removed "Design pattern" mentions and example usage blocks
- Kept all @param and @phpstan-return type annotations
- Added missing @return annotations for PHPStan

Files modified:
- src/Prime/functions.php - Namespaced helper function
- src/Prime/helpers.php - Global helper function
- src/Laravel/Translation/lang/*/messages.php - 8 translation files (ar, de, en, es, fr, pt, ru, zh)
- src/Prime/Http/RequestInterface.php - Added @return annotation
- src/Prime/Http/ResponseInterface.php - Added @param annotation
- src/Prime/Notification/NotificationBuilderInterface.php - Added @param annotation

All changes pass PHPStan analysis and tests.
2026-01-15 23:48:33 +01:00
Younes ENNAJI a0873c0082 Simplify PHPDoc comments in Prime interfaces and traits
This commit simplifies PHPDoc comments across 26 files in the src/Prime/
directory by removing verbose documentation and keeping only essential type
annotations.

Changes include:
- Removed "Design pattern" mentions and redundant explanations
- Kept all @param and @return type annotations for PHPStan
- Maintained @phpstan-* annotations for type safety

Files modified:
- Exception classes (4 files)
- Http interfaces (5 files)
- Notification builders and interfaces (3 files)
- Plugin, Response, Storage, Template, Translation, and Test interfaces (14 files)

All changes pass PHPStan analysis and tests.
2026-01-15 23:40:19 +01:00
Younes ENNAJI 1c5b533126 Refactors Toastr implementation as a plugin 2026-01-15 23:26:32 +01:00
Younes ENNAJI f78824b8d9 Downgrades dependencies for broader compatibility 2026-01-15 23:15:58 +01:00
Younes ENNAJI e212c0157e Simplify PHPDoc comments in SweetAlert integration
Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
2026-01-15 23:12:29 +01:00
Younes ENNAJI fcf5c63f58 Improves documentation with parameter types 2026-01-15 22:56:11 +01:00
Younes ENNAJI ea164fde17 Simplify PHPDoc comments in Notyf integration
Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
2026-01-15 22:51:25 +01:00
Younes ENNAJI 611fda9c61 Simplify PHPDoc comments in Noty integration
Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
2026-01-15 22:35:01 +01:00
Younes ENNAJI 21348f384d Simplify PHPDoc comments in Laravel and Symfony integrations
Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
2026-01-15 22:22:20 +01:00
Younes ENNAJI 8f594698c7 Simplify PHPDoc comments in Prime classes
Remove verbose documentation pattern references and redundant
explanations, keeping only essential type annotations.
2026-01-15 21:21:11 +01:00
Younes ENNAJI d65300a0e9 Fix PHPStan errors and test failures, improve PHPDoc documentation ordering 2026-01-10 04:36:16 +01:00
Younes ENNAJI a002590d56 Fix incorrect examples in src/Prime/README.md
- Replace non-existent static Flasher:: calls with flash() helper function
2026-01-10 04:21:03 +01:00
Younes ENNAJI 5a4d46b46b Fix incorrect code examples in Prime adapter READMEs
- Replace non-existent static method calls with correct helper functions
- Update examples to use sweetalert(), notyf(), noty(), toastr() helpers
- Pass options as arrays instead of invalid fluent chaining
2026-01-10 04:19:17 +01:00
Younes ENNAJI 0232edc9bd Adds documentation files and improves package READMEs 2026-01-10 03:54:56 +01:00
Younes ENNAJI 1c5fbbba9a Refactors and improves documentation clarity 2026-01-10 02:16:28 +01:00
Younes ENNAJI 15da40c582 Updates flasher dependencies to version 2.4.0 2025-12-31 11:43:34 +01:00
Younes ENNAJI 8c77e96eb6 Upgrade yoeunes/regex-parser to v1.0 2025-12-31 11:38:59 +01:00
Younes ENNAJI 91ab4fa0f4 Updates Flasher dependencies to version 2.3.0
Updates all flasher dependencies in composer.json files and the main Flasher version constant to the new version 2.3.0.

This ensures that all packages are using the latest compatible version of Flasher.
2025-12-09 11:23:28 +01:00
Younes ENNAJI b08117853a Merge pull request #295 from php-flasher/regex-parser
Adds regex-parser library
2025-12-09 11:22:15 +01:00
Younes ENNAJI 2417333107 Adds regex-parser library
Adds the `yoeunes/regex-parser` library as a dependency.

This library will be used for advanced regular expression
validation and analysis within the application.

Also updates the .gitignore to ignore vendor directories
within the `src` directory, and adds `composer.lock` to it.
2025-12-09 11:20:01 +01:00
Younes ENNAJI 358ad11c4d Updates flasher dependencies to v2.2.2
Bumps all flasher dependencies to the latest version 2.2.2.

This ensures all packages are aligned with the latest features and bug fixes.
2025-12-08 12:53:33 +01:00
Younes ENNAJI a7823205ce chore: Bump @flasher package versions to 2.2.1 2025-11-10 17:00:32 +01:00
Younes ENNAJI 50d6acfa94 Merge pull request #294 from php-flasher/dev
add support for Symfony v8.0
2025-11-10 15:47:02 +01:00
Younes ENNAJI dd3c9f74f0 add support for Symfony v8.0
Bumps the php-flasher dependencies in composer.json and package.json files
to version 2.2.1 across all relevant packages.

This ensures the project utilizes the latest features,
bug fixes, and improvements available in the php-flasher library.
2025-11-10 15:43:32 +01:00
Younes ENNAJI fac5ca638a Merge pull request #293 from php-flasher/dev
dev
2025-11-10 15:42:44 +01:00
Younes ENNAJI 09199d8b56 Makes notification library default optional
Makes the 'default' option for the notification library optional.

This allows the system to function without a default library being explicitly set, providing more flexibility in configurations.
2025-11-10 15:41:13 +01:00
Younes ENNAJI d9c65d274b Enhances test workflow for dev Symfony versions
Updates the test workflow to correctly handle "-dev" Symfony versions.

Sets the composer minimum-stability to "dev" and prefers stable dependencies
when testing against development versions of Symfony. This ensures that the
latest dependencies are used during testing of development versions and also
runs update with --prefer-lowest only when not on a dev branch.
2025-11-10 15:38:04 +01:00
Younes ENNAJI bde270a483 Adds new test configurations
Introduces new test configurations for Symfony 8.0 with PHP 8.4 and Symfony 7.4 with PHP 8.2, ensuring broader compatibility and comprehensive testing coverage.
2025-11-10 15:35:22 +01:00
Younes ENNAJI c454fbc768 Updates flasher library dependencies.
Updates composer.lock and package-lock.json files to include latest versions of dependencies.
This change ensures compatibility with the latest features and security patches.
It also synchronizes front-end and back-end dependencies.
2025-11-10 15:32:54 +01:00
Younes ENNAJI 17a0e756d9 Updates devDependencies
Increases the versions of various development dependencies.

This change ensures the project benefits from the latest
features, bug fixes, and performance improvements offered by
these tools.
2025-11-10 15:32:45 +01:00
Younes ENNAJI 5ee7d58d7c Updates test matrix for PHP and Symfony
Updates the test matrix in the workflow configuration.

Adds PHP 8.5 support and adjusts Symfony versions to 7.3.
Also includes Laravel 12 support with corresponding PHP versions.
2025-11-10 15:32:31 +01:00
Younes ENNAJI e0b93784bd Allows Symfony 8 compatibility
Updates Symfony dependencies to allow compatibility
with version 8.

This ensures the application can be upgraded to
the latest Symfony version without breaking changes.
2025-11-10 15:32:24 +01:00
Younes ENNAJI 18663b3214 Updates Flasher to v2.2.0
Updates the flasher dependencies in composer.json files and package.json files to version 2.2.0.

Updates the Flasher version constant in the Prime Flasher class.
2025-11-10 15:20:21 +01:00
Younes ENNAJI 7cf3de856e Merge pull request #286 from php-flasher/dev
add phpflasher configuration article
2025-03-30 07:41:23 +00:00
Younes ENNAJI e0cbc53c76 add phpflasher configuration article 2025-03-30 07:39:24 +00:00
Younes ENNAJI b5bf2325be Merge pull request #285 from php-flasher/dev
add common phpflasher implementation mistakes blog post
2025-03-30 07:18:22 +00:00
Younes ENNAJI e3158290f3 add common phpflasher implementation mistakes blog post 2025-03-30 07:15:03 +00:00
Younes ENNAJI 4070978256 Merge pull request #284 from php-flasher/dev
add blog section
2025-03-30 06:42:07 +00:00
Younes ENNAJI d96a953488 update blog icon 2025-03-30 06:39:58 +00:00
Younes ENNAJI ea8391eb6b update laravel first blog article 2025-03-30 06:03:55 +00:00
Younes ENNAJI 3fc0d83dd6 improve laravel first blog article 2025-03-30 05:22:04 +00:00
Younes ENNAJI 1cb12c819b add blog articles 2025-03-30 04:10:13 +00:00
Younes ENNAJI f9d79f4589 Merge pull request #283 from php-flasher/dev
update themes documentataion
2025-03-30 03:11:35 +00:00
Younes ENNAJI a3ce6a5262 update themes documentataion 2025-03-30 02:56:43 +00:00
Younes ENNAJI ff2cc1dc65 Merge pull request #282 from php-flasher/dev
update docs home page
2025-03-30 02:33:13 +00:00
Younes ENNAJI 2cef5b98b2 fix phpstan errors 2025-03-30 02:27:41 +00:00
Younes ENNAJI d7ecfb69a9 revert laravel install command 2025-03-30 02:24:57 +00:00
Younes ENNAJI 4909da7ba8 update docs home page 2025-03-30 02:22:45 +00:00
Younes ENNAJI f854ed4315 Wip 2025-03-28 13:05:26 +00:00
Younes ENNAJI 424617103f Wip 2025-03-28 03:37:07 +00:00
Younes ENNAJI 8dd56001db Wip 2025-03-28 03:30:26 +00:00
Younes ENNAJI e1ae7e215c update laravel intaller command 2025-03-28 03:11:45 +00:00
Younes ENNAJI 730c13c447 update laravel theme configuration 2025-03-28 02:16:33 +00:00
Younes ENNAJI 12b0842cdd update symfony and laravel demos 2025-03-28 02:06:37 +00:00
Younes ENNAJI 93ca18a67b add themes to symfony configuration 2025-03-27 21:23:53 +00:00
Younes ENNAJI 1022991877 upgrade dependencies 2025-03-27 20:06:59 +00:00
Younes ENNAJI 4345f2aa91 Merge pull request #280 from php-flasher/dev
update themes border radius
2025-03-27 02:13:45 +00:00
Younes ENNAJI 56dd1cea4c update themes border radius 2025-03-27 02:12:06 +00:00
Younes ENNAJI 315a54eb43 Merge pull request #279 from php-flasher/dev
update themes icon
2025-03-26 23:37:35 +00:00
Younes ENNAJI 82f2c69d65 update themes icon 2025-03-26 23:34:57 +00:00
Younes ENNAJI 91993e438b Merge pull request #278 from php-flasher/dev
fix flash notifications width
2025-03-26 23:08:16 +00:00
Younes ENNAJI 977c645c45 upgrade dependencies 2025-03-26 23:06:45 +00:00
Younes ENNAJI 7a083efea0 fix flash notifications width 2025-03-26 23:04:01 +00:00
Younes ENNAJI 5448e54f77 Merge pull request #277 from php-flasher/dev
update livewire and inertiajs documentation
2025-03-26 21:34:02 +00:00
Younes ENNAJI c92105377d update livewire and inertiajs documentation 2025-03-26 21:32:32 +00:00
Younes ENNAJI 5cf68f2e34 Merge pull request #276 from php-flasher/dev
update laravel documentation page
2025-03-26 04:18:09 +00:00
Younes ENNAJI 68d7bfadce upgrade dependencies 2025-03-26 04:16:45 +00:00
Younes ENNAJI ca6e75ba9d update laravel documentation page 2025-03-26 04:06:45 +00:00
Younes ENNAJI 3407e309da Merge pull request #275 from php-flasher/dev
upgrade dependencies
2025-03-26 02:24:27 +00:00
Younes ENNAJI dce23cc288 upgrade dependencies 2025-03-26 02:22:33 +00:00
Younes ENNAJI c1cc26379d Merge pull request #274 from php-flasher/dev
update symfony documentation page
2025-03-26 02:17:02 +00:00
Younes ENNAJI cc0658b3fe update symfony documentation page 2025-03-26 02:15:21 +00:00
Younes ENNAJI a2abd9455a upgrade dependencies 2025-03-23 20:39:06 +00:00
Younes ENNAJI 8d160a6659 Merge pull request #273 from php-flasher/dev
fix flasher rtl support
2025-03-23 14:57:02 +00:00
Younes ENNAJI 8e6f0e8c88 fix flasher rtl support 2025-03-23 14:54:56 +00:00
Younes ENNAJI 4471ae6135 Merge pull request #272 from php-flasher/dev
Wip
2025-03-23 14:17:48 +00:00
Younes ENNAJI 6d162c7f95 Wip 2025-03-23 14:16:10 +00:00
Younes ENNAJI 4f8e179fb8 Merge pull request #271 from php-flasher/dev
dev
2025-03-23 13:35:11 +00:00
Younes ENNAJI 53d7795292 fix flasher options 2025-03-23 13:31:29 +00:00
Younes ENNAJI eec4b88872 Wip 2025-03-22 20:06:30 +00:00
Younes ENNAJI 37ac39e2ec Merge pull request #270 from php-flasher/dev
upgrade dependencies
2025-03-19 00:33:38 +00:00
Younes ENNAJI ef05362467 upgrade dependencies 2025-03-19 00:26:48 +00:00
Younes ENNAJI 05229b0b42 Merge pull request #269 from php-flasher/dev
update inertiajs documentation page
2025-03-19 00:09:17 +00:00
Younes ENNAJI 28ca9d1370 update inertiajs documentation page 2025-03-19 00:07:22 +00:00
Younes ENNAJI 03c358ce44 Merge pull request #268 from php-flasher/dev
update livewire documentation page
2025-03-18 23:37:02 +00:00
Younes ENNAJI 0edf9cc711 update livewire documentation page 2025-03-18 23:35:22 +00:00
Younes ENNAJI 04685fad82 Merge pull request #267 from php-flasher/dev
update laravel documentation page
2025-03-18 22:57:11 +00:00
Younes ENNAJI 2bfc7ec5d4 update laravel documentation page 2025-03-18 22:55:25 +00:00
Younes ENNAJI 4ef7a12484 Merge pull request #266 from php-flasher/dev
update symfony doc page
2025-03-18 22:41:16 +00:00
Younes ENNAJI 4f09f06e07 update symfony implementation 2025-03-18 22:38:47 +00:00
Younes ENNAJI 00c8273272 Wip 2025-03-18 22:22:32 +00:00
Younes ENNAJI 2a7657b09c Wip 2025-03-18 22:20:10 +00:00
Younes ENNAJI 24d3c90752 Wip 2025-03-18 22:08:01 +00:00
Younes ENNAJI 5f4a8beaa3 Wip 2025-03-18 22:02:54 +00:00
Younes ENNAJI 70e08fc913 Wip 2025-03-18 21:56:52 +00:00
Younes ENNAJI f0a11e6600 Wip 2025-03-18 21:39:09 +00:00
Younes ENNAJI 66704f6368 Wip 2025-03-18 20:37:11 +00:00
Younes ENNAJI c18fbd1124 Wip 2025-03-17 05:56:34 +00:00
Younes ENNAJI 1899de104f Merge pull request #264 from php-flasher/2.x-dev
update docs
2025-03-17 04:54:51 +00:00
Younes ENNAJI cd3ef599e1 Wip 2025-03-17 04:53:19 +00:00
Younes ENNAJI eb537353c2 Wip 2025-03-17 04:48:54 +00:00
Younes ENNAJI 4b1b747abe Merge pull request #263 from php-flasher/2.x-dev
update the docs
2025-03-17 03:59:52 +00:00
Younes ENNAJI 56109d9ed0 Wip 2025-03-17 03:57:41 +00:00
Younes ENNAJI a80c04bb19 Merge pull request #262 from php-flasher/2.x-dev
update the docs
2025-03-17 03:38:39 +00:00
Younes ENNAJI cd2c28ff11 Wip 2025-03-17 03:37:53 +00:00
Younes ENNAJI 3c79064e8d Wip 2025-03-17 03:29:19 +00:00
Younes ENNAJI 1e87cf5380 Wip 2025-03-17 03:26:27 +00:00
Younes ENNAJI 6dbef1d819 Merge pull request #261 from php-flasher/2.x-dev
update the docs
2025-03-17 03:04:54 +00:00
Younes ENNAJI c03c28533b update the docs 2025-03-17 03:03:07 +00:00
Younes ENNAJI 59a2e502c8 Merge pull request #260 from php-flasher/2.x-dev
make sidebar scrollable
2025-03-16 01:44:50 +00:00
Younes ENNAJI 3cb6ea7ed7 make sidebar scrollable 2025-03-16 01:43:23 +00:00
Younes ENNAJI bb83f55711 Merge pull request #259 from php-flasher/2.x-dev
fix flasher studio select theme
2025-03-16 01:17:20 +00:00
Younes ENNAJI ffbb8e5053 Wip 2025-03-16 01:15:32 +00:00
Younes ENNAJI aa82f13e08 Wip 2025-03-16 01:01:56 +00:00
Younes ENNAJI e8cd7a4595 Merge pull request #258 from php-flasher/2.x-dev
update docs demo
2025-03-13 08:15:13 +00:00
Younes ENNAJI 568e3583b9 Wip 2025-03-13 08:13:26 +00:00
Younes ENNAJI 113e609b55 Wip 2025-03-13 07:41:10 +00:00
Younes ENNAJI edd684cd7c Wip 2025-03-13 07:09:14 +00:00
Younes ENNAJI 302de1f212 Wip 2025-03-13 06:26:03 +00:00
Younes ENNAJI d0bf147008 Wip 2025-03-13 06:20:41 +00:00
Younes ENNAJI 5d7145ff65 Wip 2025-03-13 06:05:44 +00:00
Younes ENNAJI 2faa760217 Wip 2025-03-13 05:39:10 +00:00
Younes ENNAJI eb0d0fbb6d Wip 2025-03-12 21:27:05 +00:00
Younes ENNAJI ccaab5b7e5 Wip 2025-03-12 00:17:23 +00:00
Younes ENNAJI 2ecfdef874 Wip 2025-03-12 00:17:01 +00:00
Younes ENNAJI 49e287f987 Wip 2025-03-11 23:56:12 +00:00
Younes ENNAJI 60c19498e3 Wip 2025-03-11 23:52:20 +00:00
Younes ENNAJI 9e59dc83fe Wip 2025-03-11 23:34:41 +00:00
Younes ENNAJI f39aaa7de5 Wip 2025-03-11 23:12:37 +00:00
Younes ENNAJI 68ae6dc9cb Wip 2025-03-11 22:47:45 +00:00
Younes ENNAJI 356672c634 Wip 2025-03-11 22:44:01 +00:00
Younes ENNAJI eff520316e Wip 2025-03-11 22:33:12 +00:00
Younes ENNAJI fa3012a711 Wip 2025-03-11 22:02:22 +00:00
Younes ENNAJI 27024721ea Merge pull request #257 from php-flasher/2.x-dev
2.x dev
2025-03-11 07:45:13 +00:00
Younes ENNAJI 50a8b334be Wip 2025-03-11 07:43:34 +00:00
Younes ENNAJI 402b443f17 Wip 2025-03-11 07:37:11 +00:00
Younes ENNAJI 220284a892 Wip 2025-03-11 07:27:52 +00:00
Younes ENNAJI 5d81a1f0f1 Wip 2025-03-11 07:23:15 +00:00
Younes ENNAJI 5b8e6c64ff Wip 2025-03-11 07:00:44 +00:00
Younes ENNAJI 4b2edce0e4 Wip 2025-03-11 06:55:49 +00:00
Younes ENNAJI ff32c639ed Wip 2025-03-11 06:48:42 +00:00
Younes ENNAJI bad7544cf8 Wip 2025-03-11 06:36:08 +00:00
Younes ENNAJI a05d72d945 Wip 2025-03-11 06:27:42 +00:00
Younes ENNAJI 4672332012 Wip 2025-03-11 06:19:57 +00:00
Younes ENNAJI b4682b462a Wip 2025-03-11 06:14:16 +00:00
Younes ENNAJI 6ea30eb6d4 Wip 2025-03-11 06:09:49 +00:00
Younes ENNAJI b451da2141 Wip 2025-03-11 06:04:59 +00:00
Younes ENNAJI b5517bedcf Wip 2025-03-11 06:02:25 +00:00
Younes ENNAJI f411c6e3cb Wip 2025-03-11 05:53:39 +00:00
Younes ENNAJI 5a769cffc8 Wip 2025-03-11 05:43:53 +00:00
Younes ENNAJI dae72342b4 Merge pull request #256 from php-flasher/2.x-dev
update the themes in the docs
2025-03-11 05:10:17 +00:00
Younes ENNAJI ea47e9043b update the themes in the docs 2025-03-11 05:07:01 +00:00
Younes ENNAJI 3b42b1a862 Merge pull request #255 from php-flasher/2.x-dev
improve themes docs
2025-03-10 04:29:14 +00:00
Younes ENNAJI e1d7ae94ee Wip 2025-03-10 04:23:31 +00:00
Younes ENNAJI 1945e03640 Wip 2025-03-10 04:16:03 +00:00
Younes ENNAJI 00eadbe207 Wip 2025-03-10 04:07:11 +00:00
Younes ENNAJI ee51cb775d Wip 2025-03-10 03:45:48 +00:00
Younes ENNAJI 0cf5e8f70d Wip 2025-03-10 03:43:57 +00:00
Younes ENNAJI 09d60d496b Wip 2025-03-10 03:31:51 +00:00
Younes ENNAJI b48d52d3bc Wip 2025-03-10 03:26:41 +00:00
Younes ENNAJI 9849c63d99 Wip 2025-03-10 03:23:41 +00:00
Younes ENNAJI e332c78512 Wip 2025-03-10 03:23:08 +00:00
Younes ENNAJI 0a148c10c2 Wip 2025-03-10 03:20:04 +00:00
Younes ENNAJI cf03403459 Wip 2025-03-10 03:10:30 +00:00
Younes ENNAJI a7491972e2 Wip 2025-03-10 02:58:26 +00:00
Younes ENNAJI 21c5cbd624 Wip 2025-03-10 02:18:48 +00:00
Younes ENNAJI 664c841e8d Wip 2025-03-10 02:07:34 +00:00
Younes ENNAJI 5f6837ea31 Merge pull request #254 from php-flasher/2.x-dev
add themes to the docs
2025-03-10 00:55:05 +00:00
Younes ENNAJI 88ae4cfc4c add themes to the docs 2025-03-10 00:54:37 +00:00
Younes ENNAJI 042b314784 Merge pull request #253 from php-flasher/2.x-dev
update docs
2025-03-09 22:43:17 +00:00
Younes ENNAJI a43cbd21ce compile assets 2025-03-09 22:41:44 +00:00
Younes ENNAJI 39ca7b3e83 update docs 2025-03-09 22:15:50 +00:00
Younes ENNAJI 9d0bf7dca7 update docs 2025-03-09 20:23:16 +00:00
Younes ENNAJI 8913b6e0e2 Merge pull request #252 from php-flasher/2.x-dev
docs: enhance code documentation with comprehensive PHPDoc comments
2025-03-09 07:58:24 +00:00
Younes ENNAJI 0631e5953e docs: enhance code documentation with comprehensive PHPDoc comments 2025-03-09 07:34:22 +00:00
Younes ENNAJI bdc7e85380 Update README.md 2025-03-08 22:44:33 +00:00
Younes ENNAJI be071be46b Merge pull request #251 from php-flasher/2.x-dev
improve rollup configuration
2025-03-08 18:13:32 +00:00
Younes ENNAJI b047c9016a improve rollup configuration 2025-03-08 18:05:43 +00:00
Younes ENNAJI 8fa6054ada improve package.json 2025-03-08 12:31:44 +00:00
Younes ENNAJI c062ae7cf5 Merge pull request #250 from php-flasher/themes
add themes doc pages
2025-03-08 12:04:37 +00:00
Younes ENNAJI 1c8b0bf081 add themes doc pages 2025-03-08 12:03:23 +00:00
Younes ENNAJI 5eddda90c5 Merge pull request #249 from php-flasher/themes
update symfony demo
2025-03-08 12:03:07 +00:00
Younes ENNAJI 46ed2f8468 update symfony demo 2025-03-08 12:02:07 +00:00
Younes ENNAJI ecabbee799 Merge pull request #248 from php-flasher/themes
compile assets
2025-03-08 12:01:41 +00:00
Younes ENNAJI 568421c66b compile assets 2025-03-08 12:01:13 +00:00
Younes ENNAJI ed4f6165c6 Merge pull request #247 from php-flasher/themes
refactor toastr plugin and improve code documentation
2025-03-08 12:00:19 +00:00
Younes ENNAJI 02f5fc0460 refactor toastr plugin and improve code documentation 2025-03-08 11:59:56 +00:00
Younes ENNAJI a9aedc1236 Merge pull request #246 from php-flasher/themes
refactor sweealert plugin and improve code documentation
2025-03-08 11:59:32 +00:00
Younes ENNAJI b4c9df4549 refactor sweealert plugin and improve code documentation 2025-03-08 11:59:01 +00:00
Younes ENNAJI 4d0b258ed7 Merge pull request #245 from php-flasher/themes
refactor notyf plugin and improve code documentation
2025-03-08 11:58:39 +00:00
Younes ENNAJI 71b21855c1 refactor notyf plugin and improve code documentation 2025-03-08 11:58:00 +00:00
Younes ENNAJI f43878571b Merge pull request #244 from php-flasher/themes
refactor noty plugin and improve code documentation
2025-03-08 11:57:27 +00:00
Younes ENNAJI fbe6238009 refactor noty plugin and improve code documentation 2025-03-08 11:56:48 +00:00
Younes ENNAJI 2ed68db245 Merge pull request #243 from php-flasher/themes
refactor assets and improve code documentation
2025-03-08 11:55:05 +00:00
Younes ENNAJI c81a99c755 refactor assets and improve code documentation 2025-03-08 11:54:29 +00:00
Younes ENNAJI 3563e195bc Merge pull request #242 from php-flasher/themes
add slack theme
2025-03-08 11:53:26 +00:00
Younes ENNAJI 47c7cd9ad8 add slack theme 2025-03-08 11:52:59 +00:00
Younes ENNAJI 6b9cf6b90c Merge pull request #241 from php-flasher/themes
add sapphire theme
2025-03-08 11:49:15 +00:00
Younes ENNAJI 8d5cadcf66 add sapphire theme 2025-03-08 11:48:51 +00:00
Younes ENNAJI 3c2b4c5546 Merge pull request #240 from php-flasher/themes
add ruby theme
2025-03-08 11:48:34 +00:00
Younes ENNAJI 12c1ca36c7 add ruby theme 2025-03-08 11:48:08 +00:00
Younes ENNAJI 9355c6a590 Merge pull request #239 from php-flasher/themes
add onyx theme
2025-03-08 11:47:56 +00:00
Younes ENNAJI 88e0de8417 add onyx theme 2025-03-08 11:47:26 +00:00
Younes ENNAJI 8aa67d017c Merge pull request #238 from php-flasher/themes
add neon theme
2025-03-08 11:47:05 +00:00
Younes ENNAJI 6b6204d264 add neon theme 2025-03-08 11:46:44 +00:00
Younes ENNAJI 50142d062e Merge pull request #237 from php-flasher/themes
add minimal theme
2025-03-08 11:46:21 +00:00
Younes ENNAJI b18069893b add minimal theme 2025-03-08 11:45:22 +00:00
Younes ENNAJI fa414e7ae5 Merge pull request #236 from php-flasher/themes
add material theme
2025-03-08 11:45:07 +00:00
Younes ENNAJI 1d48abf3b7 add material theme 2025-03-08 11:44:39 +00:00
Younes ENNAJI 43626a656b Merge pull request #235 from php-flasher/themes
add jade theme
2025-03-08 11:44:22 +00:00
Younes ENNAJI c5a6e20552 add jade theme 2025-03-08 11:44:00 +00:00
Younes ENNAJI b02a55e6a6 Merge pull request #234 from php-flasher/themes
add ios theme
2025-03-08 11:43:44 +00:00
Younes ENNAJI 176b616773 add ios theme 2025-03-08 11:43:03 +00:00
Younes ENNAJI 783822826c Merge pull request #233 from php-flasher/themes
add google theme
2025-03-08 11:42:40 +00:00
Younes ENNAJI 25ce040d00 add google theme 2025-03-08 11:42:14 +00:00
Younes ENNAJI 6ae8779f8f Merge pull request #232 from php-flasher/themes
refactor flasher theme
2025-03-08 11:42:00 +00:00
Younes ENNAJI 7fafa513a2 refactor flasher theme 2025-03-08 11:41:35 +00:00
Younes ENNAJI d20a7de308 Merge pull request #231 from php-flasher/themes
add facebook theme
2025-03-08 11:41:16 +00:00
Younes ENNAJI e1a6a85c76 add facebook theme 2025-03-08 11:40:49 +00:00
Younes ENNAJI 565438eb9f Merge pull request #230 from php-flasher/themes
add emerald theme
2025-03-08 11:40:37 +00:00
Younes ENNAJI efea45f892 add emerald theme 2025-03-08 11:40:12 +00:00
Younes ENNAJI f0bf6c61ab Merge pull request #229 from php-flasher/themes
add crystal theme
2025-03-08 11:39:55 +00:00
Younes ENNAJI f3b6183449 Merge pull request #228 from php-flasher/themes
add aurora theme
2025-03-08 11:39:20 +00:00
Younes ENNAJI d10e93b56f add crystal theme 2025-03-08 11:39:11 +00:00
Younes ENNAJI b88afe29b9 Merge pull request #227 from php-flasher/themes
add amber theme
2025-03-08 11:38:35 +00:00
Younes ENNAJI 13c72935ba add aurora theme 2025-03-08 11:38:29 +00:00
Younes ENNAJI 7fee53a92a Merge pull request #226 from php-flasher/themes
add amazon theme
2025-03-08 11:37:50 +00:00
Younes ENNAJI 8ee2442b9c add amber theme 2025-03-08 11:37:24 +00:00
Younes ENNAJI 99484054f9 Merge pull request #225 from php-flasher/themes
chore: prepare flasher to add themes + improve code documentation
2025-03-08 11:36:28 +00:00
Younes ENNAJI 3be978fc53 add amazon theme 2025-03-08 11:36:10 +00:00
Younes ENNAJI fe773941e4 chore: prepare flasher to add themes + improve code documentation 2025-03-08 11:34:23 +00:00
Younes ENNAJI 63fc0d9640 Merge pull request #224 from php-flasher/themes
chore: improve build scripts
2025-03-08 11:31:06 +00:00
Younes ENNAJI 4a83adb4f3 upgrade dependencies 2025-03-08 11:30:53 +00:00
Younes ENNAJI 3d66271ac8 update build scripts 2025-03-08 11:29:23 +00:00
Younes ENNAJI 29cea6f9d3 Merge pull request #223 from php-flasher/laravel-12
add tests for laravel 12
2025-02-26 09:14:46 +00:00
Younes ENNAJI 47ad265fb4 test laravel v12 with phpunit v11.5 2025-02-26 09:12:41 +00:00
Younes ENNAJI 99371ede9e add tests for laravel 12 2025-02-26 09:10:27 +00:00
Younes ENNAJI 6e8884d1d1 Merge pull request #222 from php-flasher/docs
Update github action file
2025-02-23 14:08:18 +00:00
Younes ENNAJI 2c95b4e6ba Wip 2025-02-23 14:05:54 +00:00
Younes ENNAJI 4b3fb8f584 Wip 2025-02-23 14:04:49 +00:00
Younes ENNAJI 40e9e83e3a Wip 2025-02-23 14:03:53 +00:00
Younes ENNAJI 59ead448e9 Wip 2025-02-23 14:02:59 +00:00
Younes ENNAJI 1397e0bb59 Merge pull request #221 from php-flasher/docs
test laravel 11 on php v8.4
2025-02-23 13:54:51 +00:00
Younes ENNAJI 56e610d6b0 test laravel 11 on php v8.4 2025-02-23 13:53:44 +00:00
Younes ENNAJI ba3dd5fe7f Merge pull request #220 from php-flasher/docs
docs
2025-02-23 13:51:02 +00:00
Younes ENNAJI d9699b7fe2 Wip 2025-02-23 13:49:16 +00:00
Younes ENNAJI ed742de9db update documentation head.html 2025-02-23 12:09:13 +00:00
Younes ENNAJI 8fdc809118 remove progress bar 2025-02-21 22:10:00 +01:00
Younes ENNAJI dfdd28b869 Wip 2025-02-21 22:01:54 +01:00
Younes ENNAJI a539235d1a update bin/release file 2025-02-21 21:58:05 +01:00
Younes ENNAJI df08f255d3 Merge pull request #219 from php-flasher/split-script
split script
2025-02-21 21:49:50 +01:00
Younes ENNAJI 31d5cb6e8e Wip 2025-02-21 21:47:42 +01:00
Younes ENNAJI d2750dee8a Wip 2025-02-21 21:46:14 +01:00
Younes ENNAJI 43553ad8ad Wip 2025-02-21 21:43:50 +01:00
Younes ENNAJI 48b2c0fae0 Wip 2025-02-21 21:27:56 +01:00
Younes ENNAJI 0988fdca9d improve the bin/split script 2025-02-21 21:20:40 +01:00
Younes ENNAJI a24159c3f0 Merge pull request #218 from php-flasher/scripts
bump flasher to v2.1.6
2025-02-21 21:07:26 +01:00
Younes ENNAJI f9c9ecf18e bump flasher to v2.1.6 2025-02-21 21:05:00 +01:00
Younes ENNAJI f93a908d25 update task file and bump script 2025-02-21 21:02:15 +01:00
Younes ENNAJI 1237b5f552 Wip 2025-02-21 20:44:12 +01:00
Younes ENNAJI bce970de5f Merge pull request #217 from php-flasher/scripts
update bin scripts
2025-02-21 20:30:33 +01:00
Younes ENNAJI 8809cd9ddf update taskfile 2025-02-21 20:28:11 +01:00
Younes ENNAJI fdbc5fd270 Wip 2025-02-21 20:12:25 +01:00
Younes ENNAJI 3d5d8d3c9a Wip 2025-02-21 19:43:37 +01:00
Younes ENNAJI 9d1bbc5d9a Wip 2025-02-21 19:35:48 +01:00
824 changed files with 76260 additions and 17560 deletions
+1
View File
@@ -1,3 +1,4 @@
> 1%
last 2 versions
not dead
not IE 11
-1
View File
@@ -1,2 +1 @@
github: yoeunes
custom: https://www.paypal.com/paypalme/yoeunes
+22
View File
@@ -5,7 +5,28 @@ on:
types: [ published ]
jobs:
test:
runs-on: ubuntu-latest
name: Run Tests Before Publishing
steps:
- name: 📥 Checkout Code
uses: actions/checkout@v4
- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: 📦 Install Dependencies
run: npm ci
- name: ✅ Run Tests
run: npm run test
publish-prime:
needs: test
runs-on: ubuntu-latest
defaults:
run:
@@ -30,6 +51,7 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-plugin:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
+76 -14
View File
@@ -1,4 +1,4 @@
name: 🧪 Run Tests & 🛠️ Static Analysis
name: qa
on:
push:
@@ -10,6 +10,29 @@ on:
- cron: '0 0 * * *' # Daily at midnight
jobs:
javascript-tests:
runs-on: ubuntu-latest
name: JavaScript Tests
steps:
- name: 📥 Checkout Code
uses: actions/checkout@v4
- name: 🔧 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: 📦 Install Dependencies
run: npm ci
- name: ✅ Run Tests
run: npm run test
- name: 📊 Run Coverage
run: npm run test:coverage
static-analysis:
runs-on: ubuntu-latest
strategy:
@@ -17,7 +40,7 @@ jobs:
matrix:
php: [ 8.2 ]
name: 🐘 PHP ${{ matrix.php }} Static Analysis
name: php v${{ matrix.php }}
steps:
- name: 📥 Checkout Code
@@ -39,18 +62,19 @@ jobs:
- name: 📦 Install Dependencies
run: |
composer config --global allow-plugins true
composer install
composer install --no-interaction --prefer-dist --optimize-autoloader
- name: 🧹 Run PHP CS Fixer (Code Style)
run: vendor/bin/php-cs-fixer fix --dry-run
run: vendor/bin/php-cs-fixer fix --dry-run --diff
- name: 🔍 Run PHPStan (Static Analysis)
run: vendor/bin/phpstan analyse
run: vendor/bin/phpstan analyse --no-progress
- name: 🚀 Run PHPLint (Syntax Check)
run: vendor/bin/phplint
prime-test:
needs: static-analysis
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -60,7 +84,7 @@ jobs:
- { php: 8.3, phpunit: 10.5.* }
- { php: 8.2, phpunit: 10.5.* }
name: 🐘 PHP ${{ matrix.php }}
name: php v${{ matrix.php }}
steps:
- name: 📥 Checkout code
@@ -84,7 +108,7 @@ jobs:
sed -i '/"require": {/,/},/d; /"require-dev": {/,/},/d' composer.json
composer config --global allow-plugins true
composer require "phpunit/phpunit:${{ matrix.phpunit }}" "mockery/mockery" "psr/container" --no-interaction --no-update
composer update --prefer-lowest -W
composer update --prefer-lowest -W --no-interaction --prefer-dist --optimize-autoloader
- name: ✅ Execute tests
run: vendor/bin/phpunit --testsuite prime
@@ -95,13 +119,17 @@ jobs:
fail-fast: false
matrix:
include:
- { symfony: 7.2.*, php: 8.4, phpunit: 10.5.* }
- { symfony: 7.2.*, php: 8.3, phpunit: 10.5.* }
- { symfony: 8.0.x-dev, php: 8.4, phpunit: 10.5.* }
- { symfony: 7.4.x-dev, php: 8.2, phpunit: 10.5.* }
- { symfony: 7.3.*, php: 8.5, phpunit: 10.5.* }
- { symfony: 7.3.*, php: 8.4, phpunit: 10.5.* }
- { symfony: 7.3.*, php: 8.3, phpunit: 10.5.* }
- { symfony: 7.3.*, php: 8.2, phpunit: 10.5.* }
- { symfony: 7.2.*, php: 8.2, phpunit: 10.5.* }
- { symfony: 7.1.*, php: 8.2, phpunit: 10.5.* }
- { symfony: 7.0.*, php: 8.2, phpunit: 10.5.* }
name: 🌉 Symfony ${{ matrix.symfony }} PHP ${{ matrix.php }}
name: symfony v${{ matrix.symfony }} x php v${{ matrix.php }}
steps:
- name: 📥 Checkout code
@@ -125,8 +153,22 @@ jobs:
sed -i '/"require": {/,/},/d; /"require-dev": {/,/},/d' composer.json
composer config --global allow-plugins true
composer config extra.symfony.require "${{ matrix.symfony }}"
if [[ "${{ matrix.symfony }}" == *"-dev"* ]]; then
echo "Setting minimum-stability to dev for ${{ matrix.symfony }}"
composer config minimum-stability "dev"
composer config prefer-stable true
fi
composer require "symfony/config:${{ matrix.symfony }}" "symfony/console:${{ matrix.symfony }}" "symfony/dependency-injection:${{ matrix.symfony }}" "symfony/framework-bundle:${{ matrix.symfony }}" "symfony/http-kernel:${{ matrix.symfony }}" "symfony/translation:${{ matrix.symfony }}" "symfony/twig-bundle:${{ matrix.symfony }}" "phpunit/phpunit:${{ matrix.phpunit }}" "mockery/mockery" "psr/container" "monolog/monolog" --no-interaction --no-update
composer update --prefer-lowest -W
if [[ "${{ matrix.symfony }}" == *"-dev"* ]]; then
echo "Running composer update (latest dependencies)"
composer update -W --no-interaction --prefer-dist --optimize-autoloader
else
echo "Running composer update --prefer-lowest"
composer update --prefer-lowest -W --no-interaction --prefer-dist --optimize-autoloader
fi
- name: ✅ Execute tests
run: vendor/bin/phpunit --testsuite symfony
@@ -137,10 +179,16 @@ jobs:
fail-fast: false
matrix:
include:
- { laravel: 11.*, testbench: 9.*, php: 8.3, phpunit: 10.5.* }
- { laravel: 13.x-dev, testbench: 11.x-dev, php: 8.5, phpunit: 11.5.* }
- { laravel: 13.x-dev, testbench: 11.x-dev, php: 8.4, phpunit: 11.5.* }
- { laravel: 13.x-dev, testbench: 11.x-dev, php: 8.3, phpunit: 11.5.* }
- { laravel: 12.*, testbench: 10.*, php: 8.5, phpunit: 11.5.* }
- { laravel: 12.*, testbench: 10.*, php: 8.4, phpunit: 11.5.* }
- { laravel: 12.*, testbench: 10.*, php: 8.3, phpunit: 11.5.* }
- { laravel: 12.*, testbench: 10.*, php: 8.2, phpunit: 11.5.* }
- { laravel: 11.*, testbench: 9.*, php: 8.2, phpunit: 10.5.* }
name: 🏗 Laravel ${{ matrix.laravel }} PHP ${{ matrix.php }}
name: laravel v${{ matrix.laravel }} x php v${{ matrix.php }}
steps:
- name: 📥 Checkout code
@@ -164,8 +212,22 @@ jobs:
run: |
sed -i '/\"require\": {/,/},/d; /\"require-dev\": {/,/},/d' composer.json
composer config --global allow-plugins true
if [[ "${{ matrix.laravel }}" == *"-dev"* ]]; then
echo "Setting minimum-stability to dev for ${{ matrix.laravel }}"
composer config minimum-stability "dev"
composer config prefer-stable true
fi
composer require "laravel/framework:${{ matrix.laravel }}" "phpunit/phpunit:${{ matrix.phpunit }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
composer update --prefer-lowest -W
if [[ "${{ matrix.laravel }}" == *"-dev"* ]]; then
echo "Running composer update (latest dependencies)"
composer update -W --no-interaction --prefer-dist --optimize-autoloader
else
echo "Running composer update --prefer-lowest"
composer update --prefer-lowest -W --no-interaction --prefer-dist --optimize-autoloader
fi
- name: ✅ Execute tests
run: vendor/bin/phpunit --testsuite laravel
+8
View File
@@ -4,6 +4,8 @@
/vendor/
/node_modules/
/coverage/
/.cache/php-cs-fixer/
/.cache/phplint/
/.cache/phpstan/
@@ -14,6 +16,12 @@
/taskfile.yml
/phpstan.neon
/demo/laravel/node_modules/
/demo/symfony/node_modules/
/npm-debug.log
/tests/Symfony/Fixtures/project/public/vendor/
src/**/vendor/
src/**/composer.lock
+4 -1
View File
@@ -1,4 +1,7 @@
{
"upgrade": true,
"target": "semver"
"target": "semver",
"format": "group",
"color": true,
"root": true
}
-2
View File
@@ -1,2 +0,0 @@
ignore-workspace-root-check=true
shell-emulator=true
-1
View File
@@ -1,2 +1 @@
github: yoeunes
custom: https://www.paypal.com/paypalme/yoeunes
+57 -1
View File
@@ -1,6 +1,62 @@
# CHANGELOG for 2.x
## [Unreleased](https://github.com/php-flasher/php-flasher/compare/v2.1.4...2.x)
## [Unreleased](https://github.com/php-flasher/php-flasher/compare/v2.5.1...2.x)
## [v2.5.1](https://github.com/php-flasher/php-flasher/compare/v2.5.0...v2.5.1) - 2026-03-28
### Fixed
* fix [Laravel] Fix Laravel 13 JSON session serialization compatibility - StorageManager now handles both array and object formats returned by session
## [v2.5.0](https://github.com/php-flasher/php-flasher/compare/v2.1.4...v2.5.0) - 2026-03-07
### Added
* 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
* 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`
- [Notyf] Dispatch events: `flasher:notyf:click`, `flasher:notyf:dismiss`
- [Themes] Dispatch events: `flasher:theme:click` (generic) and `flasher:theme:{name}:click` (specific)
- [Laravel] Add LivewireListener classes for all adapters and themes to enable Livewire event handling
* feature [Flasher] Add 16 notification themes: Amazon, Amber, Jade, Crystal, and more
* feature [DX] Add `@method` annotations to FlasherInterface and NotificationFactoryInterface for better IDE autocompletion
* feature [DX] Add Type::all() and Type::isValid() helper methods with PHPStan type narrowing
* feature [DX] Add `@throws` annotations to FlasherContainer methods for better exception documentation
* feature [DX] Add FlasherContainer::setContainer() method as convenient alias for testing
* feature [DX] Add PHPStan type alias `NotificationType` for valid notification types
### Fixed
* fix [SweetAlert] Fix SweetAlertBuilder::question() bug where options parameter was being ignored
* 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
* fix [Flasher] Fix FilterCriteria uninitialized property error when constructed with empty array
* fix [Flasher] Fix null comparison issues in PriorityCriteria, HopsCriteria, and DelayCriteria that relied on PHP's implicit null-to-0 coercion
* fix [Flasher] Add type validation for callable factory return values in NotificationFactoryLocator with descriptive error messages
* fix [Flasher] Add error handling for invalid regex patterns in ResponseExtension::isPathExcluded()
* fix [Flasher] Fix ContentSecurityPolicyHandler parsing of CSP headers with trailing semicolons creating empty directive keys
* fix [Flasher] Add reset() method to ContentSecurityPolicyHandler to fix CSP state leak in long-running processes (Octane, FrankenPHP)
* fix [Flasher] Fix FilterEvent::setFilter() type inconsistency - now accepts FilterInterface instead of concrete Filter class
* fix [Flasher] Remove redundant callable check in FlasherContainer::getContainer()
* fix [Flasher] Fix NotificationStorageMethods::resolveResourceName() return type from ?string to string (never returns null)
* fix [Flasher] Fix parameter name inconsistency in NotificationBuilderInterface::options() - changed $merge to $append to match implementation
* fix [Flasher] Add type validation for callable presenter return values in ResponseManager with descriptive error messages
* fix [Flasher] Fix FlasherPlugin::normalizePlugins() losing scripts/styles when both top-level and plugin-level configs are provided - replaced array union operator with array_merge
* fix [Flasher] Simplify FlasherPlugin::normalizeFlashBag() by replacing redundant array union with direct array_merge
* fix [Flasher] Fix array_merge() syntax in InstallCommand by removing unnecessary empty array
* fix [Flasher] Add return type validation in FilterCriteria::apply() with proper exception handling
### Changed
* refactor [Flasher] Reduce theme configuration duplication by generating themes programmatically
* refactor [Flasher] Standardize exception message format in PresetNotFoundException to use brackets like other exceptions
* refactor [Flasher] Standardize exception message wording in CriteriaNotRegisteredException to use "not found" instead of "is not found"
* refactor [SweetAlert] Replace @phpstan-ignore-line suppressions with proper @var annotations
## [v2.1.3](https://github.com/php-flasher/php-flasher/compare/v2.1.2...v2.1.3) - 2025-01-25
+267 -216
View File
@@ -11,272 +11,323 @@
</picture>
</p>
<h1 align="center">Elegant Flash Notifications for PHP</h1>
<p align="center">
<a href="https://www.linkedin.com/in/younes--ennaji"><img src="https://img.shields.io/badge/author-@yoeunes-blue.svg" alt="Author Badge"></a>
<a href="https://github.com/php-flasher/php-flasher"><img src="https://img.shields.io/badge/source-php--flasher/php--flasher-blue.svg" alt="Source Code Badge"></a>
<a href="https://github.com/php-flasher/php-flasher/releases"><img src="https://img.shields.io/github/tag/php-flasher/flasher.svg" alt="GitHub Release Badge"></a>
<a href="https://github.com/php-flasher/flasher/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="License Badge"></a>
<a href="https://packagist.org/packages/php-flasher/flasher"><img src="https://img.shields.io/packagist/dt/php-flasher/flasher.svg" alt="Packagist Downloads Badge"></a>
<a href="https://github.com/php-flasher/php-flasher"><img src="https://img.shields.io/github/stars/php-flasher/php-flasher.svg" alt="GitHub Stars Badge"></a>
<a href="https://packagist.org/packages/php-flasher/flasher"><img src="https://img.shields.io/packagist/php-v/php-flasher/flasher.svg" alt="Supported PHP Version Badge"></a>
<strong>One line of PHP. Beautiful notifications. Zero JavaScript.</strong>
</p>
## Table of Contents
<p align="center">
<a href="https://packagist.org/packages/php-flasher/flasher"><img src="https://img.shields.io/packagist/dt/php-flasher/flasher.svg?style=flat-square&label=downloads" alt="Downloads"></a>
<a href="https://github.com/php-flasher/php-flasher"><img src="https://img.shields.io/github/stars/php-flasher/php-flasher.svg?style=flat-square&label=stars" alt="Stars"></a>
<a href="https://github.com/php-flasher/php-flasher/releases"><img src="https://img.shields.io/github/v/release/php-flasher/flasher.svg?style=flat-square" alt="Release"></a>
<a href="https://packagist.org/packages/php-flasher/flasher"><img src="https://img.shields.io/packagist/php-v/php-flasher/flasher.svg?style=flat-square" alt="PHP Version"></a>
<a href="https://github.com/php-flasher/flasher/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square" alt="License"></a>
</p>
- [About PHPFlasher](#about-phpflasher)
- [Features](#features)
- [Supported Versions](#supported-versions)
- [Installation](#installation)
- [Core Package](#core-package)
- [Adapters](#adapters)
- [Quick Start](#quick-start)
- [Usage Examples](#usage-examples)
- [Adapters Overview](#adapters-overview)
- [Official Documentation](#official-documentation)
- [Contributors and Sponsors](#contributors-and-sponsors)
- [Contact](#contact)
- [License](#license)
<p align="center">
<a href="https://php-flasher.io"><strong>Documentation</strong></a> ·
<a href="https://php-flasher.io/playground"><strong>Live Playground</strong></a> ·
<a href="https://github.com/php-flasher/php-flasher/issues"><strong>Report Bug</strong></a>
</p>
## About PHPFlasher
PHPFlasher is an open-source tool that helps you add flash messages to your web applications. Flash messages are short notifications that provide feedback to users after they perform actions, such as submitting a form or encountering an error. These messages enhance the user experience by informing users about the outcomes of their actions.
PHPFlasher simplifies the process of integrating flash messages into **Laravel** and **Symfony** projects. It uses sessions to store messages, allowing you to set a message on one page and display it on another without complex setup.
## Features
- **Multiple Adapters**: Supports Laravel, Symfony, Toastr, Noty, SweetAlert, and more.
- **Flexible Configuration**: Customize the appearance and behavior of flash messages.
- **Extensible**: Easily integrate with various frontend libraries and frameworks.
- **Intuitive API**: Simple functions to create and manage flash messages.
## Supported Versions
| PHPFlasher Version | PHP Version | Symfony Version | Laravel Version |
|--------------------|-------------|-----------------|-----------------|
| **v2.x** | ≥ 8.2 | ≥ 7.2 | ≥ 11 |
| **v1.x** | ≥ 5.3 | ≥ 2.0 | ≥ 4.0 |
> **Note:** If your project uses PHP, Symfony, or Laravel versions below the requirements for PHPFlasher v2.x, please use [PHPFlasher v1.x](https://github.com/php-flasher/php-flasher/tree/1.x).
## Installation
### Core Package
Install the core PHPFlasher package via Composer:
- **For Laravel:**
```bash
composer require php-flasher/flasher-laravel
```
After installation, set up the necessary assets:
```shell
php artisan flasher:install
```
> **Note:** PHPFlasher automatically injects the necessary JavaScript and CSS assets into your Blade templates. No additional steps are required for asset injection.
- **For Symfony:**
```bash
composer require php-flasher/flasher-symfony
```
After installation, set up the necessary assets:
```shell
php bin/console flasher:install
```
> **Note:** PHPFlasher automatically injects the necessary JavaScript and CSS assets into your Twig templates. No additional steps are required for asset injection.
### Adapters
PHPFlasher provides various adapters for different frameworks and notification libraries. Below is an overview of available adapters:
#### Toastr
- [flasher-toastr](https://github.com/php-flasher/flasher-toastr) - Core Toastr Adapter
- [flasher-toastr-laravel](https://github.com/php-flasher/flasher-toastr-laravel) - Laravel Adapter
- [flasher-toastr-symfony](https://github.com/php-flasher/flasher-toastr-symfony) - Symfony Adapter
#### Noty
- [flasher-noty](https://github.com/php-flasher/flasher-noty) - Core Noty Adapter
- [flasher-noty-laravel](https://github.com/php-flasher/flasher-noty-laravel) - Laravel Adapter
- [flasher-noty-symfony](https://github.com/php-flasher/flasher-noty-symfony) - Symfony Adapter
#### Notyf
- [flasher-notyf](https://github.com/php-flasher/flasher-notyf) - Core Notyf Adapter
- [flasher-notyf-laravel](https://github.com/php-flasher/flasher-notyf-laravel) - Laravel Adapter
- [flasher-notyf-symfony](https://github.com/php-flasher/flasher-notyf-symfony) - Symfony Adapter
#### SweetAlert
- [flasher-sweetalert](https://github.com/php-flasher/flasher-sweetalert) - Core SweetAlert Adapter
- [flasher-sweetalert-laravel](https://github.com/php-flasher/flasher-sweetalert-laravel) - Laravel Adapter
- [flasher-sweetalert-symfony](https://github.com/php-flasher/flasher-sweetalert-symfony) - Symfony Adapter
For detailed installation and usage instructions for each adapter, refer to their respective [README.md](https://github.com/php-flasher/flasher-toastr).
---
## Quick Start
To display a notification message, you can either use the `flash()` helper function or obtain an instance of `flasher` from the service container. Then, before returning a view or redirecting, call the desired method (`success()`, `error()`, etc.) and pass in the message to be displayed.
### Using the `flash()` Helper
```php
class BookController
{
public function saveBook()
{
// Your logic here
flash('Your changes have been saved!');
return redirect()->back();
}
}
**Laravel:**
```bash
composer require php-flasher/flasher-laravel && php artisan flasher:install
```
### Using the `flasher` Service
```php
use Flasher\Prime\FlasherInterface;
class AnotherController
{
/**
* If you prefer to use dependency injection
*/
public function register(FlasherInterface $flasher)
{
// Your logic here
$flasher->success('Your changes have been saved!');
// ... redirect or render the view
}
public function update()
{
// Your logic here
app('flasher')->error('An error occurred while updating.'); // ony for laravel
return redirect()->back();
}
}
**Symfony:**
```bash
composer require php-flasher/flasher-symfony && php bin/console flasher:install
```
## Usage Examples
**That's it!** Now use it:
### Success Message
```php
flash()->success('Welcome aboard! Your account is ready.');
```
---
## Why PHPFlasher?
| | PHPFlasher | Others |
|---|:---:|:---:|
| **Zero JavaScript** | Write PHP only, frontend handled automatically | Requires manual JS setup |
| **Auto Asset Injection** | CSS/JS injected automatically | Manual script tags needed |
| **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 |
| **Framework Agnostic** | Laravel, Symfony, or vanilla PHP | Framework-specific |
---
## Notification Types
```php
flash()->success('Operation completed successfully!');
flash()->error('Oops! Something went wrong.');
flash()->warning('Please backup your data before continuing.');
flash()->info('A new version is available for download.');
```
### Error Message
### With Titles
```php
flash()->error('An error occurred.');
flash()->success('Your changes have been saved.', 'Update Complete');
flash()->error('Unable to connect to server.', 'Connection Failed');
```
### Info Message
### With Options
```php
flash()->info('This is an informational message.');
flash()->success('Profile updated!', [
'position' => 'bottom-right',
'timeout' => 10000,
]);
```
### Warning Message
---
## 17 Beautiful Themes
PHPFlasher includes **17 professionally designed themes** ready to use:
```php
flash()->warning('This is a warning message.');
flash()->success('Welcome!', ['theme' => 'amazon']);
flash()->success('Welcome!', ['theme' => 'ios']);
flash()->success('Welcome!', ['theme' => 'slack']);
flash()->success('Welcome!', ['theme' => 'material']);
```
### Passing Options
<details>
<summary><strong>View All Themes</strong></summary>
| Theme | Style |
|-------|-------|
| `flasher` | Default clean design |
| `amazon` | Amazon-inspired e-commerce |
| `ios` | Apple iOS notifications |
| `slack` | Slack messaging style |
| `material` | Google Material Design |
| `google` | Google notifications |
| `facebook` | Facebook style |
| `minimal` | Ultra-clean minimal |
| `amber` | Warm amber tones |
| `aurora` | Gradient effects |
| `crystal` | Transparent design |
| `emerald` | Modern green palette |
| `jade` | Soft jade colors |
| `neon` | Bright attention-grabbing |
| `onyx` | Dark mode sleek |
| `ruby` | Bold ruby accents |
| `sapphire` | Elegant blue style |
[**See all themes with live demos →**](https://php-flasher.io/themes)
</details>
---
## Notification Libraries
Need more features? Use popular notification libraries:
### Toastr
```bash
composer require php-flasher/flasher-toastr-laravel
```
```php
flash()->success('Custom message with options.', ['timeout' => 3000, 'position' => 'bottom-left']);
toastr()->success('Profile saved!', [
'positionClass' => 'toast-bottom-right',
'progressBar' => true,
]);
```
## Adapters Overview
### SweetAlert
PHPFlasher supports various adapters to integrate seamlessly with different frameworks and frontend libraries. Below is an overview of available adapters:
```bash
composer require php-flasher/flasher-sweetalert-laravel
```
| Adapter Repository | Description |
|-----------------------------------------------------------------------------------------|--------------------------------|
| [flasher-laravel](https://github.com/php-flasher/flasher-laravel) | Laravel framework adapter |
| [flasher-symfony](https://github.com/php-flasher/flasher-symfony) | Symfony framework adapter |
| [flasher-toastr-laravel](https://github.com/php-flasher/flasher-toastr-laravel) | Toastr adapter for Laravel |
| [flasher-toastr-symfony](https://github.com/php-flasher/flasher-toastr-symfony) | Toastr adapter for Symfony |
| [flasher-noty-laravel](https://github.com/php-flasher/flasher-noty-laravel) | Noty adapter for Laravel |
| [flasher-noty-symfony](https://github.com/php-flasher/flasher-noty-symfony) | Noty adapter for Symfony |
| [flasher-notyf-laravel](https://github.com/php-flasher/flasher-notyf-laravel) | Notyf adapter for Laravel |
| [flasher-notyf-symfony](https://github.com/php-flasher/flasher-notyf-symfony) | Notyf adapter for Symfony |
| [flasher-sweetalert-laravel](https://github.com/php-flasher/flasher-sweetalert-laravel) | SweetAlert adapter for Laravel |
| [flasher-sweetalert-symfony](https://github.com/php-flasher/flasher-sweetalert-symfony) | SweetAlert adapter for Symfony |
```php
sweetalert()
->showDenyButton()
->showCancelButton()
->warning('Do you want to save changes?');
```
> **Note:** Each adapter has its own repository. For detailed installation and usage instructions, please refer to the [Official Documentation](https://php-flasher.io).
### Noty
## Official Documentation
```bash
composer require php-flasher/flasher-noty-laravel
```
Comprehensive documentation for PHPFlasher is available at [https://php-flasher.io](https://php-flasher.io). Here you will find detailed guides, API references, and advanced usage examples to help you get the most out of PHPFlasher.
```php
noty()->success('Data synchronized!', [
'layout' => 'topCenter',
'timeout' => 3000,
]);
```
## Contributors and sponsors
### Notyf
Join our team of contributors and make a lasting impact on our project!
```bash
composer require php-flasher/flasher-notyf-laravel
```
We are always looking for passionate individuals who want to contribute their skills and ideas.
Whether you're a developer, designer, or simply have a great idea, we welcome your participation and collaboration.
```php
notyf()->success('Upload complete!', [
'dismissible' => true,
'ripple' => true,
]);
```
Shining stars of our community:
---
<!-- ALL-CONTRIBUTORS-LIST:START -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/younes--ennaji/"><img src="https://avatars.githubusercontent.com/u/10859693?v=4?s=100" width="100px;" alt="Younes ENNAJI"/><br /><sub><b>Younes ENNAJI</b></sub></a><br /><a href="https://github.com/php-flasher/php-flasher/commits?author=yoeunes" title="Code">💻</a> <a href="https://github.com/php-flasher/php-flasher/commits?author=yoeunes" title="Documentation">📖</a> <a href="#maintenance-yoeunes" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/salmayno"><img src="https://avatars.githubusercontent.com/u/27933199?v=4?s=100" width="100px;" alt="Salma Mourad"/><br /><sub><b>Salma Mourad</b></sub></a><br /><a href="#financial-salmayno" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.youtube.com/rstacode"><img src="https://avatars.githubusercontent.com/u/35005761?v=4?s=100" width="100px;" alt="Nashwan Abdullah"/><br /><sub><b>Nashwan Abdullah</b></sub></a><br /><a href="#financial-codenashwan" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://darvis.nl/"><img src="https://avatars.githubusercontent.com/u/7394837?v=4?s=100" width="100px;" alt="Arvid de Jong"/><br /><sub><b>Arvid de Jong</b></sub></a><br /><a href="#financial-darviscommerce" title="Financial">💵</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ashallendesign.co.uk/"><img src="https://avatars.githubusercontent.com/u/39652331?v=4?s=100" width="100px;" alt="Ash Allen"/><br /><sub><b>Ash Allen</b></sub></a><br /><a href="#design-ash-jc-allen" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://about.me/murrant"><img src="https://avatars.githubusercontent.com/u/39462?v=4?s=100" width="100px;" alt="Tony Murray"/><br /><sub><b>Tony Murray</b></sub></a><br /><a href="https://github.com/php-flasher/php-flasher/commits?author=murrant" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/n3wborn"><img src="https://avatars.githubusercontent.com/u/10246722?v=4?s=100" width="100px;" alt="Stéphane P"/><br /><sub><b>Stéphane P</b></sub></a><br /><a href="https://github.com/php-flasher/php-flasher/commits?author=n3wborn" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.instagram.com/lucas.maciel_z"><img src="https://avatars.githubusercontent.com/u/80225404?v=4?s=100" width="100px;" alt="Lucas Maciel"/><br /><sub><b>Lucas Maciel</b></sub></a><br /><a href="#design-LucasStorm" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AhmedGamal"><img src="https://avatars.githubusercontent.com/u/11786167?v=4?s=100" width="100px;" alt="Ahmed Gamal"/><br /><sub><b>Ahmed Gamal</b></sub></a><br /><a href="https://github.com/php-flasher/php-flasher/commits?author=AhmedGamal" title="Code">💻</a> <a href="https://github.com/php-flasher/php-flasher/commits?author=AhmedGamal" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BrookeDot"><img src="https://avatars.githubusercontent.com/u/150348?v=4?s=100" width="100px;" alt="Brooke."/><br /><sub><b>Brooke.</b></sub></a><br /><a href="https://github.com/php-flasher/php-flasher/commits?author=BrookeDot" title="Documentation">📖</a></td>
</tr>
</tbody>
</table>
## Livewire Integration
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
PHPFlasher integrates seamlessly with Laravel Livewire:
<!-- ALL-CONTRIBUTORS-LIST:END -->
```php
use Livewire\Attributes\On;
## Contact
class UserProfile extends Component
{
public function save()
{
// Save logic...
PHPFlasher is being actively developed by <a href="https://github.com/yoeunes">yoeunes</a>.
You can reach out with questions, bug reports, or feature requests on any of the following:
sweetalert()
->showDenyButton()
->success('Save changes?');
}
- [Github Issues](https://github.com/php-flasher/php-flasher/issues)
- [Github](https://github.com/yoeunes)
- [Twitter](https://twitter.com/yoeunes)
- [Linkedin](https://www.linkedin.com/in/younes--ennaji/)
- [Email me directly](mailto:younes.ennaji.pro@gmail.com)
#[On('sweetalert:confirmed')]
public function onConfirmed(array $payload): void
{
// User clicked confirm
$this->user->save();
flash()->success('Profile saved!');
}
#[On('sweetalert:denied')]
public function onDenied(array $payload): void
{
// User clicked deny
}
}
```
[**Livewire documentation →**](https://php-flasher.io/livewire)
---
## Configuration
### Laravel
```php
// config/flasher.php
return [
'default' => 'flasher',
'themes' => [
'flasher' => [
'options' => [
'timeout' => 5000,
'position' => 'top-right',
],
],
],
];
```
### Symfony
```yaml
# config/packages/flasher.yaml
flasher:
default: flasher
themes:
flasher:
options:
timeout: 5000
position: top-right
```
### Common Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `timeout` | int | `5000` | Auto-dismiss delay in ms (0 = sticky) |
| `position` | string | `top-right` | `top-right`, `top-left`, `bottom-right`, `bottom-left`, `top-center`, `bottom-center` |
| `closeButton` | bool | `true` | Show close button |
| `progressBar` | bool | `true` | Show timeout progress bar |
| `rtl` | bool | `false` | Right-to-left text direction |
| `escapeHtml` | bool | `true` | Escape HTML in messages |
---
## Requirements
| Requirement | Version |
|-------------|---------|
| PHP | >= 8.2 |
| Laravel | >= 11.0 |
| Symfony | >= 7.0 |
---
## Documentation
For complete documentation, visit **[php-flasher.io](https://php-flasher.io)**
- [Installation Guide](https://php-flasher.io/installation)
- [Laravel Integration](https://php-flasher.io/laravel)
- [Symfony Integration](https://php-flasher.io/symfony)
- [Livewire Integration](https://php-flasher.io/livewire)
- [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 feel free to submit a [Pull Request](https://github.com/php-flasher/php-flasher/pulls).
## Contributors
<a href="https://github.com/php-flasher/php-flasher/graphs/contributors">
<img src="https://contrib.rocks/image?repo=php-flasher/php-flasher" alt="Contributors" />
</a>
---
## Support the Project
If PHPFlasher helps you build better applications, please consider:
- **[Star this repository](https://github.com/php-flasher/php-flasher)** to show your support
- **[Report bugs](https://github.com/php-flasher/php-flasher/issues)** to help improve the library
- **[Share on Twitter](https://twitter.com/intent/tweet?text=Check%20out%20PHPFlasher%20-%20beautiful%20flash%20notifications%20for%20PHP!&url=https://github.com/php-flasher/php-flasher)** to spread the word
---
## License
PHPFlasher is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
PHPFlasher is open-source software licensed under the [MIT license](LICENSE).
<p align="center"> <b>Made with ❤️ by <a href="https://www.linkedin.com/in/younes--ennaji/">Younes ENNAJI</a> </b> </p>
<p align="center">
<br>
<strong>Made with ❤️ by <a href="https://github.com/yoeunes">Younes ENNAJI</a></strong>
<br><br>
<a href="https://github.com/php-flasher/php-flasher/stargazers">⭐ Star if you find this useful!</a>
</p>
Executable
+675
View File
@@ -0,0 +1,675 @@
#!/usr/bin/env bash
# =========================================================================
# Build System for PHP-Flasher
# =========================================================================
#
# This script provides an elegant build process for PHP-Flasher assets
# with comprehensive reporting and flexible configuration options.
#
# Author: Younes ENNAJI
# =========================================================================
# Strict error handling
set -o pipefail
# =========================================================================
# CONSTANTS AND CONFIGURATION
# =========================================================================
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
readonly WHITE='\033[37m'
# Emoji indicators
readonly ROCKET="🚀"
readonly PACKAGE="📦"
readonly CHECK="✓"
readonly ERROR="❌"
readonly WARNING="⚠️"
readonly HAMMER="🏗️"
readonly CHART="📊"
readonly SPARKLES="✨"
readonly HOURGLASS="⏳"
readonly PALETTE="🎨"
# File paths
readonly SRC_DIR="src"
readonly PRIME_PATH="${SRC_DIR}/Prime/Resources"
readonly TEMP_DIR="/tmp/php-flasher-build-$$"
readonly BUILD_LOG="${TEMP_DIR}/build.log"
readonly SIZE_DATA="${TEMP_DIR}/sizes.data"
# Default configuration
VERBOSE=false
WATCH_MODE=false
THEME_ONLY=false
MODULE_ONLY=false
ANALYZE=false
DEEP_ANALYZE=false
SKIP_CLEAR=false
NODE_ENV="production"
# =========================================================================
# UTILITY FUNCTIONS
# =========================================================================
cleanup() {
# Clean up temporary files when the script exits
rm -rf "${TEMP_DIR}" 2>/dev/null
}
trap cleanup EXIT
mkdir -p "${TEMP_DIR}"
print_header() {
echo -e "\n${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}$(whoami)${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}"
if [ "$NODE_ENV" != "production" ]; then
echo -e "${BOLD}Mode : ${RESET}${YELLOW}${NODE_ENV}${RESET}"
fi
if [ "$WATCH_MODE" = true ]; then
echo -e "${BOLD}Watch : ${RESET}${CYAN}enabled${RESET}"
fi
if [ "$THEME_ONLY" = true ]; then
echo -e "${BOLD}Scope : ${RESET}${MAGENTA}themes only${RESET}"
elif [ "$MODULE_ONLY" = true ]; then
echo -e "${BOLD}Scope : ${RESET}${MAGENTA}modules only${RESET}"
fi
echo
}
print_section() {
echo -e "\n${BOLD}${CYAN}┌─ $1 ${2:-}${RESET}"
}
success_msg() {
echo -e "${GREEN}${CHECK} $*${RESET}"
}
error_msg() {
echo -e "${RED}${ERROR} $*${RESET}"
}
warning_msg() {
echo -e "${YELLOW}${WARNING} $*${RESET}"
}
info_msg() {
echo -e "${BLUE}${HOURGLASS} $*${RESET}"
}
print_usage() {
echo -e "${BOLD}Usage:${RESET} $0 [options]"
echo
echo -e "${BOLD}Options:${RESET}"
echo -e " ${GREEN}-h, --help${RESET} Show this help message"
echo -e " ${GREEN}-v, --verbose${RESET} Display detailed build output"
echo -e " ${GREEN}-w, --watch${RESET} Run in watch mode"
echo -e " ${GREEN}-d, --development${RESET} Build in development mode (unminified)"
echo -e " ${GREEN}-t, --themes-only${RESET} Build only themes"
echo -e " ${GREEN}-m, --modules-only${RESET} Build only modules (core and plugins)"
echo -e " ${GREEN}-a, --analyze${RESET} Show detailed size analysis in table format"
echo -e " ${GREEN}-D, --deep-analyze${RESET} Perform deep analysis of files (checksums, content inspection)"
echo -e " ${GREEN}--skip-clear${RESET} Skip clearing output directories"
echo
}
parse_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
-h|--help)
print_usage
exit 0
;;
-v|--verbose)
VERBOSE=true
;;
-w|--watch)
WATCH_MODE=true
NODE_ENV="development"
;;
-d|--development)
NODE_ENV="development"
;;
-t|--themes-only)
THEME_ONLY=true
;;
-m|--modules-only)
MODULE_ONLY=true
;;
-a|--analyze)
ANALYZE=true
;;
-D|--deep-analyze)
ANALYZE=true
DEEP_ANALYZE=true
;;
--skip-clear)
SKIP_CLEAR=true
;;
*)
warning_msg "Unknown option: $1"
print_usage
exit 1
;;
esac
shift
done
# Validate conflicting options
if [ "$THEME_ONLY" = true ] && [ "$MODULE_ONLY" = true ]; then
error_msg "Cannot specify both --themes-only and --modules-only"
exit 1
fi
# Configure build mode
if [ "$WATCH_MODE" = true ]; then
ROLLUP_ARGS="-c -w"
else
ROLLUP_ARGS="-c"
fi
# When analyzing, we turn off verbosity for rollup
if [ "$ANALYZE" = true ]; then
export FILESIZE_SILENT=true
fi
}
# =========================================================================
# BUILD FUNCTIONS
# =========================================================================
get_size_info() {
local file="$1"
# Get precise byte count for more accurate comparison
local bytes=$(wc -c < "$file")
local size=$(du -h "$file" | awk '{print $1}')
local gzip=$(gzip -c "$file" | wc -c | numfmt --to=iec --format="%.1f")
local brotli_size=$(brotli -c "$file" 2>/dev/null | wc -c | numfmt --to=iec --format="%.1f" || echo "N/A")
echo "$size|$gzip|$bytes|$brotli_size"
}
get_file_hash() {
local file="$1"
if command -v sha256sum &> /dev/null; then
sha256sum "$file" | awk '{print $1}'
elif command -v shasum &> /dev/null; then
shasum -a 256 "$file" | awk '{print $1}'
else
echo "HASH_UNAVAILABLE"
fi
}
analyze_file_content() {
local file="$1"
local filename=$(basename "$file")
local dir="${TEMP_DIR}/analysis/$(dirname "$file" | sed 's/\//_/g')"
mkdir -p "$dir"
# Get first 10 lines for a quick look
head -n 10 "$file" > "${dir}/${filename}.head"
# Get hash for exact comparison
local hash=$(get_file_hash "$file")
echo "$hash" > "${dir}/${filename}.hash"
# For CSS/JS, try to determine if it's minified
if [[ "$file" == *.css || "$file" == *.js ]]; then
# Count semicolons to help identify if minified
local semicolons=$(grep -o ";" "$file" | wc -l)
# Count newlines
local newlines=$(grep -c $'\n' "$file")
# Simple heuristic: if few newlines relative to semicolons, likely minified
if [ "$newlines" -lt 10 ] || [ "$semicolons" -gt "$((newlines * 5))" ]; then
echo "MINIFIED:YES semicolons:$semicolons newlines:$newlines" > "${dir}/${filename}.analysis"
else
echo "MINIFIED:NO semicolons:$semicolons newlines:$newlines" > "${dir}/${filename}.analysis"
fi
fi
echo "$hash"
}
collect_build_statistics() {
local modules_count=0
local themes_count=0
declare -a theme_names=()
local unique_css_hashes=0
local unique_js_hashes=0
declare -A css_hashes=()
declare -A js_hashes=()
# Create temporary directory for data
mkdir -p "${TEMP_DIR}/modules"
mkdir -p "${TEMP_DIR}/themes"
mkdir -p "${TEMP_DIR}/analysis"
echo "DEBUG: Looking for modules and themes..." >> "${BUILD_LOG}"
# Check for core flasher module first
if [ -f "${PRIME_PATH}/dist/flasher.min.js" ]; then
local size_info=$(get_size_info "${PRIME_PATH}/dist/flasher.min.js")
local hash="N/A"
if [ "$DEEP_ANALYZE" = true ]; then
hash=$(analyze_file_content "${PRIME_PATH}/dist/flasher.min.js")
js_hashes["$hash"]=1
((unique_js_hashes++))
fi
echo "flasher:$size_info:$hash" >> "${TEMP_DIR}/modules/data"
((modules_count++))
fi
# Collect module statistics - FIXED to avoid subshell issue
# Store filenames to process in a temporary file
find ${SRC_DIR}/*/Prime/Resources/dist -name "*.min.js" | grep -v themes > "${TEMP_DIR}/module_files.txt" 2>/dev/null
# Process each module file
while IFS= read -r file; do
local module=$(basename "$file" .min.js)
local size_info=$(get_size_info "$file")
local hash="N/A"
if [ "$DEEP_ANALYZE" = true ]; then
hash=$(analyze_file_content "$file")
js_hashes["$hash"]=1
((unique_js_hashes++))
fi
echo "$module:$size_info:$hash" >> "${TEMP_DIR}/modules/data"
((modules_count++))
echo "DEBUG: Found module: $module in $file (${size_info})" >> "${BUILD_LOG}"
done < "${TEMP_DIR}/module_files.txt"
# Collect theme statistics - FIXED to avoid subshell issue
if [ -d "${PRIME_PATH}/dist/themes" ]; then
# Store theme files in a temporary file
find ${PRIME_PATH}/dist/themes -name "*.min.js" > "${TEMP_DIR}/theme_files.txt" 2>/dev/null
# Process each theme file
while IFS= read -r file; do
local theme=$(basename "$(dirname "$file")")
local size_info=$(get_size_info "$file")
local js_hash="N/A"
local css_hash="N/A"
# Also get the CSS file size and analyze it
local css_file="${PRIME_PATH}/dist/themes/${theme}/${theme}.min.css"
local css_size_info="N/A"
if [ -f "$css_file" ]; then
css_size_info=$(get_size_info "$css_file")
if [ "$DEEP_ANALYZE" = true ]; then
css_hash=$(analyze_file_content "$css_file")
if [ -z "${css_hashes[$css_hash]}" ]; then
css_hashes["$css_hash"]=1
((unique_css_hashes++))
else
css_hashes["$css_hash"]=$((css_hashes["$css_hash"] + 1))
fi
js_hash=$(analyze_file_content "$file")
if [ -z "${js_hashes[$js_hash]}" ]; then
js_hashes["$js_hash"]=1
else
js_hashes["$js_hash"]=$((js_hashes["$js_hash"] + 1))
fi
fi
echo "DEBUG: Theme $theme JS: $size_info, CSS: $css_size_info" >> "${BUILD_LOG}"
else
echo "DEBUG: No CSS file found for theme $theme" >> "${BUILD_LOG}"
fi
echo "$theme:$size_info:$js_hash:$css_size_info:$css_hash" >> "${TEMP_DIR}/themes/data"
theme_names+=("$theme")
((themes_count++))
done < "${TEMP_DIR}/theme_files.txt"
# Sort theme names alphabetically
if [ ${#theme_names[@]} -gt 0 ]; then
printf "%s\n" "${theme_names[@]}" | sort > "${TEMP_DIR}/theme_names"
fi
fi
# Store counts for summary
echo "$modules_count" > "${TEMP_DIR}/modules_count"
echo "$themes_count" > "${TEMP_DIR}/themes_count"
echo "$unique_js_hashes" > "${TEMP_DIR}/unique_js_hashes"
echo "$unique_css_hashes" > "${TEMP_DIR}/unique_css_hashes"
# Write hash statistics if deep analysis was enabled
if [ "$DEEP_ANALYZE" = true ]; then
echo "JS Hash Distribution:" > "${TEMP_DIR}/hash_stats.txt"
for hash in "${!js_hashes[@]}"; do
echo " $hash: ${js_hashes[$hash]} files" >> "${TEMP_DIR}/hash_stats.txt"
done
echo -e "\nCSS Hash Distribution:" >> "${TEMP_DIR}/hash_stats.txt"
for hash in "${!css_hashes[@]}"; do
echo " $hash: ${css_hashes[$hash]} files" >> "${TEMP_DIR}/hash_stats.txt"
done
fi
}
run_build() {
print_section "Starting Build Process" "${HAMMER}"
info_msg "Environment: ${NODE_ENV}"
info_msg "Building PHP-Flasher assets..."
# Set environment variables
export NODE_ENV="$NODE_ENV"
export ANALYZE="$ANALYZE"
export DEEP_ANALYZE="$DEEP_ANALYZE"
# Determine which parts to build based on flags
local rollup_args="$ROLLUP_ARGS"
if [ "$THEME_ONLY" = true ]; then
info_msg "Building themes only"
# Here we'd need to extend rollup to support theme-only builds
# For now, we'll complete the full build
elif [ "$MODULE_ONLY" = true ]; then
info_msg "Building modules only"
# Similarly, we'd need to extend rollup
fi
# Execute rollup with appropriate flags
if [ "$VERBOSE" = true ]; then
if npx rollup $rollup_args; then
success_msg "Build process completed"
return 0
else
error_msg "Build process failed"
return 1
fi
else
# Capture output for non-verbose mode
if npx rollup $rollup_args > "$BUILD_LOG" 2>&1; then
success_msg "Build process completed"
return 0
else
error_msg "Build process failed"
cat "$BUILD_LOG"
return 1
fi
fi
}
# =========================================================================
# REPORTING FUNCTIONS
# =========================================================================
print_size_table() {
local title="$1"
local data_file="$2"
local max_name_len=20
if [ ! -f "$data_file" ] || [ ! -s "$data_file" ]; then
return
fi
echo -e "\n${BOLD}${WHITE}${title}${RESET}\n"
# Print table header with or without hashes based on deep analysis
if [ "$DEEP_ANALYZE" = true ]; then
printf "${BOLD}%-${max_name_len}s %-10s %-10s %-10s %-10s${RESET}\n" "Component" "Size" "Gzip" "Bytes" "Hash"
else
printf "${BOLD}%-${max_name_len}s %-10s %-10s %-10s${RESET}\n" "Component" "Size" "Gzip" "Bytes"
fi
echo -e "${DIM}$(printf '%.0s─' {1..70})${RESET}"
# Print table rows
while IFS=: read -r line; do
# Split the line into fields
local fields=()
while IFS=: read -ra parts; do
fields=("${parts[@]}")
done <<< "$line"
local name="${fields[0]}"
local size_data="${fields[1]}"
local hash="N/A"
if [ "${#fields[@]}" -gt 2 ]; then
hash="${fields[2]}"
fi
IFS='|' read -r size gzip bytes brotli <<< "$size_data"
if [ "$DEEP_ANALYZE" = true ]; then
printf "%-${max_name_len}s ${GREEN}%-10s${RESET} ${BLUE}%-10s${RESET} ${YELLOW}%-10s${RESET} ${DIM}%.8s${RESET}\n" \
"$name" "$size" "$gzip" "$bytes" "$hash"
else
printf "%-${max_name_len}s ${GREEN}%-10s${RESET} ${BLUE}%-10s${RESET} ${YELLOW}%-10s${RESET}\n" \
"$name" "$size" "$gzip" "$bytes"
fi
done < "$data_file"
}
print_theme_table() {
local data_file="$1"
local max_name_len=15
if [ ! -f "$data_file" ] || [ ! -s "$data_file" ]; then
return
fi
echo -e "\n${BOLD}${WHITE}Theme Sizes${RESET}\n"
# Print table header
printf "${BOLD}%-${max_name_len}s %-10s %-10s %-10s %-10s${RESET}\n" "Theme" "JS Size" "CSS Size" "JS Bytes" "CSS Bytes"
echo -e "${DIM}$(printf '%.0s─' {1..70})${RESET}"
# Print table rows
while IFS=: read -r line; do
# Split the line into fields
local fields=()
while IFS=: read -ra parts; do
fields=("${parts[@]}")
done <<< "$line"
local name="${fields[0]}"
local js_size_data="${fields[1]}"
local js_hash="N/A"
local css_size_data="N/A"
local css_hash="N/A"
if [ "${#fields[@]}" -gt 2 ]; then
js_hash="${fields[2]}"
fi
if [ "${#fields[@]}" -gt 3 ]; then
css_size_data="${fields[3]}"
fi
if [ "${#fields[@]}" -gt 4 ]; then
css_hash="${fields[4]}"
fi
IFS='|' read -r js_size js_gzip js_bytes js_brotli <<< "$js_size_data"
local css_size="N/A"
local css_bytes="N/A"
if [ "$css_size_data" != "N/A" ]; then
IFS='|' read -r css_size css_gzip css_bytes css_brotli <<< "$css_size_data"
fi
printf "%-${max_name_len}s ${GREEN}%-10s${RESET} ${MAGENTA}%-10s${RESET} ${YELLOW}%-10s${RESET} ${BLUE}%-10s${RESET}\n" \
"$name" "$js_size" "$css_size" "$js_bytes" "$css_bytes"
done < "$data_file"
}
print_theme_grid() {
local theme_names_file="$1"
if [ ! -f "$theme_names_file" ] || [ ! -s "$theme_names_file" ]; then
return
fi
echo -e "\n${BOLD}${MAGENTA}${PALETTE} Themes:${RESET}"
# Define grid parameters
local columns=3
local max_width=15
local count=0
# Print themes in a grid
while read -r theme; do
if [ $((count % columns)) -eq 0 ]; then
echo -ne " "
fi
printf "• %-${max_width}s" "$theme"
count=$((count + 1))
if [ $((count % columns)) -eq 0 ]; then
echo
fi
done < "$theme_names_file"
# Add a newline if the last row wasn't complete
if [ $((count % columns)) -ne 0 ]; then
echo
fi
}
print_deep_analysis() {
if [ ! -d "${TEMP_DIR}/analysis" ]; then
return
fi
local unique_js=0
local unique_css=0
if [ -f "${TEMP_DIR}/unique_js_hashes" ]; then
unique_js=$(cat "${TEMP_DIR}/unique_js_hashes")
fi
if [ -f "${TEMP_DIR}/unique_css_hashes" ]; then
unique_css=$(cat "${TEMP_DIR}/unique_css_hashes")
fi
echo -e "\n${BOLD}${WHITE}Deep File Analysis${RESET}"
echo -e "${DIM}$(printf '%.0s─' {1..50})${RESET}"
echo -e "Unique JavaScript files: ${YELLOW}${unique_js}${RESET}"
echo -e "Unique CSS files: ${MAGENTA}${unique_css}${RESET}"
if [ -f "${TEMP_DIR}/hash_stats.txt" ]; then
echo -e "\n${BOLD}Hash Distribution:${RESET}"
local top_5=$(head -n 10 "${TEMP_DIR}/hash_stats.txt")
echo -e "${DIM}$top_5${RESET}"
echo -e "${DIM}(See ${TEMP_DIR}/hash_stats.txt for full details)${RESET}"
fi
}
print_summary() {
local success=$1
local duration=$2
# Read statistics
local modules_count=0
local themes_count=0
if [ -f "${TEMP_DIR}/modules_count" ]; then
modules_count=$(cat "${TEMP_DIR}/modules_count")
fi
if [ -f "${TEMP_DIR}/themes_count" ]; then
themes_count=$(cat "${TEMP_DIR}/themes_count")
fi
# Print summary header
echo -e "\n${BOLD}${PACKAGE} Build Summary${RESET}"
echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Print statistics
if [ "$MODULE_ONLY" = false ]; then
echo -e "${CHECK} Modules/Plugins: ${BOLD}${CYAN}${modules_count}${RESET}"
fi
if [ "$THEME_ONLY" = false ]; then
echo -e "${CHECK} Themes: ${BOLD}${MAGENTA}${themes_count}${RESET}"
fi
echo -e "${CHECK} Build completed in ${BOLD}${YELLOW}${duration}s${RESET}"
# Print theme names if available
if [ -f "${TEMP_DIR}/theme_names" ]; then
print_theme_grid "${TEMP_DIR}/theme_names"
fi
# Show size analysis if requested
if [ "$ANALYZE" = true ]; then
echo -e "\n${BOLD}${CHART} Size Analysis:${RESET}"
print_size_table "Module Sizes" "${TEMP_DIR}/modules/data"
if [ "$DEEP_ANALYZE" = true ]; then
print_theme_table "${TEMP_DIR}/themes/data"
print_deep_analysis
else
print_size_table "Theme Sizes" "${TEMP_DIR}/themes/data"
fi
# Add option to view detailed logs
echo -e "\nFor detailed build info, run: ${CYAN}cat $BUILD_LOG${RESET}"
echo -e "Temporary files at: ${CYAN}${TEMP_DIR}${RESET}"
fi
}
# =========================================================================
# MAIN EXECUTION
# =========================================================================
main() {
local start_time=$(date +%s)
local build_success=true
# Parse command-line arguments
parse_args "$@"
# Show header
print_header
# Run the build process
run_build || build_success=false
# Only collect statistics if build was successful and not in watch mode
if [ "$WATCH_MODE" = false ] && [ "$build_success" = true ]; then
if [ "$ANALYZE" = true ] || [ ! -t 1 ]; then
print_section "Analyzing Build Output" "${CHART}"
fi
collect_build_statistics
fi
# Print summary (unless in watch mode)
if [ "$WATCH_MODE" = false ]; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
print_summary "$build_success" "$duration"
fi
# Exit with appropriate code
[ "$build_success" = true ] && exit 0 || exit 1
}
# Execute main function with all arguments
main "$@"
Executable
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env bash
set -e
set -o pipefail
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
# Emojis
readonly ROCKET="🚀"
readonly UPDATE="🔄"
readonly PACKAGE="📦"
readonly SUCCESS="✨"
readonly ERROR="❌"
readonly WARNING="⚠️"
readonly CHECK="✓"
# Header
print_header() {
echo -e "\n${BOLD}${BLUE}╭──────────────────────────────────────────╮${RESET}"
echo -e "${BOLD}${BLUE}│ ${ROCKET} PHP-Flasher Version ${ROCKET} │${RESET}"
echo -e "${BOLD}${BLUE}╰──────────────────────────────────────────╯${RESET}\n"
echo -e " Date : $(date -u '+%Y-%m-%d %H:%M:%S') UTC"
echo -e " User : $(whoami)"
echo -e " Directory: $(pwd)\n"
}
# Version validation
validate_version() {
local version=$1
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "\n${RED}${ERROR} Invalid version format: $version${RESET}"
echo -e " Version must be in format X.Y.Z (e.g., 2.1.6)\n"
exit 1
fi
}
# Get current version from Flasher class
get_current_version() {
local file="src/Prime/Flasher.php"
if [ -f "$file" ]; then
local version=$(grep -o "const VERSION = '[0-9]\+\.[0-9]\+\.[0-9]\+'" "$file" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+")
echo "$version"
else
echo -e "${RED}${ERROR} Flasher.php not found in src/Prime/Flasher.php${RESET}"
exit 1
fi
}
# Update composer.json file
update_composer_file() {
local file=$1
local current_version=$2
local new_version=$3
# Create a temporary file
local tmp_file="${file}.tmp"
# Use jq with 4-space indentation
if command -v jq >/dev/null 2>&1; then
jq --indent 4 --arg cv "^${current_version}" --arg nv "^${new_version}" '
walk(
if type == "object" and has("require") then
.require |= with_entries(
if .key | startswith("php-flasher/") then
.value = $nv
else
.
end
)
else
.
end
)
' "$file" > "$tmp_file"
else
# Fallback to sed for simple replacement
sed -E "s/\"php-flasher\/[^\"]+\": \"\\^${current_version}\"/\"\\0\": \"^${new_version}\"/" "$file" > "$tmp_file"
fi
# Check if the temporary file exists and has content
if [ -s "$tmp_file" ]; then
mv "$tmp_file" "$file"
echo " ${CHECK} Updated $file"
else
echo " ${WARNING} Failed to update $file"
rm -f "$tmp_file"
fi
}
# Update package.json file
update_package_file() {
local file=$1
local current_version=$2
local new_version=$3
# Create a temporary file
local tmp_file="${file}.tmp"
# Use jq with 4-space indentation
if command -v jq >/dev/null 2>&1; then
jq --indent 4 --arg cv "^${current_version}" --arg nv "^${new_version}" --arg v "${new_version}" '
.version = $v |
if has("peerDependencies") then
.peerDependencies |= with_entries(
if .key | startswith("@flasher/") then
.value = $nv
else
.
end
)
else
.
end |
if has("dependencies") then
.dependencies |= with_entries(
if .key | startswith("@flasher/") then
.value = $nv
else
.
end
)
else
.
end
' "$file" > "$tmp_file"
else
# Fallback to sed for simple replacement
sed -E -e "s/\"version\": \"${current_version}\"/\"version\": \"${new_version}\"/" \
-e "s/\"@flasher\/[^\"]+\": \"\\^${current_version}\"/\"\\0\": \"^${new_version}\"/" \
"$file" > "$tmp_file"
fi
# Check if the temporary file exists and has content
if [ -s "$tmp_file" ]; then
mv "$tmp_file" "$file"
echo " ${CHECK} Updated $file"
else
echo " ${WARNING} Failed to update $file"
rm -f "$tmp_file"
fi
}
# Update version in files
update_version() {
local current_version=$1
local new_version=$2
echo -e " ${UPDATE} Updating version numbers ($current_version → $new_version)\n"
# Update PHP class version
echo -e " ${PACKAGE} Updating Flasher class version..."
if [ -f "src/Prime/Flasher.php" ]; then
sed -i '' "s/const VERSION = '$current_version'/const VERSION = '$new_version'/" src/Prime/Flasher.php
echo -e " ${CHECK} Updated src/Prime/Flasher.php"
fi
# Update composer.json files in src directory
echo -e "\n ${PACKAGE} Updating composer.json files..."
find src -name "composer.json" -type f | while read -r file; do
update_composer_file "$file" "$current_version" "$new_version"
done
# Update package.json files in src directory
echo -e "\n ${PACKAGE} Updating package.json files..."
find src -name "package.json" -type f | while read -r file; do
update_package_file "$file" "$current_version" "$new_version"
done
echo -e "\n ${SUCCESS} Version bump complete!\n"
}
# Main execution
if [ -z "$1" ]; then
echo -e "${RED}${ERROR} Version number is required${RESET}"
echo -e "Usage: $0 <version>\n"
exit 1
fi
print_header
new_version=$1
current_version=$(get_current_version)
validate_version "$new_version"
update_version "$current_version" "$new_version"
+27 -62
View File
@@ -1,70 +1,35 @@
#!/usr/bin/env php
<?php
#!/bin/bash
declare(strict_types=1);
# Default to the current directory if no directory is provided
dir=${1:-.}
require __DIR__.'/../vendor/autoload.php';
# Optional: A list of file extensions to filter by, e.g., "txt md". Leave empty to include all files.
extensions=($2)
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
# Temporary file to store results
temp_file=$(mktemp)
$filesystem = new Filesystem();
// Define the shared resources
$shared = realpath(__DIR__.'/../.shared');
$resources = [
$shared,
__DIR__.'/../.github/FUNDING.yml',
__DIR__.'/../README.md',
__DIR__.'/../LICENSE',
];
// Directories to search for packages
$dirs = [__DIR__.'/../src'];
// Find all composer.json files within the specified directories
$finder = new Finder();
$finder->files()
->in($dirs)
->name('composer.json')
->depth('< 3'); // Adjust depth as needed
$packages = [];
// Collect package directories
foreach ($finder as $file) {
$packages[] = dirname($file->getRealPath());
# Function to print file details (now inline within find command)
print_file_details() {
echo "File Path: $1"
echo "Contents:"
cat "$1"
echo
}
foreach ($packages as $packageDir) {
foreach ($resources as $resource) {
if (!is_string($resource)) {
continue;
}
# Finding and processing files
if [ ${#extensions[@]} -eq 0 ]; then
# If no extensions are specified, process all files
find "$dir" -type f -exec bash -c 'echo "File Path: $1"; echo "Contents:"; cat "$1"; echo' bash {} \; >> "$temp_file"
else
# Process only files with specified extensions
for ext in "${extensions[@]}"; do
find "$dir" -type f -name "*.$ext" -exec bash -c 'echo "File Path: $1"; echo "Contents:"; cat "$1"; echo' bash {} \; >> "$temp_file"
done
fi
$resourcePath = realpath($resource);
# Copy results to clipboard and remove the temporary file
cat "$temp_file" | pbcopy
rm "$temp_file"
if (!$resourcePath) {
continue; // Skip if the resource doesn't exist
}
$relativePath = str_replace(realpath(__DIR__.'/../') ?: '', '', $resourcePath);
$destination = $packageDir.$relativePath;
if (is_file($resourcePath)) {
// Ensure the destination directory exists
$filesystem->mkdir(dirname($destination));
// Copy the file
$filesystem->copy($resourcePath, $destination, true);
} elseif (is_dir($resourcePath)) {
if ($resourcePath === $shared) {
// Copy contents of the shared directory into the package directory
$filesystem->mirror($shared, $packageDir, null, ['override' => true]);
} else {
// Copy the entire directory to the destination
$filesystem->mirror($resourcePath, $destination, null, ['override' => true]);
}
}
}
}
echo "Results copied to clipboard."
Executable
+129
View File
@@ -0,0 +1,129 @@
#!/usr/bin/env bash
set -o pipefail
# PHP binary to use (defaults to PHP 8.2)
PHP_BINARY="${PHP_BINARY:-/opt/homebrew/opt/php@8.2/bin/php}"
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
readonly WARNING="⚠️"
readonly CHART="📊"
readonly TEST_TUBE="🧪"
readonly SPARKLES="✨"
readonly GLOBE="🌐"
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Test Coverage ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
cleanup_coverage() {
echo -e "${BOLD}${DIM}Cleaning up old coverage reports...${RESET}"
rm -rf coverage/phpunit coverage/vitest
mkdir -p coverage/phpunit coverage/vitest
echo -e "${CHECK} ${GREEN}Cleanup complete${RESET}\n"
}
run_phpunit_coverage() {
echo -e "${BOLD}${TEST_TUBE} Running PHPUnit with Coverage${RESET}"
if $PHP_BINARY vendor/bin/phpunit --coverage-html coverage/phpunit/html --coverage-text=coverage/phpunit/report.txt; then
echo -e "${CHECK} ${GREEN}PHPUnit coverage generated${RESET}"
echo -e " ${DIM}HTML Report: coverage/phpunit/html/index.html${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}PHPUnit coverage generation failed${RESET}\n"
return 1
fi
}
run_vitest_coverage() {
echo -e "${BOLD}${TEST_TUBE} Running Vitest with Coverage${RESET}"
if npm run test:coverage; then
echo -e "${CHECK} ${GREEN}Vitest coverage generated${RESET}"
echo -e " ${DIM}HTML Report: coverage/vitest/index.html${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Vitest coverage generation failed${RESET}\n"
return 1
fi
}
print_summary() {
echo -e "${BOLD}${CYAN}${CHART} Coverage Reports Summary${RESET}\n"
echo -e "${BOLD}PHPUnit Coverage:${RESET}"
if [ -f "coverage/phpunit/html/index.html" ]; then
echo -e " ${GREEN}${CHECK} HTML: ${RESET}coverage/phpunit/html/index.html"
if [ -f "coverage/phpunit/report.txt" ]; then
echo -e " ${GREEN}${CHECK} Text: ${RESET}coverage/phpunit/report.txt"
fi
else
echo -e " ${RED}${ERROR} No coverage report generated${RESET}"
fi
echo -e "\n${BOLD}Vitest Coverage:${RESET}"
if [ -f "coverage/vitest/index.html" ]; then
echo -e " ${GREEN}${CHECK} HTML: ${RESET}coverage/vitest/index.html"
if [ -f "coverage/vitest/coverage-final.json" ]; then
echo -e " ${GREEN}${CHECK} JSON: ${RESET}coverage/vitest/coverage-final.json"
fi
else
echo -e " ${RED}${ERROR} No coverage report generated${RESET}"
fi
echo -e "\n${BOLD}${GLOBE} Open Reports:${RESET}"
echo -e " ${CYAN}npm run coverage:open${RESET}"
echo -e " ${DIM}Or manually open the HTML files in your browser${RESET}\n"
}
open_reports() {
if [ "$1" == "--open" ] || [ "$1" == "-o" ]; then
echo -e "${BOLD}${GLOBE} Opening coverage reports...${RESET}\n"
if [ -f "coverage/phpunit/html/index.html" ]; then
open coverage/phpunit/html/index.html 2>/dev/null || xdg-open coverage/phpunit/html/index.html 2>/dev/null
fi
if [ -f "coverage/vitest/index.html" ]; then
open coverage/vitest/index.html 2>/dev/null || xdg-open coverage/vitest/index.html 2>/dev/null
fi
fi
}
main() {
local start_time=$(date +%s)
print_header
cleanup_coverage
run_phpunit_coverage
run_vitest_coverage
local end_time=$(date +%s)
local duration=$((end_time - start_time))
print_summary
echo -e "${SPARKLES} ${BOLD}Coverage Complete${RESET}"
echo -e "Duration: ${YELLOW}${duration}s${RESET}\n"
open_reports "$1"
}
main "$@"
-35
View File
@@ -1,35 +0,0 @@
#!/bin/bash
# Default to the current directory if no directory is provided
dir=${1:-.}
# Optional: A list of file extensions to filter by, e.g., "txt md". Leave empty to include all files.
extensions=($2)
# Temporary file to store results
temp_file=$(mktemp)
# Function to print file details (now inline within find command)
print_file_details() {
echo "File Path: $1"
echo "Contents:"
cat "$1"
echo
}
# Finding and processing files
if [ ${#extensions[@]} -eq 0 ]; then
# If no extensions are specified, process all files
find "$dir" -type f -exec bash -c 'echo "File Path: $1"; echo "Contents:"; cat "$1"; echo' bash {} \; >> "$temp_file"
else
# Process only files with specified extensions
for ext in "${extensions[@]}"; do
find "$dir" -type f -name "*.$ext" -exec bash -c 'echo "File Path: $1"; echo "Contents:"; cat "$1"; echo' bash {} \; >> "$temp_file"
done
fi
# Copy results to clipboard and remove the temporary file
cat "$temp_file" | pbcopy
rm "$temp_file"
echo "Results copied to clipboard."
Executable
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
set -o pipefail
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
readonly WARNING="⚠️"
readonly BOOKS="📚"
readonly HAMMER="🏗️"
readonly SPARKLES="✨"
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Documentation Builder ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}yoeunes${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$(git rev-parse --abbrev-ref HEAD)${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
install_dependencies() {
echo -e "${BOLD}${BOOKS} Installing dependencies${RESET}"
if npm install --force; then
echo -e "${CHECK} ${GREEN}Dependencies installed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Dependency installation had issues${RESET}\n"
return 1
fi
}
build_documentation() {
echo -e "${BOLD}${HAMMER} Building documentation${RESET}"
if npm run build; then
echo -e "${CHECK} ${GREEN}Documentation built successfully${RESET}\n"
return 0
else
echo -e "${ERROR} ${RED}Documentation build failed${RESET}\n"
return 1
fi
}
main() {
local command=${1:-"build"}
local start_time=$(date +%s)
local success=true
# Change directory to docs/
if [[ ! -d "docs/" ]]; then
echo -e "${ERROR} ${RED}Documentation directory 'docs/' not found${RESET}\n"
exit 1
fi
cd docs/
print_header
echo -e "${BOLD}Command : ${RESET}${CYAN}${command}${RESET}\n"
case $command in
"build")
install_dependencies || success=false
build_documentation || success=false
;;
*)
echo -e "${ERROR} ${RED}Unknown command: ${command}${RESET}"
echo -e "Available commands: build"
exit 1
;;
esac
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Summary
echo -e "${BOLD}${CYAN}Documentation ${command^} Summary${RESET}"
if [ "$success" = true ]; then
echo -e "${SPARKLES} ${GREEN}Documentation ${command} completed successfully${RESET}"
else
echo -e "${WARNING} ${YELLOW}Documentation ${command} completed with issues${RESET}"
fi
echo -e "Duration : ${YELLOW}${duration}s${RESET}\n"
[ "$success" = true ] && exit 0 || exit 1
}
main "$@"
Executable
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/env bash
set -o pipefail
# PHP binary to use (defaults to PHP 8.2)
PHP_BINARY="${PHP_BINARY:-/opt/homebrew/opt/php@8.2/bin/php}"
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
readonly WARNING="⚠️"
readonly SEARCH="🔍"
readonly PALETTE="🎨"
readonly MICROSCOPE="🔬"
readonly MEMO="📝"
readonly MAGNIFIER="🔎"
readonly TEST_TUBE="🧪"
readonly SPARKLES="✨"
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Code Quality Check ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}yoeunes${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$(git rev-parse --abbrev-ref HEAD)${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
run_rector() {
echo -e "${BOLD}${SEARCH} Running Rector${RESET}"
if $PHP_BINARY vendor/bin/rector; then
echo -e "${CHECK} ${GREEN}Rector completed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Rector found issues${RESET}\n"
return 1
fi
}
run_php_cs_fixer() {
echo -e "${BOLD}${PALETTE} Running PHP-CS-Fixer${RESET}"
if $PHP_BINARY vendor/bin/php-cs-fixer fix -v; then
echo -e "${CHECK} ${GREEN}PHP-CS-Fixer completed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}CS-Fixer found issues${RESET}\n"
return 1
fi
}
run_phpstan() {
echo -e "${BOLD}${MICROSCOPE} Running PHPStan${RESET}"
if $PHP_BINARY vendor/bin/phpstan analyse --memory-limit=-1; then
echo -e "${CHECK} ${GREEN}PHPStan analysis completed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}PHPStan found issues${RESET}\n"
return 1
fi
}
validate_composer_files() {
echo -e "${BOLD}${MEMO} Validating Composer Files${RESET}"
local validation_success=true
if ! composer validate --strict; then
echo -e "${WARNING} ${YELLOW}Main composer.json validation failed${RESET}"
validation_success=false
fi
# Find and validate all composer.json files in src/
find src/ -name "composer.json" | while read file; do
echo -e "${DIM}Validating ${file}${RESET}"
if ! composer validate --strict "$file"; then
echo -e "${WARNING} ${YELLOW}Package validation failed for ${file}${RESET}"
validation_success=false
fi
done
if [ "$validation_success" = true ]; then
echo -e "${CHECK} ${GREEN}All composer.json files are valid${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Some composer.json files have issues${RESET}\n"
return 1
fi
}
run_phplint() {
echo -e "${BOLD}${MAGNIFIER} Running PHPLint${RESET}"
if $PHP_BINARY vendor/bin/phplint; then
echo -e "${CHECK} ${GREEN}PHPLint completed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}PHPLint found issues${RESET}\n"
return 1
fi
}
run_phpunit() {
echo -e "${BOLD}${TEST_TUBE} Running PHPUnit Tests${RESET}"
if $PHP_BINARY vendor/bin/phpunit; then
echo -e "${CHECK} ${GREEN}All tests passed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Tests failed${RESET}\n"
return 1
fi
}
run_vitest() {
echo -e "${BOLD}${TEST_TUBE} Running Vitest Tests${RESET}"
if npm run test -- --run; then
echo -e "${CHECK} ${GREEN}Vitest tests passed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Vitest tests failed${RESET}\n"
return 1
fi
}
main() {
local start_time=$(date +%s)
local issues_found=false
print_header
# Run all quality tools
run_rector || issues_found=true
run_php_cs_fixer || issues_found=true
run_phpstan || issues_found=true
validate_composer_files || issues_found=true
run_phplint || issues_found=true
run_phpunit || issues_found=true
run_vitest || issues_found=true
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Summary
echo -e "${BOLD}${CYAN}Quality Check Summary${RESET}"
if [ "$issues_found" = true ]; then
echo -e "${WARNING} ${YELLOW}Quality check completed with issues${RESET}"
else
echo -e "${SPARKLES} ${GREEN}All quality checks passed successfully${RESET}"
fi
echo -e "Duration : ${YELLOW}${duration}s${RESET}\n"
echo -e "${SPARKLES} ${BOLD}Quality Check Complete${RESET}"
echo -e "${CHECK} All checks finished\n"
# We don't exit with error code because the original task continues despite failures
exit 0
}
main
+217 -18
View File
@@ -1,26 +1,225 @@
#!/usr/bin/env bash
set -e
# Set options
set -o pipefail
NPM_FOLDERS=(
"src/Prime/Resources"
"src/Noty/Prime/Resources"
"src/Notyf/Prime/Resources"
"src/SweetAlert/Prime/Resources"
"src/Toastr/Prime/Resources"
# Define colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly GREEN='\033[32m'
readonly BLUE='\033[34m'
readonly CYAN='\033[36m'
readonly YELLOW='\033[33m'
readonly RED='\033[31m'
# Define emoji
readonly ROCKET="🚀"
readonly NPM="📦"
readonly CHECK="✓"
readonly WARN="⚠️"
readonly ERROR="❌"
readonly LOADING="⏳"
readonly SKIP="⏭️"
# Configuration
readonly NPM_PACKAGES=(
"src/Prime/Resources:@flasher/flasher"
"src/Noty/Prime/Resources:@flasher/flasher-noty"
"src/Notyf/Prime/Resources:@flasher/flasher-notyf"
"src/SweetAlert/Prime/Resources:@flasher/flasher-sweetalert"
"src/Toastr/Prime/Resources:@flasher/flasher-toastr"
)
for folder in "${NPM_FOLDERS[@]}"; do
if [ -d "$folder" ]; then
echo ""
echo "Publishing $folder to npm..."
(
cd "$folder"
npm install
npm version "$VERSION" --no-git-tag-version || true
npm publish --access public
)
# Print functions
print_header() {
echo -e "\n${BOLD}${BLUE}╭──────────────────────────────────────────╮${RESET}"
echo -e "${BOLD}${BLUE}│ ${ROCKET} PHP-Flasher NPM Release ${ROCKET} │${RESET}"
echo -e "${BOLD}${BLUE}╰──────────────────────────────────────────╯${RESET}\n"
echo -e " ${DIM}Date : $(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e " ${DIM}User : $(whoami)${RESET}"
echo -e " ${DIM}Directory: $(pwd)${RESET}"
}
print_section() {
echo -e "\n${BOLD}${CYAN}┌─ $1 ${2:-}${RESET}"
}
success_msg() {
echo -e " ${GREEN}${CHECK} $*${RESET}"
}
info_msg() {
echo -e " ${BLUE}${LOADING} $*${RESET}"
}
warning_msg() {
echo -e " ${YELLOW}${WARN} $*${RESET}"
}
error_msg() {
echo -e " ${RED}${ERROR} $*${RESET}"
}
skip_msg() {
echo -e " ${YELLOW}${SKIP} $*${RESET}"
}
print_summary() {
local version=$1
local duration=$2
local success_count=$3
local skipped_count=$4
local failed_count=$5
local failed_packages=("${@:6}")
print_section "Release Summary"
echo -e "\n ${DIM}Version : ${BOLD}v${version}${RESET}"
echo -e " ${DIM}Duration : ${BOLD}${duration}s${RESET}"
if [ "$success_count" -gt 0 ]; then
echo -e "\n ${DIM}Published : ${GREEN}${success_count}${RESET} packages"
fi
if [ "$skipped_count" -gt 0 ]; then
echo -e "\n ${DIM}Skipped : ${YELLOW}${skipped_count}${RESET} packages"
fi
if [ "$failed_count" -gt 0 ]; then
echo -e "\n ${DIM}Failed : ${RED}${failed_count}${RESET} packages"
echo -e "\n ${DIM}Failed packages:${RESET}"
for package in "${failed_packages[@]}"; do
echo -e " ${RED}• ${package}${RESET}"
done
fi
echo -e ""
if [ "$success_count" -eq 0 ] && [ "$skipped_count" -gt 0 ]; then
success_msg "All packages were already up to date! ${ROCKET}"
elif [ "$success_count" -gt 0 ]; then
success_msg "NPM release completed successfully! ${ROCKET}"
elif [ "$failed_count" -gt 0 ]; then
error_msg "NPM release completed with failures! ${ERROR}"
fi
}
# Check if version exists
check_version_exists() {
local package=$1
local version=$2
npm view "${package}@${version}" version >/dev/null 2>&1
}
# Validate NPM is installed and logged in
validate_npm() {
print_section "NPM Authentication Check"
if ! command -v npm >/dev/null 2>&1; then
error_msg "npm is not installed"
return 1
fi
if ! npm whoami >/dev/null 2>&1; then
error_msg "You are not logged in to npm. Please run 'npm login' first."
return 1
fi
success_msg "Authenticated as $(npm whoami)"
return 0
}
# Process a single package
process_package() {
local path_and_name=$1
local version=$2
local path="${path_and_name%:*}"
local package="${path_and_name#*:}"
print_section "Processing ${package}"
if [ ! -d "$path" ]; then
warning_msg "Directory $path does not exist, skipping..."
return 2
fi
info_msg "Checking version ${version}..."
if check_version_exists "$package" "$version"; then
skip_msg "Version ${version} already exists for ${package}, skipping..."
return 2
fi
info_msg "Installing dependencies..."
(cd "$path" && npm install --silent) || {
error_msg "Failed to install dependencies"
return 1
}
info_msg "Updating package version..."
(cd "$path" && npm version "$version" --no-git-tag-version --allow-same-version >/dev/null 2>&1) || true
info_msg "Publishing ${package}@${version}..."
if (cd "$path" && npm publish --access public); then
success_msg "Successfully published ${package}@${version}"
return 0
else
error_msg "Failed to publish ${package}"
return 1
fi
}
# Main execution
main() {
if [ "$#" -ne 1 ]; then
echo -e "\n${YELLOW}Usage: $0 <version>${RESET}"
echo -e "Example: $0 2.1.5\n"
exit 1
fi
local VERSION=$1
VERSION="${VERSION#v}"
print_header
# Validate npm authentication
validate_npm || exit 1
# Track statistics
local start_time=$(date +%s)
local success_count=0
local failed_count=0
local skipped_count=0
local failed_packages=()
# Process each package
for package_info in "${NPM_PACKAGES[@]}"; do
local package_name="${package_info#*:}"
process_package "$package_info" "$VERSION"
local status=$?
case $status in
0)
((success_count++))
;;
1)
((failed_count++))
failed_packages+=("$package_name")
;;
2)
((skipped_count++))
;;
esac
done
echo "Release $VERSION complete tagged repositories and published packages to npm."
# Print summary
local end_time=$(date +%s)
local duration=$((end_time - start_time))
print_summary "$VERSION" "$duration" "$success_count" "$skipped_count" "$failed_count" "${failed_packages[@]}"
# Exit with failure if any package failed
[ "$failed_count" -gt 0 ] && exit 1 || exit 0
}
# Execute main function
main "$@"
+230 -67
View File
@@ -1,80 +1,243 @@
#!/usr/bin/env bash
set -e
set -o pipefail
# Make sure the release tag is provided.
if (( "$#" != 1 ))
then
echo "Tag has to be provided."
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
exit 1
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
readonly SKIP="⏭️"
# Configuration
readonly RELEASE_BRANCH="2.x"
readonly REPOSITORIES=(
"flasher"
"flasher-laravel"
"flasher-symfony"
"flasher-noty"
"flasher-noty-laravel"
"flasher-noty-symfony"
"flasher-notyf"
"flasher-notyf-laravel"
"flasher-notyf-symfony"
"flasher-sweetalert"
"flasher-sweetalert-laravel"
"flasher-sweetalert-symfony"
"flasher-toastr"
"flasher-toastr-laravel"
"flasher-toastr-symfony"
)
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Release ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}$(whoami)${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$RELEASE_BRANCH${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
draw_progress_bar() {
local percent=$1
local width=50
local filled=$(((width * percent + 99) / 100)) # Rounded up division
local empty=$((width - filled))
echo -n "["
printf "${GREEN}█%.0s${RESET}" $(seq 1 $filled)
printf "${DIM}─%.0s${RESET}" $(seq 1 $empty)
echo -n "] ${percent}%"
}
check_tag_exists() {
local repo=$1
local tag=$2
local remote_url="git@github.com:php-flasher/$repo.git"
if git ls-remote --tags "$remote_url" | grep -q "refs/tags/$tag$"; then
return 0
else
return 1
fi
}
release_repository() {
local repo=$1
local version=$2
local tmp_dir="/tmp/php-flasher"
local remote_url="git@github.com:php-flasher/$repo.git"
echo -e "\nGitHub : ${UNDERLINE}${BLUE}https://github.com/php-flasher/${repo}${RESET}"
echo -e "Tag : ${YELLOW}$version${RESET}"
if check_tag_exists "$repo" "$version"; then
echo -e "${SKIP} ${YELLOW}Tag $version already exists, skipping...${RESET}"
return 0
fi
RELEASE_BRANCH="2.x"
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
VERSION=$1
# Clean and create temporary directory
rm -rf "$tmp_dir"
mkdir -p "$tmp_dir"
# Make sure current branch and release branch match.
if [[ "$RELEASE_BRANCH" != "$CURRENT_BRANCH" ]]
then
echo "Release branch ($RELEASE_BRANCH) does not match the current active branch ($CURRENT_BRANCH)."
exit 1
echo -e "${CYAN}Cloning repository...${RESET}"
if ! git clone "$remote_url" "$tmp_dir" >/dev/null 2>&1; then
echo -e "${ERROR} ${RED}Failed to clone repository${RESET}"
return 1
fi
# Make sure the working directory is clear.
if [[ ! -z "$(git status --porcelain)" ]]
then
echo "Your working directory is dirty. Did you forget to commit your changes?"
exit 1
fi
# Make sure latest changes are fetched first.
git fetch origin
# Make sure that release branch is in sync with origin.
if [[ $(git rev-parse HEAD) != $(git rev-parse origin/$RELEASE_BRANCH) ]]
then
echo "Your branch is out of date with its upstream. Did you forget to pull or push any changes before releasing?"
exit 1
fi
# Always prepend with "v"
if [[ $VERSION != v* ]]
then
VERSION="v$VERSION"
fi
# Tag PHPFlasher
git tag "$VERSION"
git push origin --tags --force
# Tag Repositories
for REMOTE in flasher flasher-laravel flasher-symfony \
flasher-noty flasher-noty-laravel flasher-noty-symfony \
flasher-notyf flasher-notyf-laravel flasher-notyf-symfony \
flasher-sweetalert flasher-sweetalert-laravel flasher-sweetalert-symfony \
flasher-toastr flasher-toastr-laravel flasher-toastr-symfony
do
echo ""
echo ""
echo "Releasing $REMOTE";
TMP_DIR="/tmp/php-flasher"
REMOTE_URL="git@github.com:php-flasher/$REMOTE.git"
rm -rf $TMP_DIR;
mkdir $TMP_DIR;
(
cd $TMP_DIR;
cd "$tmp_dir"
echo -e "${CYAN}Creating tag $version...${RESET}"
git checkout "$RELEASE_BRANCH" >/dev/null 2>&1
git tag "$version"
git clone $REMOTE_URL .
git checkout "$RELEASE_BRANCH";
git tag "$VERSION"
git push origin --tags --force
if ! git push origin --tags --force >/dev/null 2>&1; then
echo -e "${ERROR} ${RED}Failed to push tag${RESET}"
return 1
fi
)
echo -e "${CHECK} ${GREEN}Successfully tagged${RESET}"
return 0
}
validate_version() {
local version=$1
if [[ ! $version =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "\n${ERROR} ${RED}Invalid version format: $version${RESET}"
echo -e "Version must be in format X.Y.Z or vX.Y.Z (e.g., 2.1.6 or v2.1.6)\n"
exit 1
fi
}
validate_environment() {
echo -e "${CYAN}Validating environment...${RESET}"
local has_error=0
# Check branch
local current_branch=$(git rev-parse --abbrev-ref HEAD)
echo -ne "${CYAN}Checking branch...${RESET} "
if [[ "$RELEASE_BRANCH" != "$current_branch" ]]; then
echo -e "${ERROR} ${RED}Wrong branch: $current_branch (expected: $RELEASE_BRANCH)${RESET}"
has_error=1
else
echo -e "${CHECK} ${GREEN}On correct branch: $current_branch${RESET}"
fi
# Check working directory
echo -ne "${CYAN}Checking working directory...${RESET} "
if [[ ! -z "$(git status --porcelain)" ]]; then
echo -e "${ERROR} ${RED}Working directory is not clean${RESET}"
echo -e "${DIM}Hint: Commit or stash your changes first${RESET}"
has_error=1
else
echo -e "${CHECK} ${GREEN}Working directory is clean${RESET}"
fi
# Check sync with remote
echo -ne "${CYAN}Checking remote synchronization...${RESET} "
git fetch origin > /dev/null 2>&1
local local_sha=$(git rev-parse HEAD)
local remote_sha=$(git rev-parse origin/$RELEASE_BRANCH)
if [[ $local_sha != $remote_sha ]]; then
echo -e "${ERROR} ${RED}Branch is not in sync with origin/$RELEASE_BRANCH${RESET}"
echo -e "\n${YELLOW}Local SHA: ${local_sha}${RESET}"
echo -e "${YELLOW}Remote SHA: ${remote_sha}${RESET}"
echo -e "\n${DIM}To fix this, try:${RESET}"
# Check if we have unpushed commits
if git rev-list origin/$RELEASE_BRANCH..$RELEASE_BRANCH --count > /dev/null 2>&1; then
local ahead=$(git rev-list origin/$RELEASE_BRANCH..$RELEASE_BRANCH --count)
local behind=$(git rev-list $RELEASE_BRANCH..origin/$RELEASE_BRANCH --count)
if [[ $ahead -gt 0 && $behind -gt 0 ]]; then
echo -e "${YELLOW}Your branch is $ahead commits ahead and $behind commits behind origin/$RELEASE_BRANCH${RESET}"
echo -e "1. ${CYAN}git pull origin $RELEASE_BRANCH${RESET} (to get remote changes)"
echo -e "2. ${CYAN}git push origin $RELEASE_BRANCH${RESET} (to push your changes)"
elif [[ $ahead -gt 0 ]]; then
echo -e "${YELLOW}Your branch is $ahead commits ahead of origin/$RELEASE_BRANCH${RESET}"
echo -e "Run: ${CYAN}git push origin $RELEASE_BRANCH${RESET}"
elif [[ $behind -gt 0 ]]; then
echo -e "${YELLOW}Your branch is $behind commits behind origin/$RELEASE_BRANCH${RESET}"
echo -e "Run: ${CYAN}git pull origin $RELEASE_BRANCH${RESET}"
fi
fi
has_error=1
else
echo -e "${CHECK} ${GREEN}Branch is in sync with remote${RESET}"
fi
if [[ $has_error -eq 1 ]]; then
echo -e "\n${ERROR} ${RED}Environment validation failed${RESET}"
exit 1
fi
echo -e "\n${CHECK} ${GREEN}Environment validated successfully${RESET}\n"
}
main() {
if [ "$#" -ne 1 ]; then
echo -e "${ERROR} ${RED}Version tag required${RESET}"
echo -e "Usage: $0 <version>"
echo -e "Example: $0 2.1.6\n"
exit 1
fi
local version=$1
[[ $version != v* ]] && version="v$version"
print_header
validate_version "$version"
validate_environment
local success_count=0
local skipped_count=0
local failed_count=0
local total_count=${#REPOSITORIES[@]}
local start_time=$(date +%s)
for repo in "${REPOSITORIES[@]}"; do
local current_count=$((success_count + skipped_count + failed_count + 1))
local percent=$((current_count * 100 / total_count))
echo -e "\nProgress: ${BOLD}$current_count/$total_count${RESET}"
# draw_progress_bar $percent
if release_repository "$repo" "$version"; then
if check_tag_exists "$repo" "$version"; then
((skipped_count++))
else
((success_count++))
fi
else
((failed_count++))
fi
done
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Summary
echo -e "\n${BOLD}${CYAN}Release Summary${RESET}"
echo -e "Successful : ${GREEN}$success_count${RESET}"
echo -e "Skipped : ${YELLOW}$skipped_count${RESET}"
[ $failed_count -gt 0 ] && echo -e "Failed : ${RED}$failed_count${RESET}"
echo -e "Duration : ${YELLOW}${duration}s${RESET}"
echo -e "Total repos: ${CYAN}$total_count${RESET}\n"
[ $failed_count -gt 0 ] && exit 1 || exit 0
}
main "$@"
+129 -83
View File
@@ -1,108 +1,154 @@
#!/usr/bin/env bash
# Set the "errexit" options
set -o errexit
set -o pipefail
# Define colors and emoji for better visual feedback
INDIGO='\033[0;94m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
CHECK_MARK="✅"
CROSS_MARK="❌"
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
# Initialize global flags
DEBUG=0
DRY_RUN=0
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
# Process command-line arguments
for arg in "$@"; do
case $arg in
--debug)
DEBUG=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
*)
# Unknown option
;;
esac
done
# Repository mappings
declare -A REPOSITORIES=(
# Core packages
["src/Prime"]="flasher"
["src/Laravel"]="flasher-laravel"
["src/Symfony"]="flasher-symfony"
# Debug message function
debug_msg() {
if [ "$DEBUG" -eq 1 ]; then
echo -e "${INDIGO}Debug: $*${NC}"
fi
}
# Toastr packages
["src/Toastr/Prime"]="flasher-toastr"
["src/Toastr/Laravel"]="flasher-toastr-laravel"
["src/Toastr/Symfony"]="flasher-toastr-symfony"
# Define remotes
REMOTES=(
'src/Prime:flasher'
'src/Laravel:flasher-laravel'
'src/Symfony:flasher-symfony'
# Notyf packages
["src/Notyf/Prime"]="flasher-notyf"
["src/Notyf/Laravel"]="flasher-notyf-laravel"
["src/Notyf/Symfony"]="flasher-notyf-symfony"
'src/Toastr/Prime:flasher-toastr'
'src/Toastr/Laravel:flasher-toastr-laravel'
'src/Toastr/Symfony:flasher-toastr-symfony'
# SweetAlert packages
["src/SweetAlert/Prime"]="flasher-sweetalert"
["src/SweetAlert/Laravel"]="flasher-sweetalert-laravel"
["src/SweetAlert/Symfony"]="flasher-sweetalert-symfony"
'src/Notyf/Prime:flasher-notyf'
'src/Notyf/Laravel:flasher-notyf-laravel'
'src/Notyf/Symfony:flasher-notyf-symfony'
'src/SweetAlert/Prime:flasher-sweetalert'
'src/SweetAlert/Laravel:flasher-sweetalert-laravel'
'src/SweetAlert/Symfony:flasher-sweetalert-symfony'
'src/Noty/Prime:flasher-noty'
'src/Noty/Laravel:flasher-noty-laravel'
'src/Noty/Symfony:flasher-noty-symfony'
# Noty packages
["src/Noty/Prime"]="flasher-noty"
["src/Noty/Laravel"]="flasher-noty-laravel"
["src/Noty/Symfony"]="flasher-noty-symfony"
)
# Function to get the current git branch name
function current_branch() {
git rev-parse --abbrev-ref HEAD
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Split ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}$(whoami)${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$(git rev-parse --abbrev-ref HEAD)${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
# Define a function to split and push code to a remote repository
function split() {
local prefix_and_remote="$1"
local prefix="${prefix_and_remote%:*}"
local remote="${prefix_and_remote#*:}"
local current_branch=$(current_branch)
process_split() {
local prefix=$1
local remote=${REPOSITORIES[$prefix]}
local branch=$(git rev-parse --abbrev-ref HEAD)
local repo_url="https://github.com/php-flasher/${remote}"
local packagist_url="https://packagist.org/packages/php-flasher/${remote}"
# Add remote if it does not exist (ignoring errors silently)
if git remote add "$remote" "git@github.com:php-flasher/$remote.git" 2>/dev/null; then
echo -e "${GREEN}Added remote ${INDIGO}$remote${NC} ${CHECK_MARK}"
else
debug_msg "Remote $remote already exists or could not be added."
echo -e "\nGitHub : ${UNDERLINE}${BLUE}${repo_url}${RESET}"
echo -e "Package : ${UNDERLINE}${BLUE}${packagist_url}${RESET}"
echo -e "Prefix : ${YELLOW}$prefix${RESET}"
echo -e "Branch : ${GREEN}$branch${RESET}\n"
# Split the repository
echo -e "${CYAN}Splitting code...${RESET}"
local sha1
if ! sha1=$(./bin/splitsh-lite --prefix="$prefix" 2>/dev/null); then
echo -e "${ERROR} ${RED}Split failed${RESET}"
return 1
fi
# Split the code using the splitsh-lite utility
SHA1=$(./bin/splitsh-lite --prefix="$prefix")
debug_msg "SHA1 for $prefix is $SHA1."
echo -e "${CHECK} Split complete (SHA: ${YELLOW}${sha1:0:8}${RESET})"
echo -e "${CYAN}Pushing to remote...${RESET}"
# Push the code to the remote repository on the same branch as the current branch
if [ "$DRY_RUN" -eq 0 ]; then
git push "$remote" "$SHA1:refs/heads/$current_branch" -f
else
echo -e "${INDIGO}Dry run: Would push $SHA1 to $remote on branch $current_branch${NC}"
if ! git push "$remote" "$sha1:refs/heads/$branch" -f > /dev/null 2>&1; then
echo -e "${ERROR} ${RED}Failed to push to $remote${RESET}"
return 1
fi
echo -e "${CHECK} ${GREEN}Pushed to $remote (branch: $branch)${RESET}"
return 0
}
# Pull the latest code from the origin repository
if [ "$DRY_RUN" -eq 0 ]; then
echo -e "${INDIGO}Pulling the latest code from the origin repository on branch ${current_branch}...${NC}"
git fetch origin "$current_branch"
update_repository() {
echo -e "\n${CYAN}Fetching latest changes...${RESET}"
if ! git fetch origin "$(git rev-parse --abbrev-ref HEAD)" > /dev/null 2>&1; then
echo -e "${ERROR} ${RED}Failed to fetch latest changes, continuing...${RESET}"
else
echo -e "${INDIGO}Dry run: Would fetch latest code for branch $current_branch from the origin repository.${NC}"
echo -e "${CHECK} ${GREEN}Repository updated successfully${RESET}"
fi
echo
}
# Iterate over the remotes and split and push the code
for remote in "${REMOTES[@]}"; do
split "$remote"
draw_progress_bar() {
local percent=$1
local width=50
local filled=$(( (width * percent + 99) / 100 )) # Rounded up division
local empty=$(( width - filled ))
echo -n "["
printf "${GREEN}█%.0s${RESET}" $(seq 1 $filled)
printf "${DIM}─%.0s${RESET}" $(seq 1 $empty)
echo -n "] ${percent}%"
}
main() {
print_header
update_repository
local success_count=0
local failed_count=0
local total_count=${#REPOSITORIES[@]}
local start_time=$(date +%s)
for prefix in "${!REPOSITORIES[@]}"; do
local current_count=$((success_count + failed_count + 1))
local percent=$((current_count * 100 / total_count))
echo -e "\nProgress: ${BOLD}$current_count/$total_count${RESET}"
# draw_progress_bar $percent
if process_split "$prefix"; then
((success_count++))
else
((failed_count++))
fi
done
echo -e "${GREEN}All done!${NC} ${CHECK_MARK}"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Summary
echo -e "\n${BOLD}${CYAN}Split Summary${RESET}"
echo -e "Successful : ${GREEN}$success_count${RESET}"
[ $failed_count -gt 0 ] && echo -e "Failed : ${RED}$failed_count${RESET}"
echo -e "Duration : ${YELLOW}${duration}s${RESET}"
echo -e "Total repos : ${CYAN}$total_count${RESET}"
# Links
echo -e "\n${BOLD}${CYAN}Quick Links${RESET}"
echo -e "Packagist : ${UNDERLINE}${BLUE}https://packagist.org/packages/php-flasher/${RESET}"
echo -e "NPM : ${UNDERLINE}${BLUE}https://www.npmjs.com/org/flasher${RESET}"
echo -e "GitHub : ${UNDERLINE}${BLUE}https://github.com/php-flasher${RESET}\n"
[ $failed_count -gt 0 ] && exit 1 || exit 0
}
main
Executable
+331
View File
@@ -0,0 +1,331 @@
#!/usr/bin/env bash
# Set options
set -e
set -o pipefail
# Define colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly ITALIC='\033[3m'
readonly UNDERLINE='\033[4m'
readonly RED='\033[31m'
readonly GREEN='\033[32m'
readonly YELLOW='\033[33m'
readonly BLUE='\033[34m'
readonly MAGENTA='\033[35m'
readonly CYAN='\033[36m'
readonly WHITE='\033[37m'
# Define emoji
readonly ROCKET="🚀"
readonly PACKAGE="📦"
readonly CALENDAR="📅"
readonly CLOCK="🕒"
readonly INFO=""
readonly WARNING="⚠️"
readonly CHECK="✓"
readonly BRANCH="🌿"
readonly TAG="🏷️"
readonly GITHUB="⭐"
readonly STAR="⭐"
readonly NPM="📦"
readonly PACKAGIST="🎯"
# Configuration
readonly ORGANIZATION="php-flasher"
readonly MAIN_BRANCH="2.x"
readonly REPOSITORIES=(
"flasher"
"flasher-laravel"
"flasher-symfony"
"flasher-noty"
"flasher-noty-laravel"
"flasher-noty-symfony"
"flasher-notyf"
"flasher-notyf-laravel"
"flasher-notyf-symfony"
"flasher-sweetalert"
"flasher-sweetalert-laravel"
"flasher-sweetalert-symfony"
"flasher-toastr"
"flasher-toastr-laravel"
"flasher-toastr-symfony"
)
# Print functions
print_header() {
echo -e "\n${BOLD}${BLUE}╭──────────────────────────────────────────╮${RESET}"
echo -e "${BOLD}${BLUE}│ ${ROCKET} PHP-Flasher Status ${ROCKET} │${RESET}"
echo -e "${BOLD}${BLUE}╰──────────────────────────────────────────╯${RESET}\n"
echo -e " ${DIM}Date : $(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e " ${DIM}User : $(whoami)${RESET}"
echo -e " ${DIM}Directory: $(pwd)${RESET}"
}
print_section() {
echo -e "\n${BOLD}${CYAN} $1 ${2:-}${RESET}"
}
warning_msg() {
echo -e " ${YELLOW}${WARNING} $*${RESET}"
}
success_msg() {
echo -e " ${GREEN}${CHECK} $*${RESET}"
}
error_msg() {
echo -e " ${RED}${ERROR} $*${RESET}"
}
info_msg() {
echo -e "${BLUE}${INFO} $*${RESET}"
}
# Get the latest tag for a repository
get_latest_tag() {
local repo=$1
git ls-remote --tags --refs "git@github.com:${ORGANIZATION}/${repo}.git" |
sort -t '/' -k 3 -V |
tail -n1 |
awk -F/ '{print $3}'
}
# Get commit count since last tag
get_commit_count_since_tag() {
local repo=$1
local tag=$2
git rev-list "${tag}..HEAD" --count 2>/dev/null || echo "0"
}
# Get repository statistics
get_repo_stats() {
local repo=$1
local tmp_dir="/tmp/php-flasher-status/${repo}"
mkdir -p "$tmp_dir"
if [ ! -d "$tmp_dir/.git" ]; then
git clone -q "git@github.com:${ORGANIZATION}/${repo}.git" "$tmp_dir" 2>/dev/null
fi
(
cd "$tmp_dir"
git fetch -q origin 2>/dev/null
echo "$(git rev-parse --short HEAD)|$(git rev-parse --abbrev-ref HEAD)|$(git log -1 --format='%cr')|$(git log -1 --format='%s')"
)
}
# Check composer.json version
check_composer_version() {
local file="composer.json"
if [ -f "$file" ]; then
grep -o '"version": *"[^"]*"' "$file" 2>/dev/null | cut -d'"' -f4 || echo "N/A"
else
echo "N/A"
fi
}
# Add curl with proper user agent for API calls
curl_cmd() {
# You can set your GitHub token as an environment variable
local gh_token=${GITHUB_TOKEN:-""}
local auth_header=""
if [ ! -z "$gh_token" ]; then
auth_header="-H \"Authorization: token $gh_token\""
fi
curl -s -H "User-Agent: PHP-Flasher-Status-Check" $auth_header "$@"
}
# Get GitHub stars for a repository
get_github_stars() {
local repo=$1
local response=$(curl_cmd "https://api.github.com/repos/${ORGANIZATION}/${repo}")
local stars=$(echo "$response" | grep -o '"stargazers_count":[0-9]*' | cut -d':' -f2)
echo "${stars:-0}"
}
# Get Packagist version
get_packagist_version() {
local package=$1
local response=$(curl -s -f "https://repo.packagist.org/p2/php-flasher/${package}.json")
if [ $? -eq 0 ] && [ ! -z "$response" ]; then
local version=$(echo "$response" | grep -o '"latest":"[^"]*"' | cut -d'"' -f4)
if [ ! -z "$version" ]; then
echo "$version"
else
echo "N/A"
fi
else
echo "N/A"
fi
}
# Get NPM version
get_npm_version() {
local package=$1
# Convert package name format
if [[ "$package" == "flasher" ]]; then
package="flasher"
elif [[ "$package" == *"-prime" ]]; then
# Remove -prime suffix for npm package name
package=${package%-prime}
elif [[ "$package" == *"-laravel" || "$package" == *"-symfony" ]]; then
# These packages don't have npm versions
echo "N/A"
return
fi
local response=$(curl_cmd "https://registry.npmjs.org/@flasher/${package}/latest")
local version=$(echo "$response" | grep -o '"version":"[^"]*"' | cut -d'"' -f4)
echo "${version:-N/A}"
}
# Display repository information
display_repo_info() {
local repo=$1
local latest_tag=$(get_latest_tag "$repo")
local stats=($(get_repo_stats "$repo" | tr '|' ' '))
local github_stars=$(get_github_stars "$repo")
local npm_version="N/A"
local packagist_version="N/A"
# Get NPM version for specific packages
if [[ "$repo" == "flasher" || \
"$repo" == "flasher-noty" || \
"$repo" == "flasher-notyf" || \
"$repo" == "flasher-sweetalert" || \
"$repo" == "flasher-toastr" ]]; then
npm_version=$(get_npm_version "$repo")
fi
# Get Packagist version
packagist_version=$(get_packagist_version "$repo")
# Display repository information
echo -e "\n${BOLD}${MAGENTA} ${PACKAGE} ${repo}${RESET}"
echo -e " ${BRANCH} Branch : ${stats[1]:-unknown}"
echo -e " ${TAG} Latest Tag : ${latest_tag:-none}"
echo -e " ${STAR} Stars : ${github_stars}"
echo -e " ${PACKAGIST} Packagist : v${packagist_version}"
if [ "$npm_version" != "N/A" ]; then
echo -e " ${NPM} NPM : v${npm_version}"
fi
echo -e " ${GITHUB} Last Commit : ${stats[0]:-unknown} (${stats[2]:-unknown})"
echo -e " ${INFO} Message : ${stats[3]:-unknown}"
local commits_since_tag=$(get_commit_count_since_tag "$repo" "$latest_tag")
if [ "$commits_since_tag" -gt 0 ]; then
warning_msg "$commits_since_tag commit(s) since last tag"
fi
}
# Check git status
check_git_status() {
print_section "Git Status" "${GITHUB}"
local current_branch=$(git rev-parse --abbrev-ref HEAD)
echo -e " ${BRANCH} Current Branch : ${current_branch}"
if [ "$current_branch" != "$MAIN_BRANCH" ]; then
warning_msg " Not on main branch ($MAIN_BRANCH)"
fi
if [[ ! -z "$(git status --porcelain)" ]]; then
warning_msg " Working directory is not clean"
git status --short | sed 's/^/ /'
else
success_msg " Working directory is clean"
fi
local behind=$(git rev-list HEAD..origin/$current_branch --count 2>/dev/null || echo "0")
local ahead=$(git rev-list origin/$current_branch..HEAD --count 2>/dev/null || echo "0")
if [ "$behind" -gt 0 ]; then
warning_msg " Branch is behind by $behind commit(s)"
fi
if [ "$ahead" -gt 0 ]; then
warning_msg " Branch is ahead by $ahead commit(s)"
fi
if [ "$behind" -eq 0 ] && [ "$ahead" -eq 0 ]; then
success_msg " Branch is up to date with origin"
fi
}
# Check dependencies
check_dependencies() {
print_section "Dependencies" "${PACKAGE}"
if [ -f "composer.json" ]; then
echo -e " ${BOLD}Composer Dependencies:${RESET}"
composer show | grep "php-flasher/" | sed 's/^/ /' || echo " No PHP-Flasher dependencies found"
fi
if [ -f "package.json" ]; then
echo -e "\n ${BOLD}NPM Dependencies:${RESET}"
npm list | grep "@flasher/" | sed 's/^/ /' || echo " No Flasher dependencies found"
fi
}
# Display modified files
display_modified_files() {
print_section "Modified Files" "📝"
local modified_files=$(git diff --name-only)
if [ ! -z "$modified_files" ]; then
echo -e " ${BOLD}Modified files:${RESET}"
echo "$modified_files" | sed 's/^/ /'
else
success_msg " No modified files"
fi
}
# Main execution
main() {
print_header
# Check current repository status
check_git_status
# Check dependencies
check_dependencies
# Display modified files
display_modified_files
# Display repositories information
print_section "Repositories Status" "${PACKAGE}"
for repo in "${REPOSITORIES[@]}"; do
display_repo_info "$repo"
done
# Display release readiness
print_section "Release Readiness" "${ROCKET}"
local ready=true
if [ ! -z "$(git status --porcelain)" ]; then
warning_msg "Working directory is not clean"
ready=false
fi
if [ "$(git rev-parse --abbrev-ref HEAD)" != "$MAIN_BRANCH" ]; then
warning_msg "Not on main branch ($MAIN_BRANCH)"
ready=false
fi
if $ready; then
success_msg "Ready for release!"
echo -e "\n${BOLD}${GREEN}Suggested next steps:${RESET}"
echo -e "1. Run: ${ITALIC}./bin/split${RESET}"
echo -e "2. Run: ${ITALIC}./bin/release <version>${RESET}"
else
warning_msg "Not ready for release. Please fix the issues above."
fi
}
# Execute main function
main
Executable
+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
set -o pipefail
# Colors and styles
readonly RESET='\033[0m'
readonly BOLD='\033[1m'
readonly DIM='\033[2m'
readonly UNDERLINE='\033[4m'
readonly BLUE='\033[34m'
readonly GREEN='\033[32m'
readonly RED='\033[31m'
readonly YELLOW='\033[33m'
readonly CYAN='\033[36m'
readonly MAGENTA='\033[35m'
# Essential emojis
readonly ROCKET="🚀"
readonly CHECK="✓"
readonly ERROR="❌"
readonly PACKAGE="📦"
readonly SEARCH="🔍"
readonly HAMMER="🏗️"
readonly SPARKLES="✨"
readonly WARNING="⚠️"
print_header() {
echo -e "\n${BOLD}${BLUE}${ROCKET} PHP-Flasher Update ${ROCKET}${RESET}\n"
echo -e "${BOLD}Date : ${RESET}${CYAN}$(date -u '+%Y-%m-%d %H:%M:%S') UTC${RESET}"
echo -e "${BOLD}User : ${RESET}${MAGENTA}$(whoami)${RESET}"
echo -e "${BOLD}Branch : ${RESET}${GREEN}$(git rev-parse --abbrev-ref HEAD)${RESET}"
echo -e "${BOLD}Directory : ${RESET}${BLUE}$(pwd)${RESET}\n"
}
update_composer_dependencies() {
echo -e "${BOLD}${PACKAGE} Composer Dependencies${RESET}"
if composer update --prefer-lowest -W; then
echo -e "${CHECK} ${GREEN}Dependencies updated successfully${RESET}\n"
return 0
else
echo -e "${ERROR} ${RED}Failed to update Composer dependencies${RESET}\n"
return 1
fi
}
check_npm_updates() {
echo -e "${BOLD}${SEARCH} NPM Updates Check${RESET}"
# Run the commands and capture their exit codes
ncu -u
NCU_EXIT_CODE=$?
npm run ncu --workspaces
NPM_EXIT_CODE=$?
# Check if both commands were successful
if [ $NCU_EXIT_CODE -eq 0 ] && [ $NPM_EXIT_CODE -eq 0 ]; then
echo -e "${CHECK} ${GREEN}NPM check completed successfully${RESET}\n"
else
echo -e "${WARNING} ${YELLOW}NPM check failed, continuing...${RESET}\n"
fi
return 0 # Continue regardless of outcome
}
update_npm_dependencies() {
echo -e "${BOLD}${PACKAGE} NPM Dependencies${RESET}"
if npm install --force; then
echo -e "${CHECK} ${GREEN}NPM dependencies installed successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}NPM install failed, continuing...${RESET}\n"
return 0 # Continue despite failure
fi
}
build_assets() {
echo -e "${BOLD}${HAMMER} Building Assets${RESET}"
if npm run build; then
echo -e "${CHECK} ${GREEN}Assets built successfully${RESET}\n"
return 0
else
echo -e "${WARNING} ${YELLOW}Build failed, continuing...${RESET}\n"
return 0 # Continue despite failure
fi
}
main() {
local start_time=$(date +%s)
local success=true
print_header
# Update composer dependencies
update_composer_dependencies || success=false
# Check NPM updates
check_npm_updates
# Update NPM dependencies
update_npm_dependencies
# Build assets
build_assets
local end_time=$(date +%s)
local duration=$((end_time - start_time))
# Summary
echo -e "${BOLD}${CYAN}Update Summary${RESET}"
if [ "$success" = true ]; then
echo -e "${SPARKLES} ${GREEN}Update completed successfully${RESET}"
else
echo -e "${WARNING} ${YELLOW}Update completed with some issues${RESET}"
fi
echo -e "Duration : ${YELLOW}${duration}s${RESET}\n"
[ "$success" = true ] && exit 0 || exit 1
}
main
+12 -12
View File
@@ -22,21 +22,22 @@
"require": {
"php": ">=8.2",
"ext-intl": "*",
"illuminate/contracts": "^11.0|^12.0",
"illuminate/routing": "^11.0|^12.0",
"illuminate/support": "^11.0|^12.0",
"illuminate/contracts": "^11.0|^12.0|^13.0",
"illuminate/routing": "^11.0|^12.0|^13.0",
"illuminate/support": "^11.0|^12.0|^13.0",
"laravel/octane": "^2.3",
"livewire/livewire": "^3.0",
"paragonie/random_compat": "^2.0",
"psr/container": "^1.1|^2.0",
"symfony/config": "^7.0",
"symfony/console": "^7.0",
"symfony/dependency-injection": "^7.0",
"symfony/framework-bundle": "^7.0",
"symfony/http-kernel": "^7.0",
"symfony/translation": "^7.0",
"symfony/twig-bundle": "^7.0",
"symfony/ux-twig-component": "^2.19"
"symfony/config": "^7.0|^8.0",
"symfony/console": "^7.0|^8.0",
"symfony/dependency-injection": "^7.0|^8.0",
"symfony/framework-bundle": "^7.0|^8.0",
"symfony/http-kernel": "^7.0|^8.0",
"symfony/translation": "^7.0|^8.0",
"symfony/twig-bundle": "^7.0|^8.0",
"symfony/ux-twig-component": "^2.19",
"yoeunes/regex-parser": "^1.0"
},
"require-dev": {
"larastan/larastan": "^2.9.9",
@@ -51,7 +52,6 @@
"phpunit/phpunit": "^10.5.26",
"rector/rector": "^1.2.8",
"rector/swiss-knife": "^1.0.0",
"spatie/ray": "^1.41.2",
"symplify/monorepo-builder": "^11.2.22"
},
"autoload": {
Generated
+958 -537
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
8.2
8.3
@@ -0,0 +1,54 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DashboardController extends Controller
{
/**
* Show the dashboard with overview of PHPFlasher features
*/
public function index()
{
flash()->option('timeout', 10000)
->info('Welcome to the PHPFlasher Laravel Demo! 👋 Explore different examples using the navigation.');
return view('dashboard');
}
/**
* Show all notification types at once
*/
public function showAllTypes()
{
flash()->success('This is a success notification!');
flash()->info('This is an information notification!');
flash()->warning('This is a warning notification!');
flash()->error('This is an error notification!');
return redirect()->back();
}
/**
* Show theme selector
*/
public function themeSelector()
{
return view('features.themes');
}
/**
* Display a notification with the selected theme
*/
public function showTheme(Request $request)
{
$theme = $request->input('theme', 'default');
flash()
->use("theme.$theme")
->success("This notification uses the '$theme' theme!");
return redirect()->route('themes');
}
}
@@ -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.');
}
}
@@ -0,0 +1,99 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ExampleController extends Controller
{
/**
* Form validation example
*/
public function formExample()
{
return view('examples.form');
}
/**
* Process form submission
*/
public function processForm(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:2|max:50',
'email' => 'required|email',
'subject' => 'required|min:5',
'message' => 'required|min:10',
]);
if ($validator->fails()) {
flash()->error('Please fix the errors in the form!');
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Success scenario
flash()
->success('Your message has been sent successfully!')
->option('timeout', 8000);
return redirect()->route('form.example');
}
/**
* AJAX example page
*/
public function ajaxExample()
{
return view('examples.ajax');
}
/**
* Process AJAX request
*/
public function processAjax(Request $request)
{
$action = $request->input('action');
$success = rand(0, 10) > 3; // 70% success rate
if ($success) {
flash()->success("The $action action was completed successfully!");
return response()->json(['status' => 'success']);
} else {
flash()->error("The $action action failed. Please try again.");
return response()->json(['status' => 'error'], 422);
}
}
/**
* Demonstrate different notification positions
*/
public function positionsExample()
{
return view('examples.positions');
}
/**
* Show notification at specified position
*/
public function showAtPosition(Request $request)
{
$position = $request->input('position', 'top-right');
flash()
->option('position', $position)
->info("This notification appears at the '$position' position!");
return redirect()->route('positions.example');
}
/**
* Interactive playground to test notification options
*/
public function playground()
{
return view('examples.playground');
}
}
+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');
}
}
+19 -19
View File
@@ -14,27 +14,27 @@
}
],
"require": {
"php": "^8.2",
"inertiajs/inertia-laravel": "^2.0.1",
"laravel/framework": "^11.43.2",
"laravel/tinker": "^2.10.1",
"livewire/livewire": "^3.5.20",
"php": "^8.3",
"inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^13.0",
"laravel/tinker": "^3.0",
"livewire/livewire": "^3.6",
"php-flasher/php-flasher": "@dev",
"spatie/laravel-csp": "^2.10.3",
"spatie/laravel-ray": "^1.39.1"
"spatie/laravel-csp": "^3.23",
"spatie/laravel-ray": "^1.40"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.15",
"fakerphp/faker": "^1.24.1",
"larastan/larastan": "^3.1",
"barryvdh/laravel-debugbar": "^4.1",
"fakerphp/faker": "^1.24",
"larastan/larastan": "^3.2",
"laravel/pint": "^1.21",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6.12",
"nunomaduro/collision": "^8.6.1",
"pestphp/pest": "^3.7.4",
"pestphp/pest-plugin-laravel": "^3.1",
"spatie/laravel-ignition": "^2.9.1",
"spatie/ray": "^1.41.5"
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.7",
"pestphp/pest": "^4.0",
"pestphp/pest-plugin-laravel": "^4.0",
"spatie/laravel-ignition": "^2.9",
"spatie/ray": "^1.41"
},
"autoload": {
"psr-4": {
@@ -52,15 +52,15 @@
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"@php artisan flasher:install --symlink"
"@php artisan flasher:install"
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
+2446 -1866
View File
File diff suppressed because it is too large Load Diff
+58 -8
View File
@@ -1,14 +1,64 @@
<?php
return [
declare(strict_types=1);
use Flasher\Prime\Configuration;
/*
* Default PHPFlasher configuration for Laravel.
*
* This configuration file defines the default settings for PHPFlasher when
* used within a Laravel application. It uses the Configuration class from
* the core PHPFlasher library to establish type-safe configuration.
*
* @return array<string, mixed> PHPFlasher configuration
*/
return Configuration::from([
// Default notification library (e.g., 'flasher', 'toastr', 'noty', 'notyf', 'sweetalert')
'default' => 'flasher',
// Path to the main PHPFlasher JavaScript file
'main_script' => '/vendor/flasher/flasher.min.js',
// List of CSS files to style your notifications
'styles' => [
'/vendor/flasher/flasher.min.css',
],
// Set global options for all notifications (optional)
// 'options' => [
// 'timeout' => 5000, // Time in milliseconds before the notification disappears
// 'position' => 'top-right', // Where the notification appears on the screen
// ],
// Automatically inject JavaScript and CSS assets into your HTML pages
'inject_assets' => true,
'options' => [
'timeout' => 5000, // in milliseconds
'position' => 'top-right',
// Enable message translation using Laravel's translation service
'translate' => true,
// URL patterns to exclude from asset injection and flash_bag conversion
'excluded_paths' => [],
// Map Laravel flash message keys to notification types
'flash_bag' => [
'success' => ['success'],
'error' => ['error', 'danger'],
'warning' => ['warning', 'alarm'],
'info' => ['info', 'notice', 'alert'],
],
'flash_bag' => [
'success' => ['success', 'ok', 'completed', 'passed', 'achieved'],
],
];
// Set criteria to filter which notifications are displayed (optional)
// 'filter' => [
// 'limit' => 5, // Maximum number of notifications to show at once
// ],
// Define notification presets to simplify notification creation (optional)
// 'presets' => [
// 'entity_saved' => [
// 'type' => 'success',
// 'title' => 'Entity saved',
// 'message' => 'Entity saved successfully',
// ],
// ],
]);
+13
View File
@@ -49,6 +49,19 @@ return [
'encrypt' => env('SESSION_ENCRYPT', false),
/*
|--------------------------------------------------------------------------
| Session Serialization
|--------------------------------------------------------------------------
|
| This option determines how session data is serialized before being stored.
| The "json" driver is more secure and performant but cannot serialize
| PHP objects. The "php" driver uses PHP's native serialization.
|
*/
'serialization' => env('SESSION_SERIALIZATION', 'json'),
/*
|--------------------------------------------------------------------------
| Session File Location
+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,83 @@
@extends('layouts.app')
@section('title', 'PHPFlasher Laravel Demo')
@section('content')
<div class="max-w-7xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-indigo-600 font-semibold">Laravel Demo</div>
<h2 class="mt-2 text-3xl font-extrabold tracking-tight text-gray-900">Welcome to PHPFlasher</h2>
<p class="mt-4 text-lg text-gray-500">
PHPFlasher is a powerful notification library for PHP applications.
This demo shows how to use PHPFlasher in a Laravel project.
</p>
<div class="mt-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
<!-- Feature Cards -->
<div class="bg-indigo-50 p-6 rounded-lg">
<div class="text-indigo-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Easy Integration</h3>
<p class="text-gray-600">Simple API to create notifications from anywhere in your Laravel application.</p>
</div>
<div class="bg-emerald-50 p-6 rounded-lg">
<div class="text-emerald-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<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" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Multiple Themes</h3>
<p class="text-gray-600">Choose from 15+ beautiful themes or create your own custom themes.</p>
</div>
<div class="bg-amber-50 p-6 rounded-lg">
<div class="text-amber-600 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<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" />
</svg>
</div>
<h3 class="font-bold text-lg mb-2">Flexible Positioning</h3>
<p class="text-gray-600">Display notifications at any corner or side of your screen.</p>
</div>
</div>
<div class="mt-10">
<h3 class="text-lg font-medium text-gray-900">Try It Now</h3>
<div class="mt-4 flex flex-wrap gap-2">
<a href="{{ route('all.types') }}" class="btn-primary">
Show All Notification Types
</a>
<a href="{{ route('themes') }}" class="btn-outline">
Explore Themes
</a>
</div>
</div>
<div class="mt-10">
<h3 class="text-lg font-medium text-gray-900">Code Example</h3>
<div class="mt-4 code-block">
<div class="code-header">
<span>Basic Usage</span>
</div>
<pre><code class="language-php">// Display a success notification
flash()->success('Item created successfully!');
// Display an error notification
flash()->error('An error occurred!');
// Display a warning notification
flash()->warning('Warning: This action cannot be undone.');
// Display an info notification
flash()->info('The task is running in the background.');</code></pre>
</div>
</div>
</div>
</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
@@ -0,0 +1,67 @@
@extends('layouts.app')
@section('title', 'Form Example - PHPFlasher Laravel Demo')
@section('content')
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Contact Form Example</h2>
<div class="mb-8">
<p class="text-gray-600">
This example demonstrates how PHPFlasher can be used to display form validation errors and success messages.
</p>
</div>
<div class="code-block mb-8">
<div class="code-header">
<span>Controller Code</span>
</div>
<pre><code class="language-php">public function processForm(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:2|max:50',
'email' => 'required|email',
'subject' => 'required|min:5',
'message' => 'required|min:10',
]);
if ($validator->fails()) {
flash()->error('Please fix the errors in the form!');
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Success scenario
flash()
->success('Your message has been sent successfully!')
->option('timeout', 8000);
return redirect()->route('form.example');
}</code></pre>
</div>
<form action="{{ route('form.process') }}" method="POST" class="space-y-6">
@csrf
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
<input type="text" name="name" id="name" value="{{ old('name') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
@error('name')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" name="email" id="email" value="{{ old('email') }}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
@error('email')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label for="subject" class="block text-sm font-medium text-gray-700">Subject</label>
<input type="text" name="subject" id="subject" value="{{ old('subject
+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>
<button wire:click="decrement">-</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="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,41 @@
<?php
it('stores and retrieves flash notifications with json serialization', function () {
// Trigger a flash notification
$this->get('/');
flash()->success('Test notification', ['title' => 'Success']);
// Make another request to retrieve from session
$response = $this->get('/');
$response->assertStatus(200);
});
it('handles multiple flash notifications across requests', function () {
flash()->success('First message');
flash()->error('Second message');
flash()->warning('Third message');
$response = $this->get('/');
$response->assertStatus(200);
});
it('preserves notification properties through json session roundtrip', function () {
flash()
->options(['position' => 'top-right', 'timeout' => 5000])
->success('Test message');
$response = $this->get('/');
$response->assertStatus(200);
});
it('works with different notification types', function () {
flash()->success('Success message');
flash()->error('Error message');
flash()->warning('Warning message');
flash()->info('Info message');
$response = $this->get('/');
$response->assertStatus(200);
});
@@ -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 ###
+1 -1
View File
@@ -1 +1 @@
8.2
8.4
+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
}
}
+1824 -5060
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],
];
+14 -3
View File
@@ -1,7 +1,7 @@
flasher:
# Default notification library (e.g., 'flasher', 'toastr', 'noty', etc.)
default: flasher
# themes: flasher, crystal, emerald, sapphire
default: theme.minimal
# Path to the main JavaScript file of PHPFlasher
main_script: '/vendor/flasher/flasher.min.js'
@@ -19,7 +19,7 @@ flasher:
# Global options
options:
# timeout in milliseconds
timeout: 5000
timeout: 600000
position: 'top-right'
# Map Symfony session keys to PHPFlasher notification types
@@ -33,3 +33,14 @@ flasher:
filter:
# Limit number of displayed notifications
# limit: 5
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
-3
View File
@@ -1,3 +0,0 @@
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
@@ -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'
@@ -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/'
@@ -1,6 +1,6 @@
when@dev:
web_profiler:
toolbar: true
toolbar: false
intercept_redirects: false
framework:
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"
}
@@ -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)
});
});
}
});
+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){if("object"==typeof e?(e=(o=e).type,t=o.message,s=o.title):"object"==typeof t?(t=(o=t).message,s=o.title):"object"==typeof s&&(s=(o=s).title),void 0===t)throw new Error("message option is required");const n={type:e,message:t,title:s||e,options:o||{},metadata:{plugin:""}};this.renderOptions(o||{}),this.renderEnvelopes([n])}}const o=new class extends s{renderEnvelopes(e){e.forEach((e=>{var s;const o=Object.assign({text:e.message,type:e.type},e.options),n=new t(o);n.show(),null===(s=n.layoutDom)||void 0===s||(s.dataset.turboTemporary="")}))}renderOptions(e){t.overrideDefaults(Object.assign({timeout:e.timeout||5e3},e))}};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
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("sweetalert2")):"function"==typeof define&&define.amd?define(["@flasher/flasher","sweetalert2"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).sweetalert=t(e.flasher,e.Swal)}(this,(function(e,t){"use strict";function s(e,t,s,n){return new(s||(s=Promise))((function(i,r){function o(e){try{a(n.next(e))}catch(e){r(e)}}function l(e){try{a(n.throw(e))}catch(e){r(e)}}function a(e){var t;e.done?i(e.value):(t=e.value,t instanceof s?t:new s((function(e){e(t)}))).then(o,l)}a((n=n.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class n{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,n){if("object"==typeof e?(e=(n=e).type,t=n.message,s=n.title):"object"==typeof t?(t=(n=t).message,s=n.title):"object"==typeof s&&(s=(n=s).title),void 0===t)throw new Error("message option is required");const i={type:e,message:t,title:s||e,options:n||{},metadata:{plugin:""}};this.renderOptions(n||{}),this.renderEnvelopes([i])}}const i=new class extends n{renderEnvelopes(e){return s(this,void 0,void 0,(function*(){for(const t of e)yield this.renderEnvelope(t)}))}renderOptions(e){this.sweetalert=this.sweetalert||t.mixin(Object.assign({timer:e.timer||5e3,timerProgressBar:e.timerProgressBar||!0},e)),document.addEventListener("turbo:before-cache",(()=>{var e;t.isVisible()&&(null===(e=t.getPopup())||void 0===e||e.style.setProperty("animation-duration","0ms"),t.close())}))}renderEnvelope(e){return s(this,void 0,void 0,(function*(){var t;let{options:s}=e;s=Object.assign(Object.assign({},s),{icon:(null==s?void 0:s.icon)||e.type,text:(null==s?void 0:s.text)||e.message}),yield null===(t=this.sweetalert)||void 0===t?void 0:t.fire(s).then((t=>{window.dispatchEvent(new CustomEvent("flasher:sweetalert:promise",{detail:{promise:t,envelope:e}}))}))}))}};return e.addPlugin("sweetalert",i),i}));
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("@flasher/flasher"),require("sweetalert2")):"function"==typeof define&&define.amd?define(["@flasher/flasher","sweetalert2"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).sweetalert=t(e.flasher,e.Swal)}(this,(function(e,t){"use strict";function r(e,t,r,s){return new(r||(r=Promise))((function(i,n){function o(e){try{a(s.next(e))}catch(e){n(e)}}function l(e){try{a(s.throw(e))}catch(e){n(e)}}function a(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(o,l)}a((s=s.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class s{success(e,t,r){this.flash("success",e,t,r)}error(e,t,r){this.flash("error",e,t,r)}info(e,t,r){this.flash("info",e,t,r)}warning(e,t,r){this.flash("warning",e,t,r)}flash(e,t,r,s){let i,n,o,l={};if("object"==typeof e?(l=Object.assign({},e),i=l.type,n=l.message,o=l.title,delete l.type,delete l.message,delete l.title):"object"==typeof t?(l=Object.assign({},t),i=e,n=l.message,o=l.title,delete l.message,delete l.title):(i=e,n=t,null==r?(o=void 0,l=s||{}):"string"==typeof r?(o=r,l=s||{}):"object"==typeof r&&(l=Object.assign({},r),"title"in l?(o=l.title,delete l.title):o=void 0,s&&"object"==typeof s&&(l=Object.assign(Object.assign({},l),s)))),!i)throw new Error("Type is required for notifications");if(null==n)throw new Error("Message is required for notifications");null==o&&(o=i.charAt(0).toUpperCase()+i.slice(1));const a={type:i,message:n,title:o,options:l,metadata:{plugin:""}};this.renderOptions({}),this.renderEnvelopes([a])}}const i=new class extends s{renderEnvelopes(e){return r(this,void 0,void 0,(function*(){this.sweetalert||this.initializeSweetAlert();try{for(const t of e)yield this.renderEnvelope(t)}catch(e){console.error("PHPFlasher SweetAlert: Error rendering envelopes",e)}}))}renderOptions(e){try{this.sweetalert=this.sweetalert||t.mixin(Object.assign({timer:e.timer||1e4,timerProgressBar:e.timerProgressBar||!0},e)),this.setupTurboCompatibility()}catch(e){console.error("PHPFlasher SweetAlert: Error applying options",e)}}renderEnvelope(e){return r(this,void 0,void 0,(function*(){var t;try{let{options:r}=e;r=Object.assign(Object.assign({},r),{icon:(null==r?void 0:r.icon)||e.type,text:(null==r?void 0:r.text)||e.message});const s=yield null===(t=this.sweetalert)||void 0===t?void 0:t.fire(r);this.dispatchResultEvent(s,e)}catch(t){console.error("PHPFlasher SweetAlert: Error rendering envelope",t,e)}}))}initializeSweetAlert(){this.sweetalert||this.renderOptions({timer:1e4,timerProgressBar:!0})}setupTurboCompatibility(){document.addEventListener("turbo:before-cache",(()=>{if(t.isVisible()){const e=t.getPopup();e&&e.style.setProperty("animation-duration","0ms"),t.close()}}))}dispatchResultEvent(e,t){e&&window.dispatchEvent(new CustomEvent("flasher:sweetalert:promise",{detail:{promise:e,envelope:t}}))}};return e.addPlugin("sweetalert",i),i}));
+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){if("object"==typeof e?(e=(r=e).type,t=r.message,s=r.title):"object"==typeof t?(t=(r=t).message,s=r.title):"object"==typeof s&&(s=(r=s).title),void 0===t)throw new Error("message option is required");const o={type:e,message:t,title:s||e,options:r||{},metadata:{plugin:""}};this.renderOptions(r||{}),this.renderEnvelopes([o])}}const r=new class extends s{renderEnvelopes(e){e.forEach((e=>{const{message:s,title:r,type:o,options:i}=e,n=t[o](s,r,i);n&&n.parent().attr("data-turbo-temporary","")}))}renderOptions(e){t.options=Object.assign({timeOut:e.timeOut||5e3,progressBar:e.progressBar||!0},e)}};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
+44 -9
View File
@@ -1,17 +1,52 @@
{
"/vendor/flasher/flasher.min.js": "/vendor/flasher/flasher.min.js?id=9a255a6680873c0d5fc3d394a2ba3195",
"/vendor/flasher/flasher.min.css": "/vendor/flasher/flasher.min.css?id=7a96e40c68626198d5128ad2fb5d77e0",
"/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=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=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=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=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=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=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=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=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=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=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=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=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=317b24bd9eee938ebd9c3221417fd27c",
"/vendor/flasher/mint.css": "/vendor/flasher/mint.css?id=348f135fff639305dde0005c647c1d20",
"/vendor/flasher/flasher-noty.min.js": "/vendor/flasher/flasher-noty.min.js?id=d7d160fe2043e65250dfcaa5590d2e28",
"/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=d8778adb84f0e2521d413c9ca995309c",
"/vendor/flasher/flasher-notyf.min.css": "/vendor/flasher/flasher-notyf.min.css?id=53f57ecf59a7045f8029243bde3ed054",
"/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=e0d38991a82d544068555ff7c2a2339b",
"/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=52e4f161dbe2cf71c99dfef9f94fb1ff",
"/vendor/flasher/sweetalert2.min.css": "/vendor/flasher/sweetalert2.min.css?id=a45abfe01a51ef4b1a068fb739f4e540",
"/vendor/flasher/flasher-sweetalert.min.js": "/vendor/flasher/flasher-sweetalert.min.js?id=4ede7e66493a3432961bb00ce83dd4da"
"/vendor/flasher/sweetalert2.min.js": "/vendor/flasher/sweetalert2.min.js?id=bba3afcfe7dcc339fa25fad49842c2bf",
"/vendor/flasher/sweetalert2.min.css": "/vendor/flasher/sweetalert2.min.css?id=83b54381abbed7676c22f411eba2c61d",
"/vendor/flasher/flasher-sweetalert.min.js": "/vendor/flasher/flasher-sweetalert.min.js?id=471a0b1aec6d6adc8e42a7c5dfcf0a93"
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
!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)}));
@@ -0,0 +1 @@
.fl-amber{--amber-bg-light:#fff;--amber-bg-dark:#1e293b;--amber-text-light:#4b5563;--amber-text-dark:#f1f5f9;--amber-shadow:0 5px 15px rgba(0,0,0,.08);--amber-shadow-dark:0 5px 15px rgba(0,0,0,.2);--amber-border-radius:4px;--amber-success:#10b981;--amber-info:#3b82f6;--amber-warning:#f59e0b;--amber-error:#ef4444}@keyframes amberIn{0%{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:translateY(0)}}.fl-amber{animation:amberIn .3s ease-out;background-color:var(--amber-bg-light);border-radius:var(--amber-border-radius) var(--amber-border-radius) 0 0;box-shadow:var(--amber-shadow);color:var(--amber-text-light);font-family:var(--fl-font),serif;margin:.6rem 0;padding:.85rem 1rem;position:relative;will-change:transform,opacity}.fl-amber .fl-content{align-items:center;display:flex}.fl-amber .fl-icon{font-size:1.85em;margin-right:.8rem}.fl-amber .fl-text{flex:1}.fl-amber .fl-message{font-size:.875em;line-height:1.4}.fl-amber .fl-close{background:none;border:none;color:currentColor;cursor:pointer;flex-shrink:0;font-size:1.15rem;margin-left:1rem;opacity:.6;padding:.25rem;touch-action:manipulation;transition:opacity .2s}.fl-amber .fl-close:focus,.fl-amber .fl-close:hover{opacity:1}.fl-amber .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-amber .fl-progress-bar .fl-progress{height:100%;width:100%}.fl-amber.fl-success .fl-close{color:var(--amber-success)}.fl-amber.fl-info .fl-close{color:var(--amber-info)}.fl-amber.fl-warning .fl-close{color:var(--amber-warning)}.fl-amber.fl-error .fl-close{color:var(--amber-error)}.fl-amber.fl-rtl{direction:rtl}.fl-amber.fl-rtl .fl-icon{margin-left:.8rem;margin-right:0}.fl-amber.fl-rtl .fl-close{margin-left:0;margin-right:1rem}.fl-amber.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-amber{animation:none}}.fl-amber.fl-auto-dark,body.fl-dark .fl-amber,html.fl-dark .fl-amber{background-color:var(--amber-bg-dark);box-shadow:var(--amber-shadow-dark);color:var(--amber-text-dark)}
@@ -0,0 +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="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)}));
@@ -0,0 +1 @@
.fl-aurora{--aurora-bg-light:#fff;--aurora-bg-dark:#14141c;--aurora-text-light:#1e293b;--aurora-text-dark:#f8fafc;--aurora-shadow:0 8px 25px rgba(0,0,0,.08);--aurora-shadow-dark:0 10px 30px rgba(0,0,0,.16);--aurora-border-radius:4px;--aurora-blur:15px;--aurora-success-gradient:linear-gradient(135deg,rgba(16,185,129,.08),rgba(16,185,129,.2));--aurora-info-gradient:linear-gradient(135deg,rgba(59,130,246,.08),rgba(59,130,246,.2));--aurora-warning-gradient:linear-gradient(135deg,rgba(245,158,11,.08),rgba(245,158,11,.2));--aurora-error-gradient:linear-gradient(135deg,rgba(239,68,68,.08),rgba(239,68,68,.2));--aurora-success:#10b981;--aurora-info:#3b82f6;--aurora-warning:#f59e0b;--aurora-error:#ef4444}@keyframes auroraFadeIn{0%{opacity:0;transform:translateY(-12px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.fl-aurora{animation:auroraFadeIn .35s cubic-bezier(.21,1.02,.73,1);backdrop-filter:blur(var(--aurora-blur));-webkit-backdrop-filter:blur(var(--aurora-blur));background-color:var(--aurora-bg-light);border-radius:var(--aurora-border-radius) var(--aurora-border-radius) 0 0;box-shadow:var(--aurora-shadow);color:var(--aurora-text-light);font-family:var(--fl-font),sans-serif;margin:10px 0;overflow:hidden;padding:16px 18px;position:relative;will-change:transform,opacity}.fl-aurora:before{border-radius:inherit;content:"";inset:0;opacity:.8;position:absolute;z-index:0}.fl-aurora .fl-content{align-items:center;display:flex;position:relative;z-index:1}.fl-aurora .fl-message{flex:1;font-size:.9375rem;font-weight:500;line-height:1.5;margin-right:10px}.fl-aurora .fl-close{align-items:center;background:rgba(0,0,0,.05);border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;flex-shrink:0;font-size:1rem;height:28px;justify-content:center;opacity:.7;transition:all .2s ease;width:28px}.fl-aurora .fl-close:focus,.fl-aurora .fl-close:hover{background:rgba(0,0,0,.1);opacity:1}.fl-aurora .fl-progress-bar{border-radius:6px;bottom:2px;height:3px;left:2px;opacity:.7;overflow:hidden;position:absolute;right:2px;z-index:1}.fl-aurora .fl-progress{height:100%;width:100%}.fl-aurora.fl-success:before{background:var(--aurora-success-gradient)}.fl-aurora.fl-success .fl-progress{background-color:var(--aurora-success)}.fl-aurora.fl-info:before{background:var(--aurora-info-gradient)}.fl-aurora.fl-info .fl-progress{background-color:var(--aurora-info)}.fl-aurora.fl-warning:before{background:var(--aurora-warning-gradient)}.fl-aurora.fl-warning .fl-progress{background-color:var(--aurora-warning)}.fl-aurora.fl-error:before{background:var(--aurora-error-gradient)}.fl-aurora.fl-error .fl-progress{background-color:var(--aurora-error)}.fl-aurora.fl-rtl{direction:rtl}.fl-aurora.fl-rtl .fl-message{margin-left:10px;margin-right:0}.fl-aurora.fl-rtl .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-aurora{animation:none}}.fl-aurora.fl-auto-dark,body.fl-dark .fl-aurora,html.fl-dark .fl-aurora{background-color:var(--aurora-bg-dark);box-shadow:var(--aurora-shadow-dark);color:var(--aurora-text-dark)}.fl-aurora.fl-auto-dark .fl-close,body.fl-dark .fl-aurora .fl-close,html.fl-dark .fl-aurora .fl-close{background:hsla(0,0%,100%,.1)}.fl-aurora.fl-auto-dark .fl-close:focus,.fl-aurora.fl-auto-dark .fl-close:hover,body.fl-dark .fl-aurora .fl-close:focus,body.fl-dark .fl-aurora .fl-close:hover,html.fl-dark .fl-aurora .fl-close:focus,html.fl-dark .fl-aurora .fl-close:hover{background:hsla(0,0%,100%,.15)}.fl-aurora.fl-auto-dark.fl-success:before,body.fl-dark .fl-aurora.fl-success:before,html.fl-dark .fl-aurora.fl-success:before{background:linear-gradient(135deg,rgba(16,185,129,.1),rgba(16,185,129,.25))}.fl-aurora.fl-auto-dark.fl-info:before,body.fl-dark .fl-aurora.fl-info:before,html.fl-dark .fl-aurora.fl-info:before{background:linear-gradient(135deg,rgba(59,130,246,.1),rgba(59,130,246,.25))}.fl-aurora.fl-auto-dark.fl-warning:before,body.fl-dark .fl-aurora.fl-warning:before,html.fl-dark .fl-aurora.fl-warning:before{background:linear-gradient(135deg,rgba(245,158,11,.1),rgba(245,158,11,.25))}.fl-aurora.fl-auto-dark.fl-error:before,body.fl-dark .fl-aurora.fl-error:before,html.fl-dark .fl-aurora.fl-error:before{background:linear-gradient(135deg,rgba(239,68,68,.1),rgba(239,68,68,.25))}
@@ -0,0 +1 @@
!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)}));
@@ -0,0 +1 @@
.fl-crystal{--crystal-bg-light:#fff;--crystal-bg-dark:rgba(30,30,30,.95);--crystal-text-light:#2c3e50;--crystal-text-dark:hsla(0,0%,100%,.95);--crystal-shadow:rgba(0,0,0,.08);--crystal-shadow-dark:rgba(0,0,0,.25)}@keyframes crystalIn{0%{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}@keyframes crystalPulse{0%{box-shadow:0 2px 8px var(--crystal-shadow)}50%{box-shadow:0 4px 12px var(--crystal-shadow)}to{box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal{animation:crystalIn .3s ease-out;background:var(--crystal-bg-light,var(--fl-bg-light));border-radius:var(--fl-border-radius,4px) var(--fl-border-radius,4px) 0 0;box-shadow:0 2px 8px var(--crystal-shadow);font-family:var(--fl-font),serif;margin:0 0 1rem;position:relative;transition:box-shadow .3s ease;will-change:transform,opacity}.fl-crystal:hover{animation:crystalPulse 2s ease-in-out infinite}.fl-crystal .fl-content{align-items:center;display:flex;gap:.75rem;padding:1rem 2.5rem 1rem 1rem}.fl-crystal .fl-text{flex:1}.fl-crystal .fl-message{color:var(--crystal-text-light,var(--fl-text-light));font-size:.9375rem;line-height:1.4;margin:0}.fl-crystal .fl-close{background:none;border:none;color:currentColor;cursor:pointer;font-size:1.25rem;line-height:1;opacity:.5;padding:.25rem;position:absolute;right:.75rem;top:50%;touch-action:manipulation;transform:translateY(-50%);transition:all .2s ease}.fl-crystal .fl-close:focus,.fl-crystal .fl-close:hover{opacity:1;transform:translateY(-50%) scale(1.1)}.fl-crystal .fl-progress-bar{bottom:0;height:3px;left:0;overflow:hidden;position:absolute;right:0}.fl-crystal .fl-progress-bar .fl-progress{height:100%;transform-origin:left center;width:100%}.fl-crystal.fl-success,.fl-crystal.fl-success .fl-message{color:var(--success-color,var(--fl-success))}.fl-crystal.fl-error,.fl-crystal.fl-error .fl-message{color:var(--error-color,var(--fl-error))}.fl-crystal.fl-warning,.fl-crystal.fl-warning .fl-message{color:var(--warning-color,var(--fl-warning))}.fl-crystal.fl-info,.fl-crystal.fl-info .fl-message{color:var(--info-color,var(--fl-info))}.fl-crystal.fl-rtl{direction:rtl}.fl-crystal.fl-rtl .fl-content{padding:1rem 1rem 1rem 2.5rem}.fl-crystal.fl-rtl .fl-close{left:.75rem;right:auto}.fl-crystal.fl-rtl .fl-progress .fl-progress{transform-origin:right center}@media (prefers-reduced-motion:reduce){.fl-crystal{animation:none}.fl-crystal:hover{animation:none;box-shadow:0 2px 8px var(--crystal-shadow)}}.fl-crystal.fl-auto-dark,body.fl-dark .fl-crystal,html.fl-dark .fl-crystal{background-color:var(--crystal-bg-dark,var(--fl-bg-dark));box-shadow:0 2px 8px var(--crystal-shadow-dark)}.fl-crystal.fl-auto-dark .fl-message,body.fl-dark .fl-crystal .fl-message,html.fl-dark .fl-crystal .fl-message{color:var(--crystal-text-dark,var(--fl-text-dark))}.fl-crystal.fl-auto-dark:hover,body.fl-dark .fl-crystal:hover,html.fl-dark .fl-crystal:hover{animation:none;box-shadow:0 4px 16px var(--crystal-shadow-dark)}
@@ -0,0 +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="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)}));
@@ -0,0 +1 @@
.fl-emerald{--emerald-bg-light:#fff;--emerald-bg-dark:#1e1e1e;--emerald-text-light:#333;--emerald-text-dark:hsla(0,0%,100%,.9);--emerald-shadow:rgba(0,0,0,.1);--emerald-blur:8px;--emerald-success:var(--success-color,#16a085);--emerald-error:var(--error-color,#e74c3c);--emerald-warning:var(--warning-color,#f39c12);--emerald-info:var(--info-color,#3498db)}@keyframes emeraldIn{0%{opacity:0;transform:scale(.5) translateY(20px)}60%{opacity:1;transform:scale(1.1) translateY(-5px)}to{opacity:1;transform:scale(1) translateY(0)}}.fl-emerald{animation:emeraldIn .5s cubic-bezier(.23,1,.32,1);backdrop-filter:blur(var(--emerald-blur));-webkit-backdrop-filter:blur(var(--emerald-blur));background:var(--emerald-bg-light);border-radius:4px 4px 0 0;box-shadow:0 10px 20px var(--emerald-shadow);color:var(--emerald-text-light);font-family:"Inter",var(--fl-font),serif;margin:0 0 .5rem;overflow:hidden;padding:1rem 1.5rem 1rem 1rem;position:relative}.fl-emerald .fl-content{align-items:center;display:flex}.fl-emerald .fl-message{flex:1;font-size:.9rem;font-weight:500;line-height:1.4}.fl-emerald .fl-close{background:transparent;border:none;color:currentColor;cursor:pointer;font-size:1.3rem;margin-left:auto;opacity:.7;transition:opacity .2s ease}.fl-emerald .fl-close:focus,.fl-emerald .fl-close:hover{opacity:1}.fl-emerald.fl-success{color:var(--emerald-success)}.fl-emerald.fl-error{color:var(--emerald-error)}.fl-emerald.fl-warning{color:var(--emerald-warning)}.fl-emerald.fl-info{color:var(--emerald-info)}.fl-emerald.fl-rtl{direction:rtl;padding:1rem 1rem 1rem 1.5rem}.fl-emerald.fl-rtl .fl-content{flex-direction:row-reverse}.fl-emerald.fl-rtl .fl-close{margin-left:0;margin-right:auto}@media (prefers-reduced-motion:reduce){.fl-emerald{animation:none}}.fl-emerald.fl-auto-dark,body.fl-dark .fl-emerald,html.fl-dark .fl-emerald{background:var(--emerald-bg-dark)}.fl-emerald.fl-auto-dark .fl-message,body.fl-dark .fl-emerald .fl-message,html.fl-dark .fl-emerald .fl-message{color:var(--emerald-text-dark)}
@@ -0,0 +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";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)}));
File diff suppressed because one or more lines are too long

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