@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.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +108 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs +240 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +287 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +332 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +452 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs +227 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +417 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +232 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +4482 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs +157 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +1454 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +593 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +228 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +510 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +1721 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +721 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs +861 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +1558 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +1833 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +693 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +331 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +289 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +572 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs +36 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +278 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs +355 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs +241 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +433 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +691 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +678 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +335 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +708 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +319 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +1722 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs +115 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +768 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +1147 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +346 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs +186 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs +1082 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +579 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +443 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +391 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +315 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs +994 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +508 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +147 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +178 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +1344 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +1136 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +1311 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +533 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw.mjs +61 -0
- package/fesm2022/cdevhub-ngx-tw.mjs.map +1 -0
- package/package.json +265 -0
- package/theme/_base.css +394 -0
- package/theme/_dark.css +510 -0
- package/theme/_high-contrast.css +290 -0
- package/theme/_semantic.css +317 -0
- package/theme/_typography.css +9 -0
- package/theme/index.css +54 -0
- package/types/cdevhub-ngx-tw-accordion.d.ts +48 -0
- package/types/cdevhub-ngx-tw-alert.d.ts +67 -0
- package/types/cdevhub-ngx-tw-avatar.d.ts +79 -0
- package/types/cdevhub-ngx-tw-badge.d.ts +94 -0
- package/types/cdevhub-ngx-tw-breadcrumbs.d.ts +227 -0
- package/types/cdevhub-ngx-tw-button.d.ts +74 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +200 -0
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +113 -0
- package/types/cdevhub-ngx-tw-calendar.d.ts +1704 -0
- package/types/cdevhub-ngx-tw-card.d.ts +48 -0
- package/types/cdevhub-ngx-tw-carousel.d.ts +406 -0
- package/types/cdevhub-ngx-tw-checkbox.d.ts +136 -0
- package/types/cdevhub-ngx-tw-code-block.d.ts +93 -0
- package/types/cdevhub-ngx-tw-collapsible.d.ts +151 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +389 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +215 -0
- package/types/cdevhub-ngx-tw-core.d.ts +719 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +317 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +313 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +303 -0
- package/types/cdevhub-ngx-tw-empty-state.d.ts +104 -0
- package/types/cdevhub-ngx-tw-flip-card.d.ts +95 -0
- package/types/cdevhub-ngx-tw-form-field.d.ts +210 -0
- package/types/cdevhub-ngx-tw-icon-lucide.d.ts +29 -0
- package/types/cdevhub-ngx-tw-icon.d.ts +134 -0
- package/types/cdevhub-ngx-tw-input.d.ts +110 -0
- package/types/cdevhub-ngx-tw-item.d.ts +70 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +99 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +305 -0
- package/types/cdevhub-ngx-tw-popover.d.ts +157 -0
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +100 -0
- package/types/cdevhub-ngx-tw-radio.d.ts +174 -0
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +66 -0
- package/types/cdevhub-ngx-tw-select.d.ts +384 -0
- package/types/cdevhub-ngx-tw-separator.d.ts +28 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +335 -0
- package/types/cdevhub-ngx-tw-skeleton.d.ts +52 -0
- package/types/cdevhub-ngx-tw-slider.d.ts +258 -0
- package/types/cdevhub-ngx-tw-sort.d.ts +136 -0
- package/types/cdevhub-ngx-tw-spinner.d.ts +50 -0
- package/types/cdevhub-ngx-tw-split.d.ts +265 -0
- package/types/cdevhub-ngx-tw-stat.d.ts +140 -0
- package/types/cdevhub-ngx-tw-stepper.d.ts +127 -0
- package/types/cdevhub-ngx-tw-switch.d.ts +88 -0
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +107 -0
- package/types/cdevhub-ngx-tw-table.d.ts +479 -0
- package/types/cdevhub-ngx-tw-tabs.d.ts +121 -0
- package/types/cdevhub-ngx-tw-textarea.d.ts +62 -0
- package/types/cdevhub-ngx-tw-theme.d.ts +106 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +303 -0
- package/types/cdevhub-ngx-tw-timeline.d.ts +278 -0
- package/types/cdevhub-ngx-tw-toast.d.ts +388 -0
- package/types/cdevhub-ngx-tw-tooltip.d.ts +95 -0
- 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
|