diff --git a/libs/ui/src/lib/currency-selector/currency-selector.component.html b/libs/ui/src/lib/currency-selector/currency-selector.component.html
new file mode 100644
index 000000000..29aef16f2
--- /dev/null
+++ b/libs/ui/src/lib/currency-selector/currency-selector.component.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+ {{ currencyItem.label }}
+
+
+
+
diff --git a/libs/ui/src/lib/currency-selector/currency-selector.component.scss b/libs/ui/src/lib/currency-selector/currency-selector.component.scss
new file mode 100644
index 000000000..71c06f26e
--- /dev/null
+++ b/libs/ui/src/lib/currency-selector/currency-selector.component.scss
@@ -0,0 +1,8 @@
+:host {
+ display: block;
+
+ .mat-mdc-progress-spinner {
+ right: 0;
+ top: calc(50% - 10px);
+ }
+}
diff --git a/libs/ui/src/lib/currency-selector/currency-selector.component.ts b/libs/ui/src/lib/currency-selector/currency-selector.component.ts
new file mode 100644
index 000000000..8c05600ce
--- /dev/null
+++ b/libs/ui/src/lib/currency-selector/currency-selector.component.ts
@@ -0,0 +1,154 @@
+import { FocusMonitor } from '@angular/cdk/a11y';
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ Input,
+ OnDestroy,
+ OnInit,
+ ViewChild
+} from '@angular/core';
+import { FormControl, NgControl } from '@angular/forms';
+import {
+ MatAutocomplete,
+ MatAutocompleteSelectedEvent
+} from '@angular/material/autocomplete';
+import { MatFormFieldControl } from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { Subject, of, tap } from 'rxjs';
+import {
+ debounceTime,
+ distinctUntilChanged,
+ switchMap,
+ takeUntil
+} from 'rxjs/operators';
+import { Currency } from '@ghostfolio/common/interfaces/currency.interface';
+import { AbstractMatFormField } from '../symbol-autocomplete/abstract-mat-form-field';
+
+@Component({
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {
+ '[attr.aria-describedBy]': 'describedBy',
+ '[id]': 'id'
+ },
+ selector: 'gf-currency-autocomplete',
+ styleUrls: ['./currency-selector.component.scss'],
+ templateUrl: 'currency-selector.component.html',
+ providers: [
+ {
+ provide: MatFormFieldControl,
+ useExisting: CurrencySelectorComponent
+ }
+ ]
+})
+export class CurrencySelectorComponent
+ extends AbstractMatFormField
+ implements OnInit, OnDestroy
+{
+ @Input() private currencies: Currency[] = [];
+ @Input() public isLoading = false;
+
+ @ViewChild(MatInput, { static: false }) private input: MatInput;
+
+ @ViewChild('currencyAutocomplete')
+ public currencyAutocomplete: MatAutocomplete;
+
+ public control = new FormControl();
+ public filteredCurrencies: Currency[] = [];
+
+ private unsubscribeSubject = new Subject();
+
+ public constructor(
+ public readonly _elementRef: ElementRef,
+ public readonly _focusMonitor: FocusMonitor,
+ public readonly changeDetectorRef: ChangeDetectorRef,
+ public readonly ngControl: NgControl
+ ) {
+ super(_elementRef, _focusMonitor, ngControl);
+
+ this.controlType = 'currency-autocomplete';
+ }
+
+ public ngOnInit() {
+ if (this.disabled) {
+ this.control.disable();
+ }
+
+ this.control.valueChanges
+ .pipe(
+ debounceTime(400),
+ distinctUntilChanged(),
+ takeUntil(this.unsubscribeSubject),
+ tap(() => {
+ this.isLoading = true;
+
+ this.changeDetectorRef.markForCheck();
+ }),
+ switchMap((query) => {
+ return of(
+ this.currencies.filter((currency) =>
+ currency.label.toLowerCase().includes(query?.toLowerCase() || '')
+ ) || []
+ );
+ })
+ )
+ .subscribe((filteredCurrencies: Currency[]) => {
+ this.filteredCurrencies = filteredCurrencies;
+
+ this.isLoading = false;
+
+ this.changeDetectorRef.markForCheck();
+ });
+ }
+
+ public displayFn(currency: string) {
+ return currency;
+ }
+
+ public get empty() {
+ return this.input?.empty;
+ }
+
+ public focus() {
+ this.input.focus();
+ }
+
+ public ngDoCheck() {
+ if (this.ngControl) {
+ this.validateRequired();
+ this.errorState = this.ngControl.invalid && this.ngControl.touched;
+ this.stateChanges.next();
+ }
+ }
+
+ public onUpdateCurrency(event: MatAutocompleteSelectedEvent) {
+ super.value = {
+ value: event.option.value
+ } as Currency;
+ }
+
+ public set value(value: Currency) {
+ this.control.setValue(value);
+ super.value = value;
+ }
+
+ public ngOnDestroy() {
+ super.ngOnDestroy();
+
+ this.unsubscribeSubject.next();
+ this.unsubscribeSubject.complete();
+ }
+
+ private validateRequired() {
+ const requiredCheck = super.required
+ ? !super.value?.value &&
+ !this.currencies
+ .map((currency) => currency.value)
+ .includes(super.value.value)
+ : false;
+ if (requiredCheck) {
+ this.ngControl.control.setErrors({ invalidData: true });
+ }
+ }
+}
diff --git a/libs/ui/src/lib/currency-selector/currency-selector.module.ts b/libs/ui/src/lib/currency-selector/currency-selector.module.ts
new file mode 100644
index 000000000..20de0ad15
--- /dev/null
+++ b/libs/ui/src/lib/currency-selector/currency-selector.module.ts
@@ -0,0 +1,24 @@
+import { CommonModule } from '@angular/common';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { CurrencySelectorComponent } from './currency-selector.component';
+
+@NgModule({
+ declarations: [CurrencySelectorComponent],
+ exports: [CurrencySelectorComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ MatAutocompleteModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatProgressSpinnerModule,
+ ReactiveFormsModule
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+})
+export class GfCurrencyAutocompleteModule {}