Skip to content

Instantly share code, notes, and snippets.

@ewilan-riviere
Last active February 5, 2024 18:31
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ewilan-riviere/dfca491def1bb5aabf70e1649518b5f1 to your computer and use it in GitHub Desktop.
Save ewilan-riviere/dfca491def1bb5aabf70e1649518b5f1 to your computer and use it in GitHub Desktop.
Upload file Blade component for Laravel with FilePond, ready for Livewire.
@pushOnce('head')
<link
href="https://unpkg.com/filepond/dist/filepond.css"
rel="stylesheet"
>
<link
href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css"
rel="stylesheet"
>
<style>
.filepond--drop-label {
background-color: white;
border-width: 2px;
border-style: dashed;
border-color: lightgray;
border-radius: 0.375rem;
transition: background-color 0.1s ease;
}
.filepond--drop-label:hover {
background-color: rgba(0, 0, 0, 0.01);
}
</style>
@endPushOnce
<div
class="relative"
wire:ignore
x-data="{
model: @entangle($attributes->whereStartsWith('wire:model')->first()),
isMultiple: {{ $multiple ? 'true' : 'false' }},
current: undefined,
currentList: [],
async URLtoFile(path) {
let url = `${window.appUrlStorage}/${path}`;
let name = url.split('/').pop();
const response = await fetch(url);
const data = await response.blob();
const metadata = {
name: name,
size: data.size,
type: data.type
};
let file = new File([data], name, metadata);
return {
source: file,
options: {
type: 'local',
metadata: {
name: name,
size: file.size,
type: file.type
}
}
}
}
}"
x-cloak
x-init="async () => {
let picture = model
let files = []
let exists = []
if (model) {
if (isMultiple) {
currentList = model.map((picture) => `${window.appUrlStorage}/${picture}`);
await Promise.all(model.map(async (picture) => exists.push(await URLtoFile(picture))))
} else {
if (picture) {
exists.push(await URLtoFile(picture))
}
}
}
files = exists
let modelName = '{{ $attributes->whereStartsWith('wire:model')->first() }}'
const notify = () => {
new Notification()
.title('File uploaded')
.body(`You can save changes!`)
.success()
.seconds(1.5)
.send()
}
const pond = FilePond.create($refs.{{ $attributes->get('ref') ?? 'input' }});
pond.setOptions({
allowMultiple: {{ $multiple ? 'true' : 'false' }},
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
@this.upload(modelName, file, load, error, progress)
},
revert: (filename, load) => {
@this.removeUpload(modelName, filename, load)
},
remove: (filename, load) => {
@this.removeFile(modelName, filename.name)
load();
},
},
allowImagePreview: {{ $preview ? 'true' : 'false' }},
imagePreviewMaxHeight: {{ $previewMax ? $previewMax : '256' }},
allowFileTypeValidation: {{ $validate ? 'true' : 'false' }},
acceptedFileTypes: {{ $accept ? $accept : 'null' }},
allowFileSizeValidation: {{ $validate ? 'true' : 'false' }},
maxFileSize: {!! $size ? "'" . $size . "'" : 'null' !!},
maxFiles: {{ $number ? $number : 'null' }},
required: {{ $required ? 'true' : 'false' }},
disabled: {{ $disabled ? 'true' : 'false' }},
onprocessfile: () => notify()
});
pond.addFiles(files)
pond.on('addfile', (error, file) => {
if (error) {
console.log('Oh no');
return;
}
});
}"
>
@if ($label)
<div class="flex items-center justify-between">
<label
class="block text-sm font-medium leading-6 text-gray-900 dark:text-gray-100"
for="{{ $name }}"
>
{{ $label }}
@if ($required)
<span
class="text-red-500"
title="Required"
>*</span>
@endif
</label>
<div class="text-xs text-gray-400">
Size max: {{ $sizeHuman }}
</div>
</div>
@endif
<div class="flex items-center justify-between text-xs text-gray-400">
<div>
Formats: {{ $acceptHuman }}
</div>
<div>
{{ $multiple ? 'Multiple' : 'Single' }}
@if ($multiple)
<span>({{ $number }} files max)</span>
@endif
</div>
</div>
<div class="mt-5">
<input
type="file"
x-ref="{{ $attributes->get('ref') ?? 'input' }}"
/>
</div>
@error('image')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
@pushOnce('scripts')
<script src="https://unpkg.com/filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.js"></script>
<script src="https://unpkg.com/filepond-plugin-file-validate-size/dist/filepond-plugin-file-validate-size.js"></script>
<script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
<script src="https://unpkg.com/filepond/dist/filepond.js"></script>
<script>
FilePond.registerPlugin(FilePondPluginFileValidateType);
FilePond.registerPlugin(FilePondPluginFileValidateSize);
FilePond.registerPlugin(FilePondPluginImagePreview);
</script>
@endPushOnce
<?php
namespace App\View\Components\Field;
use Illuminate\View\Component;
use Illuminate\View\View;
class UploadFile extends Component
{
public function __construct(
public string $name = 'file', // name="avatar"
public bool|int $multiple = false, // multiple
public bool|int $validate = true, // validate for allowFileTypeValidation
public bool|int $preview = true, // preview for allowImagePreview
public bool|int $required = false, // required
public bool|int $disabled = false, // disabled
public int $previewMax = 200, // preview-max="200" for imagePreviewMaxHeight
public array|string $accept = ['image/png', 'image/jpeg', 'image/webp', 'image/avif'], // accept="image/png, image/jpeg" for accept
public string $size = '2MB', // size="4mb" for maxFileSize
public int $number = 10, // number="4" for maxFiles
public string $label = '',
public string $sizeHuman = '',
public array|string $acceptHuman = [],
) {
}
public function render(): View|string
{
if (! $this->multiple) {
$this->multiple = 0;
}
if (! $this->validate) {
$this->validate = 0;
}
if (! $this->preview) {
$this->preview = 0;
}
if (! $this->required) {
$this->required = 0;
}
if (! $this->disabled) {
$this->disabled = 0;
}
if (is_string($this->accept)) {
$this->accept = explode(',', $this->accept);
}
$this->accept = array_map('trim', $this->accept);
$this->accept = array_filter($this->accept);
$this->accept = array_unique($this->accept);
$this->accept = array_values($this->accept);
$this->accept = array_map('strtolower', $this->accept);
$fileTypes = $this->accept;
$this->accept = json_encode($this->accept);
$this->sizeHuman = $this->size;
foreach ($fileTypes as $type) {
$new = explode('/', $type);
if (array_key_exists(1, $new)) {
$this->acceptHuman[] = ".{$new[1]}";
}
}
$this->acceptHuman = implode(', ', $this->acceptHuman);
return view('components.field.upload-file');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment