@internetarchive/collection-browser 3.3.2 → 3.3.4-alpha-webdev7761.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.
Files changed (543) hide show
  1. package/.editorconfig +29 -29
  2. package/.github/workflows/ci.yml +27 -27
  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/.prettierignore +1 -1
  8. package/LICENSE +661 -661
  9. package/README.md +83 -83
  10. package/dist/index.d.ts +16 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/src/app-root.d.ts +105 -0
  13. package/dist/src/app-root.js +1076 -0
  14. package/dist/src/app-root.js.map +1 -0
  15. package/dist/src/assets/img/icons/arrow-left.d.ts +2 -0
  16. package/dist/src/assets/img/icons/arrow-left.js +10 -0
  17. package/dist/src/assets/img/icons/arrow-left.js.map +1 -0
  18. package/dist/src/assets/img/icons/arrow-right.d.ts +2 -0
  19. package/dist/src/assets/img/icons/arrow-right.js +10 -0
  20. package/dist/src/assets/img/icons/arrow-right.js.map +1 -0
  21. package/dist/src/assets/img/icons/chevron.d.ts +2 -0
  22. package/dist/src/assets/img/icons/chevron.js +4 -0
  23. package/dist/src/assets/img/icons/chevron.js.map +1 -0
  24. package/dist/src/assets/img/icons/close-circle-dark.d.ts +2 -0
  25. package/dist/src/assets/img/icons/close-circle-dark.js +5 -0
  26. package/dist/src/assets/img/icons/close-circle-dark.js.map +1 -0
  27. package/dist/src/assets/img/icons/contract.d.ts +2 -0
  28. package/dist/src/assets/img/icons/contract.js +9 -0
  29. package/dist/src/assets/img/icons/contract.js.map +1 -0
  30. package/dist/src/assets/img/icons/empty-query.d.ts +2 -0
  31. package/dist/src/assets/img/icons/empty-query.js +5 -0
  32. package/dist/src/assets/img/icons/empty-query.js.map +1 -0
  33. package/dist/src/assets/img/icons/expand.d.ts +2 -0
  34. package/dist/src/assets/img/icons/expand.js +9 -0
  35. package/dist/src/assets/img/icons/expand.js.map +1 -0
  36. package/dist/src/assets/img/icons/eye-closed.d.ts +2 -0
  37. package/dist/src/assets/img/icons/eye-closed.js +5 -0
  38. package/dist/src/assets/img/icons/eye-closed.js.map +1 -0
  39. package/dist/src/assets/img/icons/eye.d.ts +2 -0
  40. package/dist/src/assets/img/icons/eye.js +5 -0
  41. package/dist/src/assets/img/icons/eye.js.map +1 -0
  42. package/dist/src/assets/img/icons/favorite-filled.d.ts +1 -0
  43. package/dist/src/assets/img/icons/favorite-filled.js +10 -0
  44. package/dist/src/assets/img/icons/favorite-filled.js.map +1 -0
  45. package/dist/src/assets/img/icons/favorite-unfilled.d.ts +1 -0
  46. package/dist/src/assets/img/icons/favorite-unfilled.js +9 -0
  47. package/dist/src/assets/img/icons/favorite-unfilled.js.map +1 -0
  48. package/dist/src/assets/img/icons/filter.d.ts +2 -0
  49. package/dist/src/assets/img/icons/filter.js +10 -0
  50. package/dist/src/assets/img/icons/filter.js.map +1 -0
  51. package/dist/src/assets/img/icons/login-required.d.ts +1 -0
  52. package/dist/src/assets/img/icons/login-required.js +18 -0
  53. package/dist/src/assets/img/icons/login-required.js.map +1 -0
  54. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -0
  55. package/dist/src/assets/img/icons/mediatype/account.js +14 -0
  56. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -0
  57. package/dist/src/assets/img/icons/mediatype/audio.d.ts +1 -0
  58. package/dist/src/assets/img/icons/mediatype/audio.js +14 -0
  59. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -0
  60. package/dist/src/assets/img/icons/mediatype/collection.d.ts +1 -0
  61. package/dist/src/assets/img/icons/mediatype/collection.js +12 -0
  62. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -0
  63. package/dist/src/assets/img/icons/mediatype/data.d.ts +1 -0
  64. package/dist/src/assets/img/icons/mediatype/data.js +15 -0
  65. package/dist/src/assets/img/icons/mediatype/data.js.map +1 -0
  66. package/dist/src/assets/img/icons/mediatype/etree.d.ts +1 -0
  67. package/dist/src/assets/img/icons/mediatype/etree.js +14 -0
  68. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -0
  69. package/dist/src/assets/img/icons/mediatype/film.d.ts +1 -0
  70. package/dist/src/assets/img/icons/mediatype/film.js +14 -0
  71. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -0
  72. package/dist/src/assets/img/icons/mediatype/images.d.ts +1 -0
  73. package/dist/src/assets/img/icons/mediatype/images.js +13 -0
  74. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -0
  75. package/dist/src/assets/img/icons/mediatype/radio.d.ts +1 -0
  76. package/dist/src/assets/img/icons/mediatype/radio.js +15 -0
  77. package/dist/src/assets/img/icons/mediatype/radio.js.map +1 -0
  78. package/dist/src/assets/img/icons/mediatype/search.d.ts +1 -0
  79. package/dist/src/assets/img/icons/mediatype/search.js +14 -0
  80. package/dist/src/assets/img/icons/mediatype/search.js.map +1 -0
  81. package/dist/src/assets/img/icons/mediatype/software.d.ts +1 -0
  82. package/dist/src/assets/img/icons/mediatype/software.js +13 -0
  83. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -0
  84. package/dist/src/assets/img/icons/mediatype/texts.d.ts +1 -0
  85. package/dist/src/assets/img/icons/mediatype/texts.js +13 -0
  86. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -0
  87. package/dist/src/assets/img/icons/mediatype/tv-commercial.d.ts +1 -0
  88. package/dist/src/assets/img/icons/mediatype/tv-commercial.js +12 -0
  89. package/dist/src/assets/img/icons/mediatype/tv-commercial.js.map +1 -0
  90. package/dist/src/assets/img/icons/mediatype/tv-fact-check.d.ts +1 -0
  91. package/dist/src/assets/img/icons/mediatype/tv-fact-check.js +12 -0
  92. package/dist/src/assets/img/icons/mediatype/tv-fact-check.js.map +1 -0
  93. package/dist/src/assets/img/icons/mediatype/tv-quote.d.ts +1 -0
  94. package/dist/src/assets/img/icons/mediatype/tv-quote.js +12 -0
  95. package/dist/src/assets/img/icons/mediatype/tv-quote.js.map +1 -0
  96. package/dist/src/assets/img/icons/mediatype/tv.d.ts +1 -0
  97. package/dist/src/assets/img/icons/mediatype/tv.js +14 -0
  98. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -0
  99. package/dist/src/assets/img/icons/mediatype/video.d.ts +1 -0
  100. package/dist/src/assets/img/icons/mediatype/video.js +14 -0
  101. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -0
  102. package/dist/src/assets/img/icons/mediatype/web.d.ts +1 -0
  103. package/dist/src/assets/img/icons/mediatype/web.js +13 -0
  104. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -0
  105. package/dist/src/assets/img/icons/null-result.d.ts +2 -0
  106. package/dist/src/assets/img/icons/null-result.js +5 -0
  107. package/dist/src/assets/img/icons/null-result.js.map +1 -0
  108. package/dist/src/assets/img/icons/quote.d.ts +1 -0
  109. package/dist/src/assets/img/icons/quote.js +7 -0
  110. package/dist/src/assets/img/icons/quote.js.map +1 -0
  111. package/dist/src/assets/img/icons/restricted.d.ts +1 -0
  112. package/dist/src/assets/img/icons/restricted.js +13 -0
  113. package/dist/src/assets/img/icons/restricted.js.map +1 -0
  114. package/dist/src/assets/img/icons/reviews.d.ts +1 -0
  115. package/dist/src/assets/img/icons/reviews.js +11 -0
  116. package/dist/src/assets/img/icons/reviews.js.map +1 -0
  117. package/dist/src/assets/img/icons/upload.d.ts +1 -0
  118. package/dist/src/assets/img/icons/upload.js +12 -0
  119. package/dist/src/assets/img/icons/upload.js.map +1 -0
  120. package/dist/src/assets/img/icons/views.d.ts +1 -0
  121. package/dist/src/assets/img/icons/views.js +11 -0
  122. package/dist/src/assets/img/icons/views.js.map +1 -0
  123. package/dist/src/circular-activity-indicator.d.ts +5 -0
  124. package/dist/src/circular-activity-indicator.js +66 -0
  125. package/dist/src/circular-activity-indicator.js.map +1 -0
  126. package/dist/src/collection-browser.d.ts +692 -0
  127. package/dist/src/collection-browser.js +2669 -0
  128. package/dist/src/collection-browser.js.map +1 -0
  129. package/dist/src/collection-facets/facet-row.d.ts +30 -0
  130. package/dist/src/collection-facets/facet-row.js +266 -0
  131. package/dist/src/collection-facets/facet-row.js.map +1 -0
  132. package/dist/src/collection-facets/facet-tombstone-row.d.ts +5 -0
  133. package/dist/src/collection-facets/facet-tombstone-row.js +43 -0
  134. package/dist/src/collection-facets/facet-tombstone-row.js.map +1 -0
  135. package/dist/src/collection-facets/facets-template.d.ts +13 -0
  136. package/dist/src/collection-facets/facets-template.js +68 -0
  137. package/dist/src/collection-facets/facets-template.js.map +1 -0
  138. package/dist/src/collection-facets/models.d.ts +9 -0
  139. package/dist/src/collection-facets/models.js +10 -0
  140. package/dist/src/collection-facets/models.js.map +1 -0
  141. package/dist/src/collection-facets/more-facets-content.d.ts +109 -0
  142. package/dist/src/collection-facets/more-facets-content.js +547 -0
  143. package/dist/src/collection-facets/more-facets-content.js.map +1 -0
  144. package/dist/src/collection-facets/more-facets-pagination.d.ts +36 -0
  145. package/dist/src/collection-facets/more-facets-pagination.js +264 -0
  146. package/dist/src/collection-facets/more-facets-pagination.js.map +1 -0
  147. package/dist/src/collection-facets/smart-facets/dedupe.d.ts +10 -0
  148. package/dist/src/collection-facets/smart-facets/dedupe.js +35 -0
  149. package/dist/src/collection-facets/smart-facets/dedupe.js.map +1 -0
  150. package/dist/src/collection-facets/smart-facets/heuristics/browser-language/browser-language-heuristic.d.ts +5 -0
  151. package/dist/src/collection-facets/smart-facets/heuristics/browser-language/browser-language-heuristic.js +24 -0
  152. package/dist/src/collection-facets/smart-facets/heuristics/browser-language/browser-language-heuristic.js.map +1 -0
  153. package/dist/src/collection-facets/smart-facets/heuristics/index.d.ts +3 -0
  154. package/dist/src/collection-facets/smart-facets/heuristics/index.js +4 -0
  155. package/dist/src/collection-facets/smart-facets/heuristics/index.js.map +1 -0
  156. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-heuristic.d.ts +4 -0
  157. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-heuristic.js +14 -0
  158. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-heuristic.js.map +1 -0
  159. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-map.d.ts +6 -0
  160. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-map.js +68 -0
  161. package/dist/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-map.js.map +1 -0
  162. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-entity-map.d.ts +9 -0
  163. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-entity-map.js +69 -0
  164. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-entity-map.js.map +1 -0
  165. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-heuristic.d.ts +21 -0
  166. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-heuristic.js +76 -0
  167. package/dist/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-heuristic.js.map +1 -0
  168. package/dist/src/collection-facets/smart-facets/models.d.ts +30 -0
  169. package/dist/src/collection-facets/smart-facets/models.js +2 -0
  170. package/dist/src/collection-facets/smart-facets/models.js.map +1 -0
  171. package/dist/src/collection-facets/smart-facets/smart-facet-bar.d.ts +54 -0
  172. package/dist/src/collection-facets/smart-facets/smart-facet-bar.js +383 -0
  173. package/dist/src/collection-facets/smart-facets/smart-facet-bar.js.map +1 -0
  174. package/dist/src/collection-facets/smart-facets/smart-facet-button.d.ts +11 -0
  175. package/dist/src/collection-facets/smart-facets/smart-facet-button.js +133 -0
  176. package/dist/src/collection-facets/smart-facets/smart-facet-button.js.map +1 -0
  177. package/dist/src/collection-facets/smart-facets/smart-facet-dropdown.d.ts +28 -0
  178. package/dist/src/collection-facets/smart-facets/smart-facet-dropdown.js +172 -0
  179. package/dist/src/collection-facets/smart-facets/smart-facet-dropdown.js.map +1 -0
  180. package/dist/src/collection-facets/smart-facets/smart-facet-equals.d.ts +2 -0
  181. package/dist/src/collection-facets/smart-facets/smart-facet-equals.js +13 -0
  182. package/dist/src/collection-facets/smart-facets/smart-facet-equals.js.map +1 -0
  183. package/dist/src/collection-facets/smart-facets/smart-facet-heuristics.d.ts +5 -0
  184. package/dist/src/collection-facets/smart-facets/smart-facet-heuristics.js +18 -0
  185. package/dist/src/collection-facets/smart-facets/smart-facet-heuristics.js.map +1 -0
  186. package/dist/src/collection-facets/toggle-switch.d.ts +41 -0
  187. package/dist/src/collection-facets/toggle-switch.js +174 -0
  188. package/dist/src/collection-facets/toggle-switch.js.map +1 -0
  189. package/dist/src/collection-facets.d.ts +113 -0
  190. package/dist/src/collection-facets.js +884 -0
  191. package/dist/src/collection-facets.js.map +1 -0
  192. package/dist/src/data-source/collection-browser-data-source-interface.d.ts +270 -0
  193. package/dist/src/data-source/collection-browser-data-source-interface.js +2 -0
  194. package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -0
  195. package/dist/src/data-source/collection-browser-data-source.d.ts +418 -0
  196. package/dist/src/data-source/collection-browser-data-source.js +1121 -0
  197. package/dist/src/data-source/collection-browser-data-source.js.map +1 -0
  198. package/dist/src/data-source/collection-browser-query-state.d.ts +48 -0
  199. package/dist/src/data-source/collection-browser-query-state.js +2 -0
  200. package/dist/src/data-source/collection-browser-query-state.js.map +1 -0
  201. package/dist/src/data-source/models.d.ts +43 -0
  202. package/dist/src/data-source/models.js +9 -0
  203. package/dist/src/data-source/models.js.map +1 -0
  204. package/dist/src/empty-placeholder.d.ts +23 -0
  205. package/dist/src/empty-placeholder.js +165 -0
  206. package/dist/src/empty-placeholder.js.map +1 -0
  207. package/dist/src/expanded-date-picker.d.ts +50 -0
  208. package/dist/src/expanded-date-picker.js +182 -0
  209. package/dist/src/expanded-date-picker.js.map +1 -0
  210. package/dist/src/language-code-handler/language-code-handler.d.ts +37 -0
  211. package/dist/src/language-code-handler/language-code-handler.js +27 -0
  212. package/dist/src/language-code-handler/language-code-handler.js.map +1 -0
  213. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -0
  214. package/dist/src/language-code-handler/language-code-mapping.js +563 -0
  215. package/dist/src/language-code-handler/language-code-mapping.js.map +1 -0
  216. package/dist/src/manage/manage-bar.d.ts +58 -0
  217. package/dist/src/manage/manage-bar.js +237 -0
  218. package/dist/src/manage/manage-bar.js.map +1 -0
  219. package/dist/src/manage/remove-items-modal-content.d.ts +9 -0
  220. package/dist/src/manage/remove-items-modal-content.js +104 -0
  221. package/dist/src/manage/remove-items-modal-content.js.map +1 -0
  222. package/dist/src/mediatype/mediatype-config.d.ts +11 -0
  223. package/dist/src/mediatype/mediatype-config.js +116 -0
  224. package/dist/src/mediatype/mediatype-config.js.map +1 -0
  225. package/dist/src/models.d.ts +298 -0
  226. package/dist/src/models.js +507 -0
  227. package/dist/src/models.js.map +1 -0
  228. package/dist/src/restoration-state-handler.d.ts +74 -0
  229. package/dist/src/restoration-state-handler.js +397 -0
  230. package/dist/src/restoration-state-handler.js.map +1 -0
  231. package/dist/src/sort-filter-bar/alpha-bar-tooltip.d.ts +6 -0
  232. package/dist/src/sort-filter-bar/alpha-bar-tooltip.js +60 -0
  233. package/dist/src/sort-filter-bar/alpha-bar-tooltip.js.map +1 -0
  234. package/dist/src/sort-filter-bar/alpha-bar.d.ts +21 -0
  235. package/dist/src/sort-filter-bar/alpha-bar.js +241 -0
  236. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -0
  237. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  238. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  239. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  240. package/dist/src/sort-filter-bar/img/list.d.ts +1 -0
  241. package/dist/src/sort-filter-bar/img/list.js +5 -0
  242. package/dist/src/sort-filter-bar/img/list.js.map +1 -0
  243. package/dist/src/sort-filter-bar/img/sort-toggle-disabled.d.ts +1 -0
  244. package/dist/src/sort-filter-bar/img/sort-toggle-disabled.js +15 -0
  245. package/dist/src/sort-filter-bar/img/sort-toggle-disabled.js.map +1 -0
  246. package/dist/src/sort-filter-bar/img/sort-toggle-down.d.ts +1 -0
  247. package/dist/src/sort-filter-bar/img/sort-toggle-down.js +17 -0
  248. package/dist/src/sort-filter-bar/img/sort-toggle-down.js.map +1 -0
  249. package/dist/src/sort-filter-bar/img/sort-toggle-up.d.ts +1 -0
  250. package/dist/src/sort-filter-bar/img/sort-toggle-up.js +17 -0
  251. package/dist/src/sort-filter-bar/img/sort-toggle-up.js.map +1 -0
  252. package/dist/src/sort-filter-bar/img/sort-triangle.d.ts +1 -0
  253. package/dist/src/sort-filter-bar/img/sort-triangle.js +5 -0
  254. package/dist/src/sort-filter-bar/img/sort-triangle.js.map +1 -0
  255. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  256. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  257. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  258. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +278 -0
  259. package/dist/src/sort-filter-bar/sort-filter-bar.js +1161 -0
  260. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -0
  261. package/dist/src/styles/ia-button.d.ts +2 -0
  262. package/dist/src/styles/ia-button.js +134 -0
  263. package/dist/src/styles/ia-button.js.map +1 -0
  264. package/dist/src/styles/item-image-styles.d.ts +8 -0
  265. package/dist/src/styles/item-image-styles.js +123 -0
  266. package/dist/src/styles/item-image-styles.js.map +1 -0
  267. package/dist/src/styles/sr-only.d.ts +1 -0
  268. package/dist/src/styles/sr-only.js +18 -0
  269. package/dist/src/styles/sr-only.js.map +1 -0
  270. package/dist/src/tiles/base-tile-component.d.ts +27 -0
  271. package/dist/src/tiles/base-tile-component.js +82 -0
  272. package/dist/src/tiles/base-tile-component.js.map +1 -0
  273. package/dist/src/tiles/collection-browser-loading-tile.d.ts +5 -0
  274. package/dist/src/tiles/collection-browser-loading-tile.js +29 -0
  275. package/dist/src/tiles/collection-browser-loading-tile.js.map +1 -0
  276. package/dist/src/tiles/grid/account-tile.d.ts +18 -0
  277. package/dist/src/tiles/grid/account-tile.js +111 -0
  278. package/dist/src/tiles/grid/account-tile.js.map +1 -0
  279. package/dist/src/tiles/grid/collection-tile.d.ts +15 -0
  280. package/dist/src/tiles/grid/collection-tile.js +160 -0
  281. package/dist/src/tiles/grid/collection-tile.js.map +1 -0
  282. package/dist/src/tiles/grid/item-tile.d.ts +41 -0
  283. package/dist/src/tiles/grid/item-tile.js +321 -0
  284. package/dist/src/tiles/grid/item-tile.js.map +1 -0
  285. package/dist/src/tiles/grid/search-tile.d.ts +10 -0
  286. package/dist/src/tiles/grid/search-tile.js +95 -0
  287. package/dist/src/tiles/grid/search-tile.js.map +1 -0
  288. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.d.ts +1 -0
  289. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.js +128 -0
  290. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.js.map +1 -0
  291. package/dist/src/tiles/grid/tile-stats.d.ts +58 -0
  292. package/dist/src/tiles/grid/tile-stats.js +204 -0
  293. package/dist/src/tiles/grid/tile-stats.js.map +1 -0
  294. package/dist/src/tiles/hover/hover-pane-controller.d.ts +228 -0
  295. package/dist/src/tiles/hover/hover-pane-controller.js +446 -0
  296. package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -0
  297. package/dist/src/tiles/hover/tile-hover-pane.d.ts +20 -0
  298. package/dist/src/tiles/hover/tile-hover-pane.js +185 -0
  299. package/dist/src/tiles/hover/tile-hover-pane.js.map +1 -0
  300. package/dist/src/tiles/image-block.d.ts +19 -0
  301. package/dist/src/tiles/image-block.js +175 -0
  302. package/dist/src/tiles/image-block.js.map +1 -0
  303. package/dist/src/tiles/item-image.d.ts +40 -0
  304. package/dist/src/tiles/item-image.js +191 -0
  305. package/dist/src/tiles/item-image.js.map +1 -0
  306. package/dist/src/tiles/list/tile-list-compact-header.d.ts +6 -0
  307. package/dist/src/tiles/list/tile-list-compact-header.js +85 -0
  308. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  309. package/dist/src/tiles/list/tile-list-compact.d.ts +19 -0
  310. package/dist/src/tiles/list/tile-list-compact.js +223 -0
  311. package/dist/src/tiles/list/tile-list-compact.js.map +1 -0
  312. package/dist/src/tiles/list/tile-list.d.ts +54 -0
  313. package/dist/src/tiles/list/tile-list.js +621 -0
  314. package/dist/src/tiles/list/tile-list.js.map +1 -0
  315. package/dist/src/tiles/models.d.ts +1 -0
  316. package/dist/src/tiles/models.js +2 -0
  317. package/dist/src/tiles/models.js.map +1 -0
  318. package/dist/src/tiles/overlay/icon-overlay.d.ts +8 -0
  319. package/dist/src/tiles/overlay/icon-overlay.js +55 -0
  320. package/dist/src/tiles/overlay/icon-overlay.js.map +1 -0
  321. package/dist/src/tiles/overlay/text-overlay.d.ts +9 -0
  322. package/dist/src/tiles/overlay/text-overlay.js +74 -0
  323. package/dist/src/tiles/overlay/text-overlay.js.map +1 -0
  324. package/dist/src/tiles/review-block.d.ts +12 -0
  325. package/dist/src/tiles/review-block.js +134 -0
  326. package/dist/src/tiles/review-block.js.map +1 -0
  327. package/dist/src/tiles/text-snippet-block.d.ts +27 -0
  328. package/dist/src/tiles/text-snippet-block.js +132 -0
  329. package/dist/src/tiles/text-snippet-block.js.map +1 -0
  330. package/dist/src/tiles/tile-dispatcher.d.ts +71 -0
  331. package/dist/src/tiles/tile-dispatcher.js +472 -0
  332. package/dist/src/tiles/tile-dispatcher.js.map +1 -0
  333. package/dist/src/tiles/tile-display-value-provider.d.ts +47 -0
  334. package/dist/src/tiles/tile-display-value-provider.js +95 -0
  335. package/dist/src/tiles/tile-display-value-provider.js.map +1 -0
  336. package/dist/src/tiles/tile-mediatype-icon.d.ts +27 -0
  337. package/dist/src/tiles/tile-mediatype-icon.js +130 -0
  338. package/dist/src/tiles/tile-mediatype-icon.js.map +1 -0
  339. package/dist/src/utils/analytics-events.d.ts +28 -0
  340. package/dist/src/utils/analytics-events.js +31 -0
  341. package/dist/src/utils/analytics-events.js.map +1 -0
  342. package/dist/src/utils/array-equals.d.ts +4 -0
  343. package/dist/src/utils/array-equals.js +11 -0
  344. package/dist/src/utils/array-equals.js.map +1 -0
  345. package/dist/src/utils/collapse-repeated-quotes.d.ts +11 -0
  346. package/dist/src/utils/collapse-repeated-quotes.js +14 -0
  347. package/dist/src/utils/collapse-repeated-quotes.js.map +1 -0
  348. package/dist/src/utils/facet-utils.d.ts +83 -0
  349. package/dist/src/utils/facet-utils.js +152 -0
  350. package/dist/src/utils/facet-utils.js.map +1 -0
  351. package/dist/src/utils/format-count.d.ts +7 -0
  352. package/dist/src/utils/format-count.js +76 -0
  353. package/dist/src/utils/format-count.js.map +1 -0
  354. package/dist/src/utils/format-date.d.ts +16 -0
  355. package/dist/src/utils/format-date.js +33 -0
  356. package/dist/src/utils/format-date.js.map +1 -0
  357. package/dist/src/utils/format-unit-size.d.ts +2 -0
  358. package/dist/src/utils/format-unit-size.js +34 -0
  359. package/dist/src/utils/format-unit-size.js.map +1 -0
  360. package/dist/src/utils/local-date-from-utc.d.ts +9 -0
  361. package/dist/src/utils/local-date-from-utc.js +16 -0
  362. package/dist/src/utils/local-date-from-utc.js.map +1 -0
  363. package/dist/src/utils/log.d.ts +7 -0
  364. package/dist/src/utils/log.js +14 -0
  365. package/dist/src/utils/log.js.map +1 -0
  366. package/dist/src/utils/resolve-mediatype.d.ts +8 -0
  367. package/dist/src/utils/resolve-mediatype.js +24 -0
  368. package/dist/src/utils/resolve-mediatype.js.map +1 -0
  369. package/dist/src/utils/sha1.d.ts +2 -0
  370. package/dist/src/utils/sha1.js +9 -0
  371. package/dist/src/utils/sha1.js.map +1 -0
  372. package/dist/test/collection-browser.test.d.ts +1 -0
  373. package/dist/test/collection-browser.test.js +1721 -0
  374. package/dist/test/collection-browser.test.js.map +1 -0
  375. package/dist/test/collection-facets/facet-row.test.d.ts +1 -0
  376. package/dist/test/collection-facets/facet-row.test.js +274 -0
  377. package/dist/test/collection-facets/facet-row.test.js.map +1 -0
  378. package/dist/test/collection-facets/facets-template.test.d.ts +1 -0
  379. package/dist/test/collection-facets/facets-template.test.js +101 -0
  380. package/dist/test/collection-facets/facets-template.test.js.map +1 -0
  381. package/dist/test/collection-facets/more-facets-content.test.d.ts +1 -0
  382. package/dist/test/collection-facets/more-facets-content.test.js +169 -0
  383. package/dist/test/collection-facets/more-facets-content.test.js.map +1 -0
  384. package/dist/test/collection-facets/more-facets-pagination.test.d.ts +1 -0
  385. package/dist/test/collection-facets/more-facets-pagination.test.js +132 -0
  386. package/dist/test/collection-facets/more-facets-pagination.test.js.map +1 -0
  387. package/dist/test/collection-facets/toggle-switch.test.d.ts +1 -0
  388. package/dist/test/collection-facets/toggle-switch.test.js +96 -0
  389. package/dist/test/collection-facets/toggle-switch.test.js.map +1 -0
  390. package/dist/test/collection-facets.test.d.ts +2 -0
  391. package/dist/test/collection-facets.test.js +745 -0
  392. package/dist/test/collection-facets.test.js.map +1 -0
  393. package/dist/test/data-source/collection-browser-data-source.test.d.ts +1 -0
  394. package/dist/test/data-source/collection-browser-data-source.test.js +102 -0
  395. package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -0
  396. package/dist/test/empty-placeholder.test.d.ts +1 -0
  397. package/dist/test/empty-placeholder.test.js +63 -0
  398. package/dist/test/empty-placeholder.test.js.map +1 -0
  399. package/dist/test/expanded-date-picker.test.d.ts +1 -0
  400. package/dist/test/expanded-date-picker.test.js +130 -0
  401. package/dist/test/expanded-date-picker.test.js.map +1 -0
  402. package/dist/test/icon-overlay.test.d.ts +1 -0
  403. package/dist/test/icon-overlay.test.js +30 -0
  404. package/dist/test/icon-overlay.test.js.map +1 -0
  405. package/dist/test/image-block.test.d.ts +1 -0
  406. package/dist/test/image-block.test.js +218 -0
  407. package/dist/test/image-block.test.js.map +1 -0
  408. package/dist/test/item-image.test.d.ts +1 -0
  409. package/dist/test/item-image.test.js +196 -0
  410. package/dist/test/item-image.test.js.map +1 -0
  411. package/dist/test/manage/manage-bar.test.d.ts +2 -0
  412. package/dist/test/manage/manage-bar.test.js +106 -0
  413. package/dist/test/manage/manage-bar.test.js.map +1 -0
  414. package/dist/test/manage/remove-items-modal-content.test.d.ts +1 -0
  415. package/dist/test/manage/remove-items-modal-content.test.js +65 -0
  416. package/dist/test/manage/remove-items-modal-content.test.js.map +1 -0
  417. package/dist/test/mediatype-config.test.d.ts +1 -0
  418. package/dist/test/mediatype-config.test.js +11 -0
  419. package/dist/test/mediatype-config.test.js.map +1 -0
  420. package/dist/test/mocks/mock-analytics-handler.d.ts +10 -0
  421. package/dist/test/mocks/mock-analytics-handler.js +16 -0
  422. package/dist/test/mocks/mock-analytics-handler.js.map +1 -0
  423. package/dist/test/mocks/mock-search-responses.d.ts +31 -0
  424. package/dist/test/mocks/mock-search-responses.js +1241 -0
  425. package/dist/test/mocks/mock-search-responses.js.map +1 -0
  426. package/dist/test/mocks/mock-search-service.d.ts +16 -0
  427. package/dist/test/mocks/mock-search-service.js +65 -0
  428. package/dist/test/mocks/mock-search-service.js.map +1 -0
  429. package/dist/test/restoration-state-handler.test.d.ts +1 -0
  430. package/dist/test/restoration-state-handler.test.js +343 -0
  431. package/dist/test/restoration-state-handler.test.js.map +1 -0
  432. package/dist/test/review-block.test.d.ts +1 -0
  433. package/dist/test/review-block.test.js +48 -0
  434. package/dist/test/review-block.test.js.map +1 -0
  435. package/dist/test/sort-filter-bar/alpha-bar-tooltip.test.d.ts +1 -0
  436. package/dist/test/sort-filter-bar/alpha-bar-tooltip.test.js +13 -0
  437. package/dist/test/sort-filter-bar/alpha-bar-tooltip.test.js.map +1 -0
  438. package/dist/test/sort-filter-bar/alpha-bar.test.d.ts +1 -0
  439. package/dist/test/sort-filter-bar/alpha-bar.test.js +74 -0
  440. package/dist/test/sort-filter-bar/alpha-bar.test.js.map +1 -0
  441. package/dist/test/sort-filter-bar/sort-filter-bar.test.d.ts +1 -0
  442. package/dist/test/sort-filter-bar/sort-filter-bar.test.js +609 -0
  443. package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -0
  444. package/dist/test/text-overlay.test.d.ts +1 -0
  445. package/dist/test/text-overlay.test.js +42 -0
  446. package/dist/test/text-overlay.test.js.map +1 -0
  447. package/dist/test/text-snippet-block.test.d.ts +1 -0
  448. package/dist/test/text-snippet-block.test.js +63 -0
  449. package/dist/test/text-snippet-block.test.js.map +1 -0
  450. package/dist/test/tile-stats.test.d.ts +1 -0
  451. package/dist/test/tile-stats.test.js +128 -0
  452. package/dist/test/tile-stats.test.js.map +1 -0
  453. package/dist/test/tiles/grid/account-tile.test.d.ts +1 -0
  454. package/dist/test/tiles/grid/account-tile.test.js +96 -0
  455. package/dist/test/tiles/grid/account-tile.test.js.map +1 -0
  456. package/dist/test/tiles/grid/collection-tile.test.d.ts +1 -0
  457. package/dist/test/tiles/grid/collection-tile.test.js +96 -0
  458. package/dist/test/tiles/grid/collection-tile.test.js.map +1 -0
  459. package/dist/test/tiles/grid/item-tile.test.d.ts +1 -0
  460. package/dist/test/tiles/grid/item-tile.test.js +427 -0
  461. package/dist/test/tiles/grid/item-tile.test.js.map +1 -0
  462. package/dist/test/tiles/grid/search-tile.test.d.ts +1 -0
  463. package/dist/test/tiles/grid/search-tile.test.js +66 -0
  464. package/dist/test/tiles/grid/search-tile.test.js.map +1 -0
  465. package/dist/test/tiles/hover/hover-pane-controller.test.d.ts +1 -0
  466. package/dist/test/tiles/hover/hover-pane-controller.test.js +329 -0
  467. package/dist/test/tiles/hover/hover-pane-controller.test.js.map +1 -0
  468. package/dist/test/tiles/hover/tile-hover-pane.test.d.ts +1 -0
  469. package/dist/test/tiles/hover/tile-hover-pane.test.js +57 -0
  470. package/dist/test/tiles/hover/tile-hover-pane.test.js.map +1 -0
  471. package/dist/test/tiles/list/tile-list-compact.test.d.ts +1 -0
  472. package/dist/test/tiles/list/tile-list-compact.test.js +251 -0
  473. package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -0
  474. package/dist/test/tiles/list/tile-list.test.d.ts +1 -0
  475. package/dist/test/tiles/list/tile-list.test.js +469 -0
  476. package/dist/test/tiles/list/tile-list.test.js.map +1 -0
  477. package/dist/test/tiles/tile-dispatcher.test.d.ts +1 -0
  478. package/dist/test/tiles/tile-dispatcher.test.js +231 -0
  479. package/dist/test/tiles/tile-dispatcher.test.js.map +1 -0
  480. package/dist/test/tiles/tile-display-value-provider.test.d.ts +1 -0
  481. package/dist/test/tiles/tile-display-value-provider.test.js +142 -0
  482. package/dist/test/tiles/tile-display-value-provider.test.js.map +1 -0
  483. package/dist/test/tiles/tile-mediatype-icon.test.d.ts +1 -0
  484. package/dist/test/tiles/tile-mediatype-icon.test.js +145 -0
  485. package/dist/test/tiles/tile-mediatype-icon.test.js.map +1 -0
  486. package/dist/test/utils/array-equals.test.d.ts +1 -0
  487. package/dist/test/utils/array-equals.test.js +27 -0
  488. package/dist/test/utils/array-equals.test.js.map +1 -0
  489. package/dist/test/utils/format-count.test.d.ts +1 -0
  490. package/dist/test/utils/format-count.test.js +24 -0
  491. package/dist/test/utils/format-count.test.js.map +1 -0
  492. package/dist/test/utils/format-date.test.d.ts +1 -0
  493. package/dist/test/utils/format-date.test.js +61 -0
  494. package/dist/test/utils/format-date.test.js.map +1 -0
  495. package/dist/test/utils/format-unit-size.test.d.ts +1 -0
  496. package/dist/test/utils/format-unit-size.test.js +18 -0
  497. package/dist/test/utils/format-unit-size.test.js.map +1 -0
  498. package/dist/test/utils/local-date-from-utc.test.d.ts +1 -0
  499. package/dist/test/utils/local-date-from-utc.test.js +27 -0
  500. package/dist/test/utils/local-date-from-utc.test.js.map +1 -0
  501. package/eslint.config.mjs +53 -53
  502. package/index.html +24 -24
  503. package/local.archive.org.cert +86 -86
  504. package/local.archive.org.key +27 -27
  505. package/package.json +118 -117
  506. package/renovate.json +6 -6
  507. package/src/collection-browser.ts +2954 -2829
  508. package/src/collection-facets/facet-row.ts +299 -296
  509. package/src/collection-facets/models.ts +10 -10
  510. package/src/collection-facets/more-facets-content.ts +639 -639
  511. package/src/collection-facets.ts +1005 -995
  512. package/src/data-source/collection-browser-data-source-interface.ts +345 -333
  513. package/src/data-source/collection-browser-data-source.ts +1441 -1401
  514. package/src/data-source/collection-browser-query-state.ts +59 -65
  515. package/src/data-source/models.ts +56 -43
  516. package/src/manage/manage-bar.ts +247 -247
  517. package/src/models.ts +866 -870
  518. package/src/restoration-state-handler.ts +542 -544
  519. package/src/tiles/base-tile-component.ts +65 -65
  520. package/src/tiles/grid/account-tile.ts +113 -113
  521. package/src/tiles/grid/collection-tile.ts +163 -163
  522. package/src/tiles/grid/item-tile.ts +340 -340
  523. package/src/tiles/grid/search-tile.ts +90 -90
  524. package/src/tiles/grid/styles/tile-grid-shared-styles.ts +130 -130
  525. package/src/tiles/hover/hover-pane-controller.ts +613 -517
  526. package/src/tiles/hover/tile-hover-pane.ts +184 -180
  527. package/src/tiles/list/tile-list-compact.ts +239 -239
  528. package/src/tiles/list/tile-list.ts +700 -700
  529. package/src/tiles/tile-dispatcher.ts +517 -490
  530. package/src/utils/format-date.ts +62 -62
  531. package/test/collection-browser.test.ts +2413 -2403
  532. package/test/collection-facets/facet-row.test.ts +375 -375
  533. package/test/collection-facets.test.ts +928 -928
  534. package/test/restoration-state-handler.test.ts +480 -510
  535. package/test/tiles/grid/item-tile.test.ts +520 -520
  536. package/test/tiles/hover/hover-pane-controller.test.ts +418 -353
  537. package/test/tiles/list/tile-list-compact.test.ts +282 -282
  538. package/test/tiles/list/tile-list.test.ts +552 -552
  539. package/test/tiles/tile-dispatcher.test.ts +283 -187
  540. package/test/utils/format-date.test.ts +89 -89
  541. package/tsconfig.json +20 -20
  542. package/web-dev-server.config.mjs +30 -30
  543. package/web-test-runner.config.mjs +41 -41
package/src/models.ts CHANGED
@@ -1,870 +1,866 @@
1
- import type { TemplateResult } from 'lit';
2
- import { msg } from '@lit/localize';
3
- import type { MediaType } from '@internetarchive/field-parsers';
4
- import {
5
- AggregationSortType,
6
- HitType,
7
- SearchReview,
8
- SearchResult,
9
- SortDirection,
10
- } from '@internetarchive/search-service';
11
- import { collapseRepeatedQuotes } from './utils/collapse-repeated-quotes';
12
- import { resolveMediatype } from './utils/resolve-mediatype';
13
-
14
- import { loginRequiredIcon } from './assets/img/icons/login-required';
15
- import { restrictedIcon } from './assets/img/icons/restricted';
16
-
17
- /**
18
- * Flags that can affect the visibility of content on a tile
19
- */
20
- interface TileFlags {
21
- loginRequired: boolean;
22
- contentWarning: boolean;
23
- }
24
-
25
- /**
26
- * Different types of tile overlays, corresponding to the above flags.
27
- */
28
- export type TileOverlayType = 'login-required' | 'content-warning';
29
-
30
- export const TILE_OVERLAY_TEXT: Record<TileOverlayType, string> = {
31
- 'login-required': msg('Log in to view this item'),
32
- 'content-warning': msg('Content may be inappropriate'),
33
- };
34
-
35
- export const TILE_OVERLAY_ICONS: Record<TileOverlayType, TemplateResult> = {
36
- 'login-required': loginRequiredIcon,
37
- 'content-warning': restrictedIcon,
38
- };
39
-
40
- /**
41
- * What type of request produced a given set of hits:
42
- * - `search_query`: Hits produced by an explicit user query and/or applied filters on any page
43
- * - `collection_members`: Hits produced for a collection page without any query or filters
44
- * - `profile_tab`: Hits produced for a tab of the profile page without any query or filters
45
- * - `unknown`: Hits produced via any other means
46
- */
47
- export type HitRequestSource =
48
- | 'search_query'
49
- | 'collection_members'
50
- | 'profile_tab'
51
- | 'unknown';
52
-
53
- /**
54
- * Class for converting & storing raw search results in the correct format for UI tiles.
55
- */
56
- export class TileModel {
57
- /** For TV hits. List of identifiers for any commercials contained. */
58
- adIds?: string[];
59
-
60
- averageRating?: number;
61
-
62
- /** For Web Archive hits on profile pages. List of capture dates for the current URL. */
63
- captureDates?: Date[];
64
-
65
- /** Whether this tile is currently checked for item management functions */
66
- checked: boolean;
67
-
68
- collectionIdentifier?: string;
69
-
70
- collectionName?: string;
71
-
72
- collectionFilesCount: number;
73
-
74
- collections: string[];
75
-
76
- collectionSize: number;
77
-
78
- commentCount: number;
79
-
80
- creator?: string;
81
-
82
- creators: string[];
83
-
84
- /** A string representation of the publication date, used strictly for passing preformatted dates to the parent */
85
- dateStr?: string;
86
-
87
- /** Date added to public search (software-defined) [from MD field: addeddate] */
88
- dateAdded?: Date;
89
-
90
- /** Date archived (software-defined) item created on archive.org [from MD field: publicdate] */
91
- dateArchived?: Date;
92
-
93
- /** Date work published in the world (user-defined) [from MD field: date] */
94
- datePublished?: Date;
95
-
96
- /** Date reviewed (user-created) most recent review [from MD field: reviewdate] */
97
- dateReviewed?: Date;
98
-
99
- description?: string;
100
-
101
- /** For TV hits. List of URLs for any fact-checks of the contained clips. */
102
- factChecks?: string[];
103
-
104
- favCount: number;
105
-
106
- hitRequestSource: HitRequestSource;
107
-
108
- hitType?: HitType;
109
-
110
- href?: string;
111
-
112
- identifier?: string;
113
-
114
- /** Whether this model represents a TV clip */
115
- isClip?: boolean;
116
-
117
- issue?: string;
118
-
119
- itemCount: number;
120
-
121
- mediatype: MediaType;
122
-
123
- review?: SearchReview;
124
-
125
- source?: string;
126
-
127
- snippets?: string[];
128
-
129
- subjects: string[];
130
-
131
- thumbnailUrl?: string;
132
-
133
- title: string;
134
-
135
- tvClipCount?: number;
136
-
137
- viewCount?: number;
138
-
139
- volume?: string;
140
-
141
- weeklyViewCount?: number;
142
-
143
- loginRequired: boolean;
144
-
145
- contentWarning: boolean;
146
-
147
- constructor(
148
- result: SearchResult,
149
- hitRequestSource: HitRequestSource = 'unknown',
150
- ) {
151
- const flags = this.getFlags(result);
152
-
153
- this.adIds = result.ad_id?.values;
154
- this.averageRating = result.avg_rating?.value;
155
- this.captureDates = result.capture_dates?.values;
156
- this.checked = false;
157
- this.collections = result.collection?.values ?? [];
158
- this.collectionFilesCount = result.collection_files_count?.value ?? 0;
159
- this.collectionSize = result.collection_size?.value ?? 0;
160
- this.commentCount = result.num_reviews?.value ?? 0;
161
- this.creator = result.creator?.value;
162
- this.creators = result.creator?.values ?? [];
163
- this.dateAdded = result.addeddate?.value;
164
- this.dateArchived = result.publicdate?.value;
165
- this.datePublished = result.date?.value;
166
- this.dateReviewed = result.reviewdate?.value;
167
- this.description = result.description?.values.join('\n');
168
- this.factChecks = result.factcheck?.values;
169
- this.favCount = result.num_favorites?.value ?? 0;
170
- this.hitRequestSource = hitRequestSource;
171
- this.hitType = result.rawMetadata?.hit_type;
172
- this.href = collapseRepeatedQuotes(
173
- result.review?.__href__ ?? result.__href__?.value,
174
- );
175
- this.identifier = TileModel.cleanIdentifier(result.identifier);
176
- this.isClip = result.is_clip?.value;
177
- this.issue = result.issue?.value;
178
- this.itemCount = result.item_count?.value ?? 0;
179
- this.mediatype = resolveMediatype(result);
180
- this.review = result.review;
181
- this.snippets = result.highlight?.values ?? [];
182
- this.source = result.source?.value;
183
- this.subjects = result.subject?.values ?? [];
184
- this.thumbnailUrl = result.__img__?.value;
185
- this.title = result.title?.value ?? '';
186
- this.tvClipCount = result.num_clips?.value ?? 0;
187
- this.volume = result.volume?.value;
188
- this.viewCount = result.downloads?.value;
189
- this.weeklyViewCount = result.week?.value;
190
- this.loginRequired = flags.loginRequired;
191
- this.contentWarning = flags.contentWarning;
192
- }
193
-
194
- /**
195
- * Copies the contents of this TileModel onto a new instance
196
- */
197
- clone(): TileModel {
198
- const cloned = new TileModel({});
199
- cloned.adIds = this.adIds;
200
- cloned.averageRating = this.averageRating;
201
- cloned.captureDates = this.captureDates;
202
- cloned.checked = this.checked;
203
- cloned.collections = this.collections;
204
- cloned.collectionFilesCount = this.collectionFilesCount;
205
- cloned.collectionSize = this.collectionSize;
206
- cloned.commentCount = this.commentCount;
207
- cloned.creator = this.creator;
208
- cloned.creators = this.creators;
209
- cloned.dateStr = this.dateStr;
210
- cloned.dateAdded = this.dateAdded;
211
- cloned.dateArchived = this.dateArchived;
212
- cloned.datePublished = this.datePublished;
213
- cloned.dateReviewed = this.dateReviewed;
214
- cloned.description = this.description;
215
- cloned.factChecks = this.factChecks;
216
- cloned.favCount = this.favCount;
217
- cloned.hitRequestSource = this.hitRequestSource;
218
- cloned.hitType = this.hitType;
219
- cloned.href = this.href;
220
- cloned.identifier = this.identifier;
221
- cloned.isClip = this.isClip;
222
- cloned.issue = this.issue;
223
- cloned.itemCount = this.itemCount;
224
- cloned.mediatype = this.mediatype;
225
- cloned.snippets = this.snippets;
226
- cloned.source = this.source;
227
- cloned.subjects = this.subjects;
228
- cloned.thumbnailUrl = this.thumbnailUrl;
229
- cloned.title = this.title;
230
- cloned.tvClipCount = this.tvClipCount;
231
- cloned.volume = this.volume;
232
- cloned.viewCount = this.viewCount;
233
- cloned.weeklyViewCount = this.weeklyViewCount;
234
- cloned.loginRequired = this.loginRequired;
235
- cloned.contentWarning = this.contentWarning;
236
- return cloned;
237
- }
238
-
239
- /**
240
- * Whether this model represents the result of a TV search query.
241
- */
242
- get isTvSearchResult(): boolean {
243
- return (
244
- this.hitType === 'tv_clip' && this.hitRequestSource === 'search_query'
245
- );
246
- }
247
-
248
- /**
249
- * Determines the appropriate tile flags for the given search result
250
- * (login required and/or content warning)
251
- */
252
- private getFlags(result: SearchResult): TileFlags {
253
- const flags: TileFlags = {
254
- loginRequired: false,
255
- contentWarning: false,
256
- };
257
-
258
- // Check if item and item in "modifying" collection, setting above flags
259
- if (
260
- result.collection?.values.length &&
261
- result.mediatype?.value !== 'collection'
262
- ) {
263
- for (const collection of result.collection?.values ?? []) {
264
- if (collection === 'loggedin') {
265
- flags.loginRequired = true;
266
- if (flags.contentWarning) break;
267
- }
268
- if (collection === 'no-preview') {
269
- flags.contentWarning = true;
270
- if (flags.loginRequired) break;
271
- }
272
- }
273
- }
274
-
275
- return flags;
276
- }
277
-
278
- private static cleanIdentifier(
279
- identifier: string | undefined,
280
- ): string | undefined {
281
- // Some identifiers (e.g., from Whisper) represent documents rather than items, and
282
- // are suffixed with values that need to be stripped. Those values are separated
283
- // from the item identifier itself with '|'.
284
- const barIndex = identifier?.indexOf('|') ?? -1;
285
- const cleaned = barIndex > 0 ? identifier?.slice(0, barIndex) : identifier;
286
- return cleaned;
287
- }
288
- }
289
-
290
- export type RequestKind = 'full' | 'hits' | 'aggregations';
291
-
292
- export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
293
-
294
- export type TileDisplayMode =
295
- | 'grid'
296
- | 'list-compact'
297
- | 'list-detail'
298
- | 'list-header';
299
-
300
- /**
301
- * This is mainly used to set the cookies for the collection display mode.
302
- *
303
- * It allows the user to set different modes for different contexts (collection page, search page, profile page etc).
304
- */
305
- export type CollectionBrowserContext = 'collection' | 'search' | 'profile';
306
-
307
- /**
308
- * The sort fields shown in the sort filter bar
309
- */
310
- export enum SortField {
311
- 'default' = 'default',
312
- 'unrecognized' = 'unrecognized',
313
- 'relevance' = 'relevance',
314
- 'alltimeview' = 'alltimeview',
315
- 'weeklyview' = 'weeklyview',
316
- 'title' = 'title',
317
- 'date' = 'date',
318
- 'datearchived' = 'datearchived',
319
- 'datereviewed' = 'datereviewed',
320
- 'dateadded' = 'dateadded',
321
- 'datefavorited' = 'datefavorited',
322
- 'creator' = 'creator',
323
- }
324
-
325
- /**
326
- * Views-related sort fields
327
- */
328
- export const ALL_VIEWS_SORT_FIELDS = [
329
- SortField.weeklyview,
330
- SortField.alltimeview,
331
- ] as const;
332
- export type ViewsSortField = (typeof ALL_VIEWS_SORT_FIELDS)[number];
333
-
334
- /**
335
- * Date-related sort fields
336
- */
337
- export const ALL_DATE_SORT_FIELDS = [
338
- SortField.datefavorited,
339
- SortField.date,
340
- SortField.datearchived,
341
- SortField.datereviewed,
342
- SortField.dateadded,
343
- ] as const;
344
- export type DateSortField = (typeof ALL_DATE_SORT_FIELDS)[number];
345
-
346
- export interface SortOption {
347
- /**
348
- * The SortField enum member corresponding to this option.
349
- */
350
- field: SortField;
351
-
352
- /**
353
- * The default sort direction to apply when this sort option is first selected.
354
- */
355
- defaultSortDirection: SortDirection | null;
356
-
357
- /**
358
- * Whether this sort option allows its sort direction to be changed from the default.
359
- */
360
- canSetDirection: boolean;
361
-
362
- /**
363
- * Whether this sort option may appear in the sort bar.
364
- */
365
- shownInSortBar: boolean;
366
-
367
- /**
368
- * Whether this sort option should be saved to the URL.
369
- * If false, then no `sort` param will be added to the URL when this sort option
370
- * is selected.
371
- */
372
- shownInURL: boolean;
373
-
374
- /**
375
- * Whether this sort option is passed to the search service.
376
- * If false, then no sort param will be passed to the search service at all when
377
- * this sort option is selected.
378
- */
379
- handledBySearchService: boolean;
380
-
381
- /**
382
- * The string identifying this sort field to the search service & backend API.
383
- */
384
- searchServiceKey?: string;
385
-
386
- /**
387
- * The human-readable name to use for this option in the sort bar (if applicable).
388
- */
389
- displayName: string;
390
-
391
- /**
392
- * A list of URL param keys that should be mapped to this sort option.
393
- * E.g., both `title` and `titleSorter` in the URL map to the `SortField.title` option.
394
- */
395
- urlNames: (string | null | undefined)[];
396
- }
397
-
398
- export const SORT_OPTIONS: Record<SortField, SortOption> = {
399
- // Default sort is the case where the user has not specified a sort option via the sort bar or URL.
400
- // In these cases, we defer to whatever sort the backend chooses.
401
- // For the search page, the default is always relevance sort.
402
- // For collection pages _without a query_, the default is usually weekly views, but this can be
403
- // overridden by the collection's `sort-by` metadata entry. If a query _is_ specified, then the
404
- // default is again relevance sort.
405
- // For fav-* collections only, the default is instead sorting by date favorited.
406
- [SortField.default]: {
407
- field: SortField.default,
408
- defaultSortDirection: null,
409
- canSetDirection: false,
410
- shownInSortBar: false,
411
- shownInURL: false,
412
- handledBySearchService: false, // We rely on the PPS default sort handling in these cases
413
- displayName: '',
414
- urlNames: ['', null, undefined], // Empty or nullish sort params result in default sorting
415
- },
416
- // Unrecognized sort is the case where the user has specified a sort in the URL, but it is not
417
- // one of the options listed in this map. We still want these unrecognized sorts to be applied
418
- // when searching, but they are not displayed in the sort bar and we do not actively manage
419
- // their URL param beyond flipping the direction as needed.
420
- [SortField.unrecognized]: {
421
- field: SortField.unrecognized,
422
- defaultSortDirection: null,
423
- canSetDirection: true,
424
- shownInSortBar: false,
425
- shownInURL: false,
426
- handledBySearchService: true, // The unrecognized sort param is passed along as-is
427
- displayName: '',
428
- urlNames: [],
429
- },
430
- // Relevance sort is unique in that it does not produce a URL param when it is set.
431
- // It is only available when there is a user-specified query that relevancy can be scored against.
432
- // Therefore, it does not appear as a sort bar option when browsing a collection with no query set.
433
- [SortField.relevance]: {
434
- field: SortField.relevance,
435
- defaultSortDirection: null,
436
- canSetDirection: false,
437
- shownInSortBar: true,
438
- shownInURL: false,
439
- handledBySearchService: false,
440
- displayName: 'Relevance',
441
- urlNames: ['_score'],
442
- },
443
- [SortField.alltimeview]: {
444
- field: SortField.alltimeview,
445
- defaultSortDirection: 'desc',
446
- canSetDirection: true,
447
- shownInSortBar: true,
448
- shownInURL: true,
449
- handledBySearchService: true,
450
- searchServiceKey: 'downloads',
451
- displayName: 'All-time views',
452
- urlNames: ['downloads'],
453
- },
454
- [SortField.weeklyview]: {
455
- field: SortField.weeklyview,
456
- defaultSortDirection: 'desc',
457
- canSetDirection: true,
458
- shownInSortBar: true,
459
- shownInURL: true,
460
- handledBySearchService: true,
461
- searchServiceKey: 'week',
462
- displayName: 'Weekly views',
463
- urlNames: ['week'],
464
- },
465
- [SortField.title]: {
466
- field: SortField.title,
467
- defaultSortDirection: 'asc',
468
- canSetDirection: true,
469
- shownInSortBar: true,
470
- shownInURL: true,
471
- handledBySearchService: true,
472
- searchServiceKey: 'titleSorter',
473
- displayName: 'Title',
474
- urlNames: ['title', 'titleSorter'],
475
- },
476
- [SortField.date]: {
477
- field: SortField.date,
478
- defaultSortDirection: 'desc',
479
- canSetDirection: true,
480
- shownInSortBar: true,
481
- shownInURL: true,
482
- handledBySearchService: true,
483
- searchServiceKey: 'date',
484
- displayName: 'Date published',
485
- urlNames: ['date'],
486
- },
487
- [SortField.datearchived]: {
488
- field: SortField.datearchived,
489
- defaultSortDirection: 'desc',
490
- canSetDirection: true,
491
- shownInSortBar: true,
492
- shownInURL: true,
493
- handledBySearchService: true,
494
- searchServiceKey: 'publicdate',
495
- displayName: 'Date archived',
496
- urlNames: ['publicdate'],
497
- },
498
- [SortField.datereviewed]: {
499
- field: SortField.datereviewed,
500
- defaultSortDirection: 'desc',
501
- canSetDirection: true,
502
- shownInSortBar: true,
503
- shownInURL: true,
504
- handledBySearchService: true,
505
- searchServiceKey: 'reviewdate',
506
- displayName: 'Date reviewed',
507
- urlNames: ['reviewdate'],
508
- },
509
- [SortField.dateadded]: {
510
- field: SortField.dateadded,
511
- defaultSortDirection: 'desc',
512
- canSetDirection: true,
513
- shownInSortBar: true,
514
- shownInURL: true,
515
- handledBySearchService: true,
516
- searchServiceKey: 'addeddate',
517
- displayName: 'Date added',
518
- urlNames: ['addeddate'],
519
- },
520
- [SortField.datefavorited]: {
521
- field: SortField.datefavorited,
522
- defaultSortDirection: 'desc',
523
- canSetDirection: false,
524
- shownInSortBar: true, // But only when viewing fav-* collections
525
- shownInURL: false,
526
- handledBySearchService: false,
527
- searchServiceKey: 'favoritedate',
528
- displayName: 'Date favorited',
529
- urlNames: ['favoritedate'],
530
- },
531
- [SortField.creator]: {
532
- field: SortField.creator,
533
- defaultSortDirection: 'asc',
534
- canSetDirection: true,
535
- shownInSortBar: true,
536
- shownInURL: true,
537
- handledBySearchService: true,
538
- searchServiceKey: 'creatorSorter',
539
- displayName: 'Creator',
540
- urlNames: ['creator', 'creatorSorter'],
541
- },
542
- };
543
-
544
- /**
545
- * Returns the SortOption corresponding to the given API sort name, or
546
- * the "unrecognized" SortOption if none matches.
547
- */
548
- export function sortOptionFromAPIString(sortName?: string | null): SortOption {
549
- return (
550
- Object.values(SORT_OPTIONS).find(opt =>
551
- opt.urlNames.some(name => sortName === name),
552
- ) ?? SORT_OPTIONS[SortField.unrecognized]
553
- );
554
- }
555
-
556
- export const defaultSortAvailability: Record<SortField, boolean> = {
557
- [SortField.relevance]: true,
558
- [SortField.weeklyview]: true,
559
- [SortField.alltimeview]: true,
560
- [SortField.title]: true,
561
- [SortField.datefavorited]: false,
562
- [SortField.date]: true,
563
- [SortField.datearchived]: true,
564
- [SortField.datereviewed]: true,
565
- [SortField.dateadded]: true,
566
- [SortField.creator]: true,
567
- [SortField.default]: false,
568
- [SortField.unrecognized]: false,
569
- };
570
-
571
- export const favoritesSortAvailability: Record<SortField, boolean> = {
572
- ...defaultSortAvailability,
573
- [SortField.datefavorited]: true,
574
- };
575
-
576
- export const tvSortAvailability: Record<SortField, boolean> = {
577
- ...defaultSortAvailability,
578
- [SortField.date]: false,
579
- [SortField.datereviewed]: false,
580
- [SortField.dateadded]: false,
581
- };
582
-
583
- export const defaultProfileElementSorts: Record<
584
- string,
585
- Exclude<SortField, SortField.default>
586
- > = {
587
- uploads: SortField.datearchived,
588
- reviews: SortField.datereviewed,
589
- collections: SortField.datearchived,
590
- web_archives: SortField.datearchived,
591
- };
592
-
593
- /** A union of the fields that permit prefix filtering (e.g., alphabetical filtering) */
594
- export type PrefixFilterType = 'title' | 'creator';
595
-
596
- /** A map from prefixes (e.g., initial letters) to the number of items matching that prefix */
597
- export type PrefixFilterCounts = Record<string, number>;
598
-
599
- /**
600
- * A map from prefix filter types to the corresponding aggregation keys
601
- * that are needed to fetch the filter counts from the backend.
602
- */
603
- export const prefixFilterAggregationKeys: Record<PrefixFilterType, string> = {
604
- title: 'firstTitle',
605
- creator: 'firstCreator',
606
- };
607
-
608
- /**
609
- * Different facet loading strategies that can be used with collection browser.
610
- * - `eager`: Facet data is always loaded as soon as a search is performed
611
- * - `lazy-mobile`: In the desktop layout, functions exactly as `eager`.
612
- * In the mobile layout, facet data will only be loaded once the "Filters" accordion is opened.
613
- * - `opt-in-or-login`: Same as `opt-in` for guest users not logged into an account, but same as `eager` for
614
- * any logged in user.
615
- * - `opt-in`: In the desktop layout, facet data will only be loaded after the user presses a "Load Facets" button.
616
- * In the mobile layout, functions exactly as `lazy-mobile`.
617
- * - `off`: Facet data will never be loaded, and a message will be displayed in place of facets
618
- * indicating that they are unavailable.
619
- */
620
- export type FacetLoadStrategy =
621
- | 'eager'
622
- | 'lazy-mobile'
623
- | 'opt-in-or-login'
624
- | 'opt-in'
625
- | 'off';
626
-
627
- /**
628
- * Union of the facet types that are available in the sidebar.
629
- */
630
- export type FacetOption =
631
- | 'subject'
632
- | 'lending'
633
- | 'mediatype'
634
- | 'language'
635
- | 'creator'
636
- | 'collection'
637
- | 'year'
638
- // TV-specific facet options:
639
- | 'program'
640
- | 'person'
641
- | 'sponsor';
642
-
643
- export type SelectedFacetState = 'selected' | 'hidden';
644
-
645
- export type FacetState = SelectedFacetState | 'none';
646
-
647
- export interface FacetBucket {
648
- key: string;
649
- count: number;
650
- state: FacetState;
651
- // for some facets, we augment the key with a display value
652
- displayText?: string;
653
- // for TV channel facets, we add a parenthesized secondary name
654
- extraNote?: string;
655
- }
656
-
657
- export interface FacetGroup {
658
- title: string;
659
- key: FacetOption;
660
- buckets: FacetBucket[];
661
- }
662
-
663
- /**
664
- * Information about a user interaction event on a facet.
665
- */
666
- export type FacetEventDetails = {
667
- /**
668
- * The type of facet that was interacted with (e.g., 'mediatype', 'language', ...).
669
- */
670
- facetType: FacetOption;
671
- /**
672
- * The bucket corresponding to the facet that was interacted with, including the
673
- * updated state of the facet after the interaction.
674
- */
675
- bucket: FacetBucket;
676
- /**
677
- * Whether the interaction occurred on a negative facet.
678
- */
679
- negative: boolean;
680
- };
681
-
682
- export type FacetValue = string;
683
-
684
- export type SelectedFacets = Partial<
685
- Record<FacetOption, Record<FacetValue, FacetBucket>>
686
- >;
687
-
688
- export const getDefaultSelectedFacets = (): Required<SelectedFacets> => ({
689
- subject: {},
690
- lending: {},
691
- mediatype: {},
692
- language: {},
693
- creator: {},
694
- collection: {},
695
- year: {},
696
- program: {},
697
- person: {},
698
- sponsor: {},
699
- });
700
-
701
- /**
702
- * For TV search results, what types of TV clips to restrict the results to.
703
- */
704
- export type TvClipFilterType = 'all' | 'commercials' | 'factchecks' | 'quotes';
705
-
706
- /**
707
- * Map from TV clip filter types to their corresponding URL params
708
- */
709
- export const tvClipFiltersToURLParams: Record<TvClipFilterType, string> = {
710
- all: '',
711
- commercials: 'only_commercials',
712
- factchecks: 'only_factchecks',
713
- quotes: 'only_quotes',
714
- };
715
-
716
- /**
717
- * Map from allowed TV filtering parameters in the URL to their corresponding filter type
718
- */
719
- export const tvClipURLParamsToFilters: Record<string, TvClipFilterType> = {
720
- only_commercials: 'commercials',
721
- only_factchecks: 'factchecks',
722
- only_quotes: 'quotes',
723
- };
724
-
725
- /**
726
- * Facet display order when presenting results for all search types *except* TV (see below).
727
- */
728
- export const defaultFacetDisplayOrder: FacetOption[] = [
729
- 'mediatype',
730
- // 'lending', Commenting this out removes the lending facet from the sidebar for now
731
- 'year',
732
- 'subject',
733
- 'collection',
734
- 'creator',
735
- 'language',
736
- ];
737
-
738
- /**
739
- * Specialized facet ordering when displaying TV search results
740
- */
741
- export const tvFacetDisplayOrder: FacetOption[] = [
742
- 'program',
743
- 'creator',
744
- 'year',
745
- 'subject',
746
- 'collection',
747
- 'person',
748
- 'sponsor',
749
- 'language',
750
- ];
751
-
752
- /**
753
- * Human-readable titles for each facet group.
754
- */
755
- export const facetTitles: Record<FacetOption, string> = {
756
- subject: 'Subject',
757
- lending: 'Availability',
758
- mediatype: 'Media Type',
759
- language: 'Language',
760
- creator: 'Creator',
761
- collection: 'Collection',
762
- year: 'Year',
763
- program: 'Program',
764
- person: 'Person',
765
- sponsor: 'Sponsor',
766
- };
767
-
768
- /**
769
- * The default sort type to use for each facet type
770
- */
771
- export const defaultFacetSort: Record<FacetOption, AggregationSortType> = {
772
- subject: AggregationSortType.COUNT,
773
- lending: AggregationSortType.COUNT,
774
- mediatype: AggregationSortType.COUNT,
775
- language: AggregationSortType.COUNT,
776
- creator: AggregationSortType.COUNT,
777
- collection: AggregationSortType.COUNT,
778
- year: AggregationSortType.NUMERIC, // Year facets are ordered by their numeric value by default
779
- program: AggregationSortType.COUNT,
780
- person: AggregationSortType.COUNT,
781
- sponsor: AggregationSortType.COUNT,
782
- };
783
-
784
- /**
785
- * The sort type corresponding to facet bucket values, for each facet type
786
- * (i.e., the opposite of "sort by count" for that type).
787
- */
788
- export const valueFacetSort: Record<FacetOption, AggregationSortType> = {
789
- subject: AggregationSortType.ALPHABETICAL,
790
- lending: AggregationSortType.ALPHABETICAL,
791
- mediatype: AggregationSortType.ALPHABETICAL,
792
- language: AggregationSortType.ALPHABETICAL,
793
- creator: AggregationSortType.ALPHABETICAL,
794
- collection: AggregationSortType.ALPHABETICAL,
795
- year: AggregationSortType.NUMERIC, // Year facets' values should be compared numerically, not lexicographically (year 2001 > year 3)
796
- program: AggregationSortType.ALPHABETICAL,
797
- person: AggregationSortType.ALPHABETICAL,
798
- sponsor: AggregationSortType.ALPHABETICAL,
799
- };
800
-
801
- export type LendingFacetKey =
802
- | 'is_lendable'
803
- | 'is_borrowable'
804
- | 'available_to_borrow'
805
- | 'is_browsable'
806
- | 'available_to_browse'
807
- | 'is_readable'
808
- | 'available_to_waitlist';
809
-
810
- /**
811
- * Maps valid lending keys to whether they should be visible in the facet sidebar
812
- */
813
- export const lendingFacetKeysVisibility: Record<LendingFacetKey, boolean> = {
814
- is_lendable: true,
815
- is_borrowable: false,
816
- available_to_borrow: true,
817
- is_browsable: false,
818
- available_to_browse: false,
819
- is_readable: true,
820
- available_to_waitlist: false,
821
- };
822
-
823
- /**
824
- * Maps valid, visible lending keys to their facet sidebar display text
825
- */
826
- export const lendingFacetDisplayNames: Partial<
827
- Record<LendingFacetKey, string>
828
- > = {
829
- is_lendable: 'Lending Library',
830
- available_to_borrow: 'Borrow 14 Days',
831
- is_readable: 'Always Available',
832
- };
833
-
834
- /**
835
- * A record of which admin-only collections should be suppressed from being displayed
836
- * as facets or in an item's list of collections.
837
- */
838
- export const suppressedCollections: Record<string, boolean> = {
839
- deemphasize: true,
840
- community: true,
841
- stream_only: true,
842
- samples_only: true,
843
- test_collection: true,
844
- printdisabled: true,
845
- 'openlibrary-ol': true,
846
- nationalemergencylibrary: true,
847
- china: true,
848
- americana: true,
849
- toronto: true,
850
- };
851
-
852
- /**
853
- * A record of manageable item
854
- */
855
- export interface ManageableItem {
856
- identifier: string;
857
- title?: string;
858
- dateStr?: string;
859
- date?: string;
860
- }
861
-
862
- /**
863
- * Possible states for whether & how the user has overridden their user preference
864
- * for blurring behavior on tiles with sensitive content.
865
- * - `no-override`: The user has not overridden their user preference, so simply
866
- * respect the preference as given.
867
- * - `on`: The user has overridden their preference and wants tile blurring enabled.
868
- * - `off`: The user has overridden their preference and wants tile blurring disabled.
869
- */
870
- export type TileBlurOverrideState = 'no-override' | 'on' | 'off';
1
+ import type { TemplateResult } from 'lit';
2
+ import { msg } from '@lit/localize';
3
+ import type { MediaType } from '@internetarchive/field-parsers';
4
+ import {
5
+ AggregationSortType,
6
+ HitType,
7
+ SearchReview,
8
+ SearchResult,
9
+ SortDirection,
10
+ } from '@internetarchive/search-service';
11
+ import { collapseRepeatedQuotes } from './utils/collapse-repeated-quotes';
12
+ import { resolveMediatype } from './utils/resolve-mediatype';
13
+
14
+ import { loginRequiredIcon } from './assets/img/icons/login-required';
15
+ import { restrictedIcon } from './assets/img/icons/restricted';
16
+
17
+ /**
18
+ * Flags that can affect the visibility of content on a tile
19
+ */
20
+ interface TileFlags {
21
+ loginRequired: boolean;
22
+ contentWarning: boolean;
23
+ }
24
+
25
+ /**
26
+ * Different types of tile overlays, corresponding to the above flags.
27
+ */
28
+ export type TileOverlayType = 'login-required' | 'content-warning';
29
+
30
+ export const TILE_OVERLAY_TEXT: Record<TileOverlayType, string> = {
31
+ 'login-required': msg('Log in to view this item'),
32
+ 'content-warning': msg('Content may be inappropriate'),
33
+ };
34
+
35
+ export const TILE_OVERLAY_ICONS: Record<TileOverlayType, TemplateResult> = {
36
+ 'login-required': loginRequiredIcon,
37
+ 'content-warning': restrictedIcon,
38
+ };
39
+
40
+ /**
41
+ * What type of request produced a given set of hits:
42
+ * - `search_query`: Hits produced by an explicit user query and/or applied filters on any page
43
+ * - `collection_members`: Hits produced for a collection page without any query or filters
44
+ * - `profile_tab`: Hits produced for a tab of the profile page without any query or filters
45
+ * - `unknown`: Hits produced via any other means
46
+ */
47
+ export type HitRequestSource =
48
+ | 'search_query'
49
+ | 'collection_members'
50
+ | 'profile_tab'
51
+ | 'unknown';
52
+
53
+ /**
54
+ * Class for converting & storing raw search results in the correct format for UI tiles.
55
+ */
56
+ export class TileModel {
57
+ /** For TV hits. List of identifiers for any commercials contained. */
58
+ adIds?: string[];
59
+
60
+ averageRating?: number;
61
+
62
+ /** For Web Archive hits on profile pages. List of capture dates for the current URL. */
63
+ captureDates?: Date[];
64
+
65
+ /** Whether this tile is currently checked for item management functions */
66
+ checked: boolean;
67
+
68
+ collectionIdentifier?: string;
69
+
70
+ collectionName?: string;
71
+
72
+ collectionFilesCount: number;
73
+
74
+ collections: string[];
75
+
76
+ collectionSize: number;
77
+
78
+ commentCount: number;
79
+
80
+ creator?: string;
81
+
82
+ creators: string[];
83
+
84
+ /** A string representation of the publication date, used strictly for passing preformatted dates to the parent */
85
+ dateStr?: string;
86
+
87
+ /** Date added to public search (software-defined) [from MD field: addeddate] */
88
+ dateAdded?: Date;
89
+
90
+ /** Date archived (software-defined) item created on archive.org [from MD field: publicdate] */
91
+ dateArchived?: Date;
92
+
93
+ /** Date work published in the world (user-defined) [from MD field: date] */
94
+ datePublished?: Date;
95
+
96
+ /** Date reviewed (user-created) most recent review [from MD field: reviewdate] */
97
+ dateReviewed?: Date;
98
+
99
+ description?: string;
100
+
101
+ /** For TV hits. List of URLs for any fact-checks of the contained clips. */
102
+ factChecks?: string[];
103
+
104
+ favCount: number;
105
+
106
+ hitRequestSource: HitRequestSource;
107
+
108
+ hitType?: HitType;
109
+
110
+ href?: string;
111
+
112
+ identifier?: string;
113
+
114
+ /** Whether this model represents a TV clip */
115
+ isClip?: boolean;
116
+
117
+ issue?: string;
118
+
119
+ itemCount: number;
120
+
121
+ mediatype: MediaType;
122
+
123
+ review?: SearchReview;
124
+
125
+ source?: string;
126
+
127
+ snippets?: string[];
128
+
129
+ subjects: string[];
130
+
131
+ thumbnailUrl?: string;
132
+
133
+ title: string;
134
+
135
+ tvClipCount?: number;
136
+
137
+ viewCount?: number;
138
+
139
+ volume?: string;
140
+
141
+ weeklyViewCount?: number;
142
+
143
+ loginRequired: boolean;
144
+
145
+ contentWarning: boolean;
146
+
147
+ constructor(
148
+ result: SearchResult,
149
+ hitRequestSource: HitRequestSource = 'unknown',
150
+ ) {
151
+ const flags = this.getFlags(result);
152
+
153
+ this.adIds = result.ad_id?.values;
154
+ this.averageRating = result.avg_rating?.value;
155
+ this.captureDates = result.capture_dates?.values;
156
+ this.checked = false;
157
+ this.collections = result.collection?.values ?? [];
158
+ this.collectionFilesCount = result.collection_files_count?.value ?? 0;
159
+ this.collectionSize = result.collection_size?.value ?? 0;
160
+ this.commentCount = result.num_reviews?.value ?? 0;
161
+ this.creator = result.creator?.value;
162
+ this.creators = result.creator?.values ?? [];
163
+ this.dateAdded = result.addeddate?.value;
164
+ this.dateArchived = result.publicdate?.value;
165
+ this.datePublished = result.date?.value;
166
+ this.dateReviewed = result.reviewdate?.value;
167
+ this.description = result.description?.values.join('\n');
168
+ this.factChecks = result.factcheck?.values;
169
+ this.favCount = result.num_favorites?.value ?? 0;
170
+ this.hitRequestSource = hitRequestSource;
171
+ this.hitType = result.rawMetadata?.hit_type;
172
+ this.href = collapseRepeatedQuotes(
173
+ result.review?.__href__ ?? result.__href__?.value,
174
+ );
175
+ this.identifier = TileModel.cleanIdentifier(result.identifier);
176
+ this.isClip = result.is_clip?.value;
177
+ this.issue = result.issue?.value;
178
+ this.itemCount = result.item_count?.value ?? 0;
179
+ this.mediatype = resolveMediatype(result);
180
+ this.review = result.review;
181
+ this.snippets = result.highlight?.values ?? [];
182
+ this.source = result.source?.value;
183
+ this.subjects = result.subject?.values ?? [];
184
+ this.thumbnailUrl = result.__img__?.value;
185
+ this.title = result.title?.value ?? '';
186
+ this.tvClipCount = result.num_clips?.value ?? 0;
187
+ this.volume = result.volume?.value;
188
+ this.viewCount = result.downloads?.value;
189
+ this.weeklyViewCount = result.week?.value;
190
+ this.loginRequired = flags.loginRequired;
191
+ this.contentWarning = flags.contentWarning;
192
+ }
193
+
194
+ /**
195
+ * Copies the contents of this TileModel onto a new instance
196
+ */
197
+ clone(): TileModel {
198
+ const cloned = new TileModel({});
199
+ cloned.adIds = this.adIds;
200
+ cloned.averageRating = this.averageRating;
201
+ cloned.captureDates = this.captureDates;
202
+ cloned.checked = this.checked;
203
+ cloned.collections = this.collections;
204
+ cloned.collectionFilesCount = this.collectionFilesCount;
205
+ cloned.collectionSize = this.collectionSize;
206
+ cloned.commentCount = this.commentCount;
207
+ cloned.creator = this.creator;
208
+ cloned.creators = this.creators;
209
+ cloned.dateStr = this.dateStr;
210
+ cloned.dateAdded = this.dateAdded;
211
+ cloned.dateArchived = this.dateArchived;
212
+ cloned.datePublished = this.datePublished;
213
+ cloned.dateReviewed = this.dateReviewed;
214
+ cloned.description = this.description;
215
+ cloned.factChecks = this.factChecks;
216
+ cloned.favCount = this.favCount;
217
+ cloned.hitRequestSource = this.hitRequestSource;
218
+ cloned.hitType = this.hitType;
219
+ cloned.href = this.href;
220
+ cloned.identifier = this.identifier;
221
+ cloned.isClip = this.isClip;
222
+ cloned.issue = this.issue;
223
+ cloned.itemCount = this.itemCount;
224
+ cloned.mediatype = this.mediatype;
225
+ cloned.snippets = this.snippets;
226
+ cloned.source = this.source;
227
+ cloned.subjects = this.subjects;
228
+ cloned.thumbnailUrl = this.thumbnailUrl;
229
+ cloned.title = this.title;
230
+ cloned.tvClipCount = this.tvClipCount;
231
+ cloned.volume = this.volume;
232
+ cloned.viewCount = this.viewCount;
233
+ cloned.weeklyViewCount = this.weeklyViewCount;
234
+ cloned.loginRequired = this.loginRequired;
235
+ cloned.contentWarning = this.contentWarning;
236
+ return cloned;
237
+ }
238
+
239
+ /**
240
+ * Whether this model represents the result of a TV search query.
241
+ */
242
+ get isTvSearchResult(): boolean {
243
+ return (
244
+ this.hitType === 'tv_clip' && this.hitRequestSource === 'search_query'
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Determines the appropriate tile flags for the given search result
250
+ * (login required and/or content warning)
251
+ */
252
+ private getFlags(result: SearchResult): TileFlags {
253
+ const flags: TileFlags = {
254
+ loginRequired: false,
255
+ contentWarning: false,
256
+ };
257
+
258
+ // Check if item and item in "modifying" collection, setting above flags
259
+ if (
260
+ result.collection?.values.length &&
261
+ result.mediatype?.value !== 'collection'
262
+ ) {
263
+ for (const collection of result.collection?.values ?? []) {
264
+ if (collection === 'loggedin') {
265
+ flags.loginRequired = true;
266
+ if (flags.contentWarning) break;
267
+ }
268
+ if (collection === 'no-preview') {
269
+ flags.contentWarning = true;
270
+ if (flags.loginRequired) break;
271
+ }
272
+ }
273
+ }
274
+
275
+ return flags;
276
+ }
277
+
278
+ private static cleanIdentifier(
279
+ identifier: string | undefined,
280
+ ): string | undefined {
281
+ // Some identifiers (e.g., from Whisper) represent documents rather than items, and
282
+ // are suffixed with values that need to be stripped. Those values are separated
283
+ // from the item identifier itself with '|'.
284
+ const barIndex = identifier?.indexOf('|') ?? -1;
285
+ const cleaned = barIndex > 0 ? identifier?.slice(0, barIndex) : identifier;
286
+ return cleaned;
287
+ }
288
+ }
289
+
290
+ export type RequestKind = 'full' | 'hits' | 'aggregations';
291
+
292
+ export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
293
+
294
+ export type TileDisplayMode =
295
+ | 'grid'
296
+ | 'list-compact'
297
+ | 'list-detail'
298
+ | 'list-header';
299
+
300
+ /**
301
+ * This is mainly used to set the cookies for the collection display mode.
302
+ *
303
+ * It allows the user to set different modes for different contexts (collection page, search page, profile page etc).
304
+ */
305
+ export type CollectionBrowserContext = 'collection' | 'search' | 'profile';
306
+
307
+ /**
308
+ * The sort fields shown in the sort filter bar
309
+ */
310
+ export enum SortField {
311
+ 'default' = 'default',
312
+ 'unrecognized' = 'unrecognized',
313
+ 'relevance' = 'relevance',
314
+ 'alltimeview' = 'alltimeview',
315
+ 'weeklyview' = 'weeklyview',
316
+ 'title' = 'title',
317
+ 'date' = 'date',
318
+ 'datearchived' = 'datearchived',
319
+ 'datereviewed' = 'datereviewed',
320
+ 'dateadded' = 'dateadded',
321
+ 'datefavorited' = 'datefavorited',
322
+ 'creator' = 'creator',
323
+ }
324
+
325
+ /**
326
+ * Views-related sort fields
327
+ */
328
+ export const ALL_VIEWS_SORT_FIELDS = [
329
+ SortField.weeklyview,
330
+ SortField.alltimeview,
331
+ ] as const;
332
+ export type ViewsSortField = (typeof ALL_VIEWS_SORT_FIELDS)[number];
333
+
334
+ /**
335
+ * Date-related sort fields
336
+ */
337
+ export const ALL_DATE_SORT_FIELDS = [
338
+ SortField.datefavorited,
339
+ SortField.date,
340
+ SortField.datearchived,
341
+ SortField.datereviewed,
342
+ SortField.dateadded,
343
+ ] as const;
344
+ export type DateSortField = (typeof ALL_DATE_SORT_FIELDS)[number];
345
+
346
+ export interface SortOption {
347
+ /**
348
+ * The SortField enum member corresponding to this option.
349
+ */
350
+ field: SortField;
351
+
352
+ /**
353
+ * The default sort direction to apply when this sort option is first selected.
354
+ */
355
+ defaultSortDirection: SortDirection | null;
356
+
357
+ /**
358
+ * Whether this sort option allows its sort direction to be changed from the default.
359
+ */
360
+ canSetDirection: boolean;
361
+
362
+ /**
363
+ * Whether this sort option may appear in the sort bar.
364
+ */
365
+ shownInSortBar: boolean;
366
+
367
+ /**
368
+ * Whether this sort option should be saved to the URL.
369
+ * If false, then no `sort` param will be added to the URL when this sort option
370
+ * is selected.
371
+ */
372
+ shownInURL: boolean;
373
+
374
+ /**
375
+ * Whether this sort option is passed to the search service.
376
+ * If false, then no sort param will be passed to the search service at all when
377
+ * this sort option is selected.
378
+ */
379
+ handledBySearchService: boolean;
380
+
381
+ /**
382
+ * The string identifying this sort field to the search service & backend API.
383
+ */
384
+ searchServiceKey?: string;
385
+
386
+ /**
387
+ * The human-readable name to use for this option in the sort bar (if applicable).
388
+ */
389
+ displayName: string;
390
+
391
+ /**
392
+ * A list of URL param keys that should be mapped to this sort option.
393
+ * E.g., both `title` and `titleSorter` in the URL map to the `SortField.title` option.
394
+ */
395
+ urlNames: (string | null | undefined)[];
396
+ }
397
+
398
+ export const SORT_OPTIONS: Record<SortField, SortOption> = {
399
+ // Default sort is the case where the user has not specified a sort option via the sort bar or URL.
400
+ // In these cases, we defer to whatever sort the backend chooses.
401
+ // For the search page, the default is always relevance sort.
402
+ // For collection pages _without a query_, the default is usually weekly views, but this can be
403
+ // overridden by the collection's `sort-by` metadata entry. If a query _is_ specified, then the
404
+ // default is again relevance sort.
405
+ // For fav-* collections only, the default is instead sorting by date favorited.
406
+ [SortField.default]: {
407
+ field: SortField.default,
408
+ defaultSortDirection: null,
409
+ canSetDirection: false,
410
+ shownInSortBar: false,
411
+ shownInURL: false,
412
+ handledBySearchService: false, // We rely on the PPS default sort handling in these cases
413
+ displayName: '',
414
+ urlNames: ['', null, undefined], // Empty or nullish sort params result in default sorting
415
+ },
416
+ // Unrecognized sort is the case where the user has specified a sort in the URL, but it is not
417
+ // one of the options listed in this map. We still want these unrecognized sorts to be applied
418
+ // when searching, but they are not displayed in the sort bar and we do not actively manage
419
+ // their URL param beyond flipping the direction as needed.
420
+ [SortField.unrecognized]: {
421
+ field: SortField.unrecognized,
422
+ defaultSortDirection: null,
423
+ canSetDirection: true,
424
+ shownInSortBar: false,
425
+ shownInURL: false,
426
+ handledBySearchService: true, // The unrecognized sort param is passed along as-is
427
+ displayName: '',
428
+ urlNames: [],
429
+ },
430
+ // Relevance sort is unique in that it does not produce a URL param when it is set.
431
+ // It is only available when there is a user-specified query that relevancy can be scored against.
432
+ // Therefore, it does not appear as a sort bar option when browsing a collection with no query set.
433
+ [SortField.relevance]: {
434
+ field: SortField.relevance,
435
+ defaultSortDirection: null,
436
+ canSetDirection: false,
437
+ shownInSortBar: true,
438
+ shownInURL: false,
439
+ handledBySearchService: false,
440
+ displayName: 'Relevance',
441
+ urlNames: ['_score'],
442
+ },
443
+ [SortField.alltimeview]: {
444
+ field: SortField.alltimeview,
445
+ defaultSortDirection: 'desc',
446
+ canSetDirection: true,
447
+ shownInSortBar: true,
448
+ shownInURL: true,
449
+ handledBySearchService: true,
450
+ searchServiceKey: 'downloads',
451
+ displayName: 'All-time views',
452
+ urlNames: ['downloads'],
453
+ },
454
+ [SortField.weeklyview]: {
455
+ field: SortField.weeklyview,
456
+ defaultSortDirection: 'desc',
457
+ canSetDirection: true,
458
+ shownInSortBar: true,
459
+ shownInURL: true,
460
+ handledBySearchService: true,
461
+ searchServiceKey: 'week',
462
+ displayName: 'Weekly views',
463
+ urlNames: ['week'],
464
+ },
465
+ [SortField.title]: {
466
+ field: SortField.title,
467
+ defaultSortDirection: 'asc',
468
+ canSetDirection: true,
469
+ shownInSortBar: true,
470
+ shownInURL: true,
471
+ handledBySearchService: true,
472
+ searchServiceKey: 'titleSorter',
473
+ displayName: 'Title',
474
+ urlNames: ['title', 'titleSorter'],
475
+ },
476
+ [SortField.date]: {
477
+ field: SortField.date,
478
+ defaultSortDirection: 'desc',
479
+ canSetDirection: true,
480
+ shownInSortBar: true,
481
+ shownInURL: true,
482
+ handledBySearchService: true,
483
+ searchServiceKey: 'date',
484
+ displayName: 'Date published',
485
+ urlNames: ['date'],
486
+ },
487
+ [SortField.datearchived]: {
488
+ field: SortField.datearchived,
489
+ defaultSortDirection: 'desc',
490
+ canSetDirection: true,
491
+ shownInSortBar: true,
492
+ shownInURL: true,
493
+ handledBySearchService: true,
494
+ searchServiceKey: 'publicdate',
495
+ displayName: 'Date archived',
496
+ urlNames: ['publicdate'],
497
+ },
498
+ [SortField.datereviewed]: {
499
+ field: SortField.datereviewed,
500
+ defaultSortDirection: 'desc',
501
+ canSetDirection: true,
502
+ shownInSortBar: true,
503
+ shownInURL: true,
504
+ handledBySearchService: true,
505
+ searchServiceKey: 'reviewdate',
506
+ displayName: 'Date reviewed',
507
+ urlNames: ['reviewdate'],
508
+ },
509
+ [SortField.dateadded]: {
510
+ field: SortField.dateadded,
511
+ defaultSortDirection: 'desc',
512
+ canSetDirection: true,
513
+ shownInSortBar: true,
514
+ shownInURL: true,
515
+ handledBySearchService: true,
516
+ searchServiceKey: 'addeddate',
517
+ displayName: 'Date added',
518
+ urlNames: ['addeddate'],
519
+ },
520
+ [SortField.datefavorited]: {
521
+ field: SortField.datefavorited,
522
+ defaultSortDirection: 'desc',
523
+ canSetDirection: false,
524
+ shownInSortBar: true, // But only when viewing fav-* collections
525
+ shownInURL: false,
526
+ handledBySearchService: false,
527
+ searchServiceKey: 'favoritedate',
528
+ displayName: 'Date favorited',
529
+ urlNames: ['favoritedate'],
530
+ },
531
+ [SortField.creator]: {
532
+ field: SortField.creator,
533
+ defaultSortDirection: 'asc',
534
+ canSetDirection: true,
535
+ shownInSortBar: true,
536
+ shownInURL: true,
537
+ handledBySearchService: true,
538
+ searchServiceKey: 'creatorSorter',
539
+ displayName: 'Creator',
540
+ urlNames: ['creator', 'creatorSorter'],
541
+ },
542
+ };
543
+
544
+ /**
545
+ * Returns the SortOption corresponding to the given API sort name, or
546
+ * the "unrecognized" SortOption if none matches.
547
+ */
548
+ export function sortOptionFromAPIString(sortName?: string | null): SortOption {
549
+ return (
550
+ Object.values(SORT_OPTIONS).find(opt =>
551
+ opt.urlNames.some(name => sortName === name),
552
+ ) ?? SORT_OPTIONS[SortField.unrecognized]
553
+ );
554
+ }
555
+
556
+ export const defaultSortAvailability: Record<SortField, boolean> = {
557
+ [SortField.relevance]: true,
558
+ [SortField.weeklyview]: true,
559
+ [SortField.alltimeview]: true,
560
+ [SortField.title]: true,
561
+ [SortField.datefavorited]: false,
562
+ [SortField.date]: true,
563
+ [SortField.datearchived]: true,
564
+ [SortField.datereviewed]: true,
565
+ [SortField.dateadded]: true,
566
+ [SortField.creator]: true,
567
+ [SortField.default]: false,
568
+ [SortField.unrecognized]: false,
569
+ };
570
+
571
+ export const favoritesSortAvailability: Record<SortField, boolean> = {
572
+ ...defaultSortAvailability,
573
+ [SortField.datefavorited]: true,
574
+ };
575
+
576
+ export const tvSortAvailability: Record<SortField, boolean> = {
577
+ ...defaultSortAvailability,
578
+ [SortField.date]: false,
579
+ [SortField.datereviewed]: false,
580
+ [SortField.dateadded]: false,
581
+ };
582
+
583
+ export const defaultProfileElementSorts: Record<
584
+ string,
585
+ Exclude<SortField, SortField.default>
586
+ > = {
587
+ uploads: SortField.datearchived,
588
+ reviews: SortField.datereviewed,
589
+ collections: SortField.datearchived,
590
+ web_archives: SortField.datearchived,
591
+ };
592
+
593
+ /** A union of the fields that permit prefix filtering (e.g., alphabetical filtering) */
594
+ export type PrefixFilterType = 'title' | 'creator';
595
+
596
+ /** A map from prefixes (e.g., initial letters) to the number of items matching that prefix */
597
+ export type PrefixFilterCounts = Record<string, number>;
598
+
599
+ /**
600
+ * A map from prefix filter types to the corresponding aggregation keys
601
+ * that are needed to fetch the filter counts from the backend.
602
+ */
603
+ export const prefixFilterAggregationKeys: Record<PrefixFilterType, string> = {
604
+ title: 'firstTitle',
605
+ creator: 'firstCreator',
606
+ };
607
+
608
+ /**
609
+ * Different facet loading strategies that can be used with collection browser.
610
+ * - `eager`: Facet data is always loaded as soon as a search is performed
611
+ * - `lazy-mobile`: In the desktop layout, functions exactly as `eager`.
612
+ * In the mobile layout, facet data will only be loaded once the "Filters" accordion is opened.
613
+ * - `opt-in-or-login`: Same as `opt-in` for guest users not logged into an account, but same as `eager` for
614
+ * any logged in user.
615
+ * - `opt-in`: In the desktop layout, facet data will only be loaded after the user presses a "Load Facets" button.
616
+ * In the mobile layout, functions exactly as `lazy-mobile`.
617
+ * - `off`: Facet data will never be loaded, and a message will be displayed in place of facets
618
+ * indicating that they are unavailable.
619
+ */
620
+ export type FacetLoadStrategy =
621
+ | 'eager'
622
+ | 'lazy-mobile'
623
+ | 'opt-in-or-login'
624
+ | 'opt-in'
625
+ | 'off';
626
+
627
+ /**
628
+ * Union of the facet types that are available in the sidebar.
629
+ */
630
+ export type FacetOption =
631
+ | 'subject'
632
+ | 'lending'
633
+ | 'mediatype'
634
+ | 'language'
635
+ | 'creator'
636
+ | 'collection'
637
+ | 'year'
638
+ // TV-specific facet options:
639
+ | 'clip_type'
640
+ | 'program'
641
+ | 'person'
642
+ | 'sponsor';
643
+
644
+ export type SelectedFacetState = 'selected' | 'hidden';
645
+
646
+ export type FacetState = SelectedFacetState | 'none';
647
+
648
+ export interface FacetBucket {
649
+ key: string;
650
+ count: number;
651
+ state: FacetState;
652
+ // for some facets, we augment the key with a display value
653
+ displayText?: string;
654
+ // for TV channel facets, we add a parenthesized secondary name
655
+ extraNote?: string;
656
+ }
657
+
658
+ export interface FacetGroup {
659
+ title: string;
660
+ key: FacetOption;
661
+ buckets: FacetBucket[];
662
+ }
663
+
664
+ /**
665
+ * Information about a user interaction event on a facet.
666
+ */
667
+ export type FacetEventDetails = {
668
+ /**
669
+ * The type of facet that was interacted with (e.g., 'mediatype', 'language', ...).
670
+ */
671
+ facetType: FacetOption;
672
+ /**
673
+ * The bucket corresponding to the facet that was interacted with, including the
674
+ * updated state of the facet after the interaction.
675
+ */
676
+ bucket: FacetBucket;
677
+ /**
678
+ * Whether the interaction occurred on a negative facet.
679
+ */
680
+ negative: boolean;
681
+ };
682
+
683
+ export type FacetValue = string;
684
+
685
+ export type SelectedFacets = Partial<
686
+ Record<FacetOption, Record<FacetValue, FacetBucket>>
687
+ >;
688
+
689
+ export const getDefaultSelectedFacets = (): Required<SelectedFacets> => ({
690
+ subject: {},
691
+ lending: {},
692
+ mediatype: {},
693
+ language: {},
694
+ creator: {},
695
+ collection: {},
696
+ year: {},
697
+ clip_type: {},
698
+ program: {},
699
+ person: {},
700
+ sponsor: {},
701
+ });
702
+
703
+ /**
704
+ * For TV search results, what types of TV clips to restrict the results to.
705
+ */
706
+ export type TvClipFilterType = 'commercial' | 'fact check' | 'quote';
707
+
708
+ /**
709
+ * Map from allowed TV filtering parameters in the URL to their corresponding filter type
710
+ */
711
+ export const tvClipURLParamsToFilters: Record<string, TvClipFilterType> = {
712
+ only_commercials: 'commercial',
713
+ only_factchecks: 'fact check',
714
+ only_quotes: 'quote',
715
+ };
716
+
717
+ /**
718
+ * Facet display order when presenting results for all search types *except* TV (see below).
719
+ */
720
+ export const defaultFacetDisplayOrder: FacetOption[] = [
721
+ 'mediatype',
722
+ // 'lending', Commenting this out removes the lending facet from the sidebar for now
723
+ 'year',
724
+ 'subject',
725
+ 'collection',
726
+ 'creator',
727
+ 'language',
728
+ ];
729
+
730
+ /**
731
+ * Specialized facet ordering when displaying TV search results
732
+ */
733
+ export const tvFacetDisplayOrder: FacetOption[] = [
734
+ 'clip_type',
735
+ 'program',
736
+ 'creator',
737
+ 'year',
738
+ 'subject',
739
+ 'collection',
740
+ 'person',
741
+ 'sponsor',
742
+ 'language',
743
+ ];
744
+
745
+ /**
746
+ * Human-readable titles for each facet group.
747
+ */
748
+ export const facetTitles: Record<FacetOption, string> = {
749
+ subject: 'Subject',
750
+ lending: 'Availability',
751
+ mediatype: 'Media Type',
752
+ language: 'Language',
753
+ creator: 'Creator',
754
+ collection: 'Collection',
755
+ year: 'Year',
756
+ clip_type: 'Clip Type',
757
+ program: 'Program',
758
+ person: 'Person',
759
+ sponsor: 'Sponsor',
760
+ };
761
+
762
+ /**
763
+ * The default sort type to use for each facet type
764
+ */
765
+ export const defaultFacetSort: Record<FacetOption, AggregationSortType> = {
766
+ subject: AggregationSortType.COUNT,
767
+ lending: AggregationSortType.COUNT,
768
+ mediatype: AggregationSortType.COUNT,
769
+ language: AggregationSortType.COUNT,
770
+ creator: AggregationSortType.COUNT,
771
+ collection: AggregationSortType.COUNT,
772
+ year: AggregationSortType.NUMERIC, // Year facets are ordered by their numeric value by default
773
+ clip_type: AggregationSortType.COUNT,
774
+ program: AggregationSortType.COUNT,
775
+ person: AggregationSortType.COUNT,
776
+ sponsor: AggregationSortType.COUNT,
777
+ };
778
+
779
+ /**
780
+ * The sort type corresponding to facet bucket values, for each facet type
781
+ * (i.e., the opposite of "sort by count" for that type).
782
+ */
783
+ export const valueFacetSort: Record<FacetOption, AggregationSortType> = {
784
+ subject: AggregationSortType.ALPHABETICAL,
785
+ lending: AggregationSortType.ALPHABETICAL,
786
+ mediatype: AggregationSortType.ALPHABETICAL,
787
+ language: AggregationSortType.ALPHABETICAL,
788
+ creator: AggregationSortType.ALPHABETICAL,
789
+ collection: AggregationSortType.ALPHABETICAL,
790
+ year: AggregationSortType.NUMERIC, // Year facets' values should be compared numerically, not lexicographically (year 2001 > year 3)
791
+ clip_type: AggregationSortType.ALPHABETICAL,
792
+ program: AggregationSortType.ALPHABETICAL,
793
+ person: AggregationSortType.ALPHABETICAL,
794
+ sponsor: AggregationSortType.ALPHABETICAL,
795
+ };
796
+
797
+ export type LendingFacetKey =
798
+ | 'is_lendable'
799
+ | 'is_borrowable'
800
+ | 'available_to_borrow'
801
+ | 'is_browsable'
802
+ | 'available_to_browse'
803
+ | 'is_readable'
804
+ | 'available_to_waitlist';
805
+
806
+ /**
807
+ * Maps valid lending keys to whether they should be visible in the facet sidebar
808
+ */
809
+ export const lendingFacetKeysVisibility: Record<LendingFacetKey, boolean> = {
810
+ is_lendable: true,
811
+ is_borrowable: false,
812
+ available_to_borrow: true,
813
+ is_browsable: false,
814
+ available_to_browse: false,
815
+ is_readable: true,
816
+ available_to_waitlist: false,
817
+ };
818
+
819
+ /**
820
+ * Maps valid, visible lending keys to their facet sidebar display text
821
+ */
822
+ export const lendingFacetDisplayNames: Partial<
823
+ Record<LendingFacetKey, string>
824
+ > = {
825
+ is_lendable: 'Lending Library',
826
+ available_to_borrow: 'Borrow 14 Days',
827
+ is_readable: 'Always Available',
828
+ };
829
+
830
+ /**
831
+ * A record of which admin-only collections should be suppressed from being displayed
832
+ * as facets or in an item's list of collections.
833
+ */
834
+ export const suppressedCollections: Record<string, boolean> = {
835
+ deemphasize: true,
836
+ community: true,
837
+ stream_only: true,
838
+ samples_only: true,
839
+ test_collection: true,
840
+ printdisabled: true,
841
+ 'openlibrary-ol': true,
842
+ nationalemergencylibrary: true,
843
+ china: true,
844
+ americana: true,
845
+ toronto: true,
846
+ };
847
+
848
+ /**
849
+ * A record of manageable item
850
+ */
851
+ export interface ManageableItem {
852
+ identifier: string;
853
+ title?: string;
854
+ dateStr?: string;
855
+ date?: string;
856
+ }
857
+
858
+ /**
859
+ * Possible states for whether & how the user has overridden their user preference
860
+ * for blurring behavior on tiles with sensitive content.
861
+ * - `no-override`: The user has not overridden their user preference, so simply
862
+ * respect the preference as given.
863
+ * - `on`: The user has overridden their preference and wants tile blurring enabled.
864
+ * - `off`: The user has overridden their preference and wants tile blurring disabled.
865
+ */
866
+ export type TileBlurOverrideState = 'no-override' | 'on' | 'off';