@libs-ui/components-datetime-input 0.2.355-9 → 0.2.356-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +303 -2
  2. package/package.json +8 -8
package/README.md CHANGED
@@ -1,3 +1,304 @@
1
- # datetime-input
1
+ # Datetime Input Component
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ > **Version**: 0.2.355-15
4
+
5
+ Component nhập giờ phút với hỗ trợ single time và time range, tự động format và validation.
6
+
7
+ ## Tính năng
8
+
9
+ - ✅ **Single Time Input**: Nhập giờ phút đơn giản (HH:mm)
10
+ - ✅ **Time Range Input**: Nhập khoảng thời gian (From - To)
11
+ - ✅ **Auto Format**: Tự động format thành HH:mm khi blur
12
+ - ✅ **Auto Focus**: Tự động chuyển focus giữa hours và minutes
13
+ - ✅ **Validation**: Hỗ trợ validation required và compare time
14
+ - ✅ **Real-time Emit**: Emit data real-time khi nhập
15
+ - ✅ **Keyboard Support**: Hỗ trợ đầy đủ keyboard navigation
16
+ - ✅ **FunctionsControl**: Điều khiển component từ bên ngoài
17
+
18
+ ## Khi nào sử dụng
19
+
20
+ - Khi cần nhập giờ phút đơn giản (HH:mm)
21
+ - Khi cần nhập khoảng thời gian (From - To)
22
+ - Khi cần validation cho time input (required, compare time)
23
+ - Khi cần so sánh thời gian bắt đầu và kết thúc
24
+ - Khi cần format time input tự động
25
+ - Khi cần emit data real-time trong quá trình nhập
26
+
27
+ ## Important Notes
28
+
29
+ ⚠️ `ISelectedMultiTime` sử dụng `WritableSignal` — phải tạo bằng `signal()` khi truyền vào `[selectedTime]`
30
+
31
+ ⚠️ Mặc định `[ignoreAllowEqualTime]="true"` — From và To bằng nhau sẽ bị coi là lỗi khi dùng `isCheckErrorTimeEndGreaterTimeStart`
32
+
33
+ ⚠️ Auto Focus tự động chuyển focus: hours → minutes → toHours → toMinutes khi nhập đủ ký tự
34
+
35
+ ⚠️ Component tự động format thành HH:mm khi blur (vd: nhập "9" → hiển thị "09")
36
+
37
+ ⚠️ Validation chỉ chạy khi không ở trạng thái disable hoặc readonly
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install @libs-ui/components-datetime-input
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ### Basic Single Time
48
+
49
+ ```typescript
50
+ import { Component } from '@angular/core';
51
+ import { LibsUiComponentsDatetimeInputComponent, ISelectedTimeInput } from '@libs-ui/components-datetime-input';
52
+
53
+ @Component({
54
+ selector: 'app-example',
55
+ standalone: true,
56
+ imports: [LibsUiComponentsDatetimeInputComponent],
57
+ template: `
58
+ <libs_ui-components-datetime-input
59
+ [labelConfig]="{ labelLeft: 'Chọn giờ' }"
60
+ (outEmitSingleTime)="onTimeSelected($event)" />
61
+ `,
62
+ })
63
+ export class ExampleComponent {
64
+ onTimeSelected(event: ISelectedTimeInput) {
65
+ console.log('Time:', event); // { hours: 9, minute: 30 }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Time Range
71
+
72
+ ```typescript
73
+ import { Component } from '@angular/core';
74
+ import { LibsUiComponentsDatetimeInputComponent, ISelectedMultiTime } from '@libs-ui/components-datetime-input';
75
+
76
+ @Component({
77
+ selector: 'app-example',
78
+ standalone: true,
79
+ imports: [LibsUiComponentsDatetimeInputComponent],
80
+ template: `
81
+ <libs_ui-components-datetime-input
82
+ [multiTime]="true"
83
+ [labelConfig]="{ labelLeft: 'Khoảng giờ' }"
84
+ (outEmitMultiTime)="onTimeRangeSelected($event)" />
85
+ `,
86
+ })
87
+ export class ExampleComponent {
88
+ onTimeRangeSelected(event: ISelectedMultiTime) {
89
+ console.log('From:', event.from?.());
90
+ console.log('To:', event.to?.());
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### With Preset Value
96
+
97
+ ```typescript
98
+ import { Component, signal } from '@angular/core';
99
+ import { ISelectedMultiTime } from '@libs-ui/components-datetime-input';
100
+
101
+ @Component({
102
+ // ...
103
+ template: `
104
+ <libs_ui-components-datetime-input
105
+ [multiTime]="true"
106
+ [selectedTime]="selectedTime()"
107
+ [labelConfig]="{ labelLeft: 'Giờ làm việc' }"
108
+ (outEmitMultiTime)="onTimeRangeSelected($event)" />
109
+ `,
110
+ })
111
+ export class ExampleComponent {
112
+ // ⚠️ from và to PHẢI là WritableSignal
113
+ readonly selectedTime = signal<ISelectedMultiTime>({
114
+ from: signal({ hours: 9, minute: 30 }),
115
+ to: signal({ hours: 17, minute: 0 }),
116
+ });
117
+
118
+ onTimeRangeSelected(event: ISelectedMultiTime) {
119
+ console.log('Time range:', event);
120
+ }
121
+ }
122
+ ```
123
+
124
+ ### With Validation
125
+
126
+ ```typescript
127
+ import { Component } from '@angular/core';
128
+
129
+ @Component({
130
+ // ...
131
+ template: `
132
+ <libs_ui-components-datetime-input
133
+ [multiTime]="true"
134
+ [validRequired]="{ message: 'Vui lòng nhập giờ' }"
135
+ [validCompareTime]="{
136
+ message: 'Giờ kết thúc phải lớn hơn giờ bắt đầu',
137
+ isCheckErrorTimeEndGreaterTimeStart: true,
138
+ }"
139
+ (outEmitMultiTime)="onTimeRangeSelected($event)"
140
+ (outEmitValid)="onValidChange($event)" />
141
+ `,
142
+ })
143
+ export class ExampleComponent {
144
+ onTimeRangeSelected(event: ISelectedMultiTime) {
145
+ console.log('Time range:', event);
146
+ }
147
+
148
+ onValidChange(valid: { validRequired: boolean; validCompare: boolean }) {
149
+ console.log('Validation:', valid);
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### FunctionsControl
155
+
156
+ ```typescript
157
+ import { Component, signal } from '@angular/core';
158
+ import { IDateTimeInputFunctionControlEvent } from '@libs-ui/components-datetime-input';
159
+
160
+ @Component({
161
+ // ...
162
+ template: `
163
+ <libs_ui-components-datetime-input
164
+ [multiTime]="true"
165
+ [validRequired]="{ message: 'Vui lòng nhập giờ' }"
166
+ (outFunctionsControl)="controls.set($event)"
167
+ />
168
+ `,
169
+ })
170
+ export class ExampleComponent {
171
+ controls = signal<IDateTimeInputFunctionControlEvent | null>(null);
172
+
173
+ async validate() {
174
+ const isValid = await this.controls()?.checkIsValid();
175
+ console.log('Valid:', isValid);
176
+ }
177
+
178
+ async setError() {
179
+ await this.controls()?.setMessageError('Thời gian không hợp lệ');
180
+ }
181
+
182
+ async resetError() {
183
+ await this.controls()?.resetError();
184
+ }
185
+ }
186
+ ```
187
+
188
+ ## API
189
+
190
+ ### Inputs
191
+
192
+ | Property | Type | Default | Description |
193
+ | ---------------------------------------- | -------------------------------- | ----------------- | ------------------------------------------------------------------------------- |
194
+ | `[classDatePickerInput]` | `string` | `undefined` | CSS class cho date picker input |
195
+ | `[classIncludeBetweenTime]` | `string` | `undefined` | CSS class cho phần between time (dấu -) |
196
+ | `[classIncludeInput]` | `string` | `libs-ui-font-h5r`| CSS class cho input element |
197
+ | `[defaultHeight]` | `number` | `28` | Chiều cao mặc định của input (px) |
198
+ | `[disable]` | `boolean` | `false` | Disable input |
199
+ | `[emitDataSingleDataWhenUseMultiTime]` | `boolean` | `false` | Emit data riêng từng phần khi dùng multiTime |
200
+ | `[fromAndToDateLabel]` | `{ from?: string; to?: string }` | `undefined` | Label cho From và To |
201
+ | `[ignoreAllowEqualTime]` | `boolean` | `true` | Khi true, From = To bị coi là lỗi (với isCheckErrorTimeEndGreaterTimeStart) |
202
+ | `[ignoreBorder]` | `boolean` | `false` | Ẩn border của input |
203
+ | `[ignoreCheckValidWhenInput]` | `boolean` | `false` | Bỏ qua validate khi nhập từ bàn phím |
204
+ | `[ignoreMultiIcon]` | `boolean` | `false` | Ẩn icon giữa 2 time range |
205
+ | `[ignoreShowValid]` | `boolean` | `false` | Ẩn hiển thị lỗi validation |
206
+ | `[ignoreWithDefault]` | `boolean` | `false` | Bỏ qua giá trị default |
207
+ | `[isEmitRealTime]` | `boolean` | `false` | Emit data real-time khi nhập (mỗi keystroke) |
208
+ | `[labelConfig]` | `ILabel` | `undefined` | Cấu hình label (labelLeft, required, ...) |
209
+ | `[multiTime]` | `boolean` | `false` | Chế độ chọn time range (From - To) |
210
+ | `[readonly]` | `boolean` | `false` | Readonly mode |
211
+ | `[resetInput]` | `boolean` | `true` | Cho phép reset input |
212
+ | `[selectedTime]` | `ISelectedMultiTime` | `undefined` | Giá trị time được set sẵn (sử dụng WritableSignal) |
213
+ | `[showBorderError]` | `boolean` | `false` | Hiển thị border error state |
214
+ | `[validCompareTime]` | `IValidCompare` | `undefined` | Cấu hình validation so sánh time |
215
+ | `[validRequired]` | `IMessageTranslate` | `undefined` | Cấu hình validation required |
216
+
217
+ ### Outputs
218
+
219
+ | Property | Type | Description |
220
+ | ----------------------- | --------------------------------------------------- | ------------------------------------------------------ |
221
+ | `(outClickButtonLabel)` | `IButton` | Event khi click button trên label |
222
+ | `(outEmitMultiTime)` | `ISelectedMultiTime` | Event khi chọn time range (cả from và to) |
223
+ | `(outEmitRealTime)` | `ISelectedMultiTime` | Event emit real-time khi nhập (mỗi keystroke) |
224
+ | `(outEmitSingleTime)` | `ISelectedTimeInput` | Event khi chọn single time |
225
+ | `(outEmitValid)` | `{ validRequired: boolean; validCompare: boolean }` | Event trạng thái validation |
226
+ | `(outFunctionsControl)` | `IDateTimeInputFunctionControlEvent` | Emit functions để điều khiển component từ bên ngoài |
227
+ | `(outLabelRightClick)` | `boolean` | Event khi click label bên phải |
228
+ | `(outResetTime)` | `ISelectedMultiTime \| undefined` | Event khi reset time |
229
+ | `(outSwitchEventLabel)` | `ISwitchEvent` | Event khi switch trên label thay đổi |
230
+
231
+ ### FunctionsControl Methods
232
+
233
+ | Method | Description |
234
+ | ----------------------------- | -------------------------------------------------- |
235
+ | `checkIsValid()` | Kiểm tra validation và trả về `Promise<boolean>` |
236
+ | `resetError(resetAll?)` | Reset tất cả trạng thái lỗi |
237
+ | `setMessageError(message)` | Đặt message lỗi tùy chỉnh (undefined để xóa) |
238
+
239
+ ## Types
240
+
241
+ ### ISelectedTimeInput
242
+
243
+ ```typescript
244
+ export interface ISelectedTimeInput {
245
+ hours?: string | number;
246
+ minute?: string | number;
247
+ }
248
+ ```
249
+
250
+ ### ISelectedMultiTime
251
+
252
+ ```typescript
253
+ export interface ISelectedMultiTime {
254
+ from?: WritableSignal<ISelectedTimeInput>;
255
+ to?: WritableSignal<ISelectedTimeInput>;
256
+ }
257
+ ```
258
+
259
+ ### IValidCompare
260
+
261
+ ```typescript
262
+ export interface IValidCompare extends IMessageTranslate {
263
+ isCheckErrorTimeEndGreaterTimeStart?: boolean;
264
+ isCheckErrorTimeDuplicate?: boolean;
265
+ }
266
+ ```
267
+
268
+ ### IDateTimeInputFunctionControlEvent
269
+
270
+ ```typescript
271
+ export interface IDateTimeInputFunctionControlEvent {
272
+ checkIsValid: () => Promise<boolean>;
273
+ resetError: (resetAll?: boolean) => Promise<void>;
274
+ setMessageError: (message: string | undefined) => Promise<void>;
275
+ }
276
+ ```
277
+
278
+ ## Hidden Logic
279
+
280
+ ### Signal Auto-Unwrapping trong selectedTime
281
+
282
+ 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.
283
+
284
+ ### Backspace Navigation
285
+
286
+ 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.
287
+
288
+ ### Input Dependencies — ignoreCheckValidWhenInput
289
+
290
+ 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.
291
+
292
+ ## Demo
293
+
294
+ - **Local**: [http://localhost:4500/datetime/input](http://localhost:4500/datetime/input)
295
+
296
+ ## Test
297
+
298
+ ```bash
299
+ nx test components-datetime-input
300
+ ```
301
+
302
+ ## License
303
+
304
+ MIT
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@libs-ui/components-datetime-input",
3
- "version": "0.2.355-9",
3
+ "version": "0.2.356-1",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=18.0.0",
6
- "@libs-ui/components-buttons-button": "0.2.355-9",
7
- "@libs-ui/components-inputs-input": "0.2.355-9",
8
- "@libs-ui/components-inputs-valid": "0.2.355-9",
9
- "@libs-ui/components-label": "0.2.355-9",
10
- "@libs-ui/components-switch": "0.2.355-9",
11
- "@libs-ui/interfaces-types": "0.2.355-9",
12
- "@libs-ui/utils": "0.2.355-9",
6
+ "@libs-ui/components-buttons-button": "0.2.356-1",
7
+ "@libs-ui/components-inputs-input": "0.2.356-1",
8
+ "@libs-ui/components-inputs-valid": "0.2.356-1",
9
+ "@libs-ui/components-label": "0.2.356-1",
10
+ "@libs-ui/components-switch": "0.2.356-1",
11
+ "@libs-ui/interfaces-types": "0.2.356-1",
12
+ "@libs-ui/utils": "0.2.356-1",
13
13
  "@ngx-translate/core": "^15.0.0",
14
14
  "rxjs": "~7.8.0"
15
15
  },