@internetarchive/collection-browser 0.2.15-0 → 0.2.16-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.
- package/dist/src/app-root.d.ts +1 -0
- package/dist/src/app-root.js +29 -2
- package/dist/src/app-root.js.map +1 -1
- package/dist/src/assets/img/icons/arrow-left.d.ts +2 -0
- package/dist/src/assets/img/icons/arrow-left.js +10 -0
- package/dist/src/assets/img/icons/arrow-left.js.map +1 -0
- package/dist/src/assets/img/icons/arrow-right.d.ts +2 -0
- package/dist/src/assets/img/icons/arrow-right.js +10 -0
- package/dist/src/assets/img/icons/arrow-right.js.map +1 -0
- package/dist/src/collection-browser.d.ts +2 -0
- package/dist/src/collection-browser.js +9 -13
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/collection-facets/more-facets-content.d.ts +56 -0
- package/dist/src/collection-facets/more-facets-content.js +374 -0
- package/dist/src/collection-facets/more-facets-content.js.map +1 -0
- package/dist/src/collection-facets/more-facets-pagination.d.ts +27 -0
- package/dist/src/collection-facets/more-facets-pagination.js +193 -0
- package/dist/src/collection-facets/more-facets-pagination.js.map +1 -0
- package/dist/src/collection-facets.d.ts +19 -2
- package/dist/src/collection-facets.js +102 -0
- package/dist/src/collection-facets.js.map +1 -1
- package/dist/src/models.d.ts +3 -2
- package/dist/src/models.js +8 -4
- package/dist/src/models.js.map +1 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +23 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.js +96 -12
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
- package/dist/test/collection-browser.test.js +35 -0
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/collection-facets/more-facets-content.test.d.ts +1 -0
- package/dist/test/collection-facets/more-facets-content.test.js +75 -0
- package/dist/test/collection-facets/more-facets-content.test.js.map +1 -0
- package/dist/test/collection-facets/more-facets-pagination.test.d.ts +1 -0
- package/dist/test/collection-facets/more-facets-pagination.test.js +38 -0
- package/dist/test/collection-facets/more-facets-pagination.test.js.map +1 -0
- package/dist/test/mocks/mock-search-responses.js +13 -0
- package/dist/test/mocks/mock-search-responses.js.map +1 -1
- package/dist/test/sort-filter-bar/sort-filter-bar.test.d.ts +1 -0
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js +122 -0
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -0
- package/package.json +3 -1
- package/src/app-root.ts +29 -2
- package/src/assets/img/icons/arrow-left.ts +10 -0
- package/src/assets/img/icons/arrow-right.ts +10 -0
- package/src/collection-browser.ts +8 -13
- package/src/collection-facets/more-facets-content.ts +393 -0
- package/src/collection-facets/more-facets-pagination.ts +201 -0
- package/src/collection-facets.ts +117 -1
- package/src/models.ts +9 -4
- package/src/sort-filter-bar/sort-filter-bar.ts +98 -12
- package/test/collection-browser.test.ts +43 -0
- package/test/collection-facets/more-facets-content.test.ts +113 -0
- package/test/collection-facets/more-facets-pagination.test.ts +70 -0
- package/test/mocks/mock-search-responses.ts +13 -0
- package/test/sort-filter-bar/sort-filter-bar.test.ts +187 -0
|
@@ -0,0 +1,393 @@
|
|
|
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 type { ModalManagerInterface } from '@internetarchive/modal-manager';
|
|
19
|
+
import { SelectedFacets, defaultSelectedFacets } from '../models';
|
|
20
|
+
import type { LanguageCodeHandlerInterface } from '../language-code-handler/language-code-handler';
|
|
21
|
+
import '@internetarchive/ia-activity-indicator/ia-activity-indicator';
|
|
22
|
+
import './more-facets-pagination';
|
|
23
|
+
|
|
24
|
+
@customElement('more-facets-content')
|
|
25
|
+
export class MoreFacetsContent extends LitElement {
|
|
26
|
+
@property({ type: String }) facetKey?: string;
|
|
27
|
+
|
|
28
|
+
@property({ type: String }) facetAggregationKey?: string;
|
|
29
|
+
|
|
30
|
+
@property({ type: String }) fullQuery?: string;
|
|
31
|
+
|
|
32
|
+
@property({ type: Object }) modalManager?: ModalManagerInterface;
|
|
33
|
+
|
|
34
|
+
@property({ type: Object }) searchService?: SearchServiceInterface;
|
|
35
|
+
|
|
36
|
+
@property({ type: Object })
|
|
37
|
+
collectionNameCache?: CollectionNameCacheInterface;
|
|
38
|
+
|
|
39
|
+
@property({ type: Object })
|
|
40
|
+
languageCodeHandler?: LanguageCodeHandlerInterface;
|
|
41
|
+
|
|
42
|
+
@property({ type: Object }) selectedFacets?: SelectedFacets;
|
|
43
|
+
|
|
44
|
+
@state() aggregations?: Record<string, Aggregation>;
|
|
45
|
+
|
|
46
|
+
@state() castedBuckets?: Bucket[] = [];
|
|
47
|
+
|
|
48
|
+
@state() pageNumber = 1;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Facets are loading on popup
|
|
52
|
+
*/
|
|
53
|
+
@state() facetsLoading = true;
|
|
54
|
+
|
|
55
|
+
@state() paginationSize = 0;
|
|
56
|
+
|
|
57
|
+
private facetsPerPage = 60; // TODO: Q. how many items we want to have on popup view
|
|
58
|
+
|
|
59
|
+
updated(changed: PropertyValues) {
|
|
60
|
+
if (changed.has('facetKey')) {
|
|
61
|
+
this.facetsLoading = true;
|
|
62
|
+
this.pageNumber = 1;
|
|
63
|
+
|
|
64
|
+
this.updateSpecificFacets();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
firstUpdated() {
|
|
69
|
+
this.setupEscapeListeners();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Close more facets modal on Escape click
|
|
74
|
+
*/
|
|
75
|
+
private setupEscapeListeners() {
|
|
76
|
+
if (this.modalManager) {
|
|
77
|
+
document.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
78
|
+
if (e.key === 'Escape') {
|
|
79
|
+
this.modalManager?.closeModal();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
document.removeEventListener('keydown', () => {});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get specific facets data from search-service API based of currently query params
|
|
89
|
+
* - this.aggregations - hold result of search service and being used for further processing.
|
|
90
|
+
*/
|
|
91
|
+
async updateSpecificFacets(): Promise<void> {
|
|
92
|
+
const aggregations = {
|
|
93
|
+
advancedParams: [
|
|
94
|
+
{
|
|
95
|
+
field: this.facetAggregationKey as string,
|
|
96
|
+
size: 1000000, // todo - do we want to have all the records at once?
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const params: SearchParams = {
|
|
102
|
+
query: this.fullQuery as string,
|
|
103
|
+
fields: ['identifier'],
|
|
104
|
+
aggregations,
|
|
105
|
+
rows: 1, // todo - do we want server-side pagination with offset/page/limit flag?
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const results = await this.searchService?.search(params);
|
|
109
|
+
this.aggregations = results?.success?.response.aggregations as any;
|
|
110
|
+
|
|
111
|
+
// filter facets data to be rendered in modal-manager
|
|
112
|
+
await this.filterFacets();
|
|
113
|
+
this.facetsLoading = false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Filter facets data stored in this.aggregations, eg.
|
|
118
|
+
* - we don't want to entertain year_histogram data since we using new date-picker
|
|
119
|
+
* - name of collections needs to be load inside cache using this.collectionNameCache
|
|
120
|
+
*
|
|
121
|
+
* this.castedBuckets - hold filtered facets data which will be render in modal
|
|
122
|
+
*/
|
|
123
|
+
async filterFacets(): Promise<void> {
|
|
124
|
+
Object.entries(this.aggregations ?? []).forEach(([key, buckets]) => {
|
|
125
|
+
if (key === 'year_histogram') return;
|
|
126
|
+
|
|
127
|
+
this.castedBuckets = buckets['buckets'] as Bucket[];
|
|
128
|
+
|
|
129
|
+
if (this.facetKey === 'collection') {
|
|
130
|
+
// for collections, we need to asynchronously load the collection name
|
|
131
|
+
// so we use the `async-collection-name` widget and for the rest, we have a static value to use
|
|
132
|
+
const collectionIds = this.castedBuckets?.map(option => option.key);
|
|
133
|
+
const collectionIdsArray = Array.from(
|
|
134
|
+
new Set(collectionIds)
|
|
135
|
+
) as string[];
|
|
136
|
+
this.collectionNameCache?.preloadIdentifiers(collectionIdsArray);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const { length } = Object.keys(this.castedBuckets as []);
|
|
141
|
+
this.paginationSize = Math.ceil(length / this.facetsPerPage);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private pageNumberClicked(e: CustomEvent<{ page: string }>) {
|
|
145
|
+
const page = e?.detail?.page;
|
|
146
|
+
if (page) {
|
|
147
|
+
this.pageNumber = Number(page);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private get getMoreFacetsTemplate() {
|
|
152
|
+
this.facetsLoading = false;
|
|
153
|
+
|
|
154
|
+
// render only items which will be visible as per this.facetsPerPage
|
|
155
|
+
const currentPageContent = this.castedBuckets?.slice(
|
|
156
|
+
(this.pageNumber - 1) * this.facetsPerPage,
|
|
157
|
+
this.pageNumber * this.facetsPerPage
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return html`<ul class="facet-list">
|
|
161
|
+
${currentPageContent?.map(facet => {
|
|
162
|
+
let displayText = facet.key;
|
|
163
|
+
|
|
164
|
+
if (this.facetKey === 'language') {
|
|
165
|
+
displayText =
|
|
166
|
+
this.languageCodeHandler?.getLanguageNameFromCodeString(
|
|
167
|
+
displayText as string
|
|
168
|
+
) ?? displayText;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return html`
|
|
172
|
+
<li class="facet-row">
|
|
173
|
+
<div class="facet-checkbox">
|
|
174
|
+
<input
|
|
175
|
+
type="checkbox"
|
|
176
|
+
class="selected-facets"
|
|
177
|
+
id="${facet.key}"
|
|
178
|
+
data-facet="${this.facetKey}"
|
|
179
|
+
.value="${facet.key}"
|
|
180
|
+
@click=${(e: Event) => {
|
|
181
|
+
this.facetClicked(e);
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
<label
|
|
186
|
+
class="facet-info-display"
|
|
187
|
+
for="${facet.key}"
|
|
188
|
+
title=${facet.key}
|
|
189
|
+
>
|
|
190
|
+
<div class="facet-title">
|
|
191
|
+
${this.facetKey !== 'collection'
|
|
192
|
+
? html`${displayText}`
|
|
193
|
+
: html`<async-collection-name
|
|
194
|
+
.collectionNameCache=${this.collectionNameCache}
|
|
195
|
+
.identifier=${displayText}
|
|
196
|
+
placeholder="-"
|
|
197
|
+
></async-collection-name>`}
|
|
198
|
+
</div>
|
|
199
|
+
<div class="facet-count">${facet.doc_count}</div>
|
|
200
|
+
</label>
|
|
201
|
+
</li>
|
|
202
|
+
`;
|
|
203
|
+
})}
|
|
204
|
+
</ul>`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private facetClicked(e: Event) {
|
|
208
|
+
const { selectedFacets } = this;
|
|
209
|
+
|
|
210
|
+
const target = e.target as HTMLInputElement;
|
|
211
|
+
const { checked, value } = target;
|
|
212
|
+
|
|
213
|
+
let newFacets: SelectedFacets;
|
|
214
|
+
if (selectedFacets) {
|
|
215
|
+
newFacets = {
|
|
216
|
+
...selectedFacets,
|
|
217
|
+
};
|
|
218
|
+
} else {
|
|
219
|
+
newFacets = defaultSelectedFacets;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (checked) {
|
|
223
|
+
newFacets[this.facetKey as keyof typeof newFacets][value] = 'selected';
|
|
224
|
+
} else {
|
|
225
|
+
delete newFacets[this.facetKey as keyof typeof newFacets][value];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.selectedFacets = newFacets;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private get loaderTemplate() {
|
|
232
|
+
return this.facetsLoading
|
|
233
|
+
? html`<div class="facets-loader">
|
|
234
|
+
<ia-activity-indicator .mode="processing"></ia-activity-indicator>
|
|
235
|
+
</div>`
|
|
236
|
+
: nothing;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// render pagination if more then 1 page
|
|
240
|
+
private get facetsPaginationTemplate() {
|
|
241
|
+
return this.paginationSize > 1
|
|
242
|
+
? html`<more-facets-pagination
|
|
243
|
+
.size=${this.paginationSize}
|
|
244
|
+
@pageNumberClicked=${this.pageNumberClicked}
|
|
245
|
+
></more-facets-pagination>`
|
|
246
|
+
: nothing;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private get facetsContentTemplate() {
|
|
250
|
+
return html`
|
|
251
|
+
<div class="facets-content">${this.getMoreFacetsTemplate}</div>
|
|
252
|
+
${this.paginationSize > 0
|
|
253
|
+
? html`${this.facetsPaginationTemplate}
|
|
254
|
+
<div class="footer">
|
|
255
|
+
<button
|
|
256
|
+
class="btn btn-cancel"
|
|
257
|
+
type="button"
|
|
258
|
+
@click=${this.cancelClick}
|
|
259
|
+
>
|
|
260
|
+
Cancel
|
|
261
|
+
</button>
|
|
262
|
+
<button
|
|
263
|
+
class="btn btn-submit"
|
|
264
|
+
type="button"
|
|
265
|
+
@click=${this.applySearchFacetsClicked}
|
|
266
|
+
>
|
|
267
|
+
Apply filters
|
|
268
|
+
</button>
|
|
269
|
+
</div>`
|
|
270
|
+
: html`No result found. please try again later.`}
|
|
271
|
+
`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
render() {
|
|
275
|
+
return html`
|
|
276
|
+
<div id="more-facets-page">
|
|
277
|
+
<form>
|
|
278
|
+
${this.facetsLoading
|
|
279
|
+
? this.loaderTemplate
|
|
280
|
+
: this.facetsContentTemplate}
|
|
281
|
+
</form>
|
|
282
|
+
</div>
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private applySearchFacetsClicked() {
|
|
287
|
+
const event = new CustomEvent<SelectedFacets>('facetsChanged', {
|
|
288
|
+
detail: this.selectedFacets,
|
|
289
|
+
bubbles: true,
|
|
290
|
+
composed: true,
|
|
291
|
+
});
|
|
292
|
+
this.dispatchEvent(event);
|
|
293
|
+
this.modalManager?.closeModal();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private cancelClick() {
|
|
297
|
+
this.modalManager?.closeModal();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static get styles(): CSSResultGroup {
|
|
301
|
+
const modalSubmitButton = css`var(--primaryButtonBGColor, #194880)`;
|
|
302
|
+
|
|
303
|
+
return css`
|
|
304
|
+
#more-facets-page {
|
|
305
|
+
margin-bottom: 2rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.facets-content {
|
|
309
|
+
-webkit-column-width: 25rem;
|
|
310
|
+
-moz-column-width: 25rem;
|
|
311
|
+
column-width: 25rem;
|
|
312
|
+
font-size: 1.2rem;
|
|
313
|
+
padding: 0 10px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
ul.facet-list {
|
|
317
|
+
list-style: none;
|
|
318
|
+
margin: 0;
|
|
319
|
+
padding: 0;
|
|
320
|
+
}
|
|
321
|
+
ul.facet-list li {
|
|
322
|
+
margin-bottom: 2px;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.facet-row {
|
|
326
|
+
text-align: left;
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: start;
|
|
329
|
+
font-weight: 500;
|
|
330
|
+
font-size: 1.2rem;
|
|
331
|
+
}
|
|
332
|
+
.facet-row input {
|
|
333
|
+
margin: 1px 5px 1px 0;
|
|
334
|
+
vertical-align: middle;
|
|
335
|
+
}
|
|
336
|
+
.facet-info-display {
|
|
337
|
+
display: flex;
|
|
338
|
+
flex: 1 1 0%;
|
|
339
|
+
cursor: pointer;
|
|
340
|
+
}
|
|
341
|
+
.facet-title {
|
|
342
|
+
flex: 1 1 0%;
|
|
343
|
+
}
|
|
344
|
+
.facet-count {
|
|
345
|
+
margin-left: 5px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.page-number {
|
|
349
|
+
background: none;
|
|
350
|
+
border: 0;
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
border-radius: 100%;
|
|
353
|
+
width: 25px;
|
|
354
|
+
height: 25px;
|
|
355
|
+
margin: 10px;
|
|
356
|
+
font-size: 1.4rem;
|
|
357
|
+
vertical-align: middle;
|
|
358
|
+
}
|
|
359
|
+
.current-page {
|
|
360
|
+
background: black;
|
|
361
|
+
color: white;
|
|
362
|
+
}
|
|
363
|
+
.facets-loader {
|
|
364
|
+
text-align: center;
|
|
365
|
+
margin-bottom: 2rem;
|
|
366
|
+
height: 7rem;
|
|
367
|
+
width: 7rem;
|
|
368
|
+
display: inline-block;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.btn {
|
|
372
|
+
border: none;
|
|
373
|
+
padding: 10px;
|
|
374
|
+
margin-bottom: 10px;
|
|
375
|
+
width: auto;
|
|
376
|
+
border-radius: 0.4rem;
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
}
|
|
379
|
+
.btn-cancel {
|
|
380
|
+
background-color: #000;
|
|
381
|
+
color: white;
|
|
382
|
+
}
|
|
383
|
+
.btn-submit {
|
|
384
|
+
background-color: ${modalSubmitButton};
|
|
385
|
+
color: white;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.footer {
|
|
389
|
+
margin-top: 10px;
|
|
390
|
+
}
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
@@ -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: #eee;
|
|
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
|
+
}
|