Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---

## [Unreleased]

### Added
- Docs: environment variable examples for presets (`MEDIA_TYPES_*`, `MEDIA_MIMES_*`, `MEDIA_MAXKB_*`).
- Tests: deterministic duplicate-detection test helper (`TestableMediaUploader`) and event-based assertions.
- Troubleshooting guidance for Testbench/SQLite and Livewire temp upload disk.


### Changed
- Test suite favors Pest; PHPUnit example retained only if desired by consumers.
- Assertions updated to reflect Spatie filename sanitization (spaces → dashes on rename).

### Fixed
- Intermittent test failures: ensured `media` table migration loads under Testbench and configured fake disks (`public`, `local`, `tmp-for-tests`).


---
## [v0.2.0] — 2025-09-01

### Added
- **Theme system** with **Tailwind (default)** and **Bootstrap** themes.
- **Custom themes** support:
1. Create a new folder under `resources/views/vendor/media-uploader/themes`, e.g. `custom/`.
2. Copy `media-uploader.blade.php` from `tailwind/` or `bootstrap/` into `custom/` (keep the filename).
3. Register in config:
```php
'themes' => [
'tailwind' => 'media-uploader::themes.tailwind.media-uploader',
'bootstrap' => 'media-uploader::themes.bootstrap.media-uploader',
'custom' => 'media-uploader::themes.custom.media-uploader',
],
'theme' => 'custom', // to make it default
```
4. Or set per-instance:
```html
<livewire:media-uploader :for="$post" collection="images" theme="custom" />
```
- Configuration docs for each option in `config/media-uploader.php`, including **ENV overrides**, presets (`types`, `mimes`, `max_kb`), and **collection → preset** mapping.

### Changed
- Default view now resolves via the **theme map** (Tailwind by default).
Existing installs continue to render with Tailwind unless you switch themes.

### Compatibility
- **No breaking changes.** Defaults preserve prior behavior.
- If you previously published the old (pre-theme) Blade, it will keep working if you’ve retained the legacy alias. If you want to use the new theme system, publish/move your override to `themes/<your-theme>/media-uploader.blade.php`.

### Migration Notes (only if you customized the old path)
- Minor migration required for users who published the old view (move file to the themed path).
- Move your customized Blade from:
```html
resources/views/vendor/media-uploader/livewire/media-uploader.blade.php
```
to:
```html
resources/views/vendor/media-uploader/themes/tailwind/media-uploader.blade.php
```
(or into your custom theme folder), and register that theme in the config.

## [v0.1.0] — 2025-08-30
### Added
Expand Down
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Livewire Media Uploader

Livewire Media Uploader is a reusable Livewire v3 component that integrates seamlessly with Spatie Laravel Media Library. It ships a clean Tailwind Blade view (fully publishable), Alpine overlays for previews and confirmations, drag-and-drop uploads, per-file metadata (caption/description/order), configurable presets, name-conflict strategies, and optional SHA-256 duplicate detection. Drop it in, point it at a model, and you’re shipping in minutes.
Livewire Media Uploader is a reusable Livewire v3 component that integrates seamlessly with Spatie Laravel Media Library. It ships a clean Tailwind Blade view by default (fully publishable), Bootstrap theme as an option, Alpine overlays for previews/confirmations, drag-and-drop uploads, per-file metadata (caption/description/order), configurable presets, name-conflict strategies, and optional SHA-256 duplicate detection. Drop it in, point it at a model, and you’re shipping in minutes.

---

Expand All @@ -25,7 +25,10 @@ Livewire Media Uploader is a reusable Livewire v3 component that integrates seam

## Features

- ✅ Livewire v3 component with Tailwind-only Blade (no UI dependency)
- ✅ Livewire v3 component with themeable Blade UI
- Tailwind (default)
- Bootstrap (optional)
- Fully publishable and overridable
- ✅ Spatie Media Library integration (attach, list, edit meta, delete)
- ✅ **Publishable view** for per-project customization
- ✅ Drag & drop uploads + progress bar
Expand All @@ -50,6 +53,9 @@ Livewire Media Uploader is a reusable Livewire v3 component that integrates seam
- spatie/laravel-medialibrary **^10.12**
- TailwindCSS (optional but recommended for the default view)
- Alpine.js (used by overlays/progress; see [Overlays & UX Notes](#overlays--ux-notes))
- CSS depending on theme:
- Tailwind theme → TailwindCSS (recommended)
- Bootstrap theme → Bootstrap CSS (no Bootstrap JS required; Alpine drives modals)

---

Expand Down Expand Up @@ -90,10 +96,34 @@ php artisan vendor:publish --tag=media-uploader-views

After publishing, customize the Blade at:
```html
resources/views/vendor/media-uploader/livewire/media-uploader.blade.php
resources/views/vendor/media-uploader/themes/tailwind/media-uploader.blade.php
resources/views/vendor/media-uploader/themes/bootstrap/media-uploader.blade.php
```


## Theme System (Tailwind + Bootstrap + custom)
Select the theme in config/media-uploader.php:
```php
// config/media-uploader.php
return [
'theme' => 'tailwind', // 'tailwind' (default) or 'bootstrap'
'themes' => [
'tailwind' => 'media-uploader::themes.tailwind.media-uploader',
'bootstrap' => 'media-uploader::themes.bootstrap.media-uploader',
],
// ...
];
```
### Custom themes
- Copy an existing theme directory (e.g. themes/tailwind) to themes/custom and edit the Blade.
- Register it in the map and select it:
```php
'theme' => 'custom',
'themes' => [
'tailwind' => 'media-uploader::themes.tailwind.media-uploader',
'bootstrap' => 'media-uploader::themes.bootstrap.media-uploader',
'custom' => 'media-uploader::themes.custom.media-uploader',
],
```
> Note: The component’s Livewire + Alpine behavior is identical across themes. Only classes/markup differ. If you use the Bootstrap theme, make sure your layout includes Bootstrap CSS.
## Environment variables (optional)
You can override preset limits and accepted types/mimes via .env. These map directly to config/media-uploader.php:

Expand Down
120 changes: 120 additions & 0 deletions config/media-uploader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,153 @@

return [

/*
|--------------------------------------------------------------------------
| Active UI Theme
|--------------------------------------------------------------------------
| Controls which Blade view is used to render the uploader UI.
| Accepts a key from the "themes" map below.
|
| Set globally via .env:
| MEDIA_UPLOADER_THEME=tailwind
|
| You can override per-instance in your Livewire component usage:
| <livewire:media-uploader
| :for="$post"
| collection="images"
| theme="custom"
| />
|
| Default: 'tailwind'
*/
'theme' => env('MEDIA_UPLOADER_THEME', 'tailwind'),

/*
|--------------------------------------------------------------------------
| Theme View Map
|--------------------------------------------------------------------------
| A list of available uploader themes. Keys are theme identifiers, values
| are the fully qualified Blade view names to render the component.
|
| Custom themes:
| - Create a new folder under the resources/views/vendor/media-uploader/themes directory, e.g. "custom".
| - Copy one of the supplied theme files (media-uploader.blade.php) from "tailwind" or "bootstrap"
| into the new "custom" folder. Keep the file name unchanged
| (e.g. media-uploader.blade.php).
| - Edit the copied file as needed.
| - Register it here using the folder name as the key and the view path as the value:
| 'custom' => 'media-uploader::themes.custom.media-uploader',
| - Make sure to clear your view and config cache after implementing the new theme
|
| Usage:
| - Globally via .env (see MEDIA_UPLOADER_THEME above), or
| - Per instance:
| <livewire:media-uploader
| :for="$post"
| collection="images"
| theme="custom"
| />
*/
'themes' => [
'tailwind' => 'media-uploader::themes.tailwind.media-uploader',
'bootstrap' => 'media-uploader::themes.bootstrap.media-uploader',
// 'custom' => 'media-uploader::themes.custom.media-uploader',
],

/*
|--------------------------------------------------------------------------
| Accept Attribute Source
|--------------------------------------------------------------------------
| When true, the uploader's HTML "accept" attribute is computed from the
| selected preset's "types" or "mimes" defined below. When false, the
| component won't auto-generate the "accept" attribute from config.
*/
'accept_from_config' => true,

/*
|--------------------------------------------------------------------------
| Collection → Preset Mapping
|--------------------------------------------------------------------------
| Define logical collections (used by your forms/models) and map each one
| to a preset name from the "presets" section below.
| Example: uploading to the "avatars" collection will apply the "images"
| preset's validation constraints.
*/
'collections' => [
'avatars' => 'images',
'images' => 'images',
'attachments' => 'docs',
],

/*
|--------------------------------------------------------------------------
| Presets
|--------------------------------------------------------------------------
| Each preset defines:
| - types: Comma-separated file extensions (used for UI accept lists).
| - mimes: Comma-separated MIME types (useful for strict validation).
| - max_kb: Maximum file size in kilobytes.
|
| All values can be overridden via env variables for environment-specific
| behavior. If an env var is missing, the default value is used.
*/
'presets' => [
/*
|----------------------------------------------------------------------
| Images Preset
|----------------------------------------------------------------------
| Env:
| - MEDIA_TYPES_IMAGES (e.g. "jpg,jpeg,png,webp,avif,gif")
| - MEDIA_MIMES_IMAGES (e.g. "image/jpeg,image/png,...")
| - MEDIA_MAXKB_IMAGES (integer KB, e.g. 10240 for 10 MB)
*/
'images' => [
'types' => env('MEDIA_TYPES_IMAGES', 'jpg,jpeg,png,webp,avif,gif'),
'mimes' => env('MEDIA_MIMES_IMAGES', 'image/jpeg,image/png,image/webp,image/avif,image/gif'),
'max_kb' => (int) env('MEDIA_MAXKB_IMAGES', 10240),
],

// ... existing code ...
/*
|----------------------------------------------------------------------
| Documents Preset
|----------------------------------------------------------------------
| Env:
| - MEDIA_TYPES_DOCS (e.g. "pdf,doc,docx,xls,xlsx,ppt,pptx,txt")
| - MEDIA_MIMES_DOCS (e.g. "application/pdf,application/msword,...")
| - MEDIA_MAXKB_DOCS (integer KB, e.g. 20480 for 20 MB)
*/
'docs' => [
'types' => env('MEDIA_TYPES_DOCS', 'pdf,doc,docx,xls,xlsx,ppt,pptx,txt'),
'mimes' => env('MEDIA_MIMES_DOCS', 'application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,text/plain'),
'max_kb' => (int) env('MEDIA_MAXKB_DOCS', 20480),
],

/*
|----------------------------------------------------------------------
| Videos Preset
|----------------------------------------------------------------------
| Env:
| - MEDIA_TYPES_VIDEOS (e.g. "mp4,mov,webm")
| - MEDIA_MIMES_VIDEOS (e.g. "video/mp4,video/quicktime,video/webm")
| - MEDIA_MAXKB_VIDEOS (integer KB, e.g. 102400 for 100 MB)
*/
'videos' => [
'types' => env('MEDIA_TYPES_VIDEOS', 'mp4,mov,webm'),
'mimes' => env('MEDIA_MIMES_VIDEOS', 'video/mp4,video/quicktime,video/webm'),
'max_kb' => (int) env('MEDIA_MAXKB_VIDEOS', 102400),
],

/*
|----------------------------------------------------------------------
| Default Preset
|----------------------------------------------------------------------
| A catch-all preset combining common image and document formats.
| Env:
| - MEDIA_TYPES_DEFAULT
| - MEDIA_MIMES_DEFAULT
| - MEDIA_MAXKB_DEFAULT
*/
'default' => [
'types' => env('MEDIA_TYPES_DEFAULT', 'jpg,jpeg,png,webp,avif,gif,pdf,doc,docx,xls,xlsx,ppt,pptx,txt'),
'mimes' => env('MEDIA_MIMES_DEFAULT', 'image/jpeg,image/png,image/webp,image/avif,image/gif,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,text/plain'),
Expand Down
Loading