@cdevhub/ngx-tw 0.2.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/fesm2022/cdevhub-ngx-tw-accordion.mjs +108 -0
  4. package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -0
  5. package/fesm2022/cdevhub-ngx-tw-alert.mjs +240 -0
  6. package/fesm2022/cdevhub-ngx-tw-alert.mjs.map +1 -0
  7. package/fesm2022/cdevhub-ngx-tw-avatar.mjs +287 -0
  8. package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -0
  9. package/fesm2022/cdevhub-ngx-tw-badge.mjs +332 -0
  10. package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -0
  11. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +452 -0
  12. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -0
  13. package/fesm2022/cdevhub-ngx-tw-button.mjs +227 -0
  14. package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -0
  15. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +417 -0
  16. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -0
  17. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +232 -0
  18. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -0
  19. package/fesm2022/cdevhub-ngx-tw-calendar.mjs +4482 -0
  20. package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -0
  21. package/fesm2022/cdevhub-ngx-tw-card.mjs +157 -0
  22. package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -0
  23. package/fesm2022/cdevhub-ngx-tw-carousel.mjs +1454 -0
  24. package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -0
  25. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +593 -0
  26. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -0
  27. package/fesm2022/cdevhub-ngx-tw-code-block.mjs +228 -0
  28. package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -0
  29. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +510 -0
  30. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -0
  31. package/fesm2022/cdevhub-ngx-tw-combobox.mjs +1721 -0
  32. package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -0
  33. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +721 -0
  34. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -0
  35. package/fesm2022/cdevhub-ngx-tw-core.mjs +861 -0
  36. package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -0
  37. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +1558 -0
  38. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -0
  39. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +1833 -0
  40. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -0
  41. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +693 -0
  42. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -0
  43. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +331 -0
  44. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -0
  45. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +289 -0
  46. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -0
  47. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +572 -0
  48. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -0
  49. package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs +36 -0
  50. package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs.map +1 -0
  51. package/fesm2022/cdevhub-ngx-tw-icon.mjs +278 -0
  52. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -0
  53. package/fesm2022/cdevhub-ngx-tw-input.mjs +355 -0
  54. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -0
  55. package/fesm2022/cdevhub-ngx-tw-item.mjs +241 -0
  56. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -0
  57. package/fesm2022/cdevhub-ngx-tw-menu.mjs +433 -0
  58. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -0
  59. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +691 -0
  60. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -0
  61. package/fesm2022/cdevhub-ngx-tw-popover.mjs +678 -0
  62. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -0
  63. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +335 -0
  64. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -0
  65. package/fesm2022/cdevhub-ngx-tw-radio.mjs +708 -0
  66. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -0
  67. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +319 -0
  68. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -0
  69. package/fesm2022/cdevhub-ngx-tw-select.mjs +1722 -0
  70. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -0
  71. package/fesm2022/cdevhub-ngx-tw-separator.mjs +115 -0
  72. package/fesm2022/cdevhub-ngx-tw-separator.mjs.map +1 -0
  73. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +768 -0
  74. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -0
  75. package/fesm2022/cdevhub-ngx-tw-skeleton.mjs +149 -0
  76. package/fesm2022/cdevhub-ngx-tw-skeleton.mjs.map +1 -0
  77. package/fesm2022/cdevhub-ngx-tw-slider.mjs +1147 -0
  78. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -0
  79. package/fesm2022/cdevhub-ngx-tw-sort.mjs +346 -0
  80. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -0
  81. package/fesm2022/cdevhub-ngx-tw-spinner.mjs +186 -0
  82. package/fesm2022/cdevhub-ngx-tw-spinner.mjs.map +1 -0
  83. package/fesm2022/cdevhub-ngx-tw-split.mjs +1082 -0
  84. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -0
  85. package/fesm2022/cdevhub-ngx-tw-stat.mjs +579 -0
  86. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -0
  87. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +443 -0
  88. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -0
  89. package/fesm2022/cdevhub-ngx-tw-switch.mjs +391 -0
  90. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -0
  91. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +315 -0
  92. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -0
  93. package/fesm2022/cdevhub-ngx-tw-table.mjs +994 -0
  94. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -0
  95. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +508 -0
  96. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -0
  97. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +147 -0
  98. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -0
  99. package/fesm2022/cdevhub-ngx-tw-theme.mjs +178 -0
  100. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -0
  101. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +1344 -0
  102. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -0
  103. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +1136 -0
  104. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -0
  105. package/fesm2022/cdevhub-ngx-tw-toast.mjs +1311 -0
  106. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -0
  107. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +533 -0
  108. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -0
  109. package/fesm2022/cdevhub-ngx-tw.mjs +61 -0
  110. package/fesm2022/cdevhub-ngx-tw.mjs.map +1 -0
  111. package/package.json +265 -0
  112. package/theme/_base.css +394 -0
  113. package/theme/_dark.css +510 -0
  114. package/theme/_high-contrast.css +290 -0
  115. package/theme/_semantic.css +317 -0
  116. package/theme/_typography.css +9 -0
  117. package/theme/index.css +54 -0
  118. package/types/cdevhub-ngx-tw-accordion.d.ts +48 -0
  119. package/types/cdevhub-ngx-tw-alert.d.ts +67 -0
  120. package/types/cdevhub-ngx-tw-avatar.d.ts +79 -0
  121. package/types/cdevhub-ngx-tw-badge.d.ts +94 -0
  122. package/types/cdevhub-ngx-tw-breadcrumbs.d.ts +227 -0
  123. package/types/cdevhub-ngx-tw-button.d.ts +74 -0
  124. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +200 -0
  125. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +113 -0
  126. package/types/cdevhub-ngx-tw-calendar.d.ts +1704 -0
  127. package/types/cdevhub-ngx-tw-card.d.ts +48 -0
  128. package/types/cdevhub-ngx-tw-carousel.d.ts +406 -0
  129. package/types/cdevhub-ngx-tw-checkbox.d.ts +136 -0
  130. package/types/cdevhub-ngx-tw-code-block.d.ts +93 -0
  131. package/types/cdevhub-ngx-tw-collapsible.d.ts +151 -0
  132. package/types/cdevhub-ngx-tw-combobox.d.ts +389 -0
  133. package/types/cdevhub-ngx-tw-command-palette.d.ts +215 -0
  134. package/types/cdevhub-ngx-tw-core.d.ts +719 -0
  135. package/types/cdevhub-ngx-tw-date-picker.d.ts +317 -0
  136. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +313 -0
  137. package/types/cdevhub-ngx-tw-dialog.d.ts +303 -0
  138. package/types/cdevhub-ngx-tw-empty-state.d.ts +104 -0
  139. package/types/cdevhub-ngx-tw-flip-card.d.ts +95 -0
  140. package/types/cdevhub-ngx-tw-form-field.d.ts +210 -0
  141. package/types/cdevhub-ngx-tw-icon-lucide.d.ts +29 -0
  142. package/types/cdevhub-ngx-tw-icon.d.ts +134 -0
  143. package/types/cdevhub-ngx-tw-input.d.ts +110 -0
  144. package/types/cdevhub-ngx-tw-item.d.ts +70 -0
  145. package/types/cdevhub-ngx-tw-menu.d.ts +99 -0
  146. package/types/cdevhub-ngx-tw-paginator.d.ts +305 -0
  147. package/types/cdevhub-ngx-tw-popover.d.ts +157 -0
  148. package/types/cdevhub-ngx-tw-progress-bar.d.ts +100 -0
  149. package/types/cdevhub-ngx-tw-radio.d.ts +174 -0
  150. package/types/cdevhub-ngx-tw-segmented-control.d.ts +66 -0
  151. package/types/cdevhub-ngx-tw-select.d.ts +384 -0
  152. package/types/cdevhub-ngx-tw-separator.d.ts +28 -0
  153. package/types/cdevhub-ngx-tw-sheet.d.ts +335 -0
  154. package/types/cdevhub-ngx-tw-skeleton.d.ts +52 -0
  155. package/types/cdevhub-ngx-tw-slider.d.ts +258 -0
  156. package/types/cdevhub-ngx-tw-sort.d.ts +136 -0
  157. package/types/cdevhub-ngx-tw-spinner.d.ts +50 -0
  158. package/types/cdevhub-ngx-tw-split.d.ts +265 -0
  159. package/types/cdevhub-ngx-tw-stat.d.ts +140 -0
  160. package/types/cdevhub-ngx-tw-stepper.d.ts +127 -0
  161. package/types/cdevhub-ngx-tw-switch.d.ts +88 -0
  162. package/types/cdevhub-ngx-tw-tab-nav.d.ts +107 -0
  163. package/types/cdevhub-ngx-tw-table.d.ts +479 -0
  164. package/types/cdevhub-ngx-tw-tabs.d.ts +121 -0
  165. package/types/cdevhub-ngx-tw-textarea.d.ts +62 -0
  166. package/types/cdevhub-ngx-tw-theme.d.ts +106 -0
  167. package/types/cdevhub-ngx-tw-time-picker.d.ts +303 -0
  168. package/types/cdevhub-ngx-tw-timeline.d.ts +278 -0
  169. package/types/cdevhub-ngx-tw-toast.d.ts +388 -0
  170. package/types/cdevhub-ngx-tw-tooltip.d.ts +95 -0
  171. package/types/cdevhub-ngx-tw.d.ts +49 -0
@@ -0,0 +1,579 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Directive, input, booleanAttribute, viewChild, signal, inject, Injector, afterRenderEffect, computed, ChangeDetectionStrategy, Component, contentChild } from '@angular/core';
3
+ import { NgTemplateOutlet } from '@angular/common';
4
+ import { tv } from 'tailwind-variants';
5
+ import { SkeletonComponent } from '@cdevhub/ngx-tw/skeleton';
6
+
7
+ // ── Slot marker directives (DOM projection) ───────────────────────────
8
+ /** Label slot — short caption ("Revenue", "Active users"). Projected into `<dt>`. */
9
+ class StatLabelDirective {
10
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
11
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: StatLabelDirective, isStandalone: true, selector: "[twStatLabel]", ngImport: i0 });
12
+ }
13
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatLabelDirective, decorators: [{
14
+ type: Directive,
15
+ args: [{ selector: '[twStatLabel]' }]
16
+ }] });
17
+ /** Value slot — the dominant numeric/text element. Projected into `<dd>`. */
18
+ class StatValueDirective {
19
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatValueDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
20
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: StatValueDirective, isStandalone: true, selector: "[twStatValue]", ngImport: i0 });
21
+ }
22
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatValueDirective, decorators: [{
23
+ type: Directive,
24
+ args: [{ selector: '[twStatValue]' }]
25
+ }] });
26
+ /** Description slot — optional secondary text under the value. Projected into a second `<dd>`. */
27
+ class StatDescriptionDirective {
28
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatDescriptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
29
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: StatDescriptionDirective, isStandalone: true, selector: "[twStatDescription]", ngImport: i0 });
30
+ }
31
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatDescriptionDirective, decorators: [{
32
+ type: Directive,
33
+ args: [{ selector: '[twStatDescription]' }]
34
+ }] });
35
+ /** Icon slot — optional leading icon. Triggers the icon-leading layout. */
36
+ class StatIconDirective {
37
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
38
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: StatIconDirective, isStandalone: true, selector: "[twStatIcon]", ngImport: i0 });
39
+ }
40
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatIconDirective, decorators: [{
41
+ type: Directive,
42
+ args: [{ selector: '[twStatIcon]' }]
43
+ }] });
44
+ /** Footer slot — optional auxiliary region (sparklines, tags, metadata). Always renders, even during loading. */
45
+ class StatFooterDirective {
46
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
47
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: StatFooterDirective, isStandalone: true, selector: "[twStatFooter]", ngImport: i0 });
48
+ }
49
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatFooterDirective, decorators: [{
50
+ type: Directive,
51
+ args: [{ selector: '[twStatFooter]' }]
52
+ }] });
53
+ // ── Stat tile tv() config ─────────────────────────────────────────────
54
+ const statTile = tv({
55
+ slots: {
56
+ root: 'block text-fg',
57
+ iconFlex: 'flex items-start gap-3',
58
+ iconWrapper: 'shrink-0 text-fg-muted',
59
+ contentStack: 'min-w-0 flex-1',
60
+ dl: 'flex flex-col',
61
+ label: 'text-fg-muted font-medium',
62
+ valueRow: 'flex items-baseline gap-2 flex-wrap',
63
+ value: 'text-fg block tabular-nums font-semibold',
64
+ description: 'text-fg-muted',
65
+ delta: 'inline-flex items-center',
66
+ footer: 'mt-3 pt-3 border-t border-border',
67
+ },
68
+ variants: {
69
+ variant: {
70
+ plain: { root: 'bg-transparent' },
71
+ outlined: { root: 'bg-surface border border-border rounded-lg' },
72
+ elevated: {
73
+ root: 'bg-surface-raised border border-border rounded-lg shadow hover:shadow-md transition-shadow duration-200 motion-reduce:transition-none',
74
+ },
75
+ filled: { root: 'bg-surface-muted rounded-lg' },
76
+ },
77
+ size: {
78
+ xs: {
79
+ root: 'p-2',
80
+ dl: 'gap-1',
81
+ label: 'text-2xs',
82
+ value: 'text-xs',
83
+ description: 'text-2xs',
84
+ delta: 'mt-1',
85
+ },
86
+ sm: {
87
+ root: 'p-3',
88
+ dl: 'gap-1',
89
+ label: 'text-xs',
90
+ value: 'text-sm',
91
+ description: 'text-xs',
92
+ delta: 'mt-1',
93
+ },
94
+ md: {
95
+ root: 'p-4',
96
+ dl: 'gap-1.5',
97
+ label: 'text-xs',
98
+ value: 'text-base',
99
+ description: 'text-xs',
100
+ delta: 'mt-1',
101
+ },
102
+ // lg/xl `value` uses `text-base` per the KPI-value carve-out
103
+ // (CLAUDE.md typography). The dominant numeric needs to outrank the
104
+ // `text-sm` label/description in visual hierarchy.
105
+ lg: {
106
+ root: 'p-6',
107
+ dl: 'gap-2',
108
+ label: 'text-sm',
109
+ value: 'text-base font-bold',
110
+ description: 'text-sm',
111
+ delta: 'mt-2',
112
+ },
113
+ xl: {
114
+ root: 'p-8',
115
+ dl: 'gap-2',
116
+ label: 'text-sm',
117
+ value: 'text-base font-extrabold',
118
+ description: 'text-sm',
119
+ delta: 'mt-2',
120
+ },
121
+ },
122
+ },
123
+ defaultVariants: {
124
+ variant: 'outlined',
125
+ size: 'md',
126
+ },
127
+ }, { twMerge: true });
128
+ // ── Delta tv() config ─────────────────────────────────────────────────
129
+ const statDelta = tv({
130
+ slots: {
131
+ root: 'relative inline-flex items-center gap-1 font-medium tabular-nums',
132
+ icon: 'shrink-0',
133
+ text: 'inline-flex items-center',
134
+ comparison: 'text-fg-muted',
135
+ },
136
+ variants: {
137
+ variant: {
138
+ badge: {
139
+ root: 'px-2 py-0.5 rounded-md text-xs',
140
+ },
141
+ inline: {
142
+ root: 'text-xs',
143
+ },
144
+ 'icon-only': {
145
+ // size-6 (24px) — square interactive-target scale at xs density per
146
+ // CLAUDE.md icon sub-scale; here it just centres the chevron.
147
+ root: 'justify-center size-6 rounded-md',
148
+ // Text and comparison stay in the DOM so the composed aria-label
149
+ // still picks them up; `sr-only` removes them visually.
150
+ text: 'sr-only',
151
+ comparison: 'sr-only',
152
+ },
153
+ },
154
+ effectiveColor: {
155
+ success: {},
156
+ error: {},
157
+ neutral: {},
158
+ },
159
+ },
160
+ compoundVariants: [
161
+ // ===== badge (soft chip) =====
162
+ {
163
+ variant: 'badge',
164
+ effectiveColor: 'success',
165
+ class: { root: 'bg-success-soft text-success-soft-fg' },
166
+ },
167
+ {
168
+ variant: 'badge',
169
+ effectiveColor: 'error',
170
+ class: { root: 'bg-error-soft text-error-soft-fg' },
171
+ },
172
+ // Neutral pinned explicitly to surface/fg tokens (advisor: never use
173
+ // `text-neutral-700` or template through neutral via -soft-fg chain).
174
+ {
175
+ variant: 'badge',
176
+ effectiveColor: 'neutral',
177
+ class: { root: 'bg-surface-muted text-fg-muted' },
178
+ },
179
+ // ===== inline (no chip) =====
180
+ {
181
+ variant: 'inline',
182
+ effectiveColor: 'success',
183
+ class: { root: 'text-success-700' },
184
+ },
185
+ {
186
+ variant: 'inline',
187
+ effectiveColor: 'error',
188
+ class: { root: 'text-error-700' },
189
+ },
190
+ {
191
+ variant: 'inline',
192
+ effectiveColor: 'neutral',
193
+ class: { root: 'text-fg-muted' },
194
+ },
195
+ // ===== icon-only =====
196
+ {
197
+ variant: 'icon-only',
198
+ effectiveColor: 'success',
199
+ class: { root: 'bg-success-soft text-success-soft-fg' },
200
+ },
201
+ {
202
+ variant: 'icon-only',
203
+ effectiveColor: 'error',
204
+ class: { root: 'bg-error-soft text-error-soft-fg' },
205
+ },
206
+ {
207
+ variant: 'icon-only',
208
+ effectiveColor: 'neutral',
209
+ class: { root: 'bg-surface-muted text-fg-muted' },
210
+ },
211
+ ],
212
+ defaultVariants: {
213
+ variant: 'badge',
214
+ effectiveColor: 'neutral',
215
+ },
216
+ }, { twMerge: true });
217
+ // ── Stat delta component ──────────────────────────────────────────────
218
+ /**
219
+ * Compact trend indicator — direction + projected delta text + optional
220
+ * comparison label. Usable standalone or projected into a `<tw-stat>` tile.
221
+ *
222
+ * Sentiment is conveyed by both icon direction and color; consumers running
223
+ * "lower is better" metrics (bounce rate, latency, churn) set `inverted` to
224
+ * swap the success/error color mapping without changing the literal direction
225
+ * or the announced verb.
226
+ */
227
+ class StatDeltaComponent {
228
+ /** Direction of change. `'up'` renders an up-chevron and (by default) the `success` color; `'down'` renders a down-chevron and the `error` color; `'neutral'` renders a horizontal-line glyph and the neutral color. Defaults to `'neutral'`. */
229
+ direction = input('neutral', ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
230
+ /** When true, swaps success/error semantics so `down` reads as success and `up` reads as error — use for metrics where lower is better (bounce rate, error rate, latency, churn). `neutral` direction is unaffected. Defaults to `false`. */
231
+ inverted = input(false, { ...(ngDevMode ? { debugName: "inverted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
232
+ /** Display style. `'badge'` (default) wraps the delta in a pill chip; `'inline'` is icon + text only with no chip; `'icon-only'` shows just the chevron for ultra-dense layouts. */
233
+ variant = input('badge', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
234
+ /** Optional comparison label rendered next to the delta value (e.g. `"vs last week"`, `"since launch"`). Defaults to `undefined`. */
235
+ comparisonLabel = input(...(ngDevMode ? [undefined, { debugName: "comparisonLabel" }] : /* istanbul ignore next */ []));
236
+ /** Explicit accessible label. When omitted, the component composes one from `direction` + projected text + `comparisonLabel`. Override when projected content is purely symbolic or already localized. Defaults to `undefined`. */
237
+ ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
238
+ /** @internal */
239
+ textEl = viewChild('textEl', ...(ngDevMode ? [{ debugName: "textEl" }] : /* istanbul ignore next */ []));
240
+ /** @internal */
241
+ projectedText = signal('', ...(ngDevMode ? [{ debugName: "projectedText" }] : /* istanbul ignore next */ []));
242
+ constructor() {
243
+ const injector = inject(Injector);
244
+ // Read the projected text via the wrapper span's textContent. textContent
245
+ // is recursive across projected children, so this resolves the consumer's
246
+ // string even when wrapped in arbitrary markup. afterRenderEffect runs
247
+ // after each render so projection changes are captured.
248
+ afterRenderEffect(() => {
249
+ const el = this.textEl()?.nativeElement;
250
+ if (!el) {
251
+ this.projectedText.set('');
252
+ return;
253
+ }
254
+ const text = (el.textContent ?? '').trim();
255
+ this.projectedText.set(text);
256
+ }, { injector });
257
+ }
258
+ /** @internal */
259
+ effectiveColor = computed(() => {
260
+ const dir = this.direction();
261
+ if (dir === 'neutral')
262
+ return 'neutral';
263
+ const inv = this.inverted();
264
+ if (dir === 'up')
265
+ return inv ? 'error' : 'success';
266
+ return inv ? 'success' : 'error';
267
+ }, ...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
268
+ /** @internal */
269
+ iconSizeClass = computed(() => {
270
+ // Glyph sub-scale: size-3 (12px) at the badge/inline xs/sm density,
271
+ // size-4 (16px) at md+. The delta lives inside text-xs, so size-3 holds.
272
+ // Larger contexts (md / lg / xl on the parent tile) get size-4.
273
+ return this.variant() === 'icon-only' ? 'size-4' : 'size-3';
274
+ }, ...(ngDevMode ? [{ debugName: "iconSizeClass" }] : /* istanbul ignore next */ []));
275
+ /** @internal */
276
+ composedAriaLabel = computed(() => {
277
+ const explicit = this.ariaLabel();
278
+ if (explicit)
279
+ return explicit;
280
+ const dir = this.direction();
281
+ const verb = dir === 'up' ? 'increased' : dir === 'down' ? 'decreased' : 'unchanged';
282
+ const text = this.projectedText();
283
+ const comp = this.comparisonLabel();
284
+ const parts = [];
285
+ if (text) {
286
+ parts.push(`${verb} by ${text}`);
287
+ }
288
+ else {
289
+ parts.push(verb);
290
+ }
291
+ if (comp)
292
+ parts.push(comp);
293
+ return parts.join(' ');
294
+ }, ...(ngDevMode ? [{ debugName: "composedAriaLabel" }] : /* istanbul ignore next */ []));
295
+ variantResult = computed(() => statDelta({ variant: this.variant(), effectiveColor: this.effectiveColor() }), ...(ngDevMode ? [{ debugName: "variantResult" }] : /* istanbul ignore next */ []));
296
+ rootClasses = computed(() => this.variantResult().root(), ...(ngDevMode ? [{ debugName: "rootClasses" }] : /* istanbul ignore next */ []));
297
+ iconClasses = computed(() => this.variantResult().icon(), ...(ngDevMode ? [{ debugName: "iconClasses" }] : /* istanbul ignore next */ []));
298
+ textClasses = computed(() => this.variantResult().text(), ...(ngDevMode ? [{ debugName: "textClasses" }] : /* istanbul ignore next */ []));
299
+ comparisonClasses = computed(() => this.variantResult().comparison(), ...(ngDevMode ? [{ debugName: "comparisonClasses" }] : /* istanbul ignore next */ []));
300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatDeltaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
301
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: StatDeltaComponent, isStandalone: true, selector: "tw-stat-delta", inputs: { direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, inverted: { classPropertyName: "inverted", publicName: "inverted", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, comparisonLabel: { classPropertyName: "comparisonLabel", publicName: "comparisonLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "img" }, properties: { "class": "rootClasses()", "attr.aria-label": "composedAriaLabel()" } }, viewQueries: [{ propertyName: "textEl", first: true, predicate: ["textEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
302
+ <svg
303
+ [class]="iconClasses() + ' ' + iconSizeClass()"
304
+ viewBox="0 0 16 16"
305
+ fill="none"
306
+ stroke="currentColor"
307
+ stroke-width="2"
308
+ stroke-linecap="round"
309
+ stroke-linejoin="round"
310
+ aria-hidden="true"
311
+ >
312
+ @switch (direction()) {
313
+ @case ('up') { <path d="M4 10l4-4 4 4" /> }
314
+ @case ('down') { <path d="M4 6l4 4 4-4" /> }
315
+ @default { <path d="M3 8h10" /> }
316
+ }
317
+ </svg>
318
+ <span #textEl [class]="textClasses()"><ng-content /></span>
319
+ @if (comparisonLabel(); as comp) {
320
+ <span [class]="comparisonClasses()">{{ comp }}</span>
321
+ }
322
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
323
+ }
324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatDeltaComponent, decorators: [{
325
+ type: Component,
326
+ args: [{
327
+ selector: 'tw-stat-delta',
328
+ changeDetection: ChangeDetectionStrategy.OnPush,
329
+ host: {
330
+ role: 'img',
331
+ '[class]': 'rootClasses()',
332
+ '[attr.aria-label]': 'composedAriaLabel()',
333
+ },
334
+ template: `
335
+ <svg
336
+ [class]="iconClasses() + ' ' + iconSizeClass()"
337
+ viewBox="0 0 16 16"
338
+ fill="none"
339
+ stroke="currentColor"
340
+ stroke-width="2"
341
+ stroke-linecap="round"
342
+ stroke-linejoin="round"
343
+ aria-hidden="true"
344
+ >
345
+ @switch (direction()) {
346
+ @case ('up') { <path d="M4 10l4-4 4 4" /> }
347
+ @case ('down') { <path d="M4 6l4 4 4-4" /> }
348
+ @default { <path d="M3 8h10" /> }
349
+ }
350
+ </svg>
351
+ <span #textEl [class]="textClasses()"><ng-content /></span>
352
+ @if (comparisonLabel(); as comp) {
353
+ <span [class]="comparisonClasses()">{{ comp }}</span>
354
+ }
355
+ `,
356
+ }]
357
+ }], ctorParameters: () => [], propDecorators: { direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], inverted: [{ type: i0.Input, args: [{ isSignal: true, alias: "inverted", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], comparisonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], textEl: [{ type: i0.ViewChild, args: ['textEl', { isSignal: true }] }] } });
358
+ // ── Stat tile component ───────────────────────────────────────────────
359
+ /**
360
+ * KPI tile — a compact display tile surfacing a single key performance
361
+ * indicator with an optional trend delta. Composes a `<dl>` / `<dt>` / `<dd>`
362
+ * definition list internally so the label-to-value pairing is announced
363
+ * naturally by assistive technology.
364
+ *
365
+ * Slots:
366
+ * - `[twStatLabel]` — caption (rendered as `<dt>`).
367
+ * - `[twStatValue]` — dominant value (rendered as `<dd>`).
368
+ * - `[twStatDescription]` — optional secondary text (second `<dd>`).
369
+ * - `[twStatIcon]` — optional leading icon (switches to icon-leading layout).
370
+ * - `[twStatFooter]` — optional auxiliary region (sparklines, tags).
371
+ * - `<tw-stat-delta>` — projected trend indicator alongside the value.
372
+ *
373
+ * Loading state replaces label/value/delta with `<tw-skeleton>` placeholders
374
+ * sized to match the density. Footer always renders during loading so
375
+ * consumers can compose their own skeleton charts.
376
+ */
377
+ class StatComponent {
378
+ /** Surface treatment. `'plain'` removes border and background; `'outlined'` (default) adds a border on the surface token; `'elevated'` adds shadow and uses the raised surface; `'filled'` uses the muted surface with no border. */
379
+ variant = input('outlined', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
380
+ /** Density scale — drives padding, internal gaps, value/label typography, and the skeleton placeholder dimensions. Defaults to `'md'`. */
381
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
382
+ /** When true, replaces label, value, and delta regions with `<tw-skeleton>` placeholders and toggles `aria-busy="true"` on the host. Projected footer content still renders. Defaults to `false`. */
383
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
384
+ /** @internal */
385
+ labelSlot = contentChild(StatLabelDirective, ...(ngDevMode ? [{ debugName: "labelSlot" }] : /* istanbul ignore next */ []));
386
+ /** @internal */
387
+ valueSlot = contentChild(StatValueDirective, ...(ngDevMode ? [{ debugName: "valueSlot" }] : /* istanbul ignore next */ []));
388
+ /** @internal */
389
+ descriptionSlot = contentChild(StatDescriptionDirective, ...(ngDevMode ? [{ debugName: "descriptionSlot" }] : /* istanbul ignore next */ []));
390
+ /** @internal */
391
+ iconSlot = contentChild(StatIconDirective, ...(ngDevMode ? [{ debugName: "iconSlot" }] : /* istanbul ignore next */ []));
392
+ /** @internal */
393
+ footerSlot = contentChild(StatFooterDirective, ...(ngDevMode ? [{ debugName: "footerSlot" }] : /* istanbul ignore next */ []));
394
+ /** @internal */
395
+ deltaSlot = contentChild(StatDeltaComponent, ...(ngDevMode ? [{ debugName: "deltaSlot" }] : /* istanbul ignore next */ []));
396
+ hasLabel = computed(() => !!this.labelSlot(), ...(ngDevMode ? [{ debugName: "hasLabel" }] : /* istanbul ignore next */ []));
397
+ hasValue = computed(() => !!this.valueSlot(), ...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
398
+ hasDescription = computed(() => !!this.descriptionSlot(), ...(ngDevMode ? [{ debugName: "hasDescription" }] : /* istanbul ignore next */ []));
399
+ hasIcon = computed(() => !!this.iconSlot(), ...(ngDevMode ? [{ debugName: "hasIcon" }] : /* istanbul ignore next */ []));
400
+ hasFooter = computed(() => !!this.footerSlot(), ...(ngDevMode ? [{ debugName: "hasFooter" }] : /* istanbul ignore next */ []));
401
+ hasDelta = computed(() => !!this.deltaSlot(), ...(ngDevMode ? [{ debugName: "hasDelta" }] : /* istanbul ignore next */ []));
402
+ iconLeading = computed(() => this.hasIcon(), ...(ngDevMode ? [{ debugName: "iconLeading" }] : /* istanbul ignore next */ []));
403
+ /** Skeleton width during loading state — scales with the size axis. */
404
+ loadingValueWidth = computed(() => {
405
+ switch (this.size()) {
406
+ case 'xs': return '4rem';
407
+ case 'sm': return '5rem';
408
+ case 'md': return '6rem';
409
+ case 'lg': return '8rem';
410
+ case 'xl': return '10rem';
411
+ }
412
+ }, ...(ngDevMode ? [{ debugName: "loadingValueWidth" }] : /* istanbul ignore next */ []));
413
+ /** Skeleton height during loading state — matches the value font size. */
414
+ loadingValueHeight = computed(() => {
415
+ switch (this.size()) {
416
+ case 'xs': return '0.75rem';
417
+ case 'sm': return '0.875rem';
418
+ case 'md': return '1rem';
419
+ case 'lg': return '1rem';
420
+ case 'xl': return '1rem';
421
+ }
422
+ }, ...(ngDevMode ? [{ debugName: "loadingValueHeight" }] : /* istanbul ignore next */ []));
423
+ variantResult = computed(() => statTile({ variant: this.variant(), size: this.size() }), ...(ngDevMode ? [{ debugName: "variantResult" }] : /* istanbul ignore next */ []));
424
+ rootClasses = computed(() => this.variantResult().root(), ...(ngDevMode ? [{ debugName: "rootClasses" }] : /* istanbul ignore next */ []));
425
+ iconFlexClasses = computed(() => this.variantResult().iconFlex(), ...(ngDevMode ? [{ debugName: "iconFlexClasses" }] : /* istanbul ignore next */ []));
426
+ iconWrapperClasses = computed(() => this.variantResult().iconWrapper(), ...(ngDevMode ? [{ debugName: "iconWrapperClasses" }] : /* istanbul ignore next */ []));
427
+ contentStackClasses = computed(() => this.variantResult().contentStack(), ...(ngDevMode ? [{ debugName: "contentStackClasses" }] : /* istanbul ignore next */ []));
428
+ dlClasses = computed(() => this.variantResult().dl(), ...(ngDevMode ? [{ debugName: "dlClasses" }] : /* istanbul ignore next */ []));
429
+ labelClasses = computed(() => this.variantResult().label(), ...(ngDevMode ? [{ debugName: "labelClasses" }] : /* istanbul ignore next */ []));
430
+ valueRowClasses = computed(() => this.variantResult().valueRow(), ...(ngDevMode ? [{ debugName: "valueRowClasses" }] : /* istanbul ignore next */ []));
431
+ valueClasses = computed(() => this.variantResult().value(), ...(ngDevMode ? [{ debugName: "valueClasses" }] : /* istanbul ignore next */ []));
432
+ descriptionClasses = computed(() => this.variantResult().description(), ...(ngDevMode ? [{ debugName: "descriptionClasses" }] : /* istanbul ignore next */ []));
433
+ deltaClasses = computed(() => this.variantResult().delta(), ...(ngDevMode ? [{ debugName: "deltaClasses" }] : /* istanbul ignore next */ []));
434
+ footerClasses = computed(() => this.variantResult().footer(), ...(ngDevMode ? [{ debugName: "footerClasses" }] : /* istanbul ignore next */ []));
435
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
436
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: StatComponent, isStandalone: true, selector: "tw-stat", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-live": "polite" }, properties: { "class": "rootClasses()", "attr.aria-busy": "loading() ? \"true\" : null" } }, queries: [{ propertyName: "labelSlot", first: true, predicate: StatLabelDirective, descendants: true, isSignal: true }, { propertyName: "valueSlot", first: true, predicate: StatValueDirective, descendants: true, isSignal: true }, { propertyName: "descriptionSlot", first: true, predicate: StatDescriptionDirective, descendants: true, isSignal: true }, { propertyName: "iconSlot", first: true, predicate: StatIconDirective, descendants: true, isSignal: true }, { propertyName: "footerSlot", first: true, predicate: StatFooterDirective, descendants: true, isSignal: true }, { propertyName: "deltaSlot", first: true, predicate: StatDeltaComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
437
+ @if (iconLeading()) {
438
+ <div [class]="iconFlexClasses()">
439
+ <div [class]="iconWrapperClasses()">
440
+ <ng-content select="[twStatIcon]" />
441
+ </div>
442
+ <div [class]="contentStackClasses()">
443
+ <ng-container [ngTemplateOutlet]="dlBlock" />
444
+ </div>
445
+ </div>
446
+ } @else {
447
+ <ng-container [ngTemplateOutlet]="dlBlock" />
448
+ }
449
+
450
+ @if (hasFooter()) {
451
+ <div [class]="footerClasses()">
452
+ <ng-content select="[twStatFooter]" />
453
+ </div>
454
+ }
455
+
456
+ <ng-template #dlBlock>
457
+ <dl [class]="dlClasses()">
458
+ @if (loading()) {
459
+ <dt [class]="labelClasses()">
460
+ <tw-skeleton width="40%" height="0.75rem" />
461
+ </dt>
462
+ } @else if (hasLabel()) {
463
+ <dt [class]="labelClasses()">
464
+ <ng-content select="[twStatLabel]" />
465
+ </dt>
466
+ }
467
+
468
+ <dd [class]="valueRowClasses()">
469
+ @if (loading()) {
470
+ <tw-skeleton [width]="loadingValueWidth()" [height]="loadingValueHeight()" />
471
+ } @else if (hasValue()) {
472
+ <span [class]="valueClasses()">
473
+ <ng-content select="[twStatValue]" />
474
+ </span>
475
+ }
476
+
477
+ @if (loading()) {
478
+ <span [class]="deltaClasses()">
479
+ <tw-skeleton width="3rem" height="1.25rem" />
480
+ </span>
481
+ } @else if (hasDelta()) {
482
+ <span [class]="deltaClasses()">
483
+ <ng-content select="tw-stat-delta" />
484
+ </span>
485
+ }
486
+ </dd>
487
+
488
+ @if (!loading() && hasDescription()) {
489
+ <dd [class]="descriptionClasses()">
490
+ <ng-content select="[twStatDescription]" />
491
+ </dd>
492
+ }
493
+ </dl>
494
+ </ng-template>
495
+ `, isInline: true, dependencies: [{ kind: "component", type: SkeletonComponent, selector: "tw-skeleton", inputs: ["shape", "animation", "width", "height", "lines", "announce"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
496
+ }
497
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: StatComponent, decorators: [{
498
+ type: Component,
499
+ args: [{
500
+ selector: 'tw-stat',
501
+ changeDetection: ChangeDetectionStrategy.OnPush,
502
+ imports: [SkeletonComponent, NgTemplateOutlet],
503
+ host: {
504
+ '[class]': 'rootClasses()',
505
+ // Permanent live region — `aria-busy` toggle alone signals state change
506
+ // so assistive tech announces the resolved content on loading→loaded
507
+ // without the live region being torn down mid-transition.
508
+ 'aria-live': 'polite',
509
+ '[attr.aria-busy]': 'loading() ? "true" : null',
510
+ },
511
+ template: `
512
+ @if (iconLeading()) {
513
+ <div [class]="iconFlexClasses()">
514
+ <div [class]="iconWrapperClasses()">
515
+ <ng-content select="[twStatIcon]" />
516
+ </div>
517
+ <div [class]="contentStackClasses()">
518
+ <ng-container [ngTemplateOutlet]="dlBlock" />
519
+ </div>
520
+ </div>
521
+ } @else {
522
+ <ng-container [ngTemplateOutlet]="dlBlock" />
523
+ }
524
+
525
+ @if (hasFooter()) {
526
+ <div [class]="footerClasses()">
527
+ <ng-content select="[twStatFooter]" />
528
+ </div>
529
+ }
530
+
531
+ <ng-template #dlBlock>
532
+ <dl [class]="dlClasses()">
533
+ @if (loading()) {
534
+ <dt [class]="labelClasses()">
535
+ <tw-skeleton width="40%" height="0.75rem" />
536
+ </dt>
537
+ } @else if (hasLabel()) {
538
+ <dt [class]="labelClasses()">
539
+ <ng-content select="[twStatLabel]" />
540
+ </dt>
541
+ }
542
+
543
+ <dd [class]="valueRowClasses()">
544
+ @if (loading()) {
545
+ <tw-skeleton [width]="loadingValueWidth()" [height]="loadingValueHeight()" />
546
+ } @else if (hasValue()) {
547
+ <span [class]="valueClasses()">
548
+ <ng-content select="[twStatValue]" />
549
+ </span>
550
+ }
551
+
552
+ @if (loading()) {
553
+ <span [class]="deltaClasses()">
554
+ <tw-skeleton width="3rem" height="1.25rem" />
555
+ </span>
556
+ } @else if (hasDelta()) {
557
+ <span [class]="deltaClasses()">
558
+ <ng-content select="tw-stat-delta" />
559
+ </span>
560
+ }
561
+ </dd>
562
+
563
+ @if (!loading() && hasDescription()) {
564
+ <dd [class]="descriptionClasses()">
565
+ <ng-content select="[twStatDescription]" />
566
+ </dd>
567
+ }
568
+ </dl>
569
+ </ng-template>
570
+ `,
571
+ }]
572
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], labelSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatLabelDirective), { isSignal: true }] }], valueSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatValueDirective), { isSignal: true }] }], descriptionSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatDescriptionDirective), { isSignal: true }] }], iconSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatIconDirective), { isSignal: true }] }], footerSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatFooterDirective), { isSignal: true }] }], deltaSlot: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StatDeltaComponent), { isSignal: true }] }] } });
573
+
574
+ /**
575
+ * Generated bundle index. Do not edit.
576
+ */
577
+
578
+ export { StatComponent, StatDeltaComponent, StatDescriptionDirective, StatFooterDirective, StatIconDirective, StatLabelDirective, StatValueDirective };
579
+ //# sourceMappingURL=cdevhub-ngx-tw-stat.mjs.map