Skip to content

Commit 40e45cc

Browse files
committed
feat: update showAsLink() to show compact list with modal preview
- showAsLink(true) now shows compact row layout (icon + title) - Clicking opens modal preview for supported file types - Non-previewable files open in new tab - Removes the big card/box layout for cleaner list view
1 parent 8c66e42 commit 40e45cc

2 files changed

Lines changed: 181 additions & 14 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ FileViewEntry::make('documents')
9999
| `withModalEye(bool $enabled)` | Add eye button for modal preview when asModal(false) |
100100
| `disk(string $disk)` | Storage disk name |
101101
| `downloadable(bool $enabled)` | Show download button |
102-
| `showAsLink(bool $enabled)` | Show as simple link (no preview) |
102+
| `showAsLink(bool $enabled)` | Show as compact list with modal preview |
103103
| `previewHeight(int\|string $height)` | Modal height (default: 300px) |
104104

105105
### Grid Columns Reference

‎resources/views/infolists/components/file-view-entry.blade.php‎

Lines changed: 180 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,27 +99,194 @@
9999
@endphp
100100

101101
@if($fileUrl)
102-
@if($showAsLink)
103-
{{-- Click opens new tab (no preview) --}}
104-
<a
105-
href="{{ $fileUrl }}"
106-
target="_blank"
107-
class="group block"
102+
@if($showAsLink && $canPreview)
103+
{{-- Compact list view with modal --}}
104+
<div
105+
x-data="{ open: false }"
106+
class="group cursor-pointer"
108107
>
109-
<div class="w-full aspect-square p-4 rounded-2xl bg-gray-50 dark:bg-gray-800 hover:bg-primary-50 dark:hover:bg-primary-900/20 border border-gray-200 dark:border-gray-700 hover:border-primary-200 dark:hover:border-primary-800 transition-all flex flex-col items-center justify-center text-center">
110-
{{-- Icon at top --}}
111-
<div class="flex-1 flex items-center justify-center">
112-
@svg($fileIcon, 'w-12 h-12 text-gray-400 group-hover:text-primary-500')
113-
</div>
108+
<div
109+
class="flex items-center gap-3 p-3 rounded-lg bg-gray-50 dark:bg-gray-800 hover:bg-primary-50 dark:hover:bg-primary-900/20 border border-gray-200 dark:border-gray-700 hover:border-primary-200 dark:hover:border-primary-800 transition-all"
110+
x-on:click="open = true"
111+
>
112+
{{-- Icon --}}
113+
@svg($fileIcon, 'w-5 h-5 text-gray-400 group-hover:text-primary-500 flex-shrink-0')
114114

115115
{{-- Filename --}}
116-
<span class="text-sm font-medium text-gray-900 dark:text-white truncate w-full mt-2">{{ $fileTitle }}</span>
116+
<span class="text-sm font-medium text-gray-900 dark:text-white truncate flex-1">{{ $fileTitle }}</span>
117117

118118
@if($displayDate)
119119
{{-- Date --}}
120-
<span class="text-xs text-gray-500 dark:text-gray-400 mt-1">{{ $displayDate }}</span>
120+
<span class="text-xs text-gray-500 dark:text-gray-400 flex-shrink-0">{{ $displayDate }}</span>
121121
@endif
122122
</div>
123+
124+
{{-- Modal --}}
125+
<div
126+
x-show="open"
127+
style="display: none;"
128+
x-on:keydown.escape.window="open = false"
129+
class="fixed inset-0 z-50 overflow-y-auto"
130+
aria-labelledby="modal-title-{{ $index }}"
131+
role="dialog"
132+
aria-modal="true"
133+
>
134+
{{-- Backdrop --}}
135+
<div
136+
x-show="open"
137+
x-transition:enter="ease-out duration-300"
138+
x-transition:enter-start="opacity-0"
139+
x-transition:enter-end="opacity-100"
140+
x-transition:leave="ease-in duration-200"
141+
x-transition:leave-start="opacity-100"
142+
x-transition:leave-end="opacity-0"
143+
class="fixed inset-0 bg-gray-500 dark:bg-gray-900 bg-opacity-75 dark:bg-opacity-75 transition-opacity"
144+
x-on:click="open = false"
145+
></div>
146+
147+
<div class="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
148+
<div
149+
x-show="open"
150+
x-transition:enter="ease-out duration-300"
151+
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
152+
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
153+
x-transition:leave="ease-in duration-200"
154+
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
155+
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
156+
class="relative transform overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-4xl"
157+
x-on:click.stop
158+
>
159+
{{-- Header --}}
160+
<div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 flex items-center justify-between">
161+
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white truncate pr-4" id="modal-title-{{ $index }}">
162+
{{ $fileTitle }}
163+
</h3>
164+
<button
165+
type="button"
166+
class="text-gray-400 hover:text-gray-500 focus:outline-none flex-shrink-0"
167+
x-on:click="open = false"
168+
>
169+
@svg('heroicon-o-x-mark', 'w-6 h-6')
170+
</button>
171+
</div>
172+
173+
{{-- Content --}}
174+
<div class="px-4 py-5 sm:p-6">
175+
<div class="w-full rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700" style="max-height: 60vh;">
176+
@switch($fileType)
177+
@case('image')
178+
<img
179+
src="{{ $fileUrl }}"
180+
alt="{{ $fileTitle }}"
181+
class="w-full h-auto object-contain bg-gray-100 dark:bg-gray-800"
182+
/>
183+
@break
184+
185+
@case('video')
186+
<video
187+
controls
188+
class="w-full bg-gray-900"
189+
style="max-height: 60vh;"
190+
>
191+
<source src="{{ $fileUrl }}" type="video/{{ pathinfo($filePath, PATHINFO_EXTENSION) }}">
192+
@lang('Your browser does not support the video tag.')
193+
</video>
194+
@break
195+
196+
@case('audio')
197+
<div class="w-full p-8 bg-gray-50 dark:bg-gray-800">
198+
<audio controls class="w-full">
199+
<source src="{{ $fileUrl }}" type="audio/{{ pathinfo($filePath, PATHINFO_EXTENSION) }}">
200+
@lang('Your browser does not support the audio tag.')
201+
</audio>
202+
</div>
203+
@break
204+
205+
@case('pdf')
206+
<iframe
207+
src="{{ $fileUrl }}"
208+
class="w-full border-0"
209+
style="height: 60vh;"
210+
title="{{ $fileTitle }}"
211+
></iframe>
212+
@break
213+
214+
@case('text')
215+
<div class="w-full p-4 bg-gray-50 dark:bg-gray-800 overflow-auto font-mono text-sm" style="max-height: 60vh;">
216+
@php
217+
$disk = \Illuminate\Support\Facades\Storage::disk($getDiskName());
218+
$fileContent = '';
219+
try {
220+
if ($disk->exists($filePath)) {
221+
$fileContent = $disk->get($filePath);
222+
}
223+
} catch (\Exception $e) {
224+
$fileContent = 'Unable to read file content.';
225+
}
226+
@endphp
227+
<pre class="whitespace-pre-wrap break-words">{{ $fileContent }}</pre>
228+
</div>
229+
@break
230+
231+
@default
232+
<div class="w-full p-8 bg-gray-50 dark:bg-gray-800 flex flex-col items-center justify-center">
233+
@svg($fileIcon, 'w-16 h-16 text-gray-400 mb-4')
234+
<p class="text-gray-600 dark:text-gray-400">Preview not available</p>
235+
</div>
236+
@endswitch
237+
</div>
238+
</div>
239+
240+
{{-- Footer --}}
241+
<div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6 gap-2">
242+
<a
243+
href="{{ $fileUrl }}"
244+
target="_blank"
245+
class="inline-flex w-full justify-center rounded-md bg-primary-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 sm:ml-3 sm:w-auto"
246+
>
247+
@svg('heroicon-o-arrow-top-right-on-square', 'w-4 h-4 mr-1')
248+
@lang('Open')
249+
</a>
250+
@if($downloadable)
251+
<a
252+
href="{{ $fileUrl }}"
253+
target="_blank"
254+
download
255+
class="mt-3 inline-flex w-full justify-center rounded-md bg-white dark:bg-gray-800 px-3 py-2 text-sm font-semibold text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 sm:mt-0 sm:w-auto"
256+
>
257+
@svg('heroicon-o-arrow-down-tray', 'w-4 h-4 mr-1')
258+
@lang('Download')
259+
</a>
260+
@endif
261+
<button
262+
type="button"
263+
class="mt-3 inline-flex w-full justify-center rounded-md bg-white dark:bg-gray-800 px-3 py-2 text-sm font-semibold text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700 sm:mt-0 sm:w-auto"
264+
x-on:click="open = false"
265+
>
266+
@lang('Close')
267+
</button>
268+
</div>
269+
</div>
270+
</div>
271+
</div>
272+
</div>
273+
@elseif($showAsLink)
274+
{{-- Simple link for non-previewable files --}}
275+
<a
276+
href="{{ $fileUrl }}"
277+
target="_blank"
278+
class="group flex items-center gap-3 p-3 rounded-lg bg-gray-50 dark:bg-gray-800 hover:bg-primary-50 dark:hover:bg-primary-900/20 border border-gray-200 dark:border-gray-700 hover:border-primary-200 dark:hover:border-primary-800 transition-all"
279+
>
280+
{{-- Icon --}}
281+
@svg($fileIcon, 'w-5 h-5 text-gray-400 group-hover:text-primary-500 flex-shrink-0')
282+
283+
{{-- Filename --}}
284+
<span class="text-sm font-medium text-gray-900 dark:text-white truncate flex-1">{{ $fileTitle }}</span>
285+
286+
@if($displayDate)
287+
{{-- Date --}}
288+
<span class="text-xs text-gray-500 dark:text-gray-400 flex-shrink-0">{{ $displayDate }}</span>
289+
@endif
123290
</a>
124291
@elseif($asModal)
125292
{{-- Click opens modal --}}

0 commit comments

Comments
 (0)