feat(pdf-viewer): implement pdf.js with pagination and swipe controls
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user