# @isa/utils/positive-integer-input

An Angular directive that ensures only positive integers can be entered into number input fields through keyboard blocking, paste sanitization, and input validation.

## Overview

The `PositiveIntegerInputDirective` provides three layers of protection to prevent invalid input in number fields:

1. **Keyboard input blocking** - Prevents invalid characters from being typed
2. **Paste sanitization** - Cleans pasted content to extract only positive integers
3. **General input validation** - Catches any other input methods (drag & drop, autofill, voice input, etc.)

This directive is particularly useful for scenarios where you need to enforce strictly positive integer values, such as:
- Quantity inputs in shopping carts
- Age or count fields
- Point or score inputs
- Any numeric field that should not accept decimals, negatives, or special characters

### Key Behaviors

**Blocked characters:** `.`, `,`, `-`, `+`, `e`, `E`, and all other non-digit characters

**Important notes:**
- This directive removes ALL non-digit characters, so `1.58` becomes `158`, not `1` or `2`
- Leading zeros are automatically removed (`007` becomes `7`)
- A single `0` input results in an empty field
- Works seamlessly with Angular forms (`ngModel`, `formControl`, `formControlName`)

## Installation

Import the directive in your component:

```ts
import { PositiveIntegerInputDirective } from '@isa/utils/positive-integer-input';

@Component({
  selector: 'app-my-component',
  standalone: true,
  imports: [PositiveIntegerInputDirective],
  // ...
})
export class MyComponent {}
```

## API Reference

### PositiveIntegerInputDirective

**Selector:** `input[type="number"][positiveIntegerInput]`

**Standalone:** Yes

A standalone Angular directive that attaches to `input[type="number"]` elements and enforces positive integer input.

#### Host Listeners

| Event | Handler | Description |
|-------|---------|-------------|
| `keydown` | `onKeyDown(event)` | Blocks invalid characters (`.`, `,`, `-`, `+`, `e`, `E`) before they can be entered |
| `paste` | `onPaste(event)` | Intercepts paste events, sanitizes clipboard content, and inserts only positive integers |
| `input` | `onInput(event)` | Catch-all for other input methods (drag & drop, autofill, voice input, IME) |

#### Private Methods

**`sanitizeInput(value: string): string`**

Sanitizes a string to contain only positive integers by:
1. Removing all non-digit characters using regex `\D`
2. Removing leading zeros to prevent parsing issues

## Usage Examples

### Basic Usage

```html
<input type="number" positiveIntegerInput />
```

### With ngModel (Two-way Binding)

```html
<input
  type="number"
  positiveIntegerInput
  [(ngModel)]="quantity"
  placeholder="Enter quantity"
/>
```

```ts
export class MyComponent {
  quantity: number | undefined;
}
```

### With Reactive Forms (FormControl)

```html
<input
  type="number"
  positiveIntegerInput
  [formControl]="pointsControl"
  placeholder="Enter points"
/>
```

```ts
import { FormControl } from '@angular/forms';

export class MyComponent {
  pointsControl = new FormControl<number | null>(null);
}
```

### With Reactive Forms (FormGroup)

```html
<form [formGroup]="orderForm">
  <input
    type="number"
    positiveIntegerInput
    formControlName="quantity"
    placeholder="Enter quantity"
  />
</form>
```

```ts
import { FormBuilder, FormGroup } from '@angular/forms';

export class MyComponent {
  orderForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.orderForm = this.fb.group({
      quantity: [null]
    });
  }
}
```

### Complete Example with Validation

```html
<form [formGroup]="productForm">
  <label for="quantity">Quantity:</label>
  <input
    id="quantity"
    type="number"
    positiveIntegerInput
    formControlName="quantity"
    placeholder="Enter quantity"
    [class.error]="quantity.invalid && quantity.touched"
  />

  @if (quantity.invalid && quantity.touched) {
    <span class="error-message">
      Quantity is required and must be a positive integer
    </span>
  }
</form>
```

```ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { PositiveIntegerInputDirective } from '@isa/utils/positive-integer-input';

@Component({
  selector: 'app-product-form',
  standalone: true,
  imports: [ReactiveFormsModule, PositiveIntegerInputDirective],
  templateUrl: './product-form.component.html',
})
export class ProductFormComponent {
  productForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.productForm = this.fb.group({
      quantity: [null, [Validators.required, Validators.min(1)]]
    });
  }

  get quantity() {
    return this.productForm.get('quantity')!;
  }

  onSubmit() {
    if (this.productForm.valid) {
      console.log('Quantity:', this.quantity.value);
    }
  }
}
```

## Behavior Examples

### Keyboard Input

| User Action | Result | Reason |
|-------------|--------|--------|
| Types `123` | `123` appears | Valid digits allowed |
| Types `1-2-3` | Only `123` appears | Minus signs blocked |
| Types `1.5` | Only `15` appears | Decimal point blocked |
| Types `1,000` | Only `1000` appears | Comma blocked |
| Types `+42` | Only `42` appears | Plus sign blocked |
| Types `1e5` | Only `15` appears | Letter `e` blocked |

### Paste Operations

| Clipboard Content | Result | Reason |
|------------------|--------|--------|
| `-100` | `100` | Negative sign removed |
| `1.000` | `1000` | Decimal point removed |
| `1,58` | `158` | Comma removed (NOT rounded to `1` or `2`) |
| `abc123xyz` | `123` | Non-digit characters removed |
| `007` | `7` | Leading zeros removed |
| `+42` | `42` | Plus sign removed |
| `3.14e2` | `3142` | All non-digits removed |
| `0` | Empty field | Single zero becomes empty string |

### Other Input Methods

The directive also sanitizes input from:
- **Drag and drop** - Dropping text from other sources
- **Browser autofill** - Autocomplete values
- **Voice input** - Speech-to-text input
- **IME input** - Input Method Editors (Asian languages)
- **Right-click paste** - Context menu paste (some browsers)

## Browser Compatibility

Works with all modern browsers that support Angular 20+ and native `input[type="number"]` elements.

## Testing

The directive includes comprehensive test coverage with Vitest, including:
- Keyboard input blocking tests
- Paste sanitization tests
- General input validation tests
- Angular forms integration tests
- Edge case handling

Run tests:
```bash
nx test utils-positive-integer-input
```

## Related

- [Angular Forms Documentation](https://angular.dev/guide/forms)
- [Angular Directives Documentation](https://angular.dev/guide/directives)
- [HTML Input Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)
