Skip to content

Instantly share code, notes, and snippets.

@ewilan-riviere
Last active September 14, 2024 13:08
Show Gist options
  • 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.

Revisions

  1. ewilan-riviere revised this gist Apr 27, 2023. 1 changed file with 75 additions and 0 deletions.
    75 changes: 75 additions & 0 deletions UploadFile.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,75 @@
    <?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');
    }
    }
  2. ewilan-riviere created this gist Apr 27, 2023.
    173 changes: 173 additions & 0 deletions upload-file.blade.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,173 @@
    @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