@@ -8,18 +8,33 @@ import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angul
88import { trigger , state , style , transition , animate } from '@angular/animations' ;
99import { TopBarService } from '../../services/top-bar.service' ;
1010import { MatTableModule } from '@angular/material/table' ;
11+ import { AsyncPipe , NgForOf } from '@angular/common' ;
12+ import { Observable } from 'rxjs' ;
13+ import { map , startWith } from 'rxjs/operators' ;
1114
1215import { MatFormFieldModule } from '@angular/material/form-field' ;
1316import { MatInputModule } from '@angular/material/input' ;
1417import { MatButtonModule } from '@angular/material/button' ;
1518import { MatSelectModule } from '@angular/material/select' ;
1619import { MatCheckboxModule } from '@angular/material/checkbox' ;
20+ import { MatAutocompleteModule } from '@angular/material/autocomplete' ;
21+
22+ /** IAU 88 constellation three-letter abbreviations (official). */
23+ export const CONSTELLATION_ABBREVIATIONS : string [ ] = [
24+ 'And' , 'Ant' , 'Aps' , 'Aqr' , 'Aql' , 'Ara' , 'Ari' , 'Aur' , 'Boo' , 'Cae' , 'Cam' , 'Cnc' , 'CVn' , 'CMa' , 'CMi' ,
25+ 'Cap' , 'Car' , 'Cas' , 'Cen' , 'Cep' , 'Cet' , 'Cha' , 'Cir' , 'Col' , 'Com' , 'CrA' , 'CrB' , 'Crv' , 'Crt' , 'Cru' ,
26+ 'Cyg' , 'Del' , 'Dor' , 'Dra' , 'Equ' , 'Eri' , 'For' , 'Gem' , 'Gru' , 'Her' , 'Hor' , 'Hya' , 'Hyi' , 'Ind' , 'Lac' ,
27+ 'Leo' , 'LMi' , 'Lep' , 'Lib' , 'Lup' , 'Lyn' , 'Lyr' , 'Men' , 'Mic' , 'Mon' , 'Mus' , 'Nor' , 'Oct' , 'Oph' , 'Ori' ,
28+ 'Pav' , 'Peg' , 'Per' , 'Phe' , 'Pic' , 'Psc' , 'PsA' , 'Pup' , 'Pyx' , 'Ret' , 'Sge' , 'Sgr' , 'Sco' , 'Scl' , 'Sct' ,
29+ 'Ser' , 'Sex' , 'Tau' , 'Tel' , 'Tri' , 'TrA' , 'Tuc' , 'UMa' , 'UMi' , 'Vel' , 'Vir' , 'Vol' , 'Vul'
30+ ] ;
1731
1832interface LoadObjectsParams {
1933 sort_by ?: string ;
2034 sort_order ?: 'asc' | 'desc' ;
2135 catalog ?: string ;
2236 name ?: string ;
37+ constellation ?: string ;
2338}
2439
2540@Component ( {
@@ -52,6 +67,9 @@ interface LoadObjectsParams {
5267 MatButtonModule ,
5368 MatSelectModule ,
5469 MatCheckboxModule ,
70+ MatAutocompleteModule ,
71+ NgForOf ,
72+ AsyncPipe ,
5573 FormsModule ,
5674 ReactiveFormsModule
5775]
@@ -81,9 +99,14 @@ export class CatalogsComponent implements OnInit, OnDestroy {
8199 private subscriptions : Subscription [ ] = [ ] ;
82100 filterForm : FormGroup ;
83101 isFilterVisible = false ;
102+ filteredConstellations$ : Observable < string [ ] > ;
84103
85104 constructor ( ) {
86105 this . initFilterForm ( ) ;
106+ this . filteredConstellations$ = this . filterForm . get ( 'constellation' ) ! . valueChanges . pipe (
107+ startWith ( '' ) ,
108+ map ( ( v : string | null ) => this . filterConstellations ( v ?? '' ) )
109+ ) ;
87110
88111 // Set initial state for top bar in constructor
89112 setTimeout ( ( ) => {
@@ -97,11 +120,24 @@ export class CatalogsComponent implements OnInit, OnDestroy {
97120
98121 private initFilterForm ( ) {
99122 this . filterForm = this . fb . group ( {
123+ name : [ null ] ,
100124 catalog : [ null ] ,
101- name : [ null ]
125+ constellation : [ null ]
102126 } ) ;
103127 }
104128
129+ private filterConstellations ( value : string ) : string [ ] {
130+ const v = ( value || '' ) . trim ( ) . toLowerCase ( ) ;
131+ if ( ! v ) return CONSTELLATION_ABBREVIATIONS ;
132+ return CONSTELLATION_ABBREVIATIONS . filter ( c => c . toLowerCase ( ) . startsWith ( v ) || c . toLowerCase ( ) . includes ( v ) ) ;
133+ }
134+
135+ /** Prevent native form submit (which would reload and clear fields); apply filters on Enter. */
136+ onFilterSubmit ( event : Event ) {
137+ event . preventDefault ( ) ;
138+ this . applyFilters ( ) ;
139+ }
140+
105141 ngOnInit ( ) {
106142 this . subscriptions . push (
107143 this . catalogsService . getTotalObjects ( ) . subscribe ( total => {
@@ -128,23 +164,28 @@ export class CatalogsComponent implements OnInit, OnDestroy {
128164 } ) ;
129165 }
130166
131- applyFilters ( ) {
132- const filters = this . filterForm . value ;
133- Object . keys ( filters ) . forEach ( key => {
134- if ( filters [ key ] === null || filters [ key ] === '' ) {
135- delete filters [ key ] ;
136- }
137- } ) ;
167+ /** Current filter params from form (non-empty only). Used so pagination/sort keep filters. */
168+ private getFilterParams ( ) : Partial < LoadObjectsParams > {
169+ const v = this . filterForm . value ;
170+ const out : Partial < LoadObjectsParams > = { } ;
171+ if ( v . name != null && v . name !== '' ) out . name = String ( v . name ) . trim ( ) ;
172+ if ( v . catalog != null && v . catalog !== '' ) out . catalog = String ( v . catalog ) . trim ( ) ;
173+ if ( v . constellation != null && v . constellation !== '' ) out . constellation = String ( v . constellation ) . trim ( ) ;
174+ return out ;
175+ }
138176
177+ applyFilters ( ) {
178+ this . currentPage = 1 ;
139179 this . loadObjects ( {
140- ...filters ,
180+ ...this . getFilterParams ( ) ,
141181 sort_by : this . currentSort . sort_by ,
142182 sort_order : this . currentSort . sort_order
143183 } ) ;
144184 }
145185
146186 clearFilters ( ) {
147- this . filterForm . reset ( ) ;
187+ this . filterForm . reset ( { name : null , catalog : null , constellation : null } ) ;
188+ this . currentPage = 1 ;
148189 this . loadObjects ( {
149190 sort_by : this . currentSort . sort_by ,
150191 sort_order : this . currentSort . sort_order
@@ -173,6 +214,7 @@ export class CatalogsComponent implements OnInit, OnDestroy {
173214 this . currentPage = event . pageIndex + 1 ;
174215 this . pageSize = event . pageSize ;
175216 this . loadObjects ( {
217+ ...this . getFilterParams ( ) ,
176218 sort_by : this . currentSort . sort_by ,
177219 sort_order : this . currentSort . sort_order
178220 } ) ;
@@ -185,6 +227,7 @@ export class CatalogsComponent implements OnInit, OnDestroy {
185227 } ;
186228
187229 this . loadObjects ( {
230+ ...this . getFilterParams ( ) ,
188231 sort_by : this . currentSort . sort_by ,
189232 sort_order : this . currentSort . sort_order
190233 } ) ;
0 commit comments