@libs-ui/components-skeleton 0.2.356-8 → 0.2.357-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 CHANGED
@@ -1,39 +1,38 @@
1
1
  # @libs-ui/components-skeleton
2
2
 
3
- > Component hiển thị hiệu ứng loading (skeleton) cho các thành phần giao diện chưa dữ liệu.
3
+ > Component hiển thị hiệu ứng loading skeleton với shimmer animation, hỗ trợ cấu hình layout linh hoạt theo dạng rows/cols.
4
4
 
5
5
  ## Giới thiệu
6
6
 
7
- `LibsUiComponentsSkeletonComponent` là một standalone Angular component giúp tạo ra các khung xương (skeleton) loading với hiệu ứng shimmer. Component hỗ trợ cấu hình linh hoạt cho phép tạo ra các layout phức tạp như danh sách, lưới, hoặc thẻ bài viết.
7
+ `LibsUiComponentsSkeletonComponent` là standalone Angular component giúp tạo khung xương (skeleton) loading với hiệu ứng shimmer mượt mà. Component tự động tính toán số lượng item cần render dựa trên chiều cao container cha, đồng thời hỗ trợ cấu hình layout phức tạp theo dạng rows cols lồng nhau để tái hiện cấu trúc nội dung thực tế trước khi dữ liệu được tải về.
8
8
 
9
- ### Tính năng
9
+ ## Tính năng
10
10
 
11
- - ✅ Hiệu ứng Shimmer Loading mượt mà
12
- - ✅ Tính toán tự động số lượng items dựa trên height container
13
- - ✅ Hỗ trợ cấu hình Layout phức tạp (Rows & Cols nested)
14
- - ✅ Custom Styles & Classes cho từng phần tử
15
- - ✅ Tối ưu hiệu năng với `ChangeDetectionStrategy.OnPush`
11
+ - ✅ Hiệu ứng shimmer loading mượt mà (CSS animation)
12
+ - ✅ Tự động tính toán số lượng items dựa trên chiều cao container cha
13
+ - ✅ Hỗ trợ cấu hình layout phức tạp: rows cols lồng nhau nhiều cấp
14
+ - ✅ Custom class cho từng cấp: container, row, col, item
15
+ - ✅ Hỗ trợ repeat mỗi cấp (container / row / col)
16
+ - ✅ Standalone component, tối ưu hiệu năng với `ChangeDetectionStrategy.OnPush`
16
17
 
17
18
  ## Khi nào sử dụng
18
19
 
19
- - Khi đang tải dữ liệu từ API và muốn hiển thị cấu trúc trang trước.
20
- - Thay thế cho loading spinner truyền thống để cải thiện trải nghiệm người dùng (Perceived Performance).
21
- - Sử dụng trong các danh sách, bảng, hoặc chi tiết nội dung.
20
+ - Khi đang tải dữ liệu từ API và muốn hiển thị trước cấu trúc layout (perceived performance).
21
+ - Thay thế loading spinner truyền thống để cải thiện trải nghiệm người dùng.
22
+ - Trong danh sách items: comments, posts, products, notifications.
23
+ - Trong màn hình chi tiết: user profile, order detail, article.
24
+ - Trong bảng dữ liệu: table rows chưa load xong.
22
25
 
23
26
  ## Cài đặt
24
27
 
25
28
  ```bash
26
- # npm
27
29
  npm install @libs-ui/components-skeleton
28
-
29
- # yarn
30
- yarn add @libs-ui/components-skeleton
31
30
  ```
32
31
 
33
32
  ## Import
34
33
 
35
34
  ```typescript
36
- import { LibsUiComponentsSkeletonComponent } from '@libs-ui/components-skeleton';
35
+ import { LibsUiComponentsSkeletonComponent, ISkeletonConfig } from '@libs-ui/components-skeleton';
37
36
 
38
37
  @Component({
39
38
  standalone: true,
@@ -43,38 +42,49 @@ import { LibsUiComponentsSkeletonComponent } from '@libs-ui/components-skeleton'
43
42
  export class YourComponent {}
44
43
  ```
45
44
 
46
- ## Ví dụ
45
+ ## Ví dụ sử dụng
47
46
 
48
- ### Basic (Tự động tính toán)
47
+ ### 1. Basic Tự động fill container
49
48
 
50
- Component sẽ tự động tính toán số lượng dòng dựa trên chiều cao của container cha.
49
+ Component tự động tính toán số lượng dòng skeleton để lấp đầy container. Chỉ cần đặt trong một thẻ có chiều cao cụ thể.
51
50
 
52
51
  ```html
53
- <div class="h-[200px] w-full">
52
+ <!-- Template -->
53
+ <div class="h-[300px] w-full">
54
54
  <libs_ui-components-skeleton />
55
55
  </div>
56
56
  ```
57
57
 
58
- ### Custom Layout (Card Example)
58
+ ### 2. Avatar & Lines (Horizontal)
59
59
 
60
- Cấu hình để tạo ra skeleton giống một card (Avatar + Text).
60
+ phỏng layout avatar tròn hai dòng nội dung bên cạnh — phù hợp cho danh sách người dùng, notifications.
61
61
 
62
62
  ```typescript
63
- const cardConfig: ISkeletonConfig = {
64
- repeat: 1,
63
+ // component.ts
64
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
65
+
66
+ readonly avatarLinesConfig: ISkeletonConfig = {
67
+ heightContainer: 118,
68
+ classRows: 'bg-[#fff] p-[16px] rounded-[8px] !flex-row',
69
+ styleMarginBottom: 16,
65
70
  rows: [
66
71
  {
67
- classRow: 'flex gap-4 p-4 border rounded shadow-sm',
72
+ classRowLast: '!w-[40px] !h-[40px] mr-[12px] shrink-0',
73
+ item: { classIncludeItem: '!rounded-[40px]' },
74
+ },
75
+ {
76
+ classColsLast: 'h-full flex-col ',
68
77
  cols: [
69
- // Avatar Column
70
78
  {
71
- item: { classIncludeItem: 'w-12 h-12 rounded-full' },
79
+ classColLast: '!h-full mb-[8px]',
80
+ item: { classIncludeItem: '!rounded-[4px]' },
72
81
  },
73
- // Text Column
74
82
  {
75
- classCol: 'flex-1 flex flex-col gap-2 justify-center',
76
- item: { classIncludeItem: 'h-4 w-3/4 rounded' },
77
- cols: [{ item: { classIncludeItem: 'h-3 w-1/2 rounded' } }],
83
+ classColLast: '!h-auto',
84
+ item: {
85
+ classInclude: 'w-full !h-[20px]',
86
+ classIncludeItem: '!rounded-[4px]',
87
+ },
78
88
  },
79
89
  ],
80
90
  },
@@ -83,63 +93,307 @@ const cardConfig: ISkeletonConfig = {
83
93
  ```
84
94
 
85
95
  ```html
86
- <libs_ui-components-skeleton [config]="cardConfig" />
96
+ <!-- Template -->
97
+ <div class="h-[300px]">
98
+ <libs_ui-components-skeleton [config]="avatarLinesConfig" />
99
+ </div>
87
100
  ```
88
101
 
89
- ## API
102
+ ### 3. Card với Thumbnail nhỏ
90
103
 
91
- ### LibsUiComponentsSkeletonComponent
104
+ Layout card có tiêu đề lớn phía trên và thumbnail kèm nội dung bên dưới — phù hợp cho danh sách bài viết, sản phẩm.
92
105
 
93
- Selector: `libs_ui-components-skeleton`
106
+ ```typescript
107
+ // component.ts
108
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
94
109
 
95
- #### Inputs
110
+ readonly cardThumbConfig: ISkeletonConfig = {
111
+ heightContainer: 158,
112
+ classRows: 'bg-[#fff] p-[16px] rounded-[8px]',
113
+ styleMarginBottom: 16,
114
+ rows: [
115
+ {
116
+ classRowLast: '!h-auto',
117
+ item: {
118
+ classInclude: 'w-full !h-[34px] mb-[8px]',
119
+ classIncludeItem: '!rounded-[4px]',
120
+ },
121
+ },
122
+ {
123
+ classColsLast: 'h-full ',
124
+ cols: [
125
+ {
126
+ classColLast: '!w-[80px] mr-[12px] shrink-0',
127
+ item: { classIncludeItem: '!rounded-[4px]' },
128
+ },
129
+ {
130
+ item: { classIncludeItem: '!rounded-[4px]' },
131
+ },
132
+ ],
133
+ },
134
+ ],
135
+ };
136
+ ```
96
137
 
97
- | Property | Type | Default | Description |
98
- | ---------- | ----------------- | ------- | ----------------------------- |
99
- | `[config]` | `ISkeletonConfig` | `{}` | Cấu hình layout cho skeleton. |
138
+ ```html
139
+ <!-- Template -->
140
+ <div class="h-[400px]">
141
+ <libs_ui-components-skeleton [config]="cardThumbConfig" />
142
+ </div>
143
+ ```
100
144
 
101
- ### Interfaces
145
+ ### 4. Header & Content (phức hợp)
102
146
 
103
- #### ISkeletonConfig
147
+ Layout phức hợp: header gồm avatar tròn + tiêu đề, bên dưới là vùng nội dung nhiều dòng — phù hợp cho detail page.
148
+
149
+ ```typescript
150
+ // component.ts
151
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
152
+
153
+ readonly headerContentConfig: ISkeletonConfig = {
154
+ heightContainer: 202,
155
+ classRows: 'bg-[#fff] p-[16px] rounded-[8px]',
156
+ styleMarginBottom: 16,
157
+ rows: [
158
+ {
159
+ classRowLast: '!h-auto ',
160
+ cols: [
161
+ {
162
+ classColLast: '!w-[40px] !h-[40px] mr-[12px] shrink-0',
163
+ item: { classIncludeItem: '!rounded-[40px]' },
164
+ },
165
+ {
166
+ classColLast: '!h-auto',
167
+ item: {
168
+ classInclude: 'w-full !h-[40px] mb-[8px]',
169
+ classIncludeItem: '!rounded-[4px]',
170
+ },
171
+ },
172
+ ],
173
+ },
174
+ {
175
+ classColsLast: 'h-full flex-col ',
176
+ cols: [
177
+ {
178
+ classColLast: '!h-full mb-[8px]',
179
+ item: { classIncludeItem: '!rounded-[4px]' },
180
+ },
181
+ {
182
+ classColLast: '!h-auto',
183
+ item: {
184
+ classInclude: 'w-full !h-[20px]',
185
+ classIncludeItem: '!rounded-[4px]',
186
+ },
187
+ },
188
+ ],
189
+ },
190
+ ],
191
+ };
192
+ ```
193
+
194
+ ```html
195
+ <!-- Template -->
196
+ <div class="h-[500px]">
197
+ <libs_ui-components-skeleton [config]="headerContentConfig" />
198
+ </div>
199
+ ```
200
+
201
+ ### 5. Sectioned Content (Table header + Body)
202
+
203
+ Layout chia hai vùng: header cố định phía trên (không có shimmer) và vùng nội dung bên dưới — phù hợp cho table loading.
204
+
205
+ ```typescript
206
+ // component.ts
207
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
208
+
209
+ readonly sectionedConfig: ISkeletonConfig = {
210
+ rows: [
211
+ {
212
+ classRowLast: '!h-[40px] bg-[#fff]',
213
+ item: { classIncludeItem: '!h-[40px] !rounded-none opacity-[0]' },
214
+ },
215
+ {
216
+ classRowLast: 'h-full flex-col',
217
+ item: { classIncludeItem: '!rounded-none' },
218
+ },
219
+ ],
220
+ };
221
+ ```
222
+
223
+ ```html
224
+ <!-- Template -->
225
+ <div class="h-[400px]">
226
+ <libs_ui-components-skeleton [config]="sectionedConfig" />
227
+ </div>
228
+ ```
229
+
230
+ ### 6. Small Icon & Line
231
+
232
+ Layout tối giản: icon nhỏ và một dòng nội dung bên cạnh — phù hợp cho menu items, breadcrumb.
233
+
234
+ ```typescript
235
+ // component.ts
236
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
237
+
238
+ readonly iconLineConfig: ISkeletonConfig = {
239
+ heightContainer: 116,
240
+ classRows: 'bg-[#fff] rounded-[8px] !flex-row',
241
+ styleMarginBottom: 16,
242
+ rows: [
243
+ {
244
+ classRowLast: '!w-[32px] !h-[32px] mr-[12px] shrink-0',
245
+ item: { classIncludeItem: '!rounded-[32px]' },
246
+ },
247
+ {
248
+ classColsLast: 'h-full flex-col ',
249
+ cols: [
250
+ {
251
+ classColLast: '!h-full',
252
+ item: { classIncludeItem: '!rounded-[4px]' },
253
+ },
254
+ ],
255
+ },
256
+ ],
257
+ };
258
+ ```
259
+
260
+ ```html
261
+ <!-- Template -->
262
+ <div class="h-[250px]">
263
+ <libs_ui-components-skeleton [config]="iconLineConfig" />
264
+ </div>
265
+ ```
266
+
267
+ ## @Input()
268
+
269
+ | Input | Type | Default | Mô tả | Ví dụ |
270
+ |---|---|---|---|---|
271
+ | `[config]` | `ISkeletonConfig` | `{}` | Object cấu hình toàn bộ layout skeleton: số lần lặp, chiều cao, rows/cols lồng nhau và class tuỳ chỉnh cho từng cấp | `[config]="avatarLinesConfig"` |
272
+
273
+ ## Types & Interfaces
274
+
275
+ ```typescript
276
+ import { ISkeletonConfig } from '@libs-ui/components-skeleton';
277
+ ```
278
+
279
+ ### ISkeletonConfig
104
280
 
105
281
  ```typescript
106
282
  export interface ISkeletonConfig {
107
- repeat?: number; // Số lần lặp lại toàn bộ structure
108
- heightContainer?: number; // Chiều cao cố định cho mỗi block
109
- classRows?: string; // Class cho container của rows
110
- styleMarginBottom?: number; // Margin bottom giữa các items (px)
283
+ /**
284
+ * Số lần lặp lại toàn bộ layout.
285
+ * Khi không set, component tự tính dựa trên chiều cao container / heightContainer.
286
+ */
287
+ repeat?: number;
288
+
289
+ /**
290
+ * Chiều cao cố định (px) cho mỗi block lặp lại.
291
+ * Khi không set, component đọc offsetHeight của container thực.
292
+ */
293
+ heightContainer?: number;
294
+
295
+ /** Class áp dụng lên wrapper container bao ngoài tất cả rows. */
296
+ classRows?: string;
297
+
298
+ /** Khoảng cách margin-bottom (px) giữa các block lặp (trừ block cuối). */
299
+ styleMarginBottom?: number;
300
+
111
301
  rows?: Array<{
112
- repeat?: number; // Số lần lặp lại row
113
- classRow?: string; // Class cho row
114
- classRowLast?: string; // Class cho row cuối cùng
115
- item?: ISkeletonItem; // Config cho item trong row (nếu )
116
- classCols?: string; // Class cho container của cols
117
- classColsLast?: string; // Class cho container cols cuối
302
+ /** Số lần lặp lại row này. */
303
+ repeat?: number;
304
+
305
+ /** Class cho row (áp dụng cho tất cả row trừ row cuối cùng). */
306
+ classRow?: string;
307
+
308
+ /** Class dành riêng cho row cuối cùng trong vòng lặp repeat. */
309
+ classRowLast?: string;
310
+
311
+ /**
312
+ * Config skeleton item trực tiếp trong row (không qua cols).
313
+ * Dùng khi row chỉ có 1 skeleton block duy nhất.
314
+ */
315
+ item?: ISkeletonItem;
316
+
317
+ /** Class cho container bao ngoài tất cả cols (áp dụng cho tất cả trừ cuối). */
318
+ classCols?: string;
319
+
320
+ /** Class cho container cols dành riêng cho row cuối. */
321
+ classColsLast?: string;
322
+
118
323
  cols?: Array<{
119
- repeat?: number; // Số lần lặp lại col
120
- classCol?: string; // Class cho col
121
- classColLast?: string; // Class cho col cuối
122
- item?: ISkeletonItem; // Config cho item trong col
324
+ /** Số lần lặp lại col này. */
325
+ repeat?: number;
326
+
327
+ /** Class cho col (áp dụng cho tất cả col trừ col cuối cùng). */
328
+ classCol?: string;
329
+
330
+ /** Class dành riêng cho col cuối cùng trong vòng lặp repeat. */
331
+ classColLast?: string;
332
+
333
+ /** Config skeleton item bên trong col. */
334
+ item?: ISkeletonItem;
123
335
  }>;
124
336
  }>;
125
337
  }
126
338
  ```
127
339
 
128
- #### ISkeletonItem
340
+ ### ISkeletonItem
129
341
 
130
342
  ```typescript
131
343
  interface ISkeletonItem {
132
- classIncludeItem?: string; // Class áp dụng trực tiếp lên skeleton div (bg, animation)
133
- classInclude?: string; // Class áp dụng lên wrapper của skeleton item
134
- class?: string; // (Legacy)
135
- styleDefault?: boolean; // Nếu true, thêm margin-bottom default (16px) trừ item cuối
344
+ /**
345
+ * Class áp dụng lên thẻ div skeleton trực tiếp (có class .skeleton và animation).
346
+ * Dùng để override kích thước, border-radius của skeleton block.
347
+ * dụ: '!rounded-[40px]' cho avatar tròn, '!rounded-[4px]' cho text line.
348
+ */
349
+ classIncludeItem?: string;
350
+
351
+ /**
352
+ * Class áp dụng lên wrapper bên ngoài bao các skeleton divs.
353
+ * Dùng để kiểm soát chiều cao wrapper khi skeleton item có height cố định.
354
+ * Ví dụ: 'w-full !h-[20px]' để giới hạn chiều cao vùng chứa.
355
+ */
356
+ classInclude?: string;
357
+
358
+ /** (Legacy) Class không khuyến nghị dùng trong code mới. */
359
+ class?: string;
360
+
361
+ /**
362
+ * Khi true, tự động thêm margin-bottom 16px cho tất cả item trừ item cuối cùng.
363
+ * Tiện dùng khi repeat > 1 và không muốn config styleMarginBottom ở cấp ngoài.
364
+ */
365
+ styleDefault?: boolean;
136
366
  }
137
367
  ```
138
368
 
139
- ## Styling
369
+ ## Logic ẩn quan trọng
370
+
371
+ ### Auto-calculation trong ngAfterViewInit
372
+
373
+ Component thực hiện tính toán tự động sau khi view khởi tạo (`ngAfterViewInit` + `setTimeout`):
374
+
375
+ 1. **Khi `config.rows` rỗng hoặc không có**: Tự tạo 1 row mặc định với item rỗng.
376
+ 2. **Khi `heightContainer` không được set**: Đọc `offsetHeight` thực tế của container DOM để làm chiều cao mỗi block.
377
+ 3. **Khi `repeat` không được set**: Tính tự động bằng công thức:
378
+ ```
379
+ repeat = Math.ceil(offsetHeight / (heightContainer + styleMarginBottom))
380
+ ```
381
+ Kết quả: skeleton tự lấp đầy container cha mà không cần set số dòng thủ công.
382
+ 4. **Khi `repeat` được set**: `heightContainer` được chia đều theo `repeat` để các block chia đều chiều cao container.
383
+
384
+ ### Cơ chế classRowLast / classColLast
385
+
386
+ Khi một row hoặc col có `repeat > 1`, phần tử cuối cùng trong vòng lặp sẽ áp dụng class `classRowLast` (hoặc `classColLast`) thay vì `classRow` (hoặc `classCol`). Điều này cho phép tuỳ chỉnh style riêng cho phần tử cuối, ví dụ: bỏ margin-bottom ở item cuối cùng.
387
+
388
+ ## Lưu ý quan trọng
389
+
390
+ ⚠️ **Container phải có chiều cao xác định**: Component đọc `offsetHeight` của container cha để tính toán. Nếu container không có `height` hoặc `min-height` cụ thể, `offsetHeight = 0` và skeleton sẽ không render đúng. Luôn bọc trong thẻ có chiều cao rõ ràng (`h-[300px]`, `min-h-[200px]`, hoặc `h-full` trong container đã có height).
391
+
392
+ ⚠️ **setTimeout trong ngAfterViewInit**: Việc tính toán được bọc trong `setTimeout` (0ms) để đảm bảo DOM đã render xong và `offsetHeight` trả về giá trị chính xác. Điều này là bình thường và có chủ ý.
393
+
394
+ ⚠️ **classRowLast vs classRow**: Khi `repeat = 1` (mặc định), phần tử duy nhất được coi là "cả first lẫn last", tức là `classRowLast` sẽ được áp dụng. Thiết kế này có nghĩa là với row không repeat, bạn chỉ cần dùng `classRowLast` để set class cho row đó.
140
395
 
141
- Component sử dụng CSS Animation `mo-skeleton-loading` linear-gradient background để tạo hiệu ứng shimmer.
142
- Mặc định skeleton sẽ fill size của container cha (`w-full h-full`).
396
+ ⚠️ **Input `config` transform**: Input `config` được khai báo với transform `(value) => value || {}`. Truyền `undefined` hoặc `null` cho `[config]` sẽ được tự động chuyển thành `{}` (config mặc định), component sẽ không crash.
143
397
 
144
398
  ## Demo
145
399
 
@@ -147,4 +401,4 @@ Mặc định skeleton sẽ fill size của container cha (`w-full h-full`).
147
401
  npx nx serve core-ui
148
402
  ```
149
403
 
150
- Truy cập: `http://localhost:4500/skeleton`
404
+ Truy cập: http://localhost:4500/skeleton
@@ -1,6 +1,6 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { signal, input, viewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { signal, input, viewChild, Component, ChangeDetectionStrategy } from '@angular/core';
4
4
 
5
5
  class LibsUiComponentsSkeletonComponent {
6
6
  configRender = signal({});
@@ -1 +1 @@
1
- {"version":3,"file":"libs-ui-components-skeleton.mjs","sources":["../../../../../libs-ui/components/skeleton/src/skeleton.component.ts","../../../../../libs-ui/components/skeleton/src/skeleton.component.html","../../../../../libs-ui/components/skeleton/src/libs-ui-components-skeleton.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, input, signal, viewChild } from '@angular/core';\nimport { ISkeletonConfig } from './interfaces/skeleton-item.interface';\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-skeleton',\n templateUrl: 'skeleton.component.html',\n styleUrls: ['./skeleton.component.scss'],\n standalone: true,\n imports: [NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsSkeletonComponent implements AfterViewInit {\n protected configRender = signal<ISkeletonConfig>({});\n\n readonly config = input<ISkeletonConfig, ISkeletonConfig | undefined>({}, { transform: (value) => value || {} });\n\n readonly skeletonRef = viewChild.required<ElementRef>('skeletonRef');\n\n ngAfterViewInit(): void {\n setTimeout(() => {\n this.configRender.update((config) => {\n config = this.config();\n let offsetHeight = this.skeletonRef().nativeElement.offsetHeight;\n if (!config.rows?.length) {\n config.rows = [{ item: {} }];\n }\n if (!config.heightContainer) {\n config.heightContainer = offsetHeight - (config.styleMarginBottom || 0);\n }\n if (config.repeat) {\n offsetHeight = offsetHeight || config.heightContainer || config.styleMarginBottom || 0;\n config.heightContainer = offsetHeight / config.repeat - (config.styleMarginBottom || 0);\n }\n config.repeat = Math.ceil(offsetHeight / ((config.heightContainer || 0) + (config.styleMarginBottom || 0)));\n\n return { ...config };\n });\n });\n }\n}\n","<div\n #skeletonRef\n class=\"w-full h-full\">\n @if (configRender(); as config) {\n @for (configRepeat of [].constructor(config.repeat || 1); track $index; let lastItem = $last) {\n <div\n [style.marginBottom.px]=\"config.styleMarginBottom && !lastItem ? config.styleMarginBottom : 0\"\n [style.height]=\"config.heightContainer ? config.heightContainer + 'px' : '100%'\"\n [class]=\"(config.classRows || '') + ' flex flex-col'\">\n @for (row of config.rows; track $index) {\n @for (rowRepeat of [].constructor(row.repeat || 1); track $index; let firstRow = $first; let lastRow = $last) {\n <div [class]=\"((lastRow || (firstRow && lastRow) ? row.classRowLast : row.classRow) || '') + ' flex flex-col w-full h-full'\">\n @if (row.item) {\n <ng-container *ngTemplateOutlet=\"templateSkeleton; context: { config: row.item }\" />\n }\n <div [class]=\"((lastRow || (firstRow && lastRow) ? row.classColsLast : row.classCols) || '') + 'flex w-full'\">\n @for (col of row.cols; track $index) {\n @for (colRepeat of [].constructor(col.repeat || 1); track $index; let firstCol = $first; let lastCol = $last) {\n <div [class]=\"((lastCol || (firstCol && lastCol) ? col.classColLast : col.classCol) || '') + ' flex w-full'\">\n @if (col.item) {\n <ng-container *ngTemplateOutlet=\"templateSkeleton; context: { config: col.item }\" />\n }\n </div>\n }\n }\n </div>\n </div>\n }\n }\n </div>\n }\n }\n</div>\n\n<ng-template\n #templateSkeleton\n let-config=\"config\">\n <div [class]=\"config?.classInclude || 'w-full h-full'\">\n @for (_ of [].constructor(config.repeat || 1); track $index; let last = $last) {\n <div\n class=\"w-full h-full skeleton rounded-[8px] {{ config?.classIncludeItem || '' }}\"\n [class.mb-[16px]]=\"config?.styleDefault && !last\"></div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;MAaa,iCAAiC,CAAA;AAClC,IAAA,YAAY,GAAG,MAAM,CAAkB,EAAE,CAAC;AAE3C,IAAA,MAAM,GAAG,KAAK,CAA+C,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,KAAK,KAAK,IAAI,EAAE,EAAE,CAAC;AAEvG,IAAA,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAa,aAAa,CAAC;IAEpE,eAAe,GAAA;QACb,UAAU,CAAC,MAAK;YACd,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,KAAI;AAClC,gBAAA,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;gBACtB,IAAI,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,aAAa,CAAC,YAAY;AAChE,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE;oBACxB,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;gBAC9B;AACA,gBAAA,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE;AAC3B,oBAAA,MAAM,CAAC,eAAe,GAAG,YAAY,IAAI,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC;gBACzE;AACA,gBAAA,IAAI,MAAM,CAAC,MAAM,EAAE;AACjB,oBAAA,YAAY,GAAG,YAAY,IAAI,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,iBAAiB,IAAI,CAAC;AACtF,oBAAA,MAAM,CAAC,eAAe,GAAG,YAAY,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC;gBACzF;gBACA,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,KAAK,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC;AAE3G,gBAAA,OAAO,EAAE,GAAG,MAAM,EAAE;AACtB,YAAA,CAAC,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;wGA3BW,iCAAiC,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAjC,iCAAiC,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,aAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECb9C,+lEA6CA,EAAA,MAAA,EAAA,CAAA,kQAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDnCY,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;4FAGf,iCAAiC,EAAA,UAAA,EAAA,CAAA;kBAT7C,SAAS;+BAEE,6BAA6B,EAAA,UAAA,EAG3B,IAAI,EAAA,OAAA,EACP,CAAC,gBAAgB,CAAC,EAAA,eAAA,EACV,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,+lEAAA,EAAA,MAAA,EAAA,CAAA,kQAAA,CAAA,EAAA;;;AEXjD;;AAEG;;;;"}
1
+ {"version":3,"file":"libs-ui-components-skeleton.mjs","sources":["../../../../../libs-ui/components/skeleton/src/skeleton.component.ts","../../../../../libs-ui/components/skeleton/src/skeleton.component.html","../../../../../libs-ui/components/skeleton/src/libs-ui-components-skeleton.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, input, signal, viewChild } from '@angular/core';\nimport { ISkeletonConfig } from './interfaces/skeleton-item.interface';\n\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'libs_ui-components-skeleton',\n templateUrl: 'skeleton.component.html',\n styleUrls: ['./skeleton.component.scss'],\n standalone: true,\n imports: [NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiComponentsSkeletonComponent implements AfterViewInit {\n protected configRender = signal<ISkeletonConfig>({});\n\n readonly config = input<ISkeletonConfig, ISkeletonConfig | undefined>({}, { transform: (value) => value || {} });\n\n readonly skeletonRef = viewChild.required<ElementRef>('skeletonRef');\n\n ngAfterViewInit(): void {\n setTimeout(() => {\n this.configRender.update((config) => {\n config = this.config();\n let offsetHeight = this.skeletonRef().nativeElement.offsetHeight;\n if (!config.rows?.length) {\n config.rows = [{ item: {} }];\n }\n if (!config.heightContainer) {\n config.heightContainer = offsetHeight - (config.styleMarginBottom || 0);\n }\n if (config.repeat) {\n offsetHeight = offsetHeight || config.heightContainer || config.styleMarginBottom || 0;\n config.heightContainer = offsetHeight / config.repeat - (config.styleMarginBottom || 0);\n }\n config.repeat = Math.ceil(offsetHeight / ((config.heightContainer || 0) + (config.styleMarginBottom || 0)));\n\n return { ...config };\n });\n });\n }\n}\n","<div\n #skeletonRef\n class=\"w-full h-full\">\n @if (configRender(); as config) {\n @for (configRepeat of [].constructor(config.repeat || 1); track $index; let lastItem = $last) {\n <div\n [style.marginBottom.px]=\"config.styleMarginBottom && !lastItem ? config.styleMarginBottom : 0\"\n [style.height]=\"config.heightContainer ? config.heightContainer + 'px' : '100%'\"\n [class]=\"(config.classRows || '') + ' flex flex-col'\">\n @for (row of config.rows; track $index) {\n @for (rowRepeat of [].constructor(row.repeat || 1); track $index; let firstRow = $first; let lastRow = $last) {\n <div [class]=\"((lastRow || (firstRow && lastRow) ? row.classRowLast : row.classRow) || '') + ' flex flex-col w-full h-full'\">\n @if (row.item) {\n <ng-container *ngTemplateOutlet=\"templateSkeleton; context: { config: row.item }\" />\n }\n <div [class]=\"((lastRow || (firstRow && lastRow) ? row.classColsLast : row.classCols) || '') + 'flex w-full'\">\n @for (col of row.cols; track $index) {\n @for (colRepeat of [].constructor(col.repeat || 1); track $index; let firstCol = $first; let lastCol = $last) {\n <div [class]=\"((lastCol || (firstCol && lastCol) ? col.classColLast : col.classCol) || '') + ' flex w-full'\">\n @if (col.item) {\n <ng-container *ngTemplateOutlet=\"templateSkeleton; context: { config: col.item }\" />\n }\n </div>\n }\n }\n </div>\n </div>\n }\n }\n </div>\n }\n }\n</div>\n\n<ng-template\n #templateSkeleton\n let-config=\"config\">\n <div [class]=\"config?.classInclude || 'w-full h-full'\">\n @for (_ of [].constructor(config.repeat || 1); track $index; let last = $last) {\n <div\n class=\"w-full h-full skeleton rounded-[8px] {{ config?.classIncludeItem || '' }}\"\n [class.mb-[16px]]=\"config?.styleDefault && !last\"></div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libs-ui/components-skeleton",
3
- "version": "0.2.356-8",
3
+ "version": "0.2.357-0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=18.0.0",
6
6
  "@angular/core": ">=18.0.0"