Jetzt über pdf.js
This commit is contained in:
BIN
mod_pdf_tree/.DS_Store
vendored
BIN
mod_pdf_tree/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
@@ -1,146 +1,72 @@
|
||||
<?php defined('_JEXEC') or die; ?>
|
||||
<?php defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
$pdfjsViewer = Uri::root() . 'media/com_eis/pdfjs/web/viewer.html';
|
||||
?>
|
||||
|
||||
<style>
|
||||
/* =========================
|
||||
Layout-Container
|
||||
========================= */
|
||||
.pdf-tree-wrapper {
|
||||
display: flex;
|
||||
align-items: stretch; /* wichtig für scrollende Kinder */
|
||||
gap: 1em;
|
||||
background: #f9f9f9;
|
||||
padding: 1em;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
||||
min-height: 0; /* erlaubt Overflow in Flex-Kindern */
|
||||
/* Layout */
|
||||
.pdf-tree-wrapper{
|
||||
display:flex; align-items:stretch; gap:1em;
|
||||
background:#f9f9f9; padding:1em; border:1px solid #ddd; border-radius:6px;
|
||||
box-shadow:0 4px 10px rgba(0,0,0,.05); min-height:0;
|
||||
}
|
||||
.pdf-tree-container{
|
||||
flex:0 0 30%;
|
||||
max-height:calc(var(--vh, 100vh) - 160px);
|
||||
overflow:auto; -webkit-overflow-scrolling:touch; min-height:0;
|
||||
}
|
||||
#pdf-preview{
|
||||
flex:1 1 auto; min-width:320px;
|
||||
height:calc(var(--vh, 100vh) - 160px);
|
||||
border:1px solid #ccc; background:#fff; display:none; min-height:0;
|
||||
position:relative; overflow:hidden;
|
||||
}
|
||||
/* Toolbar */
|
||||
.preview-actions{
|
||||
display:none; align-items:center; justify-content:space-between; gap:.5rem;
|
||||
padding:.5rem .75rem; border-bottom:1px solid #eee; background:#fafafa; height:42px;
|
||||
}
|
||||
/* Innerer Viewport (verhindert WebKit-Flex-Hickups) */
|
||||
.pdf-viewport{
|
||||
position:absolute; top:42px; left:0; right:0; bottom:0;
|
||||
overflow:auto; -webkit-overflow-scrolling:touch; min-height:0;
|
||||
}
|
||||
.pdf-viewport iframe{ position:absolute; inset:0; width:100%; height:100%; border:0; display:block; }
|
||||
|
||||
/* Linke Spalte: Baum */
|
||||
.pdf-tree-container {
|
||||
flex: 0 0 30%;
|
||||
max-height: calc(var(--vh, 100vh) - 160px); /* robust auf Desktop/iPad */
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch; /* smooth scroll iOS */
|
||||
min-height: 0; /* wichtig für Scrollbar */
|
||||
}
|
||||
|
||||
/* Rechte Spalte: Vorschau */
|
||||
#pdf-preview {
|
||||
flex: 1 1 auto;
|
||||
min-width: 320px; /* iPad-freundlicher als 800px */
|
||||
height: calc(var(--vh, 100vh) - 160px);
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
display: none; /* wird erst bei Klick sichtbar */
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Preview-Toolbar / Fallback-Link */
|
||||
.preview-actions {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: .5rem;
|
||||
padding: .5rem .75rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
background: #fafafa;
|
||||
}
|
||||
.preview-actions a,
|
||||
.preview-actions button {
|
||||
font: inherit;
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: .35rem .6rem;
|
||||
}
|
||||
|
||||
/* iFrame */
|
||||
#pdf-preview iframe {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px); /* Platz für .preview-actions */
|
||||
border: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
Baum-Liste
|
||||
========================= */
|
||||
.pdf-tree { list-style: none; margin: 0; padding: 0; }
|
||||
.pdf-tree ul { list-style: none; margin-left: 1.25em; padding: 0; display: none; }
|
||||
.pdf-tree li { margin: 0.35em 0; }
|
||||
|
||||
/* Touch-freundliche Tap-Ziele */
|
||||
.folder > .toggle {
|
||||
cursor: pointer;
|
||||
width: 1.5em; /* größer für Finger */
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
margin-right: .1em;
|
||||
}
|
||||
.folder > .folder-label { /* gesamten Text klickbar machen */
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: .15rem .1rem; /* kleine Hitbox-Polster */
|
||||
border-radius: 3px;
|
||||
}
|
||||
.folder > .folder-label:active { background: rgba(0,0,0,0.05); }
|
||||
|
||||
.pdf-tree .meta { color: #666; }
|
||||
|
||||
/* Datei-Link */
|
||||
.file-link {
|
||||
text-decoration: none;
|
||||
color: #0366d6;
|
||||
}
|
||||
.file-link:hover { text-decoration: underline; }
|
||||
.file-link:active { opacity: .7; }
|
||||
/* Baum */
|
||||
.pdf-tree{ list-style:none; margin:0; padding:0; }
|
||||
.pdf-tree ul{ list-style:none; margin-left:1.25em; padding:0; display:none; }
|
||||
.pdf-tree li{ margin:.35em 0; }
|
||||
.folder>.toggle{ cursor:pointer; width:1.5em; display:inline-flex; align-items:center; justify-content:center; user-select:none; margin-right:.1em; }
|
||||
.folder>.folder-label{ cursor:pointer; user-select:none; padding:.15rem .1rem; border-radius:3px; }
|
||||
.folder>.folder-label:active{ background:rgba(0,0,0,.05); }
|
||||
.file-link{ text-decoration:none; color:#0366d6; }
|
||||
.file-link:hover{ text-decoration:underline; }
|
||||
.file-link:active{ opacity:.7; }
|
||||
|
||||
/* Buttons */
|
||||
.expand-collapse {
|
||||
margin-bottom: 0.5em;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.expand-collapse button {
|
||||
margin-right: 0; /* via gap */
|
||||
font: inherit;
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: .4rem .6rem;
|
||||
}
|
||||
.expand-collapse{ margin-bottom:.5em; display:flex; gap:.5rem; flex-wrap:wrap; }
|
||||
.expand-collapse button{ font:inherit; border:1px solid #ccc; background:#fff; border-radius:4px; padding:.4rem .6rem; }
|
||||
|
||||
/* Scrollbar-Optik (optional) */
|
||||
.pdf-tree-container::-webkit-scrollbar { width: 10px; }
|
||||
.pdf-tree-container::-webkit-scrollbar-thumb { background: rgba(0,0,0,.2); border-radius: 6px; }
|
||||
.pdf-tree-container::-webkit-scrollbar{ width:10px; }
|
||||
.pdf-tree-container::-webkit-scrollbar-thumb{ background:rgba(0,0,0,.2); border-radius:6px; }
|
||||
|
||||
/* =========================
|
||||
Responsive: iPad Portrait / schmal
|
||||
========================= */
|
||||
@media (max-width: 1024px) {
|
||||
.pdf-tree-wrapper { flex-direction: column; }
|
||||
.pdf-tree-container,
|
||||
#pdf-preview {
|
||||
max-height: calc(var(--vh, 100vh) - 220px);
|
||||
height: auto;
|
||||
}
|
||||
/* Responsive */
|
||||
@media (max-width:1024px){
|
||||
.pdf-tree-wrapper{ flex-direction:column; }
|
||||
.pdf-tree-container, #pdf-preview{ max-height:calc(var(--vh, 100vh) - 220px); height:auto; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Buttons: Aus-/Einklappen -->
|
||||
<div class="expand-collapse">
|
||||
<button id="expand-all" type="button">ausklappen</button>
|
||||
<button id="collapse-all" type="button">einklappen</button>
|
||||
</div>
|
||||
|
||||
<!-- PDF-Baum und Vorschau -->
|
||||
<div class="pdf-tree-wrapper">
|
||||
<div class="pdf-tree-container">
|
||||
<div class="pdf-tree-wrapper" id="eis-wrapper">
|
||||
<div class="pdf-tree-container" id="eis-tree">
|
||||
<?= ModEisAnzeigeHelper::renderTree($items); ?>
|
||||
</div>
|
||||
|
||||
@@ -149,92 +75,71 @@
|
||||
<span id="preview-filename" aria-live="polite"></span>
|
||||
<a id="open-newtab" href="#" target="_blank" rel="noopener">in neuem Tab öffnen</a>
|
||||
</div>
|
||||
<!-- iFrame wird dynamisch eingesetzt -->
|
||||
<div class="pdf-viewport" id="pdf-viewport"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// --- echtes Viewport-Height-Polyfill (fix für Safari/macOS/iOS) ---
|
||||
function setRealVh() {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh * 100}px`);
|
||||
}
|
||||
setRealVh();
|
||||
window.addEventListener('resize', setRealVh, { passive: true });
|
||||
// echtes Viewport-Height-Polyfill
|
||||
function setRealVh(){ const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh*100}px`); }
|
||||
setRealVh(); window.addEventListener('resize', setRealVh, {passive:true});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const treeContainer = document.querySelector('.pdf-tree-container');
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
const tree = document.getElementById('eis-tree');
|
||||
const preview = document.getElementById('pdf-preview');
|
||||
if (!treeContainer || !preview) return;
|
||||
|
||||
const previewBar = preview.querySelector('.preview-actions');
|
||||
const openNewTab = document.getElementById('open-newtab');
|
||||
const previewName = document.getElementById('preview-filename');
|
||||
const openNewTab = document.getElementById('open-newtab');
|
||||
const viewport = document.getElementById('pdf-viewport');
|
||||
|
||||
// --- (1) Verzeichnisse toggeln – Caret und Label (Event Delegation, robust für iPad) ---
|
||||
treeContainer.addEventListener('click', (ev) => {
|
||||
// Ordner togglen
|
||||
tree.addEventListener('click', (ev) => {
|
||||
const t = ev.target;
|
||||
|
||||
// Toggle per Caret
|
||||
if (t.classList.contains('toggle')) {
|
||||
const li = t.closest('li.folder');
|
||||
if (!li) return;
|
||||
const ul = li.querySelector(':scope > ul');
|
||||
if (!ul) return;
|
||||
const visible = ul.style.display === 'block';
|
||||
ul.style.display = visible ? 'none' : 'block';
|
||||
t.textContent = visible ? '▶' : '▼';
|
||||
const li = t.closest('li.folder'); if (!li) return;
|
||||
const ul = li.querySelector(':scope > ul'); if (!ul) return;
|
||||
const open = ul.style.display==='block';
|
||||
ul.style.display = open ? 'none' : 'block';
|
||||
t.textContent = open ? '▶' : '▼';
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle per Ordner-Label
|
||||
if (t.classList.contains('folder-label')) {
|
||||
const li = t.closest('li.folder');
|
||||
if (!li) return;
|
||||
const caret = li.querySelector(':scope > .toggle');
|
||||
if (caret) caret.click();
|
||||
return;
|
||||
t.closest('li.folder')?.querySelector(':scope > .toggle')?.click();
|
||||
}
|
||||
});
|
||||
|
||||
// --- (2) Alle ausklappen (mit Scroll-to-top) ---
|
||||
document.getElementById('expand-all')?.addEventListener('click', () => {
|
||||
document.querySelectorAll('.pdf-tree ul').forEach(ul => ul.style.display = 'block');
|
||||
document.querySelectorAll('.pdf-tree .toggle').forEach(t => t.textContent = '▼');
|
||||
treeContainer.scrollTo({ top: 0, behavior: 'instant' });
|
||||
document.querySelectorAll('.pdf-tree ul').forEach(ul => ul.style.display='block');
|
||||
document.querySelectorAll('.pdf-tree .toggle').forEach(t => t.textContent='▼');
|
||||
tree.scrollTo({ top: 0, behavior: 'instant' });
|
||||
});
|
||||
|
||||
// --- (3) Alle einklappen ---
|
||||
document.getElementById('collapse-all')?.addEventListener('click', () => {
|
||||
document.querySelectorAll('.pdf-tree ul').forEach(ul => ul.style.display = 'none');
|
||||
document.querySelectorAll('.pdf-tree .toggle').forEach(t => t.textContent = '▶');
|
||||
document.querySelectorAll('.pdf-tree ul').forEach(ul => ul.style.display='none');
|
||||
document.querySelectorAll('.pdf-tree .toggle').forEach(t => t.textContent='▶');
|
||||
});
|
||||
|
||||
// --- (4) PDF-Vorschau + Fallback-Link (iOS: iframe kann zickig sein) ---
|
||||
treeContainer.addEventListener('click', (ev) => {
|
||||
// PDF.js-Viewer einbetten
|
||||
const viewerBase = '<?= addslashes($pdfjsViewer) ?>';
|
||||
|
||||
tree.addEventListener('click', (ev) => {
|
||||
const a = ev.target.closest('.file-link');
|
||||
if (!a) return;
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
const fileUrl = a.href; // dein Download-Controller (gleiches Origin)
|
||||
const viewerUrl = viewerBase + '?file=' + encodeURIComponent(fileUrl) + '#zoom=page-fit';
|
||||
|
||||
preview.style.display = 'block';
|
||||
previewBar.style.display = 'flex';
|
||||
previewName.textContent = a.getAttribute('data-filename') || a.textContent.trim();
|
||||
openNewTab.href = a.href;
|
||||
openNewTab.href = viewerUrl; // optional externer Viewer-Tab
|
||||
|
||||
// iFrame neu setzen
|
||||
preview.querySelector('iframe')?.remove();
|
||||
// alten Viewer entfernen & neuen iframe setzen
|
||||
viewport.innerHTML = '';
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('title', 'PDF-Vorschau');
|
||||
iframe.src = a.href;
|
||||
iframe.setAttribute('title', 'PDF-Vorschau (PDF.js)');
|
||||
iframe.src = viewerUrl;
|
||||
iframe.loading = 'lazy';
|
||||
|
||||
// iOS Fallback: Wenn Onload nicht in 1.5s triggert, Link oben verwenden
|
||||
let loaded = false;
|
||||
const t = setTimeout(() => { /* optional Hinweis einblenden */ }, 1500);
|
||||
iframe.addEventListener('load', () => { loaded = true; clearTimeout(t); }, { once: true });
|
||||
|
||||
preview.appendChild(iframe);
|
||||
}, { passive: false });
|
||||
viewport.appendChild(iframe);
|
||||
}, {passive:false});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user