Skip to content

Commit

Permalink
fix: improves country selector
Browse files Browse the repository at this point in the history
  • Loading branch information
trakhimenok committed Nov 10, 2024
1 parent 000578f commit 94b5478
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 83 deletions.
9 changes: 8 additions & 1 deletion libs/components/src/lib/country-selector/countries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type GeoRegion =
| 'Europe'
| 'Asia'
| 'Americas'
| 'South America'
| 'North America'
| 'Pacific Ocean'
Expand All @@ -11,6 +12,8 @@ export interface ICountry {
id3: string;
geoRegions: GeoRegion[];
title: string;
longTitle?: string;
shortTitle?: string;
emoji: string;
}

Expand Down Expand Up @@ -726,7 +729,8 @@ export const countriesByID: Record<string, ICountry> = {
id: 'IR',
id3: 'IRN',
geoRegions: ['Asia'],
title: 'Iran (Islamic Republic of)',
title: 'Iran',
longTitle: 'Iran (Islamic Republic of)',
emoji: '🇮🇷',
},
IQ: {
Expand All @@ -741,6 +745,7 @@ export const countriesByID: Record<string, ICountry> = {
id3: 'IRL',
geoRegions: ['Europe'],
title: 'Ireland',
longTitle: 'Ireland (Republic of)',
emoji: '🇮🇪',
},
IM: {
Expand Down Expand Up @@ -1637,13 +1642,15 @@ export const countriesByID: Record<string, ICountry> = {
id3: 'GBR',
geoRegions: ['Europe'],
title: 'United Kingdom',
shortTitle: 'UK',
emoji: '🇬🇧',
},
US: {
id: 'US',
id3: 'USA',
geoRegions: ['North America'],
title: 'United States of America',
shortTitle: 'USA',
emoji: '🇺🇸',
},
UM: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,24 @@
[isFilterable]="true"
[filterLabel]="label"
[label]="label"
[items]="countries"
[items]="countries()"
[(ngModel)]="countryID"
(ngModelChange)="onChanged()"
[isReadonly]="readonly"
[filterItem]="filterCountryByCode"
(filterChanged)="onFilterChanged($event)"
labelPlacement="start"
>
<ion-segment
*ngIf="!countryID"
*ngIf="!countryID && !filter()"
[(ngModel)]="geoRegion"
(ionChange)="onRegionChanged()"
(ionChange)="onRegionChanged($event)"
>
<ion-segment-button value="All">All</ion-segment-button>
<ion-segment-button value="Europe"
><small>Europe</small></ion-segment-button
>
<ion-segment-button value="Asia"><small>Asia</small></ion-segment-button>
<ion-segment-button value="Americas"
><small>Americas</small></ion-segment-button
>
<ion-segment-button value="Africa"
><small>Africa</small></ion-segment-button
>
<ion-segment-button value="Pacific Ocean"
><small>Pacific</small></ion-segment-button
>
@for (region of geoRegions; track region.id) {
<ion-segment-button [value]="region.id">
<small>{{ region.id }}</small>
</ion-segment-button>
}
</ion-segment>
</sneat-select-from-list>
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { CommonModule } from '@angular/common';
import {
Component,
computed,
EventEmitter,
Input,
OnChanges,
Output,
signal,
SimpleChanges,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
Expand All @@ -20,6 +22,16 @@ import { countries, GeoRegion, ICountry, unknownCountry } from './countries';
imports: [CommonModule, FormsModule, IonicModule, SelectFromListModule],
})
export class CountrySelectorComponent implements OnChanges {
protected readonly geoRegions: readonly {
readonly id: string;
title?: string;
}[] = [
{ id: 'Europe' },
{ id: 'Asia' },
{ id: 'Americas' },
{ id: 'Africa' },
];

@Input({ required: true }) countryID?: string;

@Input() defaultCountryID?: string;
Expand All @@ -31,41 +43,58 @@ export class CountrySelectorComponent implements OnChanges {

@Output() readonly countryIDChange = new EventEmitter<string>();

constructor() {
this.setCountries();
}
protected readonly filter = signal<string>('');
protected readonly geoRegion = signal<GeoRegion | 'All' | 'Americas'>('All');

protected countries: ISelectItem[] = countries;
protected readonly countries = computed<readonly ISelectItem[]>(() => {
const filter = this.filter();
let countriesToShow: readonly ISelectItem[] = filter
? countries
: countries.filter(
(c) =>
this.geoRegion() === 'All' ||
(this.geoRegion() === 'Americas' &&
(c.geoRegions.includes('North America') ||
c.geoRegions.includes('South America'))) ||
c.geoRegions.includes(this.geoRegion() as GeoRegion),
);
if (this.canBeUnknown) {
countriesToShow = [...countriesToShow, unknownCountry];
}
return countriesToShow;
});

protected geoRegion: GeoRegion | 'All' | 'Americas' = 'All';
private geoRegions: (GeoRegion | 'All')[] = ['All'];
// protected geoRegion: GeoRegion | 'All' | 'Americas' = 'All';

onChanged(): void {
protected onChanged(): void {
console.log('CountrySelectorComponent.onChanged()', this.countryID);
this.countryIDChange.emit(this.countryID);
}

protected onFilterChanged(filter: string): void {
console.log('CountrySelectorComponent.onFilterChanged()', filter);
this.filter.set(filter);
}

ngOnChanges(changes: SimpleChanges): void {
if (changes['canBeUnknown']) {
this.countries = this.canBeUnknown
? [...countries, unknownCountry]
: countries;
}
if (changes['country'] && this.countryID === '--' && !this.canBeUnknown) {
this.countryID = undefined;
}
if (
changes['defaultCountryID'] &&
!this.countryID &&
this.defaultCountryID
this.defaultCountryID &&
this.defaultCountryID !== '--'
) {
this.countryID = this.defaultCountryID;
this.countryIDChange.emit(this.countryID);
}
}

onRegionChanged(): void {
this.setCountries();
protected onRegionChanged(event: Event): void {
this.geoRegion.set(
(event as CustomEvent).detail.value as GeoRegion | 'All' | 'Americas',
);
}

protected readonly filterCountryByCode = (
Expand All @@ -76,15 +105,4 @@ export class CountrySelectorComponent implements OnChanges {
const c = item as ICountry;
return c.id === f || c.id3.startsWith(f);
};

private setCountries(): void {
this.countries = countries.filter(
(c) =>
this.geoRegion === 'All' ||
(this.geoRegion === 'Americas' &&
(c.geoRegions.includes('North America') ||
c.geoRegions.includes('South America'))) ||
c.geoRegions.includes(this.geoRegion as GeoRegion),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
>
@for (item of items; track item.id) {
<ion-select-option [value]="item.id">
{{ item.emoji }} {{ item.title }}
{{ item.emoji }}
@if (item.shortTitle) {
{{ item.shortTitle }}
} @else {
{{ item.title }}
}
</ion-select-option>
}<!-- <ion-select-option value="other">OTHER</ion-select-option>-->
}
<!-- <ion-select-option value="other">OTHER</ion-select-option>-->
</ion-select>
<ion-buttons slot="end" class="ion-no-margin" *ngIf="!isReadonly">
<ion-button color="medium" title="Deselect" (click)="deselect()">
Expand Down Expand Up @@ -59,10 +65,7 @@
</ion-item-divider>
<ion-item-group *ngIf="!labelPlacement">
@for (item of displayItems; track item.id) {
<ion-item
button
(click)="select(item)"
>
<ion-item button (click)="select(item)">
<ion-icon
slot="start"
*ngIf="item.iconName"
Expand All @@ -86,30 +89,37 @@
<ion-label>{{ label }}</ion-label>
</ion-item>
@for (item of displayItems; track item.id) {
<ion-item
lines="full"
(click)="select(item)"
tappable="true"
>
<ion-item lines="full" (click)="select(item)" tappable="true">
<ion-radio
[value]="item.id"
[labelPlacement]="labelPlacement"
[justify]="
justify ||
(!labelPlacement || labelPlacement === 'start'
? 'space-between'
: 'start')
"
justify ||
(!labelPlacement || labelPlacement === 'start'
? 'space-between'
: 'start')
"
>
{{ item.emoji }} {{ item.title }}
{{ item.emoji }}
@if (item.shortTitle) {
@if (item.longTitle) {
{{ item.longTitle }} - {{ item.shortTitle }}
} @else {
{{ item.title }} - {{ item.shortTitle }}
}
} @else if (item.longTitle) {
{{ item.longTitle }}
} @else {
{{ item.title }}
}
</ion-radio>
</ion-item>
}
</ion-list>
</ion-radio-group>
<ion-item-divider *ngIf="hiddenCount">
<ion-label color="medium"
>{{ hiddenCount }} out of {{ items?.length }} items are hidden by filter
>{{ hiddenCount }} out of {{ items?.length }} items are hidden by filter
</ion-label>
<ion-buttons slot="end">
<ion-button (click)="clearFilter()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export class SelectFromListComponent
@Input() canAdd = false;
@Input() filterItem?: (item: ISelectItem, filter: string) => boolean;

@Output() readonly filterChanged = new EventEmitter<string>();

@Input() isReadonly = false;

// @Input() ngModel?: string;
Expand Down Expand Up @@ -110,6 +112,7 @@ export class SelectFromListComponent
? this.items?.filter(
(v) =>
v.title.toLowerCase().includes(f) ||
v.longTitle?.toLowerCase().includes(f) ||
(this.filterItem && this.filterItem(v, f)),
)
: this.items;
Expand All @@ -121,11 +124,13 @@ export class SelectFromListComponent
console.log('SelectFromListComponent.select()', item);
this.value = item.id;
this.onChange(this.value);
this.clearFilter();
}

protected onRadioChanged(event: Event): void {
this.value = (event as CustomEvent).detail['value'] as string;
this.onChange(this.value);
this.clearFilter();
}

protected onSelectChanged(event: Event): void {
Expand Down Expand Up @@ -161,10 +166,12 @@ export class SelectFromListComponent
protected onFilterChanged(): void {
console.log('SelectFromListComponent.onFilterChanged()', this.filter);
this.applyFilter();
this.filterChanged.emit(this.filter);
}

protected clearFilter(): void {
this.filter = '';
this.filterChanged.emit(this.filter);
this.applyFilter();
}

Expand Down
2 changes: 2 additions & 0 deletions libs/components/src/lib/selector/selector-interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export interface ISelectItem {
readonly id: string;
readonly title: string;
readonly shortTitle?: string;
readonly longTitle?: string;
readonly emoji?: string;
readonly iconName?: string;
readonly labelColor?:
Expand Down
Loading

0 comments on commit 94b5478

Please sign in to comment.