Skip to content

Commit 18d2a7e

Browse files
author
ankitcodes4u
committed
feat: adding a much improved media gallery field
1 parent 88fdd37 commit 18d2a7e

4 files changed

Lines changed: 148 additions & 91 deletions

File tree

docs/MediaGallery.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ MediaGallery::make('images')
6262

6363
### Interactive Features
6464
- `preview()` - Enable lightbox modal for image preview (disabled by default)
65-
- `lightbox(bool)` - Explicitly enable/disable lightbox functionality
6665
- `orderable()` - Enable drag & drop reordering (disabled by default)
6766

6867
### Upload Configuration

resources/views/filament/forms/components/media-gallery.blade.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
}" wire:key="media-gallery-{{ str_replace('.', '-', $statePath) }}"
7575
class="eclipse-media-gallery" style="display: flex; flex-direction: column; gap: 1rem;">
7676
<div x-show="state && state.length > 0" class="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
77+
@if ($getAllowBulkDelete())
7778
<div class="flex items-center gap-3">
7879
<label class="flex items-center gap-2 cursor-pointer">
7980
<x-filament::input.checkbox :checked="false" x-bind:checked="allSelected"
@@ -91,6 +92,9 @@ class="eclipse-media-gallery" style="display: flex; flex-direction: column; gap:
9192
</x-filament::button>
9293
</div>
9394
</div>
95+
@else
96+
<div></div>
97+
@endif
9498

9599
<div class="flex items-center gap-2 flex-shrink-0">
96100
@if ($getAllowFileUploads() && $getAction('upload'))
@@ -189,9 +193,11 @@ class="shadow-sm">
189193
</x-filament::button>
190194
</div>
191195

196+
@if ($getAllowBulkDelete())
192197
<x-filament::input.checkbox :checked="false"
193198
x-bind:checked="selectedImages.includes(image.uuid)"
194199
x-on:change="toggleImageSelection(image.uuid)" class="flex-shrink-0" />
200+
@endif
195201
</div>
196202
</div>
197203
</div>

src/Filament/Forms/Components/Concerns/HasMediaUploadOptions.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ trait HasMediaUploadOptions
1818

1919
protected bool|Closure $isMultiple = true;
2020

21+
protected bool|Closure $allowBulkDelete = true;
22+
2123
public function acceptedFileTypes(array|Closure $types): static
2224
{
2325
$this->acceptedFileTypes = $types;
@@ -104,4 +106,23 @@ public function isMultiple(): bool
104106
{
105107
return $this->evaluate($this->isMultiple);
106108
}
109+
110+
public function bulkDelete(bool|Closure $condition = true): static
111+
{
112+
$this->allowBulkDelete = $condition;
113+
114+
return $this;
115+
}
116+
117+
public function disableBulkDelete(): static
118+
{
119+
$this->allowBulkDelete = false;
120+
121+
return $this;
122+
}
123+
124+
public function getAllowBulkDelete(): bool
125+
{
126+
return $this->evaluate($this->allowBulkDelete);
127+
}
107128
}

src/Filament/Forms/Components/MediaGallery.php

Lines changed: 121 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ protected function createMediaFromUrl(Model $record, array $item, int $index): v
168168
->usingFileName($this->sanitizeFilename($item['file_name'] ?? basename($item['temp_url'])))
169169
->withCustomProperties($this->getMediaCustomProperties($item, $index))
170170
->toMediaCollection($this->getCollection());
171-
} catch (\Exception $e) {
171+
} catch (Exception $e) {
172172
}
173173
}
174174

@@ -203,7 +203,7 @@ public function getAvailableLocales(): array
203203
$locales[$locale] = $plugin->getLocaleLabel($locale) ?? $locale;
204204
}
205205
}
206-
} catch (\Exception $e) {
206+
} catch (Exception $e) {
207207
}
208208

209209
if (empty($locales)) {
@@ -264,15 +264,104 @@ public function getUploadAction(): Action
264264
return;
265265
}
266266

267-
$record = $this->getRecord();
268-
$maxFiles = $this->getMaxFiles();
267+
$originalMemoryLimit = ini_get('memory_limit');
268+
ini_set('memory_limit', '192M');
269269

270-
if (! $record) {
271-
$currentState = $this->getState() ?: [];
272-
$existingCount = count($currentState);
273-
$maxPosition = count($currentState) - 1;
270+
try {
271+
$record = $this->getRecord();
272+
$maxFiles = $this->getMaxFiles();
273+
274+
if (! $record) {
275+
$currentState = $this->getState() ?: [];
276+
$existingCount = count($currentState);
277+
$maxPosition = count($currentState) - 1;
278+
$allowedCount = $maxFiles ? max(0, $maxFiles - $existingCount) : count($data['files']);
279+
$filesToProcess = array_slice($data['files'], 0, $allowedCount);
280+
281+
if ($allowedCount <= 0) {
282+
Notification::make()
283+
->title('Maximum files limit reached')
284+
->body("You can only upload {$maxFiles} file(s) total.")
285+
->warning()
286+
->send();
287+
288+
return;
289+
}
290+
291+
$initialStateCount = count($currentState);
292+
$addedInThisBatch = 0;
293+
294+
foreach ($filesToProcess as $filePath) {
295+
if (is_string($filePath)) {
296+
$fullPath = storage_path('app/public/'.$filePath);
297+
298+
if (file_exists($fullPath)) {
299+
$tempId = 'temp_'.uniqid();
300+
$fileName = $this->sanitizeFilename(basename($filePath));
301+
302+
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
303+
$mimeType = match ($extension) {
304+
'jpg', 'jpeg' => 'image/jpeg',
305+
'png' => 'image/png',
306+
'gif' => 'image/gif',
307+
'webp' => 'image/webp',
308+
default => 'image/jpeg'
309+
};
310+
311+
$currentState[] = [
312+
'id' => null,
313+
'temp_id' => $tempId,
314+
'temp_file' => $filePath,
315+
'uuid' => (string) Str::uuid(),
316+
'url' => \Storage::url($filePath),
317+
'thumb_url' => \Storage::url($filePath),
318+
'preview_url' => \Storage::url($filePath),
319+
'name' => [],
320+
'description' => [],
321+
'is_cover' => $initialStateCount === 0 && $addedInThisBatch === 0,
322+
'position' => ++$maxPosition,
323+
'file_name' => $fileName,
324+
'mime_type' => $mimeType,
325+
'size' => 0,
326+
];
327+
328+
$addedInThisBatch++;
329+
}
330+
}
331+
}
332+
333+
$this->state($currentState);
334+
335+
if (function_exists('gc_collect_cycles')) {
336+
gc_collect_cycles();
337+
}
338+
339+
$uploadedCount = count($filesToProcess);
340+
$rejectedCount = count($data['files']) - $uploadedCount;
341+
342+
if ($uploadedCount > 0) {
343+
Notification::make()
344+
->title($uploadedCount.' image(s) added successfully')
345+
->success()
346+
->send();
347+
}
348+
349+
if ($rejectedCount > 0) {
350+
Notification::make()
351+
->title($rejectedCount.' image(s) rejected')
352+
->body("Maximum files limit ({$maxFiles}) reached.")
353+
->warning()
354+
->send();
355+
}
356+
357+
return;
358+
}
359+
360+
$existingCount = $record->getMedia($this->getCollection())->count();
361+
$maxPosition = $record->getMedia($this->getCollection())->max(fn ($m) => $m->getCustomProperty('position', 0)) ?? -1;
274362
$allowedCount = $maxFiles ? max(0, $maxFiles - $existingCount) : count($data['files']);
275363
$filesToProcess = array_slice($data['files'], 0, $allowedCount);
364+
$uploadCount = 0;
276365

277366
if ($allowedCount <= 0) {
278367
Notification::make()
@@ -289,37 +378,30 @@ public function getUploadAction(): Action
289378
$fullPath = storage_path('app/public/'.$filePath);
290379

291380
if (file_exists($fullPath)) {
292-
$tempId = 'temp_'.uniqid();
293-
$fileName = $this->sanitizeFilename(basename($filePath));
294-
295-
$currentState[] = [
296-
'id' => null,
297-
'temp_id' => $tempId,
298-
'temp_file' => $filePath,
299-
'uuid' => (string) \Str::uuid(),
300-
'url' => \Storage::url($filePath),
301-
'thumb_url' => \Storage::url($filePath),
302-
'preview_url' => \Storage::url($filePath),
303-
'name' => [],
304-
'description' => [],
305-
'is_cover' => count($currentState) === 0,
306-
'position' => ++$maxPosition,
307-
'file_name' => $fileName,
308-
'mime_type' => mime_content_type($fullPath),
309-
'size' => filesize($fullPath),
310-
];
381+
$record->addMedia($fullPath)
382+
->usingFileName($this->sanitizeFilename(basename($filePath)))
383+
->withCustomProperties([
384+
'name' => [],
385+
'description' => [],
386+
'is_cover' => $existingCount === 0 && $uploadCount === 0,
387+
'position' => ++$maxPosition,
388+
])
389+
->toMediaCollection($this->getCollection());
390+
391+
$uploadCount++;
392+
393+
@unlink($fullPath);
311394
}
312395
}
313396
}
314397

315-
$this->state($currentState);
398+
$this->refreshState();
316399

317-
$uploadedCount = count($filesToProcess);
318-
$rejectedCount = count($data['files']) - $uploadedCount;
400+
$rejectedCount = count($data['files']) - $uploadCount;
319401

320-
if ($uploadedCount > 0) {
402+
if ($uploadCount > 0) {
321403
Notification::make()
322-
->title($uploadedCount.' image(s) added successfully')
404+
->title($uploadCount.' image(s) uploaded successfully')
323405
->success()
324406
->send();
325407
}
@@ -331,65 +413,14 @@ public function getUploadAction(): Action
331413
->warning()
332414
->send();
333415
}
334-
335-
return;
336-
}
337-
338-
$existingCount = $record->getMedia($this->getCollection())->count();
339-
$maxPosition = $record->getMedia($this->getCollection())->max(fn ($m) => $m->getCustomProperty('position', 0)) ?? -1;
340-
$allowedCount = $maxFiles ? max(0, $maxFiles - $existingCount) : count($data['files']);
341-
$filesToProcess = array_slice($data['files'], 0, $allowedCount);
342-
$uploadCount = 0;
343-
344-
if ($allowedCount <= 0) {
345-
Notification::make()
346-
->title('Maximum files limit reached')
347-
->body("You can only upload {$maxFiles} file(s) total.")
348-
->warning()
349-
->send();
350-
351-
return;
352-
}
353-
354-
foreach ($filesToProcess as $filePath) {
355-
if (is_string($filePath)) {
356-
$fullPath = storage_path('app/public/'.$filePath);
357-
358-
if (file_exists($fullPath)) {
359-
$record->addMedia($fullPath)
360-
->usingFileName($this->sanitizeFilename(basename($filePath)))
361-
->withCustomProperties([
362-
'name' => [],
363-
'description' => [],
364-
'is_cover' => $existingCount === 0 && $uploadCount === 0,
365-
'position' => ++$maxPosition,
366-
])
367-
->toMediaCollection($this->getCollection());
368-
369-
$uploadCount++;
370-
371-
@unlink($fullPath);
372-
}
373-
}
374-
}
375-
376-
$this->refreshState();
377-
378-
$rejectedCount = count($data['files']) - $uploadCount;
379-
380-
if ($uploadCount > 0) {
381-
Notification::make()
382-
->title($uploadCount.' image(s) uploaded successfully')
383-
->success()
384-
->send();
385-
}
386-
387-
if ($rejectedCount > 0) {
416+
} catch (Exception $e) {
388417
Notification::make()
389-
->title($rejectedCount.' image(s) rejected')
390-
->body("Maximum files limit ({$maxFiles}) reached.")
391-
->warning()
418+
->title('Upload failed')
419+
->body('An error occurred during image processing. Please try uploading fewer images at once.')
420+
->danger()
392421
->send();
422+
} finally {
423+
ini_set('memory_limit', $originalMemoryLimit);
393424
}
394425
})
395426
->modalWidth('lg')
@@ -476,7 +507,7 @@ public function getUrlUploadAction(): Action
476507
'id' => null,
477508
'temp_id' => $tempId,
478509
'temp_url' => $url,
479-
'uuid' => (string) \Str::uuid(),
510+
'uuid' => (string) Str::uuid(),
480511
'url' => $url,
481512
'thumb_url' => $url,
482513
'preview_url' => $url,

0 commit comments

Comments
 (0)