Skip to content

Commit

Permalink
feat(kit): added search function in international phone component
Browse files Browse the repository at this point in the history
  • Loading branch information
Денис Буланов authored and splincode committed Aug 15, 2024
1 parent ef83626 commit 2eab7ad
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 16 deletions.
22 changes: 22 additions & 0 deletions projects/cdk/utils/array/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Returns a new filtered array. The search is performed by the properties specified in the keys.
*
* @param list Array of objects
* @param search Search string
* @param keys Specified keys for search
*
* @returns Filtered array of objects
*/
export const tuiFilterList = <T>(
list: T[] = [],
search = '',
keys: string[] = [],
): T[] => {
search = search.toLowerCase();

return list.filter((item) =>
keys.some((key: string) =>
(item[key as keyof typeof item] as string).toLowerCase().includes(search),
),
);
};
1 change: 1 addition & 0 deletions projects/cdk/utils/array/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './filter';
1 change: 1 addition & 0 deletions projects/cdk/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from '@taiga-ui/cdk/utils/array';
export * from '@taiga-ui/cdk/utils/browser';
export * from '@taiga-ui/cdk/utils/color';
export * from '@taiga-ui/cdk/utils/dom';
Expand Down
3 changes: 3 additions & 0 deletions projects/core/components/data-list/data-list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {NgIf} from '@angular/common';
import type {AfterContentChecked, QueryList} from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChildren,
forwardRef,
Expand Down Expand Up @@ -55,6 +56,7 @@ export class TuiDataListComponent<T>
private origin?: HTMLElement;
private readonly el = tuiInjectElement();

private readonly cdr = inject(ChangeDetectorRef);
protected readonly fallback = toSignal(inject(TUI_NOTHING_FOUND_MESSAGE));
protected empty = true;

Expand Down Expand Up @@ -86,6 +88,7 @@ export class TuiDataListComponent<T>
public ngAfterContentChecked(): void {
// TODO: Refactor to :has after Safari support bumped to 15
this.empty = !this.el.querySelector('[tuiOption]');
this.cdr.markForCheck();
}

public getOptions(includeDisabled = false): readonly T[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<tui-input-phone-international
[countries]="(countries | tuiSortCountries | async) || []"
[tuiCountrySearch]="true"
[(countryIsoCode)]="countryIsoCode"
[(ngModel)]="value"
>
Expand Down
11 changes: 11 additions & 0 deletions projects/i18n/types/country-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type {TuiCountryIsoCode} from './country-iso-code';

/**
* Full information about the country for international phone component
*/
export interface TuiCountry {
isoCode: TuiCountryIsoCode;
flag: string;
name: string;
code: string;
}
1 change: 1 addition & 0 deletions projects/i18n/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './country-iso-code';
export * from './country-type';
export * from './language';
export * from './language-loader';
export * from './language-names';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {
} from '@taiga-ui/core/directives/dropdown';
import {TuiGroup} from '@taiga-ui/core/directives/group';
import {TuiFlagPipe} from '@taiga-ui/core/pipes/flag';
import type {TuiCountryIsoCode} from '@taiga-ui/i18n/types';
import type {TuiCountry, TuiCountryIsoCode} from '@taiga-ui/i18n/types';
import {TuiChevron} from '@taiga-ui/kit/directives';
import {TUI_COUNTRIES} from '@taiga-ui/kit/tokens';
import type {PolymorpheusContent} from '@taiga-ui/polymorpheus';
Expand All @@ -51,6 +51,8 @@ import {from, skip} from 'rxjs';

import {TuiGetCountryCallingCodePipe} from './get-country-calling-code.pipe';
import {TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS} from './input-phone-international.options';
import {TuiInputModule} from '@taiga-ui/legacy';
import {tuiFilterList} from '@taiga-ui/cdk';

const NOT_FORM_CONTROL_SYMBOLS = /[^+\d]/g;

Expand All @@ -68,6 +70,7 @@ const NOT_FORM_CONTROL_SYMBOLS = /[^+\d]/g;
TuiGetCountryCallingCodePipe,
TuiGroup,
TuiTextfield,
TuiInputModule,
],
templateUrl: './input-phone-international.template.html',
styleUrls: ['./input-phone-international.style.less'],
Expand All @@ -80,6 +83,8 @@ const NOT_FORM_CONTROL_SYMBOLS = /[^+\d]/g;
limitWidth: 'fixed',
align: 'right',
}),
TuiFlagPipe,
TuiGetCountryCallingCodePipe,
],
hostDirectives: [TuiGroup, TuiDropdownDirective, TuiWithDropdownOpen],
host: {
Expand All @@ -90,13 +95,32 @@ export class TuiInputPhoneInternational extends TuiControl<string> {
@ViewChild(MaskitoDirective, {read: ElementRef})
private readonly input?: ElementRef<HTMLInputElement>;

protected readonly tuiFlagPipe = inject(TuiFlagPipe);
protected readonly tuiGetCountryCallingCodePipe = inject(
TuiGetCountryCallingCodePipe,
);

protected readonly dropdown = tuiDropdown(null);
protected readonly options = inject(TUI_INPUT_PHONE_INTERNATIONAL_OPTIONS);
protected readonly size = inject(TUI_TEXTFIELD_OPTIONS).size;
protected readonly open = tuiDropdownOpen();
protected readonly names = toSignal(inject(TUI_COUNTRIES));
protected readonly metadata = toSignal(from(this.options.metadata));
protected readonly countryIsoCode = signal(this.options.countryIsoCode);

// Full list of countries
protected readonly countriesSignal = computed<TuiCountry[]>(() =>
this.getTuiCountriesFromIsoCodes(this.countries),
);

// Countries list after filtering
protected readonly countriesFilteredSignal = computed(() =>
this.filterList<TuiCountry>(this.countriesSignal(), this.searchStr(), [
'name',
'code',
]),
);

protected readonly mask = computed(() =>
this.computeMask(this.countryIsoCode(), this.metadata()),
);
Expand All @@ -106,11 +130,20 @@ export class TuiInputPhoneInternational extends TuiControl<string> {
@Input()
public countries = this.options.countries;

@Input()
public tuiCountrySearch = false;

@Output()
public readonly countryIsoCodeChange = toObservable(this.countryIsoCode).pipe(
skip(1),
);

// Countries filter function
public filterList = tuiFilterList;

// User's search string
public searchStr = signal<string>('');

@Input('countryIsoCode')
public set isoCode(code: TuiCountryIsoCode) {
this.countryIsoCode.set(code);
Expand Down Expand Up @@ -183,6 +216,29 @@ export class TuiInputPhoneInternational extends TuiControl<string> {
this.onChange(unmaskedValue === countryCallingCode ? '' : unmaskedValue);
}

/**
* Returns countries full information
* from ISO codes
*/
private getTuiCountriesFromIsoCodes(
isoCodes: readonly TuiCountryIsoCode[],
): TuiCountry[] {
return isoCodes.map((isoCode) => this.getTuiCountryFromIsoCode(isoCode));
}

/**
* Returns country full information
* from ISO code
*/
private getTuiCountryFromIsoCode(isoCode: TuiCountryIsoCode): TuiCountry {
return {
isoCode,
code: this.tuiGetCountryCallingCodePipe.transform(isoCode, this.metadata()),
flag: this.tuiFlagPipe.transform(isoCode),
name: this.names()?.[isoCode] || '',
};
}

private computeMask(
countryIsoCode: TuiCountryIsoCode,
metadata?: MetadataJson,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@
color: var(--tui-text-secondary);
margin-inline-end: 0.25rem;
}

.t-countries-search {
position: sticky;
top: 0.25rem;
margin: 0.25rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,35 @@
</label>
</tui-textfield>

<tui-data-list *tuiTextfieldDropdown>
<button
*ngFor="let item of countries"
tuiOption
(click)="onItemClick(item)"
<div *tuiTextfieldDropdown>
<tui-input
*ngIf="tuiCountrySearch"
tuiTextfieldIconLeft="@tui.search"
tuiTextfieldSize="m"
class="t-countries-search"
[ngModel]="searchStr()"
(ngModelChange)="searchStr.set($event)"
>
<img
alt=""
class="t-flag"
[src]="item | tuiFlag"
Type country or code
<input
tuiTextfieldLegacy
type="text"
/>
<span class="t-country-item-name">{{ names()?.[item] }}</span>
<span class="t-country-item-code">
{{ item | tuiGetCountryCallingCode: metadata() }}
</span>
</button>
</tui-data-list>
</tui-input>

<tui-data-list>
<button
*ngFor="let item of countriesFilteredSignal()"
tuiOption
(click)="onItemClick(item.isoCode)"
>
<img
alt=""
class="t-flag"
[src]="item.flag"
/>
<span class="t-country-item-name">{{ item.name }}</span>
<span class="t-country-item-code">{{ item.code }}</span>
</button>
</tui-data-list>
</div>

0 comments on commit 2eab7ad

Please sign in to comment.