@foxeltech/angular-ui 0.3.0 → 0.4.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 (54) hide show
  1. package/fesm2022/foxeltech-angular-ui-chart.mjs +392 -0
  2. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -0
  3. package/fesm2022/foxeltech-angular-ui-flow.mjs +38 -0
  4. package/fesm2022/foxeltech-angular-ui-flow.mjs.map +1 -0
  5. package/fesm2022/foxeltech-angular-ui-rich-text.mjs +156 -0
  6. package/fesm2022/foxeltech-angular-ui-rich-text.mjs.map +1 -0
  7. package/fesm2022/foxeltech-angular-ui.mjs +890 -1499
  8. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  9. package/fx-icons.svg +97 -0
  10. package/package.json +23 -16
  11. package/src/lib/styles/tailwind.css +0 -1
  12. package/src/lib/ui/components/button/button.component.html +14 -14
  13. package/src/lib/ui/components/checkbox/checkbox.component.html +2 -2
  14. package/src/lib/ui/components/circle-progress-bar/circle-progress-bar.component.html +2 -2
  15. package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +54 -28
  16. package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +25 -137
  17. package/src/lib/ui/components/dnd-upload/dnd-upload.component.html +3 -3
  18. package/src/lib/ui/components/drawer/drawer.component.html +75 -61
  19. package/src/lib/ui/components/empty-state/empty-state.component.html +2 -2
  20. package/src/lib/ui/components/hero-icon/hero-icon.component.html +10 -9
  21. package/src/lib/ui/components/info-field/info-field.component.html +1 -1
  22. package/src/lib/ui/components/input/input.component.html +6 -6
  23. package/src/lib/ui/components/kanban-board/kanban-board.component.html +1 -1
  24. package/src/lib/ui/components/loading-panel/loading-panel.component.html +53 -53
  25. package/src/lib/ui/components/master-detail/master-detail.component.html +9 -9
  26. package/src/lib/ui/components/menu/menu.component.html +27 -0
  27. package/src/lib/ui/components/radio-button/radio-button.component.html +26 -26
  28. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +3 -3
  29. package/src/lib/ui/components/search-bar/search-bar.component.html +12 -11
  30. package/src/lib/ui/components/section-card/section-card.component.html +1 -1
  31. package/src/lib/ui/components/select/select.component.html +14 -12
  32. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +2 -2
  33. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +1 -1
  34. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +5 -5
  35. package/src/lib/ui/components/slider/slider.component.html +22 -20
  36. package/src/lib/ui/components/stat-cards/stat-cards.component.html +1 -1
  37. package/src/lib/ui/components/switch/switch.component.html +1 -1
  38. package/src/lib/ui/components/tab-group/tab-group.component.html +35 -32
  39. package/src/lib/ui/components/table-cell/table-cell.component.html +2 -2
  40. package/src/lib/ui/components/tag/tag.component.html +4 -4
  41. package/src/lib/ui/components/toast/toast.component.html +42 -40
  42. package/src/lib/ui/components/toast-container/toast-container.component.html +1 -0
  43. package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +22 -20
  44. package/tailwind.css +0 -1
  45. package/types/foxeltech-angular-ui-chart.d.ts +53 -0
  46. package/types/foxeltech-angular-ui-flow.d.ts +14 -0
  47. package/types/foxeltech-angular-ui-rich-text.d.ts +35 -0
  48. package/{index.d.ts → types/foxeltech-angular-ui.d.ts} +394 -387
  49. package/src/lib/ui/components/chart/chart.component.html +0 -20
  50. package/src/lib/ui/components/chart/chart.component.scss +0 -49
  51. package/src/lib/ui/components/flow-connection/flow-connection.component.html +0 -10
  52. package/src/lib/ui/components/flow-connection/flow-connection.component.scss +0 -0
  53. package/src/lib/ui/components/rich-text-area/rich-text-area.component.html +0 -3
  54. package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +0 -18
@@ -1,9 +1,10 @@
1
- <ng-heroicons
2
- [stroke]="stroke"
3
- [icon]="icon"
4
- [size]="size"
5
- [solid]="solid"
6
- [outline]="outline"
7
- [color]="color"
8
- [class]="class"
9
- />
1
+ <svg
2
+ [attr.width]="size() || 24"
3
+ [attr.height]="size() || 24"
4
+ [attr.stroke-width]="solid() ? null : stroke()"
5
+ [style.color]="color() || null"
6
+ [class]="class()"
7
+ aria-hidden="true"
8
+ >
9
+ <use [attr.href]="href" />
10
+ </svg>
@@ -1,4 +1,4 @@
1
1
  <div class="flex flex-col gap-small min-w-0">
2
- <span class="txt-field-label !mb-0">{{ label }}</span>
2
+ <span class="txt-field-label !mb-0">{{ label() }}</span>
3
3
  <span class="txt-default" [ngClass]="valueClass">{{ displayValue }}</span>
4
4
  </div>
@@ -1,8 +1,8 @@
1
- <div class="mb-4 text-left w-full text-text-primary" [class]="class">
1
+ <div class="mb-4 text-left w-full text-text-primary" [class]="class()">
2
2
  @if (label) {
3
3
  <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
- @if (required) {
5
+ @if (required()) {
6
6
  <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
@@ -24,16 +24,16 @@
24
24
  [appTrimOnBlurCollapse]="true"
25
25
  [id]="fieldId"
26
26
  [type]="displayType"
27
- [placeholder]="placeholder"
27
+ [placeholder]="placeholder()"
28
28
  [disabled]="disabled"
29
29
  [value]="value"
30
30
  (input)="onInput($event)"
31
31
  (blur)="onBlur()"
32
32
  (focus)="onFocus()"
33
- [attr.maxlength]="maxlength"
33
+ [attr.maxlength]="maxlength()"
34
34
  [attr.aria-invalid]="invalid || null"
35
35
  [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
36
- [attr.aria-required]="required ? true : null"
36
+ [attr.aria-required]="required() ? true : null"
37
37
  [class]="type === 'otp' ? 'input-otp' : 'input-default'"
38
38
  [class.input-invalid]="invalid"
39
39
  [class.pr-8]="type === 'password' || suffixIcon"
@@ -48,7 +48,7 @@
48
48
  [attr.aria-label]="
49
49
  type === 'password'
50
50
  ? (showPassword ? 'Hide password' : 'Show password')
51
- : (suffixAriaLabel || null)
51
+ : (suffixAriaLabel() || null)
52
52
  "
53
53
  [attr.aria-pressed]="type === 'password' ? showPassword : null"
54
54
  >
@@ -1,6 +1,6 @@
1
1
  <div class="kanban-board">
2
2
  <div class="kanban-columns">
3
- @for (column of columns; track column.id) {
3
+ @for (column of columns(); track column.id) {
4
4
  <div class="kanban-column">
5
5
  <div class="kanban-column-header">
6
6
  <h3 class="kanban-column-title">{{ column.title }}</h3>
@@ -1,57 +1,57 @@
1
1
  <!-- Full-screen glass overlay -->
2
- <div
3
- *ngIf="show"
4
- class="fixed inset-0 z-50 flex items-center justify-center"
5
- aria-hidden="false"
6
- role="alert"
7
- aria-busy="true"
8
- >
9
- <div class="absolute inset-0 bg-bg-primary/30 backdrop-blur-xl" aria-hidden="true"></div>
10
-
2
+ @if (show()) {
11
3
  <div
12
- class="relative z-10 flex flex-col items-center gap-4 p-6 rounded-2xl shadow-xl bg-bg-primary/70 border border-border-strong backdrop-bg-primary/70"
13
- style="min-width: 220px; max-width: 90%"
14
- >
15
- <svg fill="url(#spinner-gradient)" width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
16
- <defs>
17
- <linearGradient id="spinner-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
18
- <stop offset="0%" style="stop-color: rgb(var(--gradient-primary-start)); stop-opacity: 1" />
19
- <stop offset="100%" style="stop-color: rgb(var(--gradient-primary-end)); stop-opacity: 1" />
20
- </linearGradient>
21
- </defs>
22
- <circle cx="4" cy="12" r="0">
23
- <animate begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
24
- <animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
25
- <animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
26
- <animate id="spinner_lo66" begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
27
- <animate id="spinner_z0Or" begin="spinner_lo66.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
28
- </circle>
29
- <circle cx="4" cy="12" r="3">
30
- <animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
31
- <animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
32
- <animate id="spinner_JsnR" begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
33
- <animate id="spinner_Aguh" begin="spinner_JsnR.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
34
- <animate begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
35
- </circle>
36
- <circle cx="12" cy="12" r="3">
37
- <animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
38
- <animate id="spinner_hSjk" begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
39
- <animate id="spinner_UHR2" begin="spinner_hSjk.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
40
- <animate begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
41
- <animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
42
- </circle>
43
- <circle cx="20" cy="12" r="3">
44
- <animate id="spinner_4v5M" begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
45
- <animate id="spinner_OLMs" begin="spinner_4v5M.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
46
- <animate begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
47
- <animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
48
- <animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
49
- </circle>
50
- </svg>
51
-
52
-
53
- <div *ngIf="message" class="text-sm text-text-primary text-center">
54
- {{ message }}
4
+ class="fixed inset-0 z-50 flex items-center justify-center"
5
+ aria-hidden="false"
6
+ role="alert"
7
+ aria-busy="true"
8
+ >
9
+ <div class="absolute inset-0 bg-bg-primary/30 backdrop-blur-xl" aria-hidden="true"></div>
10
+ <div
11
+ class="relative z-10 flex flex-col items-center gap-4 p-6 rounded-2xl shadow-xl bg-bg-primary/70 border border-border-strong backdrop-bg-primary/70"
12
+ style="min-width: 220px; max-width: 90%"
13
+ >
14
+ <svg fill="url(#spinner-gradient)" width="50" height="50" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
15
+ <defs>
16
+ <linearGradient id="spinner-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
17
+ <stop offset="0%" style="stop-color: rgb(var(--gradient-primary-start)); stop-opacity: 1" />
18
+ <stop offset="100%" style="stop-color: rgb(var(--gradient-primary-end)); stop-opacity: 1" />
19
+ </linearGradient>
20
+ </defs>
21
+ <circle cx="4" cy="12" r="0">
22
+ <animate begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
23
+ <animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
24
+ <animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
25
+ <animate id="spinner_lo66" begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
26
+ <animate id="spinner_z0Or" begin="spinner_lo66.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
27
+ </circle>
28
+ <circle cx="4" cy="12" r="3">
29
+ <animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
30
+ <animate begin="spinner_OLMs.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
31
+ <animate id="spinner_JsnR" begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
32
+ <animate id="spinner_Aguh" begin="spinner_JsnR.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
33
+ <animate begin="spinner_Aguh.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
34
+ </circle>
35
+ <circle cx="12" cy="12" r="3">
36
+ <animate begin="0;spinner_z0Or.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
37
+ <animate id="spinner_hSjk" begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
38
+ <animate id="spinner_UHR2" begin="spinner_hSjk.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
39
+ <animate begin="spinner_UHR2.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
40
+ <animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
41
+ </circle>
42
+ <circle cx="20" cy="12" r="3">
43
+ <animate id="spinner_4v5M" begin="0;spinner_z0Or.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="3;0" fill="freeze"/>
44
+ <animate id="spinner_OLMs" begin="spinner_4v5M.end" attributeName="cx" dur="0.001s" values="20;4" fill="freeze"/>
45
+ <animate begin="spinner_OLMs.end" attributeName="r" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="0;3" fill="freeze"/>
46
+ <animate begin="spinner_UHR2.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="4;12" fill="freeze"/>
47
+ <animate begin="spinner_Aguh.end" attributeName="cx" calcMode="spline" dur="0.5s" keySplines=".36,.6,.31,1" values="12;20" fill="freeze"/>
48
+ </circle>
49
+ </svg>
50
+ @if (message) {
51
+ <div class="text-sm text-text-primary text-center">
52
+ {{ message }}
53
+ </div>
54
+ }
55
55
  </div>
56
56
  </div>
57
- </div>
57
+ }
@@ -1,13 +1,13 @@
1
- <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth">
1
+ <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth()">
2
2
  <!-- Master: list pane -->
3
3
  <div class="fx-md-list card-common">
4
4
  <ng-content select="[mdHeader]"></ng-content>
5
5
  <ng-content select="[mdFilters]"></ng-content>
6
6
  <div
7
7
  class="flex flex-col gap-normal overflow-auto pr-xs"
8
- [style.maxHeight]="maxListHeight"
8
+ [style.maxHeight]="maxListHeight()"
9
9
  role="listbox"
10
- [attr.aria-label]="listLabel || null"
10
+ [attr.aria-label]="listLabel() || null"
11
11
  >
12
12
  @if (table.loading) {
13
13
  <fx-ui-skeleton-list [rows]="table.pageSize"></fx-ui-skeleton-list>
@@ -29,13 +29,13 @@
29
29
  "
30
30
  >
31
31
  <ng-container
32
- [ngTemplateOutlet]="rowTemplate"
32
+ [ngTemplateOutlet]="rowTemplate()"
33
33
  [ngTemplateOutletContext]="{ $implicit: row, selected: isSelected(row) }"
34
34
  ></ng-container>
35
35
  </button>
36
36
  }
37
37
  @if (!table.dataSource.data.length) {
38
- <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
38
+ <fx-ui-empty-state [message]="emptyMessage()"></fx-ui-empty-state>
39
39
  }
40
40
  }
41
41
  </div>
@@ -46,7 +46,7 @@
46
46
  class="mt-normal"
47
47
  [length]="table.total"
48
48
  [pageSize]="table.pageSize"
49
- [pageSizeOptions]="pageSizeOptions"
49
+ [pageSizeOptions]="pageSizeOptions()"
50
50
  ></mat-paginator>
51
51
  </div>
52
52
 
@@ -54,16 +54,16 @@
54
54
  <div class="w-full lg:flex-1 min-w-0">
55
55
  @if (table.selected) {
56
56
  @if (table.detailBusy) {
57
- <fx-ui-skeleton-detail [cards]="skeletonCards"></fx-ui-skeleton-detail>
57
+ <fx-ui-skeleton-detail [cards]="skeletonCards()"></fx-ui-skeleton-detail>
58
58
  } @else {
59
59
  <ng-container
60
- [ngTemplateOutlet]="detailTemplate"
60
+ [ngTemplateOutlet]="detailTemplate()"
61
61
  [ngTemplateOutletContext]="{ $implicit: table.detail, selected: table.selected }"
62
62
  ></ng-container>
63
63
  }
64
64
  } @else {
65
65
  <div class="card-common flex items-center justify-center h-[300px]">
66
- <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
66
+ <fx-ui-empty-state [message]="emptyMessage()"></fx-ui-empty-state>
67
67
  </div>
68
68
  }
69
69
  </div>
@@ -0,0 +1,27 @@
1
+ <button
2
+ type="button"
3
+ class="inline-flex items-center justify-center rounded-lg p-1 hover:bg-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
4
+ [matMenuTriggerFor]="menu"
5
+ [disabled]="disabled()"
6
+ [attr.aria-label]="ariaLabel()"
7
+ >
8
+ <ng-content></ng-content>
9
+ </button>
10
+
11
+ <mat-menu #menu="matMenu">
12
+ @for (item of items(); track item.label) {
13
+ <button
14
+ mat-menu-item
15
+ type="button"
16
+ [disabled]="item.disabled"
17
+ (click)="select.emit(item)"
18
+ >
19
+ <span class="flex items-center gap-normal" [class]="toneClass(item)">
20
+ @if (item.icon) {
21
+ <fx-ui-hero-icon [icon]="item.icon" [size]="18"></fx-ui-hero-icon>
22
+ }
23
+ <span>{{ item.label }}</span>
24
+ </span>
25
+ </button>
26
+ }
27
+ </mat-menu>
@@ -1,14 +1,14 @@
1
1
  <div
2
2
  class="w-full rounded-lg overflow-hidden"
3
- [class]="class"
3
+ [class]="class()"
4
4
  role="radiogroup"
5
5
  [attr.aria-labelledby]="label ? groupId + '-label' : null"
6
- >
6
+ >
7
7
  <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
8
8
  @if (label) {
9
9
  <span class="txt-field-label block" [id]="groupId + '-label'">
10
10
  {{ label }}
11
- @if (required) {
11
+ @if (required()) {
12
12
  <span class="txt-required" aria-hidden="true">*</span>
13
13
  }
14
14
  @if (tooltip) {
@@ -24,33 +24,33 @@
24
24
  </span>
25
25
  }
26
26
 
27
- <button
28
- *ngFor="let opt of options"
29
- type="button"
30
- role="radio"
31
- [attr.aria-checked]="value === opt.value"
32
- (click)="onSelectValue(opt.value)"
33
- [disabled]="disabled"
34
- [class.text-gray-900]="value !== opt.value"
35
- class="flex-1 px-normal w-full flex items-center gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed"
36
- [matTooltip]="opt.tooltip || ''"
37
- matTooltipPosition="right"
38
- matTooltipClass="text-text-primary text-xs font-semibold"
39
- >
40
- <span
27
+ @for (opt of options(); track opt) {
28
+ <button
29
+ type="button"
30
+ role="radio"
31
+ [attr.aria-checked]="value === opt.value"
32
+ (click)="onSelectValue(opt.value)"
33
+ [disabled]="disabled"
34
+ [class.text-gray-900]="value !== opt.value"
35
+ class="flex-1 px-normal w-full flex items-center gap-normal h-10 rounded-md hover:bg-bg-hover transition-all duration-150 disabled:opacity-60 disabled:cursor-not-allowed"
36
+ [matTooltip]="opt.tooltip || ''"
37
+ matTooltipPosition="right"
38
+ matTooltipClass="text-text-primary text-xs font-semibold"
39
+ >
40
+ <span
41
41
  [ngClass]="{
42
42
  'radio-checked': value === opt.value,
43
43
  'radio-uncheck': value !== opt.value,
44
44
  }"
45
- class="radio-container"
46
- >
47
- @if (value === opt.value) {
48
- <span class="radio-dot"></span>
49
- }
50
- </span>
51
-
52
- <span class="txt-default mb-0">{{ opt.label }}</span>
53
- </button>
45
+ class="radio-container"
46
+ >
47
+ @if (value === opt.value) {
48
+ <span class="radio-dot"></span>
49
+ }
50
+ </span>
51
+ <span class="txt-default mb-0">{{ opt.label }}</span>
52
+ </button>
53
+ }
54
54
  @if (invalid) {
55
55
  <div class="txt-invalid">
56
56
  {{ getErrorMessage(label) }}
@@ -1,8 +1,8 @@
1
- <div class="flex flex-col text-left mb-4 w-full" [class]="class">
1
+ <div class="flex flex-col text-left mb-4 w-full" [class]="class()">
2
2
  <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
3
3
  @if(label){<span class="txt-field-label" [id]="groupId + '-label'">
4
4
  {{ label }}
5
- @if(required) {<span class="txt-required" aria-hidden="true">*</span>} </span
5
+ @if(required()) {<span class="txt-required" aria-hidden="true">*</span>} </span
6
6
  >}
7
7
 
8
8
  <div
@@ -10,7 +10,7 @@
10
10
  role="radiogroup"
11
11
  [attr.aria-labelledby]="label ? groupId + '-label' : null"
12
12
  >
13
- @for(opt of options; track opt){<button
13
+ @for(opt of options(); track opt){<button
14
14
  type="button"
15
15
  role="radio"
16
16
  [attr.aria-checked]="value === opt.value"
@@ -3,20 +3,21 @@
3
3
  <input
4
4
  type="text"
5
5
  class="input-default px-9"
6
- [attr.placeholder]="placeholder"
6
+ [attr.placeholder]="placeholder()"
7
7
  [disabled]="disabled"
8
8
  [value]="value"
9
9
  (input)="onValueChange($any($event.target).value)"
10
10
  (keydown.enter)="onEnterKey()"
11
- />
11
+ />
12
12
 
13
- <button
14
- *ngIf="value && !disabled"
15
- type="button"
16
- class="absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary"
17
- (click)="clear()"
18
- aria-label="Clear"
19
- >
20
- <i class="material-symbols-outlined text-lg select-none">close</i>
21
- </button>
13
+ @if (value && !disabled) {
14
+ <button
15
+ type="button"
16
+ class="absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary"
17
+ (click)="clear()"
18
+ aria-label="Clear"
19
+ >
20
+ <i class="material-symbols-outlined text-lg select-none">close</i>
21
+ </button>
22
+ }
22
23
  </div>
@@ -3,7 +3,7 @@
3
3
  @if (icon) {
4
4
  <fx-ui-hero-icon [ngClass]="iconClass" [icon]="icon" [size]="18"></fx-ui-hero-icon>
5
5
  }
6
- <span class="txt-heading-06">{{ title }}</span>
6
+ <span class="txt-heading-06">{{ title() }}</span>
7
7
  </div>
8
8
  <ng-content></ng-content>
9
9
  </div>
@@ -1,8 +1,8 @@
1
- <div class="mb-4 text-left w-full" [class]="class">
1
+ <div class="mb-4 text-left w-full" [class]="class()">
2
2
  @if (label) {
3
3
  <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
- @if (required) {
5
+ @if (required()) {
6
6
  <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
@@ -22,21 +22,21 @@
22
22
  [attr.invalid]="invalid || null"
23
23
  class="fx-select-field w-full shadow-sm"
24
24
  appearance="fill"
25
- >
25
+ >
26
26
  <mat-select
27
27
  [id]="fieldId"
28
- [required]="!!required"
28
+ [required]="!!required()"
29
29
  [attr.aria-invalid]="invalid || null"
30
30
  [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
31
- [placeholder]="placeholder"
31
+ [placeholder]="placeholder()"
32
32
  [disabled]="disabled"
33
- [multiple]="multiple"
33
+ [multiple]="multiple()"
34
34
  [value]="value"
35
35
  (selectionChange)="onSelectionChange($event.value)"
36
36
  (openedChange)="onOpened($event)"
37
37
  (blur)="onTouched()"
38
- >
39
- @if (enableSearch) {
38
+ >
39
+ @if (enableSearch()) {
40
40
  <div class="px-semi w-full">
41
41
  <input
42
42
  type="text"
@@ -46,12 +46,14 @@
46
46
  (ngModelChange)="onSearchChange($event)"
47
47
  (click)="$event.stopPropagation()"
48
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"
49
- />
49
+ />
50
50
  </div>
51
51
  }
52
- <mat-option *ngFor="let option of filteredOptions" [value]="option.value">
53
- <span class="txt-default">{{ option.label }}</span>
54
- </mat-option>
52
+ @for (option of filteredOptions; track option) {
53
+ <mat-option [value]="option.value">
54
+ <span class="txt-default">{{ option.label }}</span>
55
+ </mat-option>
56
+ }
55
57
  </mat-select>
56
58
  </mat-form-field>
57
59
 
@@ -9,11 +9,11 @@
9
9
  </div>
10
10
  </div>
11
11
 
12
- @for (c of range(cards); track c) {
12
+ @for (c of range(cards()); track c) {
13
13
  <div class="card-common flex flex-col gap-large">
14
14
  <div class="h-4 w-1/3 rounded bg-bg-hover"></div>
15
15
  <div class="flex flex-col gap-normal">
16
- @for (r of range(rows); track r) {
16
+ @for (r of range(rows()); track r) {
17
17
  <div class="h-3 rounded bg-bg-hover" [style.width.%]="rowWidth(r)"></div>
18
18
  }
19
19
  </div>
@@ -1,5 +1,5 @@
1
1
  <div class="flex flex-col gap-normal animate-pulse">
2
- @for (r of range(rows); track r) {
2
+ @for (r of range(rows()); track r) {
3
3
  <div class="rounded-lg border border-border-default p-semi">
4
4
  <div class="flex items-center gap-normal mb-small">
5
5
  <div class="w-8 h-8 rounded-lg bg-bg-hover shrink-0"></div>
@@ -1,19 +1,19 @@
1
1
  <div class="w-full animate-pulse space-y-normal">
2
2
  <div
3
3
  class="grid gap-small"
4
- [ngStyle]="{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }"
4
+ [ngStyle]="{ 'grid-template-columns': 'repeat(' + columns() + ', minmax(0, 1fr))' }"
5
5
  >
6
- @for(col of [].constructor(columns); track col) {
6
+ @for(col of [].constructor(columns()); track col) {
7
7
  <div class="h-6 bg-bg-hover rounded"></div>
8
8
  }
9
9
  </div>
10
10
 
11
- @for(row of [].constructor(rows); track row) {
11
+ @for(row of [].constructor(rows()); track row) {
12
12
  <div
13
13
  class="grid gap-small"
14
- [ngStyle]="{ 'grid-template-columns': 'repeat(' + columns + ', minmax(0, 1fr))' }"
14
+ [ngStyle]="{ 'grid-template-columns': 'repeat(' + columns() + ', minmax(0, 1fr))' }"
15
15
  >
16
- @for(col of [].constructor(columns); track col) {
16
+ @for(col of [].constructor(columns()); track col) {
17
17
  <div class="h-7 bg-bg-hover rounded"></div>
18
18
  }
19
19
  </div>
@@ -3,7 +3,7 @@
3
3
  [class.cursor-not-allowed]="disabled"
4
4
  [class.opacity-60]="disabled"
5
5
  role="group"
6
- >
6
+ >
7
7
  <!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->
8
8
  <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
9
9
  <div
@@ -11,28 +11,30 @@
11
11
  class="relative touch-none py-4"
12
12
  (click)="onTrackClick($event)"
13
13
  [attr.aria-disabled]="disabled"
14
- [attr.aria-orientation]="vertical ? 'vertical' : 'horizontal'"
15
- >
14
+ [attr.aria-orientation]="vertical() ? 'vertical' : 'horizontal'"
15
+ >
16
16
  <div
17
17
  class="relative flex items-center"
18
- [ngClass]="{ 'flex-col h-48': vertical }"
19
- >
18
+ [ngClass]="{ 'flex-col h-48': vertical() }"
19
+ >
20
20
  <div
21
21
  class="relative flex-1 rounded-full bg-gray-600 h-2"
22
- [ngClass]="{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical }"
23
- >
24
- <div
25
- class="absolute left-0 top-0 h-full rounded-full bg-primary"
26
- *ngIf="!vertical"
27
- [style.width.%]="percent"
28
- ></div>
22
+ [ngClass]="{ 'w-1 rotate-0 h-full w-auto bg-border-gray-700': vertical() }"
23
+ >
24
+ @if (!vertical()) {
25
+ <div
26
+ class="absolute left-0 top-0 h-full rounded-full bg-primary"
27
+ [style.width.%]="percent"
28
+ ></div>
29
+ }
29
30
 
30
- <div
31
- class="absolute bottom-0 left-0 w-full rounded-full bg-primary"
32
- *ngIf="vertical"
33
- [style.height.%]="percent"
34
- style="transform-origin: bottom;"
35
- ></div>
31
+ @if (vertical()) {
32
+ <div
33
+ class="absolute bottom-0 left-0 w-full rounded-full bg-primary"
34
+ [style.height.%]="percent"
35
+ style="transform-origin: bottom;"
36
+ ></div>
37
+ }
36
38
 
37
39
  <button
38
40
  #thumb
@@ -41,8 +43,8 @@
41
43
  [ngStyle]="thumbStyle()"
42
44
  role="slider"
43
45
  aria-label="Slider"
44
- [attr.aria-valuemin]="min"
45
- [attr.aria-valuemax]="max"
46
+ [attr.aria-valuemin]="min()"
47
+ [attr.aria-valuemax]="max()"
46
48
  [attr.aria-valuenow]="value"
47
49
  [attr.aria-disabled]="disabled"
48
50
  tabindex="0"
@@ -1,5 +1,5 @@
1
1
  <div class="grid gap-normal" [ngStyle]="{ 'grid-template-columns': gridColumns }">
2
- @for (card of cards; track card.label) {
2
+ @for (card of cards(); track card.label) {
3
3
  <div class="card-common flex items-center gap-normal">
4
4
  @if (card.icon) {
5
5
  <div
@@ -2,7 +2,7 @@
2
2
  type="button"
3
3
  role="switch"
4
4
  [attr.aria-checked]="!!value"
5
- [attr.aria-label]="ariaLabel || null"
5
+ [attr.aria-label]="ariaLabel() || null"
6
6
  [disabled]="disabled"
7
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"
8
8
  [ngClass]="{