Onepager: when no images.
[wolnelektury.git] / src / references / templates / references / onepager.html
1 <!DOCTYPE html>
2
3 {% load l10n %}
4
5 <html lang="pl">
6 <head>
7 <meta charset="UTF-8">
8 <title>Karta obiektu — {{ entity.label }}</title>
9 <meta name="viewport" content="width=device-width, initial-scale=1">
10 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" />
11 <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
12 <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
13
14 <link rel="preconnect" href="https://fonts.googleapis.com">
15 <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400&family=Courier+Prime:wght@400;700&display=swap" rel="stylesheet">
16
17 <style>
18   *{ box-sizing:border-box; }
19
20   html,body{
21     margin:0; padding:0;
22     background:#fff;
23     font-family:'Newsreader', serif;
24     color:#000;
25   }
26
27   /* ---------- PAGE / PRINT ---------- */
28   @page{ size:A4; margin:0; }
29
30   .page{
31     width:210mm;
32     margin:0 auto;
33     background:#fff;
34     padding:16mm 16mm 12mm 16mm;
35     position:relative;
36   }
37
38   /* ---------- HEADER ---------- */
39   .header{
40     display:flex;
41     justify-content:space-between;
42     align-items:flex-start;
43     border-bottom:1.5px solid #000;
44     padding-bottom:5mm;
45     margin-bottom:6mm;
46     gap:8mm;
47   }
48
49   .title-block .kicker{
50     font-family:'Courier Prime', monospace;
51     font-size:10px;
52     letter-spacing:.18em;
53     text-transform:uppercase;
54     color:#000;
55     margin-bottom:2mm;
56   }
57   .title-block h1{
58     margin:0;
59     font-size:40px;
60     line-height:1;
61     font-weight:600;
62   }
63
64   /* pieczęć z kodem QR do Wikipedii — obrazek dostarczony przez serwer */
65   .stamp{
66     flex:0 0 auto;
67     width:28mm;
68     text-align:center;
69   }
70   .stamp .seal-ring{
71     width:26mm; height:26mm;
72     border-radius:50%;
73     border:1.4px solid #000;
74     outline:1px dashed #000;
75     outline-offset:2px;
76     display:flex; align-items:center; justify-content:center;
77     padding:2mm;
78   }
79   .stamp .seal-ring img{
80     width:100%; height:100%;
81     display:block;
82   }
83   .qr img{
84     position:relative;
85     z-index:1;
86   }
87   .stamp .cap{
88     margin-top:1.5mm;
89     font-family:'Courier Prime', monospace;
90     font-size:8.5px;
91     letter-spacing:.1em;
92     text-transform:uppercase;
93     color:#000;
94   }
95
96   /* ---------- MAPA + ZDJĘCIA ---------- */
97   .visuals{
98     display:grid;
99     grid-template-columns:1.1fr 0.9fr;
100     gap:7mm;
101     margin-bottom:6mm;
102   }
103   /* gdy nie ma mapy: zdjęcia zajmują całą szerokość */
104   .page.no-map .visuals{
105     grid-template-columns:1fr;
106   }
107   .page.no-map .illustrations{
108     column-count:4;
109   }
110
111   .map-frame .label,
112   .illustrations .label{
113     font-family:'Courier Prime', monospace;
114     font-size:9px;
115     letter-spacing:.12em;
116     text-transform:uppercase;
117     color:#000;
118     margin-bottom:4mm;
119     column-span:all;
120   }
121   #map{
122     width:100%;
123     height:62mm;
124     border:1px solid #000;
125   }
126   .coords{
127     margin-top:1.5mm;
128     font-family:'Courier Prime', monospace;
129     font-size:9px;
130     color:#000;
131   }
132
133   /* zdjęcia "przyklejone", dowolne proporcje — CSS columns robi
134      naturalny układ pod różne wysokości (portrait/landscape mix) */
135   .illustrations{
136     column-count:2;
137     column-gap:4mm;
138   }
139   .illu{
140     break-inside:avoid;
141     margin-bottom:5mm;
142     position:relative;
143     background:#fff;
144     padding:2mm 2mm 5mm 2mm;
145     box-shadow:0 2px 6px rgba(0,0,0,.22);
146     border:1px solid #000;
147   }
148   .illu:nth-child(odd){ transform:rotate(-2deg); }
149   .illu:nth-child(even){ transform:rotate(1.6deg); }
150   .illu .tape{
151     position:absolute; top:-2mm; left:50%; transform:translateX(-50%) rotate(-2deg);
152     width:14mm; height:5mm;
153     background:rgba(0,0,0,.35);
154     border:1px solid rgba(0,0,0,.5);
155   }
156   .illu img{
157     width:100%; height:auto; display:block; /* naturalne proporcje, bez cropu */
158   }
159   .illu .cap{
160     margin-top:1.5mm;
161     font-family:'Courier Prime', monospace;
162     font-size:7px;
163     color:#000;
164     text-align:center;
165   }
166
167   /* ---------- PRZERWA ---------- */
168   .tear{
169     display:flex; align-items:center; gap:2mm;
170     margin:0 0 6mm 0;
171   }
172   .tear .dots{ flex:1; border-top:2px dashed #000; }
173   .tear .txt{
174     font-family:'Courier Prime', monospace;
175     font-size:8.5px;
176     letter-spacing:.2em;
177     text-transform:uppercase;
178     color:#000;
179   }
180
181   /* ---------- FRAGMENTY (bilety) ---------- */
182   .fragment{
183     display:grid;
184     grid-template-columns:16mm 1fr 22mm;
185     gap:4mm;
186     align-items:center;
187     padding:3mm 0;
188     border-bottom:1px dotted #000;
189     break-inside:avoid;
190   }
191   .fragment:last-child{ border-bottom:none; }
192
193   .cover{
194     width:16mm; height:22mm;
195     border:1px solid #000;
196     box-shadow:0 2px 5px rgba(0,0,0,.28);
197     overflow:hidden;
198   }
199   .cover img{ width:100%; height:100%; object-fit:cover; display:block; }
200
201   .frag-text .book{
202     font-family:'Courier Prime', monospace;
203     font-size:9.5px;
204     color:#000;
205     margin-bottom:.8mm;
206   }
207   .frag-text .book b{ color:#000; font-weight:700; }
208   .frag-text .quote{
209     font-style:italic;
210     font-size:13px;
211     line-height:1.35;
212   }
213   .frag-text .where{
214     margin-top:1mm;
215     font-family:'Courier Prime', monospace;
216     font-size:8px;
217     color:#000;
218   }
219
220   .qr-mini{ width:22mm; text-align:center; }
221   .qr-mini .box{
222     width:20mm; height:20mm;
223     margin:0 auto;
224   }
225   .qr-mini .box img{ width:100%; height:100%; display:block; }
226   .qr-mini .cap{
227     margin-top:1mm;
228     font-family:'Courier Prime', monospace;
229     font-size:6.5px;
230     letter-spacing:.06em;
231     text-transform:uppercase;
232     color:#000;
233   }
234
235 </style>
236 </head>
237 <body>
238
239 <div class="page {% if not entity.lat %}no-map{% endif %}" id="page">
240   <div class="header">
241     <div class="title-block">
242       <div class="kicker">Mapa Wolnych Lektur</div>
243       <h1>{{ entity.label }}</h1>
244     </div>
245     {% if entity.wikipedia_link %}
246     <div class="stamp">
247       <div class="seal-ring qr" data-qr="{{ entity.wikipedia_link }}">
248       </div>
249       <div class="cap">Wikipedia →</div>
250     </div>
251     {% endif %}
252   </div>
253
254   <div class="visuals">
255
256     {% if entity.lon %}
257     <div class="map-frame">
258       <div class="label">Lokalizacja</div>
259       {% localize off %}
260       <div id="map" data-lat="{{ entity.lat }}" data-lon="{{ entity.lon }}"></div>
261       {% endlocalize %}
262       <div class="coords">{{ coords_label }}</div>
263     </div>
264     {% endif %}
265
266     {% if entity.get_images %}
267     <div class="illustrations">
268       <div class="label">Ilustracje</div>
269       {% for illu in entity.get_images|slice:":4" %}
270       <div class="illu">
271         <div class="tape"></div>
272         <img src="{{ illu.url }}">
273         <div class="cap">{{ illu.caption }}</div>
274       </div>
275       {% endfor %}
276     </div>
277     {% endif %}
278   </div>
279
280   <div class="tear"><div class="dots"></div><div class="txt">fragmenty tekstu</div><div class="dots"></div></div>
281
282   <div class="fragments">
283     {% for ref in entity.reference_set.all|slice:":3" %}
284     <div class="fragment">
285       <div class="cover">
286         <img src="{{ ref.book.cover_clean.url }}" alt="Okładka: {{ ref.book.pretty_title }}">
287       </div>
288       <div class="frag-text">
289         <div class="book"><b>
290             {% for author in ref.book.authors %}
291             {{ author.name }}{% if not forloop.last %}, {% endif %}
292             {% endfor %}
293           </b> — {{ ref.book.title }}</div>
294         {% for occ in ref.occurence_set.all|slice:":3" %}
295         <div class="quote">{{ occ.html|safe }}</div>
296         {% endfor %}
297       </div>
298       {% with occ=ref.occurence_set.all.first %}
299       <div class="qr-mini">
300         <div class="box qr" data-qr="https://wolnelektury.pl/katalog/lektura/{{ ref.book.slug }}.html#sec{{ occ.section }}">
301         </div>
302         <div class="cap">zobacz w treści</div>
303       </div>
304       {% endwith %}
305     </div>
306     {% endfor %}
307   </div>
308 </div>
309
310 <script>
311   // Jedyny JS w tym pliku: inicjalizacja mapy, jeśli jest obecna.
312   const mapEl = document.getElementById('map');
313   if (mapEl) {
314     const lat = parseFloat(mapEl.dataset.lat);
315     const lon = parseFloat(mapEl.dataset.lon);
316     const map = L.map(mapEl, {
317       zoomControl:false, attributionControl:false,
318       dragging:false, scrollWheelZoom:false
319     }).setView([lat, lon], 6);
320     L.tileLayer('https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey=a8a97f0ae5134403ac38c1a075b03e15').addTo(map);
321     L.marker([lat, lon]).addTo(map);
322     setTimeout(() => map.invalidateSize(), 200);
323   }
324
325
326   for (let e of document.getElementsByClassName('qr')) {
327       new QRCode(e, {
328           text: e.dataset.qr,
329           width: 160, height: 160,
330           colorDark: "#000000", colorLight: "#ffffff",
331           correctLevel: QRCode.CorrectLevel.M
332       });
333   }
334
335
336 </script>
337
338 </body>
339 </html>