Browse Source

Reuse currency selector

pull/7584/head
Thomas Kaul 3 weeks ago
parent
commit
b1d85c5735
  1. 11
      apps/client/src/app/components/user-account-settings/user-account-settings.component.ts
  2. 2
      apps/client/src/app/components/user-account-settings/user-account-settings.html
  3. 29
      libs/ui/src/lib/currency-selector/currency-selector.component.ts
  4. 20
      libs/ui/src/lib/shared/abstract-mat-form-field.ts

11
apps/client/src/app/components/user-account-settings/user-account-settings.component.ts

@ -32,7 +32,6 @@ import {
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { import {
NonNullableFormBuilder, NonNullableFormBuilder,
FormBuilder,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
Validators Validators
@ -80,9 +79,8 @@ import { catchError } from 'rxjs/operators';
}) })
export class GfUserAccountSettingsComponent implements OnInit { export class GfUserAccountSettingsComponent implements OnInit {
protected readonly appearancePlaceholder = $localize`Auto`; protected readonly appearancePlaceholder = $localize`Auto`;
protected readonly baseCurrency: string; protected readonly baseCurrencyForm = inject(NonNullableFormBuilder).group({
protected readonly baseCurrencyForm = inject(FormBuilder).group({ baseCurrency: [{ value: '', disabled: true }]
baseCurrency: ['']
}); });
protected closeUserAccountMailHref: string; protected closeUserAccountMailHref: string;
protected readonly currencies: string[] = []; protected readonly currencies: string[] = [];
@ -135,9 +133,8 @@ export class GfUserAccountSettingsComponent implements OnInit {
private readonly webAuthnService = inject(WebAuthnService); private readonly webAuthnService = inject(WebAuthnService);
public constructor() { public constructor() {
const { baseCurrency, currencies } = this.dataService.fetchInfo(); const { currencies } = this.dataService.fetchInfo();
this.baseCurrency = baseCurrency;
this.currencies = currencies; this.currencies = currencies;
this.userService.stateChanged this.userService.stateChanged
@ -189,7 +186,7 @@ export class GfUserAccountSettingsComponent implements OnInit {
); );
this.baseCurrencyForm.setValue( this.baseCurrencyForm.setValue(
{ baseCurrency: this.user.settings.baseCurrency ?? null }, { baseCurrency: this.user.settings.baseCurrency ?? '' },
{ emitEvent: false } { emitEvent: false }
); );

2
apps/client/src/app/components/user-account-settings/user-account-settings.html

@ -3,7 +3,7 @@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="d-flex py-1"> <div class="d-flex py-1">
<form #changeUserSettingsForm="ngForm" class="w-100"> <form class="w-100">
<div class="d-flex mb-2"> <div class="d-flex mb-2">
<div class="align-items-center d-flex pt-1 pt-1 w-50"> <div class="align-items-center d-flex pt-1 pt-1 w-50">
<ng-container i18n>Base Currency</ng-container> <ng-container i18n>Base Currency</ng-container>

29
libs/ui/src/lib/currency-selector/currency-selector.component.ts

@ -30,6 +30,7 @@ import {
MatAutocomplete, MatAutocomplete,
MatAutocompleteModule, MatAutocompleteModule,
MatAutocompleteOrigin, MatAutocompleteOrigin,
MatAutocompleteTrigger,
MatOption MatOption
} from '@angular/material/autocomplete'; } from '@angular/material/autocomplete';
import { import {
@ -78,9 +79,13 @@ export class GfCurrencySelectorComponent
public filteredCurrencies: string[] = []; public filteredCurrencies: string[] = [];
public readonly formControlName = input.required<string>(); public readonly formControlName = input.required<string>();
private readonly autocompleteTrigger = viewChild.required(
MatAutocompleteTrigger
);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly formField = inject(MAT_FORM_FIELD); private readonly formField = inject(MAT_FORM_FIELD);
private readonly input = viewChild.required(MatInput); private readonly input = viewChild.required(MatInput);
private lastSelectedCurrency: string | null = null;
public constructor( public constructor(
public override readonly _elementRef: ElementRef, public override readonly _elementRef: ElementRef,
@ -113,6 +118,8 @@ export class GfCurrencySelectorComponent
public override set value(value: string | null) { public override set value(value: string | null) {
this.control.setValue(value); this.control.setValue(value);
super.value = value; super.value = value;
this.lastSelectedCurrency = value;
} }
public focus() { public focus() {
@ -170,16 +177,24 @@ export class GfCurrencySelectorComponent
} }
} }
public override onBlur() {
// Typing clears the selected currency, so restore the last selection once
// the user leaves the field without picking an option. The panel is still
// open while an option is being clicked, in which case the selection
// itself provides the new value.
if (!super.value && !this.autocompleteTrigger().panelOpen) {
this.value = this.lastSelectedCurrency;
this.changeDetectorRef.markForCheck();
}
super.onBlur();
}
public onUpdateCurrency({ option }: { option: MatOption<string> }) { public onUpdateCurrency({ option }: { option: MatOption<string> }) {
super.value = option.value; super.value = option.value;
}
public setDisabledState(isDisabled: boolean) { this.lastSelectedCurrency = option.value;
if (isDisabled) {
this.control.disable({ emitEvent: false });
} else {
this.control.enable({ emitEvent: false });
}
} }
private filter(value: string) { private filter(value: string) {

20
libs/ui/src/lib/shared/abstract-mat-form-field.ts

@ -9,7 +9,12 @@ import {
Input, Input,
OnDestroy OnDestroy
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, NgControl, Validators } from '@angular/forms'; import {
ControlValueAccessor,
FormControl,
NgControl,
Validators
} from '@angular/forms';
import { MatFormFieldControl } from '@angular/material/form-field'; import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -23,6 +28,7 @@ export abstract class AbstractMatFormField<T>
@HostBinding('attr.aria-describedBy') public describedBy = ''; @HostBinding('attr.aria-describedBy') public describedBy = '';
public readonly autofilled: boolean; public readonly autofilled: boolean;
public abstract readonly control: FormControl;
public errorState: boolean; public errorState: boolean;
public focused = false; public focused = false;
public readonly stateChanges = new Subject<void>(); public readonly stateChanges = new Subject<void>();
@ -156,6 +162,18 @@ export abstract class AbstractMatFormField<T>
this.describedBy = ids.join(' '); this.describedBy = ids.join(' ');
} }
public setDisabledState(isDisabled: boolean) {
if (isDisabled) {
this.control.disable({ emitEvent: false });
} else {
this.control.enable({ emitEvent: false });
}
this.disabled = isDisabled;
this.stateChanges.next();
}
public writeValue(value: T) { public writeValue(value: T) {
this.value = value; this.value = value;
} }

Loading…
Cancel
Save