components([ TextInput::make('title') ->required() ->live(onBlur: true) ->afterStateUpdated(function ($state, $set): void { $set('slug', Str::slug((string) $state)); }), TextInput::make('slug') ->required() ->dehydrated() ->readOnly(), Textarea::make('description') ->columnSpanFull(), SpatieMediaLibraryFileUpload::make('featured_image') ->collection('featured-image') ->image() ->imageEditor() ->disk('public') ->visibility('public') ->columnSpanFull() ->hintAction( Action::make('featured_picker') ->label('Pick from Gallery') ->icon('heroicon-m-photo') ->schema([ Select::make('image_id') ->label('Select an existing image') ->allowHtml() ->options(function () { return Media::latest() ->limit(30) // Limit to 30 most recent items for performance ->get(['id', 'file_name', 'name', 'uuid', 'collection_name', 'model_type', 'model_id', 'disk']) ->filter(function (Media $item) { // Only include media items that have a valid disk return $item->disk !== null; }) ->mapWithKeys(function (Media $item) { try { $url = $item->getUrl(); } catch (\Exception $e) { // Skip items that can't generate URLs return []; } $fileName = e($item->file_name); $name = e($item->name ?? ''); // Smaller image preview for better performance $html = "
" . "{$fileName}" . "
" . "{$name}" . "{$fileName}" . "
"; return [$item->id => $html]; })->toArray(); }) ->searchable() ->preload() ->required(), ]) ->action(function (array $data, SpatieMediaLibraryFileUpload $component) { $record = $component->getRecord(); if (!$record) { \Filament\Notifications\Notification::make() ->warning() ->title('Save the entry first') ->send(); return; } if ($mediaItem = Media::find($data['image_id'])) { // Clear existing featured image $record->clearMediaCollection('featured-image'); // Associate the existing media with this entry instead of copying $mediaItem->update([ 'model_type' => get_class($record), 'model_id' => $record->id, 'collection_name' => 'featured-image' ]); // Update component state $component->state([$mediaItem->uuid]); \Filament\Notifications\Notification::make() ->success() ->title('Featured image set') ->send(); } }) ), Toggle::make('is_published') ->required(), Toggle::make('is_featured') ->required(), DatePicker::make('published_at'), RichEditor::make('content') ->columnSpanFull() ->hintAction( Action::make('picker') ->label('Gallery Picker') ->icon('heroicon-m-photo') ->schema([ Select::make('image_url') ->label('Select an existing image') ->allowHtml() ->options(function () { return Media::latest() ->limit(30) // Limit to 30 most recent items for performance ->get(['id', 'file_name', 'name', 'uuid', 'collection_name', 'model_type', 'model_id', 'disk']) ->filter(function (Media $item) { // Only include media items that have a valid disk return $item->disk !== null; }) ->mapWithKeys(function (Media $item) { try { $url = $item->getUrl(); } catch (\Exception $e) { // Skip items that can't generate URLs return []; } $fileName = e($item->file_name); $name = e($item->name ?? ''); // Smaller image preview for better performance $html = "
" . "{$fileName}" . "
" . "{$name}" . "{$fileName}" . "
"; return [$url => $html]; })->toArray(); }) ->searchable() ->preload() ->required(), ]) ->action(function (array $data, RichEditor $component) { // We dispatch the URL to the browser to be inserted into TipTap $component->getLivewire()->dispatch('insert-editor-content', [ 'statePath' => $component->getStatePath(), 'html' => "", ]); }) ), ]); } }