@internetarchive/collection-browser 4.7.0 → 4.8.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 (453) hide show
  1. package/.husky/pre-commit +0 -0
  2. package/dist/src/app-root.js +0 -2
  3. package/dist/src/app-root.js.map +1 -1
  4. package/dist/src/assets/img/icons/check.d.ts +2 -0
  5. package/dist/src/assets/img/icons/check.js +5 -0
  6. package/dist/src/assets/img/icons/check.js.map +1 -0
  7. package/dist/src/collection-facets/facet-row-template.d.ts +38 -0
  8. package/dist/src/collection-facets/facet-row-template.js +200 -0
  9. package/dist/src/collection-facets/facet-row-template.js.map +1 -0
  10. package/dist/src/collection-facets/facet-row.js +11 -186
  11. package/dist/src/collection-facets/facet-row.js.map +1 -1
  12. package/dist/src/collection-facets/models.d.ts +7 -2
  13. package/dist/src/collection-facets/models.js +7 -2
  14. package/dist/src/collection-facets/models.js.map +1 -1
  15. package/dist/src/collection-facets/more-facets-content.d.ts +87 -21
  16. package/dist/src/collection-facets/more-facets-content.js +477 -200
  17. package/dist/src/collection-facets/more-facets-content.js.map +1 -1
  18. package/dist/src/collection-facets/more-facets-scroller.d.ts +85 -0
  19. package/dist/src/collection-facets/more-facets-scroller.js +428 -0
  20. package/dist/src/collection-facets/more-facets-scroller.js.map +1 -0
  21. package/dist/src/collection-facets/page-window.d.ts +73 -0
  22. package/dist/src/collection-facets/page-window.js +202 -0
  23. package/dist/src/collection-facets/page-window.js.map +1 -0
  24. package/dist/src/collection-facets/toggle-switch.js +1 -0
  25. package/dist/src/collection-facets/toggle-switch.js.map +1 -1
  26. package/dist/src/models.d.ts +4 -0
  27. package/dist/src/models.js +16 -0
  28. package/dist/src/models.js.map +1 -1
  29. package/dist/src/utils/log.d.ts +1 -4
  30. package/dist/src/utils/normalize-filter-text.d.ts +28 -0
  31. package/dist/src/utils/normalize-filter-text.js +35 -0
  32. package/dist/src/utils/normalize-filter-text.js.map +1 -0
  33. package/dist/test/collection-facets/more-facets-content.test.js +367 -17
  34. package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
  35. package/dist/test/collection-facets/more-facets-scroller.test.d.ts +1 -0
  36. package/dist/test/collection-facets/more-facets-scroller.test.js +288 -0
  37. package/dist/test/collection-facets/more-facets-scroller.test.js.map +1 -0
  38. package/dist/test/collection-facets/page-window.test.d.ts +1 -0
  39. package/dist/test/collection-facets/page-window.test.js +175 -0
  40. package/dist/test/collection-facets/page-window.test.js.map +1 -0
  41. package/dist/test/mocks/mock-search-responses.d.ts +6 -0
  42. package/dist/test/mocks/mock-search-responses.js +52 -0
  43. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  44. package/dist/test/mocks/mock-search-service.js +2 -1
  45. package/dist/test/mocks/mock-search-service.js.map +1 -1
  46. package/dist/test/utils/normalize-filter-text.test.d.ts +1 -0
  47. package/dist/test/utils/normalize-filter-text.test.js +69 -0
  48. package/dist/test/utils/normalize-filter-text.test.js.map +1 -0
  49. package/docs/design/column-virtualization.md +214 -0
  50. package/package.json +20 -16
  51. package/src/app-root.ts +0 -2
  52. package/src/assets/img/icons/check.ts +5 -0
  53. package/src/collection-facets/facet-row-template.ts +242 -0
  54. package/src/collection-facets/facet-row.ts +15 -202
  55. package/src/collection-facets/models.ts +8 -2
  56. package/src/collection-facets/more-facets-content.ts +528 -221
  57. package/src/collection-facets/more-facets-scroller.ts +485 -0
  58. package/src/collection-facets/page-window.ts +247 -0
  59. package/src/collection-facets/toggle-switch.ts +1 -0
  60. package/src/models.ts +17 -0
  61. package/src/utils/normalize-filter-text.ts +54 -0
  62. package/test/collection-facets/more-facets-content.test.ts +509 -24
  63. package/test/collection-facets/more-facets-scroller.test.ts +401 -0
  64. package/test/collection-facets/page-window.test.ts +208 -0
  65. package/test/mocks/mock-search-responses.ts +56 -0
  66. package/test/mocks/mock-search-service.ts +2 -0
  67. package/test/utils/normalize-filter-text.test.ts +87 -0
  68. package/web-test-runner.config.mjs +7 -2
  69. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.editorconfig +0 -29
  70. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.github/workflows/ci.yml +0 -27
  71. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.github/workflows/gh-pages-main.yml +0 -39
  72. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.github/workflows/npm-publish.yml +0 -39
  73. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.github/workflows/pr-preview.yml +0 -38
  74. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.husky/pre-commit +0 -1
  75. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/.prettierignore +0 -1
  76. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/LICENSE +0 -661
  77. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/README.md +0 -84
  78. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/eslint.config.mjs +0 -53
  79. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/index.html +0 -24
  80. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/index.ts +0 -29
  81. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/local.archive.org.cert +0 -86
  82. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/local.archive.org.key +0 -27
  83. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/package.json +0 -120
  84. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/renovate.json +0 -6
  85. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/app-root.ts +0 -1284
  86. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/arrow-left.ts +0 -10
  87. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/arrow-right.ts +0 -10
  88. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/chevron.ts +0 -4
  89. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/close-circle-dark.ts +0 -5
  90. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/contract.ts +0 -9
  91. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/empty-query.ts +0 -5
  92. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/expand.ts +0 -9
  93. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/eye-closed.ts +0 -5
  94. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/eye.ts +0 -5
  95. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/favorite-filled.ts +0 -10
  96. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/favorite-unfilled.ts +0 -9
  97. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/filter.ts +0 -10
  98. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/login-required.ts +0 -18
  99. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/account.ts +0 -14
  100. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/audio.ts +0 -14
  101. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/collection.ts +0 -12
  102. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/data.ts +0 -15
  103. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/etree.ts +0 -14
  104. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/film.ts +0 -14
  105. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/images.ts +0 -13
  106. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/radio.ts +0 -15
  107. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/search.ts +0 -14
  108. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/software.ts +0 -13
  109. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/texts.ts +0 -13
  110. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/tv-commercial.ts +0 -12
  111. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/tv-fact-check.ts +0 -12
  112. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/tv-quote.ts +0 -12
  113. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/tv.ts +0 -14
  114. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/video.ts +0 -14
  115. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/mediatype/web.ts +0 -13
  116. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/null-result.ts +0 -5
  117. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/quote.ts +0 -7
  118. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/restricted.ts +0 -13
  119. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/reviews.ts +0 -11
  120. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/upload.ts +0 -12
  121. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/assets/img/icons/views.ts +0 -11
  122. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/circular-activity-indicator.ts +0 -64
  123. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-browser.ts +0 -3165
  124. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/facet-row.ts +0 -309
  125. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/facet-tombstone-row.ts +0 -40
  126. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/facets-template.ts +0 -85
  127. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/models.ts +0 -10
  128. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/more-facets-content.ts +0 -644
  129. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/more-facets-pagination.ts +0 -294
  130. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/dedupe.ts +0 -44
  131. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/browser-language/browser-language-heuristic.ts +0 -27
  132. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/index.ts +0 -3
  133. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-heuristic.ts +0 -17
  134. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-map.ts +0 -70
  135. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-entity-map.ts +0 -79
  136. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-heuristic.ts +0 -93
  137. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/models.ts +0 -36
  138. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/smart-facet-bar.ts +0 -437
  139. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/smart-facet-button.ts +0 -145
  140. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/smart-facet-dropdown.ts +0 -185
  141. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/smart-facet-equals.ts +0 -16
  142. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/smart-facets/smart-facet-heuristics.ts +0 -26
  143. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets/toggle-switch.ts +0 -174
  144. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/collection-facets.ts +0 -1013
  145. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/data-source/collection-browser-data-source-interface.ts +0 -351
  146. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/data-source/collection-browser-data-source.ts +0 -1474
  147. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/data-source/collection-browser-query-state.ts +0 -60
  148. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/data-source/models.ts +0 -56
  149. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/empty-placeholder.ts +0 -192
  150. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/expanded-date-picker.ts +0 -191
  151. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/language-code-handler/language-code-handler.ts +0 -64
  152. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/language-code-handler/language-code-mapping.ts +0 -564
  153. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/manage/manage-bar.ts +0 -276
  154. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/manage/remove-items-modal-content.ts +0 -102
  155. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/mediatype/mediatype-config.ts +0 -146
  156. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/models.ts +0 -895
  157. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/restoration-state-handler.ts +0 -550
  158. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/alpha-bar-tooltip.ts +0 -53
  159. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/alpha-bar.ts +0 -252
  160. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/compact.ts +0 -5
  161. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/list.ts +0 -5
  162. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/sort-toggle-disabled.ts +0 -15
  163. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/sort-toggle-down.ts +0 -17
  164. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/sort-toggle-up.ts +0 -17
  165. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/sort-triangle.ts +0 -5
  166. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/img/tile.ts +0 -5
  167. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/sort-filter-bar/sort-filter-bar.ts +0 -733
  168. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/styles/ia-button.ts +0 -139
  169. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/styles/item-image-styles.ts +0 -124
  170. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/styles/sr-only.ts +0 -18
  171. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/styles/tile-action-styles.ts +0 -59
  172. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/base-tile-component.ts +0 -124
  173. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/collection-browser-loading-tile.ts +0 -26
  174. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/account-tile.ts +0 -113
  175. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/collection-tile.ts +0 -184
  176. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/item-tile.ts +0 -347
  177. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/search-tile.ts +0 -90
  178. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/styles/tile-grid-shared-styles.ts +0 -130
  179. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/grid/tile-stats.ts +0 -211
  180. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/hover/hover-pane-controller.ts +0 -638
  181. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/hover/tile-hover-pane.ts +0 -184
  182. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/image-block.ts +0 -162
  183. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/item-image.ts +0 -214
  184. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/list/tile-list-compact-header.ts +0 -112
  185. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/list/tile-list-compact.ts +0 -278
  186. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/list/tile-list.ts +0 -718
  187. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/models.ts +0 -21
  188. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/overlay/icon-overlay.ts +0 -57
  189. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/overlay/text-overlay.ts +0 -79
  190. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/review-block.ts +0 -130
  191. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/text-snippet-block.ts +0 -132
  192. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/tile-dispatcher.ts +0 -637
  193. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/tile-display-value-provider.ts +0 -133
  194. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/tiles/tile-mediatype-icon.ts +0 -133
  195. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/analytics-events.ts +0 -29
  196. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/array-equals.ts +0 -8
  197. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/collapse-repeated-quotes.ts +0 -13
  198. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/date-filter-field.ts +0 -11
  199. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/facet-utils.ts +0 -183
  200. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/format-count.ts +0 -95
  201. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/format-date.ts +0 -62
  202. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/format-unit-size.ts +0 -43
  203. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/local-date-from-utc.ts +0 -15
  204. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/log.ts +0 -14
  205. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/resolve-mediatype.ts +0 -26
  206. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/src/utils/sha1.ts +0 -9
  207. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-browser.test.ts +0 -2639
  208. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets/facet-row.test.ts +0 -421
  209. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets/facets-template.test.ts +0 -139
  210. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets/more-facets-content.test.ts +0 -231
  211. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets/more-facets-pagination.test.ts +0 -201
  212. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets/toggle-switch.test.ts +0 -164
  213. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/collection-facets.test.ts +0 -928
  214. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/data-source/collection-browser-data-source.test.ts +0 -193
  215. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/empty-placeholder.test.ts +0 -88
  216. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/expanded-date-picker.test.ts +0 -201
  217. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/icon-overlay.test.ts +0 -38
  218. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/image-block.test.ts +0 -247
  219. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/item-image.test.ts +0 -220
  220. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/manage/manage-bar.test.ts +0 -347
  221. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/manage/remove-items-modal-content.test.ts +0 -81
  222. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/mediatype-config.test.ts +0 -11
  223. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/mocks/mock-analytics-handler.ts +0 -30
  224. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/mocks/mock-search-responses.ts +0 -1364
  225. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/mocks/mock-search-service.ts +0 -134
  226. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/restoration-state-handler.test.ts +0 -480
  227. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/review-block.test.ts +0 -62
  228. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/sort-filter-bar/alpha-bar-tooltip.test.ts +0 -17
  229. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/sort-filter-bar/alpha-bar.test.ts +0 -101
  230. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/sort-filter-bar/sort-filter-bar.test.ts +0 -443
  231. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/text-overlay.test.ts +0 -55
  232. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/text-snippet-block.test.ts +0 -85
  233. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tile-stats.test.ts +0 -191
  234. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/grid/account-tile.test.ts +0 -121
  235. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/grid/collection-tile.test.ts +0 -114
  236. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/grid/item-tile.test.ts +0 -520
  237. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/grid/search-tile.test.ts +0 -75
  238. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/hover/hover-pane-controller.test.ts +0 -482
  239. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/hover/tile-hover-pane.test.ts +0 -80
  240. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/list/tile-list-compact-header.test.ts +0 -43
  241. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/list/tile-list-compact.test.ts +0 -282
  242. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/list/tile-list.test.ts +0 -576
  243. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/tile-dispatcher.test.ts +0 -320
  244. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/tile-display-value-provider.test.ts +0 -172
  245. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/tiles/tile-mediatype-icon.test.ts +0 -160
  246. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/utils/array-equals.test.ts +0 -31
  247. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/utils/format-count.test.ts +0 -32
  248. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/utils/format-date.test.ts +0 -89
  249. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/utils/format-unit-size.test.ts +0 -21
  250. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/test/utils/local-date-from-utc.test.ts +0 -37
  251. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/tsconfig.json +0 -25
  252. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/types/dompurify.d.ts +0 -1
  253. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/vite.config.ts +0 -29
  254. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/web-dev-server.config.mjs +0 -30
  255. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/web-test-runner.config.mjs +0 -52
  256. package/.claude/worktrees/WEBDEV-8677-bump-field-parsers/yarn.lock +0 -5965
  257. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.editorconfig +0 -29
  258. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.github/workflows/ci.yml +0 -29
  259. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.github/workflows/gh-pages-main.yml +0 -41
  260. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.github/workflows/npm-publish.yml +0 -49
  261. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.github/workflows/pr-preview.yml +0 -40
  262. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.husky/pre-commit +0 -1
  263. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/.prettierignore +0 -1
  264. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/LICENSE +0 -661
  265. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/README.md +0 -68
  266. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/eslint.config.mjs +0 -53
  267. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/index.html +0 -24
  268. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/index.ts +0 -29
  269. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/local.archive.org.cert +0 -86
  270. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/local.archive.org.key +0 -27
  271. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/package.json +0 -129
  272. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/pnpm-lock.yaml +0 -7366
  273. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/pnpm-workspace.yaml +0 -46
  274. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/renovate.json +0 -6
  275. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/app-root.ts +0 -1284
  276. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/arrow-left.ts +0 -10
  277. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/arrow-right.ts +0 -10
  278. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/chevron.ts +0 -4
  279. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/close-circle-dark.ts +0 -5
  280. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/contract.ts +0 -9
  281. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/empty-query.ts +0 -5
  282. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/expand.ts +0 -9
  283. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/eye-closed.ts +0 -5
  284. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/eye.ts +0 -5
  285. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/favorite-filled.ts +0 -10
  286. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/favorite-unfilled.ts +0 -9
  287. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/filter.ts +0 -10
  288. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/login-required.ts +0 -18
  289. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/account.ts +0 -14
  290. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/audio.ts +0 -14
  291. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/collection.ts +0 -12
  292. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/data.ts +0 -15
  293. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/etree.ts +0 -14
  294. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/film.ts +0 -14
  295. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/images.ts +0 -13
  296. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/radio.ts +0 -15
  297. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/search.ts +0 -14
  298. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/software.ts +0 -13
  299. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/texts.ts +0 -13
  300. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/tv-commercial.ts +0 -12
  301. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/tv-fact-check.ts +0 -12
  302. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/tv-quote.ts +0 -12
  303. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/tv.ts +0 -14
  304. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/video.ts +0 -14
  305. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/mediatype/web.ts +0 -13
  306. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/null-result.ts +0 -5
  307. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/quote.ts +0 -7
  308. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/restricted.ts +0 -13
  309. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/reviews.ts +0 -11
  310. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/upload.ts +0 -12
  311. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/assets/img/icons/views.ts +0 -11
  312. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/circular-activity-indicator.ts +0 -64
  313. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-browser.ts +0 -3165
  314. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/facet-row.ts +0 -309
  315. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/facet-tombstone-row.ts +0 -40
  316. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/facets-template.ts +0 -85
  317. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/models.ts +0 -10
  318. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/more-facets-content.ts +0 -644
  319. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/more-facets-pagination.ts +0 -294
  320. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/dedupe.ts +0 -44
  321. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/browser-language/browser-language-heuristic.ts +0 -27
  322. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/index.ts +0 -3
  323. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-heuristic.ts +0 -17
  324. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/query-keywords/query-keywords-map.ts +0 -70
  325. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-entity-map.ts +0 -79
  326. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/heuristics/wikidata/wikidata-heuristic.ts +0 -93
  327. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/models.ts +0 -36
  328. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/smart-facet-bar.ts +0 -437
  329. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/smart-facet-button.ts +0 -145
  330. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/smart-facet-dropdown.ts +0 -185
  331. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/smart-facet-equals.ts +0 -16
  332. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/smart-facets/smart-facet-heuristics.ts +0 -26
  333. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets/toggle-switch.ts +0 -174
  334. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/collection-facets.ts +0 -1013
  335. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/data-source/collection-browser-data-source-interface.ts +0 -351
  336. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/data-source/collection-browser-data-source.ts +0 -1474
  337. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/data-source/collection-browser-query-state.ts +0 -60
  338. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/data-source/models.ts +0 -56
  339. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/empty-placeholder.ts +0 -192
  340. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/expanded-date-picker.ts +0 -191
  341. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/language-code-handler/language-code-handler.ts +0 -64
  342. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/language-code-handler/language-code-mapping.ts +0 -564
  343. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/manage/manage-bar.ts +0 -276
  344. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/manage/remove-items-modal-content.ts +0 -102
  345. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/mediatype/mediatype-config.ts +0 -146
  346. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/models.ts +0 -895
  347. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/restoration-state-handler.ts +0 -550
  348. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/alpha-bar-tooltip.ts +0 -53
  349. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/alpha-bar.ts +0 -252
  350. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/compact.ts +0 -5
  351. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/list.ts +0 -5
  352. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/sort-toggle-disabled.ts +0 -15
  353. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/sort-toggle-down.ts +0 -17
  354. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/sort-toggle-up.ts +0 -17
  355. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/sort-triangle.ts +0 -5
  356. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/img/tile.ts +0 -5
  357. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/sort-filter-bar/sort-filter-bar.ts +0 -733
  358. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/styles/ia-button.ts +0 -139
  359. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/styles/item-image-styles.ts +0 -124
  360. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/styles/sr-only.ts +0 -18
  361. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/styles/tile-action-styles.ts +0 -59
  362. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/base-tile-component.ts +0 -124
  363. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/collection-browser-loading-tile.ts +0 -26
  364. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/account-tile.ts +0 -113
  365. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/collection-tile.ts +0 -184
  366. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/item-tile.ts +0 -347
  367. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/search-tile.ts +0 -90
  368. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/styles/tile-grid-shared-styles.ts +0 -130
  369. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/grid/tile-stats.ts +0 -211
  370. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/hover/hover-pane-controller.ts +0 -638
  371. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/hover/tile-hover-pane.ts +0 -184
  372. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/image-block.ts +0 -162
  373. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/item-image.ts +0 -214
  374. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/list/tile-list-compact-header.ts +0 -112
  375. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/list/tile-list-compact.ts +0 -278
  376. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/list/tile-list.ts +0 -718
  377. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/models.ts +0 -21
  378. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/overlay/icon-overlay.ts +0 -57
  379. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/overlay/text-overlay.ts +0 -79
  380. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/review-block.ts +0 -130
  381. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/text-snippet-block.ts +0 -132
  382. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/tile-dispatcher.ts +0 -637
  383. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/tile-display-value-provider.ts +0 -133
  384. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/tiles/tile-mediatype-icon.ts +0 -133
  385. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/analytics-events.ts +0 -29
  386. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/array-equals.ts +0 -8
  387. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/collapse-repeated-quotes.ts +0 -13
  388. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/date-filter-field.ts +0 -11
  389. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/facet-utils.ts +0 -183
  390. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/format-count.ts +0 -95
  391. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/format-date.ts +0 -62
  392. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/format-unit-size.ts +0 -43
  393. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/local-date-from-utc.ts +0 -15
  394. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/log.ts +0 -14
  395. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/resolve-mediatype.ts +0 -26
  396. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/src/utils/sha1.ts +0 -9
  397. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-browser.test.ts +0 -2639
  398. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets/facet-row.test.ts +0 -421
  399. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets/facets-template.test.ts +0 -139
  400. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets/more-facets-content.test.ts +0 -231
  401. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets/more-facets-pagination.test.ts +0 -201
  402. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets/toggle-switch.test.ts +0 -164
  403. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/collection-facets.test.ts +0 -928
  404. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/data-source/collection-browser-data-source.test.ts +0 -193
  405. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/empty-placeholder.test.ts +0 -88
  406. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/expanded-date-picker.test.ts +0 -201
  407. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/icon-overlay.test.ts +0 -38
  408. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/image-block.test.ts +0 -247
  409. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/item-image.test.ts +0 -220
  410. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/manage/manage-bar.test.ts +0 -347
  411. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/manage/remove-items-modal-content.test.ts +0 -81
  412. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/mediatype-config.test.ts +0 -11
  413. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/mocks/mock-analytics-handler.ts +0 -30
  414. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/mocks/mock-search-responses.ts +0 -1364
  415. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/mocks/mock-search-service.ts +0 -134
  416. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/restoration-state-handler.test.ts +0 -480
  417. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/review-block.test.ts +0 -62
  418. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/sort-filter-bar/alpha-bar-tooltip.test.ts +0 -17
  419. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/sort-filter-bar/alpha-bar.test.ts +0 -101
  420. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/sort-filter-bar/sort-filter-bar.test.ts +0 -443
  421. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/text-overlay.test.ts +0 -55
  422. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/text-snippet-block.test.ts +0 -85
  423. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tile-stats.test.ts +0 -191
  424. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/grid/account-tile.test.ts +0 -121
  425. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/grid/collection-tile.test.ts +0 -114
  426. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/grid/item-tile.test.ts +0 -520
  427. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/grid/search-tile.test.ts +0 -75
  428. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/hover/hover-pane-controller.test.ts +0 -482
  429. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/hover/tile-hover-pane.test.ts +0 -80
  430. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/list/tile-list-compact-header.test.ts +0 -43
  431. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/list/tile-list-compact.test.ts +0 -282
  432. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/list/tile-list.test.ts +0 -576
  433. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/tile-dispatcher.test.ts +0 -320
  434. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/tile-display-value-provider.test.ts +0 -172
  435. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/tiles/tile-mediatype-icon.test.ts +0 -160
  436. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/utils/array-equals.test.ts +0 -31
  437. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/utils/format-count.test.ts +0 -32
  438. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/utils/format-date.test.ts +0 -89
  439. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/utils/format-unit-size.test.ts +0 -21
  440. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/test/utils/local-date-from-utc.test.ts +0 -37
  441. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/tsconfig.json +0 -25
  442. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/types/dompurify.d.ts +0 -1
  443. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/vite.config.ts +0 -29
  444. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/web-dev-server.config.mjs +0 -30
  445. package/.claude/worktrees/WEBDEV-9127-fix-publish-dist-tag/web-test-runner.config.mjs +0 -52
  446. package/dist/src/collection-facets/more-facets-pagination.d.ts +0 -36
  447. package/dist/src/collection-facets/more-facets-pagination.js +0 -260
  448. package/dist/src/collection-facets/more-facets-pagination.js.map +0 -1
  449. package/dist/test/collection-facets/more-facets-pagination.test.d.ts +0 -1
  450. package/dist/test/collection-facets/more-facets-pagination.test.js +0 -126
  451. package/dist/test/collection-facets/more-facets-pagination.test.js.map +0 -1
  452. package/src/collection-facets/more-facets-pagination.ts +0 -294
  453. package/test/collection-facets/more-facets-pagination.test.ts +0 -201
@@ -7,7 +7,10 @@ import {
7
7
  PropertyValues,
8
8
  TemplateResult,
9
9
  } from 'lit';
10
- import { customElement, property, state } from 'lit/decorators.js';
10
+ import { customElement, property, query, state } from 'lit/decorators.js';
11
+ import { ifDefined } from 'lit/directives/if-defined.js';
12
+ import { repeat } from 'lit/directives/repeat.js';
13
+ import { when } from 'lit/directives/when.js';
11
14
  import {
12
15
  Aggregation,
13
16
  Bucket,
@@ -20,13 +23,14 @@ import {
20
23
  } from '@internetarchive/search-service';
21
24
  import type { ModalManagerInterface } from '@internetarchive/modal-manager';
22
25
  import type { AnalyticsManagerInterface } from '@internetarchive/analytics-manager';
23
- import { msg } from '@lit/localize';
26
+ import { msg, str } from '@lit/localize';
24
27
  import {
25
28
  SelectedFacets,
26
29
  FacetGroup,
27
30
  FacetBucket,
28
31
  FacetOption,
29
32
  facetTitles,
33
+ facetPluralTitles,
30
34
  suppressedCollections,
31
35
  valueFacetSort,
32
36
  defaultFacetSort,
@@ -40,23 +44,29 @@ import type {
40
44
  TVChannelAliases,
41
45
  } from '../data-source/models';
42
46
  import '@internetarchive/elements/ia-status-indicator/ia-status-indicator';
43
- import './more-facets-pagination';
44
- import './facets-template';
47
+ import '@internetarchive/ia-clearable-text-input';
48
+ import './more-facets-scroller';
49
+ import type { MoreFacetsScroller } from './more-facets-scroller';
45
50
  import {
46
51
  analyticsActions,
47
52
  analyticsCategories,
48
53
  } from '../utils/analytics-events';
49
54
  import './toggle-switch';
50
55
  import { srOnlyStyle } from '../styles/sr-only';
56
+ import checkIcon from '../assets/img/icons/check';
57
+ import eyeClosedIcon from '../assets/img/icons/eye-closed';
58
+ import closeCircleDark from '../assets/img/icons/close-circle-dark';
51
59
  import {
52
60
  mergeSelectedFacets,
53
- sortBucketsBySelectionState,
54
61
  updateSelectedFacetBucket,
55
62
  } from '../utils/facet-utils';
56
63
  import {
57
- MORE_FACETS__DEFAULT_PAGE_SIZE,
58
- MORE_FACETS__MAX_AGGREGATIONS,
59
- } from './models';
64
+ filterTextIncludes,
65
+ normalizeFilterText,
66
+ type NormalizedFilterText,
67
+ } from '../utils/normalize-filter-text';
68
+ import { log } from '../utils/log';
69
+ import { MORE_FACETS__MAX_AGGREGATIONS } from './models';
60
70
 
61
71
  @customElement('more-facets-content')
62
72
  export class MoreFacetsContent extends LitElement {
@@ -78,11 +88,6 @@ export class MoreFacetsContent extends LitElement {
78
88
  @property({ type: Object })
79
89
  tvChannelAliases?: TVChannelAliases;
80
90
 
81
- /**
82
- * Maximum number of facets to show per page within the modal.
83
- */
84
- @property({ type: Number }) facetsPerPage = MORE_FACETS__DEFAULT_PAGE_SIZE;
85
-
86
91
  /**
87
92
  * Whether we are waiting for facet data to load.
88
93
  * We begin with this set to true so that we show an initial loading indicator.
@@ -126,21 +131,65 @@ export class MoreFacetsContent extends LitElement {
126
131
  getDefaultSelectedFacets();
127
132
 
128
133
  /**
129
- * Which page of facets we are showing.
134
+ * Text the patron has entered to narrow down the facet values shown.
135
+ */
136
+ @state() private filterText = '';
137
+
138
+ /**
139
+ * Whether the search for this facet's values failed, either with an error
140
+ * result or by throwing.
141
+ */
142
+ @state() private loadFailed = false;
143
+
144
+ /**
145
+ * The buckets from `facetGroup` that match `filterText`, in display order.
146
+ * Derived in `willUpdate`.
147
+ */
148
+ private filteredBuckets: FacetBucket[] = [];
149
+
150
+ /**
151
+ * Each bucket's display text in the normalized form that filtering compares
152
+ * against, keyed by bucket key. Filled lazily so that each value is only
153
+ * normalized once, however many times the filter text changes.
154
+ */
155
+ private filterKeys = new Map<string, NormalizedFilterText>();
156
+
157
+ /**
158
+ * The values currently selected or hidden, in the order they were selected.
159
+ * Shown as chiclets above the list. Derived in `willUpdate`.
130
160
  */
131
- @state() private pageNumber = 1;
161
+ private chosenBuckets: FacetBucket[] = [];
162
+
163
+ /**
164
+ * Position of a chiclet that was just removed, whose neighbor should get
165
+ * focus once it's gone.
166
+ */
167
+ private chicletFocusIndex?: number;
168
+
169
+ @query('more-facets-scroller')
170
+ private scroller?: MoreFacetsScroller;
171
+
172
+ @query('ia-clearable-text-input')
173
+ private filterInput?: HTMLElement;
132
174
 
133
175
  willUpdate(changed: PropertyValues): void {
134
- if (
176
+ if (changed.has('aggregations')) {
177
+ this.filterKeys.clear();
178
+ }
179
+
180
+ const facetGroupChanged =
135
181
  changed.has('aggregations') ||
136
- changed.has('facetsPerPage') ||
137
182
  changed.has('sortedBy') ||
138
183
  changed.has('selectedFacets') ||
139
- changed.has('unappliedFacetChanges')
140
- ) {
141
- // Convert the merged selected facets & aggregations into a facet group, and
142
- // store it for reuse across pages.
184
+ changed.has('unappliedFacetChanges');
185
+ if (facetGroupChanged) {
186
+ // Convert the merged selected facets & aggregations into a facet group
143
187
  this.facetGroup = this.mergedFacets;
188
+ this.chosenBuckets = this.listChosenBuckets();
189
+ }
190
+
191
+ if (facetGroupChanged || changed.has('filterText')) {
192
+ this.filteredBuckets = this.filterBuckets();
144
193
  }
145
194
 
146
195
  // If any of the search properties change, it triggers a facet fetch
@@ -151,7 +200,7 @@ export class MoreFacetsContent extends LitElement {
151
200
  changed.has('filterMap')
152
201
  ) {
153
202
  this.facetsLoading = true;
154
- this.pageNumber = 1;
203
+ this.loadFailed = false;
155
204
  this.sortedBy =
156
205
  this.searchType === SearchType.TV
157
206
  ? tvMoreFacetSort[this.facetKey as FacetOption]
@@ -161,22 +210,43 @@ export class MoreFacetsContent extends LitElement {
161
210
  }
162
211
  }
163
212
 
213
+ updated(changed: PropertyValues): void {
214
+ // A new filter or sort order shows different values, so start over from
215
+ // the first page rather than somewhere in the middle of them.
216
+ if (changed.has('filterText') || changed.has('sortedBy')) {
217
+ this.scroller?.scrollToPage(0);
218
+ }
219
+
220
+ // A removed chiclet took its focused button with it; move focus to the
221
+ // chiclet that took its place, or back to the filter field if none are left.
222
+ if (this.chicletFocusIndex !== undefined) {
223
+ const buttons =
224
+ this.shadowRoot?.querySelectorAll<HTMLElement>('.chiclet-remove') ?? [];
225
+ const index = Math.min(this.chicletFocusIndex, buttons.length - 1);
226
+ this.chicletFocusIndex = undefined;
227
+ (buttons[index] ?? this.filterInput)?.focus();
228
+ }
229
+ }
230
+
164
231
  firstUpdated(): void {
165
232
  this.setupEscapeListeners();
166
233
  }
167
234
 
235
+ disconnectedCallback(): void {
236
+ super.disconnectedCallback();
237
+ document.removeEventListener('keydown', this.escapeHandler);
238
+ }
239
+
168
240
  /**
169
241
  * Close more facets modal on Escape click
170
242
  */
243
+ private escapeHandler = (e: KeyboardEvent): void => {
244
+ if (e.key === 'Escape') this.modalManager?.closeModal();
245
+ };
246
+
171
247
  private setupEscapeListeners() {
172
248
  if (this.modalManager) {
173
- document.addEventListener('keydown', (e: KeyboardEvent) => {
174
- if (e.key === 'Escape') {
175
- this.modalManager?.closeModal();
176
- }
177
- });
178
- } else {
179
- document.removeEventListener('keydown', () => {});
249
+ document.addEventListener('keydown', this.escapeHandler);
180
250
  }
181
251
  }
182
252
 
@@ -215,88 +285,76 @@ export class MoreFacetsContent extends LitElement {
215
285
  rows: 0, // todo - do we want server-side pagination with offset/page/limit flag?
216
286
  };
217
287
 
218
- const results = await this.searchService?.search(params, this.searchType);
219
- this.aggregations = results?.success?.response.aggregations;
220
- this.facetsLoading = false;
288
+ try {
289
+ const results = await this.searchService?.search(params, this.searchType);
290
+ const response = results?.success?.response;
291
+ if (!response) {
292
+ log('More facets search failed', results?.error);
293
+ this.loadFailed = true;
294
+ this.aggregations = undefined;
295
+ return;
296
+ }
221
297
 
222
- const collectionTitles = results?.success?.response?.collectionTitles;
223
- if (collectionTitles) {
224
- for (const [id, title] of Object.entries(collectionTitles)) {
225
- this.collectionTitles?.set(id, title);
298
+ // Record collection titles before the aggregations, so that they're
299
+ // available for sorting & filtering once the facet group is built.
300
+ const { collectionTitles } = response;
301
+ if (collectionTitles) {
302
+ for (const [id, title] of Object.entries(collectionTitles)) {
303
+ this.collectionTitles?.set(id, title);
304
+ }
226
305
  }
227
- }
228
- }
229
306
 
230
- /**
231
- * Handler for page number changes from the pagination widget.
232
- */
233
- private pageNumberClicked(e: CustomEvent<{ page: number }>) {
234
- const page = e?.detail?.page;
235
- if (page) {
236
- this.pageNumber = Number(page);
307
+ this.aggregations = response.aggregations;
308
+ } catch (err) {
309
+ log('More facets search threw', err);
310
+ this.loadFailed = true;
311
+ this.aggregations = undefined;
312
+ } finally {
313
+ this.facetsLoading = false;
237
314
  }
238
-
239
- this.analyticsHandler?.sendEvent({
240
- category: analyticsCategories.default,
241
- action: analyticsActions.moreFacetsPageChange,
242
- label: `${this.pageNumber}`,
243
- });
244
315
  }
245
316
 
246
317
  /**
247
- * Combines the selected facets with the aggregations to create a single list of facets
318
+ * Combines the selected facets with the aggregations into the single list of
319
+ * values the dialog shows. Selected and hidden values keep their place in the
320
+ * current sort order rather than moving to the front; the chiclets above the
321
+ * list are what call them out.
248
322
  */
249
323
  private get mergedFacets(): FacetGroup | undefined {
250
324
  if (!this.facetKey || !this.selectedFacets) return undefined;
251
325
 
252
- const { selectedFacetGroup, aggregationFacetGroup } = this;
326
+ const { aggregationFacetGroup } = this;
253
327
 
254
328
  // If we don't have any aggregations, then there is nothing to show yet
255
329
  if (!aggregationFacetGroup) return undefined;
256
330
 
257
- // Start with either the selected group if we have one, or the aggregate group otherwise
258
- const facetGroup = { ...(selectedFacetGroup ?? aggregationFacetGroup) };
331
+ const selections = this.currentSelections;
259
332
 
260
- // Attach the counts to the selected buckets
261
- const bucketsWithCount =
262
- selectedFacetGroup?.buckets.map(bucket => {
263
- const selectedBucket = aggregationFacetGroup.buckets.find(
264
- b => b.key === bucket.key,
265
- );
266
- return selectedBucket
267
- ? {
268
- ...bucket,
269
- count: selectedBucket.count,
270
- }
271
- : bucket;
272
- }) ?? [];
273
-
274
- // Sort the buckets by selection state
275
- // We do this *prior* to considering unapplied selections, because we want the facets
276
- // to remain in position when they are selected/unselected, rather than re-sort themselves.
277
- sortBucketsBySelectionState(bucketsWithCount, this.sortedBy);
278
-
279
- // Append any additional buckets that were not selected
280
- aggregationFacetGroup.buckets.forEach(bucket => {
281
- const existingBucket = selectedFacetGroup?.buckets.find(
282
- b => b.key === bucket.key,
283
- );
284
- if (existingBucket) return;
285
- bucketsWithCount.push(bucket);
333
+ // Mark each aggregated value with its selection state, if it has one
334
+ const buckets = aggregationFacetGroup.buckets.map(bucket => {
335
+ const selection = selections[bucket.key];
336
+ return selection ? { ...bucket, state: selection.state } : bucket;
286
337
  });
287
338
 
288
- // Apply any unapplied selections that appear on this page
289
- const unappliedBuckets = this.unappliedFacetChanges[this.facetKey];
290
- for (const [index, bucket] of bucketsWithCount.entries()) {
291
- const unappliedBucket = unappliedBuckets?.[bucket.key];
292
- if (unappliedBucket) {
293
- bucketsWithCount[index] = { ...unappliedBucket };
294
- }
339
+ // Values selected before the dialog opened can be missing from the
340
+ // aggregations (a hidden value is excluded from the results it would be
341
+ // counted in). Slot them in where the current sort puts them, so they can
342
+ // still be changed from the list.
343
+ const aggregatedKeys = new Set(buckets.map(b => b.key));
344
+ const preexisting = this.selectedFacets[this.facetKey] ?? {};
345
+ for (const [key, data] of Object.entries(preexisting)) {
346
+ if (aggregatedKeys.has(key)) continue;
347
+ this.insertInSortOrder(buckets, {
348
+ displayText: this.displayTextFor(key),
349
+ key,
350
+ count: data.count,
351
+ state: selections[key]?.state ?? data.state,
352
+ });
295
353
  }
296
354
 
297
355
  // For TV creator facets, uppercase the display text
298
356
  if (this.facetKey === 'creator' && this.isTvSearch) {
299
- bucketsWithCount.forEach(b => {
357
+ buckets.forEach(b => {
300
358
  b.displayText = (b.displayText ?? b.key)?.toLocaleUpperCase();
301
359
 
302
360
  const channelLabel = this.tvChannelAliases?.get(b.displayText);
@@ -306,39 +364,75 @@ export class MoreFacetsContent extends LitElement {
306
364
  });
307
365
  }
308
366
 
309
- facetGroup.buckets = bucketsWithCount;
310
- return facetGroup;
367
+ return { ...aggregationFacetGroup, buckets };
311
368
  }
312
369
 
313
370
  /**
314
- * Converts the selected facets for the current facet key to a `FacetGroup`,
315
- * which is easier to work with.
371
+ * This facet type's selections: the ones made before the dialog opened,
372
+ * overridden by any unapplied changes made in it. Values stay in the order
373
+ * they were first selected, and cleared ones remain with state `'none'`.
316
374
  */
317
- private get selectedFacetGroup(): FacetGroup | undefined {
318
- if (!this.selectedFacets || !this.facetKey) return undefined;
375
+ private get currentSelections(): Record<string, FacetBucket> {
376
+ if (!this.facetKey) return {};
377
+ return {
378
+ ...this.selectedFacets?.[this.facetKey],
379
+ ...this.unappliedFacetChanges[this.facetKey],
380
+ };
381
+ }
319
382
 
320
- const selectedFacetsForKey = this.selectedFacets[this.facetKey];
321
- if (!selectedFacetsForKey) return undefined;
383
+ /**
384
+ * The values that are currently selected or hidden, in the order they were
385
+ * selected, as they appear in `facetGroup`.
386
+ */
387
+ private listChosenBuckets(): FacetBucket[] {
388
+ const byKey = new Map(this.facetGroup?.buckets.map(b => [b.key, b]));
389
+ // Without a list (e.g. the search failed), fall back on the selections
390
+ // themselves, so they can still be seen and cleared.
391
+ return Object.entries(this.currentSelections)
392
+ .map(
393
+ ([key, selection]) =>
394
+ byKey.get(key) ?? {
395
+ ...selection,
396
+ displayText: this.displayTextFor(key),
397
+ },
398
+ )
399
+ .filter(bucket => bucket.state !== 'none');
400
+ }
322
401
 
323
- const facetGroupTitle = facetTitles[this.facetKey];
402
+ /**
403
+ * Inserts `bucket` ahead of the first bucket that the current sort would put
404
+ * after it.
405
+ */
406
+ private insertInSortOrder(buckets: FacetBucket[], bucket: FacetBucket): void {
407
+ const index = buckets.findIndex(b => this.compareForSort(bucket, b) < 0);
408
+ buckets.splice(index === -1 ? buckets.length : index, 0, bucket);
409
+ }
324
410
 
325
- const buckets: FacetBucket[] = Object.entries(selectedFacetsForKey).map(
326
- ([value, data]) => {
327
- const displayText: string = value;
328
- return {
329
- displayText,
330
- key: value,
331
- count: data?.count,
332
- state: data?.state,
333
- };
334
- },
335
- );
411
+ /**
412
+ * Orders two buckets the way the aggregations are ordered for the current
413
+ * sort (see `getSortedBuckets` in search-service).
414
+ */
415
+ private compareForSort(a: FacetBucket, b: FacetBucket): number {
416
+ switch (this.sortedBy) {
417
+ case AggregationSortType.ALPHABETICAL:
418
+ return (a.displayText ?? a.key).localeCompare(b.displayText ?? b.key);
419
+ case AggregationSortType.NUMERIC:
420
+ return Number(b.key) - Number(a.key);
421
+ default:
422
+ return b.count - a.count;
423
+ }
424
+ }
336
425
 
337
- return {
338
- title: facetGroupTitle,
339
- key: this.facetKey,
340
- buckets,
341
- };
426
+ /**
427
+ * The text patrons see for a bucket: the collection title for collections,
428
+ * and the bucket key for everything else.
429
+ */
430
+ private displayTextFor(key: string): string {
431
+ const collectionTitle =
432
+ this.facetKey === 'collection'
433
+ ? this.collectionTitles?.get(key)
434
+ : undefined;
435
+ return collectionTitle ?? key;
342
436
  }
343
437
 
344
438
  /**
@@ -372,13 +466,24 @@ export class MoreFacetsContent extends LitElement {
372
466
  const facetBuckets: FacetBucket[] = sortedBuckets.map(bucket => {
373
467
  const bucketKeyStr = `${bucket.key}`;
374
468
  return {
375
- displayText: `${bucketKeyStr}`,
376
- key: `${bucketKeyStr}`,
469
+ displayText: this.displayTextFor(bucketKeyStr),
470
+ key: bucketKeyStr,
377
471
  count: bucket.doc_count,
378
472
  state: 'none',
379
473
  };
380
474
  });
381
475
 
476
+ // Collections sorted alphabetically should be in order of the titles
477
+ // patrons see, not of the identifiers that getSortedBuckets used.
478
+ if (
479
+ this.facetKey === 'collection' &&
480
+ this.sortedBy === AggregationSortType.ALPHABETICAL
481
+ ) {
482
+ facetBuckets.sort((a, b) =>
483
+ (a.displayText ?? a.key).localeCompare(b.displayText ?? b.key),
484
+ );
485
+ }
486
+
382
487
  return {
383
488
  title: facetGroupTitle,
384
489
  key: this.facetKey,
@@ -387,44 +492,118 @@ export class MoreFacetsContent extends LitElement {
387
492
  }
388
493
 
389
494
  /**
390
- * Returns a FacetGroup representing only the current page of facet buckets to show.
495
+ * The buckets whose visible text contains the filter text, ignoring case,
496
+ * accents and punctuation.
391
497
  */
392
- private get facetGroupForCurrentPage(): FacetGroup | undefined {
393
- const { facetGroup } = this;
394
- if (!facetGroup) return undefined;
395
-
396
- // Slice out only the current page of facet buckets
397
- const firstBucketIndexOnPage = (this.pageNumber - 1) * this.facetsPerPage;
398
- const truncatedBuckets = facetGroup.buckets.slice(
399
- firstBucketIndexOnPage,
400
- firstBucketIndexOnPage + this.facetsPerPage,
498
+ private filterBuckets(): FacetBucket[] {
499
+ const buckets = this.facetGroup?.buckets ?? [];
500
+ const needle = normalizeFilterText(this.filterText);
501
+ if (!needle.spaced) return buckets;
502
+ return buckets.filter(bucket =>
503
+ filterTextIncludes(this.filterKeyFor(bucket), needle),
401
504
  );
505
+ }
402
506
 
403
- return {
404
- ...facetGroup,
405
- buckets: truncatedBuckets,
406
- };
507
+ private filterKeyFor(bucket: FacetBucket): NormalizedFilterText {
508
+ let filterKey = this.filterKeys.get(bucket.key);
509
+ if (filterKey === undefined) {
510
+ const visibleText = [bucket.displayText ?? bucket.key, bucket.extraNote]
511
+ .filter(Boolean)
512
+ .join(' ');
513
+ filterKey = normalizeFilterText(visibleText);
514
+ this.filterKeys.set(bucket.key, filterKey);
515
+ }
516
+ return filterKey;
517
+ }
518
+
519
+ /**
520
+ * Whether the patron's filter text has ruled out every value
521
+ */
522
+ private get noValuesMatch(): boolean {
523
+ const isFiltering = normalizeFilterText(this.filterText).spaced !== '';
524
+ return isFiltering && this.filteredBuckets.length === 0;
407
525
  }
408
526
 
409
527
  private get moreFacetsTemplate(): TemplateResult {
528
+ const valuesLabel = this.facetKey
529
+ ? msg(str`${facetTitles[this.facetKey]} values`)
530
+ : undefined;
410
531
  return html`
411
- <facets-template
412
- .facetGroup=${this.facetGroupForCurrentPage}
413
- .selectedFacets=${this.selectedFacets}
532
+ <more-facets-scroller
533
+ .facetType=${this.facetKey}
534
+ .buckets=${this.filteredBuckets}
414
535
  .collectionTitles=${this.collectionTitles}
415
- @facetClick=${(e: CustomEvent<FacetEventDetails>) => {
416
- if (this.facetKey) {
417
- this.unappliedFacetChanges = updateSelectedFacetBucket(
418
- this.unappliedFacetChanges,
419
- this.facetKey,
420
- e.detail.bucket,
421
- );
422
- }
423
- }}
424
- ></facets-template>
536
+ ?omitHideButtons=${this.facetGroup?.buckets.length === 1}
537
+ label=${ifDefined(valuesLabel)}
538
+ @facetClick=${this.facetClicked}
539
+ @pageChanged=${this.pageChanged}
540
+ ></more-facets-scroller>
541
+ ${when(
542
+ this.loadFailed,
543
+ () => this.loadErrorTemplate,
544
+ () => when(this.noValuesMatch, () => this.noMatchesTemplate),
545
+ )}
546
+ `;
547
+ }
548
+
549
+ /**
550
+ * A chiclet for each selected or hidden value, each with a button to clear it
551
+ */
552
+ private get chicletsTemplate(): TemplateResult {
553
+ const valuesName = this.facetKey ? facetPluralTitles[this.facetKey] : '';
554
+ return html`
555
+ <ul class="chiclets" aria-label=${msg(str`Selected ${valuesName}`)}>
556
+ ${repeat(
557
+ this.chosenBuckets,
558
+ bucket => bucket.key,
559
+ (bucket, index) => this.chicletTemplate(bucket, index),
560
+ )}
561
+ </ul>
562
+ `;
563
+ }
564
+
565
+ private chicletTemplate(bucket: FacetBucket, index: number): TemplateResult {
566
+ const isHidden = bucket.state === 'hidden';
567
+ const text = [bucket.displayText ?? bucket.key, bucket.extraNote]
568
+ .filter(Boolean)
569
+ .join(' ');
570
+ const removeLabel = isHidden
571
+ ? msg(str`Unhide ${text}`)
572
+ : msg(str`Deselect ${text}`);
573
+
574
+ return html`
575
+ <li class="chiclet" title=${text}>
576
+ <span class="chiclet-icon" aria-hidden="true"
577
+ >${isHidden ? eyeClosedIcon : checkIcon}</span
578
+ >
579
+ <span class="sr-only"
580
+ >${isHidden ? msg('Hidden:') : msg('Selected:')}</span
581
+ >
582
+ <span class="chiclet-text">${text}</span>
583
+ <button
584
+ type="button"
585
+ class="chiclet-remove"
586
+ aria-label=${removeLabel}
587
+ @click=${() => this.chicletRemoved(bucket, index)}
588
+ >
589
+ ${closeCircleDark}
590
+ </button>
591
+ </li>
425
592
  `;
426
593
  }
427
594
 
595
+ private get noMatchesTemplate(): TemplateResult {
596
+ return html`<p class="facets-message no-matches" role="status">
597
+ ${msg('No matching values found.')}
598
+ </p>`;
599
+ }
600
+
601
+ private get loadErrorTemplate(): TemplateResult {
602
+ return html`<p class="facets-message load-error" role="alert">
603
+ ${msg('Sorry, these values couldn’t be loaded. Please try again later.')}
604
+ </p>`;
605
+ }
606
+
428
607
  private get loaderTemplate(): TemplateResult {
429
608
  return html`
430
609
  <ia-status-indicator
@@ -434,50 +613,21 @@ export class MoreFacetsContent extends LitElement {
434
613
  `;
435
614
  }
436
615
 
437
- /**
438
- * How many pages of facets to show in the modal pagination widget
439
- */
440
- private get paginationSize(): number {
441
- if (!this.aggregations || !this.facetKey) return 0;
442
-
443
- // Calculate the appropriate number of pages to show in the modal pagination widget
444
- const length = this.aggregations[this.facetKey]?.buckets.length;
445
- return Math.ceil(length / this.facetsPerPage);
446
- }
447
-
448
- // render pagination if more then 1 page
449
- private get facetsPaginationTemplate() {
450
- return this.paginationSize > 1
451
- ? html`<more-facets-pagination
452
- .size=${this.paginationSize}
453
- .currentPage=${1}
454
- @pageNumberClicked=${this.pageNumberClicked}
455
- ></more-facets-pagination>`
456
- : nothing;
457
- }
458
-
459
- private get footerTemplate() {
460
- if (this.paginationSize > 0) {
461
- return html`${this.facetsPaginationTemplate}
462
- <div class="footer">
463
- <button
464
- class="btn btn-cancel"
465
- type="button"
466
- @click=${this.cancelClick}
467
- >
468
- Cancel
469
- </button>
470
- <button
471
- class="btn btn-submit"
472
- type="button"
473
- @click=${this.applySearchFacetsClicked}
474
- >
475
- Apply filters
476
- </button>
477
- </div> `;
478
- }
479
-
480
- return nothing;
616
+ private get footerTemplate(): TemplateResult {
617
+ return html`
618
+ <div class="footer">
619
+ <button class="btn btn-cancel" type="button" @click=${this.cancelClick}>
620
+ Cancel
621
+ </button>
622
+ <button
623
+ class="btn btn-submit"
624
+ type="button"
625
+ @click=${this.applySearchFacetsClicked}
626
+ >
627
+ Apply filters
628
+ </button>
629
+ </div>
630
+ `;
481
631
  }
482
632
 
483
633
  private sortFacetAggregation(facetSortType: AggregationSortType) {
@@ -492,27 +642,43 @@ export class MoreFacetsContent extends LitElement {
492
642
  this.sortedBy ?? defaultFacetSort[this.facetKey as FacetOption];
493
643
  const defaultSwitchSide =
494
644
  facetSort === AggregationSortType.COUNT ? 'left' : 'right';
645
+ const valuesName = this.facetKey ? facetPluralTitles[this.facetKey] : '';
646
+ // From the facet key rather than the facet group, which is missing
647
+ // until the values load (or if they fail to)
648
+ const title = this.facetKey ? facetTitles[this.facetKey] : '';
495
649
 
496
650
  return html`<span class="sr-only">${msg('More facets for:')}</span>
497
- <span class="title">
498
- ${this.facetGroup?.title}
499
-
500
- <label class="sort-label">${msg('Sort by:')}</label>
501
- ${this.facetKey
502
- ? html`<toggle-switch
503
- class="sort-toggle"
504
- leftValue=${AggregationSortType.COUNT}
505
- leftLabel="Count"
506
- rightValue=${valueFacetSort[this.facetKey]}
507
- .rightLabel=${this.facetGroup?.title}
508
- side=${defaultSwitchSide}
509
- @change=${(e: CustomEvent<string>) => {
510
- this.sortFacetAggregation(
511
- Number(e.detail) as AggregationSortType,
512
- );
513
- }}
514
- ></toggle-switch>`
515
- : nothing}
651
+ <span class="title">${title}</span>
652
+ <span class="header-controls">
653
+ <span class="sort-controls">
654
+ <label class="sort-label">${msg('Sort by:')}</label>
655
+ ${this.facetKey
656
+ ? html`<toggle-switch
657
+ class="sort-toggle"
658
+ leftValue=${AggregationSortType.COUNT}
659
+ leftLabel="Count"
660
+ rightValue=${valueFacetSort[this.facetKey]}
661
+ .rightLabel=${title}
662
+ side=${defaultSwitchSide}
663
+ @change=${(e: CustomEvent<string>) => {
664
+ this.sortFacetAggregation(
665
+ Number(e.detail) as AggregationSortType,
666
+ );
667
+ }}
668
+ ></toggle-switch>`
669
+ : nothing}
670
+ </span>
671
+ <span class="filter-controls">
672
+ <label class="filter-label">${msg('Filter by:')}</label>
673
+ <ia-clearable-text-input
674
+ class="filter-input"
675
+ .value=${this.filterText}
676
+ .placeholder=${msg(str`Search ${valuesName}…`)}
677
+ .screenReaderLabel=${msg(str`Filter ${valuesName}`)}
678
+ .clearButtonScreenReaderLabel=${msg('Clear filter')}
679
+ @input=${this.filterTextChanged}
680
+ ></ia-clearable-text-input>
681
+ </span>
516
682
  </span>`;
517
683
  }
518
684
 
@@ -523,6 +689,7 @@ export class MoreFacetsContent extends LitElement {
523
689
  : html`
524
690
  <section id="more-facets">
525
691
  <div class="header-content">${this.modalHeaderTemplate}</div>
692
+ ${when(this.chosenBuckets.length, () => this.chicletsTemplate)}
526
693
  <div class="facets-content">${this.moreFacetsTemplate}</div>
527
694
  ${this.footerTemplate}
528
695
  </section>
@@ -530,6 +697,45 @@ export class MoreFacetsContent extends LitElement {
530
697
  `;
531
698
  }
532
699
 
700
+ /**
701
+ * Handler for typing in (or clearing) the filter text field
702
+ */
703
+ private filterTextChanged(e: Event): void {
704
+ const input = e.target as HTMLElement & { value: string };
705
+ this.filterText = input.value;
706
+ }
707
+
708
+ private facetClicked(e: CustomEvent<FacetEventDetails>): void {
709
+ if (!this.facetKey) return;
710
+ this.unappliedFacetChanges = updateSelectedFacetBucket(
711
+ this.unappliedFacetChanges,
712
+ this.facetKey,
713
+ e.detail.bucket,
714
+ );
715
+ }
716
+
717
+ /**
718
+ * Clears the selection a chiclet stands for, which removes the chiclet and
719
+ * unchecks the value in the list.
720
+ */
721
+ private chicletRemoved(bucket: FacetBucket, index: number): void {
722
+ if (!this.facetKey) return;
723
+ this.unappliedFacetChanges = updateSelectedFacetBucket(
724
+ this.unappliedFacetChanges,
725
+ this.facetKey,
726
+ { ...bucket, state: 'none' },
727
+ );
728
+ this.chicletFocusIndex = index;
729
+ }
730
+
731
+ private pageChanged(e: CustomEvent<number>): void {
732
+ this.analyticsHandler?.sendEvent({
733
+ category: analyticsCategories.default,
734
+ action: analyticsActions.moreFacetsPageChange,
735
+ label: `${e.detail + 1}`,
736
+ });
737
+ }
738
+
533
739
  private applySearchFacetsClicked() {
534
740
  const mergedSelections = mergeSelectedFacets(
535
741
  this.selectedFacets,
@@ -573,10 +779,9 @@ export class MoreFacetsContent extends LitElement {
573
779
  srOnlyStyle,
574
780
  css`
575
781
  section#more-facets {
576
- overflow: auto;
577
- padding: 10px; /* leaves room for scroll bar to appear without overlaying on content */
578
- --facetsColumnCount: 3;
782
+ padding: 10px;
579
783
  }
784
+
580
785
  .header-content .title {
581
786
  display: block;
582
787
  text-align: left;
@@ -585,8 +790,25 @@ export class MoreFacetsContent extends LitElement {
585
790
  font-weight: bold;
586
791
  }
587
792
 
588
- .sort-label {
589
- margin-left: 20px;
793
+ .header-controls {
794
+ display: flex;
795
+ flex-wrap: wrap;
796
+ align-items: center;
797
+ gap: 8px 20px;
798
+ padding: 5px 10px 0;
799
+ }
800
+
801
+ /* Each label stays on the same line as its control when these wrap */
802
+ .sort-controls,
803
+ .filter-controls {
804
+ display: inline-flex;
805
+ align-items: center;
806
+ gap: 5px;
807
+ white-space: nowrap;
808
+ }
809
+
810
+ .sort-label,
811
+ .filter-label {
590
812
  font-size: 1.3rem;
591
813
  }
592
814
 
@@ -594,12 +816,95 @@ export class MoreFacetsContent extends LitElement {
594
816
  font-weight: normal;
595
817
  }
596
818
 
819
+ .filter-input {
820
+ --input-height: 2.5rem;
821
+ --input-font-size: 1.3rem;
822
+ --input-border-radius: 4px;
823
+ --input-padding: 4px 8px;
824
+ --input-focused-border-color: ${modalSubmitButton};
825
+ width: 16rem;
826
+ }
827
+
828
+ .chiclets {
829
+ display: flex;
830
+ flex-wrap: wrap;
831
+ gap: 6px;
832
+ margin: 0;
833
+ padding: 10px 10px 0;
834
+ list-style: none;
835
+ /* About three rows; any more scroll, so the values stay in view */
836
+ max-height: 7.8rem;
837
+ overflow-y: auto;
838
+ }
839
+
840
+ .chiclet {
841
+ display: inline-flex;
842
+ align-items: center;
843
+ gap: 4px;
844
+ max-width: 24rem;
845
+ padding: 2px 3px 2px 6px;
846
+ border: 1px solid ${modalSubmitButton};
847
+ border-radius: 5px;
848
+ background: #fff;
849
+ color: #2c2c2c;
850
+ font-size: 1.2rem;
851
+ line-height: normal;
852
+ }
853
+
854
+ .chiclet-icon {
855
+ display: flex;
856
+ flex: none;
857
+ }
858
+
859
+ .chiclet-icon svg {
860
+ width: 12px;
861
+ height: 12px;
862
+ }
863
+
864
+ .chiclet-text {
865
+ overflow: hidden;
866
+ white-space: nowrap;
867
+ text-overflow: ellipsis;
868
+ }
869
+
870
+ .chiclet-remove {
871
+ display: flex;
872
+ flex: none;
873
+ padding: 2px;
874
+ border: none;
875
+ border-radius: 50%;
876
+ background: none;
877
+ cursor: pointer;
878
+ }
879
+
880
+ .chiclet-remove svg {
881
+ width: 10px;
882
+ height: 10px;
883
+ opacity: 0.55;
884
+ }
885
+
886
+ .chiclet-remove:hover svg,
887
+ .chiclet-remove:focus-visible svg {
888
+ opacity: 1;
889
+ }
890
+
597
891
  .facets-content {
892
+ position: relative;
598
893
  font-size: 1.2rem;
599
- max-height: 300px;
600
- overflow: auto;
601
- padding: 10px;
894
+ padding: 10px 0;
895
+ }
896
+
897
+ .facets-message {
898
+ position: absolute;
899
+ inset: 0;
900
+ display: flex;
901
+ align-items: center;
902
+ justify-content: center;
903
+ margin: 0;
904
+ font-size: 1.4rem;
905
+ color: #666;
602
906
  }
907
+
603
908
  .facets-loader {
604
909
  --icon-width: 70px;
605
910
  margin-bottom: 20px;
@@ -614,6 +919,7 @@ export class MoreFacetsContent extends LitElement {
614
919
  width: auto;
615
920
  border-radius: 4px;
616
921
  cursor: pointer;
922
+ font-family: inherit;
617
923
  }
618
924
  .btn-cancel {
619
925
  background-color: #2c2c2c;
@@ -625,17 +931,18 @@ export class MoreFacetsContent extends LitElement {
625
931
  }
626
932
  .footer {
627
933
  text-align: center;
628
- margin-top: 10px;
629
934
  }
630
935
 
631
936
  @media (max-width: 560px) {
632
- section#more-facets {
633
- max-height: 450px;
634
- --facetsColumnCount: 1;
937
+ /* The filter gets a line of its own here, so let it fill that line */
938
+ .filter-controls {
939
+ flex: 1 1 auto;
635
940
  }
636
- .facets-content {
637
- overflow-y: auto;
638
- height: 300px;
941
+ .filter-input {
942
+ flex: 1 1 auto;
943
+ width: auto;
944
+ min-width: 12rem;
945
+ --input-font-size: 1.2rem;
639
946
  }
640
947
  }
641
948
  `,