@@ -9,10 +9,14 @@ import {
9
9
} from 'lit' ;
10
10
import { repeat } from 'lit/directives/repeat.js' ;
11
11
import { customElement , property , state } from 'lit/decorators.js' ;
12
- import { msg } from '@lit/localize' ;
13
12
import type { Aggregation , Bucket } from '@internetarchive/search-service' ;
14
13
import type { CollectionTitles } from '../../data-source/models' ;
15
- import type { FacetOption , SelectedFacets } from '../../models' ;
14
+ import type {
15
+ FacetEventDetails ,
16
+ FacetOption ,
17
+ FacetState ,
18
+ SelectedFacets ,
19
+ } from '../../models' ;
16
20
import { updateSelectedFacetBucket } from '../../utils/facet-utils' ;
17
21
import { SmartQueryHeuristicGroup } from './smart-facet-heuristics' ;
18
22
import type { SmartFacetDropdown } from './smart-facet-dropdown' ;
@@ -47,8 +51,12 @@ export class SmartFacetBar extends LitElement {
47
51
@property ( { type : Object } )
48
52
collectionTitles ?: CollectionTitles ;
49
53
54
+ @property ( { type : Boolean } ) filterToggleShown = false ;
55
+
50
56
@property ( { type : Boolean } ) filterToggleActive = false ;
51
57
58
+ @property ( { type : String } ) label ?: string ;
59
+
52
60
@state ( ) private heuristicRecs : SmartFacet [ ] = [ ] ;
53
61
54
62
@state ( ) private smartFacets : SmartFacet [ ] [ ] = [ ] ;
@@ -62,9 +70,13 @@ export class SmartFacetBar extends LitElement {
62
70
render ( ) {
63
71
if ( ! this . query ) return nothing ;
64
72
73
+ const shouldShowLabel = ! ! this . label && this . smartFacets . length > 0 ;
65
74
return html `
66
75
<div id= "smart-facets-container" >
67
76
${ this . filtersToggleTemplate }
77
+ ${ shouldShowLabel
78
+ ? html `<p id= "filters-label" > ${ this . label } </ p> `
79
+ : nothing }
68
80
${ repeat (
69
81
this . smartFacets ,
70
82
f =>
@@ -106,6 +118,15 @@ export class SmartFacetBar extends LitElement {
106
118
this . updateSmartFacets ( ) ;
107
119
}
108
120
121
+ deselectAll ( ) : void {
122
+ for ( const sf of this . smartFacets ) {
123
+ for ( const facet of sf ) {
124
+ facet . selected = false ;
125
+ }
126
+ }
127
+ this . requestUpdate ( ) ;
128
+ }
129
+
109
130
private async updateSmartFacets ( ) : Promise < void > {
110
131
log ( 'updating smart facets' ) ;
111
132
if ( this . query ) {
@@ -148,13 +169,15 @@ export class SmartFacetBar extends LitElement {
148
169
.facetInfo = ${ facets }
149
170
.labelPrefix = ${ fieldPrefixes [ facets [ 0 ] . facets [ 0 ] . facetType ] }
150
171
.activeFacetRef = ${ facets [ 0 ] . facets [ 0 ] }
151
- @facetClick = ${ this . facetDropdownClicked }
152
- @dropdownClick = ${ this . onDropdownClick }
172
+ @facetClick = ${ this . dropdownOptionClicked }
173
+ @dropdownClick = ${ this . dropdownClicked }
153
174
> </ smart- facet- dropdown>
154
175
` ;
155
176
}
156
177
157
- private get filtersToggleTemplate ( ) : TemplateResult {
178
+ private get filtersToggleTemplate ( ) : TemplateResult | typeof nothing {
179
+ if ( ! this . filterToggleShown ) return nothing ;
180
+
158
181
return html `
159
182
<butto n
160
183
id= "filters-toggle"
@@ -255,15 +278,48 @@ export class SmartFacetBar extends LitElement {
255
278
} as SmartFacet ;
256
279
}
257
280
258
- private facetClicked ( e : CustomEvent < SmartFacetEvent > ) : void {
259
- if ( ! e . detail . smartFacet . selected ) {
281
+ /**
282
+ * Toggles the state of the given smart facet, and updates the selected facets accordingly.
283
+ */
284
+ private toggleSmartFacet (
285
+ facet : SmartFacet ,
286
+ details : FacetEventDetails [ ] ,
287
+ ) : void {
288
+ let newState : FacetState ;
289
+ if ( facet . selected ) {
290
+ // When deselected, leave the smart facet where it is
291
+ newState = 'none' ;
292
+ this . smartFacets = this . smartFacets . map ( f => {
293
+ if ( f [ 0 ] === facet ) return [ { ...facet , selected : false } ] ;
294
+ return f ;
295
+ } ) ;
296
+ } else {
297
+ // When selected, move the toggled smart facet to the front of the list
298
+ newState = 'selected' ;
260
299
this . smartFacets = [
261
- [ { ...e . detail . smartFacet , selected : true } ] ,
262
- ...this . smartFacets . filter ( f => f [ 0 ] !== e . detail . smartFacet ) ,
300
+ [ { ...facet , selected : true } ] ,
301
+ ...this . smartFacets . filter ( f => f [ 0 ] !== facet ) ,
263
302
] ;
264
303
}
265
304
266
- for ( const facet of e . detail . details ) {
305
+ this . updateSelectedFacets (
306
+ details . map ( facet => ( {
307
+ ...facet ,
308
+ bucket : {
309
+ ...facet . bucket ,
310
+ state : newState ,
311
+ } ,
312
+ } ) ) ,
313
+ ) ;
314
+ }
315
+
316
+ /**
317
+ * Updates the selected facet buckets for each of the given facets,
318
+ * and emits a `facetsChanged` event to notify parent components of
319
+ * the new state.
320
+ */
321
+ private updateSelectedFacets ( facets : FacetEventDetails [ ] ) : void {
322
+ for ( const facet of facets ) {
267
323
this . selectedFacets = updateSelectedFacetBucket (
268
324
this . selectedFacets ,
269
325
facet . facetType ,
@@ -278,34 +334,39 @@ export class SmartFacetBar extends LitElement {
278
334
this . dispatchEvent ( event ) ;
279
335
}
280
336
281
- private facetDropdownClicked ( e : CustomEvent < SmartFacetEvent > ) : void {
282
- if (
283
- this . smartFacets . find ( sf => smartFacetEquals ( sf [ 0 ] , e . detail . smartFacet ) )
284
- ) {
337
+ /**
338
+ * Handler for when a smart facet button is clicked
339
+ */
340
+ private facetClicked ( e : CustomEvent < SmartFacetEvent > ) : void {
341
+ this . toggleSmartFacet ( e . detail . smartFacet , e . detail . details ) ;
342
+ }
343
+
344
+ /**
345
+ * Handler for when an option in a smart facet dropdown menu is selected
346
+ */
347
+ private dropdownOptionClicked ( e : CustomEvent < SmartFacetEvent > ) : void {
348
+ const existingFacet = this . smartFacets . find (
349
+ sf => sf . length === 1 && smartFacetEquals ( sf [ 0 ] , e . detail . smartFacet ) ,
350
+ ) ;
351
+ if ( existingFacet ) {
352
+ // The facet already exists outside the dropdown, so just select it there
353
+ this . toggleSmartFacet ( existingFacet [ 0 ] , e . detail . details ) ;
285
354
return ;
286
355
}
287
356
357
+ // Otherwise, prepend a new smart facet for the selected option
288
358
this . smartFacets = [
289
359
[ { ...e . detail . smartFacet , selected : true } ] ,
290
360
...this . smartFacets ,
291
361
] ;
292
362
293
- for ( const facet of e . detail . details ) {
294
- this . selectedFacets = updateSelectedFacetBucket (
295
- this . selectedFacets ,
296
- facet . facetType ,
297
- facet . bucket ,
298
- true ,
299
- ) ;
300
- }
301
-
302
- const event = new CustomEvent < SelectedFacets > ( 'facetsChanged' , {
303
- detail : this . selectedFacets ,
304
- } ) ;
305
- this . dispatchEvent ( event ) ;
363
+ this . updateSelectedFacets ( e . detail . details ) ;
306
364
}
307
365
308
- private onDropdownClick ( e : CustomEvent < SmartFacetDropdown > ) : void {
366
+ /**
367
+ * Handler for when any dropdown is clicked (whether button, caret, or menu item)
368
+ */
369
+ private dropdownClicked ( e : CustomEvent < SmartFacetDropdown > ) : void {
309
370
log ( 'smart bar: onDropdownClick' , e . detail ) ;
310
371
this . shadowRoot
311
372
?. querySelectorAll ( 'smart-facet-dropdown' )
@@ -330,9 +391,11 @@ export class SmartFacetBar extends LitElement {
330
391
# smart-facets-container {
331
392
display : flex;
332
393
align-items : center;
333
- flex-wrap : wrap;
334
394
gap : 5px 10px ;
335
395
padding : 10px 0 ;
396
+ white-space : nowrap;
397
+ overflow : scroll hidden;
398
+ scrollbar-width : none;
336
399
}
337
400
338
401
# filters-toggle {
@@ -363,6 +426,12 @@ export class SmartFacetBar extends LitElement {
363
426
# filters-toggle .active > svg {
364
427
filter : invert (1 );
365
428
}
429
+
430
+ # filters-label {
431
+ font-size : 1.4rem ;
432
+ font-weight : var (--smartFacetLabelFontWeight , normal);
433
+ margin : 0 -5px 0 0 ;
434
+ }
366
435
` ;
367
436
}
368
437
}
0 commit comments