feat(pdf-viewer): implement pdf.js with pagination and swipe controls

This commit is contained in:
majid
2025-07-29 09:57:54 +07:00
parent c459d1be70
commit be41a815f7

View File

@@ -1,98 +1,165 @@
@extends('layouts.main')
@section('breadcrumbs')
{{-- {{ Breadcrumbs::render(request()->route()->getName()) }} --}}
@endsection
@section('content')
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form
action="{{ isset($daftarPustaka->id) ? route('daftar-pustaka.update', $daftarPustaka->id) : route('daftar-pustaka.store') }}"
method="POST">
@csrf
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<h3 class="card-title">
{{ $daftarPustaka->judul ?? '' }}
</h3>
<div class="flex items-center gap-2">
<a href="{{ route('daftar-pustaka.index') }}" class="btn btn-xs btn-info"><i
class="ki-filled ki-exit-left"></i> Back</a>
</div>
</div>
<div class="card-body grid gap-5">
<div class=" min-w-3xl">
<div class="p-4 h-full flex flex-col">
<div class="flex justify-between items-center mb-4">
<a href="{{ asset('storage/' . $daftarPustaka->attachment)}}" class="btn btn-primary btn-sm">
<i class="ki-duotone ki-cloud-download me-1"><span class="path1"></span><span
class="path2"></span></i>
Download File
</a>
</div>
@php
$fileExtension = pathinfo($daftarPustaka->attachment, PATHINFO_EXTENSION);
// cek extension
$isPdf = $fileExtension == 'pdf';
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$isImage = in_array(strtolower($fileExtension), $imageExtensions);
$fileUrl = asset('storage/' . $daftarPustaka->attachment);
@endphp
@if ($isPdf)
<iframe src="{{$fileUrl}}"
width="100%" height="600px" frameborder="0"></iframe>
@elseif ($isImage)
<img src="{{ $fileUrl }}" class="w-full object-contain rounded" />
@else
<p class="text-red-500">File tidak bisa ditampilkan, silakan <a href="{{ $fileUrl }}"
class="text-blue-500 underline" download>unduh di sini</a>.</p>
@endif
</div>
</div>
<div class="border border-t">
</div>
<div>
{{ $daftarPustaka->deskripsi ?? '' }}
</div>
<div class="w-full grid gap-5 lg:gap-7.5 mx-auto">
<form
action="{{ isset($daftarPustaka->id) ? route('daftar-pustaka.update', $daftarPustaka->id) : route('daftar-pustaka.store') }}"
method="POST">
@csrf
<div class="card border border-agi-100 pb-2.5">
<div class="card-header bg-agi-50" id="basic_settings">
<h3 class="card-title">
{{ $daftarPustaka->judul ?? '' }}
</h3>
<div class="flex items-center gap-2">
<a href="{{ route('daftar-pustaka.index') }}" class="btn btn-xs btn-info">
<i class="ki-filled ki-exit-left"></i> Back
</a>
</div>
</div>
</form>
</div>
<div class="card-body grid gap-5">
<div class="min-w-3xl">
<div class="p-4 h-full flex flex-col">
<div class="flex justify-between items-center mb-4">
<a href="{{ asset('storage/' . $daftarPustaka->attachment) }}" class="btn btn-primary btn-sm">
<i class="ki-duotone ki-cloud-download me-1"></i>
Download File
</a>
</div>
@php
$fileExtension = pathinfo($daftarPustaka->attachment, PATHINFO_EXTENSION);
$isPdf = strtolower($fileExtension) == 'pdf';
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$isImage = in_array(strtolower($fileExtension), $imageExtensions);
$fileUrl = asset('storage/' . $daftarPustaka->attachment);
@endphp
@if ($isPdf)
<canvas id="pdfViewer" class="w-full border rounded touch-pan-y"></canvas>
<div class="flex justify-center gap-3 mt-3">
<button type="button" id="prevPage" class="btn btn-primary btn-sm">Previous</button>
<span>Page: <span id="pageNum">1</span> / <span id="pageCount">0</span></span>
<button type="button" id="nextPage" class="btn btn-primary btn-sm">Next</button>
</div>
@elseif ($isImage)
<img src="{{ $fileUrl }}" class="w-full object-contain rounded" />
@else
<p class="text-red-500">File tidak bisa ditampilkan, silakan
<a href="{{ $fileUrl }}" class="text-blue-500 underline" download>unduh di sini</a>.
</p>
@endif
</div>
</div>
<div class="border border-t"></div>
<div>
{{ $daftarPustaka->deskripsi ?? '' }}
</div>
</div>
</div>
</form>
</div>
@endsection
{{-- @push('scripts')
<script src="{{ asset('vendor/pdfobject.min.js') }}"></script>
@push('scripts')
<!-- PDF.js CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const url = @json($fileUrl);
<script>
document.addEventListener('DOMContentLoaded', function() {
let url = @json($daftarPustaka->attachment);
console.log(url);
if (url.endsWith('.pdf')) {
const pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
const fileExtension = url.split('.').pop().toLowerCase();
const previewContent = document.getElementById('previewContent');
let pdfDoc = null;
let pageNum = 1;
let pageRendering = false;
let pageNumPending = null;
const scale = 1.3;
const canvas = document.getElementById('pdfViewer');
const ctx = canvas.getContext('2d');
const urlStorage = 'storage' + '/' + url;
function renderPage(num) {
pageRendering = true;
pdfDoc.getPage(num).then(function (page) {
const viewport = page.getViewport({ scale: scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
if (['pdf'].includes(fileExtension)) {
if (window.innerWidth < 768) {
document.getElementById('pdfFrame').style.display = 'block';
document.getElementById('pdfFrame').src = urlStorage;
} else {
PDFObject.embed(urlStorage, "#previewContent");
}
} else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExtension)) {
previewContent.innerHTML =
`<img src="${urlStorage}" alt="Preview" class="max-w-full max-h-full object-contain">`;
const renderContext = {
canvasContext: ctx,
viewport: viewport
};
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
pageRendering = false;
document.getElementById('pageNum').textContent = num;
if (pageNumPending !== null) {
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
}
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
previewContent.innerHTML = '<p class="text-center">Unsupported file type</p>';
renderPage(num);
}
}
function onPrevPage() {
if (pageNum <= 1) return;
pageNum--;
queueRenderPage(pageNum);
}
function onNextPage() {
if (pageNum >= pdfDoc.numPages) return;
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('prevPage').addEventListener('click', onPrevPage);
document.getElementById('nextPage').addEventListener('click', onNextPage);
// Swipe gesture
let touchStartX = 0;
let touchEndX = 0;
canvas.addEventListener('touchstart', function (e) {
touchStartX = e.changedTouches[0].screenX;
});
</script>
@endpush --}}
canvas.addEventListener('touchend', function (e) {
touchEndX = e.changedTouches[0].screenX;
handleSwipe();
});
function handleSwipe() {
const swipeThreshold = 50; // min distance for swipe
if (touchEndX < touchStartX - swipeThreshold) {
onNextPage();
} else if (touchEndX > touchStartX + swipeThreshold) {
onPrevPage();
}
}
pdfjsLib.getDocument(url).promise.then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('pageCount').textContent = pdfDoc.numPages;
renderPage(pageNum);
});
}
});
</script>
@endpush