Skip to content

Commit

Permalink
feat: introduce Formly number field (quantity input with + and - butt…
Browse files Browse the repository at this point in the history
…ons)
  • Loading branch information
shauke committed Jun 24, 2024
1 parent d2e6c82 commit 9d3bac5
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 17 deletions.
35 changes: 18 additions & 17 deletions docs/guides/formly.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ A configuration for a form containing only a basic input field could be defined
```typescript
const fields: FormlyFieldConfig[] = [
{
type: 'ish-input-field',
type: 'ish-text-input-field',
key: 'example-input',
props: {
required: true,
Expand Down Expand Up @@ -254,22 +254,23 @@ Refer to the tables below for an overview of these parts.
- Template option `inputClass`: These CSS class(es) will be added to all input/select/textarea/text tags.
- Template option `ariaLabel`: Adds an aria-label to all input/select/textarea tags.

| Name | Description | Relevant props |
| --------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ish-text-input-field | Basic input field, supports all text types | `type`: 'text (default),'email','tel','password'. `mask`: input mask for a needed pattern (see [ngx-mask](https://www.npmjs.com/package/ngx-mask) for more information) |
| ish-select-field | Basic select field | `options`: `{ value: any; label: string}[]` or Observable. `placeholder`: Translation key or string for the default selection |
| ish-textarea-field | Basic textarea field | `cols` & `rows`: Specifies the dimensions of the textarea |
| ish-checkbox-field | Basic checkbox input | `title`: Title for a checkbox |
| ish-email-field | Email input field that automatically adds an e-mail validator and error messages | ---- |
| ish-password-field | Password input field that automatically adds a password validator and error messages | ---- |
| ish-phone-field | Phone number input field that automatically adds a phone number validator and error messages | ---- |
| ish-fieldset-field | Wraps fields in a `<fieldset>` tag for styling | `fieldsetClass`: Class that will be added to the fieldset tag. `childClass`: Class that will be added to the child div. `legend`: Legend element that will be added to the fieldset, use the value as the legend text. `legendClass`: Class that will be added to the legend tag. |
| ish-captcha-field | Includes the `<ish-lazy-captcha>` component and adds the relevant `formControls` to the form | `topic`: Topic that will be passed to the Captcha component. |
| ish-radio-field | Basic radio input | ---- |
| ish-plain-text-field | Only display the form value | ---- |
| ish-html-text-field | Only display the form value as html | ---- |
| ish-date-picker-field | Basic datepicker | `minDays`: Computes the minDate by adding the minimum allowed days to today. `maxDays`: Computes the maxDate by adding the maximum allowed days to today. `isSatExcluded`: Specifies if saturdays can be disabled. `isSunExcluded`: Specifies if sundays can be disabled. |
| ish-date-range-picker-field | Datepicker with range | `minDays`: Computes the minDate by adding the minimum allowed days to today. `maxDays`: Computes the maxDate by adding the maximum allowed days to today. `startDate`: The start date. `placeholder`: Placeholder that displays the date format in the input field. |
| Name | Description | Relevant props |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ish-text-input-field | Basic input field, supports all text types | `type`: 'text (default),'email','tel','password'. `mask`: input mask for a needed pattern (see [ngx-mask](https://www.npmjs.com/package/ngx-mask) for more information) |
| ish-select-field | Basic select field | `options`: `{ value: any; label: string}[]` or Observable. `placeholder`: Translation key or string for the default selection |
| ish-textarea-field | Basic textarea field | `cols` & `rows`: Specifies the dimensions of the textarea |
| ish-checkbox-field | Basic checkbox input | `title`: Title for a checkbox |
| ish-email-field | Email input field that automatically adds an e-mail validator and error messages | ---- |
| ish-password-field | Password input field that automatically adds a password validator and error messages | ---- |
| ish-phone-field | Phone number input field that automatically adds a phone number validator and error messages | ---- |
| ish-fieldset-field | Wraps fields in a `<fieldset>` tag for styling | `fieldsetClass`: Class that will be added to the fieldset tag. `childClass`: Class that will be added to the child div. `legend`: Legend element that will be added to the fieldset, use the value as the legend text. `legendClass`: Class that will be added to the legend tag. |
| ish-captcha-field | Includes the `<ish-lazy-captcha>` component and adds the relevant `formControls` to the form | `topic`: Topic that will be passed to the Captcha component. |
| ish-radio-field | Basic radio input | ---- |
| ish-plain-text-field | Only display the form value | ---- |
| ish-html-text-field | Only display the form value as html | ---- |
| ish-date-picker-field | Basic datepicker | `minDays`: Computes the minDate by adding the minimum allowed days to today. `maxDays`: Computes the maxDate by adding the maximum allowed days to today. `isSatExcluded`: Specifies if saturdays can be disabled. `isSunExcluded`: Specifies if sundays can be disabled. |
| ish-date-range-picker-field | Datepicker with range | `minDays`: Computes the minDate by adding the minimum allowed days to today. `maxDays`: Computes the maxDate by adding the maximum allowed days to today. `startDate`: The start date. `placeholder`: Placeholder that displays the date format in the input field. |
| ish-number-field | Basic number input field for smaller Integer numbers, with `+` and `-` buttons (use `ish-text-input-field` with `mask` for larger numbers) | `min`, `max` and `step` input configuration is considered by the in-/decrease buttons |

### Wrappers

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<div class="counter-input">
<button
type="button"
class="btn btn-link decrease-button"
(click)="decrease()"
[disabled]="cannotDecrease"
translate
tabindex="-1"
>
number.decrease.text
</button>
<button
type="button"
class="btn btn-link increase-button"
(click)="increase()"
[disabled]="cannotIncrease"
translate
tabindex="-1"
>
number.increase.text
</button>
<input
type="number"
[formControl]="formControl"
[formlyAttributes]="field"
class="form-control text-center"
[ngClass]="props.inputClass"
[attr.data-testing-id]="field.key"
pattern="[0-9]*"
[min]="props.min"
[max]="props.max"
[step]="props.step"
[attr.aria-label]="props.ariaLabel"
/>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.counter-input {
position: relative;

button {
position: absolute;
width: auto;
margin: 0;
}

.decrease-button {
left: 0;
}

.increase-button {
right: 0;
}

// https://www.w3schools.com/howto/howto_css_hide_arrow_number.asp

/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
margin: 0;
appearance: none;
}

/* Firefox */
input[type='number'] {
appearance: textfield;
}
}
41 changes: 41 additions & 0 deletions src/app/shared/formly/types/number-field/number-field.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { FieldType, FieldTypeConfig } from '@ngx-formly/core';

/**
* Type for a number field
*
* @defaultWrappers form-field-horizontal & validation
*/
@Component({
selector: 'ish-number-field',
templateUrl: './number-field.component.html',
styleUrls: ['./number-field.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NumberFieldComponent extends FieldType<FieldTypeConfig> implements OnInit {
cannotIncrease = false;
cannotDecrease = false;

ngOnInit(): void {
this.evaluateButtonDisabled();
}

increase() {
this.formControl.setValue(
Number.parseInt(this.formControl.value) + (this.field.props.step ? this.field.props.step : 1)
);
this.evaluateButtonDisabled();
}

decrease() {
this.formControl.setValue(
Number.parseInt(this.formControl.value) - (this.field.props.step ? this.field.props.step : 1)
);
this.evaluateButtonDisabled();
}

private evaluateButtonDisabled() {
this.cannotDecrease = this.field.props.min && this.formControl.value <= this.field.props.min;
this.cannotIncrease = this.field.props.max && this.formControl.value >= this.field.props.max;
}
}
7 changes: 7 additions & 0 deletions src/app/shared/formly/types/types.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { LocalizedParserFormatter } from './date-picker-field/localized-parser-f
import { DateRangePickerFieldComponent } from './date-range-picker-field/date-range-picker-field.component';
import { FieldsetFieldComponent } from './fieldset-field/fieldset-field.component';
import { HtmlTextFieldComponent } from './html-text-field/html-text-field.component';
import { NumberFieldComponent } from './number-field/number-field.component';
import { PlainTextFieldComponent } from './plain-text-field/plain-text-field.component';
import { RadioFieldComponent } from './radio-field/radio-field.component';
import { SelectFieldComponent } from './select-field/select-field.component';
Expand All @@ -44,6 +45,7 @@ const fieldComponents = [
SelectFieldComponent,
TextareaFieldComponent,
TextInputFieldComponent,
NumberFieldComponent,
];

@NgModule({
Expand Down Expand Up @@ -166,6 +168,11 @@ const fieldComponents = [
component: DateRangePickerFieldComponent,
wrappers: ['form-field-horizontal', 'validation'],
},
{
name: 'ish-number-field',
component: NumberFieldComponent,
wrappers: ['form-field-horizontal', 'validation'],
},
],
}),
],
Expand Down
2 changes: 2 additions & 0 deletions src/assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,8 @@
"navigation.paging.go_to_page.label": "Go to page {{0}}",
"navigation.paging.next_page.label": "Go to next page",
"navigation.paging.previous_page.label": "Go to previous page",
"number.decrease.text": "",
"number.increase.text": "+",
"order.tracking.error": "Unfortunately, we could not locate an order with the information you provided.",
"order_template.create.heading": "Create order template",
"payment.error.PaymentInstrumentAlreadyExists": "The payment instrument could not be created. Payment data with the given parameters already exists.",
Expand Down

0 comments on commit 9d3bac5

Please sign in to comment.