@internetarchive/collection-browser 0.2.14-a1 → 0.2.14-alpha1

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 (40) hide show
  1. package/dist/src/app-root.d.ts +1 -0
  2. package/dist/src/app-root.js +28 -2
  3. package/dist/src/app-root.js.map +1 -1
  4. package/dist/src/assets/img/icons/arrow-left.d.ts +2 -0
  5. package/dist/src/assets/img/icons/arrow-left.js +10 -0
  6. package/dist/src/assets/img/icons/arrow-left.js.map +1 -0
  7. package/dist/src/assets/img/icons/arrow-right.d.ts +2 -0
  8. package/dist/src/assets/img/icons/arrow-right.js +10 -0
  9. package/dist/src/assets/img/icons/arrow-right.js.map +1 -0
  10. package/dist/src/collection-browser.d.ts +2 -0
  11. package/dist/src/collection-browser.js +7 -0
  12. package/dist/src/collection-browser.js.map +1 -1
  13. package/dist/src/collection-facets/more-facets-content.d.ts +50 -0
  14. package/dist/src/collection-facets/more-facets-content.js +354 -0
  15. package/dist/src/collection-facets/more-facets-content.js.map +1 -0
  16. package/dist/src/collection-facets/more-facets-pagination.d.ts +27 -0
  17. package/dist/src/collection-facets/more-facets-pagination.js +193 -0
  18. package/dist/src/collection-facets/more-facets-pagination.js.map +1 -0
  19. package/dist/src/collection-facets.d.ts +19 -2
  20. package/dist/src/collection-facets.js +102 -0
  21. package/dist/src/collection-facets.js.map +1 -1
  22. package/dist/test/collection-facets/more-facets-content.test.d.ts +1 -0
  23. package/dist/test/collection-facets/more-facets-content.test.js +75 -0
  24. package/dist/test/collection-facets/more-facets-content.test.js.map +1 -0
  25. package/dist/test/collection-facets/more-facets-pagination.test.d.ts +1 -0
  26. package/dist/test/collection-facets/more-facets-pagination.test.js +38 -0
  27. package/dist/test/collection-facets/more-facets-pagination.test.js.map +1 -0
  28. package/dist/test/mocks/mock-search-responses.js +13 -0
  29. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  30. package/package.json +3 -1
  31. package/src/app-root.ts +28 -2
  32. package/src/assets/img/icons/arrow-left.ts +10 -0
  33. package/src/assets/img/icons/arrow-right.ts +10 -0
  34. package/src/collection-browser.ts +6 -0
  35. package/src/collection-facets/more-facets-content.ts +371 -0
  36. package/src/collection-facets/more-facets-pagination.ts +201 -0
  37. package/src/collection-facets.ts +117 -1
  38. package/test/collection-facets/more-facets-content.test.ts +113 -0
  39. package/test/collection-facets/more-facets-pagination.test.ts +70 -0
  40. package/test/mocks/mock-search-responses.ts +13 -0
package/src/app-root.ts CHANGED
@@ -4,6 +4,7 @@ import { html, css, LitElement, PropertyValues } from 'lit';
4
4
  import { customElement, query, state } from 'lit/decorators.js';
5
5
  import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
6
6
  import { CollectionNameCache } from '@internetarchive/collection-name-cache';
7
+ import type { ModalManagerInterface } from '@internetarchive/modal-manager';
7
8
  import type { CollectionBrowser } from '../src/collection-browser';
8
9
  import '../src/collection-browser';
9
10
 
@@ -40,6 +41,8 @@ export class AppRoot extends LitElement {
40
41
 
41
42
  @query('collection-browser') private collectionBrowser!: CollectionBrowser;
42
43
 
44
+ @query('modal-manager') private modalManager!: ModalManagerInterface;
45
+
43
46
  private searchPressed(e: Event) {
44
47
  e.preventDefault();
45
48
  this.searchQuery = this.baseQueryField.value;
@@ -171,11 +174,13 @@ export class AppRoot extends LitElement {
171
174
  .collectionNameCache=${this.collectionNameCache}
172
175
  .showHistogramDatePicker=${true}
173
176
  .loggedIn=${this.loggedIn}
177
+ .modalManager=${this.modalManager}
174
178
  @visiblePageChanged=${this.visiblePageChanged}
175
179
  @baseQueryChanged=${this.baseQueryChanged}
176
180
  >
177
181
  </collection-browser>
178
182
  </div>
183
+ <modal-manager></modal-manager>
179
184
  `;
180
185
  }
181
186
 
@@ -257,13 +262,34 @@ export class AppRoot extends LitElement {
257
262
  display: block;
258
263
  }
259
264
 
265
+ /* add the following styles to ensure proper modal visibility */
266
+ body.modal-manager-open {
267
+ overflow: hidden;
268
+ }
269
+ modal-manager {
270
+ display: none;
271
+ }
272
+ modal-manager[mode='open'] {
273
+ display: block;
274
+ }
275
+ modal-manager.more-search-facets {
276
+ --modalWidth: 85rem;
277
+ --modalBorder: 2px solid #194880;
278
+ --modalTitleLineHeight: 4rem;
279
+ --modalTitleFontSize: 1.8rem;
280
+ --modalCornerRadius: 0;
281
+ --modalBottomPadding: 0;
282
+ --modalScrollOffset: 0;
283
+ --modalCornerRadius: 0.5rem;
284
+ }
285
+
260
286
  input,
261
287
  button {
262
288
  font-size: 1.6rem;
263
289
  }
264
290
 
265
291
  collection-browser {
266
- margin-top: 30rem;
292
+ margin-top: 20rem;
267
293
  }
268
294
 
269
295
  #base-query-field {
@@ -274,7 +300,7 @@ export class AppRoot extends LitElement {
274
300
  position: fixed;
275
301
  top: 0;
276
302
  left: 0;
277
- z-index: 10;
303
+ z-index: 1;
278
304
  -webkit-backdrop-filter: blur(10px);
279
305
  backdrop-filter: blur(10px);
280
306
  padding: 0.5rem 1rem;
@@ -0,0 +1,10 @@
1
+ import { svg } from 'lit';
2
+
3
+ export default svg`
4
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
5
+ <path
6
+ d="m20.1116715 50.0035012-.1116715-.1085359 43.1159942-46.61088155c2.401537-2.18938917 4.6902018-3.28408375 6.8659943-3.28408375s4.1642651.63837733 5.9654178 1.91513199c1.8011528 1.27675467 3.1520173 2.97248092 4.0525937 5.08717877l-39.4020173 42.99768924 39.4020173 42.9976892c-.9005764 2.1146979-2.2514409 3.8104241-4.0525937 5.0871788-1.8011527 1.2767547-3.7896253 1.915132-5.9654178 1.915132-2.1013449 0-4.3900096-1.0573489-6.8659943-3.1720468l-43.1159942-46.7194174z"
7
+ />
8
+ <title>Go left icon</title>
9
+ </svg>
10
+ `;
@@ -0,0 +1,10 @@
1
+ import { svg } from 'lit';
2
+
3
+ export default svg`
4
+ <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
5
+ <path
6
+ d="m79.8883285 50.0035012.1116715-.1085359-43.1159942-46.61088155c-2.401537-2.18938917-4.6902018-3.28408375-6.8659943-3.28408375s-4.1642651.63837733-5.9654178 1.91513199c-1.8011528 1.27675467-3.1520173 2.97248092-4.0525937 5.08717877l39.4020173 42.99768924-39.4020173 42.9976892c.9005764 2.1146979 2.2514409 3.8104241 4.0525937 5.0871788 1.8011527 1.2767547 3.7896253 1.915132 5.9654178 1.915132 2.1013449 0 4.3900096-1.0573489 6.8659943-3.1720468l43.1159942-46.7194174z"
7
+ />
8
+ <title>Go right icon</title>
9
+ </svg>
10
+ `;
@@ -27,6 +27,7 @@ import type {
27
27
  } from '@internetarchive/shared-resize-observer';
28
28
  import '@internetarchive/infinite-scroller';
29
29
  import type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
30
+ import type { ModalManagerInterface } from '@internetarchive/modal-manager';
30
31
  import './tiles/tile-dispatcher';
31
32
  import './tiles/collection-browser-loading-tile';
32
33
  import './sort-filter-bar/sort-filter-bar';
@@ -115,6 +116,8 @@ export class CollectionBrowser
115
116
 
116
117
  @property({ type: Boolean }) loggedIn = false;
117
118
 
119
+ @property({ type: Object }) modalManager?: ModalManagerInterface = undefined;
120
+
118
121
  /**
119
122
  * If item management UI active
120
123
  */
@@ -425,6 +428,7 @@ export class CollectionBrowser
425
428
  <collection-facets
426
429
  @facetsChanged=${this.facetsChanged}
427
430
  @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
431
+ .searchService=${this.searchService}
428
432
  .aggregations=${this.aggregations}
429
433
  .fullYearsHistogramAggregation=${this.fullYearsHistogramAggregation}
430
434
  .minSelectedDate=${this.minSelectedDate}
@@ -433,6 +437,8 @@ export class CollectionBrowser
433
437
  .collectionNameCache=${this.collectionNameCache}
434
438
  .languageCodeHandler=${this.languageCodeHandler}
435
439
  .showHistogramDatePicker=${this.showHistogramDatePicker}
440
+ .fullQuery=${this.fullQuery}
441
+ .modalManager=${this.modalManager}
436
442
  ?collapsableFacets=${this.mobileView}
437
443
  ?facetsLoading=${this.facetDataLoading}
438
444
  ?fullYearAggregationLoading=${this.fullYearAggregationLoading}
@@ -0,0 +1,371 @@
1
+ /* eslint-disable dot-notation */
2
+ import {
3
+ css,
4
+ CSSResultGroup,
5
+ html,
6
+ LitElement,
7
+ nothing,
8
+ PropertyValues,
9
+ } from 'lit';
10
+ import { customElement, property, state } from 'lit/decorators.js';
11
+ import type {
12
+ Aggregation,
13
+ Bucket,
14
+ SearchServiceInterface,
15
+ SearchParams,
16
+ } from '@internetarchive/search-service';
17
+ import type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';
18
+ import { SelectedFacets, defaultSelectedFacets } from '../models';
19
+ import type { LanguageCodeHandlerInterface } from '../language-code-handler/language-code-handler';
20
+ import '@internetarchive/ia-activity-indicator/ia-activity-indicator';
21
+ import './more-facets-pagination';
22
+
23
+ @customElement('more-facets-content')
24
+ export class MoreFacetsContent extends LitElement {
25
+ @property({ type: String }) facetKey?: string;
26
+
27
+ @property({ type: String }) facetAggregationKey?: string;
28
+
29
+ @property({ type: String }) fullQuery?: string;
30
+
31
+ @property({ type: Object }) modalManager?: any;
32
+
33
+ @property({ type: Object }) searchService?: SearchServiceInterface;
34
+
35
+ @property({ type: Object })
36
+ collectionNameCache?: CollectionNameCacheInterface;
37
+
38
+ @property({ type: Object })
39
+ languageCodeHandler?: LanguageCodeHandlerInterface;
40
+
41
+ @property({ type: Object }) selectedFacets?: SelectedFacets;
42
+
43
+ @state() aggregations?: Record<string, Aggregation>;
44
+
45
+ @state() castedBuckets?: Bucket[] = [];
46
+
47
+ @state() pageNumber = 1;
48
+
49
+ /**
50
+ * Facets are loading on popup
51
+ */
52
+ @state() facetsLoading = true;
53
+
54
+ @state() paginationSize = 0;
55
+
56
+ private facetsPerPage = 60; // Q. how many items we want to have on popup view
57
+
58
+ updated(changed: PropertyValues) {
59
+ if (changed.has('facetKey')) {
60
+ this.facetsLoading = true;
61
+ this.pageNumber = 1;
62
+
63
+ this.updateSpecificFacets();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Get specific facets data from search-service API based of currently query params
69
+ * - this.aggregations - hold result of search service and being used for further processing.
70
+ */
71
+ async updateSpecificFacets(): Promise<void> {
72
+ const aggregations = {
73
+ advancedParams: [
74
+ {
75
+ field: this.facetAggregationKey as string,
76
+ size: 1000000, // todo - do we want to have all the records at once?
77
+ },
78
+ ],
79
+ };
80
+
81
+ const params: SearchParams = {
82
+ query: this.fullQuery as string,
83
+ fields: ['identifier'],
84
+ aggregations,
85
+ rows: 1, // todo - do we want server-side pagination with offset/page/limit flag?
86
+ };
87
+
88
+ const results = await this.searchService?.search(params);
89
+ this.aggregations = results?.success?.response.aggregations as any;
90
+
91
+ // filter facets data to be rendered in modal-manager
92
+ await this.filterFacets();
93
+ this.facetsLoading = false;
94
+ }
95
+
96
+ /**
97
+ * Filter facets data stored in this.aggregations, eg.
98
+ * - we don't want to entertain year_histogram data since we using new date-picker
99
+ * - name of collections needs to be load inside cache using this.collectionNameCache
100
+ *
101
+ * this.castedBuckets - hold filtered facets data which will be render in modal
102
+ */
103
+ async filterFacets(): Promise<void> {
104
+ Object.entries(this.aggregations ?? []).forEach(([key, buckets]) => {
105
+ if (key === 'year_histogram') return;
106
+
107
+ this.castedBuckets = buckets['buckets'] as Bucket[];
108
+
109
+ if (this.facetKey === 'collection') {
110
+ // for collections, we need to asynchronously load the collection name
111
+ // so we use the `async-collection-name` widget and for the rest, we have a static value to use
112
+ const collectionIds = this.castedBuckets?.map(option => option.key);
113
+ const collectionIdsArray = Array.from(
114
+ new Set(collectionIds)
115
+ ) as string[];
116
+ this.collectionNameCache?.preloadIdentifiers(collectionIdsArray);
117
+ }
118
+ });
119
+
120
+ const { length } = Object.keys(this.castedBuckets as []);
121
+ this.paginationSize = Math.ceil(length / this.facetsPerPage);
122
+ }
123
+
124
+ private pageNumberClicked(e: CustomEvent<{ page: string }>) {
125
+ const page = e?.detail?.page;
126
+ if (page) {
127
+ this.pageNumber = Number(page);
128
+ }
129
+ }
130
+
131
+ private get getMoreFacetsTemplate() {
132
+ this.facetsLoading = false;
133
+
134
+ // render only items which will be visible as per this.facetsPerPage
135
+ const currentPageContent = this.castedBuckets?.slice(
136
+ (this.pageNumber - 1) * this.facetsPerPage,
137
+ this.pageNumber * this.facetsPerPage
138
+ );
139
+
140
+ return html`<ul class="facet-list">
141
+ ${currentPageContent?.map(facet => {
142
+ let displayText = facet.key;
143
+
144
+ if (this.facetKey === 'language') {
145
+ displayText =
146
+ this.languageCodeHandler?.getLanguageNameFromCodeString(
147
+ displayText as string
148
+ ) ?? displayText;
149
+ }
150
+
151
+ return html`
152
+ <li class="facet-row">
153
+ <label class="facet-info-display" title=${facet.key}>
154
+ <input
155
+ type="checkbox"
156
+ class="selected-facets"
157
+ .value="${facet.key}"
158
+ data-facet="${this.facetKey}"
159
+ @click=${(e: Event) => {
160
+ this.facetClicked(e);
161
+ }}
162
+ />
163
+ <div class="facet-title">
164
+ ${this.facetKey !== 'collection'
165
+ ? html`${displayText}`
166
+ : html`<async-collection-name
167
+ .collectionNameCache=${this.collectionNameCache}
168
+ .identifier=${displayText}
169
+ placeholder="-"
170
+ ></async-collection-name>`}
171
+ </div>
172
+ <div class="facet-count">${facet.doc_count}</div>
173
+ </label>
174
+ </li>
175
+ `;
176
+ })}
177
+ </ul>`;
178
+ }
179
+
180
+ private facetClicked(e: Event) {
181
+ const { selectedFacets } = this;
182
+
183
+ const target = e.target as HTMLInputElement;
184
+ const { checked, value } = target;
185
+
186
+ let newFacets: SelectedFacets;
187
+ if (selectedFacets) {
188
+ newFacets = {
189
+ ...selectedFacets,
190
+ };
191
+ } else {
192
+ newFacets = defaultSelectedFacets;
193
+ }
194
+
195
+ if (checked) {
196
+ newFacets[this.facetKey as keyof typeof newFacets][value] = 'selected';
197
+ } else {
198
+ delete newFacets[this.facetKey as keyof typeof newFacets][value];
199
+ }
200
+
201
+ this.selectedFacets = newFacets;
202
+ }
203
+
204
+ private get loaderTemplate() {
205
+ return this.facetsLoading
206
+ ? html`<div class="facets-loader">
207
+ <ia-activity-indicator .mode="processing"></ia-activity-indicator>
208
+ </div>`
209
+ : '';
210
+ }
211
+
212
+ // render pagination if more then 1 page
213
+ private get facetsPaginationTemplate() {
214
+ return this.paginationSize > 1
215
+ ? html`<more-facets-pagination
216
+ .size=${this.paginationSize}
217
+ @pageNumberClicked=${this.pageNumberClicked}
218
+ ></more-facets-pagination>`
219
+ : nothing;
220
+ }
221
+
222
+ private get facetsContentTemplate() {
223
+ return html`
224
+ <div class="facets-content">${this.getMoreFacetsTemplate}</div>
225
+ ${this.paginationSize > 0
226
+ ? html`${this.facetsPaginationTemplate}
227
+ <div class="footer">
228
+ <button
229
+ class="btn btn-cancel"
230
+ type="button"
231
+ @click=${this.cancelClick}
232
+ >
233
+ Cancel
234
+ </button>
235
+ <button
236
+ class="btn btn-submit"
237
+ type="button"
238
+ @click=${this.applySearchFacetsClicked}
239
+ >
240
+ Apply filters
241
+ </button>
242
+ </div>`
243
+ : html`No result found. please try again later.`}
244
+ `;
245
+ }
246
+
247
+ render() {
248
+ return html`
249
+ <div id="more-facets-page">
250
+ <form>
251
+ ${this.facetsLoading
252
+ ? this.loaderTemplate
253
+ : this.facetsContentTemplate}
254
+ </form>
255
+ </div>
256
+ `;
257
+ }
258
+
259
+ private applySearchFacetsClicked() {
260
+ const event = new CustomEvent<SelectedFacets>('facetsChanged', {
261
+ detail: this.selectedFacets,
262
+ bubbles: true,
263
+ composed: true,
264
+ });
265
+ this.dispatchEvent(event);
266
+ this.modalManager?.closeModal();
267
+ }
268
+
269
+ private cancelClick() {
270
+ this.modalManager?.closeModal();
271
+ }
272
+
273
+ static get styles(): CSSResultGroup {
274
+ return css`
275
+ #more-facets-page {
276
+ margin-bottom: 2rem;
277
+ }
278
+ .modal-content {
279
+ background-color: #fefefe;
280
+ margin: auto;
281
+ padding: 0;
282
+ border: 1px solid #888;
283
+ width: 80%;
284
+ }
285
+
286
+ .facets-content {
287
+ -webkit-column-width: 25rem;
288
+ -moz-column-width: 25rem;
289
+ column-width: 25rem;
290
+ font-size: 1.2rem;
291
+ padding: 0 10px;
292
+ }
293
+
294
+ ul.facet-list {
295
+ list-style: none;
296
+ margin: 0;
297
+ padding: 0;
298
+ }
299
+ ul.facet-list li {
300
+ margin-bottom: 2px;
301
+ }
302
+
303
+ .facet-row {
304
+ text-align: left;
305
+ }
306
+ .facet-row {
307
+ align-items: start;
308
+ font-weight: 500;
309
+ font-size: 1.2rem;
310
+ }
311
+ .facet-row input {
312
+ margin: 1px 5px 1px 0;
313
+ }
314
+ .facet-info-display {
315
+ display: flex;
316
+ flex: 1;
317
+ cursor: pointer;
318
+ }
319
+ .facet-title {
320
+ flex: 1;
321
+ }
322
+ .facet-count {
323
+ margin-left: 5px;
324
+ }
325
+
326
+ .page-number {
327
+ background: none;
328
+ border: 0;
329
+ cursor: pointer;
330
+ border-radius: 100%;
331
+ width: 25px;
332
+ height: 25px;
333
+ margin: 10px;
334
+ font-size: 1.4rem;
335
+ vertical-align: middle;
336
+ }
337
+ .current-page {
338
+ background: black;
339
+ color: white;
340
+ }
341
+ .facets-loader {
342
+ text-align: center;
343
+ margin-bottom: 2rem;
344
+ height: 7rem;
345
+ width: 7rem;
346
+ display: inline-block;
347
+ }
348
+
349
+ .btn {
350
+ border: none;
351
+ padding: 10px;
352
+ margin-bottom: 10px;
353
+ width: auto;
354
+ border-radius: 0.4rem;
355
+ cursor: pointer;
356
+ }
357
+ .btn-cancel {
358
+ background-color: #000;
359
+ color: white;
360
+ }
361
+ .btn-submit {
362
+ background-color: #194880;
363
+ color: white;
364
+ }
365
+
366
+ .footer {
367
+ margin-top: 10px;
368
+ }
369
+ `;
370
+ }
371
+ }
@@ -0,0 +1,201 @@
1
+ import { css, CSSResultGroup, html, LitElement, nothing } from 'lit';
2
+ import { customElement, property, state } from 'lit/decorators.js';
3
+ import arrowLeftIcon from '../assets/img/icons/arrow-left';
4
+ import arrowRightIcon from '../assets/img/icons/arrow-right';
5
+
6
+ @customElement('more-facets-pagination')
7
+ export class MoreFacetsPagination extends LitElement {
8
+ /**
9
+ * Total number of pages
10
+ */
11
+ @property({ type: Number }) size?: any | undefined;
12
+
13
+ /**
14
+ * Number of pages can be moved in back/forward
15
+ */
16
+ @property({ type: Number }) step = 2;
17
+
18
+ @state() pages?: number[] = [];
19
+
20
+ @state() currentPage = 1;
21
+
22
+ firstUpdated() {
23
+ this.observePageCount();
24
+ }
25
+
26
+ observePageCount() {
27
+ this.pages = [];
28
+ let startPage = this.currentPage - this.step;
29
+ let endPage = this.currentPage + this.step;
30
+
31
+ if (startPage <= 0) {
32
+ endPage += -startPage + 1;
33
+ startPage = 1;
34
+ }
35
+
36
+ if (endPage >= this.size) {
37
+ startPage = Math.max(startPage - (endPage - this.size), 1);
38
+ endPage = this.size;
39
+ }
40
+
41
+ // create first page node
42
+ this.createFirstNode(startPage);
43
+
44
+ // create middle pages node
45
+ this.createMiddelNode(startPage, endPage);
46
+
47
+ // create last page node
48
+ this.createLastNode(endPage);
49
+ }
50
+
51
+ private createFirstNode(startPage: number) {
52
+ if (startPage > 1) {
53
+ this.pages?.push(1);
54
+ this.pages?.push(0); // let's asssume 0 is for ellipsis template
55
+ }
56
+ }
57
+
58
+ private createMiddelNode(startPage: number, endPage: number) {
59
+ for (let page = startPage; page <= endPage; page += 1) {
60
+ this.pages?.push(page);
61
+ }
62
+ }
63
+
64
+ private createLastNode(endPage: number) {
65
+ if (endPage < this.size) {
66
+ this.pages?.push(0); // let's asssume 0 is for ellipsis template
67
+ this.pages?.push(this.size);
68
+ }
69
+ }
70
+
71
+ private get getEllipsisTemplate() {
72
+ return html`<i>...</i>`;
73
+ }
74
+
75
+ private emitPageClick() {
76
+ this.dispatchEvent(
77
+ new CustomEvent('pageNumberClicked', {
78
+ detail: { page: this.currentPage },
79
+ })
80
+ );
81
+ }
82
+
83
+ private onRewind() {
84
+ this.currentPage -= 1;
85
+ if (this.currentPage < 1) {
86
+ this.currentPage = 1;
87
+ }
88
+ this.observePageCount();
89
+ this.emitPageClick();
90
+ }
91
+
92
+ private onForward() {
93
+ this.currentPage += 1;
94
+ if (this.currentPage > this.size) {
95
+ this.currentPage = this.size;
96
+ }
97
+ this.observePageCount();
98
+ this.emitPageClick();
99
+ }
100
+
101
+ private onChange(page: number) {
102
+ this.currentPage = page;
103
+ this.observePageCount();
104
+ this.emitPageClick();
105
+ }
106
+
107
+ private getPageTemplate(page: number) {
108
+ return html`
109
+ <button
110
+ @click="${() => this.onChange(page)}"
111
+ class="${this.currentPage === page ? 'current' : nothing}"
112
+ >
113
+ ${page}
114
+ </button>
115
+ `;
116
+ }
117
+
118
+ private get getPagesTemplate() {
119
+ return html`
120
+ ${this.pages?.map(
121
+ page =>
122
+ html`${page !== 0
123
+ ? this.getPageTemplate(page)
124
+ : this.getEllipsisTemplate}`
125
+ )}
126
+ `;
127
+ }
128
+
129
+ render() {
130
+ return html`
131
+ <div class="facets-pagination">
132
+ <button class="arrow-icon" @click=${this.onRewind}>
133
+ <span class="sr-only">Rewind pagination:</span>
134
+ ${arrowLeftIcon}
135
+ </button>
136
+ <div class="page-numbers">${this.getPagesTemplate}</div>
137
+ <button class="arrow-icon" @click=${this.onForward}>
138
+ <span class="sr-only">Forward pagination:</span>
139
+ ${arrowRightIcon}
140
+ </button>
141
+ </div>
142
+ `;
143
+ }
144
+
145
+ static get styles(): CSSResultGroup {
146
+ return css`
147
+ .facets-pagination {
148
+ user-select: none;
149
+ margin-top: 10px;
150
+ background-color: rgb(239, 239, 239);
151
+ text-align: center;
152
+ font-size: 3.2rem;
153
+ }
154
+ .facets-pagination button {
155
+ border: none;
156
+ background: none;
157
+ }
158
+ .facets-pagination .arrow-icon {
159
+ width: 2.5rem;
160
+ vertical-align: initial;
161
+ }
162
+
163
+ .facets-pagination button,
164
+ .facets-pagination i {
165
+ background: none;
166
+ border: 0;
167
+ cursor: pointer;
168
+ border-radius: 100%;
169
+ margin: 10px;
170
+ padding: 5px;
171
+ font-size: 1.4rem;
172
+ vertical-align: middle;
173
+ display: inline-block;
174
+ min-width: 2.5rem;
175
+ }
176
+ .facets-pagination i {
177
+ cursor: auto;
178
+ display: inline;
179
+ }
180
+ .facets-pagination button.current {
181
+ background: black;
182
+ color: white;
183
+ }
184
+
185
+ .page-numbers {
186
+ display: inline-block;
187
+ }
188
+
189
+ .sr-only {
190
+ position: absolute;
191
+ width: 1px;
192
+ height: 1px;
193
+ padding: 0;
194
+ margin: -1px;
195
+ overflow: hidden;
196
+ clip: rect(0, 0, 0, 0);
197
+ border: 0;
198
+ }
199
+ `;
200
+ }
201
+ }