@foxeltech/angular-ui 0.0.14 → 0.3.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 (134) hide show
  1. package/BeVietnamPro/BeVietnamPro-Bold.woff2 +0 -0
  2. package/BeVietnamPro/BeVietnamPro-ExtraLight.woff2 +0 -0
  3. package/BeVietnamPro/BeVietnamPro-Light.woff2 +0 -0
  4. package/BeVietnamPro/BeVietnamPro-Medium.woff2 +0 -0
  5. package/BeVietnamPro/BeVietnamPro-Regular.woff2 +0 -0
  6. package/BeVietnamPro/BeVietnamPro-SemiBold.woff2 +0 -0
  7. package/BeVietnamPro/BeVietnamPro-Thin.woff2 +0 -0
  8. package/SpaceMono-Regular.woff2 +0 -0
  9. package/components.css +7 -17
  10. package/dm-sans/DMSans-Bold.woff2 +0 -0
  11. package/dm-sans/DMSans-Light.woff2 +0 -0
  12. package/dm-sans/DMSans-Medium.woff2 +0 -0
  13. package/dm-sans/DMSans-Regular.woff2 +0 -0
  14. package/dm-sans/DMSans-SemiBold.woff2 +0 -0
  15. package/dm-sans/DMSans-Thin.woff2 +0 -0
  16. package/fesm2022/foxeltech-angular-ui.mjs +1349 -285
  17. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  18. package/fonts.css +68 -34
  19. package/index.d.ts +624 -139
  20. package/orbitron/Orbitron-Bold.woff2 +0 -0
  21. package/orbitron/Orbitron-Medium.woff2 +0 -0
  22. package/orbitron/Orbitron-Regular.woff2 +0 -0
  23. package/orbitron/Orbitron-SemiBold.woff2 +0 -0
  24. package/package.json +9 -10
  25. package/roboto/Roboto-Black.woff2 +0 -0
  26. package/roboto/Roboto-BlackItalic.woff2 +0 -0
  27. package/roboto/Roboto-Bold.woff2 +0 -0
  28. package/roboto/Roboto-BoldCondensed.woff2 +0 -0
  29. package/roboto/Roboto-BoldCondensedItalic.woff2 +0 -0
  30. package/roboto/Roboto-BoldItalic.woff2 +0 -0
  31. package/roboto/Roboto-Condensed.woff2 +0 -0
  32. package/roboto/Roboto-CondensedItalic.woff2 +0 -0
  33. package/roboto/Roboto-Italic.woff2 +0 -0
  34. package/roboto/Roboto-Light.woff2 +0 -0
  35. package/roboto/Roboto-LightItalic.woff2 +0 -0
  36. package/roboto/Roboto-Medium.woff2 +0 -0
  37. package/roboto/Roboto-MediumItalic.woff2 +0 -0
  38. package/roboto/Roboto-Regular.woff2 +0 -0
  39. package/roboto/Roboto-Thin.woff2 +0 -0
  40. package/roboto/Roboto-ThinItalic.woff2 +0 -0
  41. package/src/lib/styles/components.css +7 -17
  42. package/src/lib/styles/fonts.css +68 -34
  43. package/src/lib/styles/tailwind.css +11 -8
  44. package/src/lib/styles/tokens.css +131 -0
  45. package/src/lib/styles/utilities.css +24 -0
  46. package/src/lib/ui/components/button/button.component.html +4 -1
  47. package/src/lib/ui/components/button/button.component.scss +0 -0
  48. package/src/lib/ui/components/chart/chart.component.scss +49 -0
  49. package/src/lib/ui/components/checkbox/checkbox.component.html +3 -2
  50. package/src/lib/ui/components/checkbox/checkbox.component.scss +0 -0
  51. package/src/lib/ui/components/circle-progress-bar/circle-progress-bar.component.scss +0 -0
  52. package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +3 -2
  53. package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +147 -0
  54. package/src/lib/ui/components/dnd-upload/dnd-upload.component.scss +87 -0
  55. package/src/lib/ui/components/drawer/drawer.component.html +3 -0
  56. package/src/lib/ui/components/drawer/drawer.component.scss +134 -0
  57. package/src/lib/ui/components/empty-state/empty-state.component.html +9 -0
  58. package/src/lib/ui/components/filter-chips/filter-chips.component.html +12 -0
  59. package/src/lib/ui/components/flow-connection/flow-connection.component.scss +0 -0
  60. package/src/lib/ui/components/hero-icon/hero-icon.component.scss +0 -0
  61. package/src/lib/ui/components/info-field/info-field.component.html +4 -0
  62. package/src/lib/ui/components/input/input.component.html +15 -3
  63. package/src/lib/ui/components/kanban-board/kanban-board.component.scss +70 -0
  64. package/src/lib/ui/components/loading-panel/loading-panel.component.scss +0 -0
  65. package/src/lib/ui/components/master-detail/master-detail.component.css +11 -0
  66. package/src/lib/ui/components/master-detail/master-detail.component.html +70 -0
  67. package/src/lib/ui/components/meter/meter.component.html +22 -0
  68. package/src/lib/ui/components/radio-button/radio-button.component.html +10 -4
  69. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +8 -3
  70. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.scss +0 -0
  71. package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +18 -0
  72. package/src/lib/ui/components/search-bar/search-bar.component.scss +0 -0
  73. package/src/lib/ui/components/section-card/section-card.component.html +9 -0
  74. package/src/lib/ui/components/select/select.component.html +9 -4
  75. package/src/lib/ui/components/severity-badges/severity-badges.component.html +11 -0
  76. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +22 -0
  77. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +12 -0
  78. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.scss +0 -0
  79. package/src/lib/ui/components/slider/slider.component.html +3 -0
  80. package/src/lib/ui/components/slider/slider.component.scss +0 -0
  81. package/src/lib/ui/components/stat-cards/stat-cards.component.html +18 -0
  82. package/src/lib/ui/components/switch/switch.component.html +11 -6
  83. package/src/lib/ui/components/switch/switch.component.scss +0 -0
  84. package/src/lib/ui/components/tab-component/tab.component.scss +0 -0
  85. package/src/lib/ui/components/tab-group/tab-group.component.html +15 -1
  86. package/src/lib/ui/components/tab-group/tab-group.component.scss +51 -0
  87. package/src/lib/ui/components/table-cell/table-cell.component.scss +0 -0
  88. package/src/lib/ui/components/tag/tag.component.html +6 -1
  89. package/src/lib/ui/components/tag/tag.component.scss +0 -0
  90. package/src/lib/ui/components/toast/toast.component.html +2 -1
  91. package/src/lib/ui/components/toast/toast.component.scss +0 -0
  92. package/src/lib/ui/components/toast-container/toast-container.component.scss +0 -0
  93. package/src/lib/ui/components/tree-diagram/tree-diagram.component.scss +0 -0
  94. package/src/lib/ui/dialogs/confirmation/confirmation.component.html +1 -1
  95. package/src/lib/ui/dialogs/confirmation/confirmation.component.scss +0 -0
  96. package/tailwind.css +11 -8
  97. package/tokens.css +131 -0
  98. package/utilities.css +24 -0
  99. package/BeVietnamPro/BeVietnamPro-Bold.ttf +0 -0
  100. package/BeVietnamPro/BeVietnamPro-ExtraLight.ttf +0 -0
  101. package/BeVietnamPro/BeVietnamPro-Light.ttf +0 -0
  102. package/BeVietnamPro/BeVietnamPro-Medium.ttf +0 -0
  103. package/BeVietnamPro/BeVietnamPro-Regular.ttf +0 -0
  104. package/BeVietnamPro/BeVietnamPro-SemiBold.ttf +0 -0
  105. package/BeVietnamPro/BeVietnamPro-Thin.ttf +0 -0
  106. package/SpaceMono-Regular.ttf +0 -0
  107. package/dm-sans/DMSans-Bold.ttf +0 -0
  108. package/dm-sans/DMSans-Light.ttf +0 -0
  109. package/dm-sans/DMSans-Medium.ttf +0 -0
  110. package/dm-sans/DMSans-Regular.ttf +0 -0
  111. package/dm-sans/DMSans-SemiBold.ttf +0 -0
  112. package/dm-sans/DMSans-Thin.ttf +0 -0
  113. package/orbitron/Orbitron-Bold.ttf +0 -0
  114. package/orbitron/Orbitron-Medium.ttf +0 -0
  115. package/orbitron/Orbitron-Regular.ttf +0 -0
  116. package/orbitron/Orbitron-SemiBold.ttf +0 -0
  117. package/roboto/Roboto-Black.ttf +0 -0
  118. package/roboto/Roboto-BlackItalic.ttf +0 -0
  119. package/roboto/Roboto-Bold.ttf +0 -0
  120. package/roboto/Roboto-BoldCondensed.ttf +0 -0
  121. package/roboto/Roboto-BoldCondensedItalic.ttf +0 -0
  122. package/roboto/Roboto-BoldItalic.ttf +0 -0
  123. package/roboto/Roboto-Condensed.ttf +0 -0
  124. package/roboto/Roboto-CondensedItalic.ttf +0 -0
  125. package/roboto/Roboto-Italic.ttf +0 -0
  126. package/roboto/Roboto-Light.ttf +0 -0
  127. package/roboto/Roboto-LightItalic.ttf +0 -0
  128. package/roboto/Roboto-Medium.ttf +0 -0
  129. package/roboto/Roboto-MediumItalic.ttf +0 -0
  130. package/roboto/Roboto-Regular.ttf +0 -0
  131. package/roboto/Roboto-Thin.ttf +0 -0
  132. package/roboto/Roboto-ThinItalic.ttf +0 -0
  133. package/tailwind-config.d.ts +0 -4
  134. package/tailwind.config.js +0 -95
@@ -0,0 +1,70 @@
1
+ <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth">
2
+ <!-- Master: list pane -->
3
+ <div class="fx-md-list card-common">
4
+ <ng-content select="[mdHeader]"></ng-content>
5
+ <ng-content select="[mdFilters]"></ng-content>
6
+ <div
7
+ class="flex flex-col gap-normal overflow-auto pr-xs"
8
+ [style.maxHeight]="maxListHeight"
9
+ role="listbox"
10
+ [attr.aria-label]="listLabel || null"
11
+ >
12
+ @if (table.loading) {
13
+ <fx-ui-skeleton-list [rows]="table.pageSize"></fx-ui-skeleton-list>
14
+ } @else {
15
+ @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {
16
+ <button
17
+ type="button"
18
+ role="option"
19
+ [id]="optionId(i)"
20
+ [attr.aria-selected]="isSelected(row)"
21
+ [tabindex]="isSelected(row) ? 0 : -1"
22
+ (click)="table.selectItem(row)"
23
+ (keydown)="onKeydown($event, i)"
24
+ class="text-left rounded-lg border p-semi transition-colors"
25
+ [ngClass]="
26
+ isSelected(row)
27
+ ? 'border-gradient-primary-start bg-gradient-primary-start/5'
28
+ : 'border-border-default hover:border-border-interactive bg-bg-primary'
29
+ "
30
+ >
31
+ <ng-container
32
+ [ngTemplateOutlet]="rowTemplate"
33
+ [ngTemplateOutletContext]="{ $implicit: row, selected: isSelected(row) }"
34
+ ></ng-container>
35
+ </button>
36
+ }
37
+ @if (!table.dataSource.data.length) {
38
+ <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
39
+ }
40
+ }
41
+ </div>
42
+ <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,
43
+ whose own BaseTable subscription (running after this child's hook)
44
+ wires page events — a binding here would double-fetch every change. -->
45
+ <mat-paginator
46
+ class="mt-normal"
47
+ [length]="table.total"
48
+ [pageSize]="table.pageSize"
49
+ [pageSizeOptions]="pageSizeOptions"
50
+ ></mat-paginator>
51
+ </div>
52
+
53
+ <!-- Detail pane -->
54
+ <div class="w-full lg:flex-1 min-w-0">
55
+ @if (table.selected) {
56
+ @if (table.detailBusy) {
57
+ <fx-ui-skeleton-detail [cards]="skeletonCards"></fx-ui-skeleton-detail>
58
+ } @else {
59
+ <ng-container
60
+ [ngTemplateOutlet]="detailTemplate"
61
+ [ngTemplateOutletContext]="{ $implicit: table.detail, selected: table.selected }"
62
+ ></ng-container>
63
+ }
64
+ } @else {
65
+ <div class="card-common flex items-center justify-center h-[300px]">
66
+ <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
67
+ </div>
68
+ }
69
+ </div>
70
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="flex flex-col gap-small w-full">
2
+ @if (label || caption) {
3
+ <div class="flex items-center justify-between gap-normal">
4
+ @if (label) {
5
+ <span class="txt-field-label !mb-0">{{ label }}</span>
6
+ }
7
+ @if (caption) {
8
+ <span class="txt-utility-caption text-text-secondary">{{ caption }}</span>
9
+ }
10
+ </div>
11
+ }
12
+ <div
13
+ class="h-2 rounded-full bg-bg-hover overflow-hidden"
14
+ role="progressbar"
15
+ [attr.aria-valuemin]="0"
16
+ [attr.aria-valuemax]="max"
17
+ [attr.aria-valuenow]="value"
18
+ [attr.aria-label]="label || null"
19
+ >
20
+ <div class="h-full rounded-full" [ngClass]="fillClass" [style.width.%]="percent"></div>
21
+ </div>
22
+ </div>
@@ -1,9 +1,15 @@
1
- <div class="w-full rounded-lg overflow-hidden" [class]="class" role="radiogroup">
1
+ <div
2
+ class="w-full rounded-lg overflow-hidden"
3
+ [class]="class"
4
+ role="radiogroup"
5
+ [attr.aria-labelledby]="label ? groupId + '-label' : null"
6
+ >
7
+ <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
2
8
  @if (label) {
3
- <label class="txt-field-label">
9
+ <span class="txt-field-label block" [id]="groupId + '-label'">
4
10
  {{ label }}
5
11
  @if (required) {
6
- <span class="txt-required">*</span>
12
+ <span class="txt-required" aria-hidden="true">*</span>
7
13
  }
8
14
  @if (tooltip) {
9
15
  <fx-ui-hero-icon
@@ -15,7 +21,7 @@
15
21
  matTooltipClass="text-text-primary text-xs font-semibold"
16
22
  ></fx-ui-hero-icon>
17
23
  }
18
- </label>
24
+ </span>
19
25
  }
20
26
 
21
27
  <button
@@ -1,10 +1,15 @@
1
1
  <div class="flex flex-col text-left mb-4 w-full" [class]="class">
2
- @if(label){<label class="txt-field-label">
2
+ <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
3
+ @if(label){<span class="txt-field-label" [id]="groupId + '-label'">
3
4
  {{ label }}
4
- @if(required) {<span class="txt-required">*</span>} </label
5
+ @if(required) {<span class="txt-required" aria-hidden="true">*</span>} </span
5
6
  >}
6
7
 
7
- <div class="inline-flex w-full rounded-lg overflow-hidden" role="radiogroup">
8
+ <div
9
+ class="inline-flex w-full rounded-lg overflow-hidden"
10
+ role="radiogroup"
11
+ [attr.aria-labelledby]="label ? groupId + '-label' : null"
12
+ >
8
13
  @for(opt of options; track opt){<button
9
14
  type="button"
10
15
  role="radio"
@@ -0,0 +1,18 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .ql-toolbar {
9
+ @apply border-b border-border-default rounded-t p-2;
10
+ }
11
+
12
+ .ql-container {
13
+ @apply rounded-b;
14
+ }
15
+
16
+ .ql-editor {
17
+ @apply min-h-[200px] p-3 text-base;
18
+ }
@@ -0,0 +1,9 @@
1
+ <div class="card-common">
2
+ <div class="flex items-center gap-normal mb-large">
3
+ @if (icon) {
4
+ <fx-ui-hero-icon [ngClass]="iconClass" [icon]="icon" [size]="18"></fx-ui-hero-icon>
5
+ }
6
+ <span class="txt-heading-06">{{ title }}</span>
7
+ </div>
8
+ <ng-content></ng-content>
9
+ </div>
@@ -1,9 +1,9 @@
1
1
  <div class="mb-4 text-left w-full" [class]="class">
2
2
  @if (label) {
3
- <label class="block txt-field-label">
3
+ <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
5
  @if (required) {
6
- <span class="txt-required">*</span>
6
+ <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
9
9
  <fx-ui-hero-icon
@@ -24,6 +24,10 @@
24
24
  appearance="fill"
25
25
  >
26
26
  <mat-select
27
+ [id]="fieldId"
28
+ [required]="!!required"
29
+ [attr.aria-invalid]="invalid || null"
30
+ [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
27
31
  [placeholder]="placeholder"
28
32
  [disabled]="disabled"
29
33
  [multiple]="multiple"
@@ -36,11 +40,12 @@
36
40
  <div class="px-semi w-full">
37
41
  <input
38
42
  type="text"
43
+ aria-label="Search"
39
44
  [placeholder]="'Search'"
40
45
  [(ngModel)]="searchTerm"
41
46
  (ngModelChange)="onSearchChange($event)"
42
47
  (click)="$event.stopPropagation()"
43
- class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
48
+ class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
44
49
  />
45
50
  </div>
46
51
  }
@@ -51,7 +56,7 @@
51
56
  </mat-form-field>
52
57
 
53
58
  @if (invalid) {
54
- <div class="txt-invalid text-left">
59
+ <div class="txt-invalid text-left" role="alert" [id]="fieldId + '-error'">
55
60
  {{ getErrorMessage(label) }}
56
61
  </div>
57
62
  }
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center gap-small">
2
+ @for (badge of badges; track badge.key) {
3
+ <button
4
+ type="button"
5
+ class="px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50"
6
+ [ngClass]="badgeClass(badge)"
7
+ [attr.aria-label]="badge.key + ': ' + badge.label"
8
+ (click)="badgeClick.emit(badge.key)"
9
+ >{{ badge.label }}</button>
10
+ }
11
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="flex flex-col gap-xl animate-pulse">
2
+ <div class="card-common">
3
+ <div class="flex items-start gap-normal">
4
+ <div class="w-10 h-10 rounded-lg bg-bg-hover shrink-0"></div>
5
+ <div class="flex-1 min-w-0 flex flex-col gap-normal">
6
+ <div class="h-5 w-1/2 rounded bg-bg-hover"></div>
7
+ <div class="h-3 w-3/4 rounded bg-bg-hover"></div>
8
+ </div>
9
+ </div>
10
+ </div>
11
+
12
+ @for (c of range(cards); track c) {
13
+ <div class="card-common flex flex-col gap-large">
14
+ <div class="h-4 w-1/3 rounded bg-bg-hover"></div>
15
+ <div class="flex flex-col gap-normal">
16
+ @for (r of range(rows); track r) {
17
+ <div class="h-3 rounded bg-bg-hover" [style.width.%]="rowWidth(r)"></div>
18
+ }
19
+ </div>
20
+ </div>
21
+ }
22
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class="flex flex-col gap-normal animate-pulse">
2
+ @for (r of range(rows); track r) {
3
+ <div class="rounded-lg border border-border-default p-semi">
4
+ <div class="flex items-center gap-normal mb-small">
5
+ <div class="w-8 h-8 rounded-lg bg-bg-hover shrink-0"></div>
6
+ <div class="h-4 flex-1 rounded bg-bg-hover"></div>
7
+ <div class="h-5 w-12 rounded-full bg-bg-hover shrink-0"></div>
8
+ </div>
9
+ <div class="h-3 w-3/4 rounded bg-bg-hover"></div>
10
+ </div>
11
+ }
12
+ </div>
@@ -4,6 +4,8 @@
4
4
  [class.opacity-60]="disabled"
5
5
  role="group"
6
6
  >
7
+ <!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->
8
+ <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
7
9
  <div
8
10
  #track
9
11
  class="relative touch-none py-4"
@@ -38,6 +40,7 @@
38
40
  class="absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary"
39
41
  [ngStyle]="thumbStyle()"
40
42
  role="slider"
43
+ aria-label="Slider"
41
44
  [attr.aria-valuemin]="min"
42
45
  [attr.aria-valuemax]="max"
43
46
  [attr.aria-valuenow]="value"
@@ -0,0 +1,18 @@
1
+ <div class="grid gap-normal" [ngStyle]="{ 'grid-template-columns': gridColumns }">
2
+ @for (card of cards; track card.label) {
3
+ <div class="card-common flex items-center gap-normal">
4
+ @if (card.icon) {
5
+ <div
6
+ class="w-10 h-10 rounded-lg flex items-center justify-center shrink-0"
7
+ [ngClass]="boxClass(card)"
8
+ >
9
+ <fx-ui-hero-icon [icon]="card.icon" [size]="20"></fx-ui-hero-icon>
10
+ </div>
11
+ }
12
+ <div class="flex flex-col min-w-0">
13
+ <span class="txt-heading-04" [ngClass]="numClass(card)">{{ card.value }}</span>
14
+ <span class="txt-utility-caption text-text-secondary truncate">{{ card.label }}</span>
15
+ </div>
16
+ </div>
17
+ }
18
+ </div>
@@ -1,5 +1,10 @@
1
- <div
2
- class="relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer"
1
+ <button
2
+ type="button"
3
+ role="switch"
4
+ [attr.aria-checked]="!!value"
5
+ [attr.aria-label]="ariaLabel || null"
6
+ [disabled]="disabled"
7
+ class="relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-primary/50"
3
8
  [ngClass]="{
4
9
  'bg-success': value && !disabled,
5
10
  'bg-button-default': !value && !disabled,
@@ -8,14 +13,14 @@
8
13
  (click)="toggle()"
9
14
  >
10
15
  @if(value) {
11
- <i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse">check</i>
12
-
16
+ <i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse" aria-hidden="true">check</i>
17
+
13
18
  }
14
19
  @if(!value) {
15
- <i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse">close</i>
20
+ <i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse" aria-hidden="true">close</i>
16
21
  }
17
22
  <div
18
23
  class="absolute top-0.5 left-0.5 w-4 h-4 bg-text-inverse rounded-full shadow-md transform transition-transform duration-300"
19
24
  [ngClass]="{ 'translate-x-5': value }"
20
25
  ></div>
21
- </div>
26
+ </button>
@@ -3,11 +3,15 @@
3
3
  <button
4
4
  class="left-scroll-button"
5
5
  type="button"
6
+ aria-label="Scroll tabs left"
6
7
  (click)="scroll('left')"
7
8
  [style.opacity]="canScrollLeft ? 1 : 0"
8
9
  [style.pointerEvents]="canScrollLeft ? 'auto' : 'none'"
10
+ [attr.tabindex]="canScrollLeft ? null : -1"
11
+ [attr.aria-hidden]="!canScrollLeft"
9
12
  >
10
13
  <svg
14
+ aria-hidden="true"
11
15
  xmlns="http://www.w3.org/2000/svg"
12
16
  class="h-5 w-5 text-text-primary"
13
17
  fill="none"
@@ -20,12 +24,18 @@
20
24
 
21
25
  <div
22
26
  #tabList
27
+ role="tablist"
23
28
  class="relative flex overflow-x-auto no-scrollbar scroll-smooth"
24
29
  (scroll)="onScroll()"
25
30
  >
26
31
  <button
27
32
  *ngFor="let tab of tabs.toArray(); let i = index"
33
+ type="button"
34
+ role="tab"
35
+ [attr.aria-selected]="i === activeIndex"
36
+ [tabindex]="i === activeIndex ? 0 : -1"
28
37
  (click)="selectTab(i)"
38
+ (keydown)="onTabKeydown($event, i)"
29
39
  class="tab-btn tab-bar-btn"
30
40
  >
31
41
  <div class="flex flex-row items-center justify-center">
@@ -56,11 +66,15 @@
56
66
  <button
57
67
  class="right-scroll-button"
58
68
  type="button"
69
+ aria-label="Scroll tabs right"
59
70
  (click)="scroll('right')"
60
71
  [style.opacity]="canScrollRight ? 1 : 0"
61
72
  [style.pointerEvents]="canScrollRight ? 'auto' : 'none'"
73
+ [attr.tabindex]="canScrollRight ? null : -1"
74
+ [attr.aria-hidden]="!canScrollRight"
62
75
  >
63
76
  <svg
77
+ aria-hidden="true"
64
78
  xmlns="http://www.w3.org/2000/svg"
65
79
  class="h-5 w-5 text-text-primary"
66
80
  fill="none"
@@ -75,7 +89,7 @@
75
89
  <!-- Tab content -->
76
90
  <div class="transition-all duration-300 ease-in-out mt-4">
77
91
  <ng-container *ngFor="let tab of tabs.toArray(); let i = index">
78
- <div *ngIf="i === activeIndex" class="animate-fadeIn">
92
+ <div *ngIf="i === activeIndex" role="tabpanel" class="animate-fadeIn">
79
93
  <ng-container *ngTemplateOutlet="tab.content"></ng-container>
80
94
  </div>
81
95
  </ng-container>
@@ -0,0 +1,51 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
4
+ @keyframes fadeIn {
5
+ from {
6
+ opacity: 0;
7
+ transform: translateY(4px);
8
+ }
9
+ to {
10
+ opacity: 1;
11
+ transform: translateY(0);
12
+ }
13
+ }
14
+
15
+ .animate-fadeIn {
16
+ animation: fadeIn 0.28s ease-in-out;
17
+ }
18
+
19
+ .no-scrollbar::-webkit-scrollbar {
20
+ display: none;
21
+ }
22
+ .no-scrollbar {
23
+ -ms-overflow-style: none;
24
+ scrollbar-width: none;
25
+ }
26
+
27
+ .underline {
28
+ position: absolute;
29
+ bottom: 0;
30
+ height: 2px;
31
+ background-color: rgb(var(--gradient-primary-start));
32
+ transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.2, 1),
33
+ width 0.25s cubic-bezier(0.2, 0.9, 0.2, 1);
34
+ will-change: transform, width;
35
+ transform: translateX(0);
36
+ width: 0;
37
+ margin-left: 32px;
38
+ margin-right: 32px;
39
+ }
40
+
41
+ .left-scroll-button {
42
+ @apply absolute left-0 top-0 h-full px-normal z-10 flex items-center bg-bg-primary transition-opacity duration-200;
43
+ }
44
+
45
+ .right-scroll-button {
46
+ @apply absolute right-0 top-0 h-full px-normal z-10 flex items-center bg-bg-primary transition-opacity duration-200;
47
+ }
48
+
49
+ .tab-bar-btn {
50
+ @apply relative px-large py-normal text-sm whitespace-nowrap transition-colors duration-200;
51
+ }
@@ -1,7 +1,12 @@
1
+ <!-- Interactive semantics only when a (clicked) listener is attached. -->
1
2
  <div
2
3
  (click)="onClick()"
4
+ (keydown.enter)="onClick()"
5
+ (keydown.space)="$event.preventDefault(); onClick()"
6
+ [attr.role]="clicked.observed ? 'button' : null"
7
+ [attr.tabindex]="clicked.observed ? 0 : null"
3
8
  [class]="
4
- `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`
9
+ `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}${clicked.observed ? ' cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50' : ''}`
5
10
  "
6
11
  >
7
12
  @if (icon) {
File without changes
@@ -1,6 +1,7 @@
1
1
  <div
2
2
  animate.enter="horizontal-fade-in-animation"
3
3
  animate.leave="horizontal-fade-out-animation"
4
+ [attr.role]="type === 'error' ? 'alert' : 'status'"
4
5
  class="flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0"
5
6
  [ngClass]="{
6
7
  'border-success': type === 'success',
@@ -9,7 +10,7 @@
9
10
  'border-information': type === 'info'
10
11
  }"
11
12
  >
12
- <div class="">
13
+ <div class="" aria-hidden="true">
13
14
  @switch (type) { @case('success') {
14
15
  <fx-ui-hero-icon
15
16
  icon="check-circle"
@@ -1,5 +1,5 @@
1
1
  <div animate.enter="fade-in-animation" class="dialog-container">
2
- <h1 class="txt-heading-05 dialog-header">{{ title }}</h1>
2
+ <h1 mat-dialog-title class="txt-heading-05 dialog-header">{{ title }}</h1>
3
3
  <mat-dialog-content>
4
4
  <div class="txt-default">{{ message }}</div>
5
5
  </mat-dialog-content>
package/tailwind.css CHANGED
@@ -1,18 +1,21 @@
1
+ /* fonts.css is NOT imported here: Tailwind v4 inlines imports and rebases
2
+ font url()s against the entry file, which breaks webpack resolution.
3
+ Consumers add '@foxeltech/angular-ui/fonts.css' to their styles array. */
4
+ @import 'tailwindcss';
5
+ @import './tokens.css';
6
+ @import './utilities.css';
1
7
  @import './theme.css';
2
- @import './components.css';
3
- @import './dialogs.css';
4
- @import './fonts.css';
8
+ @import './components.css' layer(components);
9
+ @import './dialogs.css' layer(components);
5
10
  @import './animations.css';
6
11
  @import '@danielmoncada/angular-datetime-picker/assets/style/picker.min.css';
7
- @tailwind base;
8
- @tailwind component;
9
- @tailwind utilities;
10
12
 
11
- html, body {
13
+ html,
14
+ body {
12
15
  font-family: 'Roboto';
13
16
  margin: 0;
14
17
  padding: 0;
15
18
  height: 100%;
16
19
  width: 100%;
17
20
  background: rgb(var(--bg-primary));
18
- }
21
+ }