@internetarchive/collection-browser 0.0.1-alpha.2 → 0.0.1-alpha.22

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 (183) hide show
  1. package/demo/app-root.ts +24 -158
  2. package/dist/demo/app-root.d.ts +2 -16
  3. package/dist/demo/app-root.js +23 -141
  4. package/dist/demo/app-root.js.map +1 -1
  5. package/dist/src/assets/img/icons/chevron.d.ts +2 -0
  6. package/dist/src/assets/img/icons/chevron.js +4 -0
  7. package/dist/src/assets/img/icons/chevron.js.map +1 -0
  8. package/dist/src/assets/img/icons/eye-closed.d.ts +2 -0
  9. package/dist/src/assets/img/icons/eye-closed.js +5 -0
  10. package/dist/src/assets/img/icons/eye-closed.js.map +1 -0
  11. package/dist/src/assets/img/icons/eye.d.ts +2 -0
  12. package/dist/src/assets/img/icons/eye.js +5 -0
  13. package/dist/src/assets/img/icons/eye.js.map +1 -0
  14. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -2
  15. package/dist/src/assets/img/icons/mediatype/account.js +5 -4
  16. package/dist/src/assets/img/icons/mediatype/account.js.map +1 -1
  17. package/dist/src/assets/img/icons/mediatype/audio.js +7 -4
  18. package/dist/src/assets/img/icons/mediatype/audio.js.map +1 -1
  19. package/dist/src/assets/img/icons/mediatype/collection.js +7 -4
  20. package/dist/src/assets/img/icons/mediatype/collection.js.map +1 -1
  21. package/dist/src/assets/img/icons/mediatype/etree.js +10 -5
  22. package/dist/src/assets/img/icons/mediatype/etree.js.map +1 -1
  23. package/dist/src/assets/img/icons/mediatype/film.js +2 -1
  24. package/dist/src/assets/img/icons/mediatype/film.js.map +1 -1
  25. package/dist/src/assets/img/icons/mediatype/images.js +9 -6
  26. package/dist/src/assets/img/icons/mediatype/images.js.map +1 -1
  27. package/dist/src/assets/img/icons/mediatype/software.js +9 -6
  28. package/dist/src/assets/img/icons/mediatype/software.js.map +1 -1
  29. package/dist/src/assets/img/icons/mediatype/texts.js +9 -6
  30. package/dist/src/assets/img/icons/mediatype/texts.js.map +1 -1
  31. package/dist/src/assets/img/icons/mediatype/tv.js +10 -5
  32. package/dist/src/assets/img/icons/mediatype/tv.js.map +1 -1
  33. package/dist/src/assets/img/icons/mediatype/video.js +10 -6
  34. package/dist/src/assets/img/icons/mediatype/video.js.map +1 -1
  35. package/dist/src/assets/img/icons/mediatype/web.js +9 -6
  36. package/dist/src/assets/img/icons/mediatype/web.js.map +1 -1
  37. package/dist/src/async-collection-name.d.ts +11 -0
  38. package/dist/src/async-collection-name.js +38 -0
  39. package/dist/src/async-collection-name.js.map +1 -0
  40. package/dist/src/collection-browser.d.ts +55 -17
  41. package/dist/src/collection-browser.js +466 -106
  42. package/dist/src/collection-browser.js.map +1 -1
  43. package/dist/src/collection-facets.d.ts +24 -5
  44. package/dist/src/collection-facets.js +300 -78
  45. package/dist/src/collection-facets.js.map +1 -1
  46. package/dist/src/collection-name-cache.d.ts +18 -0
  47. package/dist/src/collection-name-cache.js +89 -0
  48. package/dist/src/collection-name-cache.js.map +1 -0
  49. package/dist/src/mediatype-icon.js +10 -3
  50. package/dist/src/mediatype-icon.js.map +1 -1
  51. package/dist/src/models.d.ts +72 -14
  52. package/dist/src/models.js +57 -1
  53. package/dist/src/models.js.map +1 -1
  54. package/dist/src/restoration-state-handler.d.ts +37 -0
  55. package/dist/src/restoration-state-handler.js +177 -0
  56. package/dist/src/restoration-state-handler.js.map +1 -0
  57. package/dist/src/sort-filter-bar/alpha-bar.d.ts +1 -2
  58. package/dist/src/sort-filter-bar/alpha-bar.js +19 -9
  59. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  60. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -0
  61. package/dist/src/sort-filter-bar/img/compact.js +5 -0
  62. package/dist/src/sort-filter-bar/img/compact.js.map +1 -0
  63. package/dist/src/sort-filter-bar/img/grid.d.ts +1 -0
  64. package/dist/src/sort-filter-bar/img/grid.js +5 -0
  65. package/dist/src/sort-filter-bar/img/grid.js.map +1 -0
  66. package/dist/src/sort-filter-bar/img/list.d.ts +1 -0
  67. package/dist/src/sort-filter-bar/img/list.js +5 -0
  68. package/dist/src/sort-filter-bar/img/list.js.map +1 -0
  69. package/dist/src/sort-filter-bar/img/sort-triangle.d.ts +1 -0
  70. package/dist/src/sort-filter-bar/img/sort-triangle.js +5 -0
  71. package/dist/src/sort-filter-bar/img/sort-triangle.js.map +1 -0
  72. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -0
  73. package/dist/src/sort-filter-bar/img/tile.js +5 -0
  74. package/dist/src/sort-filter-bar/img/tile.js.map +1 -0
  75. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +65 -11
  76. package/dist/src/sort-filter-bar/sort-filter-bar.js +453 -142
  77. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  78. package/dist/src/tiles/grid/collection-tile.js +1 -2
  79. package/dist/src/tiles/grid/collection-tile.js.map +1 -1
  80. package/dist/src/tiles/grid/item-tile.d.ts +4 -0
  81. package/dist/src/tiles/grid/item-tile.js +134 -45
  82. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  83. package/dist/src/tiles/list/tile-list-compact-header.d.ts +11 -0
  84. package/dist/src/tiles/list/tile-list-compact-header.js +79 -0
  85. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -0
  86. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -0
  87. package/dist/src/tiles/list/tile-list-compact.js +122 -31
  88. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  89. package/dist/src/tiles/list/tile-list-detail.d.ts +0 -10
  90. package/dist/src/tiles/list/tile-list-detail.js +6 -159
  91. package/dist/src/tiles/list/tile-list-detail.js.map +1 -1
  92. package/dist/src/tiles/list/tile-list.d.ts +19 -6
  93. package/dist/src/tiles/list/tile-list.js +240 -108
  94. package/dist/src/tiles/list/tile-list.js.map +1 -1
  95. package/dist/src/tiles/tile-dispatcher.d.ts +8 -1
  96. package/dist/src/tiles/tile-dispatcher.js +46 -11
  97. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  98. package/dist/src/utils/format-date.js +1 -2
  99. package/dist/src/utils/format-date.js.map +1 -1
  100. package/dist/test/{utils/format-string.test.d.ts → collection-name-cache.test.d.ts} +0 -0
  101. package/dist/test/collection-name-cache.test.js +158 -0
  102. package/dist/test/collection-name-cache.test.js.map +1 -0
  103. package/dist/test/mocks/mock-search-response.d.ts +5 -0
  104. package/dist/test/mocks/mock-search-response.js +62 -0
  105. package/dist/test/mocks/mock-search-response.js.map +1 -0
  106. package/dist/test/mocks/mock-search-service.d.ts +13 -0
  107. package/dist/test/mocks/mock-search-service.js +20 -0
  108. package/dist/test/mocks/mock-search-service.js.map +1 -0
  109. package/package.json +9 -4
  110. package/src/assets/img/icons/chevron.ts +4 -0
  111. package/src/assets/img/icons/eye-closed.ts +5 -0
  112. package/src/assets/img/icons/eye.ts +5 -0
  113. package/src/assets/img/icons/mediatype/account.ts +5 -4
  114. package/src/assets/img/icons/mediatype/audio.ts +7 -4
  115. package/src/assets/img/icons/mediatype/collection.ts +7 -4
  116. package/src/assets/img/icons/mediatype/etree.ts +10 -5
  117. package/src/assets/img/icons/mediatype/film.ts +2 -1
  118. package/src/assets/img/icons/mediatype/images.ts +9 -6
  119. package/src/assets/img/icons/mediatype/software.ts +9 -6
  120. package/src/assets/img/icons/mediatype/texts.ts +9 -6
  121. package/src/assets/img/icons/mediatype/tv.ts +10 -5
  122. package/src/assets/img/icons/mediatype/video.ts +10 -6
  123. package/src/assets/img/icons/mediatype/web.ts +9 -6
  124. package/src/collection-browser.ts +490 -105
  125. package/src/collection-facets.ts +325 -109
  126. package/src/mediatype-icon.ts +10 -3
  127. package/src/models.ts +139 -14
  128. package/src/restoration-state-handler.ts +234 -0
  129. package/src/sort-filter-bar/alpha-bar.ts +19 -9
  130. package/src/sort-filter-bar/img/compact.ts +5 -0
  131. package/src/sort-filter-bar/img/list.ts +5 -0
  132. package/src/sort-filter-bar/img/sort-triangle.ts +5 -0
  133. package/src/sort-filter-bar/img/tile.ts +5 -0
  134. package/src/sort-filter-bar/sort-filter-bar.ts +499 -149
  135. package/src/tiles/grid/collection-tile.ts +1 -2
  136. package/src/tiles/grid/item-tile.ts +138 -56
  137. package/src/tiles/list/tile-list-compact-header.ts +75 -0
  138. package/src/tiles/list/tile-list-compact.ts +209 -0
  139. package/src/tiles/list/tile-list.ts +261 -110
  140. package/src/tiles/tile-dispatcher.ts +51 -11
  141. package/src/utils/format-date.ts +1 -2
  142. package/dist/src/assets/img/icons/audio.d.ts +0 -1
  143. package/dist/src/assets/img/icons/audio.js +0 -9
  144. package/dist/src/assets/img/icons/audio.js.map +0 -1
  145. package/dist/src/assets/img/icons/collection.d.ts +0 -1
  146. package/dist/src/assets/img/icons/collection.js +0 -9
  147. package/dist/src/assets/img/icons/collection.js.map +0 -1
  148. package/dist/src/assets/img/icons/etree.d.ts +0 -1
  149. package/dist/src/assets/img/icons/etree.js +0 -9
  150. package/dist/src/assets/img/icons/etree.js.map +0 -1
  151. package/dist/src/assets/img/icons/images.d.ts +0 -1
  152. package/dist/src/assets/img/icons/images.js +0 -10
  153. package/dist/src/assets/img/icons/images.js.map +0 -1
  154. package/dist/src/assets/img/icons/mediatype/etree copy.d.ts +0 -1
  155. package/dist/src/assets/img/icons/mediatype/etree copy.js +0 -9
  156. package/dist/src/assets/img/icons/mediatype/etree copy.js.map +0 -1
  157. package/dist/src/assets/img/icons/mediatype/livemusic.d.ts +0 -1
  158. package/dist/src/assets/img/icons/mediatype/livemusic.js +0 -7
  159. package/dist/src/assets/img/icons/mediatype/livemusic.js.map +0 -1
  160. package/dist/src/assets/img/icons/mediatype/photos.d.ts +0 -1
  161. package/dist/src/assets/img/icons/mediatype/photos.js +0 -7
  162. package/dist/src/assets/img/icons/mediatype/photos.js.map +0 -1
  163. package/dist/src/assets/img/icons/software.d.ts +0 -1
  164. package/dist/src/assets/img/icons/software.js +0 -10
  165. package/dist/src/assets/img/icons/software.js.map +0 -1
  166. package/dist/src/assets/img/icons/texts.d.ts +0 -1
  167. package/dist/src/assets/img/icons/texts.js +0 -10
  168. package/dist/src/assets/img/icons/texts.js.map +0 -1
  169. package/dist/src/assets/img/icons/tv.d.ts +0 -1
  170. package/dist/src/assets/img/icons/tv.js +0 -9
  171. package/dist/src/assets/img/icons/tv.js.map +0 -1
  172. package/dist/src/assets/img/icons/video.d.ts +0 -1
  173. package/dist/src/assets/img/icons/video.js +0 -10
  174. package/dist/src/assets/img/icons/video.js.map +0 -1
  175. package/dist/src/assets/img/icons/web.d.ts +0 -1
  176. package/dist/src/assets/img/icons/web.js +0 -10
  177. package/dist/src/assets/img/icons/web.js.map +0 -1
  178. package/dist/src/utils/format-string.d.ts +0 -2
  179. package/dist/src/utils/format-string.js +0 -7
  180. package/dist/src/utils/format-string.js.map +0 -1
  181. package/dist/test/utils/format-string.test.js +0 -17
  182. package/dist/test/utils/format-string.test.js.map +0 -1
  183. package/src/assets/img/icons/mediatype/foo.svg +0 -5
@@ -1,7 +1,15 @@
1
1
  import { __decorate } from "tslib";
2
- import { css, html, LitElement } from 'lit';
3
- import { customElement, property } from 'lit/decorators.js';
2
+ /* eslint-disable import/no-duplicates */
3
+ import { css, html, LitElement, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
4
5
  import { repeat } from 'lit/directives/repeat.js';
6
+ import '@internetarchive/histogram-date-range';
7
+ import '@internetarchive/feature-feedback';
8
+ import '@internetarchive/collection-name-cache';
9
+ import eyeIcon from './assets/img/icons/eye';
10
+ import eyeClosedIcon from './assets/img/icons/eye-closed';
11
+ import chevronIcon from './assets/img/icons/chevron';
12
+ import { defaultSelectedFacets, } from './models';
5
13
  const facetDisplayOrder = [
6
14
  'mediatype',
7
15
  'year',
@@ -29,26 +37,68 @@ const facetTitles = {
29
37
  let CollectionFacets = class CollectionFacets extends LitElement {
30
38
  constructor() {
31
39
  super(...arguments);
32
- this.selectedFacets = {};
33
- }
34
- get hydratedSelectedFacets() {
35
- const { selectedFacets } = this;
36
- const hydratedSelectedFacets = {};
37
- Object.entries(selectedFacets).forEach(([key]) => {
38
- const values = hydratedSelectedFacets[key];
39
- const title = facetTitles[key];
40
- hydratedSelectedFacets[title] = values || [];
41
- delete hydratedSelectedFacets[key];
42
- });
43
- return hydratedSelectedFacets;
40
+ this.facetsLoading = false;
41
+ this.fullYearAggregationLoading = false;
42
+ this.collapsableFacets = false;
43
+ this.openFacets = {
44
+ subject: false,
45
+ mediatype: false,
46
+ language: false,
47
+ creator: false,
48
+ collection: false,
49
+ year: false,
50
+ };
44
51
  }
45
52
  render() {
46
53
  return html `
47
- <h1>Facets</h1>
54
+ <div id="container" class="${this.facetsLoading ? 'loading' : ''}">
55
+ <div class="facet-group">
56
+ <h1>Year Published <feature-feedback></feature-feedback></h1>
57
+ ${this.histogramTemplate}
58
+ </div>
48
59
 
49
- ${this.mergedFacets.map(facetGroup => this.getFacetTemplate(facetGroup))}
60
+ ${this.mergedFacets.map(facetGroup => this.getFacetGroupTemplate(facetGroup))}
61
+ </div>
50
62
  `;
51
63
  }
64
+ updated(changed) {
65
+ if (changed.has('selectedFacets')) {
66
+ this.dispatchFacetsChangedEvent();
67
+ }
68
+ }
69
+ dispatchFacetsChangedEvent() {
70
+ const event = new CustomEvent('facetsChanged', {
71
+ detail: this.selectedFacets,
72
+ });
73
+ this.dispatchEvent(event);
74
+ }
75
+ get currentYearsHistogramAggregation() {
76
+ var _a;
77
+ return (_a = this.aggregations) === null || _a === void 0 ? void 0 : _a.year_histogram;
78
+ }
79
+ get histogramTemplate() {
80
+ const { fullYearsHistogramAggregation } = this;
81
+ return html `
82
+ <histogram-date-range
83
+ .minDate=${fullYearsHistogramAggregation === null || fullYearsHistogramAggregation === void 0 ? void 0 : fullYearsHistogramAggregation.first_bucket_key}
84
+ .maxDate=${fullYearsHistogramAggregation === null || fullYearsHistogramAggregation === void 0 ? void 0 : fullYearsHistogramAggregation.last_bucket_key}
85
+ .minSelectedDate=${this.minSelectedDate}
86
+ .maxSelectedDate=${this.maxSelectedDate}
87
+ .updateDelay=${100}
88
+ missingDataMessage="..."
89
+ .width=${180}
90
+ .bins=${fullYearsHistogramAggregation === null || fullYearsHistogramAggregation === void 0 ? void 0 : fullYearsHistogramAggregation.buckets}
91
+ @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
92
+ ></histogram-date-range>
93
+ `;
94
+ }
95
+ histogramDateRangeUpdated(e) {
96
+ const { minDate, maxDate } = e.detail;
97
+ const event = new CustomEvent('histogramDateRangeUpdated', {
98
+ detail: { minDate, maxDate },
99
+ });
100
+ this.dispatchEvent(event);
101
+ }
52
102
  /**
53
103
  * Combines the selected facets with the aggregations to create a single list of facets
54
104
  */
@@ -87,31 +137,32 @@ let CollectionFacets = class CollectionFacets extends LitElement {
87
137
  bucketsWithCount.push(bucket);
88
138
  });
89
139
  facetGroup.buckets = bucketsWithCount.splice(0, 5);
90
- if (facetGroup.buckets.length === 0)
91
- return;
92
140
  facetGroups.push(facetGroup);
93
141
  });
94
142
  return facetGroups;
95
143
  }
96
144
  /**
97
- * Converts the raw `selectedFacets` to `FacetGroups`, which are easier to use
145
+ * Converts the selected facets to a `FacetGroup` array,
146
+ * which is easier to work with
98
147
  */
99
148
  get selectedFacetGroups() {
100
- const selectedFacetGroups = [];
101
- Object.entries(this.selectedFacets).forEach(([key, buckets]) => {
102
- const title = facetTitles[key];
103
- const group = {
149
+ if (!this.selectedFacets)
150
+ return [];
151
+ const facetGroups = Object.entries(this.selectedFacets).map(([key, selectedFacets]) => {
152
+ const option = key;
153
+ const title = facetTitles[option];
154
+ const buckets = Object.entries(selectedFacets).map(([value, facetState]) => ({
155
+ key: value,
156
+ count: 0,
157
+ state: facetState,
158
+ }));
159
+ return {
104
160
  title,
105
- key,
106
- buckets: buckets.map(bucket => ({
107
- key: bucket,
108
- count: 0,
109
- selected: true,
110
- })),
161
+ key: option,
162
+ buckets,
111
163
  };
112
- selectedFacetGroups.push(group);
113
164
  });
114
- return selectedFacetGroups;
165
+ return facetGroups;
115
166
  }
116
167
  /**
117
168
  * Converts the raw `aggregations` to `FacetGroups`, which are easier to use
@@ -124,10 +175,11 @@ let CollectionFacets = class CollectionFacets extends LitElement {
124
175
  return;
125
176
  const option = this.getFacetOptionFromKey(key);
126
177
  const title = facetTitles[option];
127
- const facetBuckets = buckets.buckets.map(bucket => ({
178
+ const castedBuckets = buckets.buckets;
179
+ const facetBuckets = castedBuckets.map(bucket => ({
128
180
  key: `${bucket.key}`,
129
181
  count: bucket.doc_count,
130
- selected: false,
182
+ state: 'none',
131
183
  }));
132
184
  const group = {
133
185
  title,
@@ -138,65 +190,126 @@ let CollectionFacets = class CollectionFacets extends LitElement {
138
190
  });
139
191
  return facetGroups;
140
192
  }
193
+ getFacetGroupTemplate(facetGroup) {
194
+ const { key } = facetGroup;
195
+ const isOpen = this.openFacets[key];
196
+ const collapser = html `
197
+ <span class="collapser ${isOpen ? 'open' : ''}"> ${chevronIcon} </span>
198
+ `;
199
+ return html `
200
+ <div class="facet-group ${this.collapsableFacets ? 'mobile' : ''}">
201
+ <h1
202
+ @click=${() => {
203
+ const newOpenFacets = { ...this.openFacets };
204
+ newOpenFacets[key] = !isOpen;
205
+ this.openFacets = newOpenFacets;
206
+ }}
207
+ @keyup=${() => {
208
+ const newOpenFacets = { ...this.openFacets };
209
+ newOpenFacets[key] = !isOpen;
210
+ this.openFacets = newOpenFacets;
211
+ }}
212
+ >
213
+ ${this.collapsableFacets ? collapser : nothing} ${facetGroup.title}
214
+ </h1>
215
+ <div class="facet-group-content ${isOpen ? 'open' : ''}">
216
+ ${this.getFacetTemplate(facetGroup)}
217
+ </div>
218
+ </div>
219
+ `;
220
+ }
141
221
  getFacetTemplate(facetGroup) {
142
222
  return html `
143
- <h2>${facetGroup.title}</h2>
144
- ${repeat(facetGroup.buckets, bucket => `${facetGroup.key}:${bucket.key}`, bucket => html `
145
- <label class="facet-row">
146
- <div class="facet-checkbox">
147
- <input
148
- type="checkbox"
149
- .name=${facetGroup.key}
150
- .value=${bucket.key}
151
- @click=${this.facetToggled}
152
- ?checked=${bucket.selected}
153
- />
154
- </div>
155
- <div class="facet-title">${bucket.key}</div>
156
- <div class="facet-count">${bucket.count}</div>
157
- </label>
158
- `)}
223
+ <ul class="facet-list">
224
+ ${repeat(facetGroup.buckets, bucket => `${facetGroup.key}:${bucket.key}`, bucket => {
225
+ const negativeCheckboxId = `${facetGroup.key}:${bucket.key}-negative`;
226
+ // for collections, we need to asynchronously load the collection name
227
+ // so we use the `async-collection-name` widget and for the rest, we have
228
+ // a static value to use
229
+ const bucketTextDisplay = facetGroup.key === 'collection'
230
+ ? html `
231
+ <async-collection-name
232
+ .collectionNameCache=${this.collectionNameCache}
233
+ .identifier=${bucket.key}
234
+ placeholder="-"
235
+ ></async-collection-name>
236
+ `
237
+ : html `${bucket.key}`;
238
+ return html `
239
+ <li>
240
+ <label class="facet-row">
241
+ <div class="facet-checkbox">
242
+ <input
243
+ type="checkbox"
244
+ .name=${facetGroup.key}
245
+ .value=${bucket.key}
246
+ @click=${(e) => {
247
+ this.facetClicked(e, bucket, false);
248
+ }}
249
+ .checked=${bucket.state === 'selected'}
250
+ class="select-facet-checkbox"
251
+ />
252
+ <input
253
+ type="checkbox"
254
+ id=${negativeCheckboxId}
255
+ .name=${facetGroup.key}
256
+ .value=${bucket.key}
257
+ @click=${(e) => {
258
+ this.facetClicked(e, bucket, true);
259
+ }}
260
+ .checked=${bucket.state === 'hidden'}
261
+ class="hide-facet-checkbox"
262
+ />
263
+ <label for=${negativeCheckboxId} class="hide-facet-icon">
264
+ ${bucket.state === 'hidden' ? eyeClosedIcon : eyeIcon}
265
+ </label>
266
+ </div>
267
+ <div class="facet-title">${bucketTextDisplay}</div>
268
+ <div class="facet-count">${bucket.count}</div>
269
+ </label>
270
+ </li>
271
+ `;
272
+ })}
273
+ </ul>
159
274
  `;
160
275
  }
161
- facetChecked(name, value) {
162
- const { selectedFacets } = this;
163
- const facetClone = { ...selectedFacets };
164
- const currentFacetValues = facetClone[name];
165
- if (currentFacetValues) {
166
- currentFacetValues.push(value);
167
- facetClone[name] = currentFacetValues;
276
+ facetClicked(e, bucket, negative) {
277
+ const target = e.target;
278
+ const { checked, name, value } = target;
279
+ if (checked) {
280
+ this.facetChecked(name, value, negative);
168
281
  }
169
282
  else {
170
- facetClone[name] = [value];
283
+ this.facetUnchecked(name, value);
171
284
  }
172
- this.selectedFacets = facetClone;
173
285
  }
174
- facetUnchecked(name, value) {
286
+ facetChecked(key, value, negative) {
175
287
  const { selectedFacets } = this;
176
- const facetClone = { ...selectedFacets };
177
- let currentFacetValues = selectedFacets[name];
178
- if (currentFacetValues) {
179
- currentFacetValues = currentFacetValues.filter(el => el !== value);
180
- facetClone[name] = currentFacetValues;
181
- if (currentFacetValues.length === 0) {
182
- delete facetClone[name];
183
- }
288
+ let newFacets;
289
+ if (selectedFacets) {
290
+ newFacets = {
291
+ ...selectedFacets,
292
+ };
184
293
  }
185
- this.selectedFacets = facetClone;
294
+ else {
295
+ newFacets = defaultSelectedFacets;
296
+ }
297
+ newFacets[key][value] = negative ? 'hidden' : 'selected';
298
+ this.selectedFacets = newFacets;
186
299
  }
187
- facetToggled(e) {
188
- const target = e.target;
189
- const { checked, name, value } = target;
190
- if (checked) {
191
- this.facetChecked(name, value);
300
+ facetUnchecked(key, value) {
301
+ const { selectedFacets } = this;
302
+ let newFacets;
303
+ if (selectedFacets) {
304
+ newFacets = {
305
+ ...selectedFacets,
306
+ };
192
307
  }
193
308
  else {
194
- this.facetUnchecked(name, value);
309
+ newFacets = defaultSelectedFacets;
195
310
  }
196
- const event = new CustomEvent('facetsChanged', {
197
- detail: this.selectedFacets,
198
- });
199
- this.dispatchEvent(event);
311
+ delete newFacets[key][value];
312
+ this.selectedFacets = newFacets;
200
313
  }
201
314
  /**
202
315
  * Parse the aggregate key title into the human readable title
@@ -218,8 +331,79 @@ let CollectionFacets = class CollectionFacets extends LitElement {
218
331
  }
219
332
  static get styles() {
220
333
  return css `
334
+ #container.loading {
335
+ opacity: 0.5;
336
+ }
337
+
338
+ .collapser {
339
+ display: inline-block;
340
+ cursor: pointer;
341
+ width: 10px;
342
+ height: 10px;
343
+ }
344
+
345
+ .collapser svg {
346
+ transition: transform 0.2s ease-in-out;
347
+ }
348
+
349
+ .collapser.open svg {
350
+ transform: rotate(90deg);
351
+ }
352
+
353
+ .facet-group {
354
+ margin-bottom: 2rem;
355
+ }
356
+
357
+ .facet-group h1 {
358
+ margin-bottom: 0.7rem;
359
+ }
360
+
361
+ .facet-group.mobile h1 {
362
+ cursor: pointer;
363
+ }
364
+
365
+ .facet-group-content {
366
+ transition: max-height 0.2s ease-in-out;
367
+ }
368
+
369
+ .facet-group.mobile .facet-group-content {
370
+ max-height: 0;
371
+ overflow: hidden;
372
+ }
373
+
374
+ .facet-group.mobile .facet-group-content.open {
375
+ max-height: 2000px;
376
+ }
377
+
378
+ h1 {
379
+ font-size: 1.4rem;
380
+ font-weight: 200;
381
+ border-bottom: 1px solid rgb(232, 232, 232);
382
+ padding-bottom: 3px;
383
+ margin: 0;
384
+ }
385
+
386
+ ul.facet-list {
387
+ list-style: none;
388
+ margin: 0;
389
+ padding: 0;
390
+ }
391
+
392
+ ul.facet-list li {
393
+ margin-bottom: 0.2rem;
394
+ }
395
+
396
+ .facet-checkbox {
397
+ margin-right: 0.5rem;
398
+ display: flex;
399
+ align-items: center;
400
+ }
401
+
221
402
  .facet-row {
222
403
  display: flex;
404
+ align-items: center;
405
+ font-weight: 500;
406
+ font-size: 1.2rem;
223
407
  }
224
408
 
225
409
  .facet-title {
@@ -229,15 +413,53 @@ let CollectionFacets = class CollectionFacets extends LitElement {
229
413
  .facet-count {
230
414
  margin-left: 0.5rem;
231
415
  }
416
+
417
+ .select-facet-checkbox {
418
+ margin-right: 5px;
419
+ }
420
+
421
+ .hide-facet-checkbox {
422
+ display: none;
423
+ }
424
+
425
+ .hide-facet-icon {
426
+ width: 15px;
427
+ height: 15px;
428
+ cursor: pointer;
429
+ }
232
430
  `;
233
431
  }
234
432
  };
235
433
  __decorate([
236
434
  property({ type: Object })
237
435
  ], CollectionFacets.prototype, "aggregations", void 0);
436
+ __decorate([
437
+ property({ type: Object })
438
+ ], CollectionFacets.prototype, "fullYearsHistogramAggregation", void 0);
439
+ __decorate([
440
+ property({ type: String })
441
+ ], CollectionFacets.prototype, "minSelectedDate", void 0);
442
+ __decorate([
443
+ property({ type: String })
444
+ ], CollectionFacets.prototype, "maxSelectedDate", void 0);
445
+ __decorate([
446
+ property({ type: Boolean })
447
+ ], CollectionFacets.prototype, "facetsLoading", void 0);
448
+ __decorate([
449
+ property({ type: Boolean })
450
+ ], CollectionFacets.prototype, "fullYearAggregationLoading", void 0);
238
451
  __decorate([
239
452
  property({ type: Object })
240
453
  ], CollectionFacets.prototype, "selectedFacets", void 0);
454
+ __decorate([
455
+ property({ type: Boolean })
456
+ ], CollectionFacets.prototype, "collapsableFacets", void 0);
457
+ __decorate([
458
+ property({ type: Object })
459
+ ], CollectionFacets.prototype, "collectionNameCache", void 0);
460
+ __decorate([
461
+ state()
462
+ ], CollectionFacets.prototype, "openFacets", void 0);
241
463
  CollectionFacets = __decorate([
242
464
  customElement('collection-facets')
243
465
  ], CollectionFacets);
@@ -1 +1 @@
1
- {"version":3,"file":"collection-facets.js","sourceRoot":"","sources":["../../src/collection-facets.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { Aggregation } from '@internetarchive/search-service';\n\ntype FacetOption =\n | 'subject'\n | 'mediatype'\n | 'language'\n | 'creator'\n | 'collection'\n | 'year';\n\nconst facetDisplayOrder: FacetOption[] = [\n 'mediatype',\n 'year',\n 'subject',\n 'collection',\n 'creator',\n 'language',\n];\n\nconst aggregationToFacetOption: Record<string, FacetOption> = {\n subjectSorter: 'subject',\n mediatypeSorter: 'mediatype',\n languageSorter: 'language',\n creatorSorter: 'creator',\n collection: 'collection',\n year: 'year',\n};\n\nconst facetTitles: Record<FacetOption, string> = {\n subject: 'Subject',\n mediatype: 'Media Type',\n language: 'Language',\n creator: 'Creator',\n collection: 'Collection',\n year: 'Year',\n};\n\ninterface FacetBucket {\n // for some facets, we augment the key with a display value\n displayText?: string;\n key: string;\n count: number;\n selected: boolean;\n}\n\ninterface FacetGroup {\n title: string;\n key: string;\n buckets: FacetBucket[];\n}\n\n@customElement('collection-facets')\nexport class CollectionFacets extends LitElement {\n @property({ type: Object }) aggregations?: Record<string, Aggregation>;\n\n @property({ type: Object }) selectedFacets: Record<string, string[]> = {};\n\n private get hydratedSelectedFacets(): Record<string, string[]> {\n const { selectedFacets } = this;\n const hydratedSelectedFacets: Record<string, string[]> = {};\n Object.entries(selectedFacets).forEach(([key]) => {\n const values = hydratedSelectedFacets[key];\n const title = facetTitles[key as FacetOption];\n hydratedSelectedFacets[title] = values || [];\n delete hydratedSelectedFacets[key];\n });\n return hydratedSelectedFacets;\n }\n\n render() {\n return html`\n <h1>Facets</h1>\n\n ${this.mergedFacets.map(facetGroup => this.getFacetTemplate(facetGroup))}\n `;\n }\n\n /**\n * Combines the selected facets with the aggregations to create a single list of facets\n */\n private get mergedFacets(): FacetGroup[] {\n const facetGroups: FacetGroup[] = [];\n\n facetDisplayOrder.forEach(facetKey => {\n const selectedFacetGroup = this.selectedFacetGroups.find(\n group => group.key === facetKey\n );\n const aggregateFacetGroup = this.aggregationFacetGroups.find(\n group => group.key === facetKey\n );\n\n // if the user selected a facet, but it's not in the aggregation, we add it as-is\n if (selectedFacetGroup && !aggregateFacetGroup) {\n facetGroups.push(selectedFacetGroup);\n return;\n }\n\n // if we don't have an aggregate facet group, don't add this to the list\n if (!aggregateFacetGroup) return;\n\n // start with either the selected group if we have one, or the aggregate group\n const facetGroup = selectedFacetGroup ?? aggregateFacetGroup;\n\n // attach the counts to the selected buckets\n const bucketsWithCount =\n selectedFacetGroup?.buckets.map(bucket => {\n const selectedBucket = aggregateFacetGroup.buckets.find(\n b => b.key === bucket.key\n );\n if (selectedBucket) {\n return {\n ...bucket,\n count: selectedBucket.count,\n };\n }\n return bucket;\n }) ?? [];\n\n // append any additional buckets that were not selected\n aggregateFacetGroup.buckets.forEach(bucket => {\n const existingBucket = bucketsWithCount.find(b => b.key === bucket.key);\n if (existingBucket) return;\n bucketsWithCount.push(bucket);\n });\n facetGroup.buckets = bucketsWithCount.splice(0, 5);\n\n if (facetGroup.buckets.length === 0) return;\n\n facetGroups.push(facetGroup);\n });\n\n return facetGroups;\n }\n\n /**\n * Converts the raw `selectedFacets` to `FacetGroups`, which are easier to use\n */\n private get selectedFacetGroups(): FacetGroup[] {\n const selectedFacetGroups: FacetGroup[] = [];\n Object.entries(this.selectedFacets).forEach(([key, buckets]) => {\n const title = facetTitles[key as FacetOption];\n const group = {\n title,\n key,\n buckets: buckets.map(bucket => ({\n key: bucket,\n count: 0,\n selected: true,\n })),\n };\n selectedFacetGroups.push(group);\n });\n return selectedFacetGroups;\n }\n\n /**\n * Converts the raw `aggregations` to `FacetGroups`, which are easier to use\n */\n private get aggregationFacetGroups(): FacetGroup[] {\n const facetGroups: FacetGroup[] = [];\n Object.entries(this.aggregations ?? []).forEach(([key, buckets]) => {\n if (key === 'year_histogram') return;\n const option = this.getFacetOptionFromKey(key);\n const title = facetTitles[option];\n const facetBuckets: FacetBucket[] = buckets.buckets.map(bucket => ({\n key: `${bucket.key}`,\n count: bucket.doc_count,\n selected: false,\n }));\n const group: FacetGroup = {\n title,\n key: option,\n buckets: facetBuckets,\n };\n facetGroups.push(group);\n });\n return facetGroups;\n }\n\n private getFacetTemplate(facetGroup: FacetGroup) {\n return html`\n <h2>${facetGroup.title}</h2>\n ${repeat(\n facetGroup.buckets,\n bucket => `${facetGroup.key}:${bucket.key}`,\n bucket => html`\n <label class=\"facet-row\">\n <div class=\"facet-checkbox\">\n <input\n type=\"checkbox\"\n .name=${facetGroup.key}\n .value=${bucket.key}\n @click=${this.facetToggled}\n ?checked=${bucket.selected}\n />\n </div>\n <div class=\"facet-title\">${bucket.key}</div>\n <div class=\"facet-count\">${bucket.count}</div>\n </label>\n `\n )}\n `;\n }\n\n private facetChecked(name: string, value: string) {\n const { selectedFacets } = this;\n const facetClone = { ...selectedFacets };\n const currentFacetValues = facetClone[name];\n if (currentFacetValues) {\n currentFacetValues.push(value);\n facetClone[name] = currentFacetValues;\n } else {\n facetClone[name] = [value];\n }\n this.selectedFacets = facetClone;\n }\n\n private facetUnchecked(name: string, value: string) {\n const { selectedFacets } = this;\n const facetClone = { ...selectedFacets };\n let currentFacetValues = selectedFacets[name];\n if (currentFacetValues) {\n currentFacetValues = currentFacetValues.filter(el => el !== value);\n facetClone[name] = currentFacetValues;\n if (currentFacetValues.length === 0) {\n delete facetClone[name];\n }\n }\n this.selectedFacets = facetClone;\n }\n\n private facetToggled(e: Event) {\n const target = e.target as HTMLInputElement;\n const { checked, name, value } = target;\n if (checked) {\n this.facetChecked(name, value);\n } else {\n this.facetUnchecked(name, value);\n }\n\n const event = new CustomEvent<Record<string, string[]>>('facetsChanged', {\n detail: this.selectedFacets,\n });\n this.dispatchEvent(event);\n }\n\n /**\n * Parse the aggregate key title into the human readable title\n *\n * Example: user_aggs__terms__field:mediatypeSorter__size:6 => Media Type\n *\n * @param key\n * @returns\n */\n private getFacetOptionFromKey(key: string): FacetOption {\n const parts = key.split('__');\n const fieldNamePart = parts[2];\n const fieldName = fieldNamePart.split(':')[1];\n const facetMatch = Object.entries(aggregationToFacetOption).find(([key2]) =>\n fieldName.includes(key2)\n );\n const option = facetMatch?.[1];\n if (!option) throw new Error(`Could not find facet option for key: ${key}`);\n return option;\n }\n\n static get styles() {\n return css`\n .facet-row {\n display: flex;\n }\n\n .facet-title {\n flex: 1;\n }\n\n .facet-count {\n margin-left: 0.5rem;\n }\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"collection-facets.js","sourceRoot":"","sources":["../../src/collection-facets.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable import/no-duplicates */\nimport { css, html, LitElement, PropertyValues, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { Aggregation, Bucket } from '@internetarchive/search-service';\nimport '@internetarchive/histogram-date-range';\nimport '@internetarchive/feature-feedback';\nimport '@internetarchive/collection-name-cache';\nimport { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';\nimport eyeIcon from './assets/img/icons/eye';\nimport eyeClosedIcon from './assets/img/icons/eye-closed';\nimport chevronIcon from './assets/img/icons/chevron';\nimport {\n FacetOption,\n SelectedFacets,\n FacetGroup,\n FacetBucket,\n defaultSelectedFacets,\n} from './models';\n\nconst facetDisplayOrder: FacetOption[] = [\n 'mediatype',\n 'year',\n 'subject',\n 'collection',\n 'creator',\n 'language',\n];\n\nconst aggregationToFacetOption: Record<string, FacetOption> = {\n subjectSorter: 'subject',\n mediatypeSorter: 'mediatype',\n languageSorter: 'language',\n creatorSorter: 'creator',\n collection: 'collection',\n year: 'year',\n};\n\nconst facetTitles: Record<FacetOption, string> = {\n subject: 'Subject',\n mediatype: 'Media Type',\n language: 'Language',\n creator: 'Creator',\n collection: 'Collection',\n year: 'Year',\n};\n\n@customElement('collection-facets')\nexport class CollectionFacets extends LitElement {\n @property({ type: Object }) aggregations?: Record<string, Aggregation>;\n\n @property({ type: Object }) fullYearsHistogramAggregation?: Aggregation;\n\n @property({ type: String }) minSelectedDate?: string;\n\n @property({ type: String }) maxSelectedDate?: string;\n\n @property({ type: Boolean }) facetsLoading = false;\n\n @property({ type: Boolean }) fullYearAggregationLoading = false;\n\n @property({ type: Object }) selectedFacets?: SelectedFacets;\n\n @property({ type: Boolean }) collapsableFacets = false;\n\n @property({ type: Object })\n collectionNameCache?: CollectionNameCacheInterface;\n\n @state() openFacets: Record<FacetOption, boolean> = {\n subject: false,\n mediatype: false,\n language: false,\n creator: false,\n collection: false,\n year: false,\n };\n\n render() {\n return html`\n <div id=\"container\" class=\"${this.facetsLoading ? 'loading' : ''}\">\n <div class=\"facet-group\">\n <h1>Year Published <feature-feedback></feature-feedback></h1>\n ${this.histogramTemplate}\n </div>\n\n ${this.mergedFacets.map(facetGroup =>\n this.getFacetGroupTemplate(facetGroup)\n )}\n </div>\n `;\n }\n\n updated(changed: PropertyValues) {\n if (changed.has('selectedFacets')) {\n this.dispatchFacetsChangedEvent();\n }\n }\n\n private dispatchFacetsChangedEvent() {\n const event = new CustomEvent<SelectedFacets>('facetsChanged', {\n detail: this.selectedFacets,\n });\n this.dispatchEvent(event);\n }\n\n private get currentYearsHistogramAggregation(): Aggregation | undefined {\n return this.aggregations?.year_histogram;\n }\n\n private get histogramTemplate() {\n const { fullYearsHistogramAggregation } = this;\n return html`\n <histogram-date-range\n .minDate=${fullYearsHistogramAggregation?.first_bucket_key}\n .maxDate=${fullYearsHistogramAggregation?.last_bucket_key}\n .minSelectedDate=${this.minSelectedDate}\n .maxSelectedDate=${this.maxSelectedDate}\n .updateDelay=${100}\n missingDataMessage=\"...\"\n .width=${180}\n .bins=${fullYearsHistogramAggregation?.buckets as number[]}\n @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}\n ></histogram-date-range>\n `;\n }\n\n private histogramDateRangeUpdated(\n e: CustomEvent<{\n minDate: string;\n maxDate: string;\n }>\n ) {\n const { minDate, maxDate } = e.detail;\n const event = new CustomEvent('histogramDateRangeUpdated', {\n detail: { minDate, maxDate },\n });\n this.dispatchEvent(event);\n }\n\n /**\n * Combines the selected facets with the aggregations to create a single list of facets\n */\n private get mergedFacets(): FacetGroup[] {\n const facetGroups: FacetGroup[] = [];\n\n facetDisplayOrder.forEach(facetKey => {\n const selectedFacetGroup = this.selectedFacetGroups.find(\n group => group.key === facetKey\n );\n const aggregateFacetGroup = this.aggregationFacetGroups.find(\n group => group.key === facetKey\n );\n\n // if the user selected a facet, but it's not in the aggregation, we add it as-is\n if (selectedFacetGroup && !aggregateFacetGroup) {\n facetGroups.push(selectedFacetGroup);\n return;\n }\n\n // if we don't have an aggregate facet group, don't add this to the list\n if (!aggregateFacetGroup) return;\n\n // start with either the selected group if we have one, or the aggregate group\n const facetGroup = selectedFacetGroup ?? aggregateFacetGroup;\n\n // attach the counts to the selected buckets\n const bucketsWithCount =\n selectedFacetGroup?.buckets.map(bucket => {\n const selectedBucket = aggregateFacetGroup.buckets.find(\n b => b.key === bucket.key\n );\n if (selectedBucket) {\n return {\n ...bucket,\n count: selectedBucket.count,\n };\n }\n return bucket;\n }) ?? [];\n\n // append any additional buckets that were not selected\n aggregateFacetGroup.buckets.forEach(bucket => {\n const existingBucket = bucketsWithCount.find(b => b.key === bucket.key);\n if (existingBucket) return;\n bucketsWithCount.push(bucket);\n });\n facetGroup.buckets = bucketsWithCount.splice(0, 5);\n\n facetGroups.push(facetGroup);\n });\n\n return facetGroups;\n }\n\n /**\n * Converts the selected facets to a `FacetGroup` array,\n * which is easier to work with\n */\n private get selectedFacetGroups(): FacetGroup[] {\n if (!this.selectedFacets) return [];\n\n const facetGroups: FacetGroup[] = Object.entries(this.selectedFacets).map(\n ([key, selectedFacets]) => {\n const option = key as FacetOption;\n const title = facetTitles[option];\n\n const buckets: FacetBucket[] = Object.entries(selectedFacets).map(\n ([value, facetState]) => ({\n key: value,\n count: 0,\n state: facetState,\n })\n );\n\n return {\n title,\n key: option,\n buckets,\n };\n }\n );\n\n return facetGroups;\n }\n\n /**\n * Converts the raw `aggregations` to `FacetGroups`, which are easier to use\n */\n private get aggregationFacetGroups(): FacetGroup[] {\n const facetGroups: FacetGroup[] = [];\n Object.entries(this.aggregations ?? []).forEach(([key, buckets]) => {\n if (key === 'year_histogram') return;\n const option = this.getFacetOptionFromKey(key);\n const title = facetTitles[option];\n const castedBuckets = buckets.buckets as Bucket[];\n const facetBuckets: FacetBucket[] = castedBuckets.map(bucket => ({\n key: `${bucket.key}`,\n count: bucket.doc_count,\n state: 'none',\n }));\n const group: FacetGroup = {\n title,\n key: option,\n buckets: facetBuckets,\n };\n facetGroups.push(group);\n });\n return facetGroups;\n }\n\n private getFacetGroupTemplate(facetGroup: FacetGroup) {\n const { key } = facetGroup;\n const isOpen = this.openFacets[key];\n const collapser = html`\n <span class=\"collapser ${isOpen ? 'open' : ''}\"> ${chevronIcon} </span>\n `;\n\n return html`\n <div class=\"facet-group ${this.collapsableFacets ? 'mobile' : ''}\">\n <h1\n @click=${() => {\n const newOpenFacets = { ...this.openFacets };\n newOpenFacets[key] = !isOpen;\n this.openFacets = newOpenFacets;\n }}\n @keyup=${() => {\n const newOpenFacets = { ...this.openFacets };\n newOpenFacets[key] = !isOpen;\n this.openFacets = newOpenFacets;\n }}\n >\n ${this.collapsableFacets ? collapser : nothing} ${facetGroup.title}\n </h1>\n <div class=\"facet-group-content ${isOpen ? 'open' : ''}\">\n ${this.getFacetTemplate(facetGroup)}\n </div>\n </div>\n `;\n }\n\n private getFacetTemplate(facetGroup: FacetGroup) {\n return html`\n <ul class=\"facet-list\">\n ${repeat(\n facetGroup.buckets,\n bucket => `${facetGroup.key}:${bucket.key}`,\n bucket => {\n const negativeCheckboxId = `${facetGroup.key}:${bucket.key}-negative`;\n // for collections, we need to asynchronously load the collection name\n // so we use the `async-collection-name` widget and for the rest, we have\n // a static value to use\n const bucketTextDisplay =\n facetGroup.key === 'collection'\n ? html`\n <async-collection-name\n .collectionNameCache=${this.collectionNameCache}\n .identifier=${bucket.key}\n placeholder=\"-\"\n ></async-collection-name>\n `\n : html`${bucket.key}`;\n\n return html`\n <li>\n <label class=\"facet-row\">\n <div class=\"facet-checkbox\">\n <input\n type=\"checkbox\"\n .name=${facetGroup.key}\n .value=${bucket.key}\n @click=${(e: Event) => {\n this.facetClicked(e, bucket, false);\n }}\n .checked=${bucket.state === 'selected'}\n class=\"select-facet-checkbox\"\n />\n <input\n type=\"checkbox\"\n id=${negativeCheckboxId}\n .name=${facetGroup.key}\n .value=${bucket.key}\n @click=${(e: Event) => {\n this.facetClicked(e, bucket, true);\n }}\n .checked=${bucket.state === 'hidden'}\n class=\"hide-facet-checkbox\"\n />\n <label for=${negativeCheckboxId} class=\"hide-facet-icon\">\n ${bucket.state === 'hidden' ? eyeClosedIcon : eyeIcon}\n </label>\n </div>\n <div class=\"facet-title\">${bucketTextDisplay}</div>\n <div class=\"facet-count\">${bucket.count}</div>\n </label>\n </li>\n `;\n }\n )}\n </ul>\n `;\n }\n\n private facetClicked(e: Event, bucket: FacetBucket, negative: boolean) {\n const target = e.target as HTMLInputElement;\n const { checked, name, value } = target;\n if (checked) {\n this.facetChecked(name as FacetOption, value, negative);\n } else {\n this.facetUnchecked(name as FacetOption, value);\n }\n }\n\n private facetChecked(key: FacetOption, value: string, negative: boolean) {\n const { selectedFacets } = this;\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = defaultSelectedFacets;\n }\n newFacets[key][value] = negative ? 'hidden' : 'selected';\n this.selectedFacets = newFacets;\n }\n\n private facetUnchecked(key: FacetOption, value: string) {\n const { selectedFacets } = this;\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = defaultSelectedFacets;\n }\n delete newFacets[key][value];\n this.selectedFacets = newFacets;\n }\n\n /**\n * Parse the aggregate key title into the human readable title\n *\n * Example: user_aggs__terms__field:mediatypeSorter__size:6 => Media Type\n *\n * @param key\n * @returns\n */\n private getFacetOptionFromKey(key: string): FacetOption {\n const parts = key.split('__');\n const fieldNamePart = parts[2];\n const fieldName = fieldNamePart.split(':')[1];\n const facetMatch = Object.entries(aggregationToFacetOption).find(([key2]) =>\n fieldName.includes(key2)\n );\n const option = facetMatch?.[1];\n if (!option) throw new Error(`Could not find facet option for key: ${key}`);\n return option;\n }\n\n static get styles() {\n return css`\n #container.loading {\n opacity: 0.5;\n }\n\n .collapser {\n display: inline-block;\n cursor: pointer;\n width: 10px;\n height: 10px;\n }\n\n .collapser svg {\n transition: transform 0.2s ease-in-out;\n }\n\n .collapser.open svg {\n transform: rotate(90deg);\n }\n\n .facet-group {\n margin-bottom: 2rem;\n }\n\n .facet-group h1 {\n margin-bottom: 0.7rem;\n }\n\n .facet-group.mobile h1 {\n cursor: pointer;\n }\n\n .facet-group-content {\n transition: max-height 0.2s ease-in-out;\n }\n\n .facet-group.mobile .facet-group-content {\n max-height: 0;\n overflow: hidden;\n }\n\n .facet-group.mobile .facet-group-content.open {\n max-height: 2000px;\n }\n\n h1 {\n font-size: 1.4rem;\n font-weight: 200;\n border-bottom: 1px solid rgb(232, 232, 232);\n padding-bottom: 3px;\n margin: 0;\n }\n\n ul.facet-list {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul.facet-list li {\n margin-bottom: 0.2rem;\n }\n\n .facet-checkbox {\n margin-right: 0.5rem;\n display: flex;\n align-items: center;\n }\n\n .facet-row {\n display: flex;\n align-items: center;\n font-weight: 500;\n font-size: 1.2rem;\n }\n\n .facet-title {\n flex: 1;\n }\n\n .facet-count {\n margin-left: 0.5rem;\n }\n\n .select-facet-checkbox {\n margin-right: 5px;\n }\n\n .hide-facet-checkbox {\n display: none;\n }\n\n .hide-facet-icon {\n width: 15px;\n height: 15px;\n cursor: pointer;\n }\n `;\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { SearchServiceInterface } from '@internetarchive/search-service';
2
+ export interface CollectionNameCacheInterface {
3
+ collectionNameFor(identifier: string): Promise<string | null>;
4
+ preloadIdentifiers(identifiers: string[]): Promise<void>;
5
+ }
6
+ export declare class CollectionNameCache implements CollectionNameCacheInterface {
7
+ collectionNameFor(identifier: string): Promise<string | null>;
8
+ preloadIdentifiers(identifiers: string[]): Promise<void>;
9
+ private pendingIdentifierQueue;
10
+ private pendingPromises;
11
+ private collectionNameCache;
12
+ private searchService;
13
+ constructor(options: {
14
+ searchService: SearchServiceInterface;
15
+ loadInterval?: number;
16
+ });
17
+ private loadPendingIdentifiers;
18
+ }
@@ -0,0 +1,89 @@
1
+ /* eslint-disable camelcase */
2
+ /* eslint-disable no-continue */
3
+ import { SearchParams, } from '@internetarchive/search-service';
4
+ export class CollectionNameCache {
5
+ constructor(options) {
6
+ var _a;
7
+ this.pendingIdentifierQueue = [];
8
+ this.pendingPromises = {};
9
+ this.collectionNameCache = {};
10
+ this.searchService = options.searchService;
11
+ setInterval(() => {
12
+ this.loadPendingIdentifiers();
13
+ }, (_a = options.loadInterval) !== null && _a !== void 0 ? _a : 250);
14
+ }
15
+ async collectionNameFor(identifier) {
16
+ const lowercaseIdentifier = identifier.toLowerCase();
17
+ const cachedName = this.collectionNameCache[lowercaseIdentifier];
18
+ // we're specifically looking for `undefined`, because `null`
19
+ // means we queried the search service and found nothing so
20
+ // don't query again
21
+ if (cachedName !== undefined)
22
+ return cachedName;
23
+ return new Promise(resolve => {
24
+ var _a;
25
+ this.pendingIdentifierQueue.push(lowercaseIdentifier);
26
+ const currentPromises = (_a = this.pendingPromises[lowercaseIdentifier]) !== null && _a !== void 0 ? _a : [];
27
+ const resultHandler = async (name) => {
28
+ resolve(name);
29
+ };
30
+ currentPromises.push(resultHandler);
31
+ this.pendingPromises[lowercaseIdentifier] = currentPromises;
32
+ });
33
+ }
34
+ async preloadIdentifiers(identifiers) {
35
+ const lowercaseIdentifiers = identifiers.map(identifier => identifier.toLowerCase());
36
+ for (const identifier of lowercaseIdentifiers) {
37
+ if (this.collectionNameCache[identifier])
38
+ continue;
39
+ this.pendingIdentifierQueue.push(identifier);
40
+ }
41
+ await this.loadPendingIdentifiers();
42
+ }
43
+ async loadPendingIdentifiers() {
44
+ var _a, _b, _c;
45
+ const pendingIdentifiers = this.pendingIdentifierQueue.splice(0, 100);
46
+ if (pendingIdentifiers.length === 0)
47
+ return;
48
+ const searchParams = new SearchParams({
49
+ query: `identifier:(${pendingIdentifiers.join(' OR ')})`,
50
+ fields: ['title', 'identifier'],
51
+ rows: pendingIdentifiers.length,
52
+ });
53
+ const results = await this.searchService.search(searchParams);
54
+ const docs = (_b = (_a = results.success) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.docs;
55
+ // first process the identifiers that were received from the search service
56
+ // and remove them from the pendingIdentifierQueue
57
+ if (docs && docs.length > 0) {
58
+ for (const result of docs) {
59
+ const { identifier, title } = result;
60
+ if (!identifier)
61
+ continue;
62
+ const lowercaseIdentifier = identifier.toLowerCase();
63
+ pendingIdentifiers.splice(pendingIdentifiers.indexOf(lowercaseIdentifier), 1);
64
+ const collectionName = (_c = title === null || title === void 0 ? void 0 : title.value) !== null && _c !== void 0 ? _c : null;
65
+ this.collectionNameCache[lowercaseIdentifier] = collectionName;
66
+ const currentPromises = this.pendingPromises[lowercaseIdentifier];
67
+ if (currentPromises) {
68
+ for (const promise of currentPromises) {
69
+ promise(collectionName);
70
+ }
71
+ delete this.pendingPromises[lowercaseIdentifier];
72
+ }
73
+ }
74
+ }
75
+ // if the search service did not return titles for all of the identifiers,
76
+ // we still need to complete the promises and just return `null` for the rest
77
+ for (const identifier of pendingIdentifiers) {
78
+ this.collectionNameCache[identifier] = null;
79
+ const currentPromises = this.pendingPromises[identifier];
80
+ if (currentPromises) {
81
+ for (const promise of currentPromises) {
82
+ promise(null);
83
+ }
84
+ delete this.pendingPromises[identifier];
85
+ }
86
+ }
87
+ }
88
+ }
89
+ //# sourceMappingURL=collection-name-cache.js.map