@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,15 +1,22 @@
1
- import { css, html, LitElement } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ /* eslint-disable import/no-duplicates */
2
+ import { css, html, LitElement, PropertyValues, nothing } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
3
4
  import { repeat } from 'lit/directives/repeat.js';
4
- import { Aggregation } from '@internetarchive/search-service';
5
-
6
- type FacetOption =
7
- | 'subject'
8
- | 'mediatype'
9
- | 'language'
10
- | 'creator'
11
- | 'collection'
12
- | 'year';
5
+ import { Aggregation, Bucket } from '@internetarchive/search-service';
6
+ import '@internetarchive/histogram-date-range';
7
+ import '@internetarchive/feature-feedback';
8
+ import '@internetarchive/collection-name-cache';
9
+ import { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
10
+ import eyeIcon from './assets/img/icons/eye';
11
+ import eyeClosedIcon from './assets/img/icons/eye-closed';
12
+ import chevronIcon from './assets/img/icons/chevron';
13
+ import {
14
+ FacetOption,
15
+ SelectedFacets,
16
+ FacetGroup,
17
+ FacetBucket,
18
+ defaultSelectedFacets,
19
+ } from './models';
13
20
 
14
21
  const facetDisplayOrder: FacetOption[] = [
15
22
  'mediatype',
@@ -38,46 +45,98 @@ const facetTitles: Record<FacetOption, string> = {
38
45
  year: 'Year',
39
46
  };
40
47
 
41
- interface FacetBucket {
42
- // for some facets, we augment the key with a display value
43
- displayText?: string;
44
- key: string;
45
- count: number;
46
- selected: boolean;
47
- }
48
-
49
- interface FacetGroup {
50
- title: string;
51
- key: string;
52
- buckets: FacetBucket[];
53
- }
54
-
55
48
  @customElement('collection-facets')
56
49
  export class CollectionFacets extends LitElement {
57
50
  @property({ type: Object }) aggregations?: Record<string, Aggregation>;
58
51
 
59
- @property({ type: Object }) selectedFacets: Record<string, string[]> = {};
52
+ @property({ type: Object }) fullYearsHistogramAggregation?: Aggregation;
60
53
 
61
- private get hydratedSelectedFacets(): Record<string, string[]> {
62
- const { selectedFacets } = this;
63
- const hydratedSelectedFacets: Record<string, string[]> = {};
64
- Object.entries(selectedFacets).forEach(([key]) => {
65
- const values = hydratedSelectedFacets[key];
66
- const title = facetTitles[key as FacetOption];
67
- hydratedSelectedFacets[title] = values || [];
68
- delete hydratedSelectedFacets[key];
69
- });
70
- return hydratedSelectedFacets;
71
- }
54
+ @property({ type: String }) minSelectedDate?: string;
55
+
56
+ @property({ type: String }) maxSelectedDate?: string;
57
+
58
+ @property({ type: Boolean }) facetsLoading = false;
59
+
60
+ @property({ type: Boolean }) fullYearAggregationLoading = false;
61
+
62
+ @property({ type: Object }) selectedFacets?: SelectedFacets;
63
+
64
+ @property({ type: Boolean }) collapsableFacets = false;
65
+
66
+ @property({ type: Object })
67
+ collectionNameCache?: CollectionNameCacheInterface;
68
+
69
+ @state() openFacets: Record<FacetOption, boolean> = {
70
+ subject: false,
71
+ mediatype: false,
72
+ language: false,
73
+ creator: false,
74
+ collection: false,
75
+ year: false,
76
+ };
72
77
 
73
78
  render() {
74
79
  return html`
75
- <h1>Facets</h1>
80
+ <div id="container" class="${this.facetsLoading ? 'loading' : ''}">
81
+ <div class="facet-group">
82
+ <h1>Year Published <feature-feedback></feature-feedback></h1>
83
+ ${this.histogramTemplate}
84
+ </div>
85
+
86
+ ${this.mergedFacets.map(facetGroup =>
87
+ this.getFacetGroupTemplate(facetGroup)
88
+ )}
89
+ </div>
90
+ `;
91
+ }
92
+
93
+ updated(changed: PropertyValues) {
94
+ if (changed.has('selectedFacets')) {
95
+ this.dispatchFacetsChangedEvent();
96
+ }
97
+ }
98
+
99
+ private dispatchFacetsChangedEvent() {
100
+ const event = new CustomEvent<SelectedFacets>('facetsChanged', {
101
+ detail: this.selectedFacets,
102
+ });
103
+ this.dispatchEvent(event);
104
+ }
76
105
 
77
- ${this.mergedFacets.map(facetGroup => this.getFacetTemplate(facetGroup))}
106
+ private get currentYearsHistogramAggregation(): Aggregation | undefined {
107
+ return this.aggregations?.year_histogram;
108
+ }
109
+
110
+ private get histogramTemplate() {
111
+ const { fullYearsHistogramAggregation } = this;
112
+ return html`
113
+ <histogram-date-range
114
+ .minDate=${fullYearsHistogramAggregation?.first_bucket_key}
115
+ .maxDate=${fullYearsHistogramAggregation?.last_bucket_key}
116
+ .minSelectedDate=${this.minSelectedDate}
117
+ .maxSelectedDate=${this.maxSelectedDate}
118
+ .updateDelay=${100}
119
+ missingDataMessage="..."
120
+ .width=${180}
121
+ .bins=${fullYearsHistogramAggregation?.buckets as number[]}
122
+ @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
123
+ ></histogram-date-range>
78
124
  `;
79
125
  }
80
126
 
127
+ private histogramDateRangeUpdated(
128
+ e: CustomEvent<{
129
+ minDate: string;
130
+ maxDate: string;
131
+ }>
132
+ ) {
133
+ const { minDate, maxDate } = e.detail;
134
+ const event = new CustomEvent('histogramDateRangeUpdated', {
135
+ detail: { minDate, maxDate },
136
+ });
137
+ this.dispatchEvent(event);
138
+ }
139
+
81
140
  /**
82
141
  * Combines the selected facets with the aggregations to create a single list of facets
83
142
  */
@@ -127,8 +186,6 @@ export class CollectionFacets extends LitElement {
127
186
  });
128
187
  facetGroup.buckets = bucketsWithCount.splice(0, 5);
129
188
 
130
- if (facetGroup.buckets.length === 0) return;
131
-
132
189
  facetGroups.push(facetGroup);
133
190
  });
134
191
 
@@ -136,24 +193,34 @@ export class CollectionFacets extends LitElement {
136
193
  }
137
194
 
138
195
  /**
139
- * Converts the raw `selectedFacets` to `FacetGroups`, which are easier to use
196
+ * Converts the selected facets to a `FacetGroup` array,
197
+ * which is easier to work with
140
198
  */
141
199
  private get selectedFacetGroups(): FacetGroup[] {
142
- const selectedFacetGroups: FacetGroup[] = [];
143
- Object.entries(this.selectedFacets).forEach(([key, buckets]) => {
144
- const title = facetTitles[key as FacetOption];
145
- const group = {
146
- title,
147
- key,
148
- buckets: buckets.map(bucket => ({
149
- key: bucket,
150
- count: 0,
151
- selected: true,
152
- })),
153
- };
154
- selectedFacetGroups.push(group);
155
- });
156
- return selectedFacetGroups;
200
+ if (!this.selectedFacets) return [];
201
+
202
+ const facetGroups: FacetGroup[] = Object.entries(this.selectedFacets).map(
203
+ ([key, selectedFacets]) => {
204
+ const option = key as FacetOption;
205
+ const title = facetTitles[option];
206
+
207
+ const buckets: FacetBucket[] = Object.entries(selectedFacets).map(
208
+ ([value, facetState]) => ({
209
+ key: value,
210
+ count: 0,
211
+ state: facetState,
212
+ })
213
+ );
214
+
215
+ return {
216
+ title,
217
+ key: option,
218
+ buckets,
219
+ };
220
+ }
221
+ );
222
+
223
+ return facetGroups;
157
224
  }
158
225
 
159
226
  /**
@@ -165,10 +232,11 @@ export class CollectionFacets extends LitElement {
165
232
  if (key === 'year_histogram') return;
166
233
  const option = this.getFacetOptionFromKey(key);
167
234
  const title = facetTitles[option];
168
- const facetBuckets: FacetBucket[] = buckets.buckets.map(bucket => ({
235
+ const castedBuckets = buckets.buckets as Bucket[];
236
+ const facetBuckets: FacetBucket[] = castedBuckets.map(bucket => ({
169
237
  key: `${bucket.key}`,
170
238
  count: bucket.doc_count,
171
- selected: false,
239
+ state: 'none',
172
240
  }));
173
241
  const group: FacetGroup = {
174
242
  title,
@@ -180,71 +248,134 @@ export class CollectionFacets extends LitElement {
180
248
  return facetGroups;
181
249
  }
182
250
 
251
+ private getFacetGroupTemplate(facetGroup: FacetGroup) {
252
+ const { key } = facetGroup;
253
+ const isOpen = this.openFacets[key];
254
+ const collapser = html`
255
+ <span class="collapser ${isOpen ? 'open' : ''}"> ${chevronIcon} </span>
256
+ `;
257
+
258
+ return html`
259
+ <div class="facet-group ${this.collapsableFacets ? 'mobile' : ''}">
260
+ <h1
261
+ @click=${() => {
262
+ const newOpenFacets = { ...this.openFacets };
263
+ newOpenFacets[key] = !isOpen;
264
+ this.openFacets = newOpenFacets;
265
+ }}
266
+ @keyup=${() => {
267
+ const newOpenFacets = { ...this.openFacets };
268
+ newOpenFacets[key] = !isOpen;
269
+ this.openFacets = newOpenFacets;
270
+ }}
271
+ >
272
+ ${this.collapsableFacets ? collapser : nothing} ${facetGroup.title}
273
+ </h1>
274
+ <div class="facet-group-content ${isOpen ? 'open' : ''}">
275
+ ${this.getFacetTemplate(facetGroup)}
276
+ </div>
277
+ </div>
278
+ `;
279
+ }
280
+
183
281
  private getFacetTemplate(facetGroup: FacetGroup) {
184
282
  return html`
185
- <h2>${facetGroup.title}</h2>
186
- ${repeat(
187
- facetGroup.buckets,
188
- bucket => `${facetGroup.key}:${bucket.key}`,
189
- bucket => html`
190
- <label class="facet-row">
191
- <div class="facet-checkbox">
192
- <input
193
- type="checkbox"
194
- .name=${facetGroup.key}
195
- .value=${bucket.key}
196
- @click=${this.facetToggled}
197
- ?checked=${bucket.selected}
198
- />
199
- </div>
200
- <div class="facet-title">${bucket.key}</div>
201
- <div class="facet-count">${bucket.count}</div>
202
- </label>
203
- `
204
- )}
283
+ <ul class="facet-list">
284
+ ${repeat(
285
+ facetGroup.buckets,
286
+ bucket => `${facetGroup.key}:${bucket.key}`,
287
+ bucket => {
288
+ const negativeCheckboxId = `${facetGroup.key}:${bucket.key}-negative`;
289
+ // for collections, we need to asynchronously load the collection name
290
+ // so we use the `async-collection-name` widget and for the rest, we have
291
+ // a static value to use
292
+ const bucketTextDisplay =
293
+ facetGroup.key === 'collection'
294
+ ? html`
295
+ <async-collection-name
296
+ .collectionNameCache=${this.collectionNameCache}
297
+ .identifier=${bucket.key}
298
+ placeholder="-"
299
+ ></async-collection-name>
300
+ `
301
+ : html`${bucket.key}`;
302
+
303
+ return html`
304
+ <li>
305
+ <label class="facet-row">
306
+ <div class="facet-checkbox">
307
+ <input
308
+ type="checkbox"
309
+ .name=${facetGroup.key}
310
+ .value=${bucket.key}
311
+ @click=${(e: Event) => {
312
+ this.facetClicked(e, bucket, false);
313
+ }}
314
+ .checked=${bucket.state === 'selected'}
315
+ class="select-facet-checkbox"
316
+ />
317
+ <input
318
+ type="checkbox"
319
+ id=${negativeCheckboxId}
320
+ .name=${facetGroup.key}
321
+ .value=${bucket.key}
322
+ @click=${(e: Event) => {
323
+ this.facetClicked(e, bucket, true);
324
+ }}
325
+ .checked=${bucket.state === 'hidden'}
326
+ class="hide-facet-checkbox"
327
+ />
328
+ <label for=${negativeCheckboxId} class="hide-facet-icon">
329
+ ${bucket.state === 'hidden' ? eyeClosedIcon : eyeIcon}
330
+ </label>
331
+ </div>
332
+ <div class="facet-title">${bucketTextDisplay}</div>
333
+ <div class="facet-count">${bucket.count}</div>
334
+ </label>
335
+ </li>
336
+ `;
337
+ }
338
+ )}
339
+ </ul>
205
340
  `;
206
341
  }
207
342
 
208
- private facetChecked(name: string, value: string) {
209
- const { selectedFacets } = this;
210
- const facetClone = { ...selectedFacets };
211
- const currentFacetValues = facetClone[name];
212
- if (currentFacetValues) {
213
- currentFacetValues.push(value);
214
- facetClone[name] = currentFacetValues;
343
+ private facetClicked(e: Event, bucket: FacetBucket, negative: boolean) {
344
+ const target = e.target as HTMLInputElement;
345
+ const { checked, name, value } = target;
346
+ if (checked) {
347
+ this.facetChecked(name as FacetOption, value, negative);
215
348
  } else {
216
- facetClone[name] = [value];
349
+ this.facetUnchecked(name as FacetOption, value);
217
350
  }
218
- this.selectedFacets = facetClone;
219
351
  }
220
352
 
221
- private facetUnchecked(name: string, value: string) {
353
+ private facetChecked(key: FacetOption, value: string, negative: boolean) {
222
354
  const { selectedFacets } = this;
223
- const facetClone = { ...selectedFacets };
224
- let currentFacetValues = selectedFacets[name];
225
- if (currentFacetValues) {
226
- currentFacetValues = currentFacetValues.filter(el => el !== value);
227
- facetClone[name] = currentFacetValues;
228
- if (currentFacetValues.length === 0) {
229
- delete facetClone[name];
230
- }
355
+ let newFacets: SelectedFacets;
356
+ if (selectedFacets) {
357
+ newFacets = {
358
+ ...selectedFacets,
359
+ };
360
+ } else {
361
+ newFacets = defaultSelectedFacets;
231
362
  }
232
- this.selectedFacets = facetClone;
363
+ newFacets[key][value] = negative ? 'hidden' : 'selected';
364
+ this.selectedFacets = newFacets;
233
365
  }
234
366
 
235
- private facetToggled(e: Event) {
236
- const target = e.target as HTMLInputElement;
237
- const { checked, name, value } = target;
238
- if (checked) {
239
- this.facetChecked(name, value);
367
+ private facetUnchecked(key: FacetOption, value: string) {
368
+ const { selectedFacets } = this;
369
+ let newFacets: SelectedFacets;
370
+ if (selectedFacets) {
371
+ newFacets = {
372
+ ...selectedFacets,
373
+ };
240
374
  } else {
241
- this.facetUnchecked(name, value);
375
+ newFacets = defaultSelectedFacets;
242
376
  }
243
-
244
- const event = new CustomEvent<Record<string, string[]>>('facetsChanged', {
245
- detail: this.selectedFacets,
246
- });
247
- this.dispatchEvent(event);
377
+ delete newFacets[key][value];
378
+ this.selectedFacets = newFacets;
248
379
  }
249
380
 
250
381
  /**
@@ -269,8 +400,79 @@ export class CollectionFacets extends LitElement {
269
400
 
270
401
  static get styles() {
271
402
  return css`
403
+ #container.loading {
404
+ opacity: 0.5;
405
+ }
406
+
407
+ .collapser {
408
+ display: inline-block;
409
+ cursor: pointer;
410
+ width: 10px;
411
+ height: 10px;
412
+ }
413
+
414
+ .collapser svg {
415
+ transition: transform 0.2s ease-in-out;
416
+ }
417
+
418
+ .collapser.open svg {
419
+ transform: rotate(90deg);
420
+ }
421
+
422
+ .facet-group {
423
+ margin-bottom: 2rem;
424
+ }
425
+
426
+ .facet-group h1 {
427
+ margin-bottom: 0.7rem;
428
+ }
429
+
430
+ .facet-group.mobile h1 {
431
+ cursor: pointer;
432
+ }
433
+
434
+ .facet-group-content {
435
+ transition: max-height 0.2s ease-in-out;
436
+ }
437
+
438
+ .facet-group.mobile .facet-group-content {
439
+ max-height: 0;
440
+ overflow: hidden;
441
+ }
442
+
443
+ .facet-group.mobile .facet-group-content.open {
444
+ max-height: 2000px;
445
+ }
446
+
447
+ h1 {
448
+ font-size: 1.4rem;
449
+ font-weight: 200;
450
+ border-bottom: 1px solid rgb(232, 232, 232);
451
+ padding-bottom: 3px;
452
+ margin: 0;
453
+ }
454
+
455
+ ul.facet-list {
456
+ list-style: none;
457
+ margin: 0;
458
+ padding: 0;
459
+ }
460
+
461
+ ul.facet-list li {
462
+ margin-bottom: 0.2rem;
463
+ }
464
+
465
+ .facet-checkbox {
466
+ margin-right: 0.5rem;
467
+ display: flex;
468
+ align-items: center;
469
+ }
470
+
272
471
  .facet-row {
273
472
  display: flex;
473
+ align-items: center;
474
+ font-weight: 500;
475
+ font-size: 1.2rem;
274
476
  }
275
477
 
276
478
  .facet-title {
@@ -280,6 +482,20 @@ export class CollectionFacets extends LitElement {
280
482
  .facet-count {
281
483
  margin-left: 0.5rem;
282
484
  }
485
+
486
+ .select-facet-checkbox {
487
+ margin-right: 5px;
488
+ }
489
+
490
+ .hide-facet-checkbox {
491
+ display: none;
492
+ }
493
+
494
+ .hide-facet-icon {
495
+ width: 15px;
496
+ height: 15px;
497
+ cursor: pointer;
498
+ }
283
499
  `;
284
500
  }
285
501
  }
@@ -1,6 +1,7 @@
1
1
  import { css, CSSResultGroup, html, LitElement } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
 
4
+ import { accountIcon } from './assets/img/icons/mediatype/account';
4
5
  import { audioIcon } from './assets/img/icons/mediatype/audio';
5
6
  import { etreeIcon } from './assets/img/icons/mediatype/etree';
6
7
  import { imagesIcon } from './assets/img/icons/mediatype/images';
@@ -19,6 +20,7 @@ export class MediatypeIcon extends LitElement {
19
20
  @property({ type: Boolean }) showText = false;
20
21
 
21
22
  private readonly mediatypeIcons: { [key: string]: any } = {
23
+ account: accountIcon,
22
24
  audio: audioIcon,
23
25
  collection: collectionIcon,
24
26
  data: etreeIcon,
@@ -34,6 +36,7 @@ export class MediatypeIcon extends LitElement {
34
36
  };
35
37
 
36
38
  private readonly mediatypeText: { [key: string]: any } = {
39
+ account: 'Account',
37
40
  audio: 'Audio',
38
41
  collection: 'Collection',
39
42
  data: 'Data',
@@ -74,9 +77,13 @@ export class MediatypeIcon extends LitElement {
74
77
  display: none;
75
78
  }
76
79
 
77
- #icon.show-text svg {
78
- height: 10px;
79
- width: 10px;
80
+ svg {
81
+ height: var(--iconHeight, 10px);
82
+ width: var(--iconWidth, 10px);
83
+ }
84
+
85
+ .fill-color {
86
+ fill: var(--iconFillColor);
80
87
  }
81
88
  `;
82
89
  }