@internetarchive/collection-browser 4.1.0 → 4.2.0-alpha-webdev8164.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +29 -29
- package/.github/workflows/ci.yml +27 -27
- package/.github/workflows/gh-pages-main.yml +39 -39
- package/.github/workflows/npm-publish.yml +39 -39
- package/.github/workflows/pr-preview.yml +38 -38
- package/.husky/pre-commit +1 -1
- package/.prettierignore +1 -1
- package/LICENSE +661 -661
- package/README.md +83 -83
- package/dist/src/collection-browser.js +761 -761
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/collection-facets/facets-template.js +5 -0
- package/dist/src/collection-facets/facets-template.js.map +1 -1
- package/dist/src/collection-facets/more-facets-content.d.ts +95 -8
- package/dist/src/collection-facets/more-facets-content.js +576 -102
- package/dist/src/collection-facets/more-facets-content.js.map +1 -1
- package/dist/src/collection-facets/more-facets-pagination.d.ts +12 -3
- package/dist/src/collection-facets/more-facets-pagination.js +71 -9
- package/dist/src/collection-facets/more-facets-pagination.js.map +1 -1
- package/dist/src/collection-facets/toggle-switch.js +1 -0
- package/dist/src/collection-facets/toggle-switch.js.map +1 -1
- package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
- package/dist/src/data-source/collection-browser-query-state.js.map +1 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.js +280 -280
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
- package/dist/test/collection-browser.test.js +189 -189
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/collection-facets/more-facets-content.test.js +162 -3
- package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
- package/dist/test/collection-facets/more-facets-pagination.test.js +63 -3
- package/dist/test/collection-facets/more-facets-pagination.test.js.map +1 -1
- package/dist/test/mocks/mock-search-responses.d.ts +5 -0
- package/dist/test/mocks/mock-search-responses.js +44 -0
- package/dist/test/mocks/mock-search-responses.js.map +1 -1
- package/dist/test/mocks/mock-search-service.js +2 -1
- package/dist/test/mocks/mock-search-service.js.map +1 -1
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js +22 -22
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -1
- package/eslint.config.mjs +53 -53
- package/index.html +24 -24
- package/local.archive.org.cert +86 -86
- package/local.archive.org.key +27 -27
- package/package.json +121 -120
- package/renovate.json +6 -6
- package/src/collection-browser.ts +3070 -3070
- package/src/collection-facets/facets-template.ts +5 -0
- package/src/collection-facets/more-facets-content.ts +625 -113
- package/src/collection-facets/more-facets-pagination.ts +84 -10
- package/src/collection-facets/toggle-switch.ts +1 -0
- package/src/data-source/collection-browser-data-source.ts +1444 -1444
- package/src/data-source/collection-browser-query-state.ts +60 -60
- package/src/sort-filter-bar/sort-filter-bar.ts +733 -733
- package/test/collection-browser.test.ts +2402 -2402
- package/test/collection-facets/more-facets-content.test.ts +251 -4
- package/test/collection-facets/more-facets-pagination.test.ts +87 -3
- package/test/mocks/mock-search-responses.ts +48 -0
- package/test/mocks/mock-search-service.ts +2 -0
- package/test/sort-filter-bar/sort-filter-bar.test.ts +443 -443
- package/tsconfig.json +25 -25
- package/web-dev-server.config.mjs +30 -30
- package/web-test-runner.config.mjs +52 -52
- package/.claude/settings.local.json +0 -8
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
PropertyValues,
|
|
8
8
|
TemplateResult,
|
|
9
9
|
} from 'lit';
|
|
10
|
-
import { customElement, property, state } from 'lit/decorators.js';
|
|
10
|
+
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
11
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
12
|
+
import { when } from 'lit/directives/when.js';
|
|
11
13
|
import {
|
|
12
14
|
Aggregation,
|
|
13
15
|
Bucket,
|
|
@@ -40,13 +42,16 @@ import type {
|
|
|
40
42
|
TVChannelAliases,
|
|
41
43
|
} from '../data-source/models';
|
|
42
44
|
import '@internetarchive/elements/ia-status-indicator/ia-status-indicator';
|
|
43
|
-
import './more-facets-pagination';
|
|
44
45
|
import './facets-template';
|
|
45
46
|
import {
|
|
46
47
|
analyticsActions,
|
|
47
48
|
analyticsCategories,
|
|
48
49
|
} from '../utils/analytics-events';
|
|
49
50
|
import './toggle-switch';
|
|
51
|
+
import './more-facets-pagination';
|
|
52
|
+
import '@internetarchive/ia-clearable-text-input';
|
|
53
|
+
import arrowLeftIcon from '../assets/img/icons/arrow-left';
|
|
54
|
+
import arrowRightIcon from '../assets/img/icons/arrow-right';
|
|
50
55
|
import { srOnlyStyle } from '../styles/sr-only';
|
|
51
56
|
import {
|
|
52
57
|
mergeSelectedFacets,
|
|
@@ -58,6 +63,12 @@ import {
|
|
|
58
63
|
MORE_FACETS__MAX_AGGREGATIONS,
|
|
59
64
|
} from './models';
|
|
60
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Threshold for switching from horizontal scroll to pagination.
|
|
68
|
+
* If facet count >= this value, use pagination. Otherwise use horizontal scroll.
|
|
69
|
+
*/
|
|
70
|
+
const PAGINATION_THRESHOLD = 1000;
|
|
71
|
+
|
|
61
72
|
@customElement('more-facets-content')
|
|
62
73
|
export class MoreFacetsContent extends LitElement {
|
|
63
74
|
@property({ type: String }) facetKey?: FacetOption;
|
|
@@ -126,10 +137,38 @@ export class MoreFacetsContent extends LitElement {
|
|
|
126
137
|
getDefaultSelectedFacets();
|
|
127
138
|
|
|
128
139
|
/**
|
|
129
|
-
*
|
|
140
|
+
* Text entered by the user to filter facet buckets.
|
|
141
|
+
* Applied to bucket.key for case-insensitive matching.
|
|
142
|
+
*/
|
|
143
|
+
@state() private filterText = '';
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Current page number for pagination (when facet count >= PAGINATION_THRESHOLD).
|
|
130
147
|
*/
|
|
131
148
|
@state() private pageNumber = 1;
|
|
132
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Whether the component is narrow enough to warrant compact pagination.
|
|
152
|
+
* Updated via a ResizeObserver-based container query approach.
|
|
153
|
+
*/
|
|
154
|
+
@state() private isCompactView = false;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Whether the horizontal scroll is at the leftmost position.
|
|
158
|
+
*/
|
|
159
|
+
@state() private atScrollStart = true;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Whether the horizontal scroll is at the rightmost position.
|
|
163
|
+
*/
|
|
164
|
+
@state() private atScrollEnd = true;
|
|
165
|
+
|
|
166
|
+
@query('ia-clearable-text-input')
|
|
167
|
+
private filterInput!: HTMLElement;
|
|
168
|
+
|
|
169
|
+
@query('.facets-content')
|
|
170
|
+
private facetsContentEl!: HTMLElement;
|
|
171
|
+
|
|
133
172
|
willUpdate(changed: PropertyValues): void {
|
|
134
173
|
if (
|
|
135
174
|
changed.has('aggregations') ||
|
|
@@ -143,6 +182,13 @@ export class MoreFacetsContent extends LitElement {
|
|
|
143
182
|
this.facetGroup = this.mergedFacets;
|
|
144
183
|
}
|
|
145
184
|
|
|
185
|
+
// Reset to page 1 when filter text changes (only matters for pagination mode)
|
|
186
|
+
if (changed.has('filterText')) {
|
|
187
|
+
this.pageNumber = 1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
updated(changed: PropertyValues): void {
|
|
146
192
|
// If any of the search properties change, it triggers a facet fetch
|
|
147
193
|
if (
|
|
148
194
|
changed.has('facetKey') ||
|
|
@@ -159,24 +205,167 @@ export class MoreFacetsContent extends LitElement {
|
|
|
159
205
|
|
|
160
206
|
this.updateSpecificFacets();
|
|
161
207
|
}
|
|
208
|
+
|
|
209
|
+
// Reset horizontal scroll when filter text changes (e.g., switching from
|
|
210
|
+
// horizontal-scroll mode back to pagination mode)
|
|
211
|
+
if (changed.has('filterText')) {
|
|
212
|
+
const facetsContent = this.shadowRoot?.querySelector('.facets-content');
|
|
213
|
+
if (facetsContent) {
|
|
214
|
+
facetsContent.scrollLeft = 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Manage scroll listener for horizontal scroll mode arrows.
|
|
219
|
+
// Only re-evaluate when properties that affect the displayed content change.
|
|
220
|
+
if (
|
|
221
|
+
changed.has('filterText') ||
|
|
222
|
+
changed.has('aggregations') ||
|
|
223
|
+
changed.has('facetKey') ||
|
|
224
|
+
changed.has('sortedBy') ||
|
|
225
|
+
changed.has('selectedFacets') ||
|
|
226
|
+
changed.has('unappliedFacetChanges')
|
|
227
|
+
) {
|
|
228
|
+
if (!this.usePagination) {
|
|
229
|
+
this.attachScrollListener();
|
|
230
|
+
// Refresh scroll state whenever content may have changed (e.g., filtering)
|
|
231
|
+
requestAnimationFrame(() => this.updateScrollState());
|
|
232
|
+
} else {
|
|
233
|
+
this.removeScrollListener();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
162
236
|
}
|
|
163
237
|
|
|
238
|
+
private resizeObserver?: ResizeObserver;
|
|
239
|
+
|
|
164
240
|
firstUpdated(): void {
|
|
165
241
|
this.setupEscapeListeners();
|
|
242
|
+
this.setupCompactViewObserver();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
disconnectedCallback(): void {
|
|
246
|
+
super.disconnectedCallback();
|
|
247
|
+
this.resizeObserver?.disconnect();
|
|
248
|
+
this.removeScrollListener();
|
|
249
|
+
document.removeEventListener('keydown', this.escapeHandler);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private scrollHandler = () => this.updateScrollState();
|
|
253
|
+
|
|
254
|
+
private scrollListenerAttached = false;
|
|
255
|
+
|
|
256
|
+
private scrollListenerTarget?: HTMLElement;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Attaches a scroll event listener to the facets content element
|
|
260
|
+
* to track horizontal scroll position for arrow button states.
|
|
261
|
+
*/
|
|
262
|
+
private attachScrollListener(): void {
|
|
263
|
+
if (this.scrollListenerAttached || !this.facetsContentEl) return;
|
|
264
|
+
this.scrollListenerTarget = this.facetsContentEl;
|
|
265
|
+
this.scrollListenerTarget.addEventListener('scroll', this.scrollHandler, {
|
|
266
|
+
passive: true,
|
|
267
|
+
});
|
|
268
|
+
this.scrollListenerAttached = true;
|
|
269
|
+
// Defer initial state check until after browser layout, so scrollWidth
|
|
270
|
+
// reflects the actual content dimensions.
|
|
271
|
+
requestAnimationFrame(() => this.updateScrollState());
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private removeScrollListener(): void {
|
|
275
|
+
if (!this.scrollListenerAttached || !this.scrollListenerTarget) return;
|
|
276
|
+
this.scrollListenerTarget.removeEventListener('scroll', this.scrollHandler);
|
|
277
|
+
this.scrollListenerTarget = undefined;
|
|
278
|
+
this.scrollListenerAttached = false;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Updates the scroll arrow disabled states based on current scroll position.
|
|
283
|
+
*/
|
|
284
|
+
private updateScrollState(): void {
|
|
285
|
+
const el = this.facetsContentEl;
|
|
286
|
+
if (!el) return;
|
|
287
|
+
this.atScrollStart = el.scrollLeft <= 0;
|
|
288
|
+
this.atScrollEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 1;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Calculates the width of one column step (column width + gap) based on
|
|
293
|
+
* the CSS multi-column layout of the scroll container.
|
|
294
|
+
*/
|
|
295
|
+
private getColumnStep(): number {
|
|
296
|
+
const el = this.facetsContentEl;
|
|
297
|
+
if (!el) return 0;
|
|
298
|
+
|
|
299
|
+
const facetRows = el.querySelector('.facet-rows') as HTMLElement;
|
|
300
|
+
const styles = facetRows
|
|
301
|
+
? getComputedStyle(facetRows)
|
|
302
|
+
: getComputedStyle(el);
|
|
303
|
+
|
|
304
|
+
const columnCount = parseInt(styles.columnCount, 10) || 3;
|
|
305
|
+
const columnGap = parseInt(styles.columnGap, 10) || 15;
|
|
306
|
+
|
|
307
|
+
// Column width = (visible width - total gaps) / column count
|
|
308
|
+
// Column step = column width + gap = (visible width + gap) / column count
|
|
309
|
+
return (el.clientWidth + columnGap) / columnCount;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Snaps a scroll target to the nearest column boundary.
|
|
314
|
+
*/
|
|
315
|
+
private snapToColumn(target: number): number {
|
|
316
|
+
const step = this.getColumnStep();
|
|
317
|
+
if (step <= 0) return target;
|
|
318
|
+
return Math.round(target / step) * step;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Scrolls the facet content left by approximately one page, snapping to
|
|
323
|
+
* the nearest column boundary.
|
|
324
|
+
*/
|
|
325
|
+
private onScrollLeft(): void {
|
|
326
|
+
const el = this.facetsContentEl;
|
|
327
|
+
if (!el) return;
|
|
328
|
+
const rawTarget = el.scrollLeft - el.clientWidth;
|
|
329
|
+
const snapped = Math.max(0, this.snapToColumn(rawTarget));
|
|
330
|
+
el.scrollTo({ left: snapped, behavior: 'smooth' });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Scrolls the facet content right by approximately one page, snapping to
|
|
335
|
+
* the nearest column boundary.
|
|
336
|
+
*/
|
|
337
|
+
private onScrollRight(): void {
|
|
338
|
+
const el = this.facetsContentEl;
|
|
339
|
+
if (!el) return;
|
|
340
|
+
const maxScroll = el.scrollWidth - el.clientWidth;
|
|
341
|
+
const rawTarget = el.scrollLeft + el.clientWidth;
|
|
342
|
+
const snapped = Math.min(maxScroll, this.snapToColumn(rawTarget));
|
|
343
|
+
el.scrollTo({ left: snapped, behavior: 'smooth' });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Sets up a ResizeObserver to toggle compact pagination based on component width.
|
|
348
|
+
*/
|
|
349
|
+
private setupCompactViewObserver(): void {
|
|
350
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
351
|
+
for (const entry of entries) {
|
|
352
|
+
const compact = entry.contentRect.width <= 560;
|
|
353
|
+
if (this.isCompactView !== compact) this.isCompactView = compact;
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
this.resizeObserver.observe(this);
|
|
166
357
|
}
|
|
167
358
|
|
|
168
359
|
/**
|
|
169
360
|
* Close more facets modal on Escape click
|
|
170
361
|
*/
|
|
362
|
+
private escapeHandler = (e: KeyboardEvent) => {
|
|
363
|
+
if (e.key === 'Escape') this.modalManager?.closeModal();
|
|
364
|
+
};
|
|
365
|
+
|
|
171
366
|
private setupEscapeListeners() {
|
|
172
367
|
if (this.modalManager) {
|
|
173
|
-
document.addEventListener('keydown',
|
|
174
|
-
if (e.key === 'Escape') {
|
|
175
|
-
this.modalManager?.closeModal();
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
} else {
|
|
179
|
-
document.removeEventListener('keydown', () => {});
|
|
368
|
+
document.addEventListener('keydown', this.escapeHandler);
|
|
180
369
|
}
|
|
181
370
|
}
|
|
182
371
|
|
|
@@ -215,34 +404,21 @@ export class MoreFacetsContent extends LitElement {
|
|
|
215
404
|
rows: 0, // todo - do we want server-side pagination with offset/page/limit flag?
|
|
216
405
|
};
|
|
217
406
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
407
|
+
try {
|
|
408
|
+
const results = await this.searchService?.search(params, this.searchType);
|
|
409
|
+
this.aggregations = results?.success?.response.aggregations;
|
|
221
410
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
411
|
+
const collectionTitles = results?.success?.response?.collectionTitles;
|
|
412
|
+
if (collectionTitles) {
|
|
413
|
+
for (const [id, title] of Object.entries(collectionTitles)) {
|
|
414
|
+
this.collectionTitles?.set(id, title);
|
|
415
|
+
}
|
|
226
416
|
}
|
|
417
|
+
} finally {
|
|
418
|
+
this.facetsLoading = false;
|
|
227
419
|
}
|
|
228
420
|
}
|
|
229
421
|
|
|
230
|
-
/**
|
|
231
|
-
* Handler for page number changes from the pagination widget.
|
|
232
|
-
*/
|
|
233
|
-
private pageNumberClicked(e: CustomEvent<{ page: number }>) {
|
|
234
|
-
const page = e?.detail?.page;
|
|
235
|
-
if (page) {
|
|
236
|
-
this.pageNumber = Number(page);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
this.analyticsHandler?.sendEvent({
|
|
240
|
-
category: analyticsCategories.default,
|
|
241
|
-
action: analyticsActions.moreFacetsPageChange,
|
|
242
|
-
label: `${this.pageNumber}`,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
422
|
/**
|
|
247
423
|
* Combines the selected facets with the aggregations to create a single list of facets
|
|
248
424
|
*/
|
|
@@ -324,7 +500,10 @@ export class MoreFacetsContent extends LitElement {
|
|
|
324
500
|
|
|
325
501
|
const buckets: FacetBucket[] = Object.entries(selectedFacetsForKey).map(
|
|
326
502
|
([value, data]) => {
|
|
327
|
-
const displayText
|
|
503
|
+
const displayText =
|
|
504
|
+
(this.facetKey === 'collection'
|
|
505
|
+
? this.collectionTitles?.get(value)
|
|
506
|
+
: undefined) ?? value;
|
|
328
507
|
return {
|
|
329
508
|
displayText,
|
|
330
509
|
key: value,
|
|
@@ -368,17 +547,33 @@ export class MoreFacetsContent extends LitElement {
|
|
|
368
547
|
});
|
|
369
548
|
}
|
|
370
549
|
|
|
371
|
-
// Construct the array of facet buckets from the aggregation buckets
|
|
550
|
+
// Construct the array of facet buckets from the aggregation buckets,
|
|
551
|
+
// using collection display titles where available.
|
|
372
552
|
const facetBuckets: FacetBucket[] = sortedBuckets.map(bucket => {
|
|
373
553
|
const bucketKeyStr = `${bucket.key}`;
|
|
554
|
+
const displayText =
|
|
555
|
+
(this.facetKey === 'collection'
|
|
556
|
+
? this.collectionTitles?.get(bucketKeyStr)
|
|
557
|
+
: undefined) ?? bucketKeyStr;
|
|
374
558
|
return {
|
|
375
|
-
displayText
|
|
559
|
+
displayText,
|
|
376
560
|
key: `${bucketKeyStr}`,
|
|
377
561
|
count: bucket.doc_count,
|
|
378
562
|
state: 'none',
|
|
379
563
|
};
|
|
380
564
|
});
|
|
381
565
|
|
|
566
|
+
// For collection facets sorted alphabetically, re-sort by display title
|
|
567
|
+
// instead of the raw identifier used by getSortedBuckets.
|
|
568
|
+
if (
|
|
569
|
+
this.facetKey === 'collection' &&
|
|
570
|
+
this.sortedBy === AggregationSortType.ALPHABETICAL
|
|
571
|
+
) {
|
|
572
|
+
facetBuckets.sort((a, b) =>
|
|
573
|
+
(a.displayText ?? a.key).localeCompare(b.displayText ?? b.key),
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
382
577
|
return {
|
|
383
578
|
title: facetGroupTitle,
|
|
384
579
|
key: this.facetKey,
|
|
@@ -387,29 +582,81 @@ export class MoreFacetsContent extends LitElement {
|
|
|
387
582
|
}
|
|
388
583
|
|
|
389
584
|
/**
|
|
390
|
-
* Returns
|
|
585
|
+
* Returns the facet group with buckets filtered by the current filter text.
|
|
586
|
+
* Filters are applied to the full bucket list before pagination.
|
|
391
587
|
*/
|
|
392
|
-
private get
|
|
393
|
-
const { facetGroup } = this;
|
|
588
|
+
private get filteredFacetGroup(): FacetGroup | undefined {
|
|
589
|
+
const { facetGroup, filterText } = this;
|
|
394
590
|
if (!facetGroup) return undefined;
|
|
395
591
|
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
592
|
+
// If no filter text, return the full group
|
|
593
|
+
if (!filterText.trim()) {
|
|
594
|
+
return facetGroup;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Filter buckets by the text the user actually sees.
|
|
598
|
+
// For collections, match against the displayed collection title (not the identifier).
|
|
599
|
+
// For other facet types, match against the bucket key (which is also the display text).
|
|
600
|
+
const lowerFilter = filterText.toLowerCase().trim();
|
|
601
|
+
const filteredBuckets = facetGroup.buckets.filter(bucket => {
|
|
602
|
+
const displayText = this.collectionTitles?.get(bucket.key) ?? bucket.key;
|
|
603
|
+
return displayText.toLowerCase().includes(lowerFilter);
|
|
604
|
+
});
|
|
402
605
|
|
|
403
606
|
return {
|
|
404
607
|
...facetGroup,
|
|
405
|
-
buckets:
|
|
608
|
+
buckets: filteredBuckets,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Determines whether to use pagination based on the number of filtered facets.
|
|
614
|
+
* Returns true if facet count >= PAGINATION_THRESHOLD, false otherwise.
|
|
615
|
+
*/
|
|
616
|
+
private get usePagination(): boolean {
|
|
617
|
+
const facetCount = this.filteredFacetGroup?.buckets.length ?? 0;
|
|
618
|
+
return facetCount >= PAGINATION_THRESHOLD;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Returns the facet group for the current page.
|
|
623
|
+
* If using pagination (>= 1000 facets), slices to show only the current page.
|
|
624
|
+
* Otherwise, returns all facets for horizontal scrolling.
|
|
625
|
+
*/
|
|
626
|
+
private get facetGroupForCurrentPage(): FacetGroup | undefined {
|
|
627
|
+
const filteredGroup = this.filteredFacetGroup;
|
|
628
|
+
if (!filteredGroup) return undefined;
|
|
629
|
+
|
|
630
|
+
// If facet count is below threshold, show all facets with horizontal scroll
|
|
631
|
+
if (!this.usePagination) {
|
|
632
|
+
return filteredGroup;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Otherwise, use pagination - slice to current page
|
|
636
|
+
const startIndex = (this.pageNumber - 1) * this.facetsPerPage;
|
|
637
|
+
const endIndex = startIndex + this.facetsPerPage;
|
|
638
|
+
const slicedBuckets = filteredGroup.buckets.slice(startIndex, endIndex);
|
|
639
|
+
|
|
640
|
+
return {
|
|
641
|
+
...filteredGroup,
|
|
642
|
+
buckets: slicedBuckets,
|
|
406
643
|
};
|
|
407
644
|
}
|
|
408
645
|
|
|
409
646
|
private get moreFacetsTemplate(): TemplateResult {
|
|
647
|
+
const facetGroup = this.facetGroupForCurrentPage;
|
|
648
|
+
|
|
649
|
+
// Show empty state if filtering returned no results
|
|
650
|
+
if (
|
|
651
|
+
this.filterText.trim() &&
|
|
652
|
+
(!facetGroup || facetGroup.buckets.length === 0)
|
|
653
|
+
) {
|
|
654
|
+
return this.emptyFilterResultsTemplate;
|
|
655
|
+
}
|
|
656
|
+
|
|
410
657
|
return html`
|
|
411
658
|
<facets-template
|
|
412
|
-
.facetGroup=${
|
|
659
|
+
.facetGroup=${facetGroup}
|
|
413
660
|
.selectedFacets=${this.selectedFacets}
|
|
414
661
|
.collectionTitles=${this.collectionTitles}
|
|
415
662
|
@facetClick=${(e: CustomEvent<FacetEventDetails>) => {
|
|
@@ -434,50 +681,51 @@ export class MoreFacetsContent extends LitElement {
|
|
|
434
681
|
`;
|
|
435
682
|
}
|
|
436
683
|
|
|
684
|
+
private get emptyFilterResultsTemplate(): TemplateResult {
|
|
685
|
+
return html`
|
|
686
|
+
<div class="empty-results">
|
|
687
|
+
<p>${msg('No matching values found.')}</p>
|
|
688
|
+
<p class="hint">${msg('Try a different search term.')}</p>
|
|
689
|
+
</div>
|
|
690
|
+
`;
|
|
691
|
+
}
|
|
692
|
+
|
|
437
693
|
/**
|
|
438
|
-
*
|
|
694
|
+
* Number of pages for pagination (only used when facet count >= PAGINATION_THRESHOLD).
|
|
439
695
|
*/
|
|
440
696
|
private get paginationSize(): number {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// Calculate the appropriate number of pages to show in the modal pagination widget
|
|
444
|
-
const length = this.aggregations[this.facetKey]?.buckets.length;
|
|
445
|
-
return Math.ceil(length / this.facetsPerPage);
|
|
697
|
+
const filteredBuckets = this.filteredFacetGroup?.buckets ?? [];
|
|
698
|
+
return Math.ceil(filteredBuckets.length / this.facetsPerPage);
|
|
446
699
|
}
|
|
447
700
|
|
|
448
|
-
|
|
701
|
+
/**
|
|
702
|
+
* Template for pagination component.
|
|
703
|
+
*/
|
|
449
704
|
private get facetsPaginationTemplate() {
|
|
450
|
-
return
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
: nothing;
|
|
705
|
+
return html`<more-facets-pagination
|
|
706
|
+
.size=${this.paginationSize}
|
|
707
|
+
.currentPage=${this.pageNumber}
|
|
708
|
+
.compact=${this.isCompactView}
|
|
709
|
+
@pageNumberClicked=${this.pageNumberClicked}
|
|
710
|
+
></more-facets-pagination>`;
|
|
457
711
|
}
|
|
458
712
|
|
|
459
713
|
private get footerTemplate() {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
Apply filters
|
|
476
|
-
</button>
|
|
477
|
-
</div> `;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
return nothing;
|
|
714
|
+
return html`
|
|
715
|
+
${when(this.usePagination, () => this.facetsPaginationTemplate)}
|
|
716
|
+
<div class="footer">
|
|
717
|
+
<button class="btn btn-cancel" type="button" @click=${this.cancelClick}>
|
|
718
|
+
Cancel
|
|
719
|
+
</button>
|
|
720
|
+
<button
|
|
721
|
+
class="btn btn-submit"
|
|
722
|
+
type="button"
|
|
723
|
+
@click=${this.applySearchFacetsClicked}
|
|
724
|
+
>
|
|
725
|
+
Apply filters
|
|
726
|
+
</button>
|
|
727
|
+
</div>
|
|
728
|
+
`;
|
|
481
729
|
}
|
|
482
730
|
|
|
483
731
|
private sortFacetAggregation(facetSortType: AggregationSortType) {
|
|
@@ -487,6 +735,44 @@ export class MoreFacetsContent extends LitElement {
|
|
|
487
735
|
);
|
|
488
736
|
}
|
|
489
737
|
|
|
738
|
+
/**
|
|
739
|
+
* Handler for filter input changes. Updates the filter text and triggers re-render.
|
|
740
|
+
*/
|
|
741
|
+
private handleFilterInput(e: Event): void {
|
|
742
|
+
const input = e.target as HTMLElement & { value: string };
|
|
743
|
+
this.filterText = input.value;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Handler for when the filter input is cleared via the clear button.
|
|
748
|
+
*/
|
|
749
|
+
private handleFilterClear(): void {
|
|
750
|
+
this.filterText = '';
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Handler for pagination page number clicks.
|
|
755
|
+
* Only used when facet count >= PAGINATION_THRESHOLD.
|
|
756
|
+
*/
|
|
757
|
+
private pageNumberClicked(e: CustomEvent<{ page: number }>) {
|
|
758
|
+
this.pageNumber = e.detail.page;
|
|
759
|
+
|
|
760
|
+
// Track page navigation in analytics
|
|
761
|
+
this.analyticsHandler?.sendEvent({
|
|
762
|
+
category: analyticsCategories.default,
|
|
763
|
+
action: analyticsActions.moreFacetsPageChange,
|
|
764
|
+
label: `${this.pageNumber}`,
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
this.dispatchEvent(
|
|
768
|
+
new CustomEvent('pageChanged', {
|
|
769
|
+
detail: this.pageNumber,
|
|
770
|
+
bubbles: true,
|
|
771
|
+
composed: true,
|
|
772
|
+
}),
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
|
|
490
776
|
private get modalHeaderTemplate(): TemplateResult {
|
|
491
777
|
const facetSort =
|
|
492
778
|
this.sortedBy ?? defaultFacetSort[this.facetKey as FacetOption];
|
|
@@ -494,36 +780,103 @@ export class MoreFacetsContent extends LitElement {
|
|
|
494
780
|
facetSort === AggregationSortType.COUNT ? 'left' : 'right';
|
|
495
781
|
|
|
496
782
|
return html`<span class="sr-only">${msg('More facets for:')}</span>
|
|
497
|
-
<span class="title">
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
783
|
+
<span class="title"> ${this.facetGroup?.title} </span>
|
|
784
|
+
<span class="header-controls">
|
|
785
|
+
<span class="sort-controls">
|
|
786
|
+
<label class="sort-label">${msg('Sort by:')}</label>
|
|
787
|
+
${this.facetKey
|
|
788
|
+
? html`<toggle-switch
|
|
789
|
+
class="sort-toggle"
|
|
790
|
+
leftValue=${AggregationSortType.COUNT}
|
|
791
|
+
leftLabel="Count"
|
|
792
|
+
rightValue=${valueFacetSort[this.facetKey]}
|
|
793
|
+
.rightLabel=${this.facetGroup?.title}
|
|
794
|
+
side=${defaultSwitchSide}
|
|
795
|
+
@change=${(e: CustomEvent<string>) => {
|
|
796
|
+
this.sortFacetAggregation(
|
|
797
|
+
Number(e.detail) as AggregationSortType,
|
|
798
|
+
);
|
|
799
|
+
}}
|
|
800
|
+
></toggle-switch>`
|
|
801
|
+
: nothing}
|
|
802
|
+
</span>
|
|
803
|
+
|
|
804
|
+
<span class="filter-controls">
|
|
805
|
+
<label class="filter-label">${msg('Filter by:')}</label>
|
|
806
|
+
<ia-clearable-text-input
|
|
807
|
+
class="filter-input"
|
|
808
|
+
.value=${this.filterText}
|
|
809
|
+
.placeholder=${msg('Search...')}
|
|
810
|
+
.screenReaderLabel=${msg('Filter facets')}
|
|
811
|
+
.clearButtonScreenReaderLabel=${msg('Clear filter')}
|
|
812
|
+
@input=${this.handleFilterInput}
|
|
813
|
+
@clear=${this.handleFilterClear}
|
|
814
|
+
></ia-clearable-text-input>
|
|
815
|
+
</span>
|
|
516
816
|
</span>`;
|
|
517
817
|
}
|
|
518
818
|
|
|
819
|
+
private get horizontalScrollTemplate(): TemplateResult {
|
|
820
|
+
const contentClasses = classMap({
|
|
821
|
+
'facets-content': true,
|
|
822
|
+
'horizontal-scroll-mode': true,
|
|
823
|
+
});
|
|
824
|
+
const showArrows = !this.atScrollStart || !this.atScrollEnd;
|
|
825
|
+
|
|
826
|
+
return html`<div class="scroll-nav-container">
|
|
827
|
+
${when(
|
|
828
|
+
showArrows,
|
|
829
|
+
() =>
|
|
830
|
+
html`<button
|
|
831
|
+
class="scroll-arrow scroll-left"
|
|
832
|
+
@click=${this.onScrollLeft}
|
|
833
|
+
?disabled=${this.atScrollStart}
|
|
834
|
+
aria-label="Scroll facets left"
|
|
835
|
+
>
|
|
836
|
+
${arrowLeftIcon}
|
|
837
|
+
</button>`,
|
|
838
|
+
)}
|
|
839
|
+
<div class=${contentClasses}>
|
|
840
|
+
<div class="facets-horizontal-container">
|
|
841
|
+
${this.moreFacetsTemplate}
|
|
842
|
+
</div>
|
|
843
|
+
</div>
|
|
844
|
+
${when(
|
|
845
|
+
showArrows,
|
|
846
|
+
() =>
|
|
847
|
+
html`<button
|
|
848
|
+
class="scroll-arrow scroll-right"
|
|
849
|
+
@click=${this.onScrollRight}
|
|
850
|
+
?disabled=${this.atScrollEnd}
|
|
851
|
+
aria-label="Scroll facets right"
|
|
852
|
+
>
|
|
853
|
+
${arrowRightIcon}
|
|
854
|
+
</button>`,
|
|
855
|
+
)}
|
|
856
|
+
</div>`;
|
|
857
|
+
}
|
|
858
|
+
|
|
519
859
|
render() {
|
|
860
|
+
const sectionClasses = classMap({
|
|
861
|
+
'pagination-mode': this.usePagination,
|
|
862
|
+
'horizontal-scroll-mode': !this.usePagination,
|
|
863
|
+
});
|
|
864
|
+
const contentClasses = classMap({
|
|
865
|
+
'facets-content': true,
|
|
866
|
+
'pagination-mode': this.usePagination,
|
|
867
|
+
});
|
|
868
|
+
|
|
520
869
|
return html`
|
|
521
870
|
${this.facetsLoading
|
|
522
871
|
? this.loaderTemplate
|
|
523
872
|
: html`
|
|
524
|
-
<section id="more-facets">
|
|
873
|
+
<section id="more-facets" class=${sectionClasses}>
|
|
525
874
|
<div class="header-content">${this.modalHeaderTemplate}</div>
|
|
526
|
-
|
|
875
|
+
${this.usePagination
|
|
876
|
+
? html`<div class=${contentClasses}>
|
|
877
|
+
${this.moreFacetsTemplate}
|
|
878
|
+
</div>`
|
|
879
|
+
: this.horizontalScrollTemplate}
|
|
527
880
|
${this.footerTemplate}
|
|
528
881
|
</section>
|
|
529
882
|
`}
|
|
@@ -546,6 +899,9 @@ export class MoreFacetsContent extends LitElement {
|
|
|
546
899
|
// Reset the unapplied changes back to default, now that they have been applied
|
|
547
900
|
this.unappliedFacetChanges = getDefaultSelectedFacets();
|
|
548
901
|
|
|
902
|
+
// Reset filter text
|
|
903
|
+
this.filterText = '';
|
|
904
|
+
|
|
549
905
|
this.modalManager?.closeModal();
|
|
550
906
|
this.analyticsHandler?.sendEvent({
|
|
551
907
|
category: analyticsCategories.default,
|
|
@@ -558,6 +914,9 @@ export class MoreFacetsContent extends LitElement {
|
|
|
558
914
|
// Reset the unapplied changes back to default
|
|
559
915
|
this.unappliedFacetChanges = getDefaultSelectedFacets();
|
|
560
916
|
|
|
917
|
+
// Reset filter text
|
|
918
|
+
this.filterText = '';
|
|
919
|
+
|
|
561
920
|
this.modalManager?.closeModal();
|
|
562
921
|
this.analyticsHandler?.sendEvent({
|
|
563
922
|
category: analyticsCategories.default,
|
|
@@ -573,10 +932,26 @@ export class MoreFacetsContent extends LitElement {
|
|
|
573
932
|
srOnlyStyle,
|
|
574
933
|
css`
|
|
575
934
|
section#more-facets {
|
|
576
|
-
|
|
577
|
-
|
|
935
|
+
display: flex;
|
|
936
|
+
flex-direction: column;
|
|
937
|
+
max-height: calc(100vh - 16.5rem);
|
|
938
|
+
padding: 10px;
|
|
939
|
+
box-sizing: border-box;
|
|
578
940
|
--facetsColumnCount: 3;
|
|
579
941
|
}
|
|
942
|
+
|
|
943
|
+
/* Both modes need a height constraint for proper column flow */
|
|
944
|
+
section#more-facets.horizontal-scroll-mode,
|
|
945
|
+
section#more-facets.pagination-mode {
|
|
946
|
+
--facetsMaxHeight: 280px;
|
|
947
|
+
}
|
|
948
|
+
.header-content {
|
|
949
|
+
flex-shrink: 0;
|
|
950
|
+
position: relative;
|
|
951
|
+
z-index: 1;
|
|
952
|
+
background: #fff;
|
|
953
|
+
}
|
|
954
|
+
|
|
580
955
|
.header-content .title {
|
|
581
956
|
display: block;
|
|
582
957
|
text-align: left;
|
|
@@ -585,8 +960,22 @@ export class MoreFacetsContent extends LitElement {
|
|
|
585
960
|
font-weight: bold;
|
|
586
961
|
}
|
|
587
962
|
|
|
963
|
+
.header-controls {
|
|
964
|
+
display: flex;
|
|
965
|
+
flex-wrap: wrap;
|
|
966
|
+
align-items: center;
|
|
967
|
+
gap: 8px 20px;
|
|
968
|
+
padding: 0 10px 8px;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.sort-controls {
|
|
972
|
+
display: inline-flex;
|
|
973
|
+
align-items: center;
|
|
974
|
+
white-space: nowrap;
|
|
975
|
+
gap: 5px;
|
|
976
|
+
}
|
|
977
|
+
|
|
588
978
|
.sort-label {
|
|
589
|
-
margin-left: 20px;
|
|
590
979
|
font-size: 1.3rem;
|
|
591
980
|
}
|
|
592
981
|
|
|
@@ -594,11 +983,115 @@ export class MoreFacetsContent extends LitElement {
|
|
|
594
983
|
font-weight: normal;
|
|
595
984
|
}
|
|
596
985
|
|
|
986
|
+
.filter-controls {
|
|
987
|
+
display: inline-flex;
|
|
988
|
+
align-items: center;
|
|
989
|
+
white-space: nowrap;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.filter-label {
|
|
993
|
+
font-size: 1.3rem;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
.filter-input {
|
|
997
|
+
--input-height: 2.5rem;
|
|
998
|
+
--input-font-size: 1.3rem;
|
|
999
|
+
--input-border-radius: 4px;
|
|
1000
|
+
--input-padding: 4px 8px;
|
|
1001
|
+
--input-focused-border-color: ${modalSubmitButton};
|
|
1002
|
+
width: 150px;
|
|
1003
|
+
margin-left: 5px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.empty-results {
|
|
1007
|
+
text-align: center;
|
|
1008
|
+
padding: 40px 20px;
|
|
1009
|
+
color: #666;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.empty-results .hint {
|
|
1013
|
+
margin-top: 10px;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
597
1016
|
.facets-content {
|
|
598
1017
|
font-size: 1.2rem;
|
|
599
|
-
|
|
600
|
-
|
|
1018
|
+
flex: 1 1 auto;
|
|
1019
|
+
min-height: 0;
|
|
1020
|
+
overflow-y: auto;
|
|
1021
|
+
overflow-x: hidden;
|
|
601
1022
|
padding: 10px;
|
|
1023
|
+
/* Force scrollbar to always be visible */
|
|
1024
|
+
scrollbar-width: thin; /* Firefox */
|
|
1025
|
+
scrollbar-color: #888 #f1f1f1; /* Firefox - thumb and track colors */
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* Horizontal scroll mode: horizontal scrolling only */
|
|
1029
|
+
.facets-content.horizontal-scroll-mode {
|
|
1030
|
+
overflow-x: auto;
|
|
1031
|
+
overflow-y: hidden;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/* Webkit browsers scrollbar styling - always visible */
|
|
1035
|
+
.facets-content::-webkit-scrollbar {
|
|
1036
|
+
width: 12px; /* Vertical scrollbar width */
|
|
1037
|
+
height: 12px; /* Horizontal scrollbar height */
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.facets-content::-webkit-scrollbar-track {
|
|
1041
|
+
background: #f1f1f1;
|
|
1042
|
+
border-radius: 6px;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.facets-content::-webkit-scrollbar-thumb {
|
|
1046
|
+
background: #888;
|
|
1047
|
+
border-radius: 6px;
|
|
1048
|
+
min-height: 30px; /* Ensure thumb is always visible when scrolling is possible */
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
.facets-content::-webkit-scrollbar-thumb:hover {
|
|
1052
|
+
background: #555;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/* Force corner to match track color */
|
|
1056
|
+
.facets-content::-webkit-scrollbar-corner {
|
|
1057
|
+
background: #f1f1f1;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.facets-horizontal-container {
|
|
1061
|
+
display: inline-block;
|
|
1062
|
+
min-width: 100%;
|
|
1063
|
+
/* Allow natural width expansion based on content */
|
|
1064
|
+
width: fit-content;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.scroll-nav-container {
|
|
1068
|
+
display: flex;
|
|
1069
|
+
align-items: center;
|
|
1070
|
+
flex: 1 1 auto;
|
|
1071
|
+
min-height: 0;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
.scroll-nav-container .facets-content {
|
|
1075
|
+
flex: 1 1 auto;
|
|
1076
|
+
min-width: 0;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.scroll-arrow {
|
|
1080
|
+
background: none;
|
|
1081
|
+
border: none;
|
|
1082
|
+
cursor: pointer;
|
|
1083
|
+
padding: 5px;
|
|
1084
|
+
flex-shrink: 0;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
.scroll-arrow svg {
|
|
1088
|
+
height: 14px;
|
|
1089
|
+
fill: #2c2c2c;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.scroll-arrow:disabled {
|
|
1093
|
+
opacity: 0.3;
|
|
1094
|
+
cursor: default;
|
|
602
1095
|
}
|
|
603
1096
|
.facets-loader {
|
|
604
1097
|
--icon-width: 70px;
|
|
@@ -614,6 +1107,7 @@ export class MoreFacetsContent extends LitElement {
|
|
|
614
1107
|
width: auto;
|
|
615
1108
|
border-radius: 4px;
|
|
616
1109
|
cursor: pointer;
|
|
1110
|
+
font-family: inherit;
|
|
617
1111
|
}
|
|
618
1112
|
.btn-cancel {
|
|
619
1113
|
background-color: #2c2c2c;
|
|
@@ -623,19 +1117,37 @@ export class MoreFacetsContent extends LitElement {
|
|
|
623
1117
|
background-color: ${modalSubmitButton};
|
|
624
1118
|
color: white;
|
|
625
1119
|
}
|
|
1120
|
+
more-facets-pagination {
|
|
1121
|
+
flex-shrink: 0;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
626
1124
|
.footer {
|
|
627
1125
|
text-align: center;
|
|
628
1126
|
margin-top: 10px;
|
|
1127
|
+
flex-shrink: 0;
|
|
629
1128
|
}
|
|
630
1129
|
|
|
631
1130
|
@media (max-width: 560px) {
|
|
632
|
-
section#more-facets
|
|
633
|
-
|
|
634
|
-
--facetsColumnCount: 1;
|
|
1131
|
+
section#more-facets.horizontal-scroll-mode,
|
|
1132
|
+
section#more-facets.pagination-mode {
|
|
1133
|
+
--facetsColumnCount: 1; /* Single column on mobile */
|
|
1134
|
+
--facetsMaxHeight: none; /* Remove fixed height for vertical scrolling */
|
|
635
1135
|
}
|
|
636
|
-
|
|
1136
|
+
/* On mobile, always use vertical scrolling regardless of mode */
|
|
1137
|
+
.facets-content,
|
|
1138
|
+
.facets-content.horizontal-scroll-mode {
|
|
637
1139
|
overflow-y: auto;
|
|
638
|
-
|
|
1140
|
+
overflow-x: hidden;
|
|
1141
|
+
}
|
|
1142
|
+
.scroll-nav-container {
|
|
1143
|
+
display: contents; /* Remove wrapper from layout so section flex-column works */
|
|
1144
|
+
}
|
|
1145
|
+
.scroll-arrow {
|
|
1146
|
+
display: none;
|
|
1147
|
+
}
|
|
1148
|
+
.filter-input {
|
|
1149
|
+
width: 120px;
|
|
1150
|
+
--input-font-size: 1.2rem;
|
|
639
1151
|
}
|
|
640
1152
|
}
|
|
641
1153
|
`,
|