121 lines
6.2 KiB
PHP
121 lines
6.2 KiB
PHP
@props(['type' => 'text', 'name' => '', 'label' => 'Gallery', 'info', 'value' => '', 'editor' => false])
|
|
|
|
@php
|
|
$hasError = $errors->has($name);
|
|
$onchange = $attributes->get('onchange');
|
|
$value = old($name, $value);
|
|
$editor = filter_var($editor, FILTER_VALIDATE_BOOLEAN);
|
|
@endphp
|
|
|
|
@if($value !== '' || $editor === true)
|
|
<div x-data class="{{ twMerge(['mb-4'], $attributes->get('class')) }}">
|
|
@if($editor === true)
|
|
<h3 class="text-xl font-semibold">{{ $label }}</h3>
|
|
<p class="text-xs italic" x-text="$store.gallery.length + ' Image' + ($store.gallery.length !== 1 ? 's' : '')"></p>
|
|
@else
|
|
<div
|
|
class="fixed top-0 left-0 w-full h-full bg-black bg-opacity-90 flex items-center justify-center z-50"
|
|
x-cloak
|
|
x-show="$store.modelIndex!=-1"
|
|
x-on:click.prevent="$store.modelIndex=-1">
|
|
<div class="cursor-pointer z-10 flex justify-center items-center fixed top-0 left-0 h-full w-12 hover:scale-125 transition text-gray-400 text-opacity-60 hover:text-opacity-100 text-3xl" x-on:click.prevent.stop="$store.modelIndex<=0?$store.modelIndex=$store.modelCount-1:$store.modelIndex--"><i class="fa-solid fa-circle-chevron-left"></i></div>
|
|
<div class="cursor-pointer z-10 flex justify-center items-center fixed top-0 right-0 h-full w-12 hover:scale-125 transition text-gray-400 text-opacity-60 hover:text-opacity-100 text-3xl" x-on:click.prevent.stop="$store.modelIndex>=$store.modelCount-1?$store.modelIndex=0:$store.modelIndex++"><i class="fa-solid fa-circle-chevron-right"></i></div>
|
|
<ul class="flex flex-wrap justify-center px-14 mt-2">
|
|
<template x-for="(file,index) in $store.gallery" :key="file.name">
|
|
<li x-show="$store.modelIndex==index" class="flex items-center justify-center relative">
|
|
<div class="flex gap-4 px-4 py-1 cursor-pointer z-10 fixed top-4 right-14 transition bg-opacity-0 hover:bg-opacity-80 bg-black rounded items-center">
|
|
<a :href="file.url + '?download'" target="_blank" class="cursor-pointer transition text-white text-opacity-80 hover:text-opacity-100 text-2xl" x-on:click.stop><i class="fa-solid fa-download"></i></a>
|
|
<div class="cursor-pointer transition text-white text-opacity-80 hover:text-opacity-100 text-3xl" x-on:click.prevent.stop="$store.modelIndex=-1"><i class="fa-solid fa-xmark"></i></div>
|
|
</div>
|
|
<img class="rounded" :src="file.url + '?scaled'" />
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
<ul class="flex flex-wrap justify-center gap-4 mt-2">
|
|
<template x-for="(file,index) in $store.gallery" :key="file.name">
|
|
<li class="flex items-center justify-center w-44 h-28 relative {{ !$editor ? 'hover:scale-105 transition-transform' : '' }}" x-on:click.prevent="$store.modelIndex=index">
|
|
<img class="rounded max-w-44 max-h-28" :src="file.url + '?sm'" />
|
|
@if($editor)
|
|
<div class="opacity-0 hover:opacity-100 absolute rounded flex items-center justify-center top-0 left-0 h-full w-full bg-opacity-75 bg-red-500 text-white cursor-pointer text-lg transition-opacity" x-on:click.prevent="removeGalleryItem(file.name)">
|
|
<i class="fa-solid fa-trash"></i>
|
|
</div>
|
|
@endif
|
|
</li>
|
|
</template>
|
|
@if($editor)
|
|
<li class="flex items-center justify-center w-44 h-28 bg-gray-300 cursor-pointer rounded" x-on:click.prevent="SMMediaPicker.open(Alpine.store('gallery').map(file => file.name), {require_mime_type:'image/*',allow_multiple:true,allow_uploads:true}, (result)=>updateGallery(result))">
|
|
<i class="fa-solid fa-circle-plus text-3xl text-gray-800"></i>
|
|
</li>
|
|
@endif
|
|
</ul>
|
|
|
|
@if($editor)
|
|
<input class="hidden" type="text" id="{{ $name }}" name="{{ $name }}" value="{{ $value }}"/>
|
|
@endif
|
|
@if(isset($info) && $info !== '')
|
|
<div class="text-xs text-gray-500 ml-2 mt-1">{{ $info }}</div>
|
|
@endif
|
|
</div>
|
|
|
|
<script>
|
|
function removeGalleryItem(fileName) {
|
|
const fileList = Alpine.store('gallery').filter(f => f.name !== fileName);
|
|
|
|
Alpine.store('gallery', fileList);
|
|
|
|
const elem = document.getElementById('{{ $name }}');
|
|
if(elem) {
|
|
elem.value = fileList.map(f => f.name).join(',');
|
|
}
|
|
}
|
|
|
|
function updateGallery(result) {
|
|
Alpine.store('gallery', []);
|
|
|
|
result = result.filter((item) => item.length > 0);
|
|
|
|
const promises = result.map(fileName => {
|
|
return new Promise((resolve, reject) => {
|
|
SM.mediaDetails(fileName, details => {
|
|
details.extension = fileName.split('.').pop();
|
|
resolve(details); // Resolve the promise with the modified details
|
|
});
|
|
});
|
|
});
|
|
|
|
// Use Promise.all to process the promises in order
|
|
Promise.all(promises).then(allDetails => {
|
|
// allDetails is an array of details in the same order as the result array
|
|
allDetails.forEach(details => {
|
|
Alpine.store('gallery').push(details);
|
|
});
|
|
});
|
|
|
|
Alpine.store('modelCount', result.length)
|
|
|
|
const elem = document.getElementById('{{ $name }}');
|
|
if(elem) {
|
|
elem.value = result.join(',');
|
|
}
|
|
}
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.store('modelIndex', -1);
|
|
Alpine.store('modelCount', 0);
|
|
updateGallery('{{ $value }}'.split(',') || []);
|
|
});
|
|
|
|
document.addEventListener('keyup', (e) => {
|
|
if(e.key === 'Escape') {
|
|
Alpine.store('modelIndex', -1);
|
|
} else if(e.key === 'ArrowLeft' && Alpine.store('modelIndex') !== -1) {
|
|
Alpine.store('modelIndex', Alpine.store('modelIndex') <= 0 ? Alpine.store('modelCount') - 1 : Alpine.store('modelIndex') - 1);
|
|
} else if(e.key === 'ArrowRight' && Alpine.store('modelIndex') !== -1) {
|
|
Alpine.store('modelIndex', Alpine.store('modelIndex') >= Alpine.store('modelCount') - 1 ? 0 : Alpine.store('modelIndex') + 1);
|
|
}
|
|
});
|
|
</script>
|
|
@endif
|