npm package: @libs-ui/components-datetime-input
Description: > **Version**: 0.2.355-15
Latest version: 0.2.356-23

# Datetime Input Component

> **Version**: 0.2.355-15

Component nhập giờ phút với hỗ trợ single time và time range, tự động format và validation.

## Tính năng

- ✅ **Single Time Input**: Nhập giờ phút đơn giản (HH:mm)
- ✅ **Time Range Input**: Nhập khoảng thời gian (From - To)
- ✅ **Auto Format**: Tự động format thành HH:mm khi blur
- ✅ **Auto Focus**: Tự động chuyển focus giữa hours và minutes
- ✅ **Validation**: Hỗ trợ validation required và compare time
- ✅ **Real-time Emit**: Emit data real-time khi nhập
- ✅ **Keyboard Support**: Hỗ trợ đầy đủ keyboard navigation
- ✅ **FunctionsControl**: Điều khiển component từ bên ngoài

## Khi nào sử dụng

- Khi cần nhập giờ phút đơn giản (HH:mm)
- Khi cần nhập khoảng thời gian (From - To)
- Khi cần validation cho time input (required, compare time)
- Khi cần so sánh thời gian bắt đầu và kết thúc
- Khi cần format time input tự động
- Khi cần emit data real-time trong quá trình nhập

## Important Notes

⚠️ `ISelectedMultiTime` sử dụng `WritableSignal` — phải tạo bằng `signal()` khi truyền vào `[selectedTime]`

⚠️ Mặc định `[ignoreAllowEqualTime]="true"` — From và To bằng nhau sẽ bị coi là lỗi khi dùng `isCheckErrorTimeEndGreaterTimeStart`

⚠️ Auto Focus tự động chuyển focus: hours → minutes → toHours → toMinutes khi nhập đủ ký tự

⚠️ Component tự động format thành HH:mm khi blur (vd: nhập "9" → hiển thị "09")

⚠️ Validation chỉ chạy khi không ở trạng thái disable hoặc readonly

## Installation

```bash
npm install @libs-ui/components-datetime-input
```

## Usage

### Basic Single Time

```typescript
import { Component } from '@angular/core';
import { LibsUiComponentsDatetimeInputComponent, ISelectedTimeInput } from '@libs-ui/components-datetime-input';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDatetimeInputComponent],
  template: `
    <libs_ui-components-datetime-input
      [labelConfig]="{ labelLeft: 'Chọn giờ' }"
      (outEmitSingleTime)="onTimeSelected($event)" />
  `,
})
export class ExampleComponent {
  onTimeSelected(event: ISelectedTimeInput) {
    console.log('Time:', event); // { hours: 9, minute: 30 }
  }
}
```

### Time Range

```typescript
import { Component } from '@angular/core';
import { LibsUiComponentsDatetimeInputComponent, ISelectedMultiTime } from '@libs-ui/components-datetime-input';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDatetimeInputComponent],
  template: `
    <libs_ui-components-datetime-input
      [multiTime]="true"
      [labelConfig]="{ labelLeft: 'Khoảng giờ' }"
      (outEmitMultiTime)="onTimeRangeSelected($event)" />
  `,
})
export class ExampleComponent {
  onTimeRangeSelected(event: ISelectedMultiTime) {
    console.log('From:', event.from?.());
    console.log('To:', event.to?.());
  }
}
```

### With Preset Value

```typescript
import { Component, signal } from '@angular/core';
import { ISelectedMultiTime } from '@libs-ui/components-datetime-input';

@Component({
  // ...
  template: `
    <libs_ui-components-datetime-input
      [multiTime]="true"
      [selectedTime]="selectedTime()"
      [labelConfig]="{ labelLeft: 'Giờ làm việc' }"
      (outEmitMultiTime)="onTimeRangeSelected($event)" />
  `,
})
export class ExampleComponent {
  // ⚠️ from và to PHẢI là WritableSignal
  readonly selectedTime = signal<ISelectedMultiTime>({
    from: signal({ hours: 9, minute: 30 }),
    to: signal({ hours: 17, minute: 0 }),
  });

  onTimeRangeSelected(event: ISelectedMultiTime) {
    console.log('Time range:', event);
  }
}
```

### With Validation

```typescript
import { Component } from '@angular/core';

@Component({
  // ...
  template: `
    <libs_ui-components-datetime-input
      [multiTime]="true"
      [validRequired]="{ message: 'Vui lòng nhập giờ' }"
      [validCompareTime]="{
        message: 'Giờ kết thúc phải lớn hơn giờ bắt đầu',
        isCheckErrorTimeEndGreaterTimeStart: true,
      }"
      (outEmitMultiTime)="onTimeRangeSelected($event)"
      (outEmitValid)="onValidChange($event)" />
  `,
})
export class ExampleComponent {
  onTimeRangeSelected(event: ISelectedMultiTime) {
    console.log('Time range:', event);
  }

  onValidChange(valid: { validRequired: boolean; validCompare: boolean }) {
    console.log('Validation:', valid);
  }
}
```

### FunctionsControl

```typescript
import { Component, signal } from '@angular/core';
import { IDateTimeInputFunctionControlEvent } from '@libs-ui/components-datetime-input';

@Component({
  // ...
  template: `
    <libs_ui-components-datetime-input
      [multiTime]="true"
      [validRequired]="{ message: 'Vui lòng nhập giờ' }"
      (outFunctionsControl)="controls.set($event)"
    />
  `,
})
export class ExampleComponent {
  controls = signal<IDateTimeInputFunctionControlEvent | null>(null);

  async validate() {
    const isValid = await this.controls()?.checkIsValid();
    console.log('Valid:', isValid);
  }

  async setError() {
    await this.controls()?.setMessageError('Thời gian không hợp lệ');
  }

  async resetError() {
    await this.controls()?.resetError();
  }
}
```

## API

### Inputs

| Property                                 | Type                             | Default           | Description                                                                     |
| ---------------------------------------- | -------------------------------- | ----------------- | ------------------------------------------------------------------------------- |
| `[classDatePickerInput]`                 | `string`                         | `undefined`       | CSS class cho date picker input                                                 |
| `[classIncludeBetweenTime]`              | `string`                         | `undefined`       | CSS class cho phần between time (dấu -)                                         |
| `[classIncludeInput]`                    | `string`                         | `libs-ui-font-h5r`| CSS class cho input element                                                     |
| `[defaultHeight]`                        | `number`                         | `28`              | Chiều cao mặc định của input (px)                                               |
| `[disable]`                              | `boolean`                        | `false`           | Disable input                                                                   |
| `[emitDataSingleDataWhenUseMultiTime]`   | `boolean`                        | `false`           | Emit data riêng từng phần khi dùng multiTime                                    |
| `[fromAndToDateLabel]`                   | `{ from?: string; to?: string }` | `undefined`       | Label cho From và To                                                            |
| `[ignoreAllowEqualTime]`                 | `boolean`                        | `true`            | Khi true, From = To bị coi là lỗi (với isCheckErrorTimeEndGreaterTimeStart)     |
| `[ignoreBorder]`                         | `boolean`                        | `false`           | Ẩn border của input                                                             |
| `[ignoreCheckValidWhenInput]`            | `boolean`                        | `false`           | Bỏ qua validate khi nhập từ bàn phím                                           |
| `[ignoreMultiIcon]`                      | `boolean`                        | `false`           | Ẩn icon giữa 2 time range                                                      |
| `[ignoreShowValid]`                      | `boolean`                        | `false`           | Ẩn hiển thị lỗi validation                                                     |
| `[ignoreWithDefault]`                    | `boolean`                        | `false`           | Bỏ qua giá trị default                                                         |
| `[isEmitRealTime]`                       | `boolean`                        | `false`           | Emit data real-time khi nhập (mỗi keystroke)                                    |
| `[labelConfig]`                          | `ILabel`                         | `undefined`       | Cấu hình label (labelLeft, required, ...)                                       |
| `[multiTime]`                            | `boolean`                        | `false`           | Chế độ chọn time range (From - To)                                              |
| `[readonly]`                             | `boolean`                        | `false`           | Readonly mode                                                                   |
| `[resetInput]`                           | `boolean`                        | `true`            | Cho phép reset input                                                            |
| `[selectedTime]`                         | `ISelectedMultiTime`             | `undefined`       | Giá trị time được set sẵn (sử dụng WritableSignal)                              |
| `[showBorderError]`                      | `boolean`                        | `false`           | Hiển thị border error state                                                     |
| `[validCompareTime]`                     | `IValidCompare`                  | `undefined`       | Cấu hình validation so sánh time                                                |
| `[validRequired]`                        | `IMessageTranslate`              | `undefined`       | Cấu hình validation required                                                    |

### Outputs

| Property                | Type                                                | Description                                            |
| ----------------------- | --------------------------------------------------- | ------------------------------------------------------ |
| `(outClickButtonLabel)` | `IButton`                                           | Event khi click button trên label                      |
| `(outEmitMultiTime)`    | `ISelectedMultiTime`                                | Event khi chọn time range (cả from và to)              |
| `(outEmitRealTime)`     | `ISelectedMultiTime`                                | Event emit real-time khi nhập (mỗi keystroke)          |
| `(outEmitSingleTime)`   | `ISelectedTimeInput`                                | Event khi chọn single time                             |
| `(outEmitValid)`        | `{ validRequired: boolean; validCompare: boolean }` | Event trạng thái validation                            |
| `(outFunctionsControl)` | `IDateTimeInputFunctionControlEvent`                | Emit functions để điều khiển component từ bên ngoài    |
| `(outLabelRightClick)`  | `boolean`                                           | Event khi click label bên phải                         |
| `(outResetTime)`        | `ISelectedMultiTime \| undefined`                   | Event khi reset time                                   |
| `(outSwitchEventLabel)` | `ISwitchEvent`                                      | Event khi switch trên label thay đổi                   |

### FunctionsControl Methods

| Method                        | Description                                        |
| ----------------------------- | -------------------------------------------------- |
| `checkIsValid()`              | Kiểm tra validation và trả về `Promise<boolean>`   |
| `resetError(resetAll?)`       | Reset tất cả trạng thái lỗi                        |
| `setMessageError(message)`    | Đặt message lỗi tùy chỉnh (undefined để xóa)      |

## Types

### ISelectedTimeInput

```typescript
export interface ISelectedTimeInput {
  hours?: string | number;
  minute?: string | number;
}
```

### ISelectedMultiTime

```typescript
export interface ISelectedMultiTime {
  from?: WritableSignal<ISelectedTimeInput>;
  to?: WritableSignal<ISelectedTimeInput>;
}
```

### IValidCompare

```typescript
export interface IValidCompare extends IMessageTranslate {
  isCheckErrorTimeEndGreaterTimeStart?: boolean;
  isCheckErrorTimeDuplicate?: boolean;
}
```

### IDateTimeInputFunctionControlEvent

```typescript
export interface IDateTimeInputFunctionControlEvent {
  checkIsValid: () => Promise<boolean>;
  resetError: (resetAll?: boolean) => Promise<void>;
  setMessageError: (message: string | undefined) => Promise<void>;
}
```

## Hidden Logic

### Signal Auto-Unwrapping trong selectedTime

Khi truyền `[selectedTime]`, component sử dụng `effect()` để watch thay đổi. Giá trị `from`/`to` là `WritableSignal`, component sẽ gọi `.()` để unwrap. Nếu truyền plain object thay vì signal sẽ gây lỗi.

### Backspace Navigation

Khi nhấn Backspace ở ô trống, component tự động focus về ô trước đó (toMinutes → toHours → fromMinutes → fromHours). Cần nhấn Backspace 2 lần: lần 1 xóa giá trị, lần 2 chuyển focus.

### Input Dependencies — ignoreCheckValidWhenInput

Khi `[ignoreCheckValidWhenInput]="true"`, validation sẽ KHÔNG chạy ngay khi nhập từ bàn phím, chỉ chạy khi gọi `checkIsValid()` từ FunctionsControl.

## Demo

- **Local**: [http://localhost:4500/datetime/input](http://localhost:4500/datetime/input)

## Test

```bash
nx test components-datetime-input
```

## License

MIT