@tacdaed/fragments 1.0.0-beta.0 → 1.0.0-beta.2

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 (248) hide show
  1. package/README.md +2 -18
  2. package/ng-package.json +25 -0
  3. package/package.json +22 -29
  4. package/src/lib/components/accordion/accordion.component.html +103 -0
  5. package/src/lib/components/accordion/accordion.component.scss +382 -0
  6. package/src/lib/components/accordion/accordion.component.spec.ts +147 -0
  7. package/src/lib/components/accordion/accordion.component.ts +211 -0
  8. package/src/lib/components/accordion/accordion.type.ts +82 -0
  9. package/src/lib/components/breadcrumb/breadcrumb.component.html +43 -0
  10. package/src/lib/components/breadcrumb/breadcrumb.component.scss +112 -0
  11. package/src/lib/components/breadcrumb/breadcrumb.component.spec.ts +33 -0
  12. package/src/lib/components/breadcrumb/breadcrumb.component.ts +103 -0
  13. package/src/lib/components/breadcrumb/breadcrumb.interface.ts +7 -0
  14. package/src/lib/components/button/button.component.html +57 -0
  15. package/src/lib/components/button/button.component.scss +445 -0
  16. package/src/lib/components/button/button.component.spec.ts +99 -0
  17. package/src/lib/components/button/button.component.ts +143 -0
  18. package/src/lib/components/button/button.type.ts +7 -0
  19. package/src/lib/components/card/card.component.html +44 -0
  20. package/src/lib/components/card/card.component.scss +114 -0
  21. package/src/lib/components/card/card.component.spec.ts +65 -0
  22. package/src/lib/components/card/card.component.ts +21 -0
  23. package/src/lib/components/card/card.type.ts +3 -0
  24. package/src/lib/components/code-block/code-block.component.html +55 -0
  25. package/src/lib/components/code-block/code-block.component.scss +122 -0
  26. package/src/lib/components/code-block/code-block.component.spec.ts +81 -0
  27. package/src/lib/components/code-block/code-block.component.ts +302 -0
  28. package/src/lib/components/code-block/code-block.interface.ts +28 -0
  29. package/src/lib/components/code-block/code-block.type.ts +73 -0
  30. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.html +14 -0
  31. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.scss +20 -0
  32. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.spec.ts +38 -0
  33. package/src/lib/components/decorative/sparkle-field/sparkle-field.component.ts +181 -0
  34. package/src/lib/components/input/input-base.ts +187 -0
  35. package/src/lib/components/input/input-calendar/input-calendar.component.html +76 -0
  36. package/src/lib/components/input/input-calendar/input-calendar.component.scss +179 -0
  37. package/src/lib/components/input/input-calendar/input-calendar.component.spec.ts +44 -0
  38. package/src/lib/components/input/input-calendar/input-calendar.component.ts +299 -0
  39. package/src/lib/components/input/input-checkbox/input-checkbox.component.html +37 -0
  40. package/src/lib/components/input/input-checkbox/input-checkbox.component.scss +128 -0
  41. package/src/lib/components/input/input-checkbox/input-checkbox.component.spec.ts +43 -0
  42. package/src/lib/components/input/input-checkbox/input-checkbox.component.ts +112 -0
  43. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.html +43 -0
  44. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.scss +140 -0
  45. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.spec.ts +62 -0
  46. package/src/lib/components/input/input-checkbox-group/input-checkbox-group.component.ts +136 -0
  47. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.html +81 -0
  48. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.scss +228 -0
  49. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.spec.ts +62 -0
  50. package/src/lib/components/input/input-clock-picker/input-clock-picker.component.ts +178 -0
  51. package/src/lib/components/input/input-consts.ts +132 -0
  52. package/src/lib/components/input/input-date/input-date-validators.ts +41 -0
  53. package/src/lib/components/input/input-date/input-date.component.html +41 -0
  54. package/src/lib/components/input/input-date/input-date.component.scss +95 -0
  55. package/src/lib/components/input/input-date/input-date.component.spec.ts +43 -0
  56. package/src/lib/components/input/input-date/input-date.component.ts +359 -0
  57. package/src/lib/components/input/input-date-time/input-date-time.component.html +70 -0
  58. package/src/lib/components/input/input-date-time/input-date-time.component.scss +133 -0
  59. package/src/lib/components/input/input-date-time/input-date-time.component.spec.ts +36 -0
  60. package/src/lib/components/input/input-date-time/input-date-time.component.ts +387 -0
  61. package/src/lib/components/input/input-file-upload/input-file-upload.component.html +89 -0
  62. package/src/lib/components/input/input-file-upload/input-file-upload.component.scss +171 -0
  63. package/src/lib/components/input/input-file-upload/input-file-upload.component.spec.ts +43 -0
  64. package/src/lib/components/input/input-file-upload/input-file-upload.component.ts +351 -0
  65. package/src/lib/components/input/input-interface.ts +8 -0
  66. package/src/lib/components/input/input-number/input-number-validators.ts +0 -0
  67. package/src/lib/components/input/input-number/input-number.component.html +51 -0
  68. package/src/lib/components/input/input-number/input-number.component.scss +140 -0
  69. package/src/lib/components/input/input-number/input-number.component.spec.ts +44 -0
  70. package/src/lib/components/input/input-number/input-number.component.ts +343 -0
  71. package/src/lib/components/input/input-radio-group/input-radio-group.component.html +44 -0
  72. package/src/lib/components/input/input-radio-group/input-radio-group.component.scss +139 -0
  73. package/src/lib/components/input/input-radio-group/input-radio-group.component.spec.ts +58 -0
  74. package/src/lib/components/input/input-radio-group/input-radio-group.component.ts +132 -0
  75. package/src/lib/components/input/input-slider/input-slider.component.html +111 -0
  76. package/src/lib/components/input/input-slider/input-slider.component.scss +203 -0
  77. package/src/lib/components/input/input-slider/input-slider.component.spec.ts +46 -0
  78. package/src/lib/components/input/input-slider/input-slider.component.ts +410 -0
  79. package/src/lib/components/input/input-text/input-text-validators.ts +67 -0
  80. package/src/lib/components/input/input-text/input-text.component.html +71 -0
  81. package/src/lib/components/input/input-text/input-text.component.scss +118 -0
  82. package/src/lib/components/input/input-text/input-text.component.spec.ts +55 -0
  83. package/src/lib/components/input/input-text/input-text.component.ts +215 -0
  84. package/src/lib/components/input/input-time/input-time-validators.ts +42 -0
  85. package/src/lib/components/input/input-time/input-time.component.html +92 -0
  86. package/src/lib/components/input/input-time/input-time.component.scss +191 -0
  87. package/src/lib/components/input/input-time/input-time.component.spec.ts +39 -0
  88. package/src/lib/components/input/input-time/input-time.component.ts +691 -0
  89. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.html +36 -0
  90. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.scss +121 -0
  91. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.spec.ts +54 -0
  92. package/src/lib/components/input/input-toggle-switch/input-toggle-switch.component.ts +117 -0
  93. package/src/lib/components/input/input-type.ts +18 -0
  94. package/src/lib/components/input/input-validation/input-validation.component.html +19 -0
  95. package/src/lib/components/input/input-validation/input-validation.component.scss +39 -0
  96. package/src/lib/components/input/input-validation/input-validation.component.spec.ts +45 -0
  97. package/src/lib/components/input/input-validation/input-validation.component.ts +13 -0
  98. package/src/lib/components/input/input.pipe.ts +14 -0
  99. package/src/lib/components/layout/container/container.component.html +1 -0
  100. package/src/lib/components/layout/container/container.component.scss +33 -0
  101. package/src/lib/components/layout/container/container.component.ts +32 -0
  102. package/src/lib/components/layout/container/container.type.ts +1 -0
  103. package/src/lib/components/layout/divider/divider.component.html +1 -0
  104. package/src/lib/components/layout/divider/divider.component.scss +60 -0
  105. package/src/lib/components/layout/divider/divider.component.ts +38 -0
  106. package/src/lib/components/layout/divider/divider.type.ts +2 -0
  107. package/src/lib/components/layout/section/section.component.html +21 -0
  108. package/src/lib/components/layout/section/section.component.scss +43 -0
  109. package/src/lib/components/layout/section/section.component.ts +33 -0
  110. package/src/lib/components/layout/section/section.type.ts +2 -0
  111. package/src/lib/components/layout/separator/separator.component.html +9 -0
  112. package/src/lib/components/layout/separator/separator.component.scss +52 -0
  113. package/src/lib/components/layout/separator/separator.component.ts +25 -0
  114. package/src/lib/components/layout/separator/separator.type.ts +1 -0
  115. package/src/lib/components/loader/content-blur/content-blur.component.html +13 -0
  116. package/src/lib/components/loader/content-blur/content-blur.component.scss +43 -0
  117. package/src/lib/components/loader/content-blur/content-blur.component.spec.ts +42 -0
  118. package/src/lib/components/loader/content-blur/content-blur.component.ts +34 -0
  119. package/src/lib/components/loader/loader.type.ts +2 -0
  120. package/src/lib/components/loader/progress-bar/progress-bar.component.html +26 -0
  121. package/src/lib/components/loader/progress-bar/progress-bar.component.scss +151 -0
  122. package/src/lib/components/loader/progress-bar/progress-bar.component.spec.ts +47 -0
  123. package/src/lib/components/loader/progress-bar/progress-bar.component.ts +28 -0
  124. package/src/lib/components/loader/progress-bar/progress-bar.type.ts +8 -0
  125. package/src/lib/components/loader/pulse-loader/pulse-loader.component.html +12 -0
  126. package/src/lib/components/loader/pulse-loader/pulse-loader.component.scss +202 -0
  127. package/src/lib/components/loader/pulse-loader/pulse-loader.component.spec.ts +55 -0
  128. package/src/lib/components/loader/pulse-loader/pulse-loader.component.ts +73 -0
  129. package/src/lib/components/loader/pulse-loader/pulse-loader.type.ts +6 -0
  130. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.html +13 -0
  131. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.scss +113 -0
  132. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.spec.ts +37 -0
  133. package/src/lib/components/loader/skeleton-loader/skeleton-loader.component.ts +51 -0
  134. package/src/lib/components/loader/skeleton-loader/skeleton-loader.type.ts +6 -0
  135. package/src/lib/components/loader/spinner/spinner.component.html +20 -0
  136. package/src/lib/components/loader/spinner/spinner.component.scss +137 -0
  137. package/src/lib/components/loader/spinner/spinner.component.spec.ts +43 -0
  138. package/src/lib/components/loader/spinner/spinner.component.ts +32 -0
  139. package/src/lib/components/loader/spinner/spinner.type.ts +6 -0
  140. package/src/lib/components/modal/modal.component.html +47 -0
  141. package/src/lib/components/modal/modal.component.scss +139 -0
  142. package/src/lib/components/modal/modal.component.spec.ts +60 -0
  143. package/src/lib/components/modal/modal.component.ts +83 -0
  144. package/src/lib/components/modal/modal.type.ts +9 -0
  145. package/src/lib/components/morph/blob-moph/blob-moprh.component.spec.ts +79 -0
  146. package/src/lib/components/morph/blob-moph/blob-moprh.component.ts +96 -0
  147. package/src/lib/components/morph/blob-moph/blob-morph.component.html +34 -0
  148. package/src/lib/components/morph/blob-moph/blob-morph.component.scss +7 -0
  149. package/src/lib/components/morph/morph.abstract.ts +13 -0
  150. package/src/lib/components/pagination/pagination.interface.ts +4 -0
  151. package/src/lib/components/pagination/small-pagination/small-pagination.component.html +61 -0
  152. package/src/lib/components/pagination/small-pagination/small-pagination.component.scss +187 -0
  153. package/src/lib/components/pagination/small-pagination/small-pagination.component.spec.ts +88 -0
  154. package/src/lib/components/pagination/small-pagination/small-pagination.component.ts +177 -0
  155. package/src/lib/components/selection-lists/multi-select/multi-select.component.html +170 -0
  156. package/src/lib/components/selection-lists/multi-select/multi-select.component.scss +312 -0
  157. package/src/lib/components/selection-lists/multi-select/multi-select.component.spec.ts +61 -0
  158. package/src/lib/components/selection-lists/multi-select/multi-select.component.ts +372 -0
  159. package/src/lib/components/selection-lists/selection-list/selection-list.component.html +125 -0
  160. package/src/lib/components/selection-lists/selection-list/selection-list.component.scss +267 -0
  161. package/src/lib/components/selection-lists/selection-list/selection-list.component.spec.ts +66 -0
  162. package/src/lib/components/selection-lists/selection-list/selection-list.component.ts +315 -0
  163. package/src/lib/components/selection-lists/selection-lists-base.ts +35 -0
  164. package/src/lib/components/selection-lists/selection-lists-const.ts +17 -0
  165. package/src/lib/components/selection-lists/selection-lists-interface.ts +7 -0
  166. package/src/lib/components/selection-lists/selection-lists.type.ts +1 -0
  167. package/src/lib/components/side-nav/side-nav.component.html +101 -0
  168. package/src/lib/components/side-nav/side-nav.component.scss +295 -0
  169. package/src/lib/components/side-nav/side-nav.component.spec.ts +0 -0
  170. package/src/lib/components/side-nav/side-nav.component.ts +18 -0
  171. package/src/lib/components/side-nav/side-nav.type.ts +28 -0
  172. package/src/lib/components/snackbar/snackbar.component.html +33 -0
  173. package/src/lib/components/snackbar/snackbar.component.scss +195 -0
  174. package/src/lib/components/snackbar/snackbar.component.ts +112 -0
  175. package/src/lib/components/snackbar/snackbar.type.ts +27 -0
  176. package/src/lib/components/status/chip/chip.component.html +51 -0
  177. package/src/lib/components/status/chip/chip.component.scss +149 -0
  178. package/src/lib/components/status/chip/chip.component.spec.ts +62 -0
  179. package/src/lib/components/status/chip/chip.component.ts +83 -0
  180. package/src/lib/components/status/chip/chip.type.ts +42 -0
  181. package/src/lib/components/status/directives/badge/badge.directive.spec.ts +60 -0
  182. package/src/lib/components/status/directives/badge/badge.directive.ts +190 -0
  183. package/src/lib/components/status/directives/badge/badge.interface.ts +19 -0
  184. package/src/lib/components/status/pill/pill.component.html +40 -0
  185. package/src/lib/components/status/pill/pill.component.scss +113 -0
  186. package/src/lib/components/status/pill/pill.component.spec.ts +47 -0
  187. package/src/lib/components/status/pill/pill.component.ts +83 -0
  188. package/src/lib/components/status/pill/pill.type.ts +42 -0
  189. package/src/lib/components/status/status.interface.ts +57 -0
  190. package/src/lib/components/status/status.type.ts +62 -0
  191. package/src/lib/components/status/tag/tag.component.html +39 -0
  192. package/src/lib/components/status/tag/tag.component.scss +140 -0
  193. package/src/lib/components/status/tag/tag.component.spec.ts +47 -0
  194. package/src/lib/components/status/tag/tag.component.ts +83 -0
  195. package/src/lib/components/status/tag/tag.type.ts +42 -0
  196. package/src/lib/components/stepper/stepper.component.html +83 -0
  197. package/src/lib/components/stepper/stepper.component.scss +196 -0
  198. package/src/lib/components/stepper/stepper.component.ts +482 -0
  199. package/src/lib/components/stepper/stepper.type.ts +60 -0
  200. package/src/lib/components/table/table.component.html +438 -0
  201. package/src/lib/components/table/table.component.scss +259 -0
  202. package/src/lib/components/table/table.component.spec.ts +117 -0
  203. package/src/lib/components/table/table.component.ts +215 -0
  204. package/src/lib/components/table/table.enum.ts +4 -0
  205. package/src/lib/components/table/table.function.ts +47 -0
  206. package/src/lib/components/table/table.interface.ts +143 -0
  207. package/src/lib/components/table/table.pipe.ts +62 -0
  208. package/src/lib/components/table/table.type.ts +15 -0
  209. package/src/lib/components/tabs/tabs.component.html +88 -0
  210. package/src/lib/components/tabs/tabs.component.scss +305 -0
  211. package/src/lib/components/tabs/tabs.component.spec.ts +94 -0
  212. package/src/lib/components/tabs/tabs.component.ts +282 -0
  213. package/src/lib/components/tabs/tabs.type.ts +81 -0
  214. package/src/lib/components/title-bar/title-bar.component.html +21 -0
  215. package/src/lib/components/title-bar/title-bar.component.scss +139 -0
  216. package/src/lib/components/title-bar/title-bar.component.spec.ts +44 -0
  217. package/src/lib/components/title-bar/title-bar.component.ts +13 -0
  218. package/src/lib/components/toast/toast.component.html +36 -0
  219. package/src/lib/components/toast/toast.component.scss +241 -0
  220. package/src/lib/components/toast/toast.component.ts +165 -0
  221. package/src/lib/components/toast/toast.type.ts +37 -0
  222. package/src/lib/components/toast-stack/toast-stack.component.html +30 -0
  223. package/src/lib/components/toast-stack/toast-stack.component.scss +35 -0
  224. package/src/lib/components/toast-stack/toast-stack.component.ts +51 -0
  225. package/src/lib/consts/country-prefix.ts +244 -0
  226. package/src/lib/directives/tooltip/popover.directive.ts +274 -0
  227. package/src/lib/directives/tooltip/tooltip.directive.spec.ts +86 -0
  228. package/src/lib/directives/tooltip/tooltip.directive.ts +234 -0
  229. package/src/lib/directives/tooltip/tooltip.interface.ts +29 -0
  230. package/src/lib/directives/tooltip/tooltip.type.ts +9 -0
  231. package/src/lib/interfaces/common.interfaces.ts +4 -0
  232. package/src/lib/pipes/chunk.pipe.ts +16 -0
  233. package/src/lib/pipes/safe-html.pipe.ts +14 -0
  234. package/src/lib/pipes/sanitize-html.pipe.ts +23 -0
  235. package/src/lib/types/base.types.ts +23 -0
  236. package/src/lib/types/common.types.ts +98 -0
  237. package/src/lib/types/form.types.ts +5 -0
  238. package/src/lib/utils/common.utils.ts +53 -0
  239. package/src/lib/utils/date.utils.ts +474 -0
  240. package/src/lib/utils/number.utils.ts +16 -0
  241. package/src/lib/utils/uuid.utils.ts +39 -0
  242. package/src/public-api.ts +114 -0
  243. package/tsconfig.lib.json +17 -0
  244. package/tsconfig.lib.prod.json +10 -0
  245. package/tsconfig.spec.json +9 -0
  246. package/fesm2022/fragments.mjs +0 -8928
  247. package/fesm2022/fragments.mjs.map +0 -1
  248. package/index.d.ts +0 -3929
@@ -0,0 +1,117 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { By } from '@angular/platform-browser';
3
+ import { TableComponent } from './table.component';
4
+ import { RouterTestingModule } from '@angular/router/testing';
5
+ import { EnumDirectionSortTable } from './table.enum';
6
+ import { IPaginationOutput } from '../pagination/pagination.interface';
7
+ import { IConfigTable } from './table.interface';
8
+
9
+ type RowItem = { id: number; name: string };
10
+
11
+ describe('TableComponent', () => {
12
+ let fixture: ComponentFixture<TableComponent<RowItem>>;
13
+ let component: TableComponent<RowItem>;
14
+
15
+ const data: RowItem[] = [
16
+ { id: 1, name: 'Alice' },
17
+ { id: 2, name: 'Bob' }
18
+ ];
19
+
20
+ const baseConfig: IConfigTable<RowItem> = {
21
+ tTitleBar: { title: 'Users' },
22
+ tCaption: { srOnly: 'Users table' },
23
+ tSort: {
24
+ defaultDirection: EnumDirectionSortTable.asc,
25
+ defaultFieldOrder: 'name'
26
+ },
27
+ tHead: {
28
+ columns: [
29
+ { columnId: 'name', fieldLabel: 'Name', fieldOrder: 'name' }
30
+ ]
31
+ },
32
+ tBody: {
33
+ uniqueField: 'id',
34
+ columns: [
35
+ {
36
+ cells: {
37
+ fieldArrayPosition: ['name']
38
+ }
39
+ }
40
+ ],
41
+ data: [...data]
42
+ },
43
+ tPagination: {
44
+ page: 1,
45
+ pageSize: 2,
46
+ order: 'name',
47
+ direction: EnumDirectionSortTable.asc,
48
+ totalItems: data.length,
49
+ fullData: [...data],
50
+ hasScrollTop: false
51
+ }
52
+ };
53
+
54
+ beforeEach(async () => {
55
+ await TestBed.configureTestingModule({
56
+ imports: [TableComponent, RouterTestingModule]
57
+ }).compileComponents();
58
+
59
+ fixture = TestBed.createComponent(TableComponent<RowItem>);
60
+ component = fixture.componentInstance;
61
+ component.configTable = structuredClone(baseConfig);
62
+ });
63
+
64
+ it('renders rows from data', () => {
65
+ fixture.detectChanges();
66
+
67
+ const rows = fixture.debugElement.queryAll(By.css('tbody tr'));
68
+ expect(rows.length).toBe(2);
69
+ });
70
+
71
+ it('applies row size classes', () => {
72
+ component.rowSize = 'small';
73
+
74
+ fixture.detectChanges();
75
+
76
+ const table = fixture.debugElement.query(By.css('table')).nativeElement as HTMLTableElement;
77
+ expect(table.classList).toContain('frg-table--row-small');
78
+ });
79
+
80
+ it('updates pagination when page changes', () => {
81
+ fixture.detectChanges();
82
+
83
+ const event: IPaginationOutput<RowItem> = {
84
+ page: 2,
85
+ pagedData: [{ id: 3, name: 'Carol' }]
86
+ };
87
+
88
+ component.updateCurrentPage(event);
89
+
90
+ expect(component.configTable.tPagination.page).toBe(2);
91
+ expect(component.configTable.tBody.data).toEqual([{ id: 3, name: 'Carol' }]);
92
+ });
93
+
94
+ it('toggles sorting direction on orderBy', () => {
95
+ fixture.detectChanges();
96
+
97
+ component.orderBy('name');
98
+
99
+ expect(component.configTable.tPagination.order).toBe('name');
100
+ expect(component.configTable.tPagination.direction).toBe(EnumDirectionSortTable.desc);
101
+ expect(component.configTable.tPagination.fullData?.length).toBe(2);
102
+ });
103
+
104
+ it('emits row click events when configured', () => {
105
+ const clickSpy = jasmine.createSpy('clickEvent');
106
+ component.configTable.tBody.rowClick = {
107
+ id: 'row-click',
108
+ behaviourAction: { clickEvent: clickSpy }
109
+ };
110
+
111
+ fixture.detectChanges();
112
+
113
+ component.handleRowClick(new MouseEvent('click'), data[0]);
114
+
115
+ expect(clickSpy).toHaveBeenCalledWith(data[0]);
116
+ });
117
+ });
@@ -0,0 +1,215 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import {
3
+ ChangeDetectorRef,
4
+ Component,
5
+ ElementRef,
6
+ EventEmitter,
7
+ Input,
8
+ OnInit,
9
+ Output,
10
+ ViewChild,
11
+ inject,
12
+ } from '@angular/core';
13
+ import { ReactiveFormsModule } from '@angular/forms';
14
+ import { RouterLink } from '@angular/router';
15
+ import { FragmentsTooltipDirective } from '../../directives/tooltip/tooltip.directive';
16
+ import { SafeHtmlPipe } from '../../pipes/safe-html.pipe';
17
+ import { TBaseSize, TBaseStyle } from '../../types/base.types';
18
+ import { generateId } from '../../utils/common.utils';
19
+ import { IPaginationOutput } from '../pagination/pagination.interface';
20
+ import { SmallPaginationComponent } from '../pagination/small-pagination/small-pagination.component';
21
+ import { TitleBarComponent } from '../title-bar/title-bar.component';
22
+ import { ButtonComponent } from '../button/button.component';
23
+ import { EnumDirectionSortTable } from './table.enum';
24
+ import { sortTable } from './table.function';
25
+ import {
26
+ IConfigTable,
27
+ ITBodyCell,
28
+ ITPagination,
29
+ } from './table.interface';
30
+ import {
31
+ DynamicPipe,
32
+ FieldArrayPositionPipe,
33
+ TypeOfArrayPipe,
34
+ } from './table.pipe';
35
+
36
+ @Component({
37
+ selector: 'frg-table',
38
+ imports: [
39
+ CommonModule,
40
+ RouterLink,
41
+ TitleBarComponent,
42
+ SmallPaginationComponent,
43
+ FieldArrayPositionPipe,
44
+ TypeOfArrayPipe,
45
+ SafeHtmlPipe,
46
+ DynamicPipe,
47
+ FragmentsTooltipDirective,
48
+ ButtonComponent,
49
+ ReactiveFormsModule,
50
+ ],
51
+ templateUrl: './table.component.html',
52
+ styleUrls: ['./table.component.scss']
53
+ })
54
+ export class TableComponent<T> implements OnInit {
55
+ private readonly cdr = inject(ChangeDetectorRef);
56
+
57
+ /**
58
+ * Configuration of the table
59
+ *
60
+ * @see IConfigTable
61
+ */
62
+ @Input() configTable: IConfigTable<T> = {} as IConfigTable<T>;
63
+ /**
64
+ * Style type of the table
65
+ */
66
+ @Input() styleType: TBaseStyle = 'tertiary';
67
+ /**
68
+ * Size of table rows
69
+ */
70
+ @Input() rowSize: TBaseSize = 'medium';
71
+
72
+ /**
73
+ * Event emitted to reload the table data
74
+ *
75
+ * @see ITPagination
76
+ */
77
+ @Output() reloadTableEvent = new EventEmitter<{
78
+ pagination: ITPagination<T>;
79
+ reload?: boolean;
80
+ }>();
81
+ /**
82
+ * Reference to the table header element
83
+ */
84
+ @ViewChild('tableHead', { read: ElementRef })
85
+ tableHead!: ElementRef<HTMLElement>;
86
+
87
+ /**
88
+ * Enum for sorting direction
89
+ * @see EnumDirectionSortTable
90
+ */
91
+ EnumDirectionSortTable: typeof EnumDirectionSortTable =
92
+ EnumDirectionSortTable;
93
+
94
+ /**
95
+ * ID of the first table row
96
+ */
97
+ idFirstTr = '';
98
+ /**
99
+ * Count of sorting actions performed
100
+ */
101
+ countSorting = 0;
102
+ /**
103
+ * Cached full data of the table
104
+ */
105
+ fullDataCached: T[] = [];
106
+
107
+ ngOnInit(): void {
108
+ this.idFirstTr = generateId('firstTr');
109
+ this.cdr.detectChanges();
110
+ }
111
+
112
+ /**
113
+ * Update the current page of the table
114
+ * @param event Pagination output event
115
+ * @see IPaginationOutput
116
+ */
117
+ updateCurrentPage(event: IPaginationOutput<T>): void {
118
+ this.configTable.tPagination.page = event.page;
119
+ this.configTable.tBody.data = event.pagedData;
120
+
121
+ if (this.configTable.tPagination.hasScrollTop && this.tableHead) {
122
+ const element = this.tableHead.nativeElement;
123
+ const elementRect = element.getBoundingClientRect();
124
+ const offsetTop = elementRect.top + window.scrollY;
125
+ const elementHeight = elementRect.height;
126
+ const centerScrollPosition =
127
+ offsetTop - window.innerHeight / 2 + elementHeight / 2;
128
+
129
+ window.scrollTo({ top: centerScrollPosition, behavior: 'smooth' });
130
+ }
131
+
132
+ this.cdr.detectChanges();
133
+ }
134
+
135
+ /**
136
+ * Order the table by the specified field
137
+ * @param fieldOrder Field or fields to order by
138
+ */
139
+ orderBy(fieldOrder: string | string[]): void {
140
+ if (this.configTable.tPagination.order !== fieldOrder) {
141
+ this.countSorting = 0;
142
+ this.configTable.tPagination.direction = EnumDirectionSortTable.asc;
143
+ }
144
+
145
+ this.countSorting = this.countSorting + 1;
146
+
147
+ if (this.countSorting < 3) {
148
+ this.configTable.tPagination.order = fieldOrder;
149
+ this.configTable.tPagination.direction =
150
+ this.configTable.tPagination.direction === EnumDirectionSortTable.asc
151
+ ? EnumDirectionSortTable.desc
152
+ : EnumDirectionSortTable.asc;
153
+ } else if (
154
+ this.configTable.tPagination.order ===
155
+ this.configTable.tSort?.defaultFieldOrder
156
+ ) {
157
+ this.countSorting = 0;
158
+ this.configTable.tPagination.order = fieldOrder;
159
+ this.configTable.tPagination.direction =
160
+ this.configTable.tPagination.direction === EnumDirectionSortTable.asc
161
+ ? EnumDirectionSortTable.desc
162
+ : EnumDirectionSortTable.asc;
163
+ } else {
164
+ this.countSorting = 0;
165
+ this.configTable.tPagination.order =
166
+ this.configTable.tSort?.defaultFieldOrder;
167
+ this.configTable.tPagination.direction =
168
+ this.configTable.tSort?.defaultDirection;
169
+ }
170
+
171
+ const { fullData, direction } = this.configTable.tPagination;
172
+ const sortedList = sortTable<T>(
173
+ fullData ?? [],
174
+ direction ?? EnumDirectionSortTable.asc,
175
+ this.configTable.tPagination.order ?? fieldOrder
176
+ );
177
+ this.configTable.tPagination.totalItems = sortedList.length;
178
+ this.configTable.tPagination.fullData = [...sortedList];
179
+
180
+ this.cdr.detectChanges();
181
+ }
182
+
183
+ /**
184
+ * Handle the click event on a table row
185
+ * @param _event Click event
186
+ * @param item Data item associated with the clicked row
187
+ */
188
+ handleRowClick(_event: Event, item: T): void {
189
+ if (
190
+ this.configTable.tBody.rowClick &&
191
+ this.configTable.tBody.rowClick.behaviourAction &&
192
+ this.configTable.tBody.rowClick.behaviourAction.clickEvent
193
+ )
194
+ this.configTable.tBody.rowClick?.behaviourAction.clickEvent(item);
195
+ }
196
+
197
+ /**
198
+ * Handle the click event on a cell icon
199
+ * @param event Mouse event
200
+ * @param cell Table body cell configuration
201
+ * @param item Data item associated with the cell
202
+ * @see ITBodyCell
203
+ */
204
+ handleIconClick(cell: ITBodyCell, item: T): void {
205
+ if (cell?.icon?.action.behaviourAction) {
206
+ if (
207
+ cell.icon.action.behaviourAction.clickEvent &&
208
+ cell.icon.action.disabled &&
209
+ !cell.icon.action.disabled(item)
210
+ ) {
211
+ cell.icon.action.behaviourAction.clickEvent(item);
212
+ }
213
+ }
214
+ }
215
+ }
@@ -0,0 +1,4 @@
1
+ export enum EnumDirectionSortTable {
2
+ asc,
3
+ desc,
4
+ }
@@ -0,0 +1,47 @@
1
+ import { EnumDirectionSortTable } from './table.enum';
2
+
3
+ export function sortTable<T>(
4
+ fullData: T[],
5
+ direction: EnumDirectionSortTable,
6
+ fieldOrder: string | string[]
7
+ ): T[] {
8
+ const orderFields = Array.isArray(fieldOrder)
9
+ ? fieldOrder.join('.')
10
+ : fieldOrder;
11
+
12
+ return fullData.sort((a: T, b: T) => {
13
+ const aValue = getField(a, orderFields);
14
+ const bValue = getField(b, orderFields);
15
+ return direction === EnumDirectionSortTable.asc
16
+ ? compareValues(aValue, bValue)
17
+ : compareValues(bValue, aValue);
18
+ });
19
+ }
20
+
21
+ function getField(obj: unknown, path: string): unknown {
22
+ return path
23
+ .split('.')
24
+ .reduce((value, field) => (value as Record<string, unknown>)?.[field], obj);
25
+ }
26
+
27
+ function compareValues(a: unknown, b: unknown): number {
28
+ if (a === b) return 0;
29
+ if (a === undefined || a === null) return -1;
30
+ if (b === undefined || b === null) return 1;
31
+
32
+ if (typeof a === 'number' && typeof b === 'number') return a - b;
33
+ if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
34
+ if (typeof a === 'string' && typeof b === 'string') {
35
+ const dateA = Date.parse(a);
36
+ const dateB = Date.parse(b);
37
+ if (!isNaN(dateA) && !isNaN(dateB)) return dateA - dateB;
38
+
39
+ const numA = parseFloat(a);
40
+ const numB = parseFloat(b);
41
+ if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
42
+
43
+ return a.localeCompare(b);
44
+ }
45
+
46
+ return -1;
47
+ }
@@ -0,0 +1,143 @@
1
+ import { FormControl } from '@angular/forms';
2
+ import { TooltipPosition } from '../../directives/tooltip/tooltip.type';
3
+ import {
4
+ ArrayOneOrMore,
5
+ GenericFunction,
6
+ TBehaviourAction,
7
+ } from '../../types/common.types';
8
+ import { EnumDirectionSortTable } from './table.enum';
9
+ import { TIconPosition, TInputType } from './table.type';
10
+
11
+ export interface IConfigTable<T> {
12
+ tTitleBar?: ITTitleBar;
13
+ tCaption: ITCaption;
14
+ tSort?: ITSort;
15
+ tHead: ITHead;
16
+ tBody: ITBody<T>;
17
+ tPagination: ITPagination<T>;
18
+ }
19
+
20
+ export interface ITTitleBar {
21
+ title: string;
22
+ }
23
+
24
+ export interface ITTitleBarFilter {
25
+ id: string;
26
+ label: string;
27
+ placeholder: string;
28
+ type?: TInputType;
29
+ control: FormControl;
30
+ }
31
+
32
+ export interface ITTitleBarSearch {
33
+ id: string;
34
+ label: string;
35
+ placeholder: string;
36
+ type?: TInputType;
37
+ control: FormControl<string | null>;
38
+ }
39
+
40
+ export interface ITCaption {
41
+ srOnly: string;
42
+ }
43
+
44
+ export interface ITSort {
45
+ defaultDirection: EnumDirectionSortTable;
46
+ defaultFieldOrder: string | string[];
47
+ }
48
+
49
+ export interface ITPagination<T> {
50
+ page?: number;
51
+ pageSize?: number;
52
+ order?: string | string[];
53
+ direction?: EnumDirectionSortTable;
54
+ totalItems?: number;
55
+ fullData?: T[];
56
+ hasScrollTop?: boolean;
57
+ showPagination?: boolean;
58
+ hasElipsisPages?: boolean;
59
+ }
60
+
61
+ export interface ITHead {
62
+ columns: ITHeadColumn[];
63
+ action?: ITHeadAction;
64
+ }
65
+
66
+ export interface ITHeadColumn {
67
+ columnId: string;
68
+ fieldOrder?: string;
69
+ fieldLabel: string;
70
+ customClasses?: string | string[];
71
+ }
72
+
73
+ export interface ITHeadAction {
74
+ label: string;
75
+ customClass?: string;
76
+ }
77
+
78
+ export interface ITBody<T> {
79
+ uniqueField: string;
80
+ rowClick?: IActionElement;
81
+ action?: ITBodyAction;
82
+ columns: ITBodyColumn[];
83
+ data?: T[];
84
+ }
85
+
86
+ export interface IActionElement extends IBaseAction {
87
+ iconClass?: GenericFunction<string>;
88
+ }
89
+
90
+ export interface ILinkAction {
91
+ link: string;
92
+ customParamsLink?: any;
93
+ }
94
+
95
+ export interface IEventAction {
96
+ clickEvent: GenericFunction<any>;
97
+ }
98
+
99
+ export interface ITBodyAction {
100
+ customClasses?: string;
101
+ buttons: IActionElement[];
102
+ }
103
+
104
+ export interface ITBodyColumn {
105
+ cells: ITBodyCell[] | ITBodyCell;
106
+ customClass?: string;
107
+ }
108
+
109
+ export interface ITBodyCell {
110
+ fieldArrayPosition?: ArrayOneOrMore<string | Array<string>>;
111
+ icon?: ITBodyCellIcon;
112
+ fieldPipe?: IFieldPipe;
113
+ customClass?: GenericFunction<string>;
114
+ }
115
+
116
+ export interface ITBodyCellIcon {
117
+ iconClass: string;
118
+ position?: TIconPosition;
119
+ action: IBaseAction;
120
+ }
121
+
122
+ export interface IBaseAction {
123
+ id: string;
124
+ title?: string;
125
+ tooltip?: ITooltipElement;
126
+ ariaLabel?: string;
127
+ behaviourAction: TBehaviourAction<ILinkAction, IEventAction>;
128
+ disabled?: GenericFunction<boolean>;
129
+ visible?: GenericFunction<boolean>;
130
+ }
131
+
132
+ export interface IFieldPipe {
133
+ type: any;
134
+ args?: any[];
135
+ }
136
+
137
+ export interface ITooltipElement {
138
+ apply: boolean;
139
+ text?: GenericFunction<string>;
140
+ textContent?: GenericFunction<string>;
141
+ position?: TooltipPosition;
142
+ gap?: number;
143
+ }
@@ -0,0 +1,62 @@
1
+ import { inject, Injector, Pipe, PipeTransform } from '@angular/core';
2
+
3
+ @Pipe({
4
+ name: 'fieldArrayPosition',
5
+ standalone: true,
6
+ })
7
+ export class FieldArrayPositionPipe implements PipeTransform {
8
+ transform(value: any, args: any[] | any): any {
9
+ const ret = getNestedPropertyValueByPosition(value, args);
10
+ return ret;
11
+ }
12
+ }
13
+
14
+ function getNestedPropertyValueByPosition(array: any, positionArray: any): any {
15
+ if (typeof positionArray === 'object') {
16
+ const appo = positionArray[0];
17
+ const rest = positionArray.slice(1);
18
+ if (!array) return undefined;
19
+ if (rest.length == 0 && Object.prototype.hasOwnProperty.call(array, appo)) {
20
+ return array[appo];
21
+ }
22
+ if (rest.length == 0 && array instanceof Array) {
23
+ return array.map((s) => {
24
+ return s[appo];
25
+ });
26
+ }
27
+ return getNestedPropertyValueByPosition(array[appo], rest);
28
+ } else {
29
+ return array[positionArray];
30
+ }
31
+ }
32
+
33
+ @Pipe({
34
+ name: 'typeOfArray',
35
+ standalone: true,
36
+ })
37
+ export class TypeOfArrayPipe implements PipeTransform {
38
+ transform(value: any): any {
39
+ return Array.isArray(value);
40
+ }
41
+ }
42
+
43
+ @Pipe({
44
+ name: 'dynamicPipe',
45
+ standalone: true,
46
+ })
47
+ export class DynamicPipe implements PipeTransform {
48
+ private readonly injector = inject(Injector);
49
+
50
+ transform(value: any, pipeToken: any, pipeArgs?: any[]): any {
51
+ if (!pipeToken) {
52
+ return value;
53
+ } else {
54
+ const pipe = this.injector.get<PipeTransform>(pipeToken); // ✅ cast type
55
+ if (pipeArgs) {
56
+ return pipe.transform(value, ...pipeArgs);
57
+ } else {
58
+ return pipe.transform(value);
59
+ }
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,15 @@
1
+ export type TIconPosition = 'left' | 'right';
2
+ export type TInputType =
3
+ | 'text'
4
+ | 'button'
5
+ | 'checkbox'
6
+ | 'color'
7
+ | 'date'
8
+ | 'datetime'
9
+ | 'datetime-local'
10
+ | 'email'
11
+ | 'file'
12
+ | 'number'
13
+ | 'image'
14
+ | 'url'
15
+ | 'week';
@@ -0,0 +1,88 @@
1
+ <div
2
+ class="frg-tabs"
3
+ [class.is-animated]="animated"
4
+ [attr.data-style]="styleType"
5
+ [attr.data-size]="size"
6
+ [attr.data-align]="align">
7
+ <div class="frg-tabs__list" role="tablist">
8
+ @for (item of viewItems; track item?.id; let i = $index) {
9
+ <button
10
+ type="button"
11
+ #tabBtn
12
+ role="tab"
13
+ class="frg-tabs__tab"
14
+ [class.is-active]="isActive(item?.id)"
15
+ [class.is-disabled]="item?.disabled"
16
+ [attr.data-style]="item?.style || styleType"
17
+ [attr.data-size]="item?.size || size"
18
+ [attr.id]="tabId(item!.id!)"
19
+ [attr.aria-controls]="panelId(item!.id!)"
20
+ [attr.aria-selected]="isActive(item?.id)"
21
+ [attr.aria-disabled]="item?.disabled || null"
22
+ [attr.tabindex]="isActive(item?.id) ? 0 : -1"
23
+ [disabled]="item?.disabled"
24
+ (click)="onTabClick(i, $event)"
25
+ (keydown)="onTabKeydown($event, i)">
26
+ <span class="frg-tabs__tab-inner">
27
+ @if (item?.iconTemplate || iconTemplate || item?.icon) {
28
+ <span class="frg-tabs__icon" aria-hidden="true">
29
+ @if (item?.iconTemplate) {
30
+ <ng-container
31
+ [ngTemplateOutlet]="item?.iconTemplate"
32
+ [ngTemplateOutletContext]="buildContext(item!, i)">
33
+ </ng-container>
34
+ } @else if (iconTemplate) {
35
+ <ng-container
36
+ [ngTemplateOutlet]="iconTemplate"
37
+ [ngTemplateOutletContext]="buildContext(item!, i)">
38
+ </ng-container>
39
+ } @else {
40
+ <span class="frg-tabs__icon-shape">{{ item?.icon }}</span>
41
+ }
42
+ </span>
43
+ }
44
+
45
+ <span class="frg-tabs__label">
46
+ @if (item?.labelTemplate || labelTemplate) {
47
+ <ng-container
48
+ [ngTemplateOutlet]="item?.labelTemplate || labelTemplate"
49
+ [ngTemplateOutletContext]="buildContext(item!, i)">
50
+ </ng-container>
51
+ } @else {
52
+ <span class="frg-tabs__label-text">{{ item?.label }}</span>
53
+ @if (item?.description) {
54
+ <small class="frg-tabs__label-desc">{{ item?.description }}</small>
55
+ }
56
+ }
57
+ </span>
58
+ </span>
59
+ <span class="frg-tabs__underline" aria-hidden="true"></span>
60
+ </button>
61
+ }
62
+ </div>
63
+
64
+ <div class="frg-tabs__panels">
65
+ @for (item of viewItems; track item?.id; let i = $index) {
66
+ <section
67
+ class="frg-tabs__panel"
68
+ #panelBody
69
+ role="tabpanel"
70
+ [class.is-active]="isActive(item?.id)"
71
+ [attr.id]="panelId(item!.id!)"
72
+ [attr.aria-labelledby]="tabId(item!.id!)"
73
+ [hidden]="!isActive(item?.id)"
74
+ [attr.tabindex]="isActive(item?.id) ? 0 : -1">
75
+ @if (item?.contentTemplate || contentTemplate) {
76
+ <ng-container
77
+ [ngTemplateOutlet]="item?.contentTemplate || contentTemplate"
78
+ [ngTemplateOutletContext]="buildContext(item!, i)">
79
+ </ng-container>
80
+ } @else if (item?.content) {
81
+ {{ item?.content }}
82
+ } @else if (item?.data; as payload) {
83
+ {{ payload }}
84
+ }
85
+ </section>
86
+ }
87
+ </div>
88
+ </div>