@fusedashlabs/widgets 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.d.ts +1 -0
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.d.ts.map +1 -1
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.js +18 -4
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.js.map +1 -1
- package/dist/components/chart-renderer/lib/registry.d.ts.map +1 -1
- package/dist/components/chart-renderer/lib/registry.js +3 -1
- package/dist/components/chart-renderer/lib/registry.js.map +1 -1
- package/dist/components/kpi-widget/element/styles.d.ts.map +1 -1
- package/dist/components/kpi-widget/element/styles.js +268 -5
- package/dist/components/kpi-widget/element/styles.js.map +1 -1
- package/dist/components/kpi-widget/element/ui9000-kpi-widget.d.ts +10 -1
- package/dist/components/kpi-widget/element/ui9000-kpi-widget.d.ts.map +1 -1
- package/dist/components/kpi-widget/element/ui9000-kpi-widget.js +154 -27
- package/dist/components/kpi-widget/element/ui9000-kpi-widget.js.map +1 -1
- package/dist/components/kpi-widget/index.d.ts +1 -1
- package/dist/components/kpi-widget/index.d.ts.map +1 -1
- package/dist/components/kpi-widget/index.js +7 -4
- package/dist/components/kpi-widget/lib/extract.d.ts +5 -1
- package/dist/components/kpi-widget/lib/extract.d.ts.map +1 -1
- package/dist/components/kpi-widget/lib/extract.js +110 -34
- package/dist/components/kpi-widget/lib/extract.js.map +1 -1
- package/dist/components/kpi-widget/lib/format.d.ts +5 -0
- package/dist/components/kpi-widget/lib/format.d.ts.map +1 -1
- package/dist/components/kpi-widget/lib/format.js +10 -1
- package/dist/components/kpi-widget/lib/format.js.map +1 -1
- package/dist/components/kpi-widget/lib/grid.d.ts +5 -0
- package/dist/components/kpi-widget/lib/grid.d.ts.map +1 -1
- package/dist/components/kpi-widget/lib/grid.js +18 -0
- package/dist/components/kpi-widget/lib/grid.js.map +1 -1
- package/dist/components/kpi-widget/lib/index.d.ts +5 -5
- package/dist/components/kpi-widget/lib/index.d.ts.map +1 -1
- package/dist/components/kpi-widget/lib/normalize.d.ts +2 -0
- package/dist/components/kpi-widget/lib/normalize.d.ts.map +1 -1
- package/dist/components/kpi-widget/lib/normalize.js +73 -12
- package/dist/components/kpi-widget/lib/normalize.js.map +1 -1
- package/dist/components/kpi-widget/lib/types.d.ts +41 -0
- package/dist/components/kpi-widget/lib/types.d.ts.map +1 -1
- package/dist/components/kpi-widget/metadata.json.d.ts +1 -1
- package/dist/components/kpi-widget/metadata.json.js +1 -1
- package/dist/components/map-chart/element/ui9000-map-chart.d.ts +1 -0
- package/dist/components/map-chart/element/ui9000-map-chart.d.ts.map +1 -1
- package/dist/components/map-chart/element/ui9000-map-chart.js +12 -4
- package/dist/components/map-chart/element/ui9000-map-chart.js.map +1 -1
- package/dist/components/map-chart/lib/geojson.d.ts.map +1 -1
- package/dist/components/map-chart/lib/geojson.js +3 -1
- package/dist/components/map-chart/lib/geojson.js.map +1 -1
- package/dist/components/map-chart/metadata.json.d.ts +1 -1
- package/dist/components/map-chart/metadata.json.js +1 -1
- package/dist/components/map-chart/render/draw.d.ts.map +1 -1
- package/dist/components/map-chart/render/draw.js +2 -1
- package/dist/components/map-chart/render/draw.js.map +1 -1
- package/dist/components/partial-dependence-chart/element/styles.d.ts +2 -0
- package/dist/components/partial-dependence-chart/element/styles.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/element/styles.js +48 -0
- package/dist/components/partial-dependence-chart/element/styles.js.map +1 -0
- package/dist/components/partial-dependence-chart/element/ui9000-partial-dependence-chart.d.ts +30 -0
- package/dist/components/partial-dependence-chart/element/ui9000-partial-dependence-chart.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/element/ui9000-partial-dependence-chart.js +163 -0
- package/dist/components/partial-dependence-chart/element/ui9000-partial-dependence-chart.js.map +1 -0
- package/dist/components/partial-dependence-chart/index.d.ts +5 -0
- package/dist/components/partial-dependence-chart/index.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/index.js +18 -0
- package/dist/components/partial-dependence-chart/index.js.map +1 -0
- package/dist/components/partial-dependence-chart/lib/domain.d.ts +6 -0
- package/dist/components/partial-dependence-chart/lib/domain.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/lib/domain.js +16 -0
- package/dist/components/partial-dependence-chart/lib/domain.js.map +1 -0
- package/dist/components/partial-dependence-chart/lib/index.d.ts +5 -0
- package/dist/components/partial-dependence-chart/lib/index.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/lib/normalize.d.ts +4 -0
- package/dist/components/partial-dependence-chart/lib/normalize.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/lib/normalize.js +167 -0
- package/dist/components/partial-dependence-chart/lib/normalize.js.map +1 -0
- package/dist/components/partial-dependence-chart/lib/series.d.ts +15 -0
- package/dist/components/partial-dependence-chart/lib/series.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/lib/series.js +63 -0
- package/dist/components/partial-dependence-chart/lib/series.js.map +1 -0
- package/dist/components/partial-dependence-chart/lib/types.d.ts +50 -0
- package/dist/components/partial-dependence-chart/lib/types.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/metadata.json.d.ts +80 -0
- package/dist/components/partial-dependence-chart/metadata.json.js +42 -0
- package/dist/components/partial-dependence-chart/metadata.json.js.map +1 -0
- package/dist/components/partial-dependence-chart/render/draw.d.ts +34 -0
- package/dist/components/partial-dependence-chart/render/draw.d.ts.map +1 -0
- package/dist/components/partial-dependence-chart/render/draw.js +95 -0
- package/dist/components/partial-dependence-chart/render/draw.js.map +1 -0
- package/dist/components/partial-dependence-chart/render/index.d.ts +2 -0
- package/dist/components/partial-dependence-chart/render/index.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -10
- package/dist/index.js.map +1 -1
- package/dist/lazy/index.d.ts +2 -1
- package/dist/lazy/index.d.ts.map +1 -1
- package/dist/lazy/index.js +8 -0
- package/dist/lazy/index.js.map +1 -1
- package/dist/lazy/partial-dependence-chart.d.ts +4 -0
- package/dist/lazy/partial-dependence-chart.d.ts.map +1 -0
- package/dist/lazy/partial-dependence-chart.js +5 -0
- package/dist/lazy/partial-dependence-chart.js.map +1 -0
- package/dist/utils/chart-helpers.d.ts +2 -0
- package/dist/utils/chart-helpers.d.ts.map +1 -1
- package/dist/utils/chart-helpers.js +15 -0
- package/dist/utils/chart-helpers.js.map +1 -1
- package/dist/utils/fusedash-visual.d.ts +12 -0
- package/dist/utils/fusedash-visual.d.ts.map +1 -1
- package/dist/utils/fusedash-visual.js +7 -0
- package/dist/utils/fusedash-visual.js.map +1 -1
- package/package.json +11 -2
- package/dist/node_modules/lit-html/directive.js +0 -28
- package/dist/node_modules/lit-html/directive.js.map +0 -1
- package/dist/node_modules/lit-html/directives/style-map.js +0 -37
- package/dist/node_modules/lit-html/directives/style-map.js.map +0 -1
- package/dist/node_modules/lit-html/lit-html.js +0 -231
- package/dist/node_modules/lit-html/lit-html.js.map +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { nothing, html } from "lit";
|
|
2
|
-
import { styleMap
|
|
2
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
3
3
|
import { property, state, customElement } from "lit/decorators.js";
|
|
4
4
|
import { Ui9000ChartElement } from "../../../element/ui9000-chart-base.js";
|
|
5
5
|
import { parseJsonAttr } from "../../../utils/chart-helpers.js";
|
|
6
6
|
import { kpiWidgetStyles } from "./styles.js";
|
|
7
7
|
import { normalizeKpiData } from "../lib/normalize.js";
|
|
8
|
-
import { getKpiGridColumns, MIN_KPI_CELL_WIDTH, getKpiGridScrollAxis } from "../lib/grid.js";
|
|
8
|
+
import { getKpiGridColumns, getAdvancedSplitLayout, MIN_KPI_CELL_WIDTH, getKpiGridScrollAxis } from "../lib/grid.js";
|
|
9
|
+
import { visualisationChartType } from "../lib/extract.js";
|
|
9
10
|
import { chartShellStyles } from "../../../element/chart-shell-styles.js";
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
11
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -21,75 +22,198 @@ let Ui9000KpiWidget = class extends Ui9000ChartElement {
|
|
|
21
22
|
constructor() {
|
|
22
23
|
super(...arguments);
|
|
23
24
|
this.scale = "default";
|
|
24
|
-
this.
|
|
25
|
+
this._model = { layout: "empty", cards: [], supporting: [] };
|
|
25
26
|
this._gridColumns = 1;
|
|
26
27
|
this._scrollAxis = "vertical";
|
|
28
|
+
this._advancedSplit = "horizontal";
|
|
29
|
+
this._raf = 0;
|
|
30
|
+
this._rendererLoading = false;
|
|
27
31
|
}
|
|
28
32
|
connectedCallback() {
|
|
29
33
|
super.connectedCallback();
|
|
30
|
-
this._resizeObserver = new ResizeObserver(() => this.
|
|
34
|
+
this._resizeObserver = new ResizeObserver(() => this.scheduleLayout());
|
|
31
35
|
this._resizeObserver.observe(this);
|
|
32
36
|
}
|
|
33
37
|
disconnectedCallback() {
|
|
34
38
|
var _a;
|
|
35
39
|
(_a = this._resizeObserver) == null ? void 0 : _a.disconnect();
|
|
40
|
+
cancelAnimationFrame(this._raf);
|
|
36
41
|
super.disconnectedCallback();
|
|
37
42
|
}
|
|
38
43
|
updated(changed) {
|
|
39
44
|
if (changed.has("dataJson")) {
|
|
40
|
-
|
|
41
|
-
this._cards = model.cards;
|
|
45
|
+
this._model = normalizeKpiData(parseJsonAttr(this.dataJson, null));
|
|
42
46
|
this.updateGridLayout();
|
|
47
|
+
this.ensureChartRenderer();
|
|
43
48
|
}
|
|
44
49
|
}
|
|
50
|
+
/** Nested viz uses `<ui9000-chart-renderer>` — load it only for advanced KPIs. */
|
|
51
|
+
ensureChartRenderer() {
|
|
52
|
+
if (this._rendererLoading || this._model.layout !== "advanced" || !this._model.visualisation) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this._rendererLoading = true;
|
|
56
|
+
void import("../../chart-renderer/index.js").then(() => this.requestUpdate());
|
|
57
|
+
}
|
|
58
|
+
scheduleLayout() {
|
|
59
|
+
cancelAnimationFrame(this._raf);
|
|
60
|
+
this._raf = requestAnimationFrame(() => this.updateGridLayout());
|
|
61
|
+
}
|
|
45
62
|
updateGridLayout() {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
63
|
+
const layout = this._model.layout;
|
|
64
|
+
const count = layout === "advanced" ? this._model.supporting.length : this._model.cards.length;
|
|
65
|
+
const columns = getKpiGridColumns(this.clientWidth, Math.max(count, 1));
|
|
48
66
|
const scrollAxis = getKpiGridScrollAxis(columns, count);
|
|
67
|
+
const split = getAdvancedSplitLayout(
|
|
68
|
+
this.clientWidth,
|
|
69
|
+
this.clientHeight,
|
|
70
|
+
this._advancedSplit
|
|
71
|
+
);
|
|
49
72
|
if (columns !== this._gridColumns) this._gridColumns = columns;
|
|
50
73
|
if (scrollAxis !== this._scrollAxis) this._scrollAxis = scrollAxis;
|
|
74
|
+
if (split !== this._advancedSplit) this._advancedSplit = split;
|
|
75
|
+
}
|
|
76
|
+
renderBadge(card) {
|
|
77
|
+
if (!card.status) return nothing;
|
|
78
|
+
return html`
|
|
79
|
+
<span class="kpi-badge" data-variant=${card.status.variant}>${card.status.text}</span>
|
|
80
|
+
`;
|
|
51
81
|
}
|
|
52
82
|
renderValue(card) {
|
|
53
83
|
const label = card.label;
|
|
54
84
|
const unitLeft = (label == null ? void 0 : label.position) === "left" && label.text;
|
|
55
85
|
const unitRight = (label == null ? void 0 : label.position) === "right" && label.text;
|
|
86
|
+
const showIndicator = Boolean(card.showPercentage && card.indicator);
|
|
56
87
|
return html`
|
|
57
88
|
<div class="kpi-value-row">
|
|
58
89
|
${unitLeft ? html`<span class="kpi-unit">${unitLeft}</span>` : nothing}
|
|
59
90
|
<h2 class="kpi-value">${card.value}</h2>
|
|
91
|
+
${card.suffix ? html`<span class="kpi-suffix">${card.suffix}</span>` : nothing}
|
|
60
92
|
${unitRight ? html`<span class="kpi-unit">${unitRight}</span>` : nothing}
|
|
61
|
-
${
|
|
93
|
+
${showIndicator ? html`
|
|
94
|
+
<span class="kpi-indicator">
|
|
95
|
+
<span class="kpi-indicator-arrow" aria-hidden="true">›</span>
|
|
96
|
+
${card.indicator}
|
|
97
|
+
</span>
|
|
98
|
+
` : nothing}
|
|
62
99
|
</div>
|
|
63
100
|
`;
|
|
64
101
|
}
|
|
65
|
-
renderCard(card) {
|
|
102
|
+
renderCard(card, role) {
|
|
103
|
+
const showName = !card.hideName;
|
|
104
|
+
const showHeaderBadge = Boolean(card.status) && (role === "single" || role === "main");
|
|
105
|
+
const showInlineBadge = Boolean(card.status) && !showHeaderBadge;
|
|
66
106
|
return html`
|
|
67
|
-
<article class="kpi-card" aria-label=${card.name}>
|
|
68
|
-
|
|
107
|
+
<article class="kpi-card" data-role=${role} aria-label=${card.name}>
|
|
108
|
+
${showName || showHeaderBadge ? html`
|
|
109
|
+
<div class="kpi-header">
|
|
110
|
+
${showName ? html`<p class="kpi-name">${card.name}</p>` : html`<span></span>`}
|
|
111
|
+
${showHeaderBadge ? this.renderBadge(card) : nothing}
|
|
112
|
+
</div>
|
|
113
|
+
` : nothing}
|
|
69
114
|
${this.renderValue(card)}
|
|
70
|
-
${card.subtitle ? html
|
|
115
|
+
${showInlineBadge || card.subtitle ? html`
|
|
116
|
+
<div class="kpi-subtitle-row">
|
|
117
|
+
${showInlineBadge ? this.renderBadge(card) : nothing}
|
|
118
|
+
${card.subtitle ? html`<p class="kpi-subtitle">${card.subtitle}</p>` : nothing}
|
|
119
|
+
</div>
|
|
120
|
+
` : nothing}
|
|
71
121
|
</article>
|
|
72
122
|
`;
|
|
73
123
|
}
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
124
|
+
renderFooter() {
|
|
125
|
+
const { target, showTimestamp, updatedAt } = this._model;
|
|
126
|
+
const hasTarget = Boolean(target && String(target).length);
|
|
127
|
+
const updated = showTimestamp && updatedAt ? new Date(updatedAt).toLocaleDateString("en-US") : "";
|
|
128
|
+
if (!hasTarget && !updated) return nothing;
|
|
129
|
+
return html`
|
|
130
|
+
<div class="kpi-footer">
|
|
131
|
+
${hasTarget ? html`
|
|
132
|
+
<div class="kpi-footer-block">
|
|
133
|
+
<span class="kpi-footer-label">Target</span>
|
|
134
|
+
<span class="kpi-footer-value">${target}</span>
|
|
135
|
+
</div>
|
|
136
|
+
` : html`<span></span>`}
|
|
137
|
+
${updated ? html`
|
|
138
|
+
<div class="kpi-footer-block">
|
|
139
|
+
<span class="kpi-footer-label">Updated</span>
|
|
140
|
+
<span class="kpi-footer-value">${updated}</span>
|
|
141
|
+
</div>
|
|
142
|
+
` : nothing}
|
|
143
|
+
</div>
|
|
144
|
+
`;
|
|
145
|
+
}
|
|
146
|
+
renderAdvanced() {
|
|
147
|
+
const main = this._model.main;
|
|
148
|
+
if (!main) return nothing;
|
|
149
|
+
const viz = this._model.visualisation;
|
|
150
|
+
const chartType = viz && typeof viz === "object" ? visualisationChartType({ visualisation: viz }) : void 0;
|
|
151
|
+
const supporting = this._model.supporting;
|
|
152
|
+
const supportingStyle = supporting.length > 0 ? styleMap({
|
|
78
153
|
gridTemplateColumns: `repeat(${this._gridColumns}, minmax(${MIN_KPI_CELL_WIDTH}px, 1fr))`
|
|
79
|
-
});
|
|
154
|
+
}) : nothing;
|
|
80
155
|
return html`
|
|
81
|
-
<div class="kpi-
|
|
82
|
-
|
|
83
|
-
|
|
156
|
+
<div class="kpi-advanced">
|
|
157
|
+
<div class="kpi-advanced-main">
|
|
158
|
+
<article class="kpi-card" data-role="main" aria-label=${main.name}>
|
|
159
|
+
<div class="kpi-header">
|
|
160
|
+
<p class="kpi-name">${main.name}</p>
|
|
161
|
+
${this.renderBadge(main)}
|
|
162
|
+
</div>
|
|
163
|
+
<div class="kpi-advanced-split" data-split=${this._advancedSplit}>
|
|
164
|
+
<div class="kpi-advanced-value">${this.renderValue(main)}</div>
|
|
165
|
+
${viz && chartType ? html`
|
|
166
|
+
<div class="kpi-advanced-chart">
|
|
167
|
+
<ui9000-chart-renderer
|
|
168
|
+
data=${JSON.stringify(viz)}
|
|
169
|
+
chart-type=${chartType}
|
|
170
|
+
scale=${this.scale}
|
|
171
|
+
show-header="false"
|
|
172
|
+
show-legend="false"
|
|
173
|
+
show-grid
|
|
174
|
+
show-tooltip
|
|
175
|
+
></ui9000-chart-renderer>
|
|
176
|
+
</div>
|
|
177
|
+
` : nothing}
|
|
178
|
+
</div>
|
|
179
|
+
</article>
|
|
180
|
+
</div>
|
|
181
|
+
${supporting.length ? html`
|
|
84
182
|
<div
|
|
85
|
-
class="kpi-
|
|
86
|
-
data-layout=${single ? "single" : "grid"}
|
|
183
|
+
class="kpi-supporting"
|
|
87
184
|
data-scroll=${this._scrollAxis}
|
|
88
|
-
|
|
185
|
+
style=${supportingStyle}
|
|
89
186
|
>
|
|
90
|
-
${
|
|
187
|
+
${supporting.map((card) => this.renderCard(card, "supporting"))}
|
|
91
188
|
</div>
|
|
92
|
-
`}
|
|
189
|
+
` : nothing}
|
|
190
|
+
</div>
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
render() {
|
|
194
|
+
const model = this._model;
|
|
195
|
+
const layout = model.layout;
|
|
196
|
+
const single = layout === "single";
|
|
197
|
+
const title = model.title;
|
|
198
|
+
const gridStyle = layout === "grid" ? styleMap({
|
|
199
|
+
gridTemplateColumns: `repeat(${this._gridColumns}, minmax(${MIN_KPI_CELL_WIDTH}px, 1fr))`
|
|
200
|
+
}) : nothing;
|
|
201
|
+
return html`
|
|
202
|
+
<div class="kpi-root" data-layout=${layout} data-single=${single ? "true" : "false"}>
|
|
203
|
+
${title ? html`<div class="kpi-section-title">${title}</div>` : nothing}
|
|
204
|
+
${layout === "empty" ? html`<div class="empty">No KPI data</div>` : layout === "advanced" ? this.renderAdvanced() : html`
|
|
205
|
+
<div
|
|
206
|
+
class="kpi-grid"
|
|
207
|
+
data-layout=${single ? "single" : "grid"}
|
|
208
|
+
data-scroll=${this._scrollAxis}
|
|
209
|
+
style=${gridStyle}
|
|
210
|
+
>
|
|
211
|
+
${model.cards.map(
|
|
212
|
+
(card) => this.renderCard(card, single ? "single" : "grid")
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
`}
|
|
216
|
+
${this.renderFooter()}
|
|
93
217
|
${this.renderShellLabelTooltip()}
|
|
94
218
|
</div>
|
|
95
219
|
`;
|
|
@@ -101,13 +225,16 @@ __decorateClass([
|
|
|
101
225
|
], Ui9000KpiWidget.prototype, "scale", 2);
|
|
102
226
|
__decorateClass([
|
|
103
227
|
state()
|
|
104
|
-
], Ui9000KpiWidget.prototype, "
|
|
228
|
+
], Ui9000KpiWidget.prototype, "_model", 2);
|
|
105
229
|
__decorateClass([
|
|
106
230
|
state()
|
|
107
231
|
], Ui9000KpiWidget.prototype, "_gridColumns", 2);
|
|
108
232
|
__decorateClass([
|
|
109
233
|
state()
|
|
110
234
|
], Ui9000KpiWidget.prototype, "_scrollAxis", 2);
|
|
235
|
+
__decorateClass([
|
|
236
|
+
state()
|
|
237
|
+
], Ui9000KpiWidget.prototype, "_advancedSplit", 2);
|
|
111
238
|
Ui9000KpiWidget = __decorateClass([
|
|
112
239
|
customElement("ui9000-kpi-widget")
|
|
113
240
|
], Ui9000KpiWidget);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui9000-kpi-widget.js","sources":["../../../../src/components/kpi-widget/element/ui9000-kpi-widget.ts"],"sourcesContent":["import { html, nothing, type PropertyValues } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport { chartShellStyles, Ui9000ChartElement } from '../../../element/ui9000-chart-base.js';\nimport type { WidgetScale } from '../../../types/index.js';\nimport { parseJsonAttr } from '../../../utils/chart-helpers.js';\nimport {\n getKpiGridColumns,\n getKpiGridScrollAxis,\n MIN_KPI_CELL_WIDTH,\n normalizeKpiData,\n type KpiCardModel,\n type KpiGridScrollAxis,\n type KpiWidgetData,\n} from '../lib/index.js';\nimport { kpiWidgetStyles } from './styles.js';\n\n@customElement('ui9000-kpi-widget')\nexport class Ui9000KpiWidget extends Ui9000ChartElement {\n static override styles = [kpiWidgetStyles, chartShellStyles];\n\n @property({ type: String, attribute: 'scale' })\n scale: WidgetScale = 'default';\n\n @state()\n private _cards: KpiCardModel[] = [];\n\n @state()\n private _gridColumns = 1;\n\n @state()\n private _scrollAxis: KpiGridScrollAxis = 'vertical';\n\n private _resizeObserver?: ResizeObserver;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._resizeObserver = new ResizeObserver(() => this.updateGridLayout());\n this._resizeObserver.observe(this);\n }\n\n override disconnectedCallback(): void {\n this._resizeObserver?.disconnect();\n super.disconnectedCallback();\n }\n\n override updated(changed: PropertyValues): void {\n if (changed.has('dataJson')) {\n const model = normalizeKpiData(parseJsonAttr<KpiWidgetData>(this.dataJson, null));\n this._cards = model.cards;\n this.updateGridLayout();\n }\n }\n\n private updateGridLayout(): void {\n const count = this._cards.length;\n const columns = getKpiGridColumns(this.clientWidth, count);\n const scrollAxis = getKpiGridScrollAxis(columns, count);\n if (columns !== this._gridColumns) this._gridColumns = columns;\n if (scrollAxis !== this._scrollAxis) this._scrollAxis = scrollAxis;\n }\n\n private renderValue(card: KpiCardModel) {\n const label = card.label;\n const unitLeft = label?.position === 'left' && label.text;\n const unitRight = label?.position === 'right' && label.text;\n\n return html`\n <div class=\"kpi-value-row\">\n ${unitLeft ? html`<span class=\"kpi-unit\">${unitLeft}</span>` : nothing}\n <h2 class=\"kpi-value\">${card.value}</h2>\n ${unitRight ? html`<span class=\"kpi-unit\">${unitRight}</span>` : nothing}\n ${card.indicator ? html`<span class=\"kpi-indicator\">${card.indicator}</span>` : nothing}\n </div>\n `;\n }\n\n private renderCard(card: KpiCardModel) {\n return html`\n <article class=\"kpi-card\" aria-label=${card.name}>\n <p class=\"kpi-name\">${card.name}</p>\n ${this.renderValue(card)}\n ${card.subtitle ? html`<p class=\"kpi-subtitle\">${card.subtitle}</p>` : nothing}\n </article>\n `;\n }\n\n override render() {\n const title = this.headerTitle();\n const single = this._cards.length <= 1;\n\n const gridStyle = single\n ? nothing\n : styleMap({\n gridTemplateColumns: `repeat(${this._gridColumns}, minmax(${MIN_KPI_CELL_WIDTH}px, 1fr))`,\n });\n\n return html`\n <div class=\"kpi-root\" data-single=${single ? 'true' : 'false'}>\n ${this.renderShellHeader(title)}\n ${this._cards.length === 0\n ? html`<div class=\"empty\">No KPI data</div>`\n : html`\n <div\n class=\"kpi-grid\"\n data-layout=${single ? 'single' : 'grid'}\n data-scroll=${this._scrollAxis}\n ${gridStyle}\n >\n ${this._cards.map((card) => this.renderCard(card))}\n </div>\n `}\n ${this.renderShellLabelTooltip()}\n </div>\n `;\n }\n}\n\nexport function registerKpiWidget(): void {\n if (!customElements.get('ui9000-kpi-widget')) {\n customElements.define('ui9000-kpi-widget', Ui9000KpiWidget);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ui9000-kpi-widget': Ui9000KpiWidget;\n }\n}\n"],"names":["styleMap"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ui9000-kpi-widget.js","sources":["../../../../src/components/kpi-widget/element/ui9000-kpi-widget.ts"],"sourcesContent":["import { html, nothing, type PropertyValues } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport { chartShellStyles, Ui9000ChartElement } from '../../../element/ui9000-chart-base.js';\nimport type { WidgetScale } from '../../../types/index.js';\nimport { parseJsonAttr } from '../../../utils/chart-helpers.js';\nimport {\n getAdvancedSplitLayout,\n getKpiGridColumns,\n getKpiGridScrollAxis,\n MIN_KPI_CELL_WIDTH,\n visualisationChartType,\n normalizeKpiData,\n type AdvancedSplitLayout,\n type KpiCardModel,\n type KpiCardRole,\n type KpiGridScrollAxis,\n type KpiWidgetData,\n type KpiWidgetModel,\n} from '../lib/index.js';\nimport { kpiWidgetStyles } from './styles.js';\n\n@customElement('ui9000-kpi-widget')\nexport class Ui9000KpiWidget extends Ui9000ChartElement {\n static override styles = [kpiWidgetStyles, chartShellStyles];\n\n @property({ type: String, attribute: 'scale' })\n scale: WidgetScale = 'default';\n\n @state()\n private _model: KpiWidgetModel = { layout: 'empty', cards: [], supporting: [] };\n\n @state()\n private _gridColumns = 1;\n\n @state()\n private _scrollAxis: KpiGridScrollAxis = 'vertical';\n\n @state()\n private _advancedSplit: AdvancedSplitLayout = 'horizontal';\n\n private _resizeObserver?: ResizeObserver;\n private _raf = 0;\n private _rendererLoading = false;\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._resizeObserver = new ResizeObserver(() => this.scheduleLayout());\n this._resizeObserver.observe(this);\n }\n\n override disconnectedCallback(): void {\n this._resizeObserver?.disconnect();\n cancelAnimationFrame(this._raf);\n super.disconnectedCallback();\n }\n\n override updated(changed: PropertyValues): void {\n if (changed.has('dataJson')) {\n this._model = normalizeKpiData(parseJsonAttr<KpiWidgetData>(this.dataJson, null));\n this.updateGridLayout();\n this.ensureChartRenderer();\n }\n }\n\n /** Nested viz uses `<ui9000-chart-renderer>` — load it only for advanced KPIs. */\n private ensureChartRenderer(): void {\n if (\n this._rendererLoading ||\n this._model.layout !== 'advanced' ||\n !this._model.visualisation\n ) {\n return;\n }\n this._rendererLoading = true;\n void import('../../chart-renderer/index.js').then(() => this.requestUpdate());\n }\n\n private scheduleLayout(): void {\n cancelAnimationFrame(this._raf);\n this._raf = requestAnimationFrame(() => this.updateGridLayout());\n }\n\n private updateGridLayout(): void {\n const layout = this._model.layout;\n const count =\n layout === 'advanced' ? this._model.supporting.length : this._model.cards.length;\n const columns = getKpiGridColumns(this.clientWidth, Math.max(count, 1));\n const scrollAxis = getKpiGridScrollAxis(columns, count);\n const split = getAdvancedSplitLayout(\n this.clientWidth,\n this.clientHeight,\n this._advancedSplit,\n );\n if (columns !== this._gridColumns) this._gridColumns = columns;\n if (scrollAxis !== this._scrollAxis) this._scrollAxis = scrollAxis;\n if (split !== this._advancedSplit) this._advancedSplit = split;\n }\n\n private renderBadge(card: KpiCardModel) {\n if (!card.status) return nothing;\n return html`\n <span class=\"kpi-badge\" data-variant=${card.status.variant}>${card.status.text}</span>\n `;\n }\n\n private renderValue(card: KpiCardModel) {\n const label = card.label;\n const unitLeft = label?.position === 'left' && label.text;\n const unitRight = label?.position === 'right' && label.text;\n const showIndicator = Boolean(card.showPercentage && card.indicator);\n\n return html`\n <div class=\"kpi-value-row\">\n ${unitLeft ? html`<span class=\"kpi-unit\">${unitLeft}</span>` : nothing}\n <h2 class=\"kpi-value\">${card.value}</h2>\n ${card.suffix ? html`<span class=\"kpi-suffix\">${card.suffix}</span>` : nothing}\n ${unitRight ? html`<span class=\"kpi-unit\">${unitRight}</span>` : nothing}\n ${showIndicator\n ? html`\n <span class=\"kpi-indicator\">\n <span class=\"kpi-indicator-arrow\" aria-hidden=\"true\">›</span>\n ${card.indicator}\n </span>\n `\n : nothing}\n </div>\n `;\n }\n\n private renderCard(card: KpiCardModel, role: KpiCardRole) {\n const showName = !card.hideName;\n const showHeaderBadge = Boolean(card.status) && (role === 'single' || role === 'main');\n const showInlineBadge = Boolean(card.status) && !showHeaderBadge;\n\n return html`\n <article class=\"kpi-card\" data-role=${role} aria-label=${card.name}>\n ${showName || showHeaderBadge\n ? html`\n <div class=\"kpi-header\">\n ${showName ? html`<p class=\"kpi-name\">${card.name}</p>` : html`<span></span>`}\n ${showHeaderBadge ? this.renderBadge(card) : nothing}\n </div>\n `\n : nothing}\n ${this.renderValue(card)}\n ${showInlineBadge || card.subtitle\n ? html`\n <div class=\"kpi-subtitle-row\">\n ${showInlineBadge ? this.renderBadge(card) : nothing}\n ${card.subtitle ? html`<p class=\"kpi-subtitle\">${card.subtitle}</p>` : nothing}\n </div>\n `\n : nothing}\n </article>\n `;\n }\n\n private renderFooter() {\n const { target, showTimestamp, updatedAt } = this._model;\n const hasTarget = Boolean(target && String(target).length);\n const updated =\n showTimestamp && updatedAt\n ? new Date(updatedAt).toLocaleDateString('en-US')\n : '';\n if (!hasTarget && !updated) return nothing;\n\n return html`\n <div class=\"kpi-footer\">\n ${hasTarget\n ? html`\n <div class=\"kpi-footer-block\">\n <span class=\"kpi-footer-label\">Target</span>\n <span class=\"kpi-footer-value\">${target}</span>\n </div>\n `\n : html`<span></span>`}\n ${updated\n ? html`\n <div class=\"kpi-footer-block\">\n <span class=\"kpi-footer-label\">Updated</span>\n <span class=\"kpi-footer-value\">${updated}</span>\n </div>\n `\n : nothing}\n </div>\n `;\n }\n\n private renderAdvanced() {\n const main = this._model.main;\n if (!main) return nothing;\n const viz = this._model.visualisation;\n const chartType =\n viz && typeof viz === 'object'\n ? visualisationChartType({ visualisation: viz })\n : undefined;\n const supporting = this._model.supporting;\n const supportingStyle =\n supporting.length > 0\n ? styleMap({\n gridTemplateColumns: `repeat(${this._gridColumns}, minmax(${MIN_KPI_CELL_WIDTH}px, 1fr))`,\n })\n : nothing;\n\n return html`\n <div class=\"kpi-advanced\">\n <div class=\"kpi-advanced-main\">\n <article class=\"kpi-card\" data-role=\"main\" aria-label=${main.name}>\n <div class=\"kpi-header\">\n <p class=\"kpi-name\">${main.name}</p>\n ${this.renderBadge(main)}\n </div>\n <div class=\"kpi-advanced-split\" data-split=${this._advancedSplit}>\n <div class=\"kpi-advanced-value\">${this.renderValue(main)}</div>\n ${viz && chartType\n ? html`\n <div class=\"kpi-advanced-chart\">\n <ui9000-chart-renderer\n data=${JSON.stringify(viz)}\n chart-type=${chartType}\n scale=${this.scale}\n show-header=\"false\"\n show-legend=\"false\"\n show-grid\n show-tooltip\n ></ui9000-chart-renderer>\n </div>\n `\n : nothing}\n </div>\n </article>\n </div>\n ${supporting.length\n ? html`\n <div\n class=\"kpi-supporting\"\n data-scroll=${this._scrollAxis}\n style=${supportingStyle}\n >\n ${supporting.map((card) => this.renderCard(card, 'supporting'))}\n </div>\n `\n : nothing}\n </div>\n `;\n }\n\n override render() {\n const model = this._model;\n const layout = model.layout;\n const single = layout === 'single';\n const title = model.title;\n\n const gridStyle =\n layout === 'grid'\n ? styleMap({\n gridTemplateColumns: `repeat(${this._gridColumns}, minmax(${MIN_KPI_CELL_WIDTH}px, 1fr))`,\n })\n : nothing;\n\n return html`\n <div class=\"kpi-root\" data-layout=${layout} data-single=${single ? 'true' : 'false'}>\n ${title ? html`<div class=\"kpi-section-title\">${title}</div>` : nothing}\n ${layout === 'empty'\n ? html`<div class=\"empty\">No KPI data</div>`\n : layout === 'advanced'\n ? this.renderAdvanced()\n : html`\n <div\n class=\"kpi-grid\"\n data-layout=${single ? 'single' : 'grid'}\n data-scroll=${this._scrollAxis}\n style=${gridStyle}\n >\n ${model.cards.map((card) =>\n this.renderCard(card, single ? 'single' : 'grid'),\n )}\n </div>\n `}\n ${this.renderFooter()}\n ${this.renderShellLabelTooltip()}\n </div>\n `;\n }\n}\n\nexport function registerKpiWidget(): void {\n if (!customElements.get('ui9000-kpi-widget')) {\n customElements.define('ui9000-kpi-widget', Ui9000KpiWidget);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ui9000-kpi-widget': Ui9000KpiWidget;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { Ui9000KpiWidget, registerKpiWidget } from './element/ui9000-kpi-widget.js';
|
|
2
|
-
export { normalizeKpiData, extractCardsFromKpi, formatKpiValue, getKpiGridColumns, getKpiGridScrollAxis, MIN_KPI_CELL_WIDTH, type KpiCardModel, type KpiGridScrollAxis, type KpiValueLabel, type KpiWidgetData, type KpiWidgetModel, type RawKpiItem, } from './lib/index.js';
|
|
2
|
+
export { normalizeKpiData, extractCardsFromKpi, isAdvancedKpiItem, formatKpiValue, splitFormattedKpiValue, getKpiGridColumns, getKpiGridScrollAxis, getAdvancedSplitLayout, MIN_KPI_CELL_WIDTH, type AdvancedSplitLayout, type KpiCardModel, type KpiGridScrollAxis, type KpiStatusBadge, type KpiValueLabel, type KpiWidgetData, type KpiWidgetLayout, type KpiWidgetModel, type RawKpiItem, } from './lib/index.js';
|
|
3
3
|
export { default as kpiWidgetMetadata } from './metadata.json';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/kpi-widget/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACpF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/kpi-widget/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACpF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { registerKpiWidget } from "./element/ui9000-kpi-widget.js";
|
|
2
2
|
import { Ui9000KpiWidget } from "./element/ui9000-kpi-widget.js";
|
|
3
3
|
import { default as default2 } from "./metadata.json.js";
|
|
4
|
-
import { MIN_KPI_CELL_WIDTH, getKpiGridColumns, getKpiGridScrollAxis } from "./lib/grid.js";
|
|
5
|
-
import { extractCardsFromKpi } from "./lib/extract.js";
|
|
6
|
-
import { formatKpiValue } from "./lib/format.js";
|
|
4
|
+
import { MIN_KPI_CELL_WIDTH, getAdvancedSplitLayout, getKpiGridColumns, getKpiGridScrollAxis } from "./lib/grid.js";
|
|
5
|
+
import { extractCardsFromKpi, isAdvancedKpiItem } from "./lib/extract.js";
|
|
6
|
+
import { formatKpiValue, splitFormattedKpiValue } from "./lib/format.js";
|
|
7
7
|
import { normalizeKpiData } from "./lib/normalize.js";
|
|
8
8
|
export {
|
|
9
9
|
MIN_KPI_CELL_WIDTH,
|
|
10
10
|
Ui9000KpiWidget,
|
|
11
11
|
extractCardsFromKpi,
|
|
12
12
|
formatKpiValue,
|
|
13
|
+
getAdvancedSplitLayout,
|
|
13
14
|
getKpiGridColumns,
|
|
14
15
|
getKpiGridScrollAxis,
|
|
16
|
+
isAdvancedKpiItem,
|
|
15
17
|
default2 as kpiWidgetMetadata,
|
|
16
18
|
normalizeKpiData,
|
|
17
|
-
registerKpiWidget
|
|
19
|
+
registerKpiWidget,
|
|
20
|
+
splitFormattedKpiValue
|
|
18
21
|
};
|
|
19
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { KpiCardModel, RawKpiItem } from './types.js';
|
|
1
|
+
import { KpiCardModel, RawKpiItem, RawKpiVisualisation } from './types.js';
|
|
2
|
+
export declare function isAdvancedKpiItem(kpi: RawKpiItem): boolean;
|
|
3
|
+
export declare function visualisationChartType(kpi: {
|
|
4
|
+
visualisation?: RawKpiVisualisation | null;
|
|
5
|
+
}): string | undefined;
|
|
2
6
|
/** Map one FuseDash / MCP KPI item to display cards (mirrors KPIComponentWrapper). */
|
|
3
7
|
export declare function extractCardsFromKpi(kpi: RawKpiItem): KpiCardModel[];
|
|
4
8
|
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../../src/components/kpi-widget/lib/extract.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../../src/components/kpi-widget/lib/extract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EAIZ,UAAU,EACV,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAuEpB,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAM1D;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE;IAC1C,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAC5C,GAAG,MAAM,GAAG,SAAS,CAKrB;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,YAAY,EAAE,CAyGnE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { formatKpiValue } from "./format.js";
|
|
1
|
+
import { formatKpiValue, splitFormattedKpiValue } from "./format.js";
|
|
2
|
+
const HIGH_LOW_TYPES = /* @__PURE__ */ new Set(["high/low", "high/low_overall", "high/low_trend"]);
|
|
2
3
|
function valueLabel(axisInfo) {
|
|
3
4
|
if (!axisInfo || typeof axisInfo !== "object") return void 0;
|
|
4
5
|
const info = axisInfo;
|
|
@@ -10,6 +11,51 @@ function valueLabel(axisInfo) {
|
|
|
10
11
|
function asRecord(value) {
|
|
11
12
|
return value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
12
13
|
}
|
|
14
|
+
function formatDisplay(raw) {
|
|
15
|
+
const formatted = formatKpiValue(raw);
|
|
16
|
+
const split = splitFormattedKpiValue(formatted);
|
|
17
|
+
return split.suffix ? split : { value: formatted };
|
|
18
|
+
}
|
|
19
|
+
function percentLabel(raw) {
|
|
20
|
+
const n = Number(raw);
|
|
21
|
+
if (!Number.isFinite(n) || n === 0) return void 0;
|
|
22
|
+
return `${n.toFixed(2)}%`;
|
|
23
|
+
}
|
|
24
|
+
function statusFromKpi(kpi) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
if (kpi.kpiKind === "mission") {
|
|
27
|
+
return { text: "Mission", variant: "Mission" };
|
|
28
|
+
}
|
|
29
|
+
if (kpi.kpiKind === "goal") {
|
|
30
|
+
return kpi.targetStatus === "on_target" ? { text: "On target", variant: "OnTarget" } : { text: "Off target", variant: "OffTarget" };
|
|
31
|
+
}
|
|
32
|
+
const raw = (_a = kpi.status) == null ? void 0 : _a.trim();
|
|
33
|
+
if (!raw) return void 0;
|
|
34
|
+
const text = ((_b = raw.split("=")[0]) == null ? void 0 : _b.trim()) || raw;
|
|
35
|
+
const lower = raw.toLowerCase();
|
|
36
|
+
const variant = lower.includes("good") ? "Good" : lower.includes("low") ? "Low" : "Normal";
|
|
37
|
+
return { text, variant };
|
|
38
|
+
}
|
|
39
|
+
function withMeta(card, kpi) {
|
|
40
|
+
const scoreValue = kpi.kpiKind && typeof kpi.score === "number" ? `${kpi.score.toFixed(1)}%` : void 0;
|
|
41
|
+
return {
|
|
42
|
+
...card,
|
|
43
|
+
...scoreValue ? { value: scoreValue, suffix: void 0 } : {},
|
|
44
|
+
status: statusFromKpi(kpi),
|
|
45
|
+
showPercentage: Boolean(kpi.showPercentage)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function isAdvancedKpiItem(kpi) {
|
|
49
|
+
return Boolean(
|
|
50
|
+
kpi.supportingKpis || kpi.showVisualisation || kpi.variant === "advanced" && kpi.visualisation
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function visualisationChartType(kpi) {
|
|
54
|
+
const viz = kpi.visualisation;
|
|
55
|
+
if (!viz || typeof viz !== "object") return void 0;
|
|
56
|
+
const type = viz.chartType ?? viz.chart_type;
|
|
57
|
+
return typeof type === "string" && type.trim() ? type.trim() : void 0;
|
|
58
|
+
}
|
|
13
59
|
function extractCardsFromKpi(kpi) {
|
|
14
60
|
var _a;
|
|
15
61
|
const column = kpi.column ?? "value";
|
|
@@ -22,41 +68,62 @@ function extractCardsFromKpi(kpi) {
|
|
|
22
68
|
const type = kpi.type ?? "single_value";
|
|
23
69
|
const baseId = kpi.id ?? kpi.name ?? column;
|
|
24
70
|
const baseName = kpi.name ?? column;
|
|
71
|
+
const showPct = Boolean(kpi.showPercentage);
|
|
25
72
|
if (type === "trend" || type === "overall") {
|
|
26
|
-
const
|
|
73
|
+
const display = formatDisplay(dataRow[fieldKey] ?? dataRow[column]);
|
|
74
|
+
const subtitle = typeof dataRow.subtitle === "string" ? dataRow.subtitle : void 0;
|
|
27
75
|
return [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
withMeta(
|
|
77
|
+
{
|
|
78
|
+
id: String(baseId),
|
|
79
|
+
name: baseName,
|
|
80
|
+
value: display.value,
|
|
81
|
+
suffix: display.suffix,
|
|
82
|
+
indicator: showPct ? percentLabel(dataRow.percentage) : void 0,
|
|
83
|
+
subtitle,
|
|
84
|
+
label
|
|
85
|
+
},
|
|
86
|
+
kpi
|
|
87
|
+
)
|
|
35
88
|
];
|
|
36
89
|
}
|
|
37
90
|
if (type === "comparison") {
|
|
38
91
|
const current = asRecord(dataRow.current);
|
|
39
|
-
const
|
|
92
|
+
const previous = asRecord(dataRow.previous);
|
|
93
|
+
const display = formatDisplay((current == null ? void 0 : current[fieldKey]) ?? (current == null ? void 0 : current[column]));
|
|
94
|
+
const prevRaw = (previous == null ? void 0 : previous[fieldKey]) ?? (previous == null ? void 0 : previous[column]);
|
|
95
|
+
const subtitle = prevRaw != null && prevRaw !== "" ? `vs ${formatKpiValue(prevRaw)}` : void 0;
|
|
40
96
|
return [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
97
|
+
withMeta(
|
|
98
|
+
{
|
|
99
|
+
id: String(baseId),
|
|
100
|
+
name: baseName,
|
|
101
|
+
value: display.value,
|
|
102
|
+
suffix: display.suffix,
|
|
103
|
+
indicator: showPct ? percentLabel(dataRow.percentage) : void 0,
|
|
104
|
+
subtitle,
|
|
105
|
+
label
|
|
106
|
+
},
|
|
107
|
+
kpi
|
|
108
|
+
)
|
|
48
109
|
];
|
|
49
110
|
}
|
|
50
111
|
if (type === "single_value") {
|
|
51
112
|
const valueBucket = asRecord(dataRow.value) ?? dataRow;
|
|
52
113
|
const raw = valueBucket[fieldKey] ?? valueBucket[column] ?? Object.values(valueBucket)[0];
|
|
114
|
+
const display = formatDisplay(raw);
|
|
53
115
|
return [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
116
|
+
withMeta(
|
|
117
|
+
{
|
|
118
|
+
id: String(baseId),
|
|
119
|
+
name: baseName,
|
|
120
|
+
value: display.value,
|
|
121
|
+
suffix: display.suffix,
|
|
122
|
+
indicator: showPct ? percentLabel(dataRow.percentage) : void 0,
|
|
123
|
+
label
|
|
124
|
+
},
|
|
125
|
+
kpi
|
|
126
|
+
)
|
|
60
127
|
];
|
|
61
128
|
}
|
|
62
129
|
const cards = [];
|
|
@@ -64,20 +131,29 @@ function extractCardsFromKpi(kpi) {
|
|
|
64
131
|
if (key === "percentage" || key === "value") continue;
|
|
65
132
|
const local = asRecord(dataRow[key]);
|
|
66
133
|
if (!local) continue;
|
|
67
|
-
const subtitle = kpi.groupBy ? String(local[kpi.groupBy] ?? key) : key;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
134
|
+
const subtitle = kpi.groupBy ? String(local[kpi.groupBy] ?? key) : HIGH_LOW_TYPES.has(type) ? key : void 0;
|
|
135
|
+
const raw = local[fieldKey] ?? local[column];
|
|
136
|
+
const display = formatDisplay(raw);
|
|
137
|
+
cards.push(
|
|
138
|
+
withMeta(
|
|
139
|
+
{
|
|
140
|
+
id: `${baseId}-${key}`,
|
|
141
|
+
name: baseName,
|
|
142
|
+
value: display.value,
|
|
143
|
+
suffix: display.suffix,
|
|
144
|
+
indicator: showPct ? percentLabel(local.percentage) : void 0,
|
|
145
|
+
subtitle,
|
|
146
|
+
label
|
|
147
|
+
},
|
|
148
|
+
kpi
|
|
149
|
+
)
|
|
150
|
+
);
|
|
77
151
|
}
|
|
78
152
|
return cards;
|
|
79
153
|
}
|
|
80
154
|
export {
|
|
81
|
-
extractCardsFromKpi
|
|
155
|
+
extractCardsFromKpi,
|
|
156
|
+
isAdvancedKpiItem,
|
|
157
|
+
visualisationChartType
|
|
82
158
|
};
|
|
83
159
|
//# sourceMappingURL=extract.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.js","sources":["../../../../src/components/kpi-widget/lib/extract.ts"],"sourcesContent":["import { formatKpiValue } from './format.js';\nimport type { KpiCardModel, KpiValueLabel, RawKpiItem } from './types.js';\n\ntype AxisInfo = NonNullable<RawKpiItem['axisDetails']>[string];\n\nfunction valueLabel(axisInfo: AxisInfo | undefined): KpiValueLabel | undefined {\n if (!axisInfo || typeof axisInfo !== 'object') return undefined;\n const info = axisInfo;\n return {\n position: info.measure_unit_type === 'currency' ? 'left' : 'right',\n text:\n info.measure_unit_type === 'currency'\n ? (info.measure_unit_symbol ?? '')\n : (info.measure_unit ?? ''),\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value != null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\n/** Map one FuseDash / MCP KPI item to display cards (mirrors KPIComponentWrapper). */\nexport function extractCardsFromKpi(kpi: RawKpiItem): KpiCardModel[] {\n const column = kpi.column ?? 'value';\n const aggregation = kpi.aggregations ?? 'sum';\n const fieldKey = `${aggregation}_${column}`;\n const dataRow = Array.isArray(kpi.data) ? asRecord(kpi.data[0]) : null;\n if (!dataRow) return [];\n\n const axisInfo = kpi.axisDetails?.[column];\n const label = valueLabel(axisInfo);\n const type = kpi.type ?? 'single_value';\n const baseId = kpi.id ?? kpi.name ?? column;\n const baseName = kpi.name ?? column;\n\n if (type === 'trend' || type === 'overall') {\n const pct = dataRow.percentage;\n return [\n {\n id: baseId,\n name: baseName,\n value: formatKpiValue(dataRow[fieldKey] as number | string),\n indicator: Number(pct) ? `${Number(pct).toFixed(2)}%` : undefined,\n label,\n },\n ];\n }\n\n if (type === 'comparison') {\n const current = asRecord(dataRow.current);\n const pct = dataRow.percentage;\n return [\n {\n id: baseId,\n name: baseName,\n value: formatKpiValue(current?.[fieldKey] as number | string),\n indicator: Number(pct) ? `${Number(pct).toFixed(2)}%` : undefined,\n label,\n },\n ];\n }\n\n if (type === 'single_value') {\n const valueBucket = asRecord(dataRow.value) ?? dataRow;\n const raw =\n valueBucket[fieldKey] ??\n valueBucket[column] ??\n Object.values(valueBucket)[0];\n return [\n {\n id: baseId,\n name: baseName,\n value: formatKpiValue(raw as number | string),\n label,\n },\n ];\n }\n\n const cards: KpiCardModel[] = [];\n for (const key of Object.keys(dataRow)) {\n if (key === 'percentage' || key === 'value') continue;\n const local = asRecord(dataRow[key]);\n if (!local) continue;\n const subtitle = kpi.groupBy ? String(local[kpi.groupBy] ?? key) : key;\n const indicator = Number(local.percentage)\n ? `${Number(local.percentage).toFixed(2)}%`\n : undefined;\n cards.push({\n id: `${baseId}-${key}`,\n name: baseName,\n value: formatKpiValue(local[fieldKey] as number | string),\n subtitle,\n indicator,\n label,\n });\n }\n\n return cards;\n}\n"],"names":[],"mappings":";AAKA,SAAS,WAAW,UAA2D;AAC7E,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,QAAM,OAAO;AACb,SAAO;AAAA,IACL,UAAU,KAAK,sBAAsB,aAAa,SAAS;AAAA,IAC3D,MACE,KAAK,sBAAsB,aACtB,KAAK,uBAAuB,KAC5B,KAAK,gBAAgB;AAAA,EAAA;AAEhC;AAEA,SAAS,SAAS,OAAgD;AAChE,SAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACpE,QACD;AACN;AAGO,SAAS,oBAAoB,KAAiC;;AACnE,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAM,cAAc,IAAI,gBAAgB;AACxC,QAAM,WAAW,GAAG,WAAW,IAAI,MAAM;AACzC,QAAM,UAAU,MAAM,QAAQ,IAAI,IAAI,IAAI,SAAS,IAAI,KAAK,CAAC,CAAC,IAAI;AAClE,MAAI,CAAC,QAAS,QAAO,CAAA;AAErB,QAAM,YAAW,SAAI,gBAAJ,mBAAkB;AACnC,QAAM,QAAQ,WAAW,QAAQ;AACjC,QAAM,OAAO,IAAI,QAAQ;AACzB,QAAM,SAAS,IAAI,MAAM,IAAI,QAAQ;AACrC,QAAM,WAAW,IAAI,QAAQ;AAE7B,MAAI,SAAS,WAAW,SAAS,WAAW;AAC1C,UAAM,MAAM,QAAQ;AACpB,WAAO;AAAA,MACL;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,eAAe,QAAQ,QAAQ,CAAoB;AAAA,QAC1D,WAAW,OAAO,GAAG,IAAI,GAAG,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,MAAM;AAAA,QACxD;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,SAAS,cAAc;AACzB,UAAM,UAAU,SAAS,QAAQ,OAAO;AACxC,UAAM,MAAM,QAAQ;AACpB,WAAO;AAAA,MACL;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,eAAe,mCAAU,SAA4B;AAAA,QAC5D,WAAW,OAAO,GAAG,IAAI,GAAG,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,MAAM;AAAA,QACxD;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,SAAS,gBAAgB;AAC3B,UAAM,cAAc,SAAS,QAAQ,KAAK,KAAK;AAC/C,UAAM,MACJ,YAAY,QAAQ,KACpB,YAAY,MAAM,KAClB,OAAO,OAAO,WAAW,EAAE,CAAC;AAC9B,WAAO;AAAA,MACL;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,eAAe,GAAsB;AAAA,QAC5C;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,QAAM,QAAwB,CAAA;AAC9B,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,QAAQ,gBAAgB,QAAQ,QAAS;AAC7C,UAAM,QAAQ,SAAS,QAAQ,GAAG,CAAC;AACnC,QAAI,CAAC,MAAO;AACZ,UAAM,WAAW,IAAI,UAAU,OAAO,MAAM,IAAI,OAAO,KAAK,GAAG,IAAI;AACnE,UAAM,YAAY,OAAO,MAAM,UAAU,IACrC,GAAG,OAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,CAAC,MACtC;AACJ,UAAM,KAAK;AAAA,MACT,IAAI,GAAG,MAAM,IAAI,GAAG;AAAA,MACpB,MAAM;AAAA,MACN,OAAO,eAAe,MAAM,QAAQ,CAAoB;AAAA,MACxD;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAEA,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"extract.js","sources":["../../../../src/components/kpi-widget/lib/extract.ts"],"sourcesContent":["import {\n formatKpiValue,\n splitFormattedKpiValue,\n} from './format.js';\nimport type {\n KpiCardModel,\n KpiStatusBadge,\n KpiStatusVariant,\n KpiValueLabel,\n RawKpiItem,\n RawKpiVisualisation,\n} from './types.js';\n\ntype AxisInfo = NonNullable<RawKpiItem['axisDetails']>[string];\n\nconst HIGH_LOW_TYPES = new Set(['high/low', 'high/low_overall', 'high/low_trend']);\n\nfunction valueLabel(axisInfo: AxisInfo | undefined): KpiValueLabel | undefined {\n if (!axisInfo || typeof axisInfo !== 'object') return undefined;\n const info = axisInfo;\n return {\n position: info.measure_unit_type === 'currency' ? 'left' : 'right',\n text:\n info.measure_unit_type === 'currency'\n ? (info.measure_unit_symbol ?? '')\n : (info.measure_unit ?? ''),\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value != null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction formatDisplay(raw: unknown): { value: string; suffix?: string } {\n const formatted = formatKpiValue(raw as number | string);\n const split = splitFormattedKpiValue(formatted);\n return split.suffix ? split : { value: formatted };\n}\n\nfunction percentLabel(raw: unknown): string | undefined {\n const n = Number(raw);\n if (!Number.isFinite(n) || n === 0) return undefined;\n return `${n.toFixed(2)}%`;\n}\n\nfunction statusFromKpi(kpi: RawKpiItem): KpiStatusBadge | undefined {\n if (kpi.kpiKind === 'mission') {\n return { text: 'Mission', variant: 'Mission' };\n }\n if (kpi.kpiKind === 'goal') {\n return kpi.targetStatus === 'on_target'\n ? { text: 'On target', variant: 'OnTarget' }\n : { text: 'Off target', variant: 'OffTarget' };\n }\n const raw = kpi.status?.trim();\n if (!raw) return undefined;\n const text = raw.split('=')[0]?.trim() || raw;\n const lower = raw.toLowerCase();\n const variant: KpiStatusVariant = lower.includes('good')\n ? 'Good'\n : lower.includes('low')\n ? 'Low'\n : 'Normal';\n return { text, variant };\n}\n\nfunction withMeta(\n card: Omit<KpiCardModel, 'status' | 'showPercentage'>,\n kpi: RawKpiItem,\n): KpiCardModel {\n const scoreValue =\n kpi.kpiKind && typeof kpi.score === 'number' ? `${kpi.score.toFixed(1)}%` : undefined;\n return {\n ...card,\n ...(scoreValue ? { value: scoreValue, suffix: undefined } : {}),\n status: statusFromKpi(kpi),\n showPercentage: Boolean(kpi.showPercentage),\n };\n}\n\nexport function isAdvancedKpiItem(kpi: RawKpiItem): boolean {\n return Boolean(\n kpi.supportingKpis ||\n kpi.showVisualisation ||\n (kpi.variant === 'advanced' && kpi.visualisation),\n );\n}\n\nexport function visualisationChartType(kpi: {\n visualisation?: RawKpiVisualisation | null;\n}): string | undefined {\n const viz = kpi.visualisation;\n if (!viz || typeof viz !== 'object') return undefined;\n const type = viz.chartType ?? viz.chart_type;\n return typeof type === 'string' && type.trim() ? type.trim() : undefined;\n}\n\n/** Map one FuseDash / MCP KPI item to display cards (mirrors KPIComponentWrapper). */\nexport function extractCardsFromKpi(kpi: RawKpiItem): KpiCardModel[] {\n const column = kpi.column ?? 'value';\n const aggregation = kpi.aggregations ?? 'sum';\n const fieldKey = `${aggregation}_${column}`;\n const dataRow = Array.isArray(kpi.data) ? asRecord(kpi.data[0]) : null;\n if (!dataRow) return [];\n\n const axisInfo = kpi.axisDetails?.[column];\n const label = valueLabel(axisInfo);\n const type = kpi.type ?? 'single_value';\n const baseId = kpi.id ?? kpi.name ?? column;\n const baseName = kpi.name ?? column;\n const showPct = Boolean(kpi.showPercentage);\n\n if (type === 'trend' || type === 'overall') {\n const display = formatDisplay(dataRow[fieldKey] ?? dataRow[column]);\n const subtitle = typeof dataRow.subtitle === 'string' ? dataRow.subtitle : undefined;\n return [\n withMeta(\n {\n id: String(baseId),\n name: baseName,\n value: display.value,\n suffix: display.suffix,\n indicator: showPct ? percentLabel(dataRow.percentage) : undefined,\n subtitle,\n label,\n },\n kpi,\n ),\n ];\n }\n\n if (type === 'comparison') {\n const current = asRecord(dataRow.current);\n const previous = asRecord(dataRow.previous);\n const display = formatDisplay(current?.[fieldKey] ?? current?.[column]);\n const prevRaw = previous?.[fieldKey] ?? previous?.[column];\n const subtitle =\n prevRaw != null && prevRaw !== '' ? `vs ${formatKpiValue(prevRaw as number | string)}` : undefined;\n return [\n withMeta(\n {\n id: String(baseId),\n name: baseName,\n value: display.value,\n suffix: display.suffix,\n indicator: showPct ? percentLabel(dataRow.percentage) : undefined,\n subtitle,\n label,\n },\n kpi,\n ),\n ];\n }\n\n if (type === 'single_value') {\n const valueBucket = asRecord(dataRow.value) ?? dataRow;\n const raw =\n valueBucket[fieldKey] ?? valueBucket[column] ?? Object.values(valueBucket)[0];\n const display = formatDisplay(raw);\n return [\n withMeta(\n {\n id: String(baseId),\n name: baseName,\n value: display.value,\n suffix: display.suffix,\n indicator: showPct ? percentLabel(dataRow.percentage) : undefined,\n label,\n },\n kpi,\n ),\n ];\n }\n\n const cards: KpiCardModel[] = [];\n for (const key of Object.keys(dataRow)) {\n if (key === 'percentage' || key === 'value') continue;\n const local = asRecord(dataRow[key]);\n if (!local) continue;\n const subtitle = kpi.groupBy\n ? String(local[kpi.groupBy] ?? key)\n : HIGH_LOW_TYPES.has(type)\n ? key\n : undefined;\n const raw = local[fieldKey] ?? local[column];\n const display = formatDisplay(raw);\n cards.push(\n withMeta(\n {\n id: `${baseId}-${key}`,\n name: baseName,\n value: display.value,\n suffix: display.suffix,\n indicator: showPct ? percentLabel(local.percentage) : undefined,\n subtitle,\n label,\n },\n kpi,\n ),\n );\n }\n\n return cards;\n}\n"],"names":[],"mappings":";AAeA,MAAM,iBAAiB,oBAAI,IAAI,CAAC,YAAY,oBAAoB,gBAAgB,CAAC;AAEjF,SAAS,WAAW,UAA2D;AAC7E,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,QAAM,OAAO;AACb,SAAO;AAAA,IACL,UAAU,KAAK,sBAAsB,aAAa,SAAS;AAAA,IAC3D,MACE,KAAK,sBAAsB,aACtB,KAAK,uBAAuB,KAC5B,KAAK,gBAAgB;AAAA,EAAA;AAEhC;AAEA,SAAS,SAAS,OAAgD;AAChE,SAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACpE,QACD;AACN;AAEA,SAAS,cAAc,KAAkD;AACvE,QAAM,YAAY,eAAe,GAAsB;AACvD,QAAM,QAAQ,uBAAuB,SAAS;AAC9C,SAAO,MAAM,SAAS,QAAQ,EAAE,OAAO,UAAA;AACzC;AAEA,SAAS,aAAa,KAAkC;AACtD,QAAM,IAAI,OAAO,GAAG;AACpB,MAAI,CAAC,OAAO,SAAS,CAAC,KAAK,MAAM,EAAG,QAAO;AAC3C,SAAO,GAAG,EAAE,QAAQ,CAAC,CAAC;AACxB;AAEA,SAAS,cAAc,KAA6C;;AAClE,MAAI,IAAI,YAAY,WAAW;AAC7B,WAAO,EAAE,MAAM,WAAW,SAAS,UAAA;AAAA,EACrC;AACA,MAAI,IAAI,YAAY,QAAQ;AAC1B,WAAO,IAAI,iBAAiB,cACxB,EAAE,MAAM,aAAa,SAAS,WAAA,IAC9B,EAAE,MAAM,cAAc,SAAS,YAAA;AAAA,EACrC;AACA,QAAM,OAAM,SAAI,WAAJ,mBAAY;AACxB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAO,SAAI,MAAM,GAAG,EAAE,CAAC,MAAhB,mBAAmB,WAAU;AAC1C,QAAM,QAAQ,IAAI,YAAA;AAClB,QAAM,UAA4B,MAAM,SAAS,MAAM,IACnD,SACA,MAAM,SAAS,KAAK,IAClB,QACA;AACN,SAAO,EAAE,MAAM,QAAA;AACjB;AAEA,SAAS,SACP,MACA,KACc;AACd,QAAM,aACJ,IAAI,WAAW,OAAO,IAAI,UAAU,WAAW,GAAG,IAAI,MAAM,QAAQ,CAAC,CAAC,MAAM;AAC9E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,aAAa,EAAE,OAAO,YAAY,QAAQ,OAAA,IAAc,CAAA;AAAA,IAC5D,QAAQ,cAAc,GAAG;AAAA,IACzB,gBAAgB,QAAQ,IAAI,cAAc;AAAA,EAAA;AAE9C;AAEO,SAAS,kBAAkB,KAA0B;AAC1D,SAAO;AAAA,IACL,IAAI,kBACF,IAAI,qBACH,IAAI,YAAY,cAAc,IAAI;AAAA,EAAA;AAEzC;AAEO,SAAS,uBAAuB,KAEhB;AACrB,QAAM,MAAM,IAAI;AAChB,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,OAAO,IAAI,aAAa,IAAI;AAClC,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS,KAAK,SAAS;AACjE;AAGO,SAAS,oBAAoB,KAAiC;;AACnE,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAM,cAAc,IAAI,gBAAgB;AACxC,QAAM,WAAW,GAAG,WAAW,IAAI,MAAM;AACzC,QAAM,UAAU,MAAM,QAAQ,IAAI,IAAI,IAAI,SAAS,IAAI,KAAK,CAAC,CAAC,IAAI;AAClE,MAAI,CAAC,QAAS,QAAO,CAAA;AAErB,QAAM,YAAW,SAAI,gBAAJ,mBAAkB;AACnC,QAAM,QAAQ,WAAW,QAAQ;AACjC,QAAM,OAAO,IAAI,QAAQ;AACzB,QAAM,SAAS,IAAI,MAAM,IAAI,QAAQ;AACrC,QAAM,WAAW,IAAI,QAAQ;AAC7B,QAAM,UAAU,QAAQ,IAAI,cAAc;AAE1C,MAAI,SAAS,WAAW,SAAS,WAAW;AAC1C,UAAM,UAAU,cAAc,QAAQ,QAAQ,KAAK,QAAQ,MAAM,CAAC;AAClE,UAAM,WAAW,OAAO,QAAQ,aAAa,WAAW,QAAQ,WAAW;AAC3E,WAAO;AAAA,MACL;AAAA,QACE;AAAA,UACE,IAAI,OAAO,MAAM;AAAA,UACjB,MAAM;AAAA,UACN,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,WAAW,UAAU,aAAa,QAAQ,UAAU,IAAI;AAAA,UACxD;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,SAAS,cAAc;AACzB,UAAM,UAAU,SAAS,QAAQ,OAAO;AACxC,UAAM,WAAW,SAAS,QAAQ,QAAQ;AAC1C,UAAM,UAAU,eAAc,mCAAU,eAAa,mCAAU,QAAO;AACtE,UAAM,WAAU,qCAAW,eAAa,qCAAW;AACnD,UAAM,WACJ,WAAW,QAAQ,YAAY,KAAK,MAAM,eAAe,OAA0B,CAAC,KAAK;AAC3F,WAAO;AAAA,MACL;AAAA,QACE;AAAA,UACE,IAAI,OAAO,MAAM;AAAA,UACjB,MAAM;AAAA,UACN,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,WAAW,UAAU,aAAa,QAAQ,UAAU,IAAI;AAAA,UACxD;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,SAAS,gBAAgB;AAC3B,UAAM,cAAc,SAAS,QAAQ,KAAK,KAAK;AAC/C,UAAM,MACJ,YAAY,QAAQ,KAAK,YAAY,MAAM,KAAK,OAAO,OAAO,WAAW,EAAE,CAAC;AAC9E,UAAM,UAAU,cAAc,GAAG;AACjC,WAAO;AAAA,MACL;AAAA,QACE;AAAA,UACE,IAAI,OAAO,MAAM;AAAA,UACjB,MAAM;AAAA,UACN,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,WAAW,UAAU,aAAa,QAAQ,UAAU,IAAI;AAAA,UACxD;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,QAAM,QAAwB,CAAA;AAC9B,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,QAAQ,gBAAgB,QAAQ,QAAS;AAC7C,UAAM,QAAQ,SAAS,QAAQ,GAAG,CAAC;AACnC,QAAI,CAAC,MAAO;AACZ,UAAM,WAAW,IAAI,UACjB,OAAO,MAAM,IAAI,OAAO,KAAK,GAAG,IAChC,eAAe,IAAI,IAAI,IACrB,MACA;AACN,UAAM,MAAM,MAAM,QAAQ,KAAK,MAAM,MAAM;AAC3C,UAAM,UAAU,cAAc,GAAG;AACjC,UAAM;AAAA,MACJ;AAAA,QACE;AAAA,UACE,IAAI,GAAG,MAAM,IAAI,GAAG;AAAA,UACpB,MAAM;AAAA,UACN,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,WAAW,UAAU,aAAa,MAAM,UAAU,IAAI;AAAA,UACtD;AAAA,UACA;AAAA,QAAA;AAAA,QAEF;AAAA,MAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SAAO;AACT;"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
/** Port of FuseDash `formatKpiValue` (client/libs/shared/utils/kpi.ts). */
|
|
2
2
|
export declare function formatKpiValue(value: number | string, decimals?: number): string;
|
|
3
|
+
/** Split compact scientific notation so the exponent can render as a suffix. */
|
|
4
|
+
export declare function splitFormattedKpiValue(formatted: string): {
|
|
5
|
+
value: string;
|
|
6
|
+
suffix: string;
|
|
7
|
+
};
|
|
3
8
|
//# sourceMappingURL=format.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/components/kpi-widget/lib/format.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CA4B3E"}
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../src/components/kpi-widget/lib/format.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CA4B3E;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAOA"}
|
|
@@ -18,7 +18,16 @@ function formatKpiValue(value, decimals = 2) {
|
|
|
18
18
|
const formattedModifiedValue = parseFloat(modifiedValue.toFixed(decimals)).toString();
|
|
19
19
|
return `${formattedModifiedValue}×10⁻${power}`;
|
|
20
20
|
}
|
|
21
|
+
function splitFormattedKpiValue(formatted) {
|
|
22
|
+
if (!formatted.includes("×10⁻")) return { value: formatted, suffix: "" };
|
|
23
|
+
const [valuePart, suffixPart] = formatted.split("×10⁻");
|
|
24
|
+
return {
|
|
25
|
+
value: valuePart ?? formatted,
|
|
26
|
+
suffix: suffixPart != null ? `×10⁻${suffixPart}` : ""
|
|
27
|
+
};
|
|
28
|
+
}
|
|
21
29
|
export {
|
|
22
|
-
formatKpiValue
|
|
30
|
+
formatKpiValue,
|
|
31
|
+
splitFormattedKpiValue
|
|
23
32
|
};
|
|
24
33
|
//# sourceMappingURL=format.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sources":["../../../../src/components/kpi-widget/lib/format.ts"],"sourcesContent":["/** Port of FuseDash `formatKpiValue` (client/libs/shared/utils/kpi.ts). */\nexport function formatKpiValue(value: number | string, decimals = 2): string {\n const numericValue = Number(value);\n if (\n Number.isNaN(numericValue) ||\n numericValue === null ||\n !Number.isFinite(numericValue)\n ) {\n return '—';\n }\n\n const absValue = Math.abs(numericValue);\n\n if (absValue === 0) {\n return '0';\n }\n\n if (absValue >= 0.1) {\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: decimals,\n }).format(numericValue);\n }\n\n const power = Math.ceil(Math.abs(Math.log10(absValue) / 3)) * 3;\n const modifiedValue = numericValue * 10 ** power;\n const formattedModifiedValue = parseFloat(modifiedValue.toFixed(decimals)).toString();\n\n return `${formattedModifiedValue}×10⁻${power}`;\n}\n"],"names":[],"mappings":"AACO,SAAS,eAAe,OAAwB,WAAW,GAAW;AAC3E,QAAM,eAAe,OAAO,KAAK;AACjC,MACE,OAAO,MAAM,YAAY,KACzB,iBAAiB,QACjB,CAAC,OAAO,SAAS,YAAY,GAC7B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,KAAK,IAAI,YAAY;AAEtC,MAAI,aAAa,GAAG;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,KAAK;AACnB,WAAO,IAAI,KAAK,aAAa,SAAS;AAAA,MACpC,UAAU;AAAA,MACV,uBAAuB;AAAA,IAAA,CACxB,EAAE,OAAO,YAAY;AAAA,EACxB;AAEA,QAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,MAAM,QAAQ,IAAI,CAAC,CAAC,IAAI;AAC9D,QAAM,gBAAgB,eAAe,MAAM;AAC3C,QAAM,yBAAyB,WAAW,cAAc,QAAQ,QAAQ,CAAC,EAAE,SAAA;AAE3E,SAAO,GAAG,sBAAsB,OAAO,KAAK;AAC9C;"}
|
|
1
|
+
{"version":3,"file":"format.js","sources":["../../../../src/components/kpi-widget/lib/format.ts"],"sourcesContent":["/** Port of FuseDash `formatKpiValue` (client/libs/shared/utils/kpi.ts). */\nexport function formatKpiValue(value: number | string, decimals = 2): string {\n const numericValue = Number(value);\n if (\n Number.isNaN(numericValue) ||\n numericValue === null ||\n !Number.isFinite(numericValue)\n ) {\n return '—';\n }\n\n const absValue = Math.abs(numericValue);\n\n if (absValue === 0) {\n return '0';\n }\n\n if (absValue >= 0.1) {\n return new Intl.NumberFormat('en-US', {\n notation: 'compact',\n maximumFractionDigits: decimals,\n }).format(numericValue);\n }\n\n const power = Math.ceil(Math.abs(Math.log10(absValue) / 3)) * 3;\n const modifiedValue = numericValue * 10 ** power;\n const formattedModifiedValue = parseFloat(modifiedValue.toFixed(decimals)).toString();\n\n return `${formattedModifiedValue}×10⁻${power}`;\n}\n\n/** Split compact scientific notation so the exponent can render as a suffix. */\nexport function splitFormattedKpiValue(formatted: string): {\n value: string;\n suffix: string;\n} {\n if (!formatted.includes('×10⁻')) return { value: formatted, suffix: '' };\n const [valuePart, suffixPart] = formatted.split('×10⁻');\n return {\n value: valuePart ?? formatted,\n suffix: suffixPart != null ? `×10⁻${suffixPart}` : '',\n };\n}\n"],"names":[],"mappings":"AACO,SAAS,eAAe,OAAwB,WAAW,GAAW;AAC3E,QAAM,eAAe,OAAO,KAAK;AACjC,MACE,OAAO,MAAM,YAAY,KACzB,iBAAiB,QACjB,CAAC,OAAO,SAAS,YAAY,GAC7B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,KAAK,IAAI,YAAY;AAEtC,MAAI,aAAa,GAAG;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,KAAK;AACnB,WAAO,IAAI,KAAK,aAAa,SAAS;AAAA,MACpC,UAAU;AAAA,MACV,uBAAuB;AAAA,IAAA,CACxB,EAAE,OAAO,YAAY;AAAA,EACxB;AAEA,QAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,MAAM,QAAQ,IAAI,CAAC,CAAC,IAAI;AAC9D,QAAM,gBAAgB,eAAe,MAAM;AAC3C,QAAM,yBAAyB,WAAW,cAAc,QAAQ,QAAQ,CAAC,EAAE,SAAA;AAE3E,SAAO,GAAG,sBAAsB,OAAO,KAAK;AAC9C;AAGO,SAAS,uBAAuB,WAGrC;AACA,MAAI,CAAC,UAAU,SAAS,MAAM,UAAU,EAAE,OAAO,WAAW,QAAQ,GAAA;AACpE,QAAM,CAAC,WAAW,UAAU,IAAI,UAAU,MAAM,MAAM;AACtD,SAAO;AAAA,IACL,OAAO,aAAa;AAAA,IACpB,QAAQ,cAAc,OAAO,OAAO,UAAU,KAAK;AAAA,EAAA;AAEvD;"}
|