create amountPercentage field component
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
<uikit-field
|
||||
[label]="preparedLabel()"
|
||||
[name]="name"
|
||||
[control]="selectedType.value === 'amount' ? amountControl : percentageControl"
|
||||
[showLabel]="!!label"
|
||||
[showErrors]="showErrors"
|
||||
>
|
||||
<p-inputgroup>
|
||||
@if (selectedType.value === "amount") {
|
||||
<p-inputnumber
|
||||
[attr.id]="name"
|
||||
[formControl]="amountControl"
|
||||
[attr.name]="name"
|
||||
[attr.placeholder]="placeholder"
|
||||
[attr.autocomplete]="autocomplete"
|
||||
[class]="
|
||||
` w-full hasSuffix text-left ${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`
|
||||
"
|
||||
dir="ltr"
|
||||
[required]="required"
|
||||
[invalid]="amountControl.invalid && (amountControl.touched || amountControl.dirty)"
|
||||
(onInput)="onPriceInput($event)"
|
||||
/>
|
||||
<!-- (input)="onInput($event)" -->
|
||||
} @else {
|
||||
<input
|
||||
pInputText
|
||||
[attr.id]="name"
|
||||
[formControl]="percentageControl"
|
||||
[attr.name]="name"
|
||||
[attr.placeholder]="placeholder"
|
||||
[attr.inputmode]="'numeric'"
|
||||
[attr.type]="'number'"
|
||||
dir="ltr"
|
||||
[attr.autocomplete]="autocomplete"
|
||||
[classList]="
|
||||
[
|
||||
'w-full hasSuffix text-left',
|
||||
`${size === 'large' ? 'p-inputtext-lg' : size === 'small' ? 'p-inputtext-sm' : ''}`,
|
||||
].join(' ')
|
||||
"
|
||||
[required]="required"
|
||||
[invalid]="percentageControl.invalid && (percentageControl.touched || percentageControl.dirty)"
|
||||
(input)="onInput($event)"
|
||||
/>
|
||||
}
|
||||
|
||||
<p-inputgroup-addon class="py-0!">
|
||||
<p-select
|
||||
[formControl]="selectedType"
|
||||
[options]="typeSelectOptions"
|
||||
optionValue="value"
|
||||
size="small"
|
||||
class="border-0!"
|
||||
/>
|
||||
</p-inputgroup-addon>
|
||||
</p-inputgroup>
|
||||
@if (hint) {
|
||||
<small [attr.id]="name + '-help'">{{ hint }}</small>
|
||||
}
|
||||
</uikit-field>
|
||||
@@ -0,0 +1,148 @@
|
||||
import { Maybe } from '@/core';
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { UikitFieldComponent } from '@/uikit';
|
||||
import { formatWithCurrency } from '@/utils';
|
||||
import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputGroup } from 'primeng/inputgroup';
|
||||
import { InputGroupAddon } from 'primeng/inputgroupaddon';
|
||||
import { InputNumberInputEvent, InputNumberModule } from 'primeng/inputnumber';
|
||||
import { InputText } from 'primeng/inputtext';
|
||||
import { Select } from 'primeng/select';
|
||||
|
||||
type TAmountPercentageInput = 'amount' | 'percentage';
|
||||
interface IAmountPercentageInputSelect {
|
||||
label: string;
|
||||
value: TAmountPercentageInput;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-amount-percentage-input',
|
||||
templateUrl: './amount-percentage-input.component.html',
|
||||
imports: [
|
||||
UikitFieldComponent,
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
ReactiveFormsModule,
|
||||
InputText,
|
||||
Select,
|
||||
InputNumberModule,
|
||||
],
|
||||
})
|
||||
export class AmountPercentageInputComponent {
|
||||
@Input() defaultType: TAmountPercentageInput = 'percentage';
|
||||
@Input({ required: true }) percentageControl!: FormControl<Maybe<any>>;
|
||||
@Input({ required: true }) amountControl!: FormControl<Maybe<any>>;
|
||||
@Input({ required: true }) name!: string;
|
||||
@Input({ required: true }) baseAmount!: number;
|
||||
@Input() label: string = '';
|
||||
@Input() placeholder?: string;
|
||||
@Input() required = false;
|
||||
@Input() disabled = false;
|
||||
@Input() size?: 'small' | 'large';
|
||||
@Input() autocomplete?: string = 'off';
|
||||
@Input() showErrors = true;
|
||||
@Input() hint?: string;
|
||||
@Input() isLtrInput = false;
|
||||
@Input() minAmount?: number = 0;
|
||||
@Input() maxAmount?: number;
|
||||
@Input() minPercentage?: number = 0;
|
||||
@Input() maxPercentage?: number = 100;
|
||||
@Output() valueChange = new EventEmitter<string | number>();
|
||||
@Output() onTypeChange = new EventEmitter<TAmountPercentageInput>();
|
||||
@Output() blur = new EventEmitter<void>();
|
||||
|
||||
private readonly toastService = inject(ToastService);
|
||||
|
||||
readonly typeSelectOptions: IAmountPercentageInputSelect[] = [
|
||||
{
|
||||
label: 'قیمت',
|
||||
value: 'amount',
|
||||
},
|
||||
{
|
||||
label: 'درصد',
|
||||
value: 'percentage',
|
||||
},
|
||||
];
|
||||
|
||||
selectedType = new FormControl({
|
||||
value: this.defaultType,
|
||||
disabled: false,
|
||||
});
|
||||
|
||||
preparedLabel = signal(this.label);
|
||||
|
||||
onPriceInput($event: InputNumberInputEvent) {
|
||||
this.onInput($event.originalEvent, true);
|
||||
}
|
||||
|
||||
onInput($event: Event, isPriceType: boolean = false) {
|
||||
// @ts-ignore
|
||||
let value = $event.target.value as string;
|
||||
if (isPriceType) {
|
||||
value = value.replace(/,/g, '');
|
||||
}
|
||||
|
||||
this.modifyControlsData(value);
|
||||
}
|
||||
|
||||
private modifyControlsData(value: string) {
|
||||
let newValue = parseFloat(value);
|
||||
const isPercentageType = this.selectedType.value === 'percentage';
|
||||
|
||||
const min = (isPercentageType ? this.minPercentage : this.minAmount) || 0;
|
||||
const max = (isPercentageType ? this.maxPercentage : this.maxAmount) || Number.MAX_SAFE_INTEGER;
|
||||
|
||||
const maxValidator = max < newValue;
|
||||
const minValidator = min > newValue;
|
||||
|
||||
if (minValidator || maxValidator) {
|
||||
if (minValidator) {
|
||||
this.toastService.warn({
|
||||
text: `مقدار فیلد باید بیشتر از ${min} باشد.`,
|
||||
});
|
||||
}
|
||||
if (maxValidator) {
|
||||
this.toastService.warn({
|
||||
text: `مقدار فیلد باید کمتر از ${max} باشد.`,
|
||||
});
|
||||
}
|
||||
|
||||
newValue = parseFloat(value.slice(0, value.length - 1));
|
||||
if (newValue < 0 || isNaN(newValue)) {
|
||||
newValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPercentageType) {
|
||||
const amountValue = (this.baseAmount * newValue) / 100;
|
||||
this.amountControl.setValue(amountValue);
|
||||
} else {
|
||||
const percentageValue = (newValue / this.baseAmount) * 100;
|
||||
|
||||
this.percentageControl.setValue(percentageValue);
|
||||
this.preparedLabel.set(`${this.label} (${percentageValue} درصد)`);
|
||||
// @ts-ignore
|
||||
// $event.target.value = newValue;
|
||||
}
|
||||
this.preparedLabel.set(this.prepareLabel(isPercentageType));
|
||||
}
|
||||
|
||||
private prepareLabel(isPercentageType: boolean) {
|
||||
if (isPercentageType) {
|
||||
return `${this.label} (${formatWithCurrency(this.amountControl.value || 0)})`;
|
||||
}
|
||||
return `${this.label} (${this.percentageControl.value || 0} درصد)`;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const isPercentageType = this.selectedType.value === 'percentage';
|
||||
this.modifyControlsData(
|
||||
isPercentageType ? this.percentageControl.value : this.amountControl.value,
|
||||
);
|
||||
|
||||
this.selectedType.valueChanges.subscribe((value) => {
|
||||
this.preparedLabel.set(this.prepareLabel(value === 'percentage'));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user