@libs-ui/components-datetime-input 0.2.355-8 → 0.2.356-0
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.
- package/README.md +303 -2
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,3 +1,304 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Datetime Input Component
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.356-0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/core": ">=18.0.0",
|
|
6
|
-
"@libs-ui/components-buttons-button": "0.2.
|
|
7
|
-
"@libs-ui/components-inputs-input": "0.2.
|
|
8
|
-
"@libs-ui/components-inputs-valid": "0.2.
|
|
9
|
-
"@libs-ui/components-label": "0.2.
|
|
10
|
-
"@libs-ui/components-switch": "0.2.
|
|
11
|
-
"@libs-ui/interfaces-types": "0.2.
|
|
12
|
-
"@libs-ui/utils": "0.2.
|
|
6
|
+
"@libs-ui/components-buttons-button": "0.2.356-0",
|
|
7
|
+
"@libs-ui/components-inputs-input": "0.2.356-0",
|
|
8
|
+
"@libs-ui/components-inputs-valid": "0.2.356-0",
|
|
9
|
+
"@libs-ui/components-label": "0.2.356-0",
|
|
10
|
+
"@libs-ui/components-switch": "0.2.356-0",
|
|
11
|
+
"@libs-ui/interfaces-types": "0.2.356-0",
|
|
12
|
+
"@libs-ui/utils": "0.2.356-0",
|
|
13
13
|
"@ngx-translate/core": "^15.0.0",
|
|
14
14
|
"rxjs": "~7.8.0"
|
|
15
15
|
},
|