refactor sweealert plugin and improve code documentation

This commit is contained in:
Younes ENNAJI
2025-03-08 11:59:01 +00:00
parent 71b21855c1
commit b4c9df4549
2 changed files with 203 additions and 24 deletions
@@ -1,7 +1,55 @@
/**
* @file SweetAlert Plugin Entry Point
* @description Registers the SweetAlert plugin with PHPFlasher
* @author yoeunes
*/
import flasher from '@flasher/flasher'
import SweetAlertPlugin from './sweetalert'
/**
* Create and register the SweetAlert plugin with PHPFlasher.
*
* This enables using SweetAlert for displaying notifications through
* the PHPFlasher API.
*
* @example
* ```typescript
* // With the plugin already registered
* import flasher from '@flasher/flasher';
*
* // Basic success message
* flasher.use('sweetalert').success('Operation completed');
*
* // Confirmation dialog
* flasher.use('sweetalert').warning('Are you sure?', 'Confirmation', {
* showCancelButton: true,
* confirmButtonText: 'Yes, delete it!',
* cancelButtonText: 'Cancel'
* });
*
* // Listen for user interaction results
* window.addEventListener('flasher:sweetalert:promise', (event) => {
* const { promise, envelope } = event.detail;
* if (promise.isConfirmed) {
* // User clicked confirm button
* console.log('User confirmed', envelope);
* }
* });
* ```
*/
const sweetalert = new SweetAlertPlugin()
flasher.addPlugin('sweetalert', sweetalert)
/**
* Export the SweetAlert plugin instance.
*
* This allows direct access to the plugin if needed.
*
* @example
* ```typescript
* import sweetalert from '@flasher/flasher-sweetalert';
*
* sweetalert.success('Operation completed');
* ```
*/
export default sweetalert
@@ -1,49 +1,180 @@
/**
* @file SweetAlert Plugin Implementation
* @description PHPFlasher integration with the SweetAlert2 library
* @author yoeunes
*/
import { AbstractPlugin } from '@flasher/flasher/dist/plugin'
import type { Envelope, Options } from '@flasher/flasher/dist/types'
import type { SweetAlertOptions } from 'sweetalert2'
import type { SweetAlertOptions, SweetAlertResult } from 'sweetalert2'
import Swal from 'sweetalert2'
/** Type alias for Swal constructor */
type SwalType = typeof Swal
/**
* Plugin implementation for SweetAlert2 notification library.
*
* The SweetAlertPlugin integrates SweetAlert2 with PHPFlasher, allowing
* for beautiful, responsive modal dialogs with a wide range of customization
* options.
*
* @example
* ```typescript
* import flasher from '@flasher/flasher';
* import SweetAlertPlugin from '@flasher/flasher-sweetalert';
*
* // Register the plugin
* flasher.addPlugin('sweetalert', new SweetAlertPlugin());
*
* // Show a notification
* flasher.use('sweetalert').success('Operation completed');
* ```
*/
export default class SweetAlertPlugin extends AbstractPlugin {
sweetalert?: SwalType
/**
* The SweetAlert instance used to display notifications.
* Lazy-initialized when first needed.
*
* @private
*/
private sweetalert?: SwalType
/**
* Creates SweetAlert notifications from envelopes.
*
* This method processes each envelope sequentially, ensuring
* modals are displayed one after another rather than simultaneously.
*
* @param envelopes - Array of notification envelopes to render
* @returns Promise that resolves when all notifications have been displayed
*/
public async renderEnvelopes(envelopes: Envelope[]): Promise<void> {
for (const envelope of envelopes) {
await this.renderEnvelope(envelope)
if (!this.sweetalert) {
this.initializeSweetAlert()
}
try {
// Process envelopes sequentially to avoid multiple modals at once
for (const envelope of envelopes) {
await this.renderEnvelope(envelope)
}
} catch (error) {
console.error('PHPFlasher SweetAlert: Error rendering envelopes', error)
}
}
/**
* Apply global options to SweetAlert.
*
* This method configures SweetAlert with default options that will
* be applied to all subsequent modal dialogs.
*
* @param options - Configuration options for SweetAlert
*/
public renderOptions(options: Options): void {
this.sweetalert = this.sweetalert || Swal.mixin({
timer: (options.timer || 5000) as unknown,
timerProgressBar: (options.timerProgressBar || true) as unknown,
...options,
} as SweetAlertOptions)
try {
// Create a SweetAlert mixin with default options
this.sweetalert = this.sweetalert || Swal.mixin({
timer: (options.timer || 10000) as unknown, // Default 10 seconds
timerProgressBar: (options.timerProgressBar || true) as unknown,
...options,
} as SweetAlertOptions)
// Handle Turbo/Hotwire page transitions
this.setupTurboCompatibility()
} catch (error) {
console.error('PHPFlasher SweetAlert: Error applying options', error)
}
}
/**
* Render a single notification envelope as a SweetAlert modal.
*
* This method transforms a PHPFlasher envelope into a SweetAlert modal
* and dispatches a custom event with the result for potential listeners.
*
* @param envelope - The notification envelope to render
* @returns Promise that resolves when the modal is closed
* @private
*/
private async renderEnvelope(envelope: Envelope): Promise<void> {
try {
// Extract and merge options
let { options } = envelope
// Set up the SweetAlert options
options = {
...options,
icon: (options?.icon || envelope.type) as unknown,
text: (options?.text || envelope.message) as unknown,
}
// Show the modal and get the result
const promise = await this.sweetalert?.fire(options as SweetAlertOptions)
// Dispatch a custom event with the result and envelope
this.dispatchResultEvent(promise, envelope)
} catch (error) {
console.error('PHPFlasher SweetAlert: Error rendering envelope', error, envelope)
}
}
/**
* Initialize SweetAlert with default options if not already created.
*
* @private
*/
private initializeSweetAlert(): void {
if (!this.sweetalert) {
this.renderOptions({
timer: 10000, // 10 seconds
timerProgressBar: true,
})
}
}
/**
* Set up event listeners for compatibility with Turbo/Hotwire page transitions.
* Ensures modals are properly closed when navigating between pages.
*
* @private
*/
private setupTurboCompatibility(): void {
// Close any open modals before Turbo caches the page
document.addEventListener('turbo:before-cache', () => {
if (Swal.isVisible()) {
Swal.getPopup()?.style.setProperty('animation-duration', '0ms')
// Remove animations to prevent visual glitches during page transitions
const popup = Swal.getPopup()
if (popup) {
popup.style.setProperty('animation-duration', '0ms')
}
// Close the modal immediately
Swal.close()
}
})
}
private async renderEnvelope(envelope: Envelope): Promise<void> {
let { options } = envelope
options = {
...options,
icon: (options?.icon || envelope.type) as unknown[],
text: (options?.text || envelope.message) as unknown[],
/**
* Dispatch a custom event with the SweetAlert result and envelope.
* This allows external code to react to modal interactions.
*
* @param promise - The result from SweetAlert
* @param envelope - The original notification envelope
* @private
*/
private dispatchResultEvent(
promise: SweetAlertResult<any> | undefined,
envelope: Envelope,
): void {
if (promise) {
window.dispatchEvent(new CustomEvent('flasher:sweetalert:promise', {
detail: {
promise,
envelope,
},
}))
}
await this.sweetalert?.fire(options as SweetAlertOptions).then((promise) => {
window.dispatchEvent(new CustomEvent('flasher:sweetalert:promise', { detail: {
promise,
envelope,
} }))
})
}
}