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
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added

- **Deferred uploads on create**: You can now queue files before the target model exists and attach them after save by dispatching the `media:attach` event. The uploader accepts `model="post"` (no `id`) on create screens, holds the queue + per-file meta, and attaches once you dispatch. Emits `media-attached` when done.
- **`list-all` view**: Set `:list-all="true"` to show **all collections** for the current model in a single list, grouped by collection name. Items remain fully editable (caption/description/order).
- **Tailwind dark mode docs/snippets**: Added guidance and examples for enabling global light/dark mode with the Tailwind theme.

### Changed
- Graceful “no target yet” behavior on create screens:
- `nextOrder()` now derives order from the local queue if the model isn’t saved yet.
- `uploadFiles()` **queues** when there’s no target (instead of erroring) and flashes: _“Files queued. They will be attached after you save.”_

### Fixed

- N/A

---
## [v0.2.0] — 2025-09-01
Expand Down Expand Up @@ -95,5 +100,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

[Unreleased]: https://github.com/codebyray/livewire-media-uploader/compare/v0.1.0...HEAD
[Unreleased]: https://github.com/codebyray/livewire-media-uploader/compare/v0.2.0...HEAD
[v0.1.0]: https://github.com/codebyray/livewire-media-uploader/releases/tag/v0.1.0
103 changes: 100 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ Livewire Media Uploader is a reusable Livewire v3 component that integrates seam
- [Requirements](#requirements)
- [Installation](#installation)
- [Publishing Assets](#publishing-assets)
- [Theme System](#theme-system-tailwind--bootstrap--custom)
- [Dark Mode - Tailwind](#dark-mode-tailwind-theme)
- [Custom Theme](#custom-themes)
- [Quick Start](#quick-start)
- [Usage Examples](#usage-examples)
- [Create flow (deferred uploads)](#create-flow-deferred-uploads)
- [Configuration](#configuration)
- [Props](#props)
- [Events](#events)
Expand Down Expand Up @@ -112,6 +116,19 @@ return [
// ...
];
```
### Dark mode (Tailwind theme)
This package’s Tailwind theme is dark-ready. Add this tiny snippet in your main layout `<head>` to apply the user’s saved choice / system default:

```html
<script>
(() => {
const t = localStorage.theme ?? 'system';
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const dark = t === 'dark' || (t === 'system' && prefersDark);
if (dark) document.documentElement.classList.add('dark');
})();
</script>
```
### 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:
Expand Down Expand Up @@ -293,7 +310,75 @@ MEDIA_MAXKB_DEFAULT=10240
:maxSizeKb="5120"
/>
```
### Create flow (deferred uploads)

You can let users pick files **before** the model exists, and attach them **after** save.

**Blade (create page)**
```html
<!-- Note: pass model class/alias without id -->
<livewire:media-uploader
model="post"
collection="images"
preset="images"
:multiple="true"
:showList="true"
/>
```
#### Livewire component (simplified)
```php
use App\Models\Post;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\On;
use Livewire\Component;

class PostCreate extends Component
{
public string $title = '';
public string $body = '';
public ?int $pendingPostId = null;

protected function rules(): array
{
return ['title' => 'required|string|max:255', 'body' => 'required|string'];
}

public function save(): void
{
$post = Post::create([
'user_id' => Auth::id(),
'title' => $this->title,
'body' => $this->body,
]);

// Let uploaders attach everything queued for this collection
$this->pendingPostId = $post->id;

// Fire once per collection rendered on the page
$this->dispatch('media:attach', model: 'post', id: $post->id, collection: 'images');
}

#[On('media-attached')]
public function afterMediaAttached(string $model, string|int $id): void
{
if ($this->pendingPostId && (int)$id === (int)$this->pendingPostId) {
$this->pendingPostId = null;
$this->redirectRoute('posts.show', ['post' => $id], navigate: true);
}
}

public function render() { return view('livewire.posts.post-create'); }
}
```

#### How it works
- On create screens, the component accepts model="post" without an id.
- Files and per-file metadata are queued locally.
- After you persist the model, dispatch:
```php
$this->dispatch('media:attach', model: 'post', id: $post->id, collection: 'images');
```
- The uploader resolves the saved target, attaches any queued files, and emits media-attached.
---

## Configuration
Expand All @@ -314,7 +399,16 @@ Example:
'attachments' => 'docs',
],
```
Show all collections together (grouped)
Set `:list-all="true"` to render a grouped list of **every collection** on the target model. Items stay fully editable.

```html
<livewire:media-uploader
:for="$post"
:list-all="true"
:showList="true"
/>
```
The component decides the active preset in this order:
1. Explicit `$preset` prop
2. Mapping from `collections`
Expand Down Expand Up @@ -343,16 +437,19 @@ The component decides the active preset in this order:
| `namespaces` | `array` | `['App\\Models']` | Namespaces for dotted-path resolution. |
| `aliases` | `array` | `[]` | Local alias map, e.g. `['profile' => \App\Models\User::class]`. |
| `attachedFilesTitle` | `string` | `"Current gallery"` | Heading text in the list card. |
| `listAll` | `bool` | `false` | When `true`, the attached media list shows **all collections**, grouped by collection name (still editable). |

---

## Events

The component dispatches browser events you can listen for:

- `media-uploaded` — after an upload completes
- `media-deleted` — after a deletion (`detail.id` contains the Media ID)
- `media-meta-updated` — after saving inline metadata
- `media:attach` — **incoming** event the component listens for. Arguments: `model` (class/alias), `id`, optional `collection`, optional `disk`. Triggers attaching of any queued files to the now-saved target.
- `media-attached` — emitted after a successful `media:attach`. Payload: `{ model: FQCN, id: string }`.
- `media-uploaded` — emitted after an immediate upload (when a target already exists).
- `media-deleted` — emitted after deletion (`detail.id` contains the Media ID).
- `media-meta-updated` — emitted after inline metadata is saved.

Example:
```html
Expand Down
Loading