From 3d56324159c48165edb31c4e49e101b6ac467416 Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Thu, 9 Oct 2025 19:14:56 +0545 Subject: [PATCH 1/3] fix: Renamed mediaColumns methods to avoid trait conflict and updated Action namespace --- .../Components/Concerns/HasMediaPreview.php | 12 +++++------ .../Forms/Components/MediaGallery.php | 2 +- tests/Feature/MediaGalleryTest.php | 20 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Filament/Forms/Components/Concerns/HasMediaPreview.php b/src/Filament/Forms/Components/Concerns/HasMediaPreview.php index 916cf3e..207a5c3 100644 --- a/src/Filament/Forms/Components/Concerns/HasMediaPreview.php +++ b/src/Filament/Forms/Components/Concerns/HasMediaPreview.php @@ -51,14 +51,14 @@ public function thumbnailHeight(int|Closure $height): static return $this; } - public function columns(array|string|int|null $columns = 2): static + public function mediaColumns(array|string|int|null $columns = 2): static { $this->mediaColumns = $columns; return $this; } - public function gridColumns(int $columns): static + public function mediaGridColumns(int $columns): static { $this->mediaColumns = $columns; @@ -85,7 +85,7 @@ public function getThumbnailHeight(): int return $this->evaluate($this->thumbnailHeight); } - public function getColumns(?string $breakpoint = null): array|string|int|null + public function getMediaColumns(?string $breakpoint = null): array|string|int|null { $columns = $this->evaluate($this->mediaColumns); @@ -98,7 +98,7 @@ public function getColumns(?string $breakpoint = null): array|string|int|null public function getGridStyle(): string { - $columns = $this->getColumns(); + $columns = $this->getMediaColumns(); if (is_array($columns)) { $default = $columns['default'] ?? 4; @@ -133,9 +133,9 @@ public function getGridClasses(): string return 'grid gap-3'; } - public function getGridColumns(): int + public function getMediaGridColumns(): int { - $columns = $this->getColumns(); + $columns = $this->getMediaColumns(); if (is_array($columns)) { return $columns['default'] ?? 4; diff --git a/src/Filament/Forms/Components/MediaGallery.php b/src/Filament/Forms/Components/MediaGallery.php index 407a4c5..02fc4eb 100644 --- a/src/Filament/Forms/Components/MediaGallery.php +++ b/src/Filament/Forms/Components/MediaGallery.php @@ -7,7 +7,7 @@ use Eclipse\Common\Filament\Forms\Components\Concerns\HasMediaPreview; use Eclipse\Common\Filament\Forms\Components\Concerns\HasMediaUploadOptions; use Exception; -use Filament\Forms\Components\Actions\Action; +use Filament\Actions\Action; use Filament\Forms\Components\Concerns\HasLoadingMessage; use Filament\Forms\Components\Concerns\HasNestedRecursiveValidationRules; use Filament\Forms\Components\Concerns\HasPlaceholder; diff --git a/tests/Feature/MediaGalleryTest.php b/tests/Feature/MediaGalleryTest.php index 73f104e..0c6d012 100644 --- a/tests/Feature/MediaGalleryTest.php +++ b/tests/Feature/MediaGalleryTest.php @@ -1,7 +1,7 @@ columns(6) + ->mediaColumns(6) ->thumbnailHeight(200) ->lightbox(false) ->orderable(false); - expect($field->getColumns())->toBe(6) + expect($field->getMediaColumns())->toBe(6) ->and($field->getThumbnailHeight())->toBe(200) ->and($field->hasLightbox())->toBeFalse() ->and($field->isDragReorderable())->toBeFalse(); @@ -53,7 +53,7 @@ expect($field->hasLightbox())->toBeFalse() ->and($field->isDragReorderable())->toBeFalse() - ->and($field->getColumns())->toBe(4) + ->and($field->getMediaColumns())->toBe(4) ->and($field->getThumbnailHeight())->toBe(150) ->and($field->getAllowFileUploads())->toBeFalse() ->and($field->getAllowUrlUploads())->toBeFalse(); @@ -138,20 +138,20 @@ test('media gallery supports responsive columns', function () { $field = MediaGallery::make('images') - ->columns([ + ->mediaColumns([ 'default' => 2, 'sm' => 3, 'lg' => 4, 'xl' => 6, ]); - expect($field->getColumns())->toBe([ + expect($field->getMediaColumns())->toBe([ 'default' => 2, 'sm' => 3, 'lg' => 4, 'xl' => 6, ]) - ->and($field->getGridColumns())->toBe(2) + ->and($field->getMediaGridColumns())->toBe(2) ->and($field->getGridClasses())->toBe('grid gap-3') ->and($field->getGridStyle())->toContain('grid-template-columns: repeat(2, 1fr)') ->and($field->getGridStyle())->toContain('@media (min-width: 640px)') @@ -159,10 +159,10 @@ }); test('media gallery columns method works with simple integer', function () { - $field = MediaGallery::make('images')->columns(5); + $field = MediaGallery::make('images')->mediaColumns(5); - expect($field->getColumns())->toBe(5) - ->and($field->getGridColumns())->toBe(5) + expect($field->getMediaColumns())->toBe(5) + ->and($field->getMediaGridColumns())->toBe(5) ->and($field->getGridClasses())->toBe('grid gap-3') ->and($field->getGridStyle())->toBe('grid-template-columns: repeat(5, 1fr);'); }); From 209879eea8332bf032fce9c8c17fdd917646b21e Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Thu, 9 Oct 2025 20:18:14 +0545 Subject: [PATCH 2/3] refactor: remove duplicate methods --- docs/MediaGallery.md | 16 ++++++++-------- .../Components/Concerns/HasMediaPreview.php | 18 ------------------ tests/Feature/MediaGalleryTest.php | 2 -- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/docs/MediaGallery.md b/docs/MediaGallery.md index 12397f4..f317400 100644 --- a/docs/MediaGallery.md +++ b/docs/MediaGallery.md @@ -36,7 +36,7 @@ MediaGallery::make('images') ```php MediaGallery::make('images') - ->columns(6) // Number of columns (default: 4) + ->mediaColumns(6) // Number of columns (default: 4) ->thumbnailHeight(200) // Thumbnail height in pixels (default: 150) ->preview() // Enable lightbox preview (disabled by default) ->orderable() // Enable drag & drop reordering (disabled by default) @@ -46,7 +46,7 @@ MediaGallery::make('images') ```php MediaGallery::make('images') - ->columns([ + ->mediaColumns([ 'default' => 2, 'sm' => 3, 'lg' => 4, @@ -57,7 +57,7 @@ MediaGallery::make('images') ## Methods Available ### Layout Control -- `columns(int|array)` - Set number of grid columns or responsive column configuration (default: 4) +- `mediaColumns(int|array)` - Set number of grid columns or responsive column configuration (default: 4) - `thumbnailHeight(int)` - Set thumbnail image height in pixels (default: 150) ### Interactive Features @@ -108,7 +108,7 @@ MediaGallery::make('images') ->collection('gallery') ->allowUploads() ->preview() // Enables lightbox - ->columns(3) + ->mediaColumns(3) ->thumbnailHeight(180) ``` @@ -118,7 +118,7 @@ MediaGallery::make('images') ->collection('portfolio') ->allowUploads() ->orderable() // Enables drag & drop - ->columns(5) + ->mediaColumns(5) ->maxFiles(50) ``` @@ -135,7 +135,7 @@ MediaGallery::make('images') MediaGallery::make('images') ->collection('external-media') ->allowUrlUploads() // Only URL uploads - ->columns(6) + ->mediaColumns(6) ``` ### Complete Configuration @@ -144,7 +144,7 @@ MediaGallery::make('images') ->collection('products') ->allowUploads() // Enable both upload methods ->maxFiles(20) - ->columns(4) + ->mediaColumns(4) ->thumbnailHeight(180) ->preview() // Enable lightbox ->orderable() // Enable reordering @@ -156,7 +156,7 @@ MediaGallery::make('images') MediaGallery::make('images') ->collection(fn () => $this->record?->category . '-images') ->maxFiles(fn () => $this->record?->isPremium() ? 50 : 10) - ->columns(fn () => $this->getColumnCount()) + ->mediaColumns(fn () => $this->getColumnCount()) ``` ## Default Behaviors diff --git a/src/Filament/Forms/Components/Concerns/HasMediaPreview.php b/src/Filament/Forms/Components/Concerns/HasMediaPreview.php index 207a5c3..24c5322 100644 --- a/src/Filament/Forms/Components/Concerns/HasMediaPreview.php +++ b/src/Filament/Forms/Components/Concerns/HasMediaPreview.php @@ -58,13 +58,6 @@ public function mediaColumns(array|string|int|null $columns = 2): static return $this; } - public function mediaGridColumns(int $columns): static - { - $this->mediaColumns = $columns; - - return $this; - } - public function hasLightbox(): bool { return $this->evaluate($this->hasLightbox); @@ -132,15 +125,4 @@ public function getGridClasses(): string { return 'grid gap-3'; } - - public function getMediaGridColumns(): int - { - $columns = $this->getMediaColumns(); - - if (is_array($columns)) { - return $columns['default'] ?? 4; - } - - return (int) ($columns ?? 4); - } } diff --git a/tests/Feature/MediaGalleryTest.php b/tests/Feature/MediaGalleryTest.php index 0c6d012..aa86e44 100644 --- a/tests/Feature/MediaGalleryTest.php +++ b/tests/Feature/MediaGalleryTest.php @@ -151,7 +151,6 @@ 'lg' => 4, 'xl' => 6, ]) - ->and($field->getMediaGridColumns())->toBe(2) ->and($field->getGridClasses())->toBe('grid gap-3') ->and($field->getGridStyle())->toContain('grid-template-columns: repeat(2, 1fr)') ->and($field->getGridStyle())->toContain('@media (min-width: 640px)') @@ -162,7 +161,6 @@ $field = MediaGallery::make('images')->mediaColumns(5); expect($field->getMediaColumns())->toBe(5) - ->and($field->getMediaGridColumns())->toBe(5) ->and($field->getGridClasses())->toBe('grid gap-3') ->and($field->getGridStyle())->toBe('grid-template-columns: repeat(5, 1fr);'); }); From cbbfda5b2f82ff51cb67732e040008da501a1b4e Mon Sep 17 00:00:00 2001 From: thapacodes4u Date: Fri, 10 Oct 2025 07:14:31 +0545 Subject: [PATCH 3/3] fix: complete MediaGallery Filament v4 migration --- resources/css/media-gallery.css | 337 +++++++++++++++++- .../views/components/media-lightbox.blade.php | 6 +- .../views/components/media-preview.blade.php | 6 +- .../forms/components/media-gallery.blade.php | 122 +++---- src/CommonServiceProvider.php | 11 +- .../Components/Concerns/HasMediaPreview.php | 2 +- .../Forms/Components/MediaGallery.php | 75 ++-- tests/Feature/MediaGalleryTest.php | 4 +- 8 files changed, 430 insertions(+), 133 deletions(-) diff --git a/resources/css/media-gallery.css b/resources/css/media-gallery.css index 77be8dc..80a86ee 100644 --- a/resources/css/media-gallery.css +++ b/resources/css/media-gallery.css @@ -2,6 +2,12 @@ display: none !important; } +.eclipse-media-gallery { + display: flex; + flex-direction: column; + gap: 1rem; +} + .eclipse-media-gallery [draggable="true"] { cursor: move; } @@ -10,6 +16,291 @@ cursor: grabbing; } +.eclipse-media-gallery-header { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +@media (min-width: 640px) { + .eclipse-media-gallery-header { + flex-direction: row; + align-items: center; + justify-content: space-between; + } +} + +.eclipse-media-gallery-bulk-controls { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.eclipse-media-gallery-select-label { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; +} + +.eclipse-media-gallery-select-text { + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + color: rgb(55 65 81); + user-select: none; +} + +.dark .eclipse-media-gallery-select-text { + color: rgb(209 213 219); +} + +.eclipse-media-gallery-bulk-actions { + display: flex; + align-items: center; + gap: 0.5rem; + margin-left: 0.5rem; +} + +.eclipse-media-gallery-action-buttons { + display: flex; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.eclipse-media-gallery-grid-wrapper { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.eclipse-media-gallery-grid { + display: grid; + gap: 0.75rem; +} + +.eclipse-media-gallery-card { + position: relative; + overflow: hidden; + transition: all 300ms; + border-radius: 0.75rem; + background-color: rgb(255 255 255); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-color: rgb(0 0 0 / 0.05); +} + +.dark .eclipse-media-gallery-card { + background-color: rgb(17 24 39); + --tw-ring-color: rgb(255 255 255 / 0.1); +} + +.eclipse-media-gallery-card.draggable { + cursor: move; +} + +.eclipse-media-gallery-card.hoverable:hover { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.eclipse-media-gallery-card.selected { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-color: rgb(59 130 246); + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + background-color: rgb(239 246 255); +} + +.dark .eclipse-media-gallery-card.selected { + --tw-ring-color: rgb(96 165 250); + background-color: rgb(30 58 138 / 0.2); +} + +.eclipse-media-gallery-card.dragged { + opacity: 0.5; +} + +.eclipse-media-gallery-card.drag-over { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-color: rgb(96 165 250); + background-color: rgb(239 246 255); +} + +.dark .eclipse-media-gallery-card.drag-over { + background-color: rgb(30 58 138 / 0.2); +} + +.eclipse-image-card-container { + position: relative; + background-color: rgb(243 244 246); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} + +.eclipse-image-card-img { + max-width: 100%; + max-height: 100%; + width: auto; + height: auto; + object-fit: contain; + user-select: none; + pointer-events: none; +} + +.eclipse-image-card-img.clickable { + cursor: pointer; + pointer-events: auto; +} + +.eclipse-image-card-cover-badge { + position: absolute; + z-index: 20; + top: 8px; + left: 8px; +} + +.eclipse-image-card-content { + padding: 0.75rem; +} + +.eclipse-image-card-title { + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + color: rgb(17 24 39); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dark .eclipse-image-card-title { + color: rgb(243 244 246); +} + +.eclipse-image-card-description { + font-size: 0.75rem; + line-height: 1rem; + color: rgb(75 85 99); + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.dark .eclipse-image-card-description { + color: rgb(156 163 175); +} + +.eclipse-image-card-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + margin-top: 0.5rem; +} + +.eclipse-image-card-buttons { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.eclipse-media-gallery-empty { + text-align: center; + border-radius: 0.75rem; + background-color: rgb(249 250 251 / 0.3); + min-height: 280px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1.5rem; + padding: 3rem 1.5rem; + border-width: 2px; + border-style: dashed; + border-color: rgb(209 213 219); +} + +.dark .eclipse-media-gallery-empty { + background-color: rgb(17 24 39 / 0.3); + border-color: rgb(75 85 99); +} + +.eclipse-media-gallery-empty-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.eclipse-media-gallery-empty-icon-wrapper { + padding: 1rem; + border-radius: 9999px; + background-color: rgb(243 244 246); +} + +.dark .eclipse-media-gallery-empty-icon-wrapper { + background-color: rgb(31 41 55); +} + +.eclipse-media-gallery-empty-icon { + height: 3rem; + width: 3rem; + color: rgb(156 163 175); +} + +.dark .eclipse-media-gallery-empty-icon { + color: rgb(107 114 128); +} + +.eclipse-media-gallery-empty-text { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.eclipse-media-gallery-empty-title { + font-size: 1rem; + line-height: 1.5rem; + font-weight: 600; + color: rgb(55 65 81); +} + +.dark .eclipse-media-gallery-empty-title { + color: rgb(229 231 235); +} + +.eclipse-media-gallery-empty-description { + font-size: 0.875rem; + line-height: 1.25rem; + color: rgb(107 114 128); + max-width: 32rem; +} + +.dark .eclipse-media-gallery-empty-description { + color: rgb(156 163 175); +} + +.eclipse-media-gallery-empty-actions { + display: flex; + align-items: center; + gap: 0.75rem; + margin-top: 0.5rem; +} + .eclipse-image-lightbox-overlay { position: fixed; top: 0; @@ -135,19 +426,47 @@ line-height: 1.5; } -.eclipse-image-card-container { - background-color: #f3f4f6; - cursor: pointer; +.eclipse-icon-sm { + height: 1rem; + width: 1rem; +} + +.eclipse-icon-md { + height: 1.5rem; + width: 1.5rem; +} + +.eclipse-icon-lg { + height: 2rem; + width: 2rem; +} + +.eclipse-icon-xl { + height: 3rem; + width: 3rem; +} + +.eclipse-media-preview { display: flex; + flex-direction: column; align-items: center; - justify-content: center; - overflow: hidden; + gap: 0.5rem; } -.eclipse-image-card-img { +.eclipse-media-preview-image { max-width: 100%; - max-height: 100%; - width: auto; - height: auto; + max-height: 12rem; object-fit: contain; + border-radius: 0.5rem; + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); +} + +.eclipse-media-preview-filename { + font-size: 0.75rem; + line-height: 1rem; + color: rgb(107 114 128); +} + +.dark .eclipse-media-preview-filename { + color: rgb(156 163 175); } \ No newline at end of file diff --git a/resources/views/components/media-lightbox.blade.php b/resources/views/components/media-lightbox.blade.php index 628c4a9..6e7e57b 100644 --- a/resources/views/components/media-lightbox.blade.php +++ b/resources/views/components/media-lightbox.blade.php @@ -12,7 +12,7 @@ class="eclipse-image-lightbox-overlay"
@@ -27,11 +27,11 @@ class="eclipse-image-lightbox-image"> diff --git a/resources/views/components/media-preview.blade.php b/resources/views/components/media-preview.blade.php index c8a2147..a339059 100644 --- a/resources/views/components/media-preview.blade.php +++ b/resources/views/components/media-preview.blade.php @@ -1,8 +1,8 @@ -
+
{{ $filename }} -

{{ $filename }}

+

{{ $filename }}

\ No newline at end of file diff --git a/resources/views/filament/forms/components/media-gallery.blade.php b/resources/views/filament/forms/components/media-gallery.blade.php index 83b667d..64c4882 100644 --- a/resources/views/filament/forms/components/media-gallery.blade.php +++ b/resources/views/filament/forms/components/media-gallery.blade.php @@ -5,32 +5,35 @@ $gridStyle = $getGridStyle(); $thumbnailHeight = $getThumbnailHeight(); $statePath = $getStatePath(); + $componentKey = $getKey(); $gridId = 'media-gallery-grid-' . str_replace(['.', '[', ']'], '-', $statePath); @endphp +@assets(['eclipse-common::media-gallery-styles', 'eclipse-common::media-gallery-scripts']) +
$wire.activeLocale || 'en', }), - + selectedImages: [], - + bulkActionsOpen: false, - + draggedIndex: null, - + get hasSelection() { return this.selectedImages.length > 0; }, - + get selectedCount() { return this.selectedImages.length; }, - + get totalCount() { return this.state ? this.state.length : 0; }, - + get allSelected() { return this.totalCount > 0 && this.selectedImages.length === this.totalCount; }, - + get someSelected() { return this.selectedImages.length > 0 && this.selectedImages.length < this.totalCount; }, - + toggleSelectAll() { if (this.allSelected) { this.selectedImages = []; @@ -39,7 +42,7 @@ } this.updateBulkActionsVisibility(); }, - + toggleImageSelection(uuid) { const index = this.selectedImages.indexOf(uuid); if (index > -1) { @@ -49,44 +52,42 @@ } this.updateBulkActionsVisibility(); }, - + updateBulkActionsVisibility() { this.bulkActionsOpen = this.hasSelection; }, - + clearSelection() { this.selectedImages = []; this.bulkActionsOpen = false; }, - + async bulkDelete() { if (this.selectedImages.length === 0) return; - + try { - await $wire.mountFormComponentAction('{{ $statePath }}', 'bulkDelete', { - arguments: { uuids: this.selectedImages } - }); + await $wire.mountAction('bulkDelete', { uuids: this.selectedImages }, { schemaComponent: '{{ $componentKey }}' }); this.clearSelection(); } catch (error) { // Don't clear selection on error so user can retry } } }" wire:key="media-gallery-{{ str_replace('.', '-', $statePath) }}" - class="eclipse-media-gallery" style="display: flex; flex-direction: column; gap: 1rem;"> -
+ class="eclipse-media-gallery"> +