UNPKG

@hadyfayed/filament-react-wrapper

Version:

Enterprise React integration for Laravel/Filament - Smart asset loading, 90%+ React-PHP function mapping, no-plugin Filament integration

180 lines (154 loc) 4.92 kB
// Laravel-style method calls inspired by MingleJS $wire export interface FilamentBridgeConfig { baseUrl?: string; token?: string; timeout?: number; } export class FilamentBridge { private config: FilamentBridgeConfig; private eventListeners: Map<string, Set<(data: any) => void>> = new Map(); constructor(config: FilamentBridgeConfig = {}) { this.config = { baseUrl: config.baseUrl || '/filament', token: config.token || this.getCSRFToken(), timeout: config.timeout || 5000, }; } // Laravel-style method calls async call(method: string, ...args: any[]): Promise<any> { try { const response = await fetch(`${this.config.baseUrl}/react-bridge`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': this.config.token || '', Accept: 'application/json', }, body: JSON.stringify({ method, args, }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error('FilamentBridge call failed:', error); throw error; } } // Event emission (Laravel events) emit(event: string, data: any = null): void { // Emit to server this.call('emit', event, data).catch(error => { console.error('Failed to emit event to server:', error); }); // Emit locally this.emitLocal(event, data); } // Local event emission emitLocal(event: string, data: any): void { const listeners = this.eventListeners.get(event); if (listeners) { listeners.forEach(listener => listener(data)); } } // Event listening on(event: string, callback: (data: any) => void): () => void { if (!this.eventListeners.has(event)) { this.eventListeners.set(event, new Set()); } this.eventListeners.get(event)!.add(callback); // Return unsubscribe function return () => { const listeners = this.eventListeners.get(event); if (listeners) { listeners.delete(callback); if (listeners.size === 0) { this.eventListeners.delete(event); } } }; } // State setting (similar to Livewire) set(path: string, value: any): Promise<any> { return this.call('set', path, value); } // State getting get(path: string): Promise<any> { return this.call('get', path); } // Form submission submit(form: Record<string, any>): Promise<any> { return this.call('submit', form); } // File upload async upload(file: File, path: string = 'uploads'): Promise<any> { const formData = new FormData(); formData.append('file', file); formData.append('path', path); const response = await fetch(`${this.config.baseUrl}/upload`, { method: 'POST', headers: { 'X-CSRF-TOKEN': this.config.token || '', }, body: formData, }); if (!response.ok) { throw new Error(`Upload failed! status: ${response.status}`); } return response.json(); } // Validation validate(data: Record<string, any>, rules: Record<string, string>): Promise<any> { return this.call('validate', data, rules); } // Refresh component refresh(): Promise<any> { return this.call('refresh'); } // Private helper methods private getCSRFToken(): string { const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); return token || ''; } } // Create singleton instance export const filamentBridge = new FilamentBridge(); // React hook for using the bridge export const useFilamentBridge = () => { return { $filament: { call: filamentBridge.call.bind(filamentBridge), emit: filamentBridge.emit.bind(filamentBridge), on: filamentBridge.on.bind(filamentBridge), set: filamentBridge.set.bind(filamentBridge), get: filamentBridge.get.bind(filamentBridge), submit: filamentBridge.submit.bind(filamentBridge), upload: filamentBridge.upload.bind(filamentBridge), validate: filamentBridge.validate.bind(filamentBridge), refresh: filamentBridge.refresh.bind(filamentBridge), }, }; }; // MingleJS-style $wire compatibility export const use$wire = () => { return { $wire: { call: filamentBridge.call.bind(filamentBridge), emit: filamentBridge.emit.bind(filamentBridge), set: filamentBridge.set.bind(filamentBridge), get: filamentBridge.get.bind(filamentBridge), submit: filamentBridge.submit.bind(filamentBridge), upload: filamentBridge.upload.bind(filamentBridge), refresh: filamentBridge.refresh.bind(filamentBridge), }, }; }; // Global access if (typeof window !== 'undefined') { (window as any).FilamentBridge = filamentBridge; (window as any).$filament = filamentBridge; }