@internetarchive/collection-browser 0.0.1-alpha.8 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +8 -11
  2. package/demo/app-root.ts +16 -92
  3. package/dist/demo/app-root.d.ts +3 -5
  4. package/dist/demo/app-root.js +13 -83
  5. package/dist/demo/app-root.js.map +1 -1
  6. package/dist/index.d.ts +6 -0
  7. package/dist/index.js +6 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/src/assets/img/icons/chevron.d.ts +2 -0
  10. package/dist/src/assets/img/icons/chevron.js +4 -0
  11. package/dist/src/assets/img/icons/chevron.js.map +1 -0
  12. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -2
  13. package/dist/src/assets/img/icons/mediatype/account.js +6 -4
  14. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -1
  15. package/dist/src/assets/img/icons/mediatype/audio.js +7 -4
  16. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -1
  17. package/dist/src/assets/img/icons/mediatype/collection.js +7 -4
  18. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -1
  19. package/dist/src/assets/img/icons/mediatype/data.d.ts +1 -0
  20. package/dist/src/assets/img/icons/mediatype/data.js +15 -0
  21. package/dist/src/assets/img/icons/mediatype/data.js.map +1 -0
  22. package/dist/src/assets/img/icons/mediatype/etree.js +10 -5
  23. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -1
  24. package/dist/src/assets/img/icons/mediatype/film.js +2 -1
  25. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -1
  26. package/dist/src/assets/img/icons/mediatype/images.js +9 -6
  27. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -1
  28. package/dist/src/assets/img/icons/mediatype/radio.d.ts +1 -0
  29. package/dist/src/assets/img/icons/mediatype/radio.js +15 -0
  30. package/dist/src/assets/img/icons/mediatype/radio.js.map +1 -0
  31. package/dist/src/assets/img/icons/mediatype/software.js +9 -6
  32. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -1
  33. package/dist/src/assets/img/icons/mediatype/texts.js +9 -6
  34. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -1
  35. package/dist/src/assets/img/icons/mediatype/tv.js +10 -5
  36. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -1
  37. package/dist/src/assets/img/icons/mediatype/video.js +10 -6
  38. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -1
  39. package/dist/src/assets/img/icons/mediatype/web.js +9 -6
  40. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -1
  41. package/dist/src/collection-browser.d.ts +53 -31
  42. package/dist/src/collection-browser.js +504 -166
  43. package/dist/src/collection-browser.js.map +1 -1
  44. package/dist/src/collection-facets.d.ts +28 -13
  45. package/dist/src/collection-facets.js +276 -160
  46. package/dist/src/collection-facets.js.map +1 -1
  47. package/dist/src/helpers.d.ts +1 -0
  48. package/dist/src/helpers.js +20 -0
  49. package/dist/src/helpers.js.map +1 -0
  50. package/dist/src/language-code-handler/language-code-handler.d.ts +37 -0
  51. package/dist/src/language-code-handler/language-code-handler.js +27 -0
  52. package/dist/src/language-code-handler/language-code-handler.js.map +1 -0
  53. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -0
  54. package/dist/src/language-code-handler/language-code-mapping.js +563 -0
  55. package/dist/src/language-code-handler/language-code-mapping.js.map +1 -0
  56. package/dist/src/mediatype/mediatype-color.d.ts +3 -0
  57. package/dist/src/mediatype/mediatype-color.js +15 -0
  58. package/dist/src/mediatype/mediatype-color.js.map +1 -0
  59. package/dist/src/mediatype/mediatype-config.d.ts +3 -0
  60. package/dist/src/mediatype/mediatype-config.js +86 -0
  61. package/dist/src/mediatype/mediatype-config.js.map +1 -0
  62. package/dist/src/mediatype/mediatype-display.d.ts +3 -0
  63. package/dist/src/mediatype/mediatype-display.js +86 -0
  64. package/dist/src/mediatype/mediatype-display.js.map +1 -0
  65. package/dist/src/mediatype/mediatype-icon.d.ts +10 -0
  66. package/dist/src/mediatype/mediatype-icon.js +105 -0
  67. package/dist/src/mediatype/mediatype-icon.js.map +1 -0
  68. package/dist/src/mediatype/mediatype-text.d.ts +3 -0
  69. package/dist/src/mediatype/mediatype-text.js +17 -0
  70. package/dist/src/mediatype/mediatype-text.js.map +1 -0
  71. package/dist/src/mediatype/mediatypeConfig.d.ts +3 -0
  72. package/dist/src/mediatype/mediatypeConfig.js +86 -0
  73. package/dist/src/mediatype/mediatypeConfig.js.map +1 -0
  74. package/dist/src/mediatype-icon.d.ts +2 -2
  75. package/dist/src/mediatype-icon.js +35 -46
  76. package/dist/src/mediatype-icon.js.map +1 -1
  77. package/dist/src/models.d.ts +72 -13
  78. package/dist/src/models.js +57 -1
  79. package/dist/src/models.js.map +1 -1
  80. package/dist/src/restoration-state-handler.d.ts +38 -0
  81. package/dist/src/restoration-state-handler.js +204 -0
  82. package/dist/src/restoration-state-handler.js.map +1 -0
  83. package/dist/src/sort-filter-bar/alpha-bar.d.ts +1 -1
  84. package/dist/src/sort-filter-bar/alpha-bar.js +9 -2
  85. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  86. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  87. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  88. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  89. package/dist/src/sort-filter-bar/img/list.js +1 -1
  90. package/dist/src/sort-filter-bar/img/list.js.map +1 -1
  91. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  92. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  93. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  94. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +71 -14
  95. package/dist/src/sort-filter-bar/sort-filter-bar.js +499 -216
  96. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  97. package/dist/src/tiles/collection-browser-loading-tile.d.ts +5 -0
  98. package/dist/src/tiles/collection-browser-loading-tile.js +32 -0
  99. package/dist/src/tiles/collection-browser-loading-tile.js.map +1 -0
  100. package/dist/src/tiles/grid/account-tile.d.ts +1 -1
  101. package/dist/src/tiles/grid/account-tile.js +5 -5
  102. package/dist/src/tiles/grid/account-tile.js.map +1 -1
  103. package/dist/src/tiles/grid/collection-tile.js +1 -2
  104. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  105. package/dist/src/tiles/grid/icons/views.d.ts +1 -1
  106. package/dist/src/tiles/grid/icons/views.js +2 -2
  107. package/dist/src/tiles/grid/icons/views.js.map +1 -1
  108. package/dist/src/tiles/grid/item-tile.d.ts +2 -2
  109. package/dist/src/tiles/grid/item-tile.js +58 -150
  110. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  111. package/dist/src/tiles/item-image.d.ts +21 -0
  112. package/dist/src/tiles/item-image.js +215 -0
  113. package/dist/src/tiles/item-image.js.map +1 -0
  114. package/dist/src/tiles/list/account-label.d.ts +1 -0
  115. package/dist/src/tiles/list/account-label.js +7 -0
  116. package/dist/src/tiles/list/account-label.js.map +1 -0
  117. package/dist/src/tiles/list/date-label.d.ts +1 -0
  118. package/dist/src/tiles/list/date-label.js +13 -0
  119. package/dist/src/tiles/list/date-label.js.map +1 -0
  120. package/dist/src/tiles/list/tile-list-compact-header.d.ts +12 -0
  121. package/dist/src/tiles/list/tile-list-compact-header.js +84 -0
  122. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  123. package/dist/src/tiles/list/tile-list-compact.d.ts +12 -0
  124. package/dist/src/tiles/list/tile-list-compact.js +203 -6
  125. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  126. package/dist/src/tiles/list/tile-list.d.ts +35 -10
  127. package/dist/src/tiles/list/tile-list.js +368 -104
  128. package/dist/src/tiles/list/tile-list.js.map +1 -1
  129. package/dist/src/tiles/loading-tile.js +1 -42
  130. package/dist/src/tiles/loading-tile.js.map +1 -1
  131. package/dist/src/tiles/mediatype-icon.d.ts +9 -0
  132. package/dist/src/tiles/mediatype-icon.js +78 -0
  133. package/dist/src/tiles/mediatype-icon.js.map +1 -0
  134. package/dist/src/tiles/tile-dispatcher.d.ts +11 -4
  135. package/dist/src/tiles/tile-dispatcher.js +56 -19
  136. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  137. package/dist/src/utils/format-date.js +2 -2
  138. package/dist/src/utils/format-date.js.map +1 -1
  139. package/dist/src/waveform-thumbnail.d.ts +7 -0
  140. package/dist/src/waveform-thumbnail.js +106 -0
  141. package/dist/src/waveform-thumbnail.js.map +1 -0
  142. package/dist/{demo/icon-provider-icon.d.ts → src/waveform-view.d.ts} +0 -0
  143. package/dist/src/waveform-view.js +2 -0
  144. package/dist/src/waveform-view.js.map +1 -0
  145. package/dist/src/your-webcomponent.d.ts +8 -0
  146. package/dist/src/your-webcomponent.js +38 -0
  147. package/dist/src/your-webcomponent.js.map +1 -0
  148. package/dist/test/collection-browser.test.d.ts +1 -0
  149. package/dist/test/collection-browser.test.js +16 -2
  150. package/dist/test/collection-browser.test.js.map +1 -1
  151. package/dist/test/mediatype-config.test.d.ts +1 -0
  152. package/dist/test/mediatype-config.test.js +17 -0
  153. package/dist/test/mediatype-config.test.js.map +1 -0
  154. package/dist/{src/assets/img/icons/eye-hidden.d.ts → test/mediatype-icon.test.d.ts} +0 -0
  155. package/dist/test/mediatype-icon.test.js +3 -0
  156. package/dist/test/mediatype-icon.test.js.map +1 -0
  157. package/dist/test/mediatypeConfig.test.d.ts +1 -0
  158. package/dist/test/mediatypeConfig.test.js +17 -0
  159. package/dist/test/mediatypeConfig.test.js.map +1 -0
  160. package/dist/test/utils/format-date.test.js +1 -1
  161. package/dist/test/utils/format-date.test.js.map +1 -1
  162. package/dist/test/your-webcomponent.test.d.ts +1 -0
  163. package/dist/test/your-webcomponent.test.js +23 -0
  164. package/dist/test/your-webcomponent.test.js.map +1 -0
  165. package/index.ts +6 -0
  166. package/local.archive.org.cert +86 -0
  167. package/local.archive.org.key +27 -0
  168. package/package.json +9 -5
  169. package/src/assets/img/icons/chevron.ts +4 -0
  170. package/src/assets/img/icons/mediatype/account.ts +6 -4
  171. package/src/assets/img/icons/mediatype/audio.ts +7 -4
  172. package/src/assets/img/icons/mediatype/collection.ts +7 -4
  173. package/src/assets/img/icons/mediatype/data.ts +15 -0
  174. package/src/assets/img/icons/mediatype/etree.ts +10 -5
  175. package/src/assets/img/icons/mediatype/film.ts +2 -1
  176. package/src/assets/img/icons/mediatype/images.ts +9 -6
  177. package/src/assets/img/icons/mediatype/radio.ts +15 -0
  178. package/src/assets/img/icons/mediatype/software.ts +9 -6
  179. package/src/assets/img/icons/mediatype/texts.ts +9 -6
  180. package/src/assets/img/icons/mediatype/tv.ts +10 -5
  181. package/src/assets/img/icons/mediatype/video.ts +10 -6
  182. package/src/assets/img/icons/mediatype/web.ts +9 -6
  183. package/src/collection-browser.ts +529 -163
  184. package/src/collection-facets.ts +307 -205
  185. package/src/language-code-handler/language-code-handler.ts +64 -0
  186. package/src/language-code-handler/language-code-mapping.ts +564 -0
  187. package/src/mediatype/mediatype-config.ts +86 -0
  188. package/src/models.ts +141 -13
  189. package/src/restoration-state-handler.ts +266 -0
  190. package/src/sort-filter-bar/alpha-bar.ts +9 -3
  191. package/src/sort-filter-bar/img/compact.ts +5 -0
  192. package/src/sort-filter-bar/img/list.ts +1 -1
  193. package/src/sort-filter-bar/img/tile.ts +5 -0
  194. package/src/sort-filter-bar/sort-filter-bar.ts +557 -225
  195. package/src/tiles/collection-browser-loading-tile.ts +29 -0
  196. package/src/tiles/grid/account-tile.ts +1 -1
  197. package/src/tiles/grid/collection-tile.ts +1 -2
  198. package/src/tiles/grid/icons/views.ts +2 -2
  199. package/src/tiles/grid/item-tile.ts +57 -162
  200. package/src/tiles/item-image.ts +218 -0
  201. package/src/tiles/list/account-label.ts +6 -0
  202. package/src/tiles/list/date-label.ts +12 -0
  203. package/src/tiles/list/tile-list-compact-header.ts +77 -0
  204. package/src/tiles/list/tile-list-compact.ts +218 -0
  205. package/src/tiles/list/tile-list.ts +412 -107
  206. package/src/tiles/mediatype-icon.ts +75 -0
  207. package/src/tiles/tile-dispatcher.ts +66 -18
  208. package/src/utils/format-date.ts +2 -2
  209. package/test/collection-browser.test.ts +20 -1
  210. package/test/mediatype-config.test.ts +18 -0
  211. package/test/utils/format-date.test.ts +1 -1
  212. package/web-dev-server.config.mjs +3 -1
  213. package/dist/demo/icon-provider/icon-provider-icon.d.ts +0 -10
  214. package/dist/demo/icon-provider/icon-provider-icon.js +0 -32
  215. package/dist/demo/icon-provider/icon-provider-icon.js.map +0 -1
  216. package/dist/demo/icon-provider/icon-provider.d.ts +0 -10
  217. package/dist/demo/icon-provider/icon-provider.js +0 -9
  218. package/dist/demo/icon-provider/icon-provider.js.map +0 -1
  219. package/dist/demo/icon-provider-icon.js +0 -2
  220. package/dist/demo/icon-provider-icon.js.map +0 -1
  221. package/dist/demo/icon-provider.d.ts +0 -10
  222. package/dist/demo/icon-provider.js +0 -12
  223. package/dist/demo/icon-provider.js.map +0 -1
  224. package/dist/src/assets/img/icons/audio.d.ts +0 -1
  225. package/dist/src/assets/img/icons/audio.js +0 -9
  226. package/dist/src/assets/img/icons/audio.js.map +0 -1
  227. package/dist/src/assets/img/icons/collection.d.ts +0 -1
  228. package/dist/src/assets/img/icons/collection.js +0 -9
  229. package/dist/src/assets/img/icons/collection.js.map +0 -1
  230. package/dist/src/assets/img/icons/etree.d.ts +0 -1
  231. package/dist/src/assets/img/icons/etree.js +0 -9
  232. package/dist/src/assets/img/icons/etree.js.map +0 -1
  233. package/dist/src/assets/img/icons/eye-hidden.js +0 -2
  234. package/dist/src/assets/img/icons/eye-hidden.js.map +0 -1
  235. package/dist/src/assets/img/icons/images.d.ts +0 -1
  236. package/dist/src/assets/img/icons/images.js +0 -10
  237. package/dist/src/assets/img/icons/images.js.map +0 -1
  238. package/dist/src/assets/img/icons/mediatype/etree copy.d.ts +0 -1
  239. package/dist/src/assets/img/icons/mediatype/etree copy.js +0 -9
  240. package/dist/src/assets/img/icons/mediatype/etree copy.js.map +0 -1
  241. package/dist/src/assets/img/icons/software.d.ts +0 -1
  242. package/dist/src/assets/img/icons/software.js +0 -10
  243. package/dist/src/assets/img/icons/software.js.map +0 -1
  244. package/dist/src/assets/img/icons/texts.d.ts +0 -1
  245. package/dist/src/assets/img/icons/texts.js +0 -10
  246. package/dist/src/assets/img/icons/texts.js.map +0 -1
  247. package/dist/src/assets/img/icons/tv.d.ts +0 -1
  248. package/dist/src/assets/img/icons/tv.js +0 -9
  249. package/dist/src/assets/img/icons/tv.js.map +0 -1
  250. package/dist/src/assets/img/icons/video.d.ts +0 -1
  251. package/dist/src/assets/img/icons/video.js +0 -10
  252. package/dist/src/assets/img/icons/video.js.map +0 -1
  253. package/dist/src/assets/img/icons/web.d.ts +0 -1
  254. package/dist/src/assets/img/icons/web.js +0 -10
  255. package/dist/src/assets/img/icons/web.js.map +0 -1
  256. package/dist/src/search-handler.d.ts +0 -11
  257. package/dist/src/search-handler.js +0 -34
  258. package/dist/src/search-handler.js.map +0 -1
  259. package/src/mediatype-icon.ts +0 -83
  260. package/src/sort-filter-bar/img/grid.ts +0 -5
  261. package/src/tiles/loading-tile.ts +0 -70
@@ -8,36 +8,54 @@ import {
8
8
  nothing,
9
9
  } from 'lit';
10
10
  import { customElement, property, query, state } from 'lit/decorators.js';
11
+ import { ifDefined } from 'lit/directives/if-defined.js';
11
12
  import type {
12
13
  InfiniteScroller,
13
14
  InfiniteScrollerCellProviderInterface,
14
15
  } from '@internetarchive/infinite-scroller';
15
- import {
16
+ import type {
16
17
  Aggregation,
17
18
  Metadata,
18
19
  SearchParams,
19
20
  SearchServiceInterface,
21
+ SortDirection,
22
+ SortParam,
20
23
  } from '@internetarchive/search-service';
21
24
  import {
22
- AggregateSearchParams,
23
- SortParam,
24
- } from '@internetarchive/search-service/dist/src/search-params';
25
- import { SharedResizeObserverInterface } from '@internetarchive/shared-resize-observer';
26
- import type { TileModel, CollectionDisplayMode } from './models';
25
+ SharedResizeObserverInterface,
26
+ SharedResizeObserverResizeHandlerInterface,
27
+ } from '@internetarchive/shared-resize-observer';
27
28
  import '@internetarchive/infinite-scroller';
29
+ import type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
28
30
  import './tiles/tile-dispatcher';
29
- import './tiles/loading-tile';
31
+ import './tiles/collection-browser-loading-tile';
30
32
  import './sort-filter-bar/sort-filter-bar';
31
33
  import './collection-facets';
32
- import { CollectionFacets } from './collection-facets';
33
34
  import './circular-activity-indicator';
34
35
  import './sort-filter-bar/sort-filter-bar';
35
- import { SortFilterBar } from './sort-filter-bar/sort-filter-bar';
36
+ import {
37
+ SelectedFacets,
38
+ SortField,
39
+ SortFieldToMetadataField,
40
+ CollectionBrowserContext,
41
+ defaultSelectedFacets,
42
+ TileModel,
43
+ CollectionDisplayMode,
44
+ } from './models';
45
+ import {
46
+ RestorationStateHandlerInterface,
47
+ RestorationStateHandler,
48
+ RestorationState,
49
+ } from './restoration-state-handler';
50
+ import chevronIcon from './assets/img/icons/chevron';
51
+ import { LanguageCodeHandler } from './language-code-handler/language-code-handler';
36
52
 
37
53
  @customElement('collection-browser')
38
54
  export class CollectionBrowser
39
55
  extends LitElement
40
- implements InfiniteScrollerCellProviderInterface
56
+ implements
57
+ InfiniteScrollerCellProviderInterface,
58
+ SharedResizeObserverResizeHandlerInterface
41
59
  {
42
60
  @property({ type: String }) baseNavigationUrl?: string;
43
61
 
@@ -45,21 +63,51 @@ export class CollectionBrowser
45
63
 
46
64
  @property({ type: String }) baseQuery?: string;
47
65
 
48
- @property({ type: Boolean }) showDeleteButtons = false;
49
-
50
- @property({ type: String }) displayMode: CollectionDisplayMode = 'grid';
66
+ @property({ type: String }) displayMode?: CollectionDisplayMode;
51
67
 
52
68
  @property({ type: Object }) sortParam: SortParam | null = null;
53
69
 
70
+ @property({ type: String }) selectedSort: SortField = SortField.relevance;
71
+
72
+ @property({ type: String }) selectedTitleFilter: string | null = null;
73
+
74
+ @property({ type: String }) selectedCreatorFilter: string | null = null;
75
+
76
+ @property({ type: String }) sortDirection: SortDirection | null = null;
77
+
54
78
  @property({ type: String }) dateRangeQueryClause?: string;
55
79
 
56
80
  @property({ type: Number }) pageSize = 50;
57
81
 
58
82
  @property({ type: Object }) resizeObserver?: SharedResizeObserverInterface;
59
83
 
60
- @query('collection-facets') private collectionFacets!: CollectionFacets;
84
+ @property({ type: String }) titleQuery?: string;
85
+
86
+ @property({ type: String }) creatorQuery?: string;
87
+
88
+ @property({ type: Number }) currentPage?: number;
61
89
 
62
- @query('sort-filter-bar') private sortFilterBar!: SortFilterBar;
90
+ @property({ type: String }) minSelectedDate?: string;
91
+
92
+ @property({ type: String }) maxSelectedDate?: string;
93
+
94
+ @property({ type: Object }) selectedFacets?: SelectedFacets;
95
+
96
+ @property({ type: Boolean }) showHistogramDatePicker = false;
97
+
98
+ @property({ type: Object })
99
+ collectionNameCache?: CollectionNameCacheInterface;
100
+
101
+ @property({ type: String }) pageContext: CollectionBrowserContext = 'search';
102
+
103
+ @property({ type: Object })
104
+ restorationStateHandler: RestorationStateHandlerInterface = new RestorationStateHandler(
105
+ {
106
+ context: this.pageContext,
107
+ }
108
+ );
109
+
110
+ @property({ type: Number }) mobileBreakpoint = 600;
63
111
 
64
112
  /**
65
113
  * The page that the consumer wants to load.
@@ -76,10 +124,6 @@ export class CollectionBrowser
76
124
 
77
125
  @state() private searchResultsLoading = false;
78
126
 
79
- @state() private selectedFacets: Record<string, string[]> = {};
80
-
81
- @state() private hiddenFacets: Record<string, string[]> = {};
82
-
83
127
  @state() private facetsLoading = false;
84
128
 
85
129
  @state() private fullYearAggregationLoading = false;
@@ -90,11 +134,13 @@ export class CollectionBrowser
90
134
 
91
135
  @state() private totalResults?: number;
92
136
 
93
- @state() private titleQuery?: string;
137
+ @state() private mobileView = false;
138
+
139
+ @state() private mobileFacetsVisible = false;
94
140
 
95
- @state() private creatorQuery?: string;
141
+ @query('#content-container') private contentContainer!: HTMLDivElement;
96
142
 
97
- @state() private currentPage?: number;
143
+ private languageCodeHandler = new LanguageCodeHandler();
98
144
 
99
145
  /**
100
146
  * When we're animated scrolling to the page, we don't want to fetch
@@ -107,7 +153,7 @@ export class CollectionBrowser
107
153
  */
108
154
  private endOfDataReached = false;
109
155
 
110
- private placeholderCellTemplate = html`<loading-tile></loading-tile>`;
156
+ private placeholderCellTemplate = html`<collection-browser-loading-tile></collection-browser-loading-tile>`;
111
157
 
112
158
  private tileModelAtCellIndex(index: number): TileModel | undefined {
113
159
  const pageNumber = Math.floor(index / this.pageSize) + 1;
@@ -162,9 +208,9 @@ export class CollectionBrowser
162
208
  private infiniteScroller!: InfiniteScroller;
163
209
 
164
210
  /**
211
+ * Go to the given page of results
165
212
  *
166
213
  * @param pageNumber
167
- * @param scroll
168
214
  */
169
215
  goToPage(pageNumber: number) {
170
216
  this.initialPageNumber = pageNumber;
@@ -172,45 +218,92 @@ export class CollectionBrowser
172
218
  this.scrollToPage(pageNumber);
173
219
  }
174
220
 
221
+ clearFilters() {
222
+ this.selectedFacets = defaultSelectedFacets;
223
+ this.sortParam = null;
224
+ this.selectedTitleFilter = null;
225
+ this.selectedCreatorFilter = null;
226
+ this.titleQuery = undefined;
227
+ this.creatorQuery = undefined;
228
+ this.selectedSort = SortField.relevance;
229
+ this.sortDirection = null;
230
+ }
231
+
175
232
  render() {
176
233
  return html`
177
- ${this.queryDebuggingTemplate}
178
-
179
- <div id="content-container">
234
+ <div id="content-container" class=${this.mobileView ? 'mobile' : ''}>
180
235
  <div id="left-column" class="column">
181
- <div id="results-total">
182
- <span id="big-results-count"
183
- >${this.totalResults
184
- ? this.totalResults.toLocaleString()
185
- : '-'}</span
186
- >
187
- <span id="big-results-label">Results</span>
236
+ <div id="mobile-header-container">
237
+ ${this.mobileView
238
+ ? html`
239
+ <div id="mobile-filter-collapse">
240
+ <h1
241
+ @click=${() => {
242
+ this.mobileFacetsVisible = !this.mobileFacetsVisible;
243
+ }}
244
+ @keyup=${() => {
245
+ this.mobileFacetsVisible = !this.mobileFacetsVisible;
246
+ }}
247
+ >
248
+ <span
249
+ class="collapser ${this.mobileFacetsVisible
250
+ ? 'open'
251
+ : ''}"
252
+ >
253
+ ${chevronIcon}
254
+ </span>
255
+ Filters
256
+ </h1>
257
+ </div>
258
+ `
259
+ : nothing}
260
+ <div id="results-total">
261
+ <span id="big-results-count"
262
+ >${this.totalResults !== undefined
263
+ ? this.totalResults.toLocaleString()
264
+ : '-'}</span
265
+ >
266
+ <span id="big-results-label">Results</span>
267
+ </div>
188
268
  </div>
189
- <div id="facets-container">
190
- ${this.facetsLoading ? this.loadingTemplate : nothing}
191
- <collection-facets
192
- @facetsChanged=${this.facetsChanged}
193
- @hiddenFacetsChanged=${this.hiddenFacetsChanged}
194
- @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
195
- .aggregations=${this.aggregations}
196
- .fullYearsHistogramAggregation=${this
197
- .fullYearsHistogramAggregation}
198
- ?facetsLoading=${this.facetDataLoading}
199
- ?fullYearAggregationLoading=${this.fullYearAggregationLoading}
200
- ></collection-facets>
269
+ <div
270
+ id="facets-container"
271
+ class=${!this.mobileView || this.mobileFacetsVisible
272
+ ? 'expanded'
273
+ : ''}
274
+ >
275
+ ${this.facetsTemplate}
201
276
  </div>
202
277
  </div>
203
278
  <div id="right-column" class="column">
204
279
  ${this.searchResultsLoading ? this.loadingTemplate : nothing}
205
280
  <sort-filter-bar
206
- @sortChanged=${this.sortChanged}
281
+ .selectedSort=${this.selectedSort}
282
+ .sortDirection=${this.sortDirection}
283
+ .displayMode=${this.displayMode}
284
+ .selectedTitleFilter=${this.selectedTitleFilter}
285
+ .selectedCreatorFilter=${this.selectedCreatorFilter}
286
+ .resizeObserver=${this.resizeObserver}
287
+ @sortChanged=${this.userChangedSort}
207
288
  @displayModeChanged=${this.displayModeChanged}
208
- @titleLetterChanged=${this.titleLetterChanged}
209
- @creatorLetterChanged=${this.creatorLetterChanged}
289
+ @titleLetterChanged=${this.titleLetterSelected}
290
+ @creatorLetterChanged=${this.creatorLetterSelected}
210
291
  ></sort-filter-bar>
211
292
 
293
+ ${this.displayMode === `list-compact`
294
+ ? this.listHeaderTemplate
295
+ : nothing}
296
+ ${!this.searchResultsLoading && this.totalResults === 0
297
+ ? html`
298
+ <h2>
299
+ Your search did not match any items in the Archive. Try
300
+ different keywords or a more general search.
301
+ </h2>
302
+ `
303
+ : nothing}
304
+
212
305
  <infinite-scroller
213
- class="${this.displayMode}"
306
+ class="${ifDefined(this.displayMode)}"
214
307
  .cellProvider=${this}
215
308
  .placeholderCellTemplate=${this.placeholderCellTemplate}
216
309
  @scrollThresholdReached=${this.scrollThresholdReached}
@@ -222,19 +315,15 @@ export class CollectionBrowser
222
315
  `;
223
316
  }
224
317
 
225
- private sortChanged(
318
+ private userChangedSort(
226
319
  e: CustomEvent<{
227
- sortField: string | null;
228
- sortDirection: 'asc' | 'desc' | null;
320
+ selectedSort: SortField;
321
+ sortDirection: SortDirection | null;
229
322
  }>
230
323
  ) {
231
- const { sortField, sortDirection } = e.detail;
232
- console.debug('sortChanged', sortField, sortDirection);
233
- if (sortField && sortDirection) {
234
- this.sortParam = new SortParam(sortField, sortDirection);
235
- } else {
236
- this.sortParam = null;
237
- }
324
+ const { selectedSort, sortDirection } = e.detail;
325
+ this.selectedSort = selectedSort;
326
+ this.sortDirection = sortDirection;
238
327
 
239
328
  if ((this.currentPage ?? 1) > 1) {
240
329
  this.goToPage(1);
@@ -242,34 +331,69 @@ export class CollectionBrowser
242
331
  this.currentPage = 1;
243
332
  }
244
333
 
334
+ private selectedSortChanged() {
335
+ if (this.selectedSort === 'relevance' || this.sortDirection === null) {
336
+ this.sortParam = null;
337
+ return;
338
+ }
339
+ const sortField = SortFieldToMetadataField[this.selectedSort];
340
+ if (!sortField) return;
341
+ this.sortParam = { field: sortField, direction: this.sortDirection };
342
+ }
343
+
245
344
  private displayModeChanged(
246
345
  e: CustomEvent<{ displayMode: CollectionDisplayMode }>
247
346
  ) {
248
347
  this.displayMode = e.detail.displayMode;
249
348
  }
250
349
 
251
- private titleLetterChanged(e: CustomEvent<{ selectedLetter: string }>) {
252
- const letter = e.detail.selectedLetter;
253
- if (letter) {
254
- this.titleQuery = `firstTitle:${letter}`;
255
- } else {
256
- this.titleQuery = undefined;
257
- }
350
+ private selectedTitleLetterChanged() {
351
+ this.titleQuery = this.selectedTitleFilter
352
+ ? `firstTitle:${this.selectedTitleFilter}`
353
+ : undefined;
258
354
  }
259
355
 
260
- private creatorLetterChanged(e: CustomEvent<{ selectedLetter: string }>) {
261
- const letter = e.detail.selectedLetter;
262
- if (letter) {
263
- this.creatorQuery = `firstCreator:${letter}`;
264
- } else {
265
- this.creatorQuery = undefined;
266
- }
356
+ private selectedCreatorLetterChanged() {
357
+ this.creatorQuery = this.selectedCreatorFilter
358
+ ? `firstCreator:${this.selectedCreatorFilter}`
359
+ : undefined;
360
+ }
361
+
362
+ private titleLetterSelected(e: CustomEvent<{ selectedLetter: string }>) {
363
+ this.selectedCreatorFilter = null;
364
+ this.selectedTitleFilter = e.detail.selectedLetter;
365
+ }
366
+
367
+ private creatorLetterSelected(e: CustomEvent<{ selectedLetter: string }>) {
368
+ this.selectedTitleFilter = null;
369
+ this.selectedCreatorFilter = e.detail.selectedLetter;
267
370
  }
268
371
 
269
372
  private get facetDataLoading(): boolean {
270
373
  return this.facetsLoading || this.fullYearAggregationLoading;
271
374
  }
272
375
 
376
+ private get facetsTemplate() {
377
+ return html`
378
+ ${this.facetsLoading ? this.loadingTemplate : nothing}
379
+ <collection-facets
380
+ @facetsChanged=${this.facetsChanged}
381
+ @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
382
+ .aggregations=${this.aggregations}
383
+ .fullYearsHistogramAggregation=${this.fullYearsHistogramAggregation}
384
+ .minSelectedDate=${this.minSelectedDate}
385
+ .maxSelectedDate=${this.maxSelectedDate}
386
+ .selectedFacets=${this.selectedFacets}
387
+ .collectionNameCache=${this.collectionNameCache}
388
+ .languageCodeHandler=${this.languageCodeHandler}
389
+ .showHistogramDatePicker=${this.showHistogramDatePicker}
390
+ ?collapsableFacets=${this.mobileView}
391
+ ?facetsLoading=${this.facetDataLoading}
392
+ ?fullYearAggregationLoading=${this.fullYearAggregationLoading}
393
+ ></collection-facets>
394
+ `;
395
+ }
396
+
273
397
  private get loadingTemplate() {
274
398
  return html`
275
399
  <div class="loading-cover">
@@ -278,6 +402,20 @@ export class CollectionBrowser
278
402
  `;
279
403
  }
280
404
 
405
+ private get listHeaderTemplate() {
406
+ return html`
407
+ <div id="list-header">
408
+ <tile-dispatcher
409
+ .tileDisplayMode=${'list-header'}
410
+ .resizeObserver=${this.resizeObserver}
411
+ .sortParam=${this.sortParam}
412
+ .mobileBreakpoint=${this.mobileBreakpoint}
413
+ >
414
+ </tile-dispatcher>
415
+ </div>
416
+ `;
417
+ }
418
+
281
419
  private get queryDebuggingTemplate() {
282
420
  return html`
283
421
  <div>
@@ -303,14 +441,21 @@ export class CollectionBrowser
303
441
  this.dateRangeQueryClause = `year:[${minDate} TO ${maxDate}]`;
304
442
  }
305
443
 
444
+ firstUpdated(): void {
445
+ this.setupStateRestorationObserver();
446
+ this.restoreState();
447
+ }
448
+
306
449
  updated(changed: PropertyValues) {
307
- if (
308
- changed.has('displayMode') ||
309
- changed.has('showDeleteButtons') ||
310
- changed.has('baseNavigationUrl')
311
- ) {
450
+ if (changed.has('displayMode') || changed.has('baseNavigationUrl')) {
312
451
  this.infiniteScroller.reload();
313
452
  }
453
+ if (changed.has('baseQuery')) {
454
+ this.emitBaseQueryChanged();
455
+ }
456
+ if (changed.has('currentPage') || changed.has('displayMode')) {
457
+ this.persistState();
458
+ }
314
459
  if (
315
460
  changed.has('baseQuery') ||
316
461
  changed.has('titleQuery') ||
@@ -318,16 +463,73 @@ export class CollectionBrowser
318
463
  changed.has('dateRangeQueryClause') ||
319
464
  changed.has('sortParam') ||
320
465
  changed.has('selectedFacets') ||
321
- changed.has('hiddenFacets') ||
322
466
  changed.has('searchService')
323
467
  ) {
324
468
  this.handleQueryChange();
325
469
  }
470
+ if (changed.has('selectedSort') || changed.has('sortDirection')) {
471
+ this.selectedSortChanged();
472
+ }
473
+ if (changed.has('selectedTitleFilter')) {
474
+ this.selectedTitleLetterChanged();
475
+ }
476
+ if (changed.has('selectedCreatorFilter')) {
477
+ this.selectedCreatorLetterChanged();
478
+ }
326
479
  if (changed.has('pagesToRender')) {
327
480
  if (!this.endOfDataReached) {
328
481
  this.infiniteScroller.itemCount = this.estimatedTileCount;
329
482
  }
330
483
  }
484
+ if (changed.has('resizeObserver')) {
485
+ const oldObserver = changed.get(
486
+ 'resizeObserver'
487
+ ) as SharedResizeObserverInterface;
488
+ if (oldObserver) this.disconnectResizeObserver(oldObserver);
489
+ this.setupResizeObserver();
490
+ }
491
+ }
492
+
493
+ disconnectedCallback(): void {
494
+ if (this.resizeObserver) {
495
+ this.disconnectResizeObserver(this.resizeObserver);
496
+ }
497
+ if (this.boundNavigationHandler) {
498
+ window.removeEventListener('popstate', this.boundNavigationHandler);
499
+ }
500
+ }
501
+
502
+ handleResize(entry: ResizeObserverEntry): void {
503
+ if (entry.target === this.contentContainer) {
504
+ this.mobileView = entry.contentRect.width < 600;
505
+ }
506
+ }
507
+
508
+ private emitBaseQueryChanged() {
509
+ this.dispatchEvent(
510
+ new CustomEvent<{ baseQuery?: string }>('baseQueryChanged', {
511
+ detail: {
512
+ baseQuery: this.baseQuery,
513
+ },
514
+ })
515
+ );
516
+ }
517
+
518
+ private disconnectResizeObserver(
519
+ resizeObserver: SharedResizeObserverInterface
520
+ ) {
521
+ resizeObserver.removeObserver({
522
+ target: this.contentContainer,
523
+ handler: this,
524
+ });
525
+ }
526
+
527
+ private setupResizeObserver() {
528
+ if (!this.resizeObserver) return;
529
+ this.resizeObserver.addObserver({
530
+ target: this.contentContainer,
531
+ handler: this,
532
+ });
331
533
  }
332
534
 
333
535
  /**
@@ -347,6 +549,9 @@ export class CollectionBrowser
347
549
  visibleCellIndices[visibleCellIndices.length - 1];
348
550
  const lastVisibleCellPage =
349
551
  Math.floor(lastVisibleCellIndex / this.pageSize) + 1;
552
+ if (this.currentPage !== lastVisibleCellPage) {
553
+ this.currentPage = lastVisibleCellPage;
554
+ }
350
555
  const event = new CustomEvent('visiblePageChanged', {
351
556
  detail: {
352
557
  pageNumber: lastVisibleCellPage,
@@ -359,6 +564,8 @@ export class CollectionBrowser
359
564
  // so this keeps track of whether we've already set the initial query
360
565
  private initialQueryChangeHappened = false;
361
566
 
567
+ private historyPopOccurred = false;
568
+
362
569
  // this lets us store the query key so we know if it's actually changed or not
363
570
  private previousQueryKey?: string;
364
571
 
@@ -376,6 +583,12 @@ export class CollectionBrowser
376
583
  this.scrollToPage(this.initialPageNumber);
377
584
  }
378
585
  this.initialQueryChangeHappened = true;
586
+ // if the query changed as part of a window.history pop event, we don't want to
587
+ // persist the state because it overwrites the forward history
588
+ if (!this.historyPopOccurred) {
589
+ this.persistState();
590
+ this.historyPopOccurred = false;
591
+ }
379
592
 
380
593
  await Promise.all([
381
594
  this.doInitialPageFetch(),
@@ -384,6 +597,61 @@ export class CollectionBrowser
384
597
  ]);
385
598
  }
386
599
 
600
+ private setupStateRestorationObserver() {
601
+ if (this.boundNavigationHandler) return;
602
+ this.boundNavigationHandler = this.historyNavigationHandler.bind(this);
603
+ // when the user navigates back, we want to update the UI to match the URL
604
+ window.addEventListener('popstate', this.boundNavigationHandler);
605
+ }
606
+
607
+ private boundNavigationHandler?: () => void;
608
+
609
+ private historyNavigationHandler() {
610
+ this.historyPopOccurred = true;
611
+ this.restoreState();
612
+ }
613
+
614
+ private restoreState() {
615
+ const restorationState = this.restorationStateHandler.getRestorationState();
616
+ this.displayMode = restorationState.displayMode;
617
+ this.selectedSort = restorationState.selectedSort ?? SortField.relevance;
618
+ this.sortDirection = restorationState.sortDirection ?? null;
619
+ this.selectedTitleFilter = restorationState.selectedTitleFilter ?? null;
620
+ this.selectedCreatorFilter = restorationState.selectedCreatorFilter ?? null;
621
+ this.selectedFacets = restorationState.selectedFacets;
622
+ this.baseQuery = restorationState.baseQuery;
623
+ this.titleQuery = restorationState.titleQuery;
624
+ this.creatorQuery = restorationState.creatorQuery;
625
+ this.dateRangeQueryClause = restorationState.dateRangeQueryClause;
626
+ this.sortParam = restorationState.sortParam ?? null;
627
+ this.currentPage = restorationState.currentPage ?? 1;
628
+ this.minSelectedDate = restorationState.minSelectedDate;
629
+ this.maxSelectedDate = restorationState.maxSelectedDate;
630
+ if (this.currentPage > 1) {
631
+ this.goToPage(this.currentPage);
632
+ }
633
+ }
634
+
635
+ private persistState() {
636
+ const restorationState: RestorationState = {
637
+ displayMode: this.displayMode,
638
+ sortParam: this.sortParam ?? undefined,
639
+ selectedSort: this.selectedSort,
640
+ sortDirection: this.sortDirection ?? undefined,
641
+ selectedFacets: this.selectedFacets ?? defaultSelectedFacets,
642
+ baseQuery: this.baseQuery,
643
+ currentPage: this.currentPage,
644
+ dateRangeQueryClause: this.dateRangeQueryClause,
645
+ titleQuery: this.titleQuery,
646
+ creatorQuery: this.creatorQuery,
647
+ minSelectedDate: this.minSelectedDate,
648
+ maxSelectedDate: this.maxSelectedDate,
649
+ selectedTitleFilter: this.selectedTitleFilter ?? undefined,
650
+ selectedCreatorFilter: this.selectedCreatorFilter ?? undefined,
651
+ };
652
+ this.restorationStateHandler.persistState(restorationState);
653
+ }
654
+
387
655
  private async doInitialPageFetch() {
388
656
  this.searchResultsLoading = true;
389
657
  await this.fetchPage(this.initialPageNumber);
@@ -412,54 +680,48 @@ export class CollectionBrowser
412
680
  return fullQuery;
413
681
  }
414
682
 
415
- private get facetQuery(): string | undefined {
416
- const facets = this.getFacetQuery(this.selectedFacets, false);
417
- const hiddenFacets = this.getFacetQuery(this.hiddenFacets, true);
418
-
419
- if (facets && hiddenFacets) return [facets, hiddenFacets].join(' AND ');
420
- if (facets) return facets;
421
- if (hiddenFacets) return hiddenFacets;
422
- return undefined;
423
- }
424
-
425
683
  /**
426
684
  * Generates a query string for the given facets
427
685
  *
428
- * Example: `mediatype:("collection" OR "audio") AND year:("2000" OR "2001")`
429
- *
430
- * For negative facets, we prefix the field with `-`, ie:
431
- * `-mediatype:("collection" OR "audio")`
432
- *
433
- * @param facets
434
- * @param negative
435
- * @returns
686
+ * Example: `mediatype:("collection" OR "audio" OR -"etree") AND year:("2000" OR "2001")`
436
687
  */
437
- private getFacetQuery(
438
- facets: Record<string, string[]>,
439
- negative: boolean
440
- ): string | undefined {
688
+ private get facetQuery(): string | undefined {
689
+ if (!this.selectedFacets) return undefined;
441
690
  const facetQuery = [];
442
- for (const [facetName, facetValues] of Object.entries(facets)) {
443
- const negation = negative ? '-' : '';
444
- const wrappedValues = facetValues.map(value => `"${value}"`);
445
- const valueQuery = wrappedValues.join(` OR `);
446
- facetQuery.push(`${negation}${facetName}:(${valueQuery})`);
691
+ for (const [facetName, facetValues] of Object.entries(
692
+ this.selectedFacets
693
+ )) {
694
+ const facetEntries = Object.entries(facetValues);
695
+ // eslint-disable-next-line no-continue
696
+ if (facetEntries.length === 0) continue;
697
+ const facetValuesArray: string[] = [];
698
+ for (const [key, facetState] of facetEntries) {
699
+ const plusMinusPrefix = facetState === 'hidden' ? '-' : '';
700
+
701
+ if (facetName === 'language') {
702
+ const languages =
703
+ this.languageCodeHandler.getCodeArrayFromCodeString(key);
704
+ for (const language of languages) {
705
+ facetValuesArray.push(`${plusMinusPrefix}"${language}"`);
706
+ }
707
+ } else {
708
+ facetValuesArray.push(`${plusMinusPrefix}"${key}"`);
709
+ }
710
+ }
711
+ const valueQuery = facetValuesArray.join(` OR `);
712
+ facetQuery.push(`${facetName}:(${valueQuery})`);
447
713
  }
448
714
  return facetQuery.length > 0 ? `(${facetQuery.join(' AND ')})` : undefined;
449
715
  }
450
716
 
451
- facetsChanged(e: CustomEvent<Record<string, string[]>>) {
717
+ facetsChanged(e: CustomEvent<SelectedFacets>) {
452
718
  this.selectedFacets = e.detail;
453
719
  }
454
720
 
455
- hiddenFacetsChanged(e: CustomEvent<Record<string, string[]>>) {
456
- this.hiddenFacets = e.detail;
457
- }
458
-
459
721
  private async fetchFacets() {
460
722
  if (!this.fullQuery) return;
461
723
 
462
- const aggregations = new AggregateSearchParams({
724
+ const aggregations = {
463
725
  advancedParams: [
464
726
  {
465
727
  field: 'subjectSorter',
@@ -486,14 +748,14 @@ export class CollectionBrowser
486
748
  size: 50,
487
749
  },
488
750
  ],
489
- });
751
+ };
490
752
 
491
- const params = new SearchParams({
753
+ const params: SearchParams = {
492
754
  query: this.fullQuery,
493
755
  fields: ['identifier'],
494
756
  aggregations,
495
757
  rows: 1,
496
- });
758
+ };
497
759
  this.facetsLoading = true;
498
760
  const results = await this.searchService?.search(params);
499
761
  this.facetsLoading = false;
@@ -517,7 +779,7 @@ export class CollectionBrowser
517
779
  * If this doesn't change, we don't need to re-fetch the histogram date range
518
780
  */
519
781
  private get fullQueryNoDateKey() {
520
- return `${this.fullQueryWithoutDate}-${this.sortParam?.asString}`;
782
+ return `${this.fullQueryWithoutDate}-${this.sortParam?.field}-${this.sortParam?.direction}`;
521
783
  }
522
784
 
523
785
  /**
@@ -536,16 +798,16 @@ export class CollectionBrowser
536
798
  return;
537
799
  this.previousFullQueryNoDate = fullQueryNoDateKey;
538
800
 
539
- const aggregations = new AggregateSearchParams({
801
+ const aggregations = {
540
802
  simpleParams: ['year'],
541
- });
803
+ };
542
804
 
543
- const params = new SearchParams({
805
+ const params = {
544
806
  query: this.fullQueryWithoutDate,
545
807
  fields: ['identifier'],
546
808
  aggregations,
547
809
  rows: 1,
548
- });
810
+ };
549
811
 
550
812
  this.fullYearAggregationLoading = true;
551
813
  const results = await this.searchService?.search(params);
@@ -580,7 +842,7 @@ export class CollectionBrowser
580
842
  * no longer relevant.
581
843
  */
582
844
  private get pageFetchQueryKey() {
583
- return `${this.fullQuery}-${this.sortParam?.asString}`;
845
+ return `${this.fullQuery}-${this.sortParam?.field}-${this.sortParam?.direction}`;
584
846
  }
585
847
 
586
848
  // this maps the query to the pages being fetched for that query
@@ -603,32 +865,33 @@ export class CollectionBrowser
603
865
  this.pageFetchesInProgress[pageFetchQueryKey] = pageFetches;
604
866
 
605
867
  const sortParams = this.sortParam ? [this.sortParam] : [];
606
- console.debug('sortParam', sortParams);
607
- const params = new SearchParams({
868
+ const params = {
608
869
  query: this.fullQuery,
609
870
  fields: [
871
+ 'addeddate',
872
+ 'avg_rating',
873
+ 'collections_raw',
874
+ 'creator',
875
+ 'date',
876
+ 'description',
877
+ 'downloads',
610
878
  'identifier',
611
- 'title',
879
+ 'issue',
880
+ 'item_count',
612
881
  'mediatype',
613
- 'downloads',
614
- 'avg_rating',
615
882
  'num_favorites',
616
883
  'num_reviews',
617
- 'item_count',
618
- 'description',
619
- 'date',
620
- 'addeddate',
621
884
  'publicdate',
622
885
  'reviewdate',
623
- 'creator',
624
- 'subject', // topic
625
886
  'source',
626
- 'collections_raw',
887
+ 'subject', // topic
888
+ 'title',
889
+ 'volume',
627
890
  ],
628
891
  page: pageNumber,
629
892
  rows: this.pageSize,
630
893
  sort: sortParams,
631
- });
894
+ };
632
895
  const results = await this.searchService?.search(params);
633
896
  const success = results?.success;
634
897
 
@@ -641,12 +904,33 @@ export class CollectionBrowser
641
904
  // right behind it
642
905
  const searchQuery = success.responseHeader.params.qin;
643
906
  const searchSort = success.responseHeader.params.sort;
907
+ let sortChanged = false;
908
+ if (!searchSort) {
909
+ // if we went from no sort to sort, the sort has changed
910
+ if (this.sortParam) {
911
+ sortChanged = true;
912
+ }
913
+ } else {
914
+ // check if the sort has changed
915
+ const split = searchSort.split(' ');
916
+ if (split.length > 1) {
917
+ const field = searchSort.split(' ')[0];
918
+ const direction = searchSort.split(' ')[1];
919
+ if (
920
+ field !== this.sortParam?.field ||
921
+ direction !== this.sortParam?.direction
922
+ ) {
923
+ sortChanged = true;
924
+ }
925
+ }
926
+ }
644
927
  const queryChangedSinceFetch =
645
- searchQuery !== this.fullQuery || searchSort !== this.sortParam?.asString;
928
+ searchQuery !== this.fullQuery || sortChanged;
646
929
  if (queryChangedSinceFetch) return;
647
930
 
648
931
  const { docs } = success.response;
649
932
  if (docs && docs.length > 0) {
933
+ this.preloadCollectionNames(docs);
650
934
  this.updateDataSource(pageNumber, docs);
651
935
  }
652
936
  if (docs.length < this.pageSize) {
@@ -658,6 +942,12 @@ export class CollectionBrowser
658
942
  this.searchResultsLoading = false;
659
943
  }
660
944
 
945
+ private preloadCollectionNames(docs: Metadata[]) {
946
+ const collectionIds = docs.map(doc => doc.collections_raw?.values).flat();
947
+ const collectionIdsArray = Array.from(new Set(collectionIds)) as string[];
948
+ this.collectionNameCache?.preloadIdentifiers(collectionIdsArray);
949
+ }
950
+
661
951
  /**
662
952
  * This is useful for determining whether we need to reload the scroller.
663
953
  *
@@ -689,23 +979,30 @@ export class CollectionBrowser
689
979
  docs?.forEach(doc => {
690
980
  if (!doc.identifier) return;
691
981
  tiles.push({
692
- identifier: doc.identifier,
693
- title: this.etreeTitle(doc.title?.value, doc.mediatype?.value),
694
- mediatype: doc.mediatype?.value ?? 'data',
695
- viewCount: doc.downloads?.value ?? 0,
696
- favCount: doc.num_favorites?.value ?? 0,
982
+ averageRating: doc.avg_rating?.value,
983
+ collections: doc.collections_raw?.values ?? [],
697
984
  commentCount: doc.num_reviews?.value ?? 0,
698
- itemCount: doc.item_count?.value ?? 0,
699
- description: doc.description?.value,
985
+ creator: doc.creator?.value,
986
+ creators: doc.creator?.values ?? [],
700
987
  dateAdded: doc.addeddate?.value,
701
988
  dateArchived: doc.publicdate?.value,
702
- dateReviewed: doc.reviewdate?.value,
703
989
  datePublished: doc.date?.value,
704
- creator: doc.creator?.value,
705
- averageRating: doc.avg_rating?.value,
706
- subject: doc.subject?.value,
990
+ dateReviewed: doc.reviewdate?.value,
991
+ description: doc.description?.value,
992
+ favCount: doc.num_favorites?.value ?? 0,
993
+ identifier: doc.identifier,
994
+ issue: doc.issue?.value,
995
+ itemCount: doc.item_count?.value ?? 0,
996
+ mediatype: doc.mediatype?.value ?? 'data',
707
997
  source: doc.source?.value,
708
- collections: doc.collections_raw?.values ?? [],
998
+ subjects: doc.subject?.values ?? [],
999
+ title: this.etreeTitle(
1000
+ doc.title?.value,
1001
+ doc.mediatype?.value,
1002
+ doc.collection?.values
1003
+ ),
1004
+ volume: doc.volume?.value,
1005
+ viewCount: doc.downloads?.value ?? 0,
709
1006
  });
710
1007
  });
711
1008
  datasource[pageNumber] = tiles;
@@ -726,10 +1023,10 @@ export class CollectionBrowser
726
1023
  */
727
1024
  private etreeTitle(
728
1025
  title: string | undefined,
729
- mediatype: string | undefined
1026
+ mediatype: string | undefined,
1027
+ collections: string[] | undefined
730
1028
  ): string {
731
- if (mediatype === 'etree') {
732
- // || collections.includes('etree')) {
1029
+ if (mediatype === 'etree' || collections?.includes('etree')) {
733
1030
  const regex = /^(.*) Live at (.*) on (\d\d\d\d-\d\d-\d\d)$/;
734
1031
  const newTitle = title?.replace(regex, '$3: $2');
735
1032
  if (newTitle) {
@@ -746,10 +1043,11 @@ export class CollectionBrowser
746
1043
  return html` <tile-dispatcher
747
1044
  .baseNavigationUrl=${this.baseNavigationUrl}
748
1045
  .model=${model}
749
- .displayMode=${this.displayMode}
1046
+ .tileDisplayMode=${this.displayMode}
750
1047
  .resizeObserver=${this.resizeObserver}
1048
+ .collectionNameCache=${this.collectionNameCache}
751
1049
  .sortParam=${this.sortParam}
752
- ?showDeleteButton=${this.showDeleteButtons}
1050
+ .mobileBreakpoint=${this.mobileBreakpoint}
753
1051
  ></tile-dispatcher>`;
754
1052
  }
755
1053
 
@@ -771,23 +1069,73 @@ export class CollectionBrowser
771
1069
  display: flex;
772
1070
  }
773
1071
 
1072
+ .collapser {
1073
+ display: inline-block;
1074
+ }
1075
+
1076
+ .collapser svg {
1077
+ width: 10px;
1078
+ height: 10px;
1079
+ transition: transform 0.2s ease-out;
1080
+ }
1081
+
1082
+ .collapser.open svg {
1083
+ transform: rotate(90deg);
1084
+ }
1085
+
1086
+ #mobile-filter-collapse h1 {
1087
+ cursor: pointer;
1088
+ }
1089
+
1090
+ #content-container.mobile {
1091
+ display: block;
1092
+ }
1093
+
1094
+ .column {
1095
+ padding-top: 2rem;
1096
+ }
1097
+
774
1098
  #right-column {
775
1099
  flex: 1;
776
1100
  position: relative;
777
1101
  border-left: 1px solid rgb(232, 232, 232);
1102
+ padding-left: 1rem;
1103
+ }
1104
+
1105
+ .mobile #right-column {
1106
+ border-left: none;
1107
+ padding: 0;
778
1108
  }
779
1109
 
780
1110
  #left-column {
781
1111
  width: 18rem;
782
1112
  padding-right: 12px;
1113
+ padding-right: 1rem;
783
1114
  }
784
1115
 
785
- .column {
786
- padding-top: 2rem;
1116
+ .mobile #left-column {
1117
+ width: 100%;
1118
+ padding: 0;
1119
+ }
1120
+
1121
+ #mobile-header-container {
1122
+ display: flex;
1123
+ justify-content: space-between;
787
1124
  }
788
1125
 
789
1126
  #facets-container {
790
1127
  position: relative;
1128
+ max-height: 0;
1129
+ transition: max-height 0.2s ease-in-out;
1130
+ z-index: 1;
1131
+ }
1132
+
1133
+ .mobile #facets-container {
1134
+ overflow: hidden;
1135
+ }
1136
+
1137
+ #facets-container.expanded {
1138
+ max-height: 2000px;
791
1139
  }
792
1140
 
793
1141
  #results-total {
@@ -796,6 +1144,10 @@ export class CollectionBrowser
796
1144
  margin-bottom: 5rem;
797
1145
  }
798
1146
 
1147
+ .mobile #results-total {
1148
+ margin-bottom: 0;
1149
+ }
1150
+
799
1151
  #big-results-count {
800
1152
  font-size: 2.4rem;
801
1153
  font-weight: 500;
@@ -808,6 +1160,10 @@ export class CollectionBrowser
808
1160
  text-transform: uppercase;
809
1161
  }
810
1162
 
1163
+ #list-header {
1164
+ max-height: 4.2rem;
1165
+ }
1166
+
811
1167
  .loading-cover {
812
1168
  position: absolute;
813
1169
  top: 0;
@@ -825,6 +1181,11 @@ export class CollectionBrowser
825
1181
  height: 30px;
826
1182
  }
827
1183
 
1184
+ sort-filter-bar {
1185
+ display: block;
1186
+ margin-bottom: 4rem;
1187
+ }
1188
+
828
1189
  infinite-scroller {
829
1190
  display: block;
830
1191
  --infiniteScrollerRowGap: var(--collectionBrowserRowGap, 1.7rem);
@@ -836,14 +1197,9 @@ export class CollectionBrowser
836
1197
  --collectionBrowserCellMinWidth,
837
1198
  100%
838
1199
  );
839
- --infiniteScrollerCellMinHeight: var(
840
- --collectionBrowserCellMinHeight,
841
- 2rem
842
- );
843
- --infiniteScrollerCellMaxHeight: var(
844
- --collectionBrowserCellMaxHeight,
845
- 2rem
846
- );
1200
+ --infiniteScrollerCellMinHeight: 34px; /* override infinite scroller component */
1201
+ --infiniteScrollerCellMaxHeight: 56px;
1202
+ --infiniteScrollerRowGap: 0px;
847
1203
  }
848
1204
 
849
1205
  infinite-scroller.list-detail {
@@ -855,6 +1211,16 @@ export class CollectionBrowser
855
1211
  --collectionBrowserCellMinHeight,
856
1212
  5rem
857
1213
  );
1214
+ /*
1215
+ 30px in spec, compensating for a -4px margin
1216
+ to align title with top of item image
1217
+ src/tiles/list/tile-list.ts
1218
+ */
1219
+ --infiniteScrollerRowGap: 34px;
1220
+ }
1221
+
1222
+ .mobile infinite-scroller.list-detail {
1223
+ --infiniteScrollerRowGap: 24px;
858
1224
  }
859
1225
 
860
1226
  infinite-scroller.grid {