Skip to content

Commit

Permalink
Story/currency feedback (#1269)
Browse files Browse the repository at this point in the history
* Add form display configuration

* Remove code

* Add currency feedback

* wip

* wip

* final fixes

* Remove unnecessary code

---------

Co-authored-by: Maxim Buur <[email protected]>
  • Loading branch information
sofiaIvarsRitense and mbritense authored Nov 7, 2024
1 parent 473af32 commit 7412b30
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 93 deletions.
14 changes: 10 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
"@ng-bootstrap/ng-bootstrap": "11.0.1",
"@ngx-translate/core": "15.0.0",
"@ngx-translate/http-loader": "8.0.0",
"@tadashi/currency": "3.4.0",
"@types/topojson": "3.2.6",
"@typescript-eslint/eslint-plugin": "8.0.1",
"@typescript-eslint/parser": "8.0.1",
Expand All @@ -249,7 +250,7 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "50.0.0",
"eslint-plugin-prefer-arrow": "1.2.3",
"eslint-plugin-react": "7.35.0",
"eslint-plugin-react": "7.35.0",
"flatpickr": "4.6.13",
"formiojs": "4.19.5",
"hammerjs": "2.0.8",
Expand Down
3 changes: 2 additions & 1 deletion projects/valtimo/components/ng-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"formiojs",
"deepmerge-ts",
"@carbon/charts-angular",
"@types/topojson"
"@types/topojson",
"@tadashi/currency"
]
}
3 changes: 2 additions & 1 deletion projects/valtimo/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"jwt-decode": "4.0.0",
"deepmerge-ts": "7.1.0",
"@carbon/charts-angular": "1.17.3",
"@types/topojson": "3.2.6"
"@types/topojson": "3.2.6",
"@tadashi/currency": "3.4.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2015-2024 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {Components} from 'formiojs';

const TextFieldEditForm = Components.components.textfield.editForm;

export const currencyEditForm = () => {
const editForm = TextFieldEditForm();

const localeSelection = {
type: 'select',
input: true,
key: 'customOptions.currencyLocale',
label: 'Currency locale',
tooltip: 'The locale used for the currency input',
weight: 20,
defaultValue: 'nl-NL',
dataSrc: 'values',
data: {
values: [
{
label: 'Dutch (Netherlands)',
value: 'nl-NL',
},
{
label: 'English (US)',
value: 'en-US',
},
{
label: 'English (UK)',
value: 'en-GB',
},
{
label: 'German',
value: 'de-DE',
},
],
},
};

const currencySelection = {
type: 'select',
input: true,
key: 'customOptions.currencyCurrency',
label: 'Currency',
tooltip: 'The currency used for the currency input',
weight: 20,
defaultValue: 'EUR',
dataSrc: 'values',
data: {
values: [
{
label: 'Euro',
value: 'EUR',
},
{
label: 'British pound',
value: 'GBP',
},
{
label: 'United States Dollar',
value: 'USD',
},
],
},
};

const allowEmptyValueCheckbox = {
type: 'checkbox',
input: true,
key: 'customOptions.allowEmptyValue',
label: 'Allow Empty Value',
tooltip: 'Check to allow empty values for this field',
weight: 10,
defaultValue: false, // Default to false
};

const tabsComponent = editForm.components.find(component => component.key === 'tabs');
if (tabsComponent) {
const displayTab = tabsComponent.components.find(tab => tab.key === 'display');
if (displayTab) {
displayTab.components.unshift(localeSelection);
displayTab.components.unshift(currencySelection);
displayTab.components.unshift(allowEmptyValueCheckbox);
}
}

return editForm;
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
<div class="currencyForm-container" [formGroup]="currencyForm">
<div class="input-group">
<input id="currency" formControlName="currency" class="form-control" placeholder="0.00" />
<div
class="formio-errors invalid-feedback"
*ngIf="
currencyForm.controls.currency.invalid &&
(currencyForm.controls.currency.touched || !currencyForm.controls.currency.pristine) &&
currencyForm.controls?.currency?.errors
"
>
<div class="form-text error">
{{ 'formioTranslations.formioCurrencyComponent.errorMessage' | translate }}
</div>
</div>
<input #currencyElement id="currency" formControlName="currencyValue" class="form-control" />
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import {AfterViewInit, Component, EventEmitter, Input, OnDestroy, Output} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
import {Subscription} from 'rxjs';
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild,
} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
import {FormioCustomComponent} from '../../../modules';
import {currencyValidator} from './currency.validators';
import Currency from '@tadashi/currency';
import {Subscription} from 'rxjs';

/**
* Custom formio component for currency number.
Expand All @@ -12,33 +24,57 @@ import {currencyValidator} from './currency.validators';
templateUrl: './currency.component.html',
})
export class FormIoCurrencyComponent
implements FormioCustomComponent<any>, AfterViewInit, OnDestroy
implements FormioCustomComponent<number>, OnInit, AfterViewInit, OnChanges, OnDestroy
{
@Input() public value: string;
@Input() public disabled = false;
@Input() public required = false;
@Output() public valueChange = new EventEmitter<any>();
public currencyForm = new FormGroup({
currency: new FormControl(''),
@ViewChild('currencyElement') currencyElement!: ElementRef<HTMLInputElement>;

public readonly currencyForm = new FormGroup({
currencyValue: new FormControl<string>(''),
});
private readonly _subscriptions = new Subscription();

public ngAfterViewInit(): void {
this.currencyForm.controls.currency.setValue(this.value);
this.currencyForm.controls.currency.setValidators(
this.required ? [Validators.required, currencyValidator()] : [currencyValidator()]
);
this.currencyForm.controls.currency.updateValueAndValidity();
private _value: number | null = null;

if (this.disabled) {
Object.keys(this.currencyForm.controls).forEach(key => {
this.currencyForm?.get(key)?.disable();
});
public get value(): number | null {
return this._value;
}

@Input() public set value(value: number) {
this._value = value;
this.currencyForm.setValue({
currencyValue: Currency.masking(value, this._currencyInstance.opts.maskOpts),
});
}

@Output() public readonly valueChange = new EventEmitter<number>();

@Input() public set disabled(value: boolean) {
if (value) {
this.currencyForm.disable();
} else {
this.currencyForm.enable();
}
}

@Input() public readonly currencyLocale: string;
@Input() public readonly currencyCurrency: string;
@Input() public readonly allowEmptyValue: boolean;

private _currencyInstance!: Currency;

private readonly _subscriptions = new Subscription();

public ngOnInit(): void {
this._subscriptions.add(
this.currencyForm.valueChanges.subscribe(() => {
this.onValueChange();
const unmasked = this._currencyInstance.getUnmasked(this.currencyForm.value.currencyValue);

if (unmasked === 0 && this.allowEmptyValue) {
this._value = null;
this.valueChange.emit(null);
} else {
this._value = unmasked;
this.valueChange.emit(unmasked);
}
})
);
}
Expand All @@ -47,10 +83,40 @@ export class FormIoCurrencyComponent
this._subscriptions.unsubscribe();
}

private onValueChange(): void {
(this.value as any) = this.currencyForm.valid
? this.currencyForm.controls.currency.value
: this.currencyForm.value;
this.valueChange.emit(this.value);
public ngAfterViewInit(): void {
this._currencyInstance = new Currency(this.currencyElement.nativeElement, {
maskOpts: {
empty: this.allowEmptyValue || false,
locales: this.currencyLocale || 'nl-NL',
digits: 2,
options: {
style: 'currency',
currency: this.currencyCurrency || 'EUR',
},
},
});
}

public ngOnChanges(changes: SimpleChanges): void {
if (changes.currencyLocale || changes.currencyCurrency || changes.allowEmptyValue) {
if (typeof this.currencyLocale === 'string') {
this._currencyInstance.opts.maskOpts.locales = this.currencyLocale;
}

if (typeof this.currencyCurrency === 'string') {
this._currencyInstance.opts.maskOpts.options.currency = this.currencyCurrency;
}

if (typeof this.allowEmptyValue === 'boolean') {
this._currencyInstance.opts.maskOpts.empty = this.allowEmptyValue;
}

this.currencyForm.setValue({
currencyValue:
typeof this._value === 'number'
? Currency.masking(this._value, this._currencyInstance.opts.maskOpts)
: '',
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Injector} from '@angular/core';
import {FormIoCurrencyComponent} from './currency.component';
import {FormioCustomComponentInfo, registerCustomFormioComponent} from '../../../modules';
import {currencyEditForm} from './currency-edit-form';

const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
type: 'currency',
Expand All @@ -17,6 +18,7 @@ const COMPONENT_OPTIONS: FormioCustomComponentInfo = {
required: true,
},
},
editForm: currencyEditForm,
};

export function registerFormioCurrencyComponent(injector: Injector) {
Expand Down
Loading

0 comments on commit 7412b30

Please sign in to comment.