@internetarchive/collection-browser 0.2.18 → 0.2.20-alpha.1
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/dist/src/app-root.d.ts +6 -0
- package/dist/src/app-root.js +94 -1
- package/dist/src/app-root.js.map +1 -1
- package/dist/src/collection-browser.d.ts +14 -1
- package/dist/src/collection-browser.js +133 -20
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/collection-facets.d.ts +2 -0
- package/dist/src/collection-facets.js +9 -0
- package/dist/src/collection-facets.js.map +1 -1
- package/dist/src/models.d.ts +2 -0
- package/dist/src/models.js +8 -0
- package/dist/src/models.js.map +1 -1
- package/dist/src/tiles/grid/item-tile.d.ts +5 -2
- package/dist/src/tiles/grid/item-tile.js +28 -2
- package/dist/src/tiles/grid/item-tile.js.map +1 -1
- package/dist/src/tiles/image-block.js +1 -1
- package/dist/src/tiles/image-block.js.map +1 -1
- package/dist/src/tiles/list/tile-list.d.ts +2 -0
- package/dist/src/tiles/list/tile-list.js +14 -1
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/src/tiles/text-snippet-block.d.ts +29 -0
- package/dist/src/tiles/text-snippet-block.js +128 -0
- package/dist/src/tiles/text-snippet-block.js.map +1 -0
- package/dist/src/tiles/tile-dispatcher.js +1 -0
- package/dist/src/tiles/tile-dispatcher.js.map +1 -1
- package/dist/src/utils/analytics-events.d.ts +18 -0
- package/dist/src/utils/analytics-events.js +21 -0
- package/dist/src/utils/analytics-events.js.map +1 -0
- package/dist/test/collection-browser.test.js +91 -5
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/mocks/mock-analytics-handler.d.ts +10 -0
- package/dist/test/mocks/mock-analytics-handler.js +16 -0
- package/dist/test/mocks/mock-analytics-handler.js.map +1 -0
- package/dist/test/mocks/mock-search-responses.d.ts +2 -1
- package/dist/test/mocks/mock-search-responses.js +27 -1
- package/dist/test/mocks/mock-search-responses.js.map +1 -1
- package/dist/test/mocks/mock-search-service.js +6 -2
- package/dist/test/mocks/mock-search-service.js.map +1 -1
- package/dist/test/text-snippet-block.test.d.ts +1 -0
- package/dist/test/text-snippet-block.test.js +52 -0
- package/dist/test/text-snippet-block.test.js.map +1 -0
- package/dist/test/tiles/grid/item-tile.test.js +15 -0
- package/dist/test/tiles/grid/item-tile.test.js.map +1 -1
- package/dist/test/tiles/list/tile-list.test.d.ts +1 -0
- package/dist/test/tiles/list/tile-list.test.js +42 -0
- package/dist/test/tiles/list/tile-list.test.js.map +1 -0
- package/package.json +4 -3
- package/src/app-root.ts +103 -2
- package/src/collection-browser.ts +153 -17
- package/src/collection-facets.ts +13 -1
- package/src/models.ts +10 -0
- package/src/tiles/grid/item-tile.ts +35 -2
- package/src/tiles/image-block.ts +1 -1
- package/src/tiles/list/tile-list.ts +14 -1
- package/src/tiles/text-snippet-block.ts +130 -0
- package/src/tiles/tile-dispatcher.ts +4 -0
- package/src/utils/analytics-events.ts +19 -0
- package/test/collection-browser.test.ts +131 -4
- package/test/mocks/mock-analytics-handler.ts +30 -0
- package/test/mocks/mock-search-responses.ts +34 -1
- package/test/mocks/mock-search-service.ts +10 -2
- package/test/text-snippet-block.test.ts +69 -0
- package/test/tiles/grid/item-tile.test.ts +19 -0
- package/test/tiles/list/tile-list.test.ts +51 -0
package/src/app-root.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnalyticsEvent,
|
|
3
|
+
AnalyticsManager,
|
|
4
|
+
} from '@internetarchive/analytics-manager';
|
|
1
5
|
import { SearchService } from '@internetarchive/search-service';
|
|
2
6
|
import { LocalCache } from '@internetarchive/local-cache';
|
|
3
7
|
import { html, css, LitElement, PropertyValues } from 'lit';
|
|
4
|
-
import { customElement, query, state } from 'lit/decorators.js';
|
|
8
|
+
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
5
9
|
import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
|
|
6
10
|
import { CollectionNameCache } from '@internetarchive/collection-name-cache';
|
|
11
|
+
|
|
12
|
+
import type { AnalyticsManagerInterface } from '@internetarchive/analytics-manager';
|
|
7
13
|
import type { CollectionBrowser } from '../src/collection-browser';
|
|
14
|
+
|
|
8
15
|
import '../src/collection-browser';
|
|
9
16
|
|
|
10
17
|
@customElement('app-root')
|
|
@@ -34,12 +41,28 @@ export class AppRoot extends LitElement {
|
|
|
34
41
|
|
|
35
42
|
@state() private loggedIn: boolean = false;
|
|
36
43
|
|
|
44
|
+
@property({ type: Object, reflect: false }) latestAction?: AnalyticsEvent;
|
|
45
|
+
|
|
37
46
|
@query('#base-query-field') private baseQueryField!: HTMLInputElement;
|
|
38
47
|
|
|
39
48
|
@query('#page-number-input') private pageNumberInput!: HTMLInputElement;
|
|
40
49
|
|
|
41
50
|
@query('collection-browser') private collectionBrowser!: CollectionBrowser;
|
|
42
51
|
|
|
52
|
+
private analyticsManager = new AnalyticsManager();
|
|
53
|
+
|
|
54
|
+
private analyticsHandler: AnalyticsManagerInterface = {
|
|
55
|
+
sendPing: this.sendAnalytics.bind(this),
|
|
56
|
+
sendEvent: this.sendAnalytics.bind(this),
|
|
57
|
+
sendEventNoSampling: this.sendAnalytics.bind(this),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
private sendAnalytics(ae: AnalyticsEvent) {
|
|
61
|
+
console.log('Analytics Received ----', ae);
|
|
62
|
+
this.latestAction = ae;
|
|
63
|
+
this.analyticsManager?.sendEventNoSampling(ae);
|
|
64
|
+
}
|
|
65
|
+
|
|
43
66
|
private searchPressed(e: Event) {
|
|
44
67
|
e.preventDefault();
|
|
45
68
|
this.searchQuery = this.baseQueryField.value;
|
|
@@ -54,7 +77,7 @@ export class AppRoot extends LitElement {
|
|
|
54
77
|
this.collectionBrowser.goToPage(this.currentPage);
|
|
55
78
|
}
|
|
56
79
|
|
|
57
|
-
protected updated(changed: PropertyValues): void {
|
|
80
|
+
protected override updated(changed: PropertyValues): void {
|
|
58
81
|
if (changed.has('currentPage') && this.currentPage) {
|
|
59
82
|
this.pageNumberInput.value = this.currentPage.toString();
|
|
60
83
|
}
|
|
@@ -86,6 +109,23 @@ export class AppRoot extends LitElement {
|
|
|
86
109
|
<input type="submit" value="Go" />
|
|
87
110
|
</form>
|
|
88
111
|
|
|
112
|
+
<div id="last-event">
|
|
113
|
+
<button
|
|
114
|
+
@click=${() => {
|
|
115
|
+
const details = this.shadowRoot?.getElementById(
|
|
116
|
+
'latest-event-details'
|
|
117
|
+
);
|
|
118
|
+
details?.classList.toggle('hidden');
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
Last Event Captured
|
|
122
|
+
</button>
|
|
123
|
+
<pre id="latest-event-details">
|
|
124
|
+
${JSON.stringify(this.latestAction, null, 2)}
|
|
125
|
+
</pre
|
|
126
|
+
>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
89
129
|
<div id="cell-controls">
|
|
90
130
|
<div id="cell-size-control">
|
|
91
131
|
<div>
|
|
@@ -130,6 +170,14 @@ export class AppRoot extends LitElement {
|
|
|
130
170
|
@click=${this.loginChanged}
|
|
131
171
|
/>
|
|
132
172
|
</div>
|
|
173
|
+
<div>
|
|
174
|
+
<label for="show-dummy-snippets">Show dummy snippets:</label>
|
|
175
|
+
<input
|
|
176
|
+
type="checkbox"
|
|
177
|
+
id="show-dummy-snippets"
|
|
178
|
+
@click=${this.snippetsChanged}
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
133
181
|
</div>
|
|
134
182
|
<div id="cell-gap-control">
|
|
135
183
|
<div>
|
|
@@ -171,6 +219,7 @@ export class AppRoot extends LitElement {
|
|
|
171
219
|
.collectionNameCache=${this.collectionNameCache}
|
|
172
220
|
.showHistogramDatePicker=${true}
|
|
173
221
|
.loggedIn=${this.loggedIn}
|
|
222
|
+
.analyticsHandler=${this.analyticsHandler}
|
|
174
223
|
@visiblePageChanged=${this.visiblePageChanged}
|
|
175
224
|
@baseQueryChanged=${this.baseQueryChanged}
|
|
176
225
|
>
|
|
@@ -206,6 +255,48 @@ export class AppRoot extends LitElement {
|
|
|
206
255
|
}
|
|
207
256
|
}
|
|
208
257
|
|
|
258
|
+
private async snippetsChanged(e: Event) {
|
|
259
|
+
const target = e.target as HTMLInputElement;
|
|
260
|
+
if (target.checked) {
|
|
261
|
+
// Decorate the default search service with a wrapper that adds
|
|
262
|
+
// dummy snippets to any successful searches
|
|
263
|
+
this.searchService = {
|
|
264
|
+
...SearchService.default,
|
|
265
|
+
async search(params) {
|
|
266
|
+
const result = await SearchService.default.search(params);
|
|
267
|
+
result.success?.response.docs.forEach(doc => {
|
|
268
|
+
const metadata = doc.rawMetadata;
|
|
269
|
+
if (metadata) {
|
|
270
|
+
metadata.snippets = [
|
|
271
|
+
'this is a text {{{snippet}}} block with potentially',
|
|
272
|
+
'multiple {{{snippets}}} and such',
|
|
273
|
+
'but the {{{snippet}}} block may be quite long perhaps',
|
|
274
|
+
'depending on how many {{{snippet}}} matches there are',
|
|
275
|
+
'there may be multiple lines of {{{snippets}}} to show',
|
|
276
|
+
'but each {{{snippet}}} should be relatively short',
|
|
277
|
+
'and {{{snippets}}} are each a {{{snippet}}} of text',
|
|
278
|
+
'but every {{{snippet}}} might have multiple matches',
|
|
279
|
+
'the {{{snippets}}} should be separated and surrounded by ellipses',
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return result;
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
} else {
|
|
287
|
+
// Restore the default seach service
|
|
288
|
+
this.searchService = SearchService.default;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Re-perform the current search to show/hide the snippets immediately
|
|
292
|
+
const oldQuery = this.searchQuery;
|
|
293
|
+
this.searchQuery = ''; // Should just reset to the placeholder
|
|
294
|
+
await this.updateComplete;
|
|
295
|
+
// For unclear reasons, Safari refuses to re-apply the old query until the next tick, hence:
|
|
296
|
+
await new Promise(res => setTimeout(res, 0));
|
|
297
|
+
this.searchQuery = oldQuery; // Re-apply the original query
|
|
298
|
+
}
|
|
299
|
+
|
|
209
300
|
private rowGapChanged(e: Event) {
|
|
210
301
|
const input = e.target as HTMLInputElement;
|
|
211
302
|
this.rowGap = parseFloat(input.value);
|
|
@@ -293,5 +384,15 @@ export class AppRoot extends LitElement {
|
|
|
293
384
|
#cell-gap-control {
|
|
294
385
|
margin-left: 1rem;
|
|
295
386
|
}
|
|
387
|
+
|
|
388
|
+
#last-event {
|
|
389
|
+
background-color: aliceblue;
|
|
390
|
+
padding: 5px;
|
|
391
|
+
margin: 5px auto;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.hidden {
|
|
395
|
+
display: none;
|
|
396
|
+
}
|
|
296
397
|
`;
|
|
297
398
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from 'lit';
|
|
10
10
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
11
11
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
12
|
+
|
|
13
|
+
import type { AnalyticsManagerInterface } from '@internetarchive/analytics-manager';
|
|
12
14
|
import type {
|
|
13
15
|
InfiniteScroller,
|
|
14
16
|
InfiniteScrollerCellProviderInterface,
|
|
@@ -41,6 +43,7 @@ import {
|
|
|
41
43
|
defaultSelectedFacets,
|
|
42
44
|
TileModel,
|
|
43
45
|
CollectionDisplayMode,
|
|
46
|
+
FacetOption,
|
|
44
47
|
} from './models';
|
|
45
48
|
import {
|
|
46
49
|
RestorationStateHandlerInterface,
|
|
@@ -52,6 +55,11 @@ import { LanguageCodeHandler } from './language-code-handler/language-code-handl
|
|
|
52
55
|
import type { PlaceholderType } from './empty-placeholder';
|
|
53
56
|
import './empty-placeholder';
|
|
54
57
|
|
|
58
|
+
import {
|
|
59
|
+
analyticsActions,
|
|
60
|
+
analyticsCategories,
|
|
61
|
+
} from './utils/analytics-events';
|
|
62
|
+
|
|
55
63
|
@customElement('collection-browser')
|
|
56
64
|
export class CollectionBrowser
|
|
57
65
|
extends LitElement
|
|
@@ -155,6 +163,9 @@ export class CollectionBrowser
|
|
|
155
163
|
|
|
156
164
|
private languageCodeHandler = new LanguageCodeHandler();
|
|
157
165
|
|
|
166
|
+
@property({ type: Object, attribute: false })
|
|
167
|
+
private analyticsHandler?: AnalyticsManagerInterface;
|
|
168
|
+
|
|
158
169
|
/**
|
|
159
170
|
* When we're animated scrolling to the page, we don't want to fetch
|
|
160
171
|
* all of the pages as it scrolls so this lets us know if we're scrolling
|
|
@@ -295,7 +306,9 @@ export class CollectionBrowser
|
|
|
295
306
|
? this.totalResults.toLocaleString()
|
|
296
307
|
: '-'}
|
|
297
308
|
</span>
|
|
298
|
-
<span id="big-results-label">
|
|
309
|
+
<span id="big-results-label">
|
|
310
|
+
${this.totalResults === 1 ? 'Result' : 'Results'}
|
|
311
|
+
</span>
|
|
299
312
|
</div>
|
|
300
313
|
</div>
|
|
301
314
|
<div
|
|
@@ -328,18 +341,21 @@ export class CollectionBrowser
|
|
|
328
341
|
}
|
|
329
342
|
|
|
330
343
|
private get sortFilterBarTemplate() {
|
|
331
|
-
return html
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
344
|
+
return html`
|
|
345
|
+
<sort-filter-bar
|
|
346
|
+
.selectedSort=${this.selectedSort}
|
|
347
|
+
.sortDirection=${this.sortDirection}
|
|
348
|
+
.displayMode=${this.displayMode}
|
|
349
|
+
.selectedTitleFilter=${this.selectedTitleFilter}
|
|
350
|
+
.selectedCreatorFilter=${this.selectedCreatorFilter}
|
|
351
|
+
.resizeObserver=${this.resizeObserver}
|
|
352
|
+
@sortChanged=${this.userChangedSort}
|
|
353
|
+
@displayModeChanged=${this.displayModeChanged}
|
|
354
|
+
@titleLetterChanged=${this.titleLetterSelected}
|
|
355
|
+
@creatorLetterChanged=${this.creatorLetterSelected}
|
|
356
|
+
>
|
|
357
|
+
</sort-filter-bar>
|
|
358
|
+
`;
|
|
343
359
|
}
|
|
344
360
|
|
|
345
361
|
private userChangedSort(
|
|
@@ -358,12 +374,25 @@ export class CollectionBrowser
|
|
|
358
374
|
this.currentPage = 1;
|
|
359
375
|
}
|
|
360
376
|
|
|
361
|
-
private
|
|
377
|
+
private sendSortByAnaltyics(prevSortDirection: SortDirection | null): void {
|
|
378
|
+
const directionCleared = prevSortDirection && !this.sortDirection;
|
|
379
|
+
|
|
380
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
381
|
+
category: analyticsCategories.default,
|
|
382
|
+
action: analyticsActions.sortBy,
|
|
383
|
+
label: `${this.selectedSort}${
|
|
384
|
+
this.sortDirection || directionCleared ? `-${this.sortDirection}` : ''
|
|
385
|
+
}`,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private selectedSortChanged(): void {
|
|
362
390
|
if (this.selectedSort === 'relevance' || this.sortDirection === null) {
|
|
363
391
|
this.sortParam = null;
|
|
364
392
|
return;
|
|
365
393
|
}
|
|
366
394
|
const sortField = SortFieldToMetadataField[this.selectedSort];
|
|
395
|
+
|
|
367
396
|
if (!sortField) return;
|
|
368
397
|
this.sortParam = { field: sortField, direction: this.sortDirection };
|
|
369
398
|
}
|
|
@@ -372,15 +401,61 @@ export class CollectionBrowser
|
|
|
372
401
|
e: CustomEvent<{ displayMode: CollectionDisplayMode }>
|
|
373
402
|
) {
|
|
374
403
|
this.displayMode = e.detail.displayMode;
|
|
404
|
+
|
|
405
|
+
if (this.displayMode) {
|
|
406
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
407
|
+
category: analyticsCategories.default,
|
|
408
|
+
action: analyticsActions.displayMode,
|
|
409
|
+
label: this.displayMode,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** Send Analytics when sorting by title's first letter
|
|
415
|
+
* labels: 'start-<ToLetter>' | 'clear-<FromLetter>' | '<FromLetter>-<ToLetter>'
|
|
416
|
+
* */
|
|
417
|
+
private sendFilterByTitleAnalytics(prevSelectedLetter: string | null): void {
|
|
418
|
+
if (!prevSelectedLetter && !this.selectedTitleFilter) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const cleared = prevSelectedLetter && this.selectedTitleFilter === null;
|
|
422
|
+
|
|
423
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
424
|
+
category: analyticsCategories.default,
|
|
425
|
+
action: analyticsActions.filterByTitle,
|
|
426
|
+
label: cleared
|
|
427
|
+
? `clear-${prevSelectedLetter}`
|
|
428
|
+
: `${prevSelectedLetter || 'start'}-${this.selectedTitleFilter}`,
|
|
429
|
+
});
|
|
375
430
|
}
|
|
376
431
|
|
|
377
|
-
private selectedTitleLetterChanged() {
|
|
432
|
+
private selectedTitleLetterChanged(): void {
|
|
378
433
|
this.titleQuery = this.selectedTitleFilter
|
|
379
434
|
? `firstTitle:${this.selectedTitleFilter}`
|
|
380
435
|
: undefined;
|
|
381
436
|
}
|
|
382
437
|
|
|
383
|
-
|
|
438
|
+
/** Send Analytics when filtering by creator's first letter
|
|
439
|
+
* labels: 'start-<ToLetter>' | 'clear-<FromLetter>' | '<FromLetter>-<ToLetter>'
|
|
440
|
+
* */
|
|
441
|
+
private sendFilterByCreatorAnalytics(
|
|
442
|
+
prevSelectedLetter: string | null
|
|
443
|
+
): void {
|
|
444
|
+
if (!prevSelectedLetter && !this.selectedCreatorFilter) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const cleared = prevSelectedLetter && this.selectedCreatorFilter === null;
|
|
448
|
+
|
|
449
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
450
|
+
category: analyticsCategories.default,
|
|
451
|
+
action: analyticsActions.filterByCreator,
|
|
452
|
+
label: cleared
|
|
453
|
+
? `clear-${prevSelectedLetter}`
|
|
454
|
+
: `${prevSelectedLetter || 'start'}-${this.selectedCreatorFilter}`,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private selectedCreatorLetterChanged(): void {
|
|
384
459
|
this.creatorQuery = this.selectedCreatorFilter
|
|
385
460
|
? `firstCreator:${this.selectedCreatorFilter}`
|
|
386
461
|
: undefined;
|
|
@@ -439,7 +514,10 @@ export class CollectionBrowser
|
|
|
439
514
|
?collapsableFacets=${this.mobileView}
|
|
440
515
|
?facetsLoading=${this.facetDataLoading}
|
|
441
516
|
?fullYearAggregationLoading=${this.fullYearAggregationLoading}
|
|
442
|
-
|
|
517
|
+
.onFacetClick=${this.facetClickHandler}
|
|
518
|
+
.analyticsHandler=${this.analyticsHandler}
|
|
519
|
+
>
|
|
520
|
+
</collection-facets>
|
|
443
521
|
`;
|
|
444
522
|
}
|
|
445
523
|
|
|
@@ -489,6 +567,14 @@ export class CollectionBrowser
|
|
|
489
567
|
) {
|
|
490
568
|
const { minDate, maxDate } = e.detail;
|
|
491
569
|
this.dateRangeQueryClause = `year:[${minDate} TO ${maxDate}]`;
|
|
570
|
+
|
|
571
|
+
if (this.dateRangeQueryClause) {
|
|
572
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
573
|
+
category: analyticsCategories.default,
|
|
574
|
+
action: analyticsActions.histogramChanged,
|
|
575
|
+
label: this.dateRangeQueryClause,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
492
578
|
}
|
|
493
579
|
|
|
494
580
|
firstUpdated(): void {
|
|
@@ -523,12 +609,20 @@ export class CollectionBrowser
|
|
|
523
609
|
this.handleQueryChange();
|
|
524
610
|
}
|
|
525
611
|
if (changed.has('selectedSort') || changed.has('sortDirection')) {
|
|
612
|
+
const prevSortDirection = changed.get('sortDirection') as SortDirection;
|
|
613
|
+
this.sendSortByAnaltyics(prevSortDirection);
|
|
526
614
|
this.selectedSortChanged();
|
|
527
615
|
}
|
|
528
616
|
if (changed.has('selectedTitleFilter')) {
|
|
617
|
+
this.sendFilterByTitleAnalytics(
|
|
618
|
+
changed.get('selectedTitleFilter') as string
|
|
619
|
+
);
|
|
529
620
|
this.selectedTitleLetterChanged();
|
|
530
621
|
}
|
|
531
622
|
if (changed.has('selectedCreatorFilter')) {
|
|
623
|
+
this.sendFilterByCreatorAnalytics(
|
|
624
|
+
changed.get('selectedCreatorFilter') as string
|
|
625
|
+
);
|
|
532
626
|
this.selectedCreatorLetterChanged();
|
|
533
627
|
}
|
|
534
628
|
if (changed.has('pagesToRender')) {
|
|
@@ -748,6 +842,7 @@ export class CollectionBrowser
|
|
|
748
842
|
private get facetQuery(): string | undefined {
|
|
749
843
|
if (!this.selectedFacets) return undefined;
|
|
750
844
|
const facetQuery = [];
|
|
845
|
+
// console.log('selectedFacets: ', this.selectedFacets);
|
|
751
846
|
for (const [facetName, facetValues] of Object.entries(
|
|
752
847
|
this.selectedFacets
|
|
753
848
|
)) {
|
|
@@ -778,6 +873,30 @@ export class CollectionBrowser
|
|
|
778
873
|
this.selectedFacets = e.detail;
|
|
779
874
|
}
|
|
780
875
|
|
|
876
|
+
facetClickHandler(
|
|
877
|
+
name: FacetOption,
|
|
878
|
+
facetSelected: boolean,
|
|
879
|
+
negative: boolean
|
|
880
|
+
): void {
|
|
881
|
+
if (negative) {
|
|
882
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
883
|
+
category: analyticsCategories.default,
|
|
884
|
+
action: facetSelected
|
|
885
|
+
? analyticsActions.facetNegativeSelected
|
|
886
|
+
: analyticsActions.facetNegativeDeselected,
|
|
887
|
+
label: name,
|
|
888
|
+
});
|
|
889
|
+
} else {
|
|
890
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
891
|
+
category: analyticsCategories.default,
|
|
892
|
+
action: facetSelected
|
|
893
|
+
? analyticsActions.facetSelected
|
|
894
|
+
: analyticsActions.facetDeselected,
|
|
895
|
+
label: name,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
781
900
|
private async fetchFacets() {
|
|
782
901
|
if (!this.fullQuery) return;
|
|
783
902
|
|
|
@@ -1076,6 +1195,7 @@ export class CollectionBrowser
|
|
|
1076
1195
|
issue: doc.issue?.value,
|
|
1077
1196
|
itemCount: doc.item_count?.value ?? 0,
|
|
1078
1197
|
mediatype: doc.mediatype?.value ?? 'data',
|
|
1198
|
+
snippets: doc.snippets?.values ?? [],
|
|
1079
1199
|
source: doc.source?.value,
|
|
1080
1200
|
subjects: doc.subject?.values ?? [],
|
|
1081
1201
|
title: this.etreeTitle(
|
|
@@ -1120,6 +1240,21 @@ export class CollectionBrowser
|
|
|
1120
1240
|
return title ?? '';
|
|
1121
1241
|
}
|
|
1122
1242
|
|
|
1243
|
+
/** callback when a result is selected */
|
|
1244
|
+
resultSelected(event: CustomEvent<TileModel>): void {
|
|
1245
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
1246
|
+
category: analyticsCategories.default,
|
|
1247
|
+
action: analyticsActions.resultSelected,
|
|
1248
|
+
label: event.detail.mediatype === 'collection' ? 'collection' : 'item',
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
this.analyticsHandler?.sendEventNoSampling({
|
|
1252
|
+
category: analyticsCategories.default,
|
|
1253
|
+
action: analyticsActions.resultSelected,
|
|
1254
|
+
label: `page-${this.currentPage}`,
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1123
1258
|
cellForIndex(index: number): TemplateResult | undefined {
|
|
1124
1259
|
const model = this.tileModelAtCellIndex(index);
|
|
1125
1260
|
if (!model) return undefined;
|
|
@@ -1135,6 +1270,7 @@ export class CollectionBrowser
|
|
|
1135
1270
|
.sortParam=${this.sortParam}
|
|
1136
1271
|
.mobileBreakpoint=${this.mobileBreakpoint}
|
|
1137
1272
|
.loggedIn=${this.loggedIn}
|
|
1273
|
+
@resultSelected=${(e: CustomEvent) => this.resultSelected(e)}
|
|
1138
1274
|
>
|
|
1139
1275
|
</tile-dispatcher>
|
|
1140
1276
|
`;
|
package/src/collection-facets.ts
CHANGED
|
@@ -79,6 +79,13 @@ export class CollectionFacets extends LitElement {
|
|
|
79
79
|
@property({ type: Object })
|
|
80
80
|
collectionNameCache?: CollectionNameCacheInterface;
|
|
81
81
|
|
|
82
|
+
/** Fires when a facet is clicked */
|
|
83
|
+
@property({ type: Function }) onFacetClick?: (
|
|
84
|
+
name: FacetOption,
|
|
85
|
+
facetChecked: boolean,
|
|
86
|
+
negative: boolean
|
|
87
|
+
) => void;
|
|
88
|
+
|
|
82
89
|
@state() openFacets: Record<FacetOption, boolean> = {
|
|
83
90
|
subject: false,
|
|
84
91
|
mediatype: false,
|
|
@@ -88,6 +95,7 @@ export class CollectionFacets extends LitElement {
|
|
|
88
95
|
year: false,
|
|
89
96
|
};
|
|
90
97
|
|
|
98
|
+
@property({ type: Object, attribute: false })
|
|
91
99
|
render() {
|
|
92
100
|
return html`
|
|
93
101
|
<div id="container" class="${this.facetsLoading ? 'loading' : ''}">
|
|
@@ -426,7 +434,7 @@ export class CollectionFacets extends LitElement {
|
|
|
426
434
|
`;
|
|
427
435
|
}
|
|
428
436
|
|
|
429
|
-
private facetClicked(e: Event, bucket: FacetBucket, negative: boolean) {
|
|
437
|
+
private facetClicked(e: Event, bucket: FacetBucket, negative: boolean): void {
|
|
430
438
|
const target = e.target as HTMLInputElement;
|
|
431
439
|
const { checked, name, value } = target;
|
|
432
440
|
if (checked) {
|
|
@@ -434,6 +442,10 @@ export class CollectionFacets extends LitElement {
|
|
|
434
442
|
} else {
|
|
435
443
|
this.facetUnchecked(name as FacetOption, value);
|
|
436
444
|
}
|
|
445
|
+
|
|
446
|
+
if (this.onFacetClick) {
|
|
447
|
+
this.onFacetClick(name as FacetOption, checked, negative);
|
|
448
|
+
}
|
|
437
449
|
}
|
|
438
450
|
|
|
439
451
|
private facetChecked(key: FacetOption, value: string, negative: boolean) {
|
package/src/models.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface TileModel {
|
|
|
19
19
|
itemCount: number;
|
|
20
20
|
mediatype: MediaType;
|
|
21
21
|
source?: string;
|
|
22
|
+
snippets?: string[];
|
|
22
23
|
subjects: string[];
|
|
23
24
|
title: string;
|
|
24
25
|
viewCount: number;
|
|
@@ -158,3 +159,12 @@ export const defaultSelectedFacets: SelectedFacets = {
|
|
|
158
159
|
collection: {},
|
|
159
160
|
year: {},
|
|
160
161
|
};
|
|
162
|
+
|
|
163
|
+
export const mockedSelectedFacets: SelectedFacets = {
|
|
164
|
+
subject: {},
|
|
165
|
+
mediatype: { data: 'selected' },
|
|
166
|
+
language: {},
|
|
167
|
+
creator: {},
|
|
168
|
+
collection: {},
|
|
169
|
+
year: {},
|
|
170
|
+
};
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/* eslint-disable import/no-duplicates */
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
css,
|
|
4
|
+
CSSResultGroup,
|
|
5
|
+
html,
|
|
6
|
+
LitElement,
|
|
7
|
+
nothing,
|
|
8
|
+
TemplateResult,
|
|
9
|
+
} from 'lit';
|
|
3
10
|
import { customElement, property } from 'lit/decorators.js';
|
|
4
11
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
12
|
import type { SortParam } from '@internetarchive/search-service';
|
|
@@ -8,6 +15,7 @@ import { formatDate } from '../../utils/format-date';
|
|
|
8
15
|
import type { TileModel } from '../../models';
|
|
9
16
|
|
|
10
17
|
import '../image-block';
|
|
18
|
+
import '../text-snippet-block';
|
|
11
19
|
import '../item-image';
|
|
12
20
|
import '../mediatype-icon';
|
|
13
21
|
import './tile-stats';
|
|
@@ -35,6 +43,7 @@ export class ItemTile extends LitElement {
|
|
|
35
43
|
</div>
|
|
36
44
|
|
|
37
45
|
<image-block
|
|
46
|
+
class=${this.hasSnippets ? 'has-snippets' : nothing}
|
|
38
47
|
.model=${this.model}
|
|
39
48
|
.baseImageUrl=${this.baseImageUrl}
|
|
40
49
|
.loggedIn=${this.loggedIn}
|
|
@@ -43,6 +52,8 @@ export class ItemTile extends LitElement {
|
|
|
43
52
|
.viewSize=${'grid'}>
|
|
44
53
|
</image-block>
|
|
45
54
|
|
|
55
|
+
${this.textSnippetsTemplate}
|
|
56
|
+
|
|
46
57
|
${
|
|
47
58
|
this.doesSortedByDate
|
|
48
59
|
? this.sortedDateInfoTemplate
|
|
@@ -109,6 +120,19 @@ export class ItemTile extends LitElement {
|
|
|
109
120
|
`;
|
|
110
121
|
}
|
|
111
122
|
|
|
123
|
+
private get textSnippetsTemplate(): TemplateResult | typeof nothing {
|
|
124
|
+
if (!this.hasSnippets) return nothing;
|
|
125
|
+
|
|
126
|
+
return html`<text-snippet-block
|
|
127
|
+
viewsize="grid"
|
|
128
|
+
.snippets=${this.model?.snippets}
|
|
129
|
+
></text-snippet-block>`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private get hasSnippets(): boolean {
|
|
133
|
+
return !!this.model?.snippets?.length;
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
static get styles(): CSSResultGroup {
|
|
113
137
|
return css`
|
|
114
138
|
.container {
|
|
@@ -142,6 +166,16 @@ export class ItemTile extends LitElement {
|
|
|
142
166
|
text-decoration: underline;
|
|
143
167
|
}
|
|
144
168
|
|
|
169
|
+
image-block {
|
|
170
|
+
display: block;
|
|
171
|
+
margin-bottom: 5px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
image-block.has-snippets {
|
|
175
|
+
/* If there is a text snippet block present, the image block needs to shrink */
|
|
176
|
+
--imgBlockHeight: 11rem;
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
.created-by,
|
|
146
180
|
.date-sorted-by {
|
|
147
181
|
display: flex;
|
|
@@ -149,7 +183,6 @@ export class ItemTile extends LitElement {
|
|
|
149
183
|
align-items: flex-end; /* Important to start text from bottom */
|
|
150
184
|
height: 3rem;
|
|
151
185
|
padding-top: 1rem;
|
|
152
|
-
margin-top: 5px;
|
|
153
186
|
}
|
|
154
187
|
|
|
155
188
|
.truncated {
|
package/src/tiles/image-block.ts
CHANGED
|
@@ -140,7 +140,7 @@ export class TileList extends LitElement {
|
|
|
140
140
|
${this.viewsTemplate} ${this.ratingTemplate} ${this.reviewsTemplate}
|
|
141
141
|
</div>
|
|
142
142
|
${this.topicsTemplate} ${this.collectionsTemplate}
|
|
143
|
-
${this.descriptionTemplate}
|
|
143
|
+
${this.descriptionTemplate} ${this.textSnippetsTemplate}
|
|
144
144
|
`;
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -292,6 +292,19 @@ export class TileList extends LitElement {
|
|
|
292
292
|
);
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
private get textSnippetsTemplate(): TemplateResult | typeof nothing {
|
|
296
|
+
if (!this.hasSnippets) return nothing;
|
|
297
|
+
|
|
298
|
+
return html`<text-snippet-block
|
|
299
|
+
viewsize="list"
|
|
300
|
+
.snippets=${this.model?.snippets}
|
|
301
|
+
></text-snippet-block>`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private get hasSnippets(): boolean {
|
|
305
|
+
return !!this.model?.snippets?.length;
|
|
306
|
+
}
|
|
307
|
+
|
|
295
308
|
// Utility functions
|
|
296
309
|
private metadataTemplate(text: any, label = '', id?: string) {
|
|
297
310
|
if (!text) return nothing;
|