@internetarchive/collection-browser 0.4.11 → 0.4.12

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 (251) hide show
  1. package/.editorconfig +29 -29
  2. package/.github/workflows/ci.yml +26 -26
  3. package/.github/workflows/gh-pages-main.yml +39 -39
  4. package/.github/workflows/npm-publish.yml +39 -39
  5. package/.github/workflows/pr-preview.yml +38 -38
  6. package/.husky/pre-commit +4 -4
  7. package/LICENSE +661 -661
  8. package/README.md +83 -83
  9. package/dist/index.d.ts +9 -9
  10. package/dist/index.js +9 -9
  11. package/dist/src/app-root.d.ts +50 -50
  12. package/dist/src/app-root.js +278 -278
  13. package/dist/src/assets/img/icons/arrow-left.d.ts +2 -2
  14. package/dist/src/assets/img/icons/arrow-left.js +2 -2
  15. package/dist/src/assets/img/icons/arrow-right.d.ts +2 -2
  16. package/dist/src/assets/img/icons/arrow-right.js +2 -2
  17. package/dist/src/assets/img/icons/chevron.d.ts +2 -2
  18. package/dist/src/assets/img/icons/chevron.js +2 -2
  19. package/dist/src/assets/img/icons/empty-query.d.ts +2 -2
  20. package/dist/src/assets/img/icons/empty-query.js +2 -2
  21. package/dist/src/assets/img/icons/eye-closed.d.ts +2 -2
  22. package/dist/src/assets/img/icons/eye-closed.js +2 -2
  23. package/dist/src/assets/img/icons/eye.d.ts +2 -2
  24. package/dist/src/assets/img/icons/eye.js +2 -2
  25. package/dist/src/assets/img/icons/favorite-filled.d.ts +1 -1
  26. package/dist/src/assets/img/icons/favorite-filled.js +2 -2
  27. package/dist/src/assets/img/icons/login-required.d.ts +1 -1
  28. package/dist/src/assets/img/icons/login-required.js +2 -2
  29. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -1
  30. package/dist/src/assets/img/icons/mediatype/account.js +2 -2
  31. package/dist/src/assets/img/icons/mediatype/audio.d.ts +1 -1
  32. package/dist/src/assets/img/icons/mediatype/audio.js +2 -2
  33. package/dist/src/assets/img/icons/mediatype/collection.d.ts +1 -1
  34. package/dist/src/assets/img/icons/mediatype/collection.js +2 -2
  35. package/dist/src/assets/img/icons/mediatype/data.d.ts +1 -1
  36. package/dist/src/assets/img/icons/mediatype/data.js +2 -2
  37. package/dist/src/assets/img/icons/mediatype/etree.d.ts +1 -1
  38. package/dist/src/assets/img/icons/mediatype/etree.js +2 -2
  39. package/dist/src/assets/img/icons/mediatype/film.d.ts +1 -1
  40. package/dist/src/assets/img/icons/mediatype/film.js +2 -2
  41. package/dist/src/assets/img/icons/mediatype/images.d.ts +1 -1
  42. package/dist/src/assets/img/icons/mediatype/images.js +2 -2
  43. package/dist/src/assets/img/icons/mediatype/radio.d.ts +1 -1
  44. package/dist/src/assets/img/icons/mediatype/radio.js +2 -2
  45. package/dist/src/assets/img/icons/mediatype/software.d.ts +1 -1
  46. package/dist/src/assets/img/icons/mediatype/software.js +2 -2
  47. package/dist/src/assets/img/icons/mediatype/texts.d.ts +1 -1
  48. package/dist/src/assets/img/icons/mediatype/texts.js +2 -2
  49. package/dist/src/assets/img/icons/mediatype/tv.d.ts +1 -1
  50. package/dist/src/assets/img/icons/mediatype/tv.js +2 -2
  51. package/dist/src/assets/img/icons/mediatype/video.d.ts +1 -1
  52. package/dist/src/assets/img/icons/mediatype/video.js +2 -2
  53. package/dist/src/assets/img/icons/mediatype/web.d.ts +1 -1
  54. package/dist/src/assets/img/icons/mediatype/web.js +2 -2
  55. package/dist/src/assets/img/icons/null-result.d.ts +2 -2
  56. package/dist/src/assets/img/icons/null-result.js +2 -2
  57. package/dist/src/assets/img/icons/restricted.d.ts +1 -1
  58. package/dist/src/assets/img/icons/restricted.js +2 -2
  59. package/dist/src/assets/img/icons/reviews.d.ts +1 -1
  60. package/dist/src/assets/img/icons/reviews.js +2 -2
  61. package/dist/src/assets/img/icons/upload.d.ts +1 -1
  62. package/dist/src/assets/img/icons/upload.js +2 -2
  63. package/dist/src/assets/img/icons/views.d.ts +1 -1
  64. package/dist/src/assets/img/icons/views.js +2 -2
  65. package/dist/src/circular-activity-indicator.d.ts +5 -5
  66. package/dist/src/circular-activity-indicator.js +17 -17
  67. package/dist/src/collection-browser.d.ts +280 -279
  68. package/dist/src/collection-browser.js +1175 -1156
  69. package/dist/src/collection-browser.js.map +1 -1
  70. package/dist/src/collection-facets/facet-tombstone-row.d.ts +5 -5
  71. package/dist/src/collection-facets/facet-tombstone-row.js +15 -15
  72. package/dist/src/collection-facets/facets-template.d.ts +16 -16
  73. package/dist/src/collection-facets/facets-template.js +125 -125
  74. package/dist/src/collection-facets/more-facets-content.d.ts +76 -75
  75. package/dist/src/collection-facets/more-facets-content.js +354 -377
  76. package/dist/src/collection-facets/more-facets-content.js.map +1 -1
  77. package/dist/src/collection-facets/more-facets-pagination.d.ts +36 -36
  78. package/dist/src/collection-facets/more-facets-pagination.js +197 -195
  79. package/dist/src/collection-facets/more-facets-pagination.js.map +1 -1
  80. package/dist/src/collection-facets/toggle-switch.d.ts +41 -0
  81. package/dist/src/collection-facets/toggle-switch.js +184 -0
  82. package/dist/src/collection-facets/toggle-switch.js.map +1 -0
  83. package/dist/src/collection-facets.d.ts +81 -81
  84. package/dist/src/collection-facets.js +375 -375
  85. package/dist/src/empty-placeholder.d.ts +13 -11
  86. package/dist/src/empty-placeholder.js +80 -44
  87. package/dist/src/empty-placeholder.js.map +1 -1
  88. package/dist/src/language-code-handler/language-code-handler.d.ts +37 -37
  89. package/dist/src/language-code-handler/language-code-handler.js +26 -26
  90. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -1
  91. package/dist/src/language-code-handler/language-code-mapping.js +562 -562
  92. package/dist/src/mediatype/mediatype-config.d.ts +3 -3
  93. package/dist/src/mediatype/mediatype-config.js +85 -85
  94. package/dist/src/models.d.ts +115 -115
  95. package/dist/src/models.js +125 -125
  96. package/dist/src/restoration-state-handler.d.ts +45 -45
  97. package/dist/src/restoration-state-handler.js +230 -230
  98. package/dist/src/sort-filter-bar/alpha-bar-tooltip.d.ts +6 -6
  99. package/dist/src/sort-filter-bar/alpha-bar-tooltip.js +24 -24
  100. package/dist/src/sort-filter-bar/alpha-bar.d.ts +20 -20
  101. package/dist/src/sort-filter-bar/alpha-bar.js +128 -128
  102. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -1
  103. package/dist/src/sort-filter-bar/img/compact.js +2 -2
  104. package/dist/src/sort-filter-bar/img/list.d.ts +1 -1
  105. package/dist/src/sort-filter-bar/img/list.js +2 -2
  106. package/dist/src/sort-filter-bar/img/sort-triangle.d.ts +1 -1
  107. package/dist/src/sort-filter-bar/img/sort-triangle.js +2 -2
  108. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -1
  109. package/dist/src/sort-filter-bar/img/tile.js +2 -2
  110. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +108 -108
  111. package/dist/src/sort-filter-bar/sort-filter-bar.js +438 -438
  112. package/dist/src/styles/item-image-styles.d.ts +8 -8
  113. package/dist/src/styles/item-image-styles.js +9 -9
  114. package/dist/src/tiles/collection-browser-loading-tile.d.ts +5 -5
  115. package/dist/src/tiles/collection-browser-loading-tile.js +15 -15
  116. package/dist/src/tiles/grid/account-tile.d.ts +20 -20
  117. package/dist/src/tiles/grid/account-tile.js +64 -64
  118. package/dist/src/tiles/grid/collection-tile.d.ts +17 -17
  119. package/dist/src/tiles/grid/collection-tile.js +71 -71
  120. package/dist/src/tiles/grid/item-tile.d.ts +32 -32
  121. package/dist/src/tiles/grid/item-tile.js +122 -122
  122. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.d.ts +1 -1
  123. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.js +7 -7
  124. package/dist/src/tiles/grid/tile-stats.d.ts +10 -10
  125. package/dist/src/tiles/grid/tile-stats.js +40 -40
  126. package/dist/src/tiles/hover/hover-pane-controller.d.ts +219 -219
  127. package/dist/src/tiles/hover/hover-pane-controller.js +352 -352
  128. package/dist/src/tiles/hover/tile-hover-pane.d.ts +15 -15
  129. package/dist/src/tiles/hover/tile-hover-pane.js +38 -38
  130. package/dist/src/tiles/image-block.d.ts +17 -17
  131. package/dist/src/tiles/image-block.js +72 -72
  132. package/dist/src/tiles/item-image.d.ts +35 -35
  133. package/dist/src/tiles/item-image.js +117 -117
  134. package/dist/src/tiles/list/account-label.d.ts +1 -1
  135. package/dist/src/tiles/list/account-label.js +6 -6
  136. package/dist/src/tiles/list/date-label.d.ts +1 -1
  137. package/dist/src/tiles/list/date-label.js +12 -12
  138. package/dist/src/tiles/list/tile-list-compact-header.d.ts +12 -12
  139. package/dist/src/tiles/list/tile-list-compact-header.js +41 -41
  140. package/dist/src/tiles/list/tile-list-compact.d.ts +22 -21
  141. package/dist/src/tiles/list/tile-list-compact.js +114 -101
  142. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  143. package/dist/src/tiles/list/tile-list.d.ts +55 -55
  144. package/dist/src/tiles/list/tile-list.js +304 -301
  145. package/dist/src/tiles/list/tile-list.js.map +1 -1
  146. package/dist/src/tiles/mediatype-icon.d.ts +9 -9
  147. package/dist/src/tiles/mediatype-icon.js +47 -47
  148. package/dist/src/tiles/overlay/icon-overlay.d.ts +10 -10
  149. package/dist/src/tiles/overlay/icon-overlay.js +40 -40
  150. package/dist/src/tiles/overlay/icon-text-overlay.d.ts +9 -9
  151. package/dist/src/tiles/overlay/icon-text-overlay.js +38 -38
  152. package/dist/src/tiles/overlay/text-overlay.d.ts +10 -10
  153. package/dist/src/tiles/overlay/text-overlay.js +42 -42
  154. package/dist/src/tiles/text-snippet-block.d.ts +27 -27
  155. package/dist/src/tiles/text-snippet-block.js +73 -73
  156. package/dist/src/tiles/tile-dispatcher.d.ts +58 -58
  157. package/dist/src/tiles/tile-dispatcher.js +194 -194
  158. package/dist/src/utils/analytics-events.d.ts +22 -22
  159. package/dist/src/utils/analytics-events.js +24 -24
  160. package/dist/src/utils/format-count.d.ts +7 -7
  161. package/dist/src/utils/format-count.js +76 -76
  162. package/dist/src/utils/format-date.d.ts +2 -2
  163. package/dist/src/utils/format-date.js +23 -23
  164. package/dist/src/utils/format-unit-size.d.ts +2 -2
  165. package/dist/src/utils/format-unit-size.js +33 -33
  166. package/dist/test/collection-browser.test.d.ts +1 -1
  167. package/dist/test/collection-browser.test.js +584 -584
  168. package/dist/test/collection-facets/facets-template.test.d.ts +1 -1
  169. package/dist/test/collection-facets/facets-template.test.js +62 -62
  170. package/dist/test/collection-facets/more-facets-content.test.d.ts +1 -1
  171. package/dist/test/collection-facets/more-facets-content.test.js +114 -114
  172. package/dist/test/collection-facets/more-facets-pagination.test.d.ts +1 -1
  173. package/dist/test/collection-facets/more-facets-pagination.test.js +117 -117
  174. package/dist/test/collection-facets/toggle-switch.test.d.ts +1 -0
  175. package/dist/test/collection-facets/toggle-switch.test.js +87 -0
  176. package/dist/test/collection-facets/toggle-switch.test.js.map +1 -0
  177. package/dist/test/collection-facets.test.d.ts +2 -2
  178. package/dist/test/collection-facets.test.js +460 -460
  179. package/dist/test/empty-placeholder.test.d.ts +1 -1
  180. package/dist/test/empty-placeholder.test.js +33 -33
  181. package/dist/test/icon-overlay.test.d.ts +1 -1
  182. package/dist/test/icon-overlay.test.js +24 -24
  183. package/dist/test/image-block.test.d.ts +1 -1
  184. package/dist/test/image-block.test.js +48 -48
  185. package/dist/test/item-image.test.d.ts +1 -1
  186. package/dist/test/item-image.test.js +84 -84
  187. package/dist/test/mediatype-config.test.d.ts +1 -1
  188. package/dist/test/mediatype-config.test.js +16 -16
  189. package/dist/test/mocks/mock-analytics-handler.d.ts +10 -10
  190. package/dist/test/mocks/mock-analytics-handler.js +15 -15
  191. package/dist/test/mocks/mock-collection-name-cache.d.ts +7 -7
  192. package/dist/test/mocks/mock-collection-name-cache.js +13 -13
  193. package/dist/test/mocks/mock-search-responses.d.ts +12 -12
  194. package/dist/test/mocks/mock-search-responses.js +341 -341
  195. package/dist/test/mocks/mock-search-service.d.ts +13 -13
  196. package/dist/test/mocks/mock-search-service.js +40 -40
  197. package/dist/test/restoration-state-handler.test.d.ts +1 -1
  198. package/dist/test/restoration-state-handler.test.js +125 -125
  199. package/dist/test/sort-filter-bar/alpha-bar-tooltip.test.d.ts +1 -1
  200. package/dist/test/sort-filter-bar/alpha-bar-tooltip.test.js +12 -12
  201. package/dist/test/sort-filter-bar/alpha-bar.test.d.ts +1 -1
  202. package/dist/test/sort-filter-bar/alpha-bar.test.js +73 -73
  203. package/dist/test/sort-filter-bar/sort-filter-bar.test.d.ts +1 -1
  204. package/dist/test/sort-filter-bar/sort-filter-bar.test.js +197 -197
  205. package/dist/test/text-overlay.test.d.ts +1 -1
  206. package/dist/test/text-overlay.test.js +48 -48
  207. package/dist/test/text-snippet-block.test.d.ts +1 -1
  208. package/dist/test/text-snippet-block.test.js +57 -57
  209. package/dist/test/tile-stats.test.d.ts +1 -1
  210. package/dist/test/tile-stats.test.js +33 -33
  211. package/dist/test/tiles/grid/account-tile.test.d.ts +1 -1
  212. package/dist/test/tiles/grid/account-tile.test.js +76 -76
  213. package/dist/test/tiles/grid/collection-tile.test.d.ts +1 -1
  214. package/dist/test/tiles/grid/collection-tile.test.js +73 -73
  215. package/dist/test/tiles/grid/item-tile.test.d.ts +1 -1
  216. package/dist/test/tiles/grid/item-tile.test.js +158 -158
  217. package/dist/test/tiles/hover/hover-pane-controller.test.d.ts +1 -1
  218. package/dist/test/tiles/hover/hover-pane-controller.test.js +257 -257
  219. package/dist/test/tiles/hover/tile-hover-pane.test.d.ts +1 -1
  220. package/dist/test/tiles/hover/tile-hover-pane.test.js +13 -13
  221. package/dist/test/tiles/list/tile-list-compact.test.d.ts +1 -1
  222. package/dist/test/tiles/list/tile-list-compact.test.js +104 -92
  223. package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -1
  224. package/dist/test/tiles/list/tile-list.test.d.ts +1 -1
  225. package/dist/test/tiles/list/tile-list.test.js +175 -163
  226. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  227. package/dist/test/tiles/tile-dispatcher.test.d.ts +1 -1
  228. package/dist/test/tiles/tile-dispatcher.test.js +67 -67
  229. package/dist/test/utils/format-count.test.d.ts +1 -1
  230. package/dist/test/utils/format-count.test.js +23 -23
  231. package/dist/test/utils/format-date.test.d.ts +1 -1
  232. package/dist/test/utils/format-date.test.js +17 -17
  233. package/dist/test/utils/format-unit-size.test.d.ts +1 -1
  234. package/dist/test/utils/format-unit-size.test.js +17 -17
  235. package/local.archive.org.cert +86 -86
  236. package/local.archive.org.key +27 -27
  237. package/package.json +3 -3
  238. package/renovate.json +6 -6
  239. package/src/collection-browser.ts +25 -1
  240. package/src/collection-facets/more-facets-content.ts +25 -48
  241. package/src/collection-facets/more-facets-pagination.ts +5 -3
  242. package/src/collection-facets/toggle-switch.ts +184 -0
  243. package/src/empty-placeholder.ts +53 -7
  244. package/src/tiles/list/tile-list-compact.ts +15 -2
  245. package/src/tiles/list/tile-list.ts +3 -0
  246. package/test/collection-facets/toggle-switch.test.ts +154 -0
  247. package/test/tiles/list/tile-list-compact.test.ts +14 -0
  248. package/test/tiles/list/tile-list.test.ts +14 -0
  249. package/tsconfig.json +21 -21
  250. package/web-dev-server.config.mjs +30 -30
  251. package/web-test-runner.config.mjs +41 -41
@@ -37,6 +37,7 @@ import {
37
37
  analyticsActions,
38
38
  analyticsCategories,
39
39
  } from '../utils/analytics-events';
40
+ import './toggle-switch';
40
41
 
41
42
  @customElement('more-facets-content')
42
43
  export class MoreFacetsContent extends LitElement {
@@ -59,7 +60,8 @@ export class MoreFacetsContent extends LitElement {
59
60
 
60
61
  @property({ type: Object }) selectedFacets?: SelectedFacets;
61
62
 
62
- @property({ type: String }) sortedBy: 'count' | 'alpha' = 'count';
63
+ @property({ type: String }) sortedBy: AggregationSortType =
64
+ AggregationSortType.COUNT;
63
65
 
64
66
  @property({ type: Object, attribute: false })
65
67
  analyticsHandler?: AnalyticsManagerInterface;
@@ -253,9 +255,7 @@ export class MoreFacetsContent extends LitElement {
253
255
 
254
256
  // sort facets in specific order
255
257
  let castedBuckets = aggregation.getSortedBuckets(
256
- this.sortedBy === 'alpha'
257
- ? AggregationSortType.ALPHABETICAL
258
- : AggregationSortType.COUNT
258
+ this.sortedBy
259
259
  ) as Bucket[];
260
260
 
261
261
  if (option === 'collection') {
@@ -369,28 +369,28 @@ export class MoreFacetsContent extends LitElement {
369
369
  return nothing;
370
370
  }
371
371
 
372
- private sortFacetAggregation() {
373
- this.sortedBy = this.sortedBy === 'count' ? 'alpha' : 'count';
372
+ private sortFacetAggregation(facetSortType: AggregationSortType) {
373
+ this.sortedBy = facetSortType;
374
374
  this.dispatchEvent(
375
375
  new CustomEvent('sortedFacets', { detail: this.sortedBy })
376
376
  );
377
377
  }
378
378
 
379
379
  private get getModalHeaderTemplate(): TemplateResult {
380
- const title = this.sortedBy === 'alpha' ? 'Sort by count' : 'Sort by value';
381
-
382
380
  return html`<span class="sr-only">More facets for:</span>
383
381
  <span class="title">
384
382
  ${this.facetGroupTitle}
385
- <button
386
- class="sort-button"
387
- @click=${(e: Event) => {
388
- e.preventDefault();
389
- this.sortFacetAggregation();
383
+ <label class="sort-label">Sort by:</label>
384
+ <toggle-switch
385
+ class="sort-toggle"
386
+ leftValue=${AggregationSortType.COUNT}
387
+ leftLabel="Count"
388
+ rightValue=${AggregationSortType.ALPHABETICAL}
389
+ rightLabel=${this.facetGroupTitle}
390
+ @change=${(e: CustomEvent<string>) => {
391
+ this.sortFacetAggregation(Number(e.detail) as AggregationSortType);
390
392
  }}
391
- >
392
- ${title}
393
- </button>
393
+ ></toggle-switch>
394
394
  </span>`;
395
395
  }
396
396
 
@@ -447,7 +447,7 @@ export class MoreFacetsContent extends LitElement {
447
447
  }
448
448
  section#more-facets {
449
449
  overflow: auto;
450
- padding: 10px; // leaves room for scroll bar to appear without overlaying on content
450
+ padding: 10px; /* leaves room for scroll bar to appear without overlaying on content */
451
451
  }
452
452
  .header-content .title {
453
453
  display: block;
@@ -456,40 +456,22 @@ export class MoreFacetsContent extends LitElement {
456
456
  padding: 0 10px;
457
457
  font-weight: bold;
458
458
  }
459
- .sort-button {
460
- margin-left: 0.7rem;
461
- padding: 0;
462
- border: none;
463
- background: transparent;
464
- color: var(--ia-theme-link-color, #4b64ff);
459
+
460
+ .sort-label {
461
+ margin-left: 20px;
465
462
  font-size: 1.3rem;
466
- font-weight: normal;
467
- cursor: pointer;
468
463
  }
469
- .sort-button:hover {
470
- text-decoration: underline;
464
+
465
+ .sort-toggle {
466
+ font-weight: normal;
471
467
  }
468
+
472
469
  .facets-content {
473
470
  font-size: 1.2rem;
474
471
  max-height: 300px;
475
472
  overflow: auto;
476
473
  padding: 10px;
477
474
  }
478
- .page-number {
479
- background: none;
480
- border: 0;
481
- cursor: pointer;
482
- border-radius: 100%;
483
- width: 25px;
484
- height: 25px;
485
- margin: 10px;
486
- font-size: 1.4rem;
487
- vertical-align: middle;
488
- }
489
- .current-page {
490
- background: black;
491
- color: white;
492
- }
493
475
  .facets-loader {
494
476
  margin-bottom: 20px;
495
477
  width: 70px;
@@ -506,7 +488,7 @@ export class MoreFacetsContent extends LitElement {
506
488
  cursor: pointer;
507
489
  }
508
490
  .btn-cancel {
509
- background-color: #000;
491
+ background-color: #2c2c2c;
510
492
  color: white;
511
493
  }
512
494
  .btn-submit {
@@ -528,11 +510,6 @@ export class MoreFacetsContent extends LitElement {
528
510
  clip: rect(0, 0, 0, 0);
529
511
  border: 0;
530
512
  }
531
- .sorting-icon {
532
- height: 15px;
533
- vertical-align: baseline;
534
- cursor: pointer;
535
- }
536
513
  `;
537
514
  }
538
515
  }
@@ -257,17 +257,19 @@ export class MoreFacetsPagination extends LitElement {
257
257
  }
258
258
  .facets-pagination .arrow-icon svg {
259
259
  height: 14px;
260
+ fill: #2c2c2c;
260
261
  }
261
262
  .facets-pagination button,
262
263
  .facets-pagination i {
263
264
  background: none;
264
265
  border: 0;
265
266
  cursor: pointer;
266
- border-radius: 100%;
267
+ border-radius: 4px;
267
268
  margin: 10px 5px;
268
269
  padding: 5px;
269
270
  font-size: 1.4rem;
270
- vertical-align: middle;
271
+ color: inherit;
272
+ vertical-align: baseline;
271
273
  display: inline-block;
272
274
  min-width: 2.5rem;
273
275
  }
@@ -276,7 +278,7 @@ export class MoreFacetsPagination extends LitElement {
276
278
  display: inline;
277
279
  }
278
280
  .facets-pagination button.current {
279
- background: black;
281
+ background: #2c2c2c;
280
282
  color: white;
281
283
  }
282
284
  .page-numbers {
@@ -0,0 +1,184 @@
1
+ import { css, html, LitElement, CSSResultGroup } from 'lit';
2
+ import { customElement, property, query } from 'lit/decorators.js';
3
+
4
+ export type Side = 'left' | 'right';
5
+
6
+ @customElement('toggle-switch')
7
+ export class ToggleSwitch extends LitElement {
8
+ /**
9
+ * The value this switch should have when toggled to the left.
10
+ */
11
+ @property({ type: String, attribute: true }) leftValue: string = '';
12
+
13
+ /**
14
+ * The human-readable label to display on the left side of this switch.
15
+ * If none is provided, `leftValue` is used.
16
+ */
17
+ @property({ type: String, attribute: true }) leftLabel?: string;
18
+
19
+ /**
20
+ * The value this switch should have when toggled to the right.
21
+ */
22
+ @property({ type: String, attribute: true }) rightValue: string = '';
23
+
24
+ /**
25
+ * The human-readable label to display on the right side of this switch.
26
+ * If none is provided, `rightValue` is used.
27
+ */
28
+ @property({ type: String, attribute: true }) rightLabel?: string;
29
+
30
+ /**
31
+ * Which side of the switch is selected (`'left'` or `'right'`).
32
+ */
33
+ @property({ type: String, attribute: true }) side: Side = 'left';
34
+
35
+ @query('#switch-left')
36
+ private leftRadio!: HTMLInputElement;
37
+
38
+ render() {
39
+ return html`
40
+ <div id="container">
41
+ <label for="switch-left">${this.leftLabel ?? this.leftValue}</label>
42
+ <input
43
+ type="radio"
44
+ id="switch-left"
45
+ class="sr-only"
46
+ name="switch"
47
+ .value=${this.leftValue}
48
+ .checked=${this.side === 'left'}
49
+ @change=${this.handleRadioChange}
50
+ />
51
+ <input
52
+ type="radio"
53
+ id="switch-right"
54
+ class="sr-only"
55
+ name="switch"
56
+ .value=${this.rightValue}
57
+ .checked=${this.side === 'right'}
58
+ @change=${this.handleRadioChange}
59
+ />
60
+ <button
61
+ id="switch-button"
62
+ class=${this.side}
63
+ aria-hidden="true"
64
+ @click=${this.handleClick}
65
+ >
66
+ <div id="knob"></div>
67
+ </button>
68
+ <label for="switch-right">${this.rightLabel ?? this.rightValue}</label>
69
+ </div>
70
+ `;
71
+ }
72
+
73
+ /**
74
+ * The currently selected value of this switch.
75
+ */
76
+ get value(): string {
77
+ return this.side === 'left' ? this.leftValue : this.rightValue;
78
+ }
79
+
80
+ /**
81
+ * The label for the currently selected value of this switch.
82
+ * Falls back to the current value if its label is not defined.
83
+ */
84
+ get selectedLabel(): string {
85
+ return this.side === 'left'
86
+ ? this.leftLabel ?? this.leftValue
87
+ : this.rightLabel ?? this.rightValue;
88
+ }
89
+
90
+ private handleClick(): void {
91
+ this.side = this.side === 'left' ? 'right' : 'left';
92
+ this.emitChangeEvent();
93
+ }
94
+
95
+ private handleRadioChange(): void {
96
+ this.side = this.leftRadio.checked ? 'left' : 'right';
97
+ this.emitChangeEvent();
98
+ }
99
+
100
+ private emitChangeEvent(): void {
101
+ const event = new CustomEvent<string>('change', {
102
+ detail: this.value,
103
+ });
104
+ this.dispatchEvent(event);
105
+ }
106
+
107
+ static get styles(): CSSResultGroup {
108
+ const switchWidth = css`var(--switchWidth, 30px)`;
109
+ const switchHeight = css`var(--switchHeight, 14px)`;
110
+ const switchMarginLeft = css`var(--switchMarginLeft, 5px)`;
111
+ const switchMarginRight = css`var(--switchMarginRight, 5px)`;
112
+ const switchBorderWidth = css`var(--switchBorderWidth, 3px)`;
113
+ const switchBgColor = css`var(--switchBgColor, #194880)`;
114
+ const switchBorderColor = css`var(--switchBorderColor, #194880)`;
115
+ const labelFontSize = css`var(--labelFontSize, 1.3rem)`;
116
+ const knobColor = css`var(--knobColor, white)`;
117
+ const knobTransitionDuration = css`var(--knobTransitionDuration, 0.25s)`;
118
+ const knobTransitionFn = css`var(--knobTransitionFn, ease)`;
119
+
120
+ return css`
121
+ #container {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ flex-wrap: nowrap;
125
+ font-size: ${labelFontSize};
126
+ }
127
+
128
+ #switch-button {
129
+ width: ${switchWidth};
130
+ height: ${switchHeight};
131
+ margin: 0 ${switchMarginRight} 0 ${switchMarginLeft};
132
+ padding: 0;
133
+ border: ${switchBorderWidth} solid ${switchBorderColor};
134
+ border-radius: ${switchHeight};
135
+ box-sizing: content-box;
136
+ background: ${switchBgColor};
137
+ appearance: none;
138
+ cursor: pointer;
139
+ }
140
+
141
+ #switch-button.left #knob {
142
+ transform: translateX(0);
143
+ }
144
+
145
+ #switch-button.right #knob {
146
+ transform: translateX(calc(${switchWidth} - ${switchHeight}));
147
+ }
148
+
149
+ #switch-button:focus-visible {
150
+ outline: 2px solid black;
151
+ outline-offset: 2px;
152
+ }
153
+
154
+ #knob {
155
+ display: block;
156
+ width: ${switchHeight};
157
+ height: ${switchHeight};
158
+ border-radius: 50%;
159
+ background: ${knobColor};
160
+ transition: transform ${knobTransitionDuration} ${knobTransitionFn};
161
+ }
162
+
163
+ .sr-only {
164
+ position: absolute !important;
165
+ width: 1px !important;
166
+ height: 1px !important;
167
+ margin: -1px !important;
168
+ padding: 0 !important;
169
+ border: 0 !important;
170
+ overflow: hidden !important;
171
+ white-space: nowrap !important;
172
+ clip: rect(1px, 1px, 1px, 1px) !important;
173
+ -webkit-clip-path: inset(50%) !important;
174
+ clip-path: inset(50%) !important;
175
+ }
176
+
177
+ @media (prefers-reduced-motion: reduce) {
178
+ #knob {
179
+ transition-duration: 0.001s !important; /* Imperceptibly fast */
180
+ }
181
+ }
182
+ `;
183
+ }
184
+ }
@@ -1,22 +1,35 @@
1
- import { css, html, LitElement, CSSResultGroup, nothing } from 'lit';
1
+ import {
2
+ css,
3
+ html,
4
+ LitElement,
5
+ CSSResultGroup,
6
+ nothing,
7
+ TemplateResult,
8
+ } from 'lit';
2
9
  import { customElement, property } from 'lit/decorators.js';
3
10
  import { choose } from 'lit/directives/choose.js';
4
11
 
5
12
  import emptyQueryIcon from './assets/img/icons/empty-query';
6
13
  import nullResultIcon from './assets/img/icons/null-result';
7
14
 
8
- export type PlaceholderType = 'empty-query' | 'null-result' | null;
15
+ export type PlaceholderType =
16
+ | 'empty-query'
17
+ | 'null-result'
18
+ | 'query-error'
19
+ | null;
9
20
  @customElement('empty-placeholder')
10
21
  export class EmptyPlaceholder extends LitElement {
11
22
  @property({ type: String }) placeholderType: PlaceholderType = null;
12
23
 
13
24
  @property({ type: Boolean }) isMobileView?: false;
14
25
 
26
+ @property({ type: String }) detailMessage?: string = '';
27
+
15
28
  render() {
16
29
  return this.placeholderType ? html`${this.placeholderTemplate}` : nothing;
17
30
  }
18
31
 
19
- private get placeholderTemplate() {
32
+ private get placeholderTemplate(): TemplateResult {
20
33
  return html`
21
34
  <div
22
35
  class="placeholder ${this.placeholderType} ${this.isMobileView
@@ -26,12 +39,13 @@ export class EmptyPlaceholder extends LitElement {
26
39
  ${choose(this.placeholderType, [
27
40
  ['empty-query', () => this.emptyQueryTemplate],
28
41
  ['null-result', () => this.nullResultTemplate],
42
+ ['query-error', () => this.queryErrorTemplate],
29
43
  ])}
30
44
  </div>
31
45
  `;
32
46
  }
33
47
 
34
- private get emptyQueryTemplate() {
48
+ private get emptyQueryTemplate(): TemplateResult {
35
49
  return html`
36
50
  <h2 class="title">
37
51
  To begin searching, enter a search term in the box above and hit "Go".
@@ -40,7 +54,7 @@ export class EmptyPlaceholder extends LitElement {
40
54
  `;
41
55
  }
42
56
 
43
- private get nullResultTemplate() {
57
+ private get nullResultTemplate(): TemplateResult {
44
58
  return html`
45
59
  <h2 class="title">
46
60
  Your search did not match any items in the Archive. Try different
@@ -50,6 +64,22 @@ export class EmptyPlaceholder extends LitElement {
50
64
  `;
51
65
  }
52
66
 
67
+ private get queryErrorTemplate(): TemplateResult {
68
+ return html`
69
+ <h2 class="title">
70
+ The search engine encountered an error, which might be related to your
71
+ search query.
72
+ <a
73
+ href="https://help.archive.org/help/search-building-powerful-complex-queries/"
74
+ >
75
+ Tips for constructing search queries.
76
+ </a>
77
+ </h2>
78
+ <div>${nullResultIcon}</div>
79
+ <p class="error-details">Error details: ${this.detailMessage}</p>
80
+ `;
81
+ }
82
+
53
83
  static get styles(): CSSResultGroup {
54
84
  return css`
55
85
  :host {
@@ -57,6 +87,16 @@ export class EmptyPlaceholder extends LitElement {
57
87
  width: 100%;
58
88
  }
59
89
 
90
+ a {
91
+ text-decoration: none;
92
+ }
93
+ a:link {
94
+ color: var(--ia-theme-link-color, #4b64ff);
95
+ }
96
+ a:hover {
97
+ text-decoration: underline;
98
+ }
99
+
60
100
  .placeholder {
61
101
  display: block;
62
102
  }
@@ -64,16 +104,22 @@ export class EmptyPlaceholder extends LitElement {
64
104
  .desktop svg {
65
105
  max-height: 40rem;
66
106
  }
67
- .desktop .title {
107
+ .desktop .title,
108
+ .desktop .error-details {
68
109
  margin: 4rem 0;
69
110
  }
70
111
 
71
112
  .mobile svg {
72
113
  max-height: 20rem;
73
114
  }
74
- .mobile .title {
115
+ .mobile .title,
116
+ .mobile .error-details {
75
117
  margin: 2rem 0.5;
76
118
  }
119
+
120
+ .error-details {
121
+ font-size: 1.2rem;
122
+ }
77
123
  `;
78
124
  }
79
125
  }
@@ -41,7 +41,9 @@ export class TileListCompact extends LitElement {
41
41
  .loggedIn=${this.loggedIn}
42
42
  >
43
43
  </image-block>
44
- <div id="title">${DOMPurify.sanitize(this.model?.title ?? '')}</div>
44
+ <a href=${this.href} id="title"
45
+ >${DOMPurify.sanitize(this.model?.title ?? '')}</a
46
+ >
45
47
  <div id="creator">
46
48
  ${this.model?.mediatype === 'account'
47
49
  ? accountLabel(this.model?.dateAdded)
@@ -60,6 +62,14 @@ export class TileListCompact extends LitElement {
60
62
  `;
61
63
  }
62
64
 
65
+ private get href(): string {
66
+ // Use the server-specified href if available.
67
+ // Otherwise, construct a details page URL from the item identifier.
68
+ return this.model?.href
69
+ ? `${this.baseNavigationUrl}${this.model.href}`
70
+ : `${this.baseNavigationUrl}/details/${this.model?.identifier}`;
71
+ }
72
+
63
73
  /*
64
74
  * TODO: fix field names to match model in src/collection-browser.ts
65
75
  * private get dateSortSelector()
@@ -145,10 +155,13 @@ export class TileListCompact extends LitElement {
145
155
  }
146
156
 
147
157
  #title {
148
- color: #4b64ff;
149
158
  text-decoration: none;
150
159
  }
151
160
 
161
+ #title:link {
162
+ color: var(--ia-theme-link-color, #4b64ff);
163
+ }
164
+
152
165
  #title,
153
166
  #creator {
154
167
  text-overflow: ellipsis;
@@ -453,6 +453,9 @@ export class TileList extends LitElement {
453
453
 
454
454
  div a {
455
455
  text-decoration: none;
456
+ }
457
+
458
+ div a:link {
456
459
  color: var(--ia-theme-link-color, #4b64ff);
457
460
  }
458
461
 
@@ -0,0 +1,154 @@
1
+ import { expect, fixture } from '@open-wc/testing';
2
+ import sinon from 'sinon';
3
+ import { html } from 'lit';
4
+ import type { ToggleSwitch } from '../../src/collection-facets/toggle-switch';
5
+
6
+ import '../../src/collection-facets/toggle-switch';
7
+
8
+ describe('Toggle switch', () => {
9
+ it('renders component', async () => {
10
+ const el = await fixture<ToggleSwitch>(
11
+ html`<toggle-switch></toggle-switch>`
12
+ );
13
+
14
+ expect(el.shadowRoot?.querySelector('#switch-button')).to.exist;
15
+ expect(el.shadowRoot?.querySelector('#knob')).to.exist;
16
+ });
17
+
18
+ it('renders provided L/R values', async () => {
19
+ const el = await fixture<ToggleSwitch>(
20
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
21
+ );
22
+
23
+ expect(el.value).to.equal('L');
24
+ expect(el.selectedLabel).to.equal('L');
25
+
26
+ expect(
27
+ el.shadowRoot
28
+ ?.querySelector('label[for=switch-left]')
29
+ ?.textContent?.trim()
30
+ ).to.equal('L');
31
+ expect(
32
+ el.shadowRoot
33
+ ?.querySelector('label[for=switch-right]')
34
+ ?.textContent?.trim()
35
+ ).to.equal('R');
36
+ });
37
+
38
+ it('renders provided L/R labels instead of values', async () => {
39
+ const el = await fixture<ToggleSwitch>(
40
+ html`<toggle-switch
41
+ .leftValue=${'L'}
42
+ .leftLabel=${'L-label'}
43
+ .rightValue=${'R'}
44
+ .rightLabel=${'R-label'}
45
+ ></toggle-switch>`
46
+ );
47
+
48
+ expect(el.value).to.equal('L');
49
+ expect(el.selectedLabel).to.equal('L-label');
50
+
51
+ expect(
52
+ (el.shadowRoot?.querySelector('#switch-left') as HTMLInputElement)?.value
53
+ ).to.equal('L');
54
+ expect(
55
+ (el.shadowRoot?.querySelector('#switch-right') as HTMLInputElement)?.value
56
+ ).to.equal('R');
57
+
58
+ expect(
59
+ el.shadowRoot
60
+ ?.querySelector('label[for=switch-left]')
61
+ ?.textContent?.trim()
62
+ ).to.equal('L-label');
63
+ expect(
64
+ el.shadowRoot
65
+ ?.querySelector('label[for=switch-right]')
66
+ ?.textContent?.trim()
67
+ ).to.equal('R-label');
68
+ });
69
+
70
+ it('sets the initial side', async () => {
71
+ const el = await fixture<ToggleSwitch>(
72
+ html`<toggle-switch
73
+ .leftValue=${'L'}
74
+ .rightValue=${'R'}
75
+ .side=${'right'}
76
+ ></toggle-switch>`
77
+ );
78
+
79
+ expect(el.value).to.equal('R');
80
+ expect(el.selectedLabel).to.equal('R');
81
+ });
82
+
83
+ it('toggles on click', async () => {
84
+ const el = await fixture<ToggleSwitch>(
85
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
86
+ );
87
+
88
+ const button = el.shadowRoot?.querySelector(
89
+ '#switch-button'
90
+ ) as HTMLButtonElement;
91
+
92
+ expect(el.value).to.equal('L');
93
+ button.click();
94
+ await el.updateComplete;
95
+
96
+ expect(el.value).to.equal('R');
97
+ button.click();
98
+ await el.updateComplete;
99
+
100
+ expect(el.value).to.equal('L');
101
+ });
102
+
103
+ it('toggles on radio change', async () => {
104
+ const el = await fixture<ToggleSwitch>(
105
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
106
+ );
107
+
108
+ const leftRadio = el.shadowRoot?.querySelector(
109
+ '#switch-left'
110
+ ) as HTMLInputElement;
111
+ const rightRadio = el.shadowRoot?.querySelector(
112
+ '#switch-right'
113
+ ) as HTMLInputElement;
114
+
115
+ expect(el.value).to.equal('L');
116
+ rightRadio.click();
117
+ await el.updateComplete;
118
+
119
+ expect(el.value).to.equal('R');
120
+ leftRadio.click();
121
+ await el.updateComplete;
122
+
123
+ expect(el.value).to.equal('L');
124
+ });
125
+
126
+ it('emits change event when toggled', async () => {
127
+ const changeSpy = sinon.spy();
128
+ const el = await fixture<ToggleSwitch>(
129
+ html`<toggle-switch
130
+ .leftValue=${'L'}
131
+ .rightValue=${'R'}
132
+ @change=${changeSpy}
133
+ ></toggle-switch>`
134
+ );
135
+
136
+ const button = el.shadowRoot?.querySelector(
137
+ '#switch-button'
138
+ ) as HTMLButtonElement;
139
+
140
+ button.click();
141
+ await el.updateComplete;
142
+
143
+ expect(changeSpy.callCount).to.equal(1);
144
+
145
+ const leftRadio = el.shadowRoot?.querySelector(
146
+ '#switch-left'
147
+ ) as HTMLInputElement;
148
+
149
+ leftRadio.click();
150
+ await el.updateComplete;
151
+
152
+ expect(changeSpy.callCount).to.equal(2);
153
+ });
154
+ });