fix: spa on Filament image gallery column, entry, and component with Viewer.js integration.
This commit is contained in:
203
resources/dist/image-gallery.js
vendored
203
resources/dist/image-gallery.js
vendored
@@ -1,3 +1,202 @@
|
||||
// Image Gallery Plugin - Viewer.js initialization
|
||||
// The main Viewer.js initialization is done inline via CDN in the viewer-script.blade.php component
|
||||
// This file is a placeholder for future compiled assets
|
||||
// This script handles SPA navigation and dynamic content loading
|
||||
|
||||
(function () {
|
||||
const VIEWER_JS_URL = 'https://unpkg.com/viewerjs@1.11.6/dist/viewer.min.js';
|
||||
const VIEWER_CSS_URL = 'https://unpkg.com/viewerjs@1.11.6/dist/viewer.min.css';
|
||||
|
||||
let loadingPromise = null;
|
||||
|
||||
// Load Viewer.js CSS dynamically
|
||||
function loadViewerCSS() {
|
||||
if (document.querySelector('link[href="' + VIEWER_CSS_URL + '"]')) {
|
||||
return;
|
||||
}
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = VIEWER_CSS_URL;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
// Load Viewer.js JS dynamically
|
||||
function loadViewerJS() {
|
||||
if (loadingPromise) {
|
||||
return loadingPromise;
|
||||
}
|
||||
|
||||
if (typeof window.Viewer !== 'undefined') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (document.querySelector('script[src="' + VIEWER_JS_URL + '"]')) {
|
||||
// Script tag exists but Viewer may not be loaded yet
|
||||
return new Promise(function (resolve) {
|
||||
const checkInterval = setInterval(function () {
|
||||
if (typeof window.Viewer !== 'undefined') {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
loadingPromise = new Promise(function (resolve, reject) {
|
||||
const script = document.createElement('script');
|
||||
script.src = VIEWER_JS_URL;
|
||||
script.onload = function () {
|
||||
loadingPromise = null;
|
||||
resolve();
|
||||
};
|
||||
script.onerror = function () {
|
||||
loadingPromise = null;
|
||||
reject(new Error('Failed to load Viewer.js'));
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
return loadingPromise;
|
||||
}
|
||||
|
||||
// Check if Viewer.js is available
|
||||
function isViewerAvailable() {
|
||||
return typeof window.Viewer !== 'undefined';
|
||||
}
|
||||
|
||||
function initOne(el) {
|
||||
if (!el || el._viewer || !isViewerAvailable()) return;
|
||||
el._viewer = new Viewer(el, {
|
||||
toolbar: {
|
||||
zoomIn: 1,
|
||||
zoomOut: 1,
|
||||
oneToOne: 1,
|
||||
reset: 1,
|
||||
prev: 1,
|
||||
play: 0,
|
||||
next: 1,
|
||||
rotateLeft: 1,
|
||||
rotateRight: 1,
|
||||
flipHorizontal: 1,
|
||||
flipVertical: 1,
|
||||
},
|
||||
navbar: false,
|
||||
inline: false,
|
||||
movable: true,
|
||||
rotatable: true,
|
||||
scalable: true,
|
||||
fullscreen: true,
|
||||
transition: true,
|
||||
title: false,
|
||||
});
|
||||
}
|
||||
|
||||
function destroyOne(el) {
|
||||
if (el && el._viewer) {
|
||||
try {
|
||||
el._viewer.destroy();
|
||||
} catch (e) { }
|
||||
el._viewer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function scan() {
|
||||
const galleries = document.querySelectorAll('[data-viewer-gallery]');
|
||||
if (galleries.length === 0) return;
|
||||
|
||||
// Ensure Viewer.js is loaded before initializing
|
||||
loadViewerCSS();
|
||||
loadViewerJS().then(function () {
|
||||
galleries.forEach(function (el) {
|
||||
// Destroy and reinitialize to handle SPA navigation
|
||||
destroyOne(el);
|
||||
initOne(el);
|
||||
});
|
||||
}).catch(function (err) {
|
||||
console.error('ImageGallery:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// Run on various lifecycle events
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', scan);
|
||||
} else {
|
||||
// DOM is already ready
|
||||
scan();
|
||||
}
|
||||
|
||||
// Filament/Livewire 3.x SPA navigation
|
||||
document.addEventListener('livewire:navigated', function () {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
|
||||
// Livewire 3.x morph updates
|
||||
document.addEventListener('livewire:init', function () {
|
||||
if (window.Livewire) {
|
||||
Livewire.hook('morph.updated', function ({ el }) {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// For Livewire 2.x compatibility
|
||||
document.addEventListener('livewire:load', scan);
|
||||
if (window.Livewire && window.Livewire.hook) {
|
||||
try {
|
||||
window.Livewire.hook('message.processed', function () {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
// Turbolinks/Turbo compatibility
|
||||
document.addEventListener('turbo:load', scan);
|
||||
document.addEventListener('turbolinks:load', scan);
|
||||
|
||||
// Alpine.js x-init hook
|
||||
document.addEventListener('alpine:init', function () {
|
||||
if (window.Alpine) {
|
||||
Alpine.directive('image-gallery-init', function (el) {
|
||||
loadViewerCSS();
|
||||
loadViewerJS().then(function () {
|
||||
initOne(el);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// MutationObserver for dynamic content
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
let shouldScan = false;
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.addedNodes.length) {
|
||||
mutation.addedNodes.forEach(function (node) {
|
||||
if (node.nodeType === 1) {
|
||||
if (node.hasAttribute && node.hasAttribute('data-viewer-gallery')) {
|
||||
shouldScan = true;
|
||||
}
|
||||
if (node.querySelectorAll) {
|
||||
const galleries = node.querySelectorAll('[data-viewer-gallery]');
|
||||
if (galleries.length) {
|
||||
shouldScan = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (shouldScan) {
|
||||
setTimeout(scan, 100);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
// Expose scan function globally for manual triggering if needed
|
||||
window.ImageGallery = {
|
||||
scan: scan,
|
||||
init: initOne,
|
||||
destroy: destroyOne
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,87 +1,71 @@
|
||||
@php
|
||||
$state = $getState();
|
||||
|
||||
|
||||
if ($state instanceof \Illuminate\Support\Collection) {
|
||||
$state = $state->all();
|
||||
}
|
||||
|
||||
|
||||
$state = \Illuminate\Support\Arr::wrap($state);
|
||||
|
||||
|
||||
$limit = $getLimit();
|
||||
$limitedState = $limit ? array_slice($state, 0, $limit) : $state;
|
||||
$remaining = $limit ? max(0, count($state) - $limit) : 0;
|
||||
|
||||
|
||||
$isCircular = $isCircular();
|
||||
$isSquare = $isSquare();
|
||||
$isStacked = $isStacked();
|
||||
$overlap = $isStacked ? ($getOverlap() ?? 2) : null;
|
||||
|
||||
$overlap = $isStacked ? $getOverlap() ?? 2 : null;
|
||||
|
||||
$defaultWidth = $getWidth();
|
||||
$defaultHeight = $getHeight();
|
||||
|
||||
|
||||
$defaultWidth = $defaultWidth ? (is_numeric($defaultWidth) ? $defaultWidth . 'px' : $defaultWidth) : 'auto';
|
||||
$defaultHeight = $defaultHeight ? (is_numeric($defaultHeight) ? $defaultHeight . 'px' : $defaultHeight) : '40px';
|
||||
|
||||
|
||||
$galleryId = 'gallery-' . str_replace(['{', '}', '-'], '', (string) \Illuminate\Support\Str::uuid());
|
||||
@endphp
|
||||
|
||||
<div
|
||||
id="{{ $galleryId }}"
|
||||
{{
|
||||
$attributes
|
||||
->merge($getExtraAttributes(), escape: false)
|
||||
->class([
|
||||
'fi-ta-image',
|
||||
'flex items-center',
|
||||
match ($overlap) {
|
||||
1 => '-space-x-1 rtl:space-x-reverse',
|
||||
2 => '-space-x-2 rtl:space-x-reverse',
|
||||
3 => '-space-x-3 rtl:space-x-reverse',
|
||||
4 => '-space-x-4 rtl:space-x-reverse',
|
||||
5 => '-space-x-5 rtl:space-x-reverse',
|
||||
6 => '-space-x-6 rtl:space-x-reverse',
|
||||
7 => '-space-x-7 rtl:space-x-reverse',
|
||||
8 => '-space-x-8 rtl:space-x-reverse',
|
||||
default => 'gap-1.5',
|
||||
},
|
||||
])
|
||||
}}
|
||||
data-viewer-gallery
|
||||
wire:ignore.self
|
||||
>
|
||||
<div id="{{ $galleryId }}"
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class([
|
||||
'fi-ta-image',
|
||||
'flex items-center',
|
||||
match ($overlap) {
|
||||
1 => '-space-x-1 rtl:space-x-reverse',
|
||||
2 => '-space-x-2 rtl:space-x-reverse',
|
||||
3 => '-space-x-3 rtl:space-x-reverse',
|
||||
4 => '-space-x-4 rtl:space-x-reverse',
|
||||
5 => '-space-x-5 rtl:space-x-reverse',
|
||||
6 => '-space-x-6 rtl:space-x-reverse',
|
||||
7 => '-space-x-7 rtl:space-x-reverse',
|
||||
8 => '-space-x-8 rtl:space-x-reverse',
|
||||
default => 'gap-1.5',
|
||||
},
|
||||
]) }}
|
||||
data-viewer-gallery wire:ignore.self>
|
||||
@foreach ($limitedState as $stateItem)
|
||||
<img
|
||||
src="{{ $getImageUrl($stateItem) }}"
|
||||
<img src="{{ $getImageUrl($stateItem) }}"
|
||||
style="
|
||||
height: {{ $defaultHeight }};
|
||||
width: {{ $defaultWidth }};
|
||||
cursor: pointer;
|
||||
"
|
||||
{{
|
||||
$getExtraImgAttributeBag()
|
||||
->class([
|
||||
'max-w-none object-cover object-center curor',
|
||||
'rounded-full' => $isCircular,
|
||||
'rounded-lg' => $isSquare,
|
||||
'ring-white dark:ring-gray-900' => $isStacked,
|
||||
'ring-2' => $isStacked && ($overlap === null || $overlap > 0),
|
||||
])
|
||||
}}
|
||||
/>
|
||||
{{ $getExtraImgAttributeBag()->class([
|
||||
'max-w-none object-cover object-center curor',
|
||||
'rounded-full' => $isCircular,
|
||||
'rounded-lg' => $isSquare,
|
||||
'ring-white dark:ring-gray-900' => $isStacked,
|
||||
'ring-2' => $isStacked && ($overlap === null || $overlap > 0),
|
||||
]) }} />
|
||||
@endforeach
|
||||
|
||||
@if ($remaining > 0 && ($limitedRemainingText ?? true))
|
||||
<div
|
||||
style="
|
||||
<div style="
|
||||
min-height: {{ $defaultHeight }};
|
||||
min-width: {{ $defaultWidth }};
|
||||
height: {{ $defaultHeight }};
|
||||
width: {{ $defaultWidth }};
|
||||
"
|
||||
@class([
|
||||
'flex items-center justify-center font-medium text-gray-500',
|
||||
])
|
||||
>
|
||||
@class(['flex items-center justify-center font-medium text-gray-500'])>
|
||||
<span class="-ms-0.5 text-xs">
|
||||
+{{ $remaining }}
|
||||
</span>
|
||||
@@ -89,6 +73,4 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@once
|
||||
<x-image-gallery::viewer-script />
|
||||
@endonce
|
||||
{{-- Viewer.js assets are loaded dynamically via image-gallery.js --}}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
$ringWidth = $getRingWidth();
|
||||
$ringColor = $getRingColor();
|
||||
$galleryId = 'gallery-col-' . str_replace(['{', '}', '-'], '', (string) \Illuminate\Support\Str::uuid());
|
||||
|
||||
|
||||
// Determine border radius class
|
||||
if ($isCircular) {
|
||||
$borderRadiusClass = 'rounded-full';
|
||||
@@ -21,7 +21,7 @@
|
||||
} else {
|
||||
$borderRadiusClass = 'rounded';
|
||||
}
|
||||
|
||||
|
||||
// Border/Ring styles - only add if ringWidth > 0
|
||||
$hasRing = $ringWidth > 0;
|
||||
if ($hasRing) {
|
||||
@@ -36,14 +36,14 @@
|
||||
$ringStyle = '';
|
||||
$borderColorClass = '';
|
||||
}
|
||||
|
||||
|
||||
// Stacked spacing - use dynamic -space-x value
|
||||
if ($isStacked) {
|
||||
$stackedClass = "-space-x-{$stackedOverlap} rtl:space-x-reverse";
|
||||
} else {
|
||||
$stackedClass = 'gap-1';
|
||||
}
|
||||
|
||||
|
||||
// Size styles - only add if width/height specified
|
||||
$sizeStyle = '';
|
||||
if ($width) {
|
||||
@@ -54,30 +54,19 @@
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div
|
||||
id="{{ $galleryId }}"
|
||||
class="flex items-center {{ $stackedClass }}"
|
||||
data-viewer-gallery
|
||||
wire:ignore.self
|
||||
>
|
||||
@foreach($visibleUrls as $src)
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
loading="lazy"
|
||||
<div id="{{ $galleryId }}" class="flex items-center {{ $stackedClass }}" data-viewer-gallery wire:ignore.self>
|
||||
@foreach ($visibleUrls as $src)
|
||||
<img src="{{ $src }}" loading="lazy"
|
||||
class="object-cover {{ $borderColorClass }} {{ $borderRadiusClass }} hover:scale-110 transition cursor-pointer"
|
||||
style="{{ $sizeStyle }} {{ $ringStyle }}"
|
||||
alt="image"
|
||||
/>
|
||||
style="{{ $sizeStyle }} {{ $ringStyle }}" alt="image" />
|
||||
@endforeach
|
||||
|
||||
@if($shouldShowRemainingText() && $remaining > 0 && $width)
|
||||
@if ($shouldShowRemainingText() && $remaining > 0 && $width)
|
||||
<span class="flex items-center justify-center text-xs font-medium text-gray-600 dark:text-gray-200"
|
||||
style="width: {{ $width }}px; height: {{ $height ?? $width }}px; min-width: {{ $width }}px;">
|
||||
style="width: {{ $width }}px; height: {{ $height ?? $width }}px; min-width: {{ $width }}px;">
|
||||
+{{ $remaining }}
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@once
|
||||
<x-image-gallery::viewer-script />
|
||||
@endonce
|
||||
{{-- Viewer.js assets are loaded dynamically via image-gallery.js --}}
|
||||
|
||||
@@ -12,31 +12,35 @@
|
||||
|
||||
@php
|
||||
$galleryId = $id ?? 'gallery-' . str_replace(['{', '}', '-'], '', (string) \Illuminate\Support\Str::uuid());
|
||||
$urls = collect($images)->map(function($item) {
|
||||
if (is_string($item)) return $item;
|
||||
if (is_array($item)) return $item['image'] ?? $item['url'] ?? null;
|
||||
if (is_object($item)) return $item->image ?? $item->url ?? null;
|
||||
return null;
|
||||
})->filter()->values();
|
||||
$urls = collect($images)
|
||||
->map(function ($item) {
|
||||
if (is_string($item)) {
|
||||
return $item;
|
||||
}
|
||||
if (is_array($item)) {
|
||||
return $item['image'] ?? ($item['url'] ?? null);
|
||||
}
|
||||
if (is_object($item)) {
|
||||
return $item->image ?? ($item->url ?? null);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
->filter()
|
||||
->values();
|
||||
$emptyTextDisplay = $emptyText ?? __('image-gallery::messages.empty');
|
||||
@endphp
|
||||
|
||||
<div
|
||||
id="{{ $galleryId }}"
|
||||
<div id="{{ $galleryId }}"
|
||||
class="image-gallery flex overflow-x-auto {{ $gap }} my-4 pb-2 select-none {{ $wrapperClass }}"
|
||||
data-viewer-gallery
|
||||
>
|
||||
data-viewer-gallery>
|
||||
@forelse($urls as $src)
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
loading="lazy"
|
||||
<img src="{{ $src }}" loading="lazy"
|
||||
class="{{ $rounded }} shadow object-cover border border-gray-200 dark:border-gray-700 hover:scale-105 transition {{ $zoomCursor ? 'cursor-zoom-in' : '' }}"
|
||||
style="width: {{ (int) $thumbWidth }}px; height: {{ (int) $thumbHeight }}px; flex-shrink: 0;"
|
||||
alt="image"
|
||||
/>
|
||||
alt="image" />
|
||||
@empty
|
||||
<span class="text-gray-400 dark:text-gray-500">{{ $emptyTextDisplay }}</span>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<x-image-gallery::viewer-script />
|
||||
{{-- Viewer.js assets are loaded dynamically via image-gallery.js --}}
|
||||
|
||||
@@ -1,125 +1,4 @@
|
||||
@once
|
||||
<!-- Viewer.js CDN assets -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/viewerjs@1.11.6/dist/viewer.min.css" />
|
||||
<script src="https://unpkg.com/viewerjs@1.11.6/dist/viewer.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
function initOne(el) {
|
||||
if (!el || el._viewer || !window.Viewer) return;
|
||||
el._viewer = new Viewer(el, {
|
||||
toolbar: {
|
||||
zoomIn: 1,
|
||||
zoomOut: 1,
|
||||
oneToOne: 1,
|
||||
reset: 1,
|
||||
prev: 1,
|
||||
play: 0,
|
||||
next: 1,
|
||||
rotateLeft: 1,
|
||||
rotateRight: 1,
|
||||
flipHorizontal: 1,
|
||||
flipVertical: 1,
|
||||
},
|
||||
navbar: false,
|
||||
inline: false,
|
||||
movable: true,
|
||||
rotatable: true,
|
||||
scalable: true,
|
||||
fullscreen: true,
|
||||
transition: true,
|
||||
title: false,
|
||||
});
|
||||
}
|
||||
|
||||
function destroyOne(el) {
|
||||
if (el && el._viewer) {
|
||||
try {
|
||||
el._viewer.destroy();
|
||||
} catch(e) {}
|
||||
el._viewer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function scan() {
|
||||
document.querySelectorAll('[data-viewer-gallery]').forEach(function(el) {
|
||||
// Destroy and reinitialize to handle SPA navigation
|
||||
destroyOne(el);
|
||||
initOne(el);
|
||||
});
|
||||
}
|
||||
|
||||
// Run on various lifecycle events
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', scan);
|
||||
} else {
|
||||
// DOM is already ready
|
||||
scan();
|
||||
}
|
||||
|
||||
// Filament/Livewire 3.x SPA navigation
|
||||
document.addEventListener('livewire:navigated', function() {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
|
||||
// Livewire 3.x morph updates
|
||||
document.addEventListener('livewire:init', function() {
|
||||
if (window.Livewire) {
|
||||
Livewire.hook('morph.updated', function({ el }) {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// For Livewire 2.x compatibility
|
||||
document.addEventListener('livewire:load', scan);
|
||||
if (window.Livewire && window.Livewire.hook) {
|
||||
try {
|
||||
window.Livewire.hook('message.processed', function() {
|
||||
setTimeout(scan, 100);
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Turbolinks/Turbo compatibility
|
||||
document.addEventListener('turbo:load', scan);
|
||||
document.addEventListener('turbolinks:load', scan);
|
||||
|
||||
// Alpine.js x-init hook
|
||||
document.addEventListener('alpine:init', function() {
|
||||
Alpine.directive('image-gallery-init', function(el) {
|
||||
initOne(el);
|
||||
});
|
||||
});
|
||||
|
||||
// MutationObserver for dynamic content
|
||||
const observer = new MutationObserver(function(mutations) {
|
||||
let shouldScan = false;
|
||||
mutations.forEach(function(mutation) {
|
||||
if (mutation.addedNodes.length) {
|
||||
mutation.addedNodes.forEach(function(node) {
|
||||
if (node.nodeType === 1) {
|
||||
if (node.hasAttribute && node.hasAttribute('data-viewer-gallery')) {
|
||||
shouldScan = true;
|
||||
}
|
||||
if (node.querySelectorAll) {
|
||||
const galleries = node.querySelectorAll('[data-viewer-gallery]');
|
||||
if (galleries.length) {
|
||||
shouldScan = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (shouldScan) {
|
||||
setTimeout(scan, 100);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endonce
|
||||
{{--
|
||||
Viewer.js assets are now loaded dynamically via image-gallery.js
|
||||
This component is kept for backward compatibility but can be safely removed from blade files
|
||||
--}}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
$zoomCursor = $hasZoomCursor();
|
||||
$wrapperClass = $getWrapperClass() ?? '';
|
||||
$galleryId = 'gallery-entry-' . str_replace(['{', '}', '-'], '', (string) \Illuminate\Support\Str::uuid());
|
||||
|
||||
|
||||
// Size styles - only add if width/height specified
|
||||
$sizeStyle = '';
|
||||
if ($width) {
|
||||
@@ -17,28 +17,20 @@
|
||||
$sizeStyle .= " height: {$height}px;";
|
||||
}
|
||||
if ($width || $height) {
|
||||
$sizeStyle .= " flex-shrink: 0;";
|
||||
$sizeStyle .= ' flex-shrink: 0;';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$getEntryWrapperView()" :entry="$entry">
|
||||
<div
|
||||
id="{{ $galleryId }}"
|
||||
<div id="{{ $galleryId }}"
|
||||
class="image-gallery flex overflow-x-auto {{ $gap }} my-2 pb-2 select-none {{ $wrapperClass }}"
|
||||
data-viewer-gallery
|
||||
>
|
||||
@foreach($urls as $src)
|
||||
<img
|
||||
src="{{ $src }}"
|
||||
loading="lazy"
|
||||
data-viewer-gallery>
|
||||
@foreach ($urls as $src)
|
||||
<img src="{{ $src }}" loading="lazy"
|
||||
class="{{ $rounded }} shadow object-cover border border-gray-200 dark:border-gray-700 hover:scale-105 transition cursor-pointer"
|
||||
@if($sizeStyle) style="{{ $sizeStyle }}" @endif
|
||||
alt="image"
|
||||
/>
|
||||
@if ($sizeStyle) style="{{ $sizeStyle }}" @endif alt="image" />
|
||||
@endforeach
|
||||
</div>
|
||||
</x-dynamic-component>
|
||||
|
||||
@once
|
||||
<x-image-gallery::viewer-script />
|
||||
@endonce
|
||||
{{-- Viewer.js assets are loaded dynamically via image-gallery.js --}}
|
||||
|
||||
@@ -1,79 +1,66 @@
|
||||
@php
|
||||
$state = $getState();
|
||||
|
||||
|
||||
if ($state instanceof \Illuminate\Support\Collection) {
|
||||
$state = $state->all();
|
||||
}
|
||||
|
||||
|
||||
$state = \Illuminate\Support\Arr::wrap($state);
|
||||
|
||||
|
||||
$limit = $getLimit();
|
||||
$limitedState = $limit ? array_slice($state, 0, $limit) : $state;
|
||||
$remaining = $limit ? max(0, count($state) - $limit) : 0;
|
||||
|
||||
|
||||
$isCircular = $isCircular();
|
||||
$isSquare = $isSquare();
|
||||
$isStacked = $isStacked();
|
||||
$overlap = $isStacked ? ($getOverlap() ?? 2) : null;
|
||||
|
||||
$overlap = $isStacked ? $getOverlap() ?? 2 : null;
|
||||
|
||||
$defaultWidth = $getWidth();
|
||||
$defaultHeight = $getHeight();
|
||||
|
||||
|
||||
$defaultWidth = $defaultWidth ? (is_numeric($defaultWidth) ? $defaultWidth . 'px' : $defaultWidth) : 'auto';
|
||||
$defaultHeight = $defaultHeight ? (is_numeric($defaultHeight) ? $defaultHeight . 'px' : $defaultHeight) : '150px';
|
||||
|
||||
|
||||
$galleryId = 'gallery-' . str_replace(['{', '}', '-'], '', (string) \Illuminate\Support\Str::uuid());
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$getEntryWrapperView()" :entry="$entry">
|
||||
<div
|
||||
id="{{ $galleryId }}"
|
||||
{{
|
||||
$attributes
|
||||
->merge($getExtraAttributes(), escape: false)
|
||||
->class([
|
||||
'fi-in-image',
|
||||
'flex items-center',
|
||||
match ($overlap) {
|
||||
1 => '-space-x-1 rtl:space-x-reverse',
|
||||
2 => '-space-x-2 rtl:space-x-reverse',
|
||||
3 => '-space-x-3 rtl:space-x-reverse',
|
||||
4 => '-space-x-4 rtl:space-x-reverse',
|
||||
5 => '-space-x-5 rtl:space-x-reverse',
|
||||
6 => '-space-x-6 rtl:space-x-reverse',
|
||||
7 => '-space-x-7 rtl:space-x-reverse',
|
||||
8 => '-space-x-8 rtl:space-x-reverse',
|
||||
default => 'gap-1.5',
|
||||
},
|
||||
])
|
||||
}}
|
||||
data-viewer-gallery
|
||||
wire:ignore.self
|
||||
>
|
||||
<div id="{{ $galleryId }}"
|
||||
{{ $attributes->merge($getExtraAttributes(), escape: false)->class([
|
||||
'fi-in-image',
|
||||
'flex items-center',
|
||||
match ($overlap) {
|
||||
1 => '-space-x-1 rtl:space-x-reverse',
|
||||
2 => '-space-x-2 rtl:space-x-reverse',
|
||||
3 => '-space-x-3 rtl:space-x-reverse',
|
||||
4 => '-space-x-4 rtl:space-x-reverse',
|
||||
5 => '-space-x-5 rtl:space-x-reverse',
|
||||
6 => '-space-x-6 rtl:space-x-reverse',
|
||||
7 => '-space-x-7 rtl:space-x-reverse',
|
||||
8 => '-space-x-8 rtl:space-x-reverse',
|
||||
default => 'gap-1.5',
|
||||
},
|
||||
]) }}
|
||||
data-viewer-gallery wire:ignore.self>
|
||||
@foreach ($limitedState as $stateItem)
|
||||
<img
|
||||
src="{{ $getImageUrl($stateItem) }}"
|
||||
<img src="{{ $getImageUrl($stateItem) }}"
|
||||
style="
|
||||
height: {{ $defaultHeight }};
|
||||
width: {{ $defaultWidth }};
|
||||
cursor: pointer;
|
||||
"
|
||||
{{
|
||||
$getExtraImgAttributeBag()
|
||||
->class([
|
||||
'max-w-none object-cover object-center',
|
||||
'rounded-full' => $isCircular,
|
||||
'rounded-lg' => $isSquare,
|
||||
'ring-white dark:ring-gray-900' => $isStacked,
|
||||
'ring-2' => $isStacked && ($overlap === null || $overlap > 0),
|
||||
])
|
||||
}}
|
||||
/>
|
||||
{{ $getExtraImgAttributeBag()->class([
|
||||
'max-w-none object-cover object-center',
|
||||
'rounded-full' => $isCircular,
|
||||
'rounded-lg' => $isSquare,
|
||||
'ring-white dark:ring-gray-900' => $isStacked,
|
||||
'ring-2' => $isStacked && ($overlap === null || $overlap > 0),
|
||||
]) }} />
|
||||
@endforeach
|
||||
|
||||
@if ($remaining > 0 && ($limitedRemainingText ?? true))
|
||||
<div
|
||||
style="
|
||||
<div style="
|
||||
min-height: {{ $defaultHeight }};
|
||||
min-width: {{ $defaultWidth }};
|
||||
height: {{ $defaultHeight }};
|
||||
@@ -84,8 +71,7 @@
|
||||
'rounded-full' => $isCircular,
|
||||
'rounded-lg' => $isSquare,
|
||||
'ring-2' => $isStacked && ($overlap === null || $overlap > 0),
|
||||
])
|
||||
>
|
||||
])>
|
||||
<span class="-ms-0.5 text-xs">
|
||||
+{{ $remaining }}
|
||||
</span>
|
||||
@@ -93,7 +79,5 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@once
|
||||
<x-image-gallery::viewer-script />
|
||||
@endonce
|
||||
{{-- Viewer.js assets are loaded dynamically via image-gallery.js --}}
|
||||
</x-dynamic-component>
|
||||
|
||||
Reference in New Issue
Block a user