@internetarchive/collection-browser 4.9.0 → 4.9.1-webdev-9670.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/src/app-root.d.ts +11 -0
- package/dist/src/app-root.js +95 -1
- package/dist/src/app-root.js.map +1 -1
- package/dist/src/collection-browser.d.ts +42 -0
- package/dist/src/collection-browser.js +186 -2
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +12 -0
- package/dist/src/sort-filter-bar/sort-filter-bar.js +64 -0
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
- package/dist/src/tiles/grid/collection-tile.js +5 -0
- package/dist/src/tiles/grid/collection-tile.js.map +1 -1
- package/dist/src/tiles/grid/item-tile.d.ts +4 -0
- package/dist/src/tiles/grid/item-tile.js +16 -0
- package/dist/src/tiles/grid/item-tile.js.map +1 -1
- package/dist/src/tiles/hover/hover-pane-controller.d.ts +2 -1
- package/dist/src/tiles/hover/hover-pane-controller.js +1 -1
- package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -1
- package/dist/src/tiles/image-block.d.ts +5 -0
- package/dist/src/tiles/image-block.js +29 -0
- package/dist/src/tiles/image-block.js.map +1 -1
- package/dist/src/tiles/models.d.ts +2 -1
- package/dist/src/tiles/models.js.map +1 -1
- package/dist/src/tiles/preview/tile-preview-controller.d.ts +61 -0
- package/dist/src/tiles/preview/tile-preview-controller.js +216 -0
- package/dist/src/tiles/preview/tile-preview-controller.js.map +1 -0
- package/dist/src/tiles/preview/tile-preview-media.d.ts +44 -0
- package/dist/src/tiles/preview/tile-preview-media.js +151 -0
- package/dist/src/tiles/preview/tile-preview-media.js.map +1 -0
- package/dist/src/tiles/preview/tile-preview.d.ts +66 -0
- package/dist/src/tiles/preview/tile-preview.js +494 -0
- package/dist/src/tiles/preview/tile-preview.js.map +1 -0
- package/dist/src/tiles/tile-dispatcher.d.ts +7 -0
- package/dist/src/tiles/tile-dispatcher.js +31 -0
- package/dist/src/tiles/tile-dispatcher.js.map +1 -1
- package/dist/src/utils/grid-zoom-controller.d.ts +69 -0
- package/dist/src/utils/grid-zoom-controller.js +261 -0
- package/dist/src/utils/grid-zoom-controller.js.map +1 -0
- package/dist/src/utils/grid-zoom.d.ts +31 -0
- package/dist/src/utils/grid-zoom.js +109 -0
- package/dist/src/utils/grid-zoom.js.map +1 -0
- package/dist/src/utils/log.d.ts +1 -4
- package/package.json +5 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { css, html, LitElement, nothing } from 'lit';
|
|
3
|
+
import { msg } from '@lit/localize';
|
|
4
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
|
+
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
6
|
+
import { PREVIEW_CLIP_SECONDS, resolvePreviewMedia, } from './tile-preview-media';
|
|
7
|
+
const SLIDE_INTERVAL_MS = 700;
|
|
8
|
+
/** How long the first video frame may take before the frames take over. */
|
|
9
|
+
const FIRST_FRAME_TIMEOUT_MS = 2500;
|
|
10
|
+
const MAX_VOLUME = 0.5;
|
|
11
|
+
const FADE_IN_MS = 250;
|
|
12
|
+
const FADE_OUT_MS = 120;
|
|
13
|
+
/**
|
|
14
|
+
* Fills a tile's image area with a live preview of the item while `active`:
|
|
15
|
+
* a muted clip for movies, a few page images for texts, a waveform for audio,
|
|
16
|
+
* cycling images for images, and a slow pan over the thumbnail for anything
|
|
17
|
+
* else. It is absolutely positioned over the existing thumbnail, so showing
|
|
18
|
+
* and hiding it never moves the layout.
|
|
19
|
+
*/
|
|
20
|
+
let TilePreview = class TilePreview extends LitElement {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.baseUrl = 'https://archive.org';
|
|
24
|
+
this.active = false;
|
|
25
|
+
this.options = {};
|
|
26
|
+
this.slideUrls = [];
|
|
27
|
+
this.slide = 0;
|
|
28
|
+
this.ready = false;
|
|
29
|
+
/** Whether sound is audibly playing, as opposed to muted or refused. */
|
|
30
|
+
this.audible = false;
|
|
31
|
+
/** Pending fade animation frame per element, so one element's fade never cancels another's. */
|
|
32
|
+
this.fadeFrames = new WeakMap();
|
|
33
|
+
/** Bumped on every activation so a slow resolve can't show a stale preview. */
|
|
34
|
+
this.activation = 0;
|
|
35
|
+
}
|
|
36
|
+
willUpdate(changed) {
|
|
37
|
+
// Release while the media elements are still in the DOM.
|
|
38
|
+
if (changed.has('active') && !this.active)
|
|
39
|
+
this.stop();
|
|
40
|
+
}
|
|
41
|
+
updated(changed) {
|
|
42
|
+
if (changed.has('active') || changed.has('identifier')) {
|
|
43
|
+
if (this.active) {
|
|
44
|
+
this.stop();
|
|
45
|
+
this.start();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
disconnectedCallback() {
|
|
50
|
+
super.disconnectedCallback();
|
|
51
|
+
this.stop();
|
|
52
|
+
}
|
|
53
|
+
async start() {
|
|
54
|
+
if (!this.identifier || !this.mediatype)
|
|
55
|
+
return;
|
|
56
|
+
const activation = ++this.activation;
|
|
57
|
+
const media = await resolvePreviewMedia(this.identifier, this.mediatype, this.baseUrl, this.options);
|
|
58
|
+
if (activation !== this.activation || !this.active)
|
|
59
|
+
return;
|
|
60
|
+
this.media = media;
|
|
61
|
+
this.slideUrls = media.kind === 'slides' ? media.urls : [];
|
|
62
|
+
this.slide = 0;
|
|
63
|
+
this.ready = media.kind === 'zoom' || media.kind === 'audio';
|
|
64
|
+
await this.updateComplete;
|
|
65
|
+
if (activation !== this.activation)
|
|
66
|
+
return;
|
|
67
|
+
if (media.kind === 'slides')
|
|
68
|
+
this.startSlides();
|
|
69
|
+
if (media.kind === 'audio' && this.options.sound)
|
|
70
|
+
this.playSnippet();
|
|
71
|
+
if (media.kind === 'video') {
|
|
72
|
+
this.frameTimer = window.setTimeout(() => this.useFallbackFrames(), FIRST_FRAME_TIMEOUT_MS);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
stop() {
|
|
76
|
+
this.activation += 1;
|
|
77
|
+
window.clearInterval(this.slideTimer);
|
|
78
|
+
window.clearTimeout(this.frameTimer);
|
|
79
|
+
this.releaseMedia(this.video);
|
|
80
|
+
this.releaseMedia(this.audio);
|
|
81
|
+
this.media = undefined;
|
|
82
|
+
this.slideUrls = [];
|
|
83
|
+
this.ready = false;
|
|
84
|
+
this.audible = false;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Stops a media element: a short fade-out when it is audible, then pause and
|
|
88
|
+
* drop the source so the browser stops downloading it.
|
|
89
|
+
*/
|
|
90
|
+
releaseMedia(el) {
|
|
91
|
+
if (!el)
|
|
92
|
+
return;
|
|
93
|
+
this.cancelFade(el);
|
|
94
|
+
const finish = () => {
|
|
95
|
+
el.pause();
|
|
96
|
+
el.removeAttribute('src');
|
|
97
|
+
el.load();
|
|
98
|
+
};
|
|
99
|
+
if (el.paused || el.muted || el.volume === 0) {
|
|
100
|
+
finish();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.fade(el, 0, FADE_OUT_MS, finish);
|
|
104
|
+
}
|
|
105
|
+
cancelFade(el) {
|
|
106
|
+
const frame = this.fadeFrames.get(el);
|
|
107
|
+
if (frame !== undefined)
|
|
108
|
+
window.cancelAnimationFrame(frame);
|
|
109
|
+
this.fadeFrames.delete(el);
|
|
110
|
+
}
|
|
111
|
+
/** Ramps a media element's volume to `target` over `ms`. */
|
|
112
|
+
fade(el, target, ms, done) {
|
|
113
|
+
this.cancelFade(el);
|
|
114
|
+
const from = el.volume;
|
|
115
|
+
const t0 = performance.now();
|
|
116
|
+
const step = (now) => {
|
|
117
|
+
const t = Math.min(1, (now - t0) / ms);
|
|
118
|
+
el.volume = from + (target - from) * t;
|
|
119
|
+
if (t < 1)
|
|
120
|
+
this.fadeFrames.set(el, window.requestAnimationFrame(step));
|
|
121
|
+
else {
|
|
122
|
+
this.fadeFrames.delete(el);
|
|
123
|
+
done?.();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
this.fadeFrames.set(el, window.requestAnimationFrame(step));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Starts a media element, with sound when the options ask for it. A browser
|
|
130
|
+
* that refuses audible playback gets a muted one instead.
|
|
131
|
+
*/
|
|
132
|
+
async playMedia(el, activation) {
|
|
133
|
+
if (this.options.sound) {
|
|
134
|
+
try {
|
|
135
|
+
el.muted = false;
|
|
136
|
+
el.volume = 0;
|
|
137
|
+
await el.play();
|
|
138
|
+
if (activation !== this.activation)
|
|
139
|
+
return true;
|
|
140
|
+
this.audible = true;
|
|
141
|
+
this.fade(el, MAX_VOLUME, FADE_IN_MS);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Refused, so play it muted below.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
el.muted = true;
|
|
150
|
+
await el.play();
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// A stopped preview isn't a failure; only a current one needs the frames.
|
|
155
|
+
return activation !== this.activation;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Swaps a clip that is slow or unplayable for the item's frame thumbnails. */
|
|
159
|
+
useFallbackFrames() {
|
|
160
|
+
window.clearTimeout(this.frameTimer);
|
|
161
|
+
if (this.media?.kind !== 'video')
|
|
162
|
+
return;
|
|
163
|
+
const urls = this.media.fallbackUrls;
|
|
164
|
+
this.releaseMedia(this.video);
|
|
165
|
+
this.audible = false;
|
|
166
|
+
if (urls.length > 1) {
|
|
167
|
+
this.media = { kind: 'slides', fit: 'cover', urls };
|
|
168
|
+
this.slideUrls = urls;
|
|
169
|
+
this.slide = 0;
|
|
170
|
+
this.ready = false;
|
|
171
|
+
const { activation } = this;
|
|
172
|
+
this.updateComplete.then(() => {
|
|
173
|
+
if (activation === this.activation)
|
|
174
|
+
this.startSlides();
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.media = { kind: 'zoom' };
|
|
179
|
+
this.ready = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
startSlides() {
|
|
183
|
+
window.clearInterval(this.slideTimer);
|
|
184
|
+
this.slideTimer = window.setInterval(() => {
|
|
185
|
+
const loaded = this.loadedSlides();
|
|
186
|
+
if (loaded.length < 2)
|
|
187
|
+
return;
|
|
188
|
+
let next = this.slide;
|
|
189
|
+
do {
|
|
190
|
+
next = (next + 1) % this.slideUrls.length;
|
|
191
|
+
} while (!loaded.includes(next));
|
|
192
|
+
this.slide = next;
|
|
193
|
+
}, SLIDE_INTERVAL_MS);
|
|
194
|
+
}
|
|
195
|
+
loadedSlides() {
|
|
196
|
+
const imgs = [...(this.shadowRoot?.querySelectorAll('.slides img') ?? [])];
|
|
197
|
+
return imgs.flatMap((img, i) => img.complete &&
|
|
198
|
+
img.naturalWidth
|
|
199
|
+
? [i]
|
|
200
|
+
: []);
|
|
201
|
+
}
|
|
202
|
+
onSlideError(url) {
|
|
203
|
+
this.slideUrls = this.slideUrls.filter(u => u !== url);
|
|
204
|
+
this.slide = 0;
|
|
205
|
+
if (this.slideUrls.length < 2) {
|
|
206
|
+
window.clearInterval(this.slideTimer);
|
|
207
|
+
this.media = { kind: 'zoom' };
|
|
208
|
+
this.ready = true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
onSlideLoad() {
|
|
212
|
+
this.ready = true;
|
|
213
|
+
}
|
|
214
|
+
onVideoPlaying() {
|
|
215
|
+
window.clearTimeout(this.frameTimer);
|
|
216
|
+
this.ready = true;
|
|
217
|
+
}
|
|
218
|
+
/** Loops the clip within its start-to-start+clip-length window. */
|
|
219
|
+
onVideoTimeUpdate() {
|
|
220
|
+
const video = this.video;
|
|
221
|
+
if (this.media?.kind === 'video' &&
|
|
222
|
+
video &&
|
|
223
|
+
video.currentTime >= this.media.start + PREVIEW_CLIP_SECONDS) {
|
|
224
|
+
video.currentTime = this.media.start;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async onVideoLoaded() {
|
|
228
|
+
const { video, activation } = this;
|
|
229
|
+
if (video && !(await this.playMedia(video, activation))) {
|
|
230
|
+
this.useFallbackFrames();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async playSnippet() {
|
|
234
|
+
if (this.audio)
|
|
235
|
+
await this.playMedia(this.audio, this.activation);
|
|
236
|
+
}
|
|
237
|
+
render() {
|
|
238
|
+
if (!this.active || !this.media)
|
|
239
|
+
return nothing;
|
|
240
|
+
return html `<div
|
|
241
|
+
class="layer ${this.media.kind} ${this.ready ? 'ready' : ''}"
|
|
242
|
+
aria-hidden="true"
|
|
243
|
+
>
|
|
244
|
+
${this.mediaTemplate(this.media)} ${this.soundBadgeTemplate}
|
|
245
|
+
</div>`;
|
|
246
|
+
}
|
|
247
|
+
/** A muted-speaker hint, so people know a clip or snippet could have sound. */
|
|
248
|
+
get soundBadgeTemplate() {
|
|
249
|
+
const kind = this.media?.kind;
|
|
250
|
+
if ((kind !== 'video' && kind !== 'audio') || this.audible)
|
|
251
|
+
return nothing;
|
|
252
|
+
return html `<span class="badge" title=${msg('Sound is off')}>
|
|
253
|
+
<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
|
|
254
|
+
<path
|
|
255
|
+
fill="currentColor"
|
|
256
|
+
d="M3 9v6h4l5 5V4L7 9H3zm13.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4-2.7-2.7z"
|
|
257
|
+
/>
|
|
258
|
+
</svg>
|
|
259
|
+
</span>`;
|
|
260
|
+
}
|
|
261
|
+
mediaTemplate(media) {
|
|
262
|
+
switch (media.kind) {
|
|
263
|
+
case 'video':
|
|
264
|
+
return html `<video
|
|
265
|
+
muted
|
|
266
|
+
loop
|
|
267
|
+
playsinline
|
|
268
|
+
preload="auto"
|
|
269
|
+
src="${media.src}#t=${media.start}"
|
|
270
|
+
@loadeddata=${this.onVideoLoaded}
|
|
271
|
+
@error=${this.useFallbackFrames}
|
|
272
|
+
@playing=${this.onVideoPlaying}
|
|
273
|
+
@timeupdate=${this.onVideoTimeUpdate}
|
|
274
|
+
></video>`;
|
|
275
|
+
case 'slides':
|
|
276
|
+
return html `<div class="slides ${media.fit}">
|
|
277
|
+
${this.slideUrls.map((url, i) => html `<img
|
|
278
|
+
src=${url}
|
|
279
|
+
alt=""
|
|
280
|
+
class=${i === this.slide ? 'on' : ''}
|
|
281
|
+
@load=${this.onSlideLoad}
|
|
282
|
+
@error=${() => this.onSlideError(url)}
|
|
283
|
+
/>`)}
|
|
284
|
+
</div>`;
|
|
285
|
+
case 'audio':
|
|
286
|
+
return html `
|
|
287
|
+
${media.waveform
|
|
288
|
+
? html `<img class="waveform" src=${media.waveform} alt="" />`
|
|
289
|
+
: html `<div class="bars">
|
|
290
|
+
${Array.from({ length: 12 }, (_, i) => html `<span style="animation-delay:${i * -97}ms"></span>`)}
|
|
291
|
+
</div>`}
|
|
292
|
+
<div class="playhead"></div>
|
|
293
|
+
${this.options.sound
|
|
294
|
+
? html `<audio
|
|
295
|
+
preload="auto"
|
|
296
|
+
src="${media.src}#t=${media.start}"
|
|
297
|
+
></audio>`
|
|
298
|
+
: nothing}
|
|
299
|
+
`;
|
|
300
|
+
default:
|
|
301
|
+
return html `<img
|
|
302
|
+
class="pan"
|
|
303
|
+
src=${ifDefined(this.thumbnailUrl)}
|
|
304
|
+
alt=""
|
|
305
|
+
/>`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
static { this.styles = css `
|
|
309
|
+
:host {
|
|
310
|
+
position: absolute;
|
|
311
|
+
inset: 0;
|
|
312
|
+
pointer-events: none;
|
|
313
|
+
overflow: hidden;
|
|
314
|
+
border-radius: inherit;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.layer {
|
|
318
|
+
position: absolute;
|
|
319
|
+
inset: 0;
|
|
320
|
+
opacity: 0;
|
|
321
|
+
transition: opacity 200ms ease-out;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.layer.ready {
|
|
325
|
+
opacity: 1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
video,
|
|
329
|
+
.slides img {
|
|
330
|
+
position: absolute;
|
|
331
|
+
inset: 0;
|
|
332
|
+
width: 100%;
|
|
333
|
+
height: 100%;
|
|
334
|
+
object-fit: cover;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.slides {
|
|
338
|
+
position: absolute;
|
|
339
|
+
inset: 0;
|
|
340
|
+
background: #fff;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.slides.contain img {
|
|
344
|
+
object-fit: contain;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.slides img {
|
|
348
|
+
opacity: 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.slides img.on {
|
|
352
|
+
opacity: 1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.audio {
|
|
356
|
+
background: #10161b;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.waveform {
|
|
360
|
+
position: absolute;
|
|
361
|
+
inset: 0;
|
|
362
|
+
width: 100%;
|
|
363
|
+
height: 100%;
|
|
364
|
+
object-fit: fill;
|
|
365
|
+
filter: invert(1) hue-rotate(180deg);
|
|
366
|
+
opacity: 0.85;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.badge {
|
|
370
|
+
position: absolute;
|
|
371
|
+
left: 6px;
|
|
372
|
+
bottom: 6px;
|
|
373
|
+
display: flex;
|
|
374
|
+
padding: 3px;
|
|
375
|
+
border-radius: 4px;
|
|
376
|
+
color: #fff;
|
|
377
|
+
background: rgb(0 0 0 / 0.65);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.playhead {
|
|
381
|
+
position: absolute;
|
|
382
|
+
top: 0;
|
|
383
|
+
bottom: 0;
|
|
384
|
+
width: 2px;
|
|
385
|
+
background: #f0a500;
|
|
386
|
+
box-shadow: 0 0 8px #f0a500;
|
|
387
|
+
animation: scan 2.4s linear infinite;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.bars {
|
|
391
|
+
position: absolute;
|
|
392
|
+
inset: 20% 10%;
|
|
393
|
+
display: flex;
|
|
394
|
+
gap: 4px;
|
|
395
|
+
align-items: end;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.bars span {
|
|
399
|
+
flex: 1;
|
|
400
|
+
height: 30%;
|
|
401
|
+
background: #5bb8e0;
|
|
402
|
+
animation: bounce 700ms ease-in-out infinite alternate;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.pan {
|
|
406
|
+
position: absolute;
|
|
407
|
+
inset: 0;
|
|
408
|
+
width: 100%;
|
|
409
|
+
height: 100%;
|
|
410
|
+
object-fit: contain;
|
|
411
|
+
animation: pan 6s ease-in-out infinite alternate;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@keyframes pan {
|
|
415
|
+
from {
|
|
416
|
+
transform: scale(1) translate(0, 0);
|
|
417
|
+
}
|
|
418
|
+
to {
|
|
419
|
+
transform: scale(1.3) translate(-5%, -4%);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
@keyframes scan {
|
|
424
|
+
from {
|
|
425
|
+
left: 0;
|
|
426
|
+
}
|
|
427
|
+
to {
|
|
428
|
+
left: 100%;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@keyframes bounce {
|
|
433
|
+
to {
|
|
434
|
+
height: 95%;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@media (prefers-reduced-motion: reduce) {
|
|
439
|
+
.layer {
|
|
440
|
+
transition: none;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.playhead,
|
|
444
|
+
.pan,
|
|
445
|
+
.bars span {
|
|
446
|
+
animation: none;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
`; }
|
|
450
|
+
};
|
|
451
|
+
__decorate([
|
|
452
|
+
property({ type: String })
|
|
453
|
+
], TilePreview.prototype, "identifier", void 0);
|
|
454
|
+
__decorate([
|
|
455
|
+
property({ type: String })
|
|
456
|
+
], TilePreview.prototype, "mediatype", void 0);
|
|
457
|
+
__decorate([
|
|
458
|
+
property({ type: String })
|
|
459
|
+
], TilePreview.prototype, "baseUrl", void 0);
|
|
460
|
+
__decorate([
|
|
461
|
+
property({ type: String })
|
|
462
|
+
], TilePreview.prototype, "thumbnailUrl", void 0);
|
|
463
|
+
__decorate([
|
|
464
|
+
property({ type: Boolean, reflect: true })
|
|
465
|
+
], TilePreview.prototype, "active", void 0);
|
|
466
|
+
__decorate([
|
|
467
|
+
property({ type: Object })
|
|
468
|
+
], TilePreview.prototype, "options", void 0);
|
|
469
|
+
__decorate([
|
|
470
|
+
state()
|
|
471
|
+
], TilePreview.prototype, "media", void 0);
|
|
472
|
+
__decorate([
|
|
473
|
+
state()
|
|
474
|
+
], TilePreview.prototype, "slideUrls", void 0);
|
|
475
|
+
__decorate([
|
|
476
|
+
state()
|
|
477
|
+
], TilePreview.prototype, "slide", void 0);
|
|
478
|
+
__decorate([
|
|
479
|
+
state()
|
|
480
|
+
], TilePreview.prototype, "ready", void 0);
|
|
481
|
+
__decorate([
|
|
482
|
+
state()
|
|
483
|
+
], TilePreview.prototype, "audible", void 0);
|
|
484
|
+
__decorate([
|
|
485
|
+
query('video')
|
|
486
|
+
], TilePreview.prototype, "video", void 0);
|
|
487
|
+
__decorate([
|
|
488
|
+
query('audio')
|
|
489
|
+
], TilePreview.prototype, "audio", void 0);
|
|
490
|
+
TilePreview = __decorate([
|
|
491
|
+
customElement('tile-preview')
|
|
492
|
+
], TilePreview);
|
|
493
|
+
export { TilePreview };
|
|
494
|
+
//# sourceMappingURL=tile-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tile-preview.js","sourceRoot":"","sources":["../../../../src/tiles/preview/tile-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, PropertyValues } from 'lit';\nimport { msg } from '@lit/localize';\nimport { ifDefined } from 'lit/directives/if-defined.js';\nimport { customElement, property, query, state } from 'lit/decorators.js';\nimport {\n PREVIEW_CLIP_SECONDS,\n resolvePreviewMedia,\n type TilePreviewMedia,\n type TilePreviewOptions,\n} from './tile-preview-media';\n\nconst SLIDE_INTERVAL_MS = 700;\n/** How long the first video frame may take before the frames take over. */\nconst FIRST_FRAME_TIMEOUT_MS = 2500;\nconst MAX_VOLUME = 0.5;\nconst FADE_IN_MS = 250;\nconst FADE_OUT_MS = 120;\n\n/**\n * Fills a tile's image area with a live preview of the item while `active`:\n * a muted clip for movies, a few page images for texts, a waveform for audio,\n * cycling images for images, and a slow pan over the thumbnail for anything\n * else. It is absolutely positioned over the existing thumbnail, so showing\n * and hiding it never moves the layout.\n */\n@customElement('tile-preview')\nexport class TilePreview extends LitElement {\n @property({ type: String }) identifier?: string;\n\n @property({ type: String }) mediatype?: string;\n\n @property({ type: String }) baseUrl = 'https://archive.org';\n\n /** The tile's thumbnail, panned slowly when there is nothing richer to show. */\n @property({ type: String }) thumbnailUrl?: string;\n\n @property({ type: Boolean, reflect: true }) active = false;\n\n @property({ type: Object }) options: TilePreviewOptions = {};\n\n @state() private media?: TilePreviewMedia;\n\n @state() private slideUrls: string[] = [];\n\n @state() private slide = 0;\n\n @state() private ready = false;\n\n /** Whether sound is audibly playing, as opposed to muted or refused. */\n @state() private audible = false;\n\n @query('video') private video?: HTMLVideoElement;\n\n @query('audio') private audio?: HTMLAudioElement;\n\n private slideTimer?: number;\n\n private frameTimer?: number;\n\n /** Pending fade animation frame per element, so one element's fade never cancels another's. */\n private fadeFrames = new WeakMap<HTMLMediaElement, number>();\n\n /** Bumped on every activation so a slow resolve can't show a stale preview. */\n private activation = 0;\n\n protected willUpdate(changed: PropertyValues<this>): void {\n // Release while the media elements are still in the DOM.\n if (changed.has('active') && !this.active) this.stop();\n }\n\n protected updated(changed: PropertyValues<this>): void {\n if (changed.has('active') || changed.has('identifier')) {\n if (this.active) {\n this.stop();\n this.start();\n }\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.stop();\n }\n\n private async start(): Promise<void> {\n if (!this.identifier || !this.mediatype) return;\n const activation = ++this.activation;\n const media = await resolvePreviewMedia(\n this.identifier,\n this.mediatype,\n this.baseUrl,\n this.options,\n );\n if (activation !== this.activation || !this.active) return;\n\n this.media = media;\n this.slideUrls = media.kind === 'slides' ? media.urls : [];\n this.slide = 0;\n this.ready = media.kind === 'zoom' || media.kind === 'audio';\n await this.updateComplete;\n if (activation !== this.activation) return;\n\n if (media.kind === 'slides') this.startSlides();\n if (media.kind === 'audio' && this.options.sound) this.playSnippet();\n if (media.kind === 'video') {\n this.frameTimer = window.setTimeout(\n () => this.useFallbackFrames(),\n FIRST_FRAME_TIMEOUT_MS,\n );\n }\n }\n\n private stop(): void {\n this.activation += 1;\n window.clearInterval(this.slideTimer);\n window.clearTimeout(this.frameTimer);\n this.releaseMedia(this.video);\n this.releaseMedia(this.audio);\n this.media = undefined;\n this.slideUrls = [];\n this.ready = false;\n this.audible = false;\n }\n\n /**\n * Stops a media element: a short fade-out when it is audible, then pause and\n * drop the source so the browser stops downloading it.\n */\n private releaseMedia(el?: HTMLMediaElement): void {\n if (!el) return;\n this.cancelFade(el);\n const finish = () => {\n el.pause();\n el.removeAttribute('src');\n el.load();\n };\n if (el.paused || el.muted || el.volume === 0) {\n finish();\n return;\n }\n this.fade(el, 0, FADE_OUT_MS, finish);\n }\n\n private cancelFade(el: HTMLMediaElement): void {\n const frame = this.fadeFrames.get(el);\n if (frame !== undefined) window.cancelAnimationFrame(frame);\n this.fadeFrames.delete(el);\n }\n\n /** Ramps a media element's volume to `target` over `ms`. */\n private fade(\n el: HTMLMediaElement,\n target: number,\n ms: number,\n done?: () => void,\n ): void {\n this.cancelFade(el);\n const from = el.volume;\n const t0 = performance.now();\n const step = (now: number) => {\n const t = Math.min(1, (now - t0) / ms);\n el.volume = from + (target - from) * t;\n if (t < 1) this.fadeFrames.set(el, window.requestAnimationFrame(step));\n else {\n this.fadeFrames.delete(el);\n done?.();\n }\n };\n this.fadeFrames.set(el, window.requestAnimationFrame(step));\n }\n\n /**\n * Starts a media element, with sound when the options ask for it. A browser\n * that refuses audible playback gets a muted one instead.\n */\n private async playMedia(\n el: HTMLMediaElement,\n activation: number,\n ): Promise<boolean> {\n if (this.options.sound) {\n try {\n el.muted = false;\n el.volume = 0;\n await el.play();\n if (activation !== this.activation) return true;\n this.audible = true;\n this.fade(el, MAX_VOLUME, FADE_IN_MS);\n return true;\n } catch {\n // Refused, so play it muted below.\n }\n }\n try {\n el.muted = true;\n await el.play();\n return true;\n } catch {\n // A stopped preview isn't a failure; only a current one needs the frames.\n return activation !== this.activation;\n }\n }\n\n /** Swaps a clip that is slow or unplayable for the item's frame thumbnails. */\n private useFallbackFrames(): void {\n window.clearTimeout(this.frameTimer);\n if (this.media?.kind !== 'video') return;\n const urls = this.media.fallbackUrls;\n this.releaseMedia(this.video);\n this.audible = false;\n if (urls.length > 1) {\n this.media = { kind: 'slides', fit: 'cover', urls };\n this.slideUrls = urls;\n this.slide = 0;\n this.ready = false;\n const { activation } = this;\n this.updateComplete.then(() => {\n if (activation === this.activation) this.startSlides();\n });\n } else {\n this.media = { kind: 'zoom' };\n this.ready = true;\n }\n }\n\n private startSlides(): void {\n window.clearInterval(this.slideTimer);\n this.slideTimer = window.setInterval(() => {\n const loaded = this.loadedSlides();\n if (loaded.length < 2) return;\n let next = this.slide;\n do {\n next = (next + 1) % this.slideUrls.length;\n } while (!loaded.includes(next));\n this.slide = next;\n }, SLIDE_INTERVAL_MS);\n }\n\n private loadedSlides(): number[] {\n const imgs = [...(this.shadowRoot?.querySelectorAll('.slides img') ?? [])];\n return imgs.flatMap((img, i) =>\n (img as HTMLImageElement).complete &&\n (img as HTMLImageElement).naturalWidth\n ? [i]\n : [],\n );\n }\n\n private onSlideError(url: string): void {\n this.slideUrls = this.slideUrls.filter(u => u !== url);\n this.slide = 0;\n if (this.slideUrls.length < 2) {\n window.clearInterval(this.slideTimer);\n this.media = { kind: 'zoom' };\n this.ready = true;\n }\n }\n\n private onSlideLoad(): void {\n this.ready = true;\n }\n\n private onVideoPlaying(): void {\n window.clearTimeout(this.frameTimer);\n this.ready = true;\n }\n\n /** Loops the clip within its start-to-start+clip-length window. */\n private onVideoTimeUpdate(): void {\n const video = this.video;\n if (\n this.media?.kind === 'video' &&\n video &&\n video.currentTime >= this.media.start + PREVIEW_CLIP_SECONDS\n ) {\n video.currentTime = this.media.start;\n }\n }\n\n private async onVideoLoaded(): Promise<void> {\n const { video, activation } = this;\n if (video && !(await this.playMedia(video, activation))) {\n this.useFallbackFrames();\n }\n }\n\n private async playSnippet(): Promise<void> {\n if (this.audio) await this.playMedia(this.audio, this.activation);\n }\n\n render() {\n if (!this.active || !this.media) return nothing;\n return html`<div\n class=\"layer ${this.media.kind} ${this.ready ? 'ready' : ''}\"\n aria-hidden=\"true\"\n >\n ${this.mediaTemplate(this.media)} ${this.soundBadgeTemplate}\n </div>`;\n }\n\n /** A muted-speaker hint, so people know a clip or snippet could have sound. */\n private get soundBadgeTemplate() {\n const kind = this.media?.kind;\n if ((kind !== 'video' && kind !== 'audio') || this.audible) return nothing;\n return html`<span class=\"badge\" title=${msg('Sound is off')}>\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" aria-hidden=\"true\">\n <path\n fill=\"currentColor\"\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.6 3 2.7-2.7-1.4-1.4-2.7 2.7-2.7-2.7-1.4 1.4 2.7 2.7-2.7 2.7 1.4 1.4 2.7-2.7 2.7 2.7 1.4-1.4-2.7-2.7z\"\n />\n </svg>\n </span>`;\n }\n\n private mediaTemplate(media: TilePreviewMedia) {\n switch (media.kind) {\n case 'video':\n return html`<video\n muted\n loop\n playsinline\n preload=\"auto\"\n src=\"${media.src}#t=${media.start}\"\n @loadeddata=${this.onVideoLoaded}\n @error=${this.useFallbackFrames}\n @playing=${this.onVideoPlaying}\n @timeupdate=${this.onVideoTimeUpdate}\n ></video>`;\n case 'slides':\n return html`<div class=\"slides ${media.fit}\">\n ${this.slideUrls.map(\n (url, i) =>\n html`<img\n src=${url}\n alt=\"\"\n class=${i === this.slide ? 'on' : ''}\n @load=${this.onSlideLoad}\n @error=${() => this.onSlideError(url)}\n />`,\n )}\n </div>`;\n case 'audio':\n return html`\n ${media.waveform\n ? html`<img class=\"waveform\" src=${media.waveform} alt=\"\" />`\n : html`<div class=\"bars\">\n ${Array.from(\n { length: 12 },\n (_, i) =>\n html`<span style=\"animation-delay:${i * -97}ms\"></span>`,\n )}\n </div>`}\n <div class=\"playhead\"></div>\n ${this.options.sound\n ? html`<audio\n preload=\"auto\"\n src=\"${media.src}#t=${media.start}\"\n ></audio>`\n : nothing}\n `;\n default:\n return html`<img\n class=\"pan\"\n src=${ifDefined(this.thumbnailUrl)}\n alt=\"\"\n />`;\n }\n }\n\n static styles = css`\n :host {\n position: absolute;\n inset: 0;\n pointer-events: none;\n overflow: hidden;\n border-radius: inherit;\n }\n\n .layer {\n position: absolute;\n inset: 0;\n opacity: 0;\n transition: opacity 200ms ease-out;\n }\n\n .layer.ready {\n opacity: 1;\n }\n\n video,\n .slides img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .slides {\n position: absolute;\n inset: 0;\n background: #fff;\n }\n\n .slides.contain img {\n object-fit: contain;\n }\n\n .slides img {\n opacity: 0;\n }\n\n .slides img.on {\n opacity: 1;\n }\n\n .audio {\n background: #10161b;\n }\n\n .waveform {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: fill;\n filter: invert(1) hue-rotate(180deg);\n opacity: 0.85;\n }\n\n .badge {\n position: absolute;\n left: 6px;\n bottom: 6px;\n display: flex;\n padding: 3px;\n border-radius: 4px;\n color: #fff;\n background: rgb(0 0 0 / 0.65);\n }\n\n .playhead {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n background: #f0a500;\n box-shadow: 0 0 8px #f0a500;\n animation: scan 2.4s linear infinite;\n }\n\n .bars {\n position: absolute;\n inset: 20% 10%;\n display: flex;\n gap: 4px;\n align-items: end;\n }\n\n .bars span {\n flex: 1;\n height: 30%;\n background: #5bb8e0;\n animation: bounce 700ms ease-in-out infinite alternate;\n }\n\n .pan {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n animation: pan 6s ease-in-out infinite alternate;\n }\n\n @keyframes pan {\n from {\n transform: scale(1) translate(0, 0);\n }\n to {\n transform: scale(1.3) translate(-5%, -4%);\n }\n }\n\n @keyframes scan {\n from {\n left: 0;\n }\n to {\n left: 100%;\n }\n }\n\n @keyframes bounce {\n to {\n height: 95%;\n }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .layer {\n transition: none;\n }\n\n .playhead,\n .pan,\n .bars span {\n animation: none;\n }\n }\n `;\n}\n"]}
|
|
@@ -14,6 +14,7 @@ import type { TileHoverPane } from './hover/tile-hover-pane';
|
|
|
14
14
|
import { BaseTileComponent } from './base-tile-component';
|
|
15
15
|
import { LayoutType } from './models';
|
|
16
16
|
import { HoverPaneProperties, HoverPaneProviderInterface } from './hover/hover-pane-controller';
|
|
17
|
+
import type { TilePreviewOptions } from './preview/tile-preview-media';
|
|
17
18
|
export declare class TileDispatcher extends BaseTileComponent implements SharedResizeObserverResizeHandlerInterface, HoverPaneProviderInterface {
|
|
18
19
|
tileDisplayMode?: TileDisplayMode;
|
|
19
20
|
isManageView: boolean;
|
|
@@ -24,6 +25,10 @@ export declare class TileDispatcher extends BaseTileComponent implements SharedR
|
|
|
24
25
|
layoutType: LayoutType;
|
|
25
26
|
/** Whether this tile should include a hover pane at all (for applicable tile modes) */
|
|
26
27
|
enableHoverPane: boolean;
|
|
28
|
+
/** Whether hovering, focusing or long-pressing this tile starts a live preview */
|
|
29
|
+
enableTilePreview: boolean;
|
|
30
|
+
tilePreviewOptions: TilePreviewOptions;
|
|
31
|
+
private previewController;
|
|
27
32
|
manageCheckTitle: string;
|
|
28
33
|
private hoverPaneController?;
|
|
29
34
|
private container;
|
|
@@ -45,6 +50,8 @@ export declare class TileDispatcher extends BaseTileComponent implements SharedR
|
|
|
45
50
|
* (e.g., whether mouse listeners should be attached, etc.)
|
|
46
51
|
*/
|
|
47
52
|
private get shouldPrepareHoverPane();
|
|
53
|
+
/** Only plain item tiles preview: not collections, accounts, searches or gated items. */
|
|
54
|
+
private get shouldPreview();
|
|
48
55
|
private get isHoverEnabled();
|
|
49
56
|
/**
|
|
50
57
|
* Whether the info button should be shown on this tile.
|
|
@@ -17,6 +17,7 @@ import { BaseTileComponent } from './base-tile-component';
|
|
|
17
17
|
import { HoverPaneController, } from './hover/hover-pane-controller';
|
|
18
18
|
import { srOnlyStyle } from '../styles/sr-only';
|
|
19
19
|
import { tileActionStyles } from '../styles/tile-action-styles';
|
|
20
|
+
import { PREVIEW_PANE_DELAY_MS, TilePreviewController, } from './preview/tile-preview-controller';
|
|
20
21
|
let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
21
22
|
constructor() {
|
|
22
23
|
/*
|
|
@@ -43,6 +44,12 @@ let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
|
43
44
|
this.layoutType = 'default';
|
|
44
45
|
/** Whether this tile should include a hover pane at all (for applicable tile modes) */
|
|
45
46
|
this.enableHoverPane = false;
|
|
47
|
+
/** Whether hovering, focusing or long-pressing this tile starts a live preview */
|
|
48
|
+
this.enableTilePreview = false;
|
|
49
|
+
this.tilePreviewOptions = {};
|
|
50
|
+
this.previewController = new TilePreviewController(this, {
|
|
51
|
+
enabled: () => this.shouldPreview,
|
|
52
|
+
});
|
|
46
53
|
this.manageCheckTitle = msg('Remove this item from the list');
|
|
47
54
|
/**
|
|
48
55
|
* When the mouse enters the grid-mode tile actions area, dispatch a
|
|
@@ -90,6 +97,8 @@ let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
|
90
97
|
this.hoverPaneController = new HoverPaneController(this, {
|
|
91
98
|
mobileBreakpoint: this.mobileBreakpoint,
|
|
92
99
|
enableLongPress: false,
|
|
100
|
+
// A playing preview gets the tile to itself; the info pane follows later.
|
|
101
|
+
showDelay: () => (this.shouldPreview ? PREVIEW_PANE_DELAY_MS : 300),
|
|
93
102
|
});
|
|
94
103
|
}
|
|
95
104
|
}
|
|
@@ -170,6 +179,18 @@ let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
|
170
179
|
!this.model?.captureDates // don't show hover panes on web archive tiles
|
|
171
180
|
);
|
|
172
181
|
}
|
|
182
|
+
/** Only plain item tiles preview: not collections, accounts, searches or gated items. */
|
|
183
|
+
get shouldPreview() {
|
|
184
|
+
const { model } = this;
|
|
185
|
+
return (this.enableTilePreview &&
|
|
186
|
+
this.tileDisplayMode === 'grid' &&
|
|
187
|
+
!this.isManageView &&
|
|
188
|
+
!!model?.identifier &&
|
|
189
|
+
!['collection', 'account', 'search'].includes(model.mediatype) &&
|
|
190
|
+
!model.captureDates &&
|
|
191
|
+
(this.suppressBlurring ||
|
|
192
|
+
(!model.contentWarning && !(model.loginRequired && !this.loggedIn))));
|
|
193
|
+
}
|
|
173
194
|
get isHoverEnabled() {
|
|
174
195
|
return window.matchMedia('(hover: hover)').matches;
|
|
175
196
|
}
|
|
@@ -210,6 +231,8 @@ let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
|
210
231
|
});
|
|
211
232
|
}
|
|
212
233
|
updated(props) {
|
|
234
|
+
if (!this.shouldPreview)
|
|
235
|
+
this.previewController.deactivate();
|
|
213
236
|
if (props.has('resizeObserver')) {
|
|
214
237
|
const previousObserver = props.get('resizeObserver');
|
|
215
238
|
this.stopResizeObservation(previousObserver);
|
|
@@ -342,6 +365,8 @@ let TileDispatcher = class TileDispatcher extends BaseTileComponent {
|
|
|
342
365
|
?showTvClips=${this.showTvClips}
|
|
343
366
|
?showInfoButton=${this.shouldShowInfoButton}
|
|
344
367
|
?useLocalTime=${this.useLocalTime}
|
|
368
|
+
?previewActive=${this.previewController.active}
|
|
369
|
+
.previewOptions=${this.tilePreviewOptions}
|
|
345
370
|
@infoButtonPressed=${this.tileInfoButtonPressed}
|
|
346
371
|
>
|
|
347
372
|
</item-tile>`;
|
|
@@ -557,6 +582,12 @@ __decorate([
|
|
|
557
582
|
__decorate([
|
|
558
583
|
property({ type: Boolean })
|
|
559
584
|
], TileDispatcher.prototype, "enableHoverPane", void 0);
|
|
585
|
+
__decorate([
|
|
586
|
+
property({ type: Boolean })
|
|
587
|
+
], TileDispatcher.prototype, "enableTilePreview", void 0);
|
|
588
|
+
__decorate([
|
|
589
|
+
property({ type: Object })
|
|
590
|
+
], TileDispatcher.prototype, "tilePreviewOptions", void 0);
|
|
560
591
|
__decorate([
|
|
561
592
|
property({ type: String })
|
|
562
593
|
], TileDispatcher.prototype, "manageCheckTitle", void 0);
|