@internetarchive/collection-browser 0.3.4 → 0.3.5

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.
Files changed (62) hide show
  1. package/dist/src/app-root.d.ts +4 -0
  2. package/dist/src/app-root.js +99 -53
  3. package/dist/src/app-root.js.map +1 -1
  4. package/dist/src/collection-browser.d.ts +1 -2
  5. package/dist/src/collection-browser.js +21 -49
  6. package/dist/src/collection-browser.js.map +1 -1
  7. package/dist/src/collection-facets/facet-tombstone-row.d.ts +5 -0
  8. package/dist/src/collection-facets/facet-tombstone-row.js +43 -0
  9. package/dist/src/collection-facets/facet-tombstone-row.js.map +1 -0
  10. package/dist/src/collection-facets/facets-template.js +5 -3
  11. package/dist/src/collection-facets/facets-template.js.map +1 -1
  12. package/dist/src/collection-facets.d.ts +2 -0
  13. package/dist/src/collection-facets.js +44 -18
  14. package/dist/src/collection-facets.js.map +1 -1
  15. package/dist/src/models.d.ts +1 -0
  16. package/dist/src/models.js.map +1 -1
  17. package/dist/src/restoration-state-handler.d.ts +2 -1
  18. package/dist/src/restoration-state-handler.js +10 -0
  19. package/dist/src/restoration-state-handler.js.map +1 -1
  20. package/dist/src/tiles/grid/tile-stats.js +11 -5
  21. package/dist/src/tiles/grid/tile-stats.js.map +1 -1
  22. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -0
  23. package/dist/src/tiles/list/tile-list-compact.js +8 -4
  24. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  25. package/dist/src/tiles/list/tile-list.js +5 -2
  26. package/dist/src/tiles/list/tile-list.js.map +1 -1
  27. package/dist/src/tiles/mediatype-icon.js +2 -0
  28. package/dist/src/tiles/mediatype-icon.js.map +1 -1
  29. package/dist/test/collection-browser.test.js +125 -3
  30. package/dist/test/collection-browser.test.js.map +1 -1
  31. package/dist/test/collection-facets/facets-template.test.js +4 -4
  32. package/dist/test/collection-facets/facets-template.test.js.map +1 -1
  33. package/dist/test/mocks/mock-search-responses.d.ts +3 -0
  34. package/dist/test/mocks/mock-search-responses.js +95 -0
  35. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  36. package/dist/test/mocks/mock-search-service.js +15 -8
  37. package/dist/test/mocks/mock-search-service.js.map +1 -1
  38. package/dist/test/restoration-state-handler.test.js +9 -0
  39. package/dist/test/restoration-state-handler.test.js.map +1 -1
  40. package/dist/test/tiles/list/tile-list-compact.test.js +99 -0
  41. package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -1
  42. package/dist/test/tiles/list/tile-list.test.js +32 -0
  43. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/app-root.ts +104 -55
  46. package/src/collection-browser.ts +26 -48
  47. package/src/collection-facets/facet-tombstone-row.ts +40 -0
  48. package/src/collection-facets/facets-template.ts +5 -3
  49. package/src/collection-facets.ts +49 -18
  50. package/src/models.ts +1 -0
  51. package/src/restoration-state-handler.ts +19 -1
  52. package/src/tiles/grid/tile-stats.ts +18 -5
  53. package/src/tiles/list/tile-list-compact.ts +7 -3
  54. package/src/tiles/list/tile-list.ts +6 -1
  55. package/src/tiles/mediatype-icon.ts +2 -0
  56. package/test/collection-browser.test.ts +169 -3
  57. package/test/collection-facets/facets-template.test.ts +5 -3
  58. package/test/mocks/mock-search-responses.ts +107 -0
  59. package/test/mocks/mock-search-service.ts +16 -8
  60. package/test/restoration-state-handler.test.ts +12 -0
  61. package/test/tiles/list/tile-list-compact.test.ts +110 -0
  62. package/test/tiles/list/tile-list.test.ts +36 -0
package/src/app-root.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  } from '@internetarchive/analytics-manager';
6
6
  import {
7
7
  SearchService,
8
+ SearchServiceInterface,
8
9
  SearchType,
9
10
  StringField,
10
11
  } from '@internetarchive/search-service';
@@ -22,7 +23,8 @@ import '../src/collection-browser';
22
23
 
23
24
  @customElement('app-root')
24
25
  export class AppRoot extends LitElement {
25
- private searchService = SearchService.default;
26
+ private searchService: SearchServiceInterface =
27
+ this.initSearchServiceFromUrlParams();
26
28
 
27
29
  private resizeObserver = new SharedResizeObserver();
28
30
 
@@ -73,6 +75,15 @@ export class AppRoot extends LitElement {
73
75
  this.analyticsManager?.sendEventNoSampling(ae);
74
76
  }
75
77
 
78
+ private initSearchServiceFromUrlParams() {
79
+ const params = new URL(window.location.href).searchParams;
80
+ return new SearchService({
81
+ baseUrl: params.get('search_base_url') ?? undefined,
82
+ servicePath: params.get('search_service_path') ?? undefined,
83
+ debuggingEnabled: !!params.get('debugging') ?? undefined,
84
+ });
85
+ }
86
+
76
87
  private searchPressed(e: Event) {
77
88
  e.preventDefault();
78
89
  this.searchQuery = this.baseQueryField.value;
@@ -122,23 +133,28 @@ export class AppRoot extends LitElement {
122
133
 
123
134
  <div id="search-types">
124
135
  Search type:
125
- <input
126
- type="radio"
127
- id="metadata-search"
128
- name="search-type"
129
- value="metadata"
130
- checked
131
- @click=${this.searchTypeChanged}
132
- />
133
- <label for="metadata-search">Metadata</label>
134
- <input
135
- type="radio"
136
- id="fulltext-search"
137
- name="search-type"
138
- value="fulltext"
139
- @click=${this.searchTypeChanged}
140
- />
141
- <label for="fulltext-search">Full text</label>
136
+ <span class="search-type">
137
+ <input
138
+ type="radio"
139
+ id="metadata-search"
140
+ name="search-type"
141
+ value="metadata"
142
+ ?checked=${this.searchType === SearchType.METADATA}
143
+ @click=${this.searchTypeSelected}
144
+ />
145
+ <label for="metadata-search">Metadata</label>
146
+ </span>
147
+ <span class="search-type">
148
+ <input
149
+ type="radio"
150
+ id="fulltext-search"
151
+ name="search-type"
152
+ value="fulltext"
153
+ ?checked=${this.searchType === SearchType.FULLTEXT}
154
+ @click=${this.searchTypeSelected}
155
+ />
156
+ <label for="fulltext-search">Full text</label>
157
+ </span>
142
158
  </div>
143
159
 
144
160
  <div id="toggle-controls">
@@ -176,7 +192,7 @@ export class AppRoot extends LitElement {
176
192
  <div id="cell-controls" class="hidden">
177
193
  <div id="cell-size-control">
178
194
  <div>
179
- <label for="cell-width-slider">Minimum cell width:</label>
195
+ <label for="cell-width-slider">Min cell width:</label>
180
196
  <input
181
197
  type="range"
182
198
  min="10"
@@ -201,40 +217,6 @@ export class AppRoot extends LitElement {
201
217
  />
202
218
  <span>${this.cellHeight}rem</span>
203
219
  </div>
204
- <div>
205
- <label for="show-outline-check">Show outlines:</label>
206
- <input
207
- type="checkbox"
208
- id="show-outline-check"
209
- @click=${this.outlineChanged}
210
- />
211
- </div>
212
- <div>
213
- <label for="show-facet-group-outline-check"
214
- >Show Facet Group Outlines:</label
215
- >
216
- <input
217
- type="checkbox"
218
- id="show-facet-group-outline-check"
219
- @click=${this.toggleFacetGroupOutline}
220
- />
221
- </div>
222
- <div>
223
- <label for="simulate-login">Simulate Login:</label>
224
- <input
225
- type="checkbox"
226
- id="simulate-login"
227
- @click=${this.loginChanged}
228
- />
229
- </div>
230
- <div>
231
- <label for="show-dummy-snippets">Show dummy snippets:</label>
232
- <input
233
- type="checkbox"
234
- id="show-dummy-snippets"
235
- @click=${this.snippetsChanged}
236
- />
237
- </div>
238
220
  </div>
239
221
  <div id="cell-gap-control">
240
222
  <div>
@@ -265,6 +247,42 @@ export class AppRoot extends LitElement {
265
247
  </div>
266
248
  </div>
267
249
  </div>
250
+ <div id="checkbox-controls">
251
+ <div class="checkbox-control">
252
+ <input
253
+ type="checkbox"
254
+ id="show-outline-check"
255
+ @click=${this.outlineChanged}
256
+ />
257
+ <label for="show-outline-check">Show cell outlines</label>
258
+ </div>
259
+ <div class="checkbox-control">
260
+ <input
261
+ type="checkbox"
262
+ id="show-facet-group-outline-check"
263
+ @click=${this.toggleFacetGroupOutline}
264
+ />
265
+ <label for="show-facet-group-outline-check">
266
+ Show facet group outlines
267
+ </label>
268
+ </div>
269
+ <div class="checkbox-control">
270
+ <input
271
+ type="checkbox"
272
+ id="simulate-login"
273
+ @click=${this.loginChanged}
274
+ />
275
+ <label for="simulate-login">Simulate login</label>
276
+ </div>
277
+ <div class="checkbox-control">
278
+ <input
279
+ type="checkbox"
280
+ id="show-dummy-snippets"
281
+ @click=${this.snippetsChanged}
282
+ />
283
+ <label for="show-dummy-snippets">Show dummy snippets</label>
284
+ </div>
285
+ </div>
268
286
  </div>
269
287
 
270
288
  <div id="collection-browser-container">
@@ -281,6 +299,7 @@ export class AppRoot extends LitElement {
281
299
  .analyticsHandler=${this.analyticsHandler}
282
300
  @visiblePageChanged=${this.visiblePageChanged}
283
301
  @baseQueryChanged=${this.baseQueryChanged}
302
+ @searchTypeChanged=${this.searchTypeChanged}
284
303
  >
285
304
  </collection-browser>
286
305
  </div>
@@ -288,11 +307,17 @@ export class AppRoot extends LitElement {
288
307
  `;
289
308
  }
290
309
 
291
- private baseQueryChanged(e: CustomEvent<{ baseQuery?: string }>) {
310
+ private baseQueryChanged(e: CustomEvent<{ baseQuery?: string }>): void {
292
311
  this.searchQuery = e.detail.baseQuery;
293
312
  }
294
313
 
295
- private searchTypeChanged(e: Event) {
314
+ /** Handler for search type changes coming from collection browser */
315
+ private searchTypeChanged(e: CustomEvent<SearchType>): void {
316
+ this.searchType = e.detail;
317
+ }
318
+
319
+ /** Handler for user input selecting a search type */
320
+ private searchTypeSelected(e: Event) {
296
321
  const target = e.target as HTMLInputElement;
297
322
  this.searchType =
298
323
  target.value === 'fulltext' ? SearchType.FULLTEXT : SearchType.METADATA;
@@ -494,8 +519,17 @@ export class AppRoot extends LitElement {
494
519
  display: flex;
495
520
  }
496
521
 
522
+ #search-and-page-inputs > form {
523
+ margin-right: 1rem;
524
+ }
525
+
526
+ .search-type {
527
+ margin-right: 1rem;
528
+ }
529
+
497
530
  #cell-controls {
498
531
  display: flex;
532
+ flex-wrap: wrap;
499
533
  }
500
534
 
501
535
  #cell-controls label {
@@ -503,10 +537,25 @@ export class AppRoot extends LitElement {
503
537
  width: 10rem;
504
538
  }
505
539
 
540
+ #cell-size-control,
541
+ #cell-gap-control {
542
+ flex-basis: calc(50% - 1rem);
543
+ flex-grow: 1;
544
+ }
545
+
506
546
  #cell-gap-control {
507
547
  margin-left: 1rem;
508
548
  }
509
549
 
550
+ #checkbox-controls {
551
+ padding-top: 0.5rem;
552
+ flex-wrap: wrap;
553
+ }
554
+
555
+ .checkbox-control {
556
+ flex-basis: 50%;
557
+ }
558
+
510
559
  #last-event {
511
560
  background-color: aliceblue;
512
561
  padding: 5px;
@@ -155,8 +155,6 @@ export class CollectionBrowser
155
155
 
156
156
  @state() private facetsLoading = false;
157
157
 
158
- @state() private lendingFacetLoading = false;
159
-
160
158
  @state() private fullYearAggregationLoading = false;
161
159
 
162
160
  @state() private aggregations?: Record<string, Aggregation>;
@@ -306,7 +304,10 @@ export class CollectionBrowser
306
304
  this.placeholderType = 'empty-query';
307
305
  }
308
306
 
309
- if (!this.searchResultsLoading && this.totalResults === 0) {
307
+ if (
308
+ (!this.searchResultsLoading && this.totalResults === 0) ||
309
+ !this.searchService
310
+ ) {
310
311
  this.placeholderType = 'null-result';
311
312
  }
312
313
  }
@@ -348,7 +349,6 @@ export class CollectionBrowser
348
349
  </div>
349
350
  </div>
350
351
  <div id="right-column" class="column">
351
- ${this.searchResultsLoading ? this.loadingTemplate : nothing}
352
352
  ${this.sortFilterBarTemplate}
353
353
  ${this.displayMode === `list-compact`
354
354
  ? this.listHeaderTemplate
@@ -499,11 +499,7 @@ export class CollectionBrowser
499
499
  }
500
500
 
501
501
  private get facetDataLoading(): boolean {
502
- return (
503
- this.facetsLoading ||
504
- this.lendingFacetLoading ||
505
- this.fullYearAggregationLoading
506
- );
502
+ return this.facetsLoading || this.fullYearAggregationLoading;
507
503
  }
508
504
 
509
505
  private get mobileFacetsTemplate() {
@@ -530,7 +526,6 @@ export class CollectionBrowser
530
526
 
531
527
  private get facetsTemplate() {
532
528
  return html`
533
- ${this.facetsLoading ? this.loadingTemplate : nothing}
534
529
  <collection-facets
535
530
  @facetsChanged=${this.facetsChanged}
536
531
  @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
@@ -630,6 +625,9 @@ export class CollectionBrowser
630
625
  if (changed.has('baseQuery')) {
631
626
  this.emitBaseQueryChanged();
632
627
  }
628
+ if (changed.has('searchType')) {
629
+ this.emitSearchTypeChanged();
630
+ }
633
631
  if (changed.has('currentPage') || changed.has('displayMode')) {
634
632
  this.persistState();
635
633
  }
@@ -705,6 +703,14 @@ export class CollectionBrowser
705
703
  );
706
704
  }
707
705
 
706
+ private emitSearchTypeChanged() {
707
+ this.dispatchEvent(
708
+ new CustomEvent<SearchType>('searchTypeChanged', {
709
+ detail: this.searchType,
710
+ })
711
+ );
712
+ }
713
+
708
714
  private disconnectResizeObserver(
709
715
  resizeObserver: SharedResizeObserverInterface
710
716
  ) {
@@ -775,20 +781,15 @@ export class CollectionBrowser
775
781
  this.initialQueryChangeHappened = true;
776
782
  // if the query changed as part of a window.history pop event, we don't want to
777
783
  // persist the state because it overwrites the forward history
784
+
778
785
  if (!this.historyPopOccurred) {
779
786
  this.persistState();
780
787
  this.historyPopOccurred = false;
781
788
  }
782
789
 
783
- // Ensure lending aggregations don't carry over to non-metadata searches
784
- if (this.searchType !== SearchType.METADATA) {
785
- delete this.aggregations?.lending;
786
- }
787
-
788
790
  await Promise.all([
789
791
  this.doInitialPageFetch(),
790
792
  this.fetchFacets(),
791
- this.fetchLendingFacet(),
792
793
  this.fetchFullYearHistogram(),
793
794
  ]);
794
795
  }
@@ -810,6 +811,8 @@ export class CollectionBrowser
810
811
  private restoreState() {
811
812
  const restorationState = this.restorationStateHandler.getRestorationState();
812
813
  this.displayMode = restorationState.displayMode;
814
+ if (restorationState.searchType != null)
815
+ this.searchType = restorationState.searchType;
813
816
  this.selectedSort = restorationState.selectedSort ?? SortField.relevance;
814
817
  this.sortDirection = restorationState.sortDirection ?? null;
815
818
  this.selectedTitleFilter = restorationState.selectedTitleFilter ?? null;
@@ -831,6 +834,7 @@ export class CollectionBrowser
831
834
  private persistState() {
832
835
  const restorationState: RestorationState = {
833
836
  displayMode: this.displayMode,
837
+ searchType: this.searchType,
834
838
  sortParam: this.sortParam ?? undefined,
835
839
  selectedSort: this.selectedSort,
836
840
  sortDirection: this.sortDirection ?? undefined,
@@ -957,34 +961,7 @@ export class CollectionBrowser
957
961
  const results = await this.searchService?.search(params, this.searchType);
958
962
  this.facetsLoading = false;
959
963
 
960
- this.aggregations = {
961
- ...this.aggregations,
962
- ...results?.success?.response.aggregations,
963
- };
964
- }
965
-
966
- private async fetchLendingFacet() {
967
- // Only retrieve lending facet for metadata searches
968
- if (this.searchType !== SearchType.METADATA) return;
969
- if (!this.fullQuery) return;
970
-
971
- const params: SearchParams = {
972
- query: this.fullQuery,
973
- rows: 0,
974
- aggregations: {
975
- simpleParams: ['lending___status'],
976
- },
977
- aggregationsSize: 10, // Larger size to ensure we get all possible statuses
978
- };
979
-
980
- this.lendingFacetLoading = true;
981
- const results = await this.searchService?.search(params, this.searchType);
982
- this.lendingFacetLoading = false;
983
-
984
- this.aggregations = {
985
- ...this.aggregations,
986
- ...results?.success?.response.aggregations,
987
- };
964
+ this.aggregations = results?.success?.response.aggregations;
988
965
  }
989
966
 
990
967
  /**
@@ -1037,7 +1014,8 @@ export class CollectionBrowser
1037
1014
  this.fullYearAggregationLoading = false;
1038
1015
 
1039
1016
  this.fullYearsHistogramAggregation =
1040
- results?.success?.response?.aggregations?.year_histogram;
1017
+ results?.success?.response?.aggregations?.year_histogram ??
1018
+ results?.success?.response?.aggregations?.['year-histogram']; // Temp fix until PPS FTS key is fixed to use underscore
1041
1019
  }
1042
1020
 
1043
1021
  private scrollToPage(pageNumber: number) {
@@ -1233,6 +1211,7 @@ export class CollectionBrowser
1233
1211
  ),
1234
1212
  volume: result.volume?.value,
1235
1213
  viewCount: result.downloads?.value ?? 0,
1214
+ weeklyViewCount: result.week?.value,
1236
1215
  loginRequired,
1237
1216
  contentWarning,
1238
1217
  });
@@ -1423,7 +1402,7 @@ export class CollectionBrowser
1423
1402
 
1424
1403
  #results-total {
1425
1404
  display: flex;
1426
- align-items: center;
1405
+ align-items: baseline;
1427
1406
  margin-bottom: 5rem;
1428
1407
  }
1429
1408
 
@@ -1438,9 +1417,8 @@ export class CollectionBrowser
1438
1417
  }
1439
1418
 
1440
1419
  #big-results-label {
1441
- font-size: 1rem;
1420
+ font-size: 1.4rem;
1442
1421
  font-weight: 200;
1443
- text-transform: uppercase;
1444
1422
  }
1445
1423
 
1446
1424
  #list-header {
@@ -0,0 +1,40 @@
1
+ import { css, html, LitElement, CSSResultGroup } from 'lit';
2
+ import { customElement } from 'lit/decorators.js';
3
+
4
+ @customElement('facet-tombstone-row')
5
+ export class FacetTombstoneRow extends LitElement {
6
+ render() {
7
+ return html`
8
+ <div id="row">
9
+ <input type="checkbox" disabled />
10
+ <div class="tombstone-line"></div>
11
+ <div class="tombstone-line"></div>
12
+ </div>
13
+ `;
14
+ }
15
+
16
+ static get styles(): CSSResultGroup {
17
+ return css`
18
+ #row {
19
+ display: grid;
20
+ grid-template-columns: 15px 1fr 36px;
21
+ grid-gap: 9px 6px;
22
+ align-items: center;
23
+ margin: 2.5px auto;
24
+ border: 1px solid transparent;
25
+ }
26
+
27
+ .tombstone-line {
28
+ background: #ddd;
29
+ height: 6px;
30
+ border-radius: 50px;
31
+ }
32
+
33
+ input[type='checkbox'] {
34
+ width: 15px;
35
+ height: 15px;
36
+ margin: 0;
37
+ }
38
+ `;
39
+ }
40
+ }
@@ -168,14 +168,16 @@ export class FacetsTemplate extends LitElement {
168
168
  <span class="eye-closed">${eyeClosedIcon}</span>
169
169
  </label>
170
170
  </div>
171
- <div
171
+ <label
172
172
  for=${showOnlyCheckboxId}
173
173
  class="facet-info-display"
174
174
  title=${onlyShowText}
175
175
  >
176
176
  <div class="facet-title">${bucketTextDisplay}</div>
177
- <div class="facet-count">${bucket.count}</div>
178
- </div>
177
+ <div class="facet-count">
178
+ ${bucket.count.toLocaleString()}
179
+ </div>
180
+ </label>
179
181
  </div>
180
182
  `;
181
183
  }
@@ -8,6 +8,7 @@ import {
8
8
  TemplateResult,
9
9
  } from 'lit';
10
10
  import { customElement, property, state } from 'lit/decorators.js';
11
+ import { map } from 'lit/directives/map.js';
11
12
  import type {
12
13
  Aggregation,
13
14
  Bucket,
@@ -39,6 +40,7 @@ import {
39
40
  import type { LanguageCodeHandlerInterface } from './language-code-handler/language-code-handler';
40
41
  import './collection-facets/more-facets-content';
41
42
  import './collection-facets/facets-template';
43
+ import './collection-facets/facet-tombstone-row';
42
44
  import {
43
45
  analyticsActions,
44
46
  analyticsCategories,
@@ -111,7 +113,9 @@ export class CollectionFacets extends LitElement {
111
113
  render() {
112
114
  return html`
113
115
  <div id="container" class="${this.facetsLoading ? 'loading' : ''}">
114
- ${this.showHistogramDatePicker && this.fullYearsHistogramAggregation
116
+ ${(this.showHistogramDatePicker &&
117
+ this.fullYearsHistogramAggregation) ||
118
+ this.fullYearAggregationLoading
115
119
  ? html`
116
120
  <div class="facet-group">
117
121
  <h1>Year Published <feature-feedback></feature-feedback></h1>
@@ -146,19 +150,21 @@ export class CollectionFacets extends LitElement {
146
150
 
147
151
  private get histogramTemplate() {
148
152
  const { fullYearsHistogramAggregation } = this;
149
- return html`
150
- <histogram-date-range
151
- .minDate=${fullYearsHistogramAggregation?.first_bucket_key}
152
- .maxDate=${fullYearsHistogramAggregation?.last_bucket_key}
153
- .minSelectedDate=${this.minSelectedDate}
154
- .maxSelectedDate=${this.maxSelectedDate}
155
- .updateDelay=${100}
156
- missingDataMessage="..."
157
- .width=${180}
158
- .bins=${fullYearsHistogramAggregation?.buckets as number[]}
159
- @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
160
- ></histogram-date-range>
161
- `;
153
+ return this.fullYearAggregationLoading
154
+ ? html`<div class="histogram-loading-indicator">&hellip;</div>` // Ellipsis block
155
+ : html`
156
+ <histogram-date-range
157
+ .minDate=${fullYearsHistogramAggregation?.first_bucket_key}
158
+ .maxDate=${fullYearsHistogramAggregation?.last_bucket_key}
159
+ .minSelectedDate=${this.minSelectedDate}
160
+ .maxSelectedDate=${this.maxSelectedDate}
161
+ .updateDelay=${100}
162
+ missingDataMessage="..."
163
+ .width=${180}
164
+ .bins=${fullYearsHistogramAggregation?.buckets as number[]}
165
+ @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
166
+ ></histogram-date-range>
167
+ `;
162
168
  }
163
169
 
164
170
  private histogramDateRangeUpdated(
@@ -366,7 +372,8 @@ export class CollectionFacets extends LitElement {
366
372
  private getFacetGroupTemplate(
367
373
  facetGroup: FacetGroup
368
374
  ): TemplateResult | typeof nothing {
369
- if (facetGroup.buckets.length === 0) return nothing;
375
+ if (!this.facetsLoading && facetGroup.buckets.length === 0) return nothing;
376
+
370
377
  const { key } = facetGroup;
371
378
  const isOpen = this.openFacets[key];
372
379
  const collapser = html`
@@ -390,16 +397,32 @@ export class CollectionFacets extends LitElement {
390
397
  >
391
398
  ${this.collapsableFacets ? collapser : nothing} ${facetGroup.title}
392
399
  </h1>
393
- ${this.moreFacetsSortingIcon(facetGroup)}
400
+ ${this.facetsLoading
401
+ ? nothing
402
+ : this.moreFacetsSortingIcon(facetGroup)}
394
403
  </div>
395
404
  <div class="facet-group-content ${isOpen ? 'open' : ''}">
396
- ${this.getFacetTemplate(facetGroup)}
397
- ${this.searchMoreFacetsLink(facetGroup)}
405
+ ${this.facetsLoading
406
+ ? this.getTombstoneFacetGroupTemplate()
407
+ : html`
408
+ ${this.getFacetTemplate(facetGroup)}
409
+ ${this.searchMoreFacetsLink(facetGroup)}
410
+ `}
398
411
  </div>
399
412
  </div>
400
413
  `;
401
414
  }
402
415
 
416
+ private getTombstoneFacetGroupTemplate(): TemplateResult {
417
+ // Render five tombstone rows
418
+ return html`
419
+ ${map(
420
+ Array(5).fill(null),
421
+ () => html`<facet-tombstone-row></facet-tombstone-row>`
422
+ )}
423
+ `;
424
+ }
425
+
403
426
  private moreFacetsSortingIcon(
404
427
  facetGroup: FacetGroup
405
428
  ): TemplateResult | typeof nothing {
@@ -531,6 +554,14 @@ export class CollectionFacets extends LitElement {
531
554
  opacity: 0.5;
532
555
  }
533
556
 
557
+ .histogram-loading-indicator {
558
+ width: 100%;
559
+ height: 2.25rem;
560
+ margin-top: 1.75rem;
561
+ font-size: 1.4rem;
562
+ text-align: center;
563
+ }
564
+
534
565
  .collapser {
535
566
  display: inline-block;
536
567
  cursor: pointer;
package/src/models.ts CHANGED
@@ -24,6 +24,7 @@ export interface TileModel {
24
24
  title: string;
25
25
  viewCount: number;
26
26
  volume?: string;
27
+ weeklyViewCount?: number;
27
28
  loginRequired: boolean;
28
29
  contentWarning: boolean;
29
30
  }
@@ -1,4 +1,8 @@
1
- import type { SortDirection, SortParam } from '@internetarchive/search-service';
1
+ import {
2
+ SearchType,
3
+ SortDirection,
4
+ SortParam,
5
+ } from '@internetarchive/search-service';
2
6
  import { getCookie, setCookie } from 'typescript-cookie';
3
7
  import {
4
8
  MetadataFieldToSortField,
@@ -14,6 +18,7 @@ import {
14
18
 
15
19
  export interface RestorationState {
16
20
  displayMode?: CollectionDisplayMode;
21
+ searchType?: SearchType;
17
22
  sortParam?: SortParam;
18
23
  selectedSort?: SortField;
19
24
  sortDirection?: SortDirection;
@@ -87,12 +92,20 @@ export class RestorationStateHandler
87
92
  private persistQueryStateToUrl(state: RestorationState) {
88
93
  const url = new URL(window.location.href);
89
94
  const { searchParams } = url;
95
+ searchParams.delete('sin');
90
96
  searchParams.delete('sort');
91
97
  searchParams.delete('query');
92
98
  searchParams.delete('page');
93
99
  searchParams.delete('and[]');
94
100
  searchParams.delete('not[]');
95
101
 
102
+ if (state.searchType) {
103
+ searchParams.set(
104
+ 'sin',
105
+ state.searchType === SearchType.FULLTEXT ? 'TXT' : ''
106
+ );
107
+ }
108
+
96
109
  if (state.sortParam) {
97
110
  const prefix = state.sortParam.direction === 'desc' ? '-' : '';
98
111
  searchParams.set('sort', `${prefix}${state.sortParam.field}`);
@@ -155,6 +168,7 @@ export class RestorationStateHandler
155
168
 
156
169
  private loadQueryStateFromUrl(): RestorationState {
157
170
  const url = new URL(window.location.href);
171
+ const searchInside = url.searchParams.get('sin');
158
172
  const pageNumber = url.searchParams.get('page');
159
173
  const searchQuery = url.searchParams.get('query');
160
174
  const sortQuery = url.searchParams.get('sort');
@@ -173,6 +187,10 @@ export class RestorationStateHandler
173
187
  },
174
188
  };
175
189
 
190
+ if (searchInside) {
191
+ restorationState.searchType =
192
+ searchInside === 'TXT' ? SearchType.FULLTEXT : SearchType.METADATA;
193
+ }
176
194
  if (pageNumber) {
177
195
  const parsed = parseInt(pageNumber, 10);
178
196
  restorationState.currentPage = parsed;