@libs-ui/components-dropdown 0.2.355-9 → 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.
Files changed (2) hide show
  1. package/README.md +411 -2
  2. package/package.json +11 -11
package/README.md CHANGED
@@ -1,3 +1,412 @@
1
- # dropdown
1
+ # @libs-ui/components-dropdown
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ > Component dropdown đa năng hỗ trợ nhiều chế độ hiển thị: text, radio, checkbox, group. Tích hợp sẵn tìm kiếm, validation, popover, tabs và điều khiển từ bên ngoài.
4
+
5
+ **Version:** `0.2.355-14`
6
+
7
+ ## Giới thiệu
8
+
9
+ `LibsUiComponentsDropdownComponent` là một standalone Angular component cung cấp dropdown selection với nhiều chế độ hiển thị và tính năng nâng cao.
10
+
11
+ ### Tính năng
12
+
13
+ - ✅ Nhiều chế độ hiển thị: text, radio, checkbox, group
14
+ - ✅ Tìm kiếm online/offline tích hợp sẵn
15
+ - ✅ Validation (required, max items selected)
16
+ - ✅ Điều khiển từ bên ngoài qua `IDropdownFunctionControlEvent`
17
+ - ✅ Hỗ trợ tabs để phân loại dữ liệu
18
+ - ✅ Tự động load dữ liệu từ API (httpRequestData)
19
+ - ✅ Auto-select first item / all items
20
+ - ✅ Hiển thị avatar, icon, image cho item
21
+ - ✅ Custom content qua ng-content
22
+ - ✅ OnPush Change Detection
23
+ - ✅ Angular Signals
24
+
25
+ ## Khi nào sử dụng
26
+
27
+ - Chọn một hoặc nhiều giá trị từ danh sách dữ liệu
28
+ - Dropdown với tìm kiếm online/offline
29
+ - Chọn dữ liệu dạng nhóm (group), cây (tree), JSON tree
30
+ - Cần validation (required, max items)
31
+ - Cần điều khiển dropdown từ component cha (reset, refresh, check valid)
32
+ - Hiển thị dropdown với tabs để phân loại dữ liệu
33
+ - Dropdown với custom content (ng-content)
34
+
35
+ ## Lưu ý quan trọng
36
+
37
+ - ⚠️ **Bắt buộc `[listConfig]`**: Phải cung cấp `[listConfig]` để dropdown hiển thị danh sách.
38
+ - ⚠️ **Chế độ chọn**: Sử dụng `[listKeySelected]` cho chọn đơn (text/radio), `[listMultiKeySelected]` cho chọn nhiều (checkbox/group).
39
+ - ⚠️ **Auto-load API**: Khi dùng `httpRequestData` trong `listConfig`, dropdown tự động gọi API để load dữ liệu.
40
+ - ⚠️ **FunctionControl**: Output `outFunctionsControl` emit `IDropdownFunctionControlEvent` để điều khiển dropdown từ bên ngoài.
41
+
42
+ ## Cài đặt
43
+
44
+ ```bash
45
+ # npm
46
+ npm install @libs-ui/components-dropdown
47
+
48
+ # yarn
49
+ yarn add @libs-ui/components-dropdown
50
+ ```
51
+
52
+ ## Import
53
+
54
+ ```typescript
55
+ import {
56
+ LibsUiComponentsDropdownComponent,
57
+ IDropdownFunctionControlEvent,
58
+ IEmitSelectKey,
59
+ IEmitMultiKey,
60
+ IPopoverCustomConfig,
61
+ IDropdownTabsItem,
62
+ IValidMaxItemSelected,
63
+ IDropdown,
64
+ } from '@libs-ui/components-dropdown';
65
+
66
+ @Component({
67
+ standalone: true,
68
+ imports: [LibsUiComponentsDropdownComponent],
69
+ // ...
70
+ })
71
+ export class YourComponent {}
72
+ ```
73
+
74
+ ## Ví dụ
75
+
76
+ ### Basic - Text
77
+
78
+ ```html
79
+ <libs_ui-components-dropdown
80
+ [labelConfig]="{ labelLeft: 'Chọn item', required: true }"
81
+ [listConfig]="listConfig"
82
+ [listSearchConfig]="{ noBorder: true }"
83
+ [listMaxItemShow]="5"
84
+ [convertItemSelected]="convertItemSelected"
85
+ (outSelectKey)="onSelectKey($event)"
86
+ />
87
+ ```
88
+
89
+ ```typescript
90
+ listConfig: IListConfigItem = {
91
+ type: 'text',
92
+ httpRequestData: signal<IHttpRequestConfig>({
93
+ objectInstance: getConfigListDataDemo(),
94
+ functionName: 'list',
95
+ argumentsValue: [new UtilsHttpParamsRequest({ fromObject: { page: 1, per_page: 20 } })],
96
+ }),
97
+ configTemplateText: signal({
98
+ fieldKey: 'id',
99
+ notUseVirtualScroll: true,
100
+ getValue: (item) => escapeHtml(item.name),
101
+ }),
102
+ };
103
+ ```
104
+
105
+ ### Checkbox
106
+
107
+ ```html
108
+ <libs_ui-components-dropdown
109
+ [labelConfig]="{ labelLeft: 'Chọn nhiều (checkbox)', required: true }"
110
+ [listConfig]="checkboxConfig"
111
+ [listMaxItemShow]="5"
112
+ [convertItemSelected]="convertItemSelected"
113
+ (outSelectMultiKey)="onSelectMultiKey($event)"
114
+ />
115
+ ```
116
+
117
+ ### Radio
118
+
119
+ ```html
120
+ <libs_ui-components-dropdown
121
+ [labelConfig]="{ labelLeft: 'Chọn item (radio)', required: true }"
122
+ [listConfig]="radioConfig"
123
+ [listMaxItemShow]="5"
124
+ [convertItemSelected]="convertItemSelected"
125
+ (outSelectKey)="onSelectKey($event)"
126
+ />
127
+ ```
128
+
129
+ ### Interactive - Controls
130
+
131
+ ```html
132
+ <libs_ui-components-dropdown
133
+ [labelConfig]="{ labelLeft: 'Interactive', required: true }"
134
+ [listConfig]="listConfig"
135
+ [listMaxItemShow]="5"
136
+ [validRequired]="{}"
137
+ (outFunctionsControl)="onFunctionControl($event)"
138
+ />
139
+ ```
140
+
141
+ ```typescript
142
+ dropdownControl: IDropdownFunctionControlEvent | undefined;
143
+
144
+ onFunctionControl(event: IDropdownFunctionControlEvent) {
145
+ this.dropdownControl = event;
146
+ }
147
+
148
+ async resetDropdown() {
149
+ await this.dropdownControl?.reset();
150
+ }
151
+
152
+ async checkValid() {
153
+ const isValid = await this.dropdownControl?.checkIsValid();
154
+ console.log('Valid:', isValid);
155
+ }
156
+
157
+ async refreshList() {
158
+ await this.dropdownControl?.refreshList();
159
+ }
160
+ ```
161
+
162
+ ### States
163
+
164
+ ```html
165
+ <!-- Disable -->
166
+ <libs_ui-components-dropdown
167
+ [labelConfig]="{ labelLeft: 'Disable' }"
168
+ [disable]="true"
169
+ [listConfig]="listConfig"
170
+ />
171
+
172
+ <!-- Readonly -->
173
+ <libs_ui-components-dropdown
174
+ [labelConfig]="{ labelLeft: 'Readonly' }"
175
+ [readonly]="true"
176
+ [listConfig]="listConfig"
177
+ />
178
+ ```
179
+
180
+ ## API
181
+
182
+ ### libs_ui-components-dropdown
183
+
184
+ #### Inputs
185
+
186
+ | Property | Type | Default | Description |
187
+ |----------|------|---------|-------------|
188
+ | `[allowSelectItemMultiple]` | `boolean` | `undefined` | Cho phép chọn lại item đã chọn |
189
+ | `[changeValidUndefinedResetError]` | `boolean` | `undefined` | Reset error khi valid undefined |
190
+ | `[classAvatarInclude]` | `string` | `'mr-[8px]'` | Class CSS cho avatar |
191
+ | `[classInclude]` | `string` | `undefined` | Class CSS bổ sung cho container |
192
+ | `[classIncludeContent]` | `string` | `undefined` | Class CSS cho content wrapper |
193
+ | `[classIncludeIcon]` | `string` | `'ml-[8px]'` | Class CSS cho icon mũi tên |
194
+ | `[classIncludeTextDisplayWhenNoSelect]` | `string` | `'libs-ui-font-h5r'` | Class CSS cho text placeholder |
195
+ | `[convertItemSelected]` | `(item, translate?) => void` | `defaultConvert` | Hàm convert item sau khi chọn để hiển thị label |
196
+ | `[disable]` | `boolean` | `undefined` | Vô hiệu hóa dropdown |
197
+ | `[disableLabel]` | `boolean` | `undefined` | Vô hiệu hóa label |
198
+ | `[fieldGetColorAvatar]` | `string` | `undefined` | Field lấy color cho avatar |
199
+ | `[fieldGetIcon]` | `string` | `undefined` | Field lấy icon class từ item |
200
+ | `[fieldGetImage]` | `string` | `undefined` | Field lấy URL ảnh từ item |
201
+ | `[fieldGetLabel]` | `string` | `undefined` | Field lấy label từ item |
202
+ | `[fieldGetTextAvatar]` | `string` | `'username'` | Field lấy text cho avatar |
203
+ | `[fieldLabel]` | `string` | `'labelDisplay'` | Field lưu label hiển thị trên dropdown |
204
+ | `[flagMouse]` | `IFlagMouse (model)` | `{ isMouseEnter: false, isMouseEnterContent: false }` | Trạng thái mouse |
205
+ | `[flagMouseContent]` | `IFlagMouse (model)` | `undefined` | Trạng thái mouse content |
206
+ | `[focusInputSearch]` | `boolean` | `true` | Tự động focus input tìm kiếm khi mở |
207
+ | `[getLastTextAfterSpace]` | `boolean` | `undefined` | Lấy text cuối sau dấu cách |
208
+ | `[getPopoverItemSelected]` | `(item, translate?) => Promise<IPopover>` | `undefined` | Hàm lấy popover config cho item đã chọn |
209
+ | `[hasContentUnitRight]` | `boolean` | `undefined` | Có content bên phải unit |
210
+ | `[httpRequestDetailItemById]` | `IHttpRequestConfig` | `undefined` | Config HTTP request để lấy chi tiết item theo key |
211
+ | `[ignoreBorderBottom]` | `boolean` | `undefined` | Bỏ border bottom |
212
+ | `[ignoreStopPropagationEvent]` | `boolean` | `false` | Bỏ qua stopPropagation |
213
+ | `[imageSize]` | `TYPE_SIZE_AVATAR_CONFIG` | `16` | Kích thước avatar |
214
+ | `[isNgContent]` | `boolean` | `undefined` | Sử dụng ng-content thay vì giao diện mặc định |
215
+ | `[isSearchOnline]` | `boolean` | `false` | Tìm kiếm online (gọi API) |
216
+ | `[labelConfig]` | `ILabel` | `undefined` | Cấu hình label phía trên dropdown |
217
+ | `[labelPopoverConfig]` | `IPopoverOverlay` | `undefined` | Config popover cho label |
218
+ | `[labelPopoverFullWidth]` | `boolean` | `true` | Popover label full width |
219
+ | `[lengthKeys]` | `number (model)` | `0` | Số lượng keys đã chọn |
220
+ | `[linkImageError]` | `string` | `undefined` | Link ảnh khi lỗi |
221
+ | `[listBackgroundCustom]` | `string` | `undefined` | Background custom cho list |
222
+ | `[listButtonsOther]` | `Array<IButton>` | `undefined` | Các button khác trong list |
223
+ | `[listClickExactly]` | `boolean` | `undefined` | Click chính xác item |
224
+ | `[listConfig]` | `IListConfigItem` | `undefined` | Cấu hình danh sách (type, httpRequestData, template config) |
225
+ | `[listConfigHasDivider]` | `boolean` | `true` | Hiển thị divider trong list |
226
+ | `[listDividerClassInclude]` | `string` | `undefined` | Class CSS cho divider |
227
+ | `[listHasButtonUnSelectOption]` | `boolean` | `undefined` | Hiển thị nút bỏ chọn |
228
+ | `[listHiddenInputSearch]` | `boolean` | `undefined` | Ẩn input tìm kiếm |
229
+ | `[listIgnoreClassDisableDefaultWhenUseKeysDisableItem]` | `boolean` | `undefined` | Bỏ class disable mặc định khi dùng keysDisable |
230
+ | `[listKeysDisable]` | `Array<string>` | `undefined` | Danh sách keys bị disable |
231
+ | `[listKeysHidden]` | `Array<string>` | `undefined` | Danh sách keys bị ẩn |
232
+ | `[listKeySearch]` | `string` | `undefined` | Key tìm kiếm |
233
+ | `[listKeySelected]` | `unknown (model)` | `undefined` | Key item đang được chọn (single select) |
234
+ | `[listMaxItemShow]` | `number` | `5` | Số item tối đa hiển thị |
235
+ | `[listMultiKeySelected]` | `Array<unknown> (model)` | `undefined` | Danh sách keys đang được chọn (multi select) |
236
+ | `[listSearchConfig]` | `IInputSearchConfig` | `{ noBorder: true }` | Cấu hình input tìm kiếm |
237
+ | `[listSearchNoDataTemplateRef]` | `TemplateRef<unknown>` | `undefined` | Template khi không có dữ liệu tìm kiếm |
238
+ | `[listSearchPadding]` | `boolean` | `undefined` | Padding cho search |
239
+ | `[onlyEmitDataWhenReset]` | `boolean` | `undefined` | Chỉ emit sự kiện khi reset |
240
+ | `[popoverCustomConfig]` | `IPopoverCustomConfig` | `undefined` | Cấu hình popover overlay |
241
+ | `[popoverElementRefCustom]` | `HTMLElement` | `undefined` | Element ref custom cho popover |
242
+ | `[readonly]` | `boolean` | `undefined` | Chế độ chỉ đọc |
243
+ | `[resetKeyWhenSelectAllKey]` | `boolean` | `undefined` | Reset key khi select all |
244
+ | `[showBorderError]` | `boolean (model)` | `undefined` | Hiển thị border error |
245
+ | `[showError]` | `boolean` | `true` | Hiển thị error message |
246
+ | `[tabKeyActive]` | `string (model)` | `undefined` | Key tab đang active |
247
+ | `[tabsConfig]` | `Array<IDropdownTabsItem>` | `undefined` | Cấu hình tabs cho dropdown |
248
+ | `[textDisplayWhenMultiSelect]` | `string` | `'i18n_selecting_options'` | Text hiển thị khi chọn nhiều |
249
+ | `[textDisplayWhenNoSelect]` | `string` | `'i18n_select_information'` | Text hiển thị khi chưa chọn |
250
+ | `[typeShape]` | `TYPE_SHAPE_AVATAR` | `'circle'` | Hình dạng avatar |
251
+ | `[useXssFilter]` | `boolean` | `false` | Bật XSS filter |
252
+ | `[validMaxItemSelected]` | `IValidMaxItemSelected` | `undefined` | Validation số item chọn tối đa |
253
+ | `[validRequired]` | `IMessageTranslate` | `undefined` | Validation required |
254
+ | `[zIndex]` | `number` | `undefined` | z-index cho popover |
255
+
256
+ #### Outputs
257
+
258
+ | Property | Type | Description |
259
+ |----------|------|-------------|
260
+ | `(outChangStageFlagMouse)` | `IFlagMouse` | Emit khi trạng thái mouse thay đổi |
261
+ | `(outChangeTabKeyActive)` | `string \| undefined` | Emit khi tab active thay đổi |
262
+ | `(outClickButtonOther)` | `IButton` | Emit khi click button khác trong list |
263
+ | `(outDataChange)` | `Array<unknown>` | Emit khi dữ liệu list thay đổi |
264
+ | `(outFunctionsControl)` | `IDropdownFunctionControlEvent` | Emit functions để điều khiển dropdown |
265
+ | `(outSelectKey)` | `IEmitSelectKey \| undefined` | Emit khi chọn item (single select) |
266
+ | `(outSelectMultiKey)` | `IEmitMultiKey \| undefined` | Emit khi chọn items (multi select) |
267
+ | `(outShowList)` | `boolean` | Emit khi show/hide list |
268
+ | `(outValidEvent)` | `boolean` | Emit kết quả validation |
269
+
270
+ #### FunctionControl Methods
271
+
272
+ | Method | Description |
273
+ |--------|-------------|
274
+ | `checkIsValid()` | Kiểm tra validation và trả về boolean |
275
+ | `getDisable()` | Lấy trạng thái disable |
276
+ | `refreshList()` | Refresh lại danh sách dữ liệu |
277
+ | `removeList()` | Đóng popup danh sách |
278
+ | `reset()` | Reset dropdown về trạng thái ban đầu |
279
+ | `resetError()` | Xóa error hiện tại |
280
+ | `setError(message)` | Đặt error message |
281
+ | `setItemSelectedByKey(id)` | Chọn item theo key (gọi API nếu có httpRequestDetailItemById) |
282
+ | `updateLabelItemSelected(label)` | Cập nhật label của item đã chọn |
283
+
284
+ ## Types & Interfaces
285
+
286
+ ```typescript
287
+ interface IEmitSelectKey {
288
+ key?: unknown;
289
+ item?: any;
290
+ isClickManual?: boolean;
291
+ tabKeyActive?: string;
292
+ }
293
+
294
+ interface IEmitMultiKey {
295
+ keys?: Array<unknown>;
296
+ mapKeys?: Array<IEmitSelectKey>;
297
+ isClickManual?: boolean;
298
+ tabKeyActive?: string;
299
+ }
300
+
301
+ interface IPopoverCustomConfig {
302
+ widthByParent?: boolean;
303
+ parentBorderWidth?: number;
304
+ maxHeight?: number;
305
+ maxWidth?: number;
306
+ direction?: TYPE_POPOVER_DIRECTION;
307
+ ignoreArrow?: boolean;
308
+ classInclude?: string;
309
+ disable?: boolean;
310
+ clickExactly?: boolean;
311
+ paddingLeftItem?: boolean;
312
+ timerDestroy?: number;
313
+ position?: { mode: TYPE_POPOVER_POSITION_MODE; distance: number };
314
+ animationConfig?: { time?: number; distance?: number };
315
+ width?: number;
316
+ classIncludeOverlayBody?: string;
317
+ }
318
+
319
+ interface IDropdownTabsItem {
320
+ key: string;
321
+ name: string;
322
+ httpRequestData?: IHttpRequestConfig;
323
+ }
324
+
325
+ interface IValidMaxItemSelected extends IMessageTranslate {
326
+ value: number;
327
+ }
328
+
329
+ interface IDropdown {
330
+ listConfig: IListConfigItem;
331
+ listBackgroundListCustom?: string;
332
+ listMaxItemShow?: number;
333
+ classIncludePopup?: string;
334
+ paddingLeftItem?: boolean;
335
+ clickExactly?: boolean;
336
+ zIndex?: number;
337
+ popoverCustomConfig?: IPopoverCustomConfig;
338
+ disable?: boolean;
339
+ }
340
+
341
+ interface IDropdownFunctionControlEvent {
342
+ checkIsValid: () => Promise<boolean>;
343
+ resetError: () => Promise<void>;
344
+ setError?: (message: string) => Promise<void>;
345
+ removeList: () => Promise<void>;
346
+ updateLabelItemSelected: (label: string) => Promise<void>;
347
+ reset: () => Promise<void>;
348
+ refreshList: () => Promise<void>;
349
+ setItemSelectedByKey: (id: unknown) => Promise<void>;
350
+ getDisable: () => Promise<boolean>;
351
+ }
352
+ ```
353
+
354
+ | Type | Mô tả |
355
+ |------|--------|
356
+ | `IEmitSelectKey` | Dữ liệu emit khi chọn 1 item (single select) |
357
+ | `IEmitMultiKey` | Dữ liệu emit khi chọn nhiều items (multi select) |
358
+ | `IPopoverCustomConfig` | Cấu hình tùy chỉnh popover overlay |
359
+ | `IDropdownTabsItem` | Cấu hình cho từng tab trong dropdown |
360
+ | `IValidMaxItemSelected` | Cấu hình validation giới hạn số item được chọn |
361
+ | `IDropdown` | Interface cấu hình dropdown |
362
+ | `IDropdownFunctionControlEvent` | Interface chứa các methods để điều khiển dropdown từ component cha |
363
+
364
+ ## Dependencies
365
+
366
+ | Package | Version | Mục đích |
367
+ |---------|---------|----------|
368
+ | `@libs-ui/components-avatar` | `0.2.355-14` | Hiển thị avatar trong dropdown |
369
+ | `@libs-ui/components-buttons-button` | `0.2.355-14` | Button interface |
370
+ | `@libs-ui/components-inputs-search` | `0.2.355-14` | Input tìm kiếm |
371
+ | `@libs-ui/components-inputs-valid` | `0.2.355-14` | Validation input |
372
+ | `@libs-ui/components-label` | `0.2.355-14` | Label component |
373
+ | `@libs-ui/components-list` | `0.2.355-14` | Danh sách hiển thị |
374
+ | `@libs-ui/components-popover` | `0.2.355-14` | Popover overlay |
375
+ | `@libs-ui/pipes-security-trust` | `0.2.355-14` | Security trust pipe |
376
+ | `@libs-ui/services-http-request` | `0.2.355-14` | HTTP request service |
377
+ | `@libs-ui/utils` | `0.2.355-14` | Utilities |
378
+ | `@ngx-translate/core` | `^15.0.0` | Đa ngôn ngữ |
379
+
380
+ ## Công nghệ
381
+
382
+ | Technology | Version | Purpose |
383
+ |------------|---------|---------|
384
+ | Angular | 18+ | Framework |
385
+ | Angular Signals | - | State management |
386
+ | TailwindCSS | 3.x | Styling |
387
+ | OnPush | - | Change Detection |
388
+
389
+ ## Demo
390
+
391
+ ```bash
392
+ npx nx serve core-ui
393
+ ```
394
+
395
+ Truy cập: http://localhost:4500/dropdown
396
+
397
+ ## Unit Tests
398
+
399
+ ```bash
400
+ # Chạy tests
401
+ npx nx test components-dropdown
402
+
403
+ # Coverage
404
+ npx nx test components-dropdown --coverage
405
+
406
+ # Watch mode
407
+ npx nx test components-dropdown --watch
408
+ ```
409
+
410
+ ## License
411
+
412
+ MIT
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@libs-ui/components-dropdown",
3
- "version": "0.2.355-9",
3
+ "version": "0.2.356-0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=18.0.0",
6
6
  "@angular/core": ">=18.0.0",
7
- "@libs-ui/components-avatar": "0.2.355-9",
8
- "@libs-ui/components-buttons-button": "0.2.355-9",
9
- "@libs-ui/components-inputs-search": "0.2.355-9",
10
- "@libs-ui/components-inputs-valid": "0.2.355-9",
11
- "@libs-ui/components-label": "0.2.355-9",
12
- "@libs-ui/components-list": "0.2.355-9",
13
- "@libs-ui/components-popover": "0.2.355-9",
14
- "@libs-ui/pipes-security-trust": "0.2.355-9",
15
- "@libs-ui/services-http-request": "0.2.355-9",
16
- "@libs-ui/utils": "0.2.355-9",
7
+ "@libs-ui/components-avatar": "0.2.356-0",
8
+ "@libs-ui/components-buttons-button": "0.2.356-0",
9
+ "@libs-ui/components-inputs-search": "0.2.356-0",
10
+ "@libs-ui/components-inputs-valid": "0.2.356-0",
11
+ "@libs-ui/components-label": "0.2.356-0",
12
+ "@libs-ui/components-list": "0.2.356-0",
13
+ "@libs-ui/components-popover": "0.2.356-0",
14
+ "@libs-ui/pipes-security-trust": "0.2.356-0",
15
+ "@libs-ui/services-http-request": "0.2.356-0",
16
+ "@libs-ui/utils": "0.2.356-0",
17
17
  "@ngx-translate/core": "^15.0.0"
18
18
  },
19
19
  "sideEffects": false,