@aquera/nile-visualization 2.9.6 → 2.9.7

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.
@@ -47,14 +47,14 @@ export const styles = css `
47
47
  --nile-kpi-label-color: var(--nile-colors-neutral-700, var(--ng-colors-text-secondary-700));
48
48
  --nile-kpi-label-font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));
49
49
  --nile-kpi-label-font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-medium));
50
- --nile-kpi-value-font-size: clamp(1rem, 5cqi + 0.25rem, 36px);
50
+ --nile-kpi-value-font-size: clamp(20px, 9cqmin, 56px);
51
51
  --nile-kpi-value-color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));
52
52
  --nile-kpi-prefix-suffix-font-size: var(--nile-type-scale-6, var(--ng-font-size-text-xl));
53
53
  --nile-kpi-prefix-suffix-color: var(--nile-colors-neutral-700, var(--ng-colors-text-secondary-700));
54
54
  --nile-kpi-trend-up-color: var(--nile-colors-success-700, var(--ng-color-success-700));
55
55
  --nile-kpi-trend-down-color: var(--nile-colors-error-700, var(--ng-color-error-700));
56
56
  --nile-kpi-trend-neutral-color: var(--nile-colors-neutral-700, var(--ng-colors-text-secondary-700));
57
- --nile-kpi-description-font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
57
+ --nile-kpi-description-font-size: clamp(0.75rem, 1.2cqmin, 1rem);
58
58
  --nile-kpi-description-color: var(--nile-colors-neutral-700, var(--ng-colors-text-secondary-700));
59
59
  display: flex;
60
60
  flex-direction: column;
@@ -63,7 +63,8 @@ export const styles = css `
63
63
  position: relative;
64
64
  box-sizing: border-box;
65
65
  overflow: hidden;
66
- container-type: inline-size;
66
+ container-type: size;
67
+ container-name: kpi-card;
67
68
  }
68
69
 
69
70
  :host([hidden]) {
@@ -120,6 +121,118 @@ export const styles = css `
120
121
  overflow: hidden;
121
122
  }
122
123
 
124
+ /* ── Adaptive layout: classes set by render() based on what content exists.
125
+ .kpi--no-chart — no sparkline and no gauge
126
+ .kpi--no-desc — no description
127
+ Use both together for "value-only" centering. ── */
128
+ .kpi.kpi--no-chart {
129
+ display: flex;
130
+ flex-direction: column;
131
+ align-items: center;
132
+ }
133
+
134
+ /* When there is no chart, center value-row vertically in the leftover space
135
+ below the label, and push the description toward the bottom. With a chart
136
+ present, items stack from the top naturally (no auto margins). */
137
+ .kpi--no-chart .kpi-value-row {
138
+ margin-top: auto;
139
+ }
140
+
141
+ .kpi--no-chart .kpi-description {
142
+ margin-bottom: auto;
143
+ }
144
+
145
+ /* Reset any grid-only props inherited from the side-by-side rule. */
146
+ .kpi.kpi--no-chart .kpi-label,
147
+ .kpi.kpi--no-chart .kpi-value-row,
148
+ .kpi.kpi--no-chart .kpi-description {
149
+ grid-column: auto;
150
+ grid-row: auto;
151
+ }
152
+
153
+ .kpi--no-chart .kpi-label {
154
+ align-self: flex-start;
155
+ width: 100%;
156
+ }
157
+
158
+ .kpi--no-chart .kpi-value-row {
159
+ overflow: hidden;
160
+ flex-wrap: wrap;
161
+ row-gap: var(--nile-spacing-xs, var(--ng-spacing-xs));
162
+ justify-content: center;
163
+ align-self: stretch;
164
+ width: 100%;
165
+ max-width: 100%;
166
+ }
167
+
168
+ /* Center value-row vertically when there's nothing below it (no desc, no chart). */
169
+ .kpi--no-chart.kpi--no-desc .kpi-value-row {
170
+ margin-top: auto;
171
+ margin-bottom: auto;
172
+ }
173
+
174
+ /* Grow the value when there's room — no chart AND (no description OR no trend). */
175
+ .kpi--no-chart.kpi--no-desc,
176
+ .kpi--no-chart.kpi--no-trend {
177
+ --nile-kpi-value-font-size: clamp(20px, 9cqmin, 56px);
178
+ }
179
+
180
+ /* When trend is absent, the value sits alone in the row — make sure it
181
+ centers horizontally and the row sizes to content. */
182
+ .kpi--no-chart.kpi--no-trend .kpi-value-row {
183
+ justify-content: center;
184
+ }
185
+
186
+ .kpi--no-chart.kpi--no-trend .kpi-value {
187
+ text-align: center;
188
+ }
189
+
190
+ .kpi--no-chart .kpi-trend {
191
+ flex: 0 1 auto;
192
+ min-width: 0;
193
+ max-width: 100%;
194
+ overflow: hidden;
195
+ text-overflow: ellipsis;
196
+ white-space: nowrap;
197
+ }
198
+
199
+ /* Let the trend text size to its own content when there's no chart. The
200
+ default flex 1 1 0 (basis 0) gives the trend nearly zero natural width,
201
+ so the parent row gives it almost no space and the text clips. */
202
+ .kpi--no-chart .kpi-trend > span:last-child {
203
+ flex: 0 0 auto;
204
+ }
205
+
206
+ .kpi--no-chart:not(.kpi--no-desc) .kpi-description {
207
+ margin-bottom: auto;
208
+ align-self: center;
209
+ text-align: center;
210
+ }
211
+
212
+
213
+ /* When the card is narrow, the centered no-chart value can overflow both
214
+ edges (cut off on the left). Switch to left-aligned at narrow widths so
215
+ the value's left edge stays visible. */
216
+ @container (max-width: 160px) {
217
+ .kpi--no-chart {
218
+ align-items: stretch;
219
+ }
220
+
221
+ .kpi--no-chart .kpi-value-row {
222
+ justify-content: flex-start;
223
+ align-self: stretch;
224
+ width: 100%;
225
+ }
226
+
227
+ .kpi--no-chart.kpi--no-trend .kpi-value-row {
228
+ justify-content: flex-start;
229
+ }
230
+
231
+ .kpi--no-chart.kpi--no-trend .kpi-value {
232
+ text-align: left;
233
+ }
234
+ }
235
+
123
236
  .kpi-label {
124
237
  display: block;
125
238
  margin: 0;
@@ -141,11 +254,24 @@ export const styles = css `
141
254
  .kpi-value-row {
142
255
  display: flex;
143
256
  align-items: center;
257
+ justify-content: flex-start;
144
258
  gap: var(--nile-spacing-md, var(--ng-spacing-md));
145
259
  flex-wrap: nowrap;
260
+ width: 100%;
261
+ max-width: 100%;
146
262
  min-width: 0;
147
263
  overflow: hidden;
148
264
  flex-shrink: 0;
265
+ box-sizing: border-box;
266
+ }
267
+
268
+ /* When a chart is rendered, force value-row to the left of its column,
269
+ overriding any centering inherited from the .kpi--no-chart rules. */
270
+ .kpi:not(.kpi--no-chart) .kpi-value-row {
271
+ justify-content: flex-start;
272
+ align-self: stretch;
273
+ margin-left: 0;
274
+ margin-right: 0;
149
275
  }
150
276
 
151
277
  .kpi-value {
@@ -157,10 +283,9 @@ export const styles = css `
157
283
  line-height: 1.2;
158
284
  cursor: default;
159
285
  white-space: nowrap;
160
- min-width: 0;
161
- flex: 0 1 auto;
162
- overflow: hidden;
163
- text-overflow: ellipsis;
286
+ flex: 0 0 auto;
287
+ flex-shrink: 0;
288
+ overflow: visible;
164
289
  }
165
290
 
166
291
  .kpi-prefix,
@@ -172,7 +297,7 @@ export const styles = css `
172
297
  }
173
298
 
174
299
  .kpi-trend {
175
- display: inline-flex;
300
+ display: flex;
176
301
  align-items: center;
177
302
  gap: var(--nile-spacing-xs, var(--ng-spacing-xs));
178
303
  padding: var(--nile-spacing-xs, var(--ng-spacing-xs)) var(--nile-spacing-md, var(--ng-spacing-md));
@@ -181,10 +306,36 @@ export const styles = css `
181
306
  font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
182
307
  font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-medium));
183
308
  line-height: 1;
184
- flex-shrink: 1;
309
+ flex: 0 1 auto;
310
+ min-width: 0;
311
+ max-width: 100%;
312
+ overflow: hidden;
313
+ }
314
+
315
+ .kpi-trend-text {
185
316
  min-width: 0;
317
+ min-height: 0;
318
+ max-width: 100%;
186
319
  overflow: hidden;
320
+ text-overflow: ellipsis;
187
321
  white-space: nowrap;
322
+ box-sizing: border-box;
323
+ }
324
+
325
+ .kpi-trend > span:last-child {
326
+ flex: 1 1 0;
327
+ overflow: hidden;
328
+ text-overflow: ellipsis;
329
+ white-space: nowrap;
330
+ min-width: 0;
331
+ }
332
+
333
+ /* ── When no chart is rendered, the trend has room — don't truncate. ── */
334
+ .kpi--no-chart .kpi-trend,
335
+ .kpi--no-chart .kpi-trend > span:last-child {
336
+ overflow: hidden;
337
+ text-overflow: ellipsis;
338
+ flex-shrink: 0;
188
339
  }
189
340
 
190
341
  .kpi-trend--up {
@@ -215,7 +366,9 @@ export const styles = css `
215
366
  }
216
367
 
217
368
  .kpi-description {
218
- display: block;
369
+ display: -webkit-box;
370
+ -webkit-box-orient: vertical;
371
+ -webkit-line-clamp: 3;
219
372
  margin: 0;
220
373
  font-family: var(--nile-font-family-serif, var(--ng-font-family-body));
221
374
  font-size: var(--nile-kpi-description-font-size);
@@ -223,13 +376,19 @@ export const styles = css `
223
376
  line-height: 1.5;
224
377
  overflow: hidden;
225
378
  text-overflow: ellipsis;
379
+ white-space: normal;
380
+ word-break: break-word;
226
381
  min-width: 0;
227
382
  width: 100%;
228
383
  max-width: 100%;
229
384
  box-sizing: border-box;
230
- flex-shrink: 1;
385
+ flex-shrink: 0;
231
386
  }
232
387
 
388
+ /* Description stays visible whenever there's room — sparkline gives up its
389
+ space first because it has flex-shrink and flex-basis: 0. */
390
+
391
+
233
392
  .kpi-sparkline {
234
393
  width: 100%;
235
394
  flex: 1 1 0;
@@ -241,7 +400,7 @@ export const styles = css `
241
400
  /* ── Container queries: scale down for narrow cards ── */
242
401
 
243
402
  /* Medium-small: ~280px and below — tighten padding, shrink prefix/suffix. */
244
- @container (max-width: 280px) {
403
+ @container (max-width: 10px) {
245
404
  :host {
246
405
  --nile-kpi-padding-v: var(--nile-spacing-lg, var(--ng-spacing-lg));
247
406
  --nile-kpi-padding-h: var(--nile-spacing-xl, var(--ng-spacing-xl));
@@ -268,7 +427,9 @@ export const styles = css `
268
427
  }
269
428
 
270
429
  .kpi-sparkline {
271
- min-height: 0;
430
+
431
+ min-height: 18px;
432
+
272
433
  }
273
434
  }
274
435
 
@@ -284,6 +445,136 @@ export const styles = css `
284
445
  }
285
446
  }
286
447
 
448
+
449
+
450
+ /* ── Named-container hide thresholds ──────────────────────────────────────
451
+ Use kpi-card (this host) so descendants hide when the host can't fit them.
452
+ Tune each height to match where the section first starts clipping. ── */
453
+
454
+ /* Hide sparkline (and gauge) when the card can't fit it cleanly. */
455
+ @container kpi-card (max-height: 10px) {
456
+ .kpi-sparkline,
457
+ .kpi-gauge-container {
458
+ display: none;
459
+ }
460
+ }
461
+
462
+ /* Hide description when the card is even shorter. */
463
+ @container kpi-card (max-height: 110px) {
464
+ .kpi-description {
465
+ display: none;
466
+ }
467
+ }
468
+
469
+ /* Hide trend at very small sizes — keeps only label + value. */
470
+ @container kpi-card (max-height: 20px) {
471
+ .kpi-trend {
472
+ display: none;
473
+ }
474
+ }
475
+
476
+ /* When the host is short, hide the sparkline. Centering of the value-row is
477
+ restricted to .kpi--no-chart only — with a chart present, items stack from
478
+ the top of the card naturally. */
479
+ @container (max-height: 80px) {
480
+ .kpi-sparkline {
481
+ display: none;
482
+ }
483
+
484
+ .kpi.kpi--no-chart {
485
+ align-items: center;
486
+ }
487
+
488
+ .kpi--no-chart .kpi-label {
489
+ align-self: flex-start;
490
+ width: 100%;
491
+ }
492
+
493
+ .kpi--no-chart .kpi-value-row {
494
+ overflow: visible;
495
+ flex-wrap: wrap;
496
+ row-gap: var(--nile-spacing-xs, var(--ng-spacing-xs));
497
+ justify-content: center;
498
+ align-self: center;
499
+ width: auto;
500
+ max-width: 100%;
501
+ margin-top: auto;
502
+ margin-bottom: auto;
503
+ }
504
+ }
505
+
506
+ /* SIDE_HEIGHT × SIDE_WIDTH — wide + short, chart on the right of text.
507
+ Only when a chart is present; otherwise text would be squeezed into the
508
+ left half while the right half stays empty. */
509
+ @container (max-height: 108px) and (min-width: 390px) {
510
+ .kpi:not(.kpi--no-chart) {
511
+ display: grid;
512
+ grid-template-columns: minmax(0, 1fr) minmax(1px, 1fr);
513
+
514
+ grid-template-rows: auto auto auto 1fr;
515
+ column-gap: var(--nile-spacing-2xl, var(--ng-spacing-2xl));
516
+ align-items: start;
517
+ height: 100%;
518
+ }
519
+
520
+ .kpi:not(.kpi--no-chart) .kpi-label {
521
+ grid-column: 1;
522
+ grid-row: 1;
523
+ }
524
+
525
+ .kpi:not(.kpi--no-chart) .kpi-value-row {
526
+ grid-column: 1;
527
+ grid-row: 2;
528
+ overflow: hidden;
529
+ min-width: 0;
530
+ width: 100%;
531
+ max-width: 100%;
532
+ justify-content: flex-start;
533
+ justify-self: stretch;
534
+ align-self: start;
535
+ }
536
+
537
+ .kpi:not(.kpi--no-chart) .kpi-description {
538
+ grid-column: 1;
539
+ grid-row: 3;
540
+ }
541
+
542
+ .kpi:not(.kpi--no-chart) .kpi-trend {
543
+ flex: 0 1 auto;
544
+ min-width: 0;
545
+ max-width: 100%;
546
+ overflow: hidden;
547
+ text-overflow: ellipsis;
548
+ white-space: nowrap;
549
+ }
550
+
551
+ .kpi:not(.kpi--no-chart) .kpi-trend > span:last-child {
552
+ overflow: hidden;
553
+ text-overflow: ellipsis;
554
+ min-width: 0;
555
+ }
556
+
557
+ .kpi-sparkline {
558
+ display: block;
559
+ grid-column: 2;
560
+ grid-row: 1 / -1;
561
+ align-self: stretch;
562
+ min-height: 0;
563
+ height: 100%;
564
+ margin-top: 0;
565
+ }
566
+
567
+ .kpi-gauge-container {
568
+ display: block;
569
+ grid-column: 2;
570
+ grid-row: 1 / -1;
571
+ align-self: stretch;
572
+ min-height: 0;
573
+ height: 100%;
574
+ margin-top: 0;
575
+ }
576
+ }
577
+
287
578
  /* ── Gauge variant ── */
288
579
 
289
580
  .kpi--gauge {
@@ -293,11 +584,13 @@ export const styles = css `
293
584
 
294
585
  .kpi-gauge-container {
295
586
  width: 100%;
296
- max-width: 160px;
297
- aspect-ratio: 1;
298
- flex: 0 1 160px;
299
- min-width: 72px;
587
+ flex: 1 1 0;
588
+ min-width: 0;
589
+ min-height: 0;
590
+ max-width: 100%;
591
+ max-height: 100%;
300
592
  margin: 0 auto;
593
+ overflow: hidden;
301
594
  }
302
595
 
303
596
  .kpi--gauge .kpi-value-row {
@@ -3,8 +3,8 @@ import type Highcharts from 'highcharts';
3
3
  import NileElement from '../internal/nile-element.js';
4
4
  import type { AqConfigType } from '../internal/types/aq-config.type.js';
5
5
  export type TrendDirection = 'up' | 'down' | 'neutral';
6
- export type KpiVariant = 'default' | 'card' | 'gauge' | 'accent';
7
- export type SparklineType = 'area' | 'line';
6
+ export type KpiVariant = 'default' | 'card' | 'sparkline' | 'gauge' | 'accent';
7
+ export type SparklineType = 'area' | 'line' | 'column' | 'bar' | 'spline' | 'areaspline' | 'pie' | 'scatter';
8
8
  export type KpiValueFormat = 'auto' | 'K' | 'M' | 'B' | 'T' | 'none';
9
9
  export type KpiNumberSystem = 'indian' | 'international';
10
10
  /** `chart` slice for `<nile-kpi-chart>.config` (discriminated by `type: 'kpi'`). */
@@ -265,6 +265,7 @@ export declare class NileKpiChart extends NileElement {
265
265
  protected firstUpdated(): void;
266
266
  protected updated(changedProperties: PropertyValues): void;
267
267
  private syncSparklineChartSize;
268
+ private syncGaugeChartSize;
268
269
  private setupResizeObserver;
269
270
  private _onSparklineMouseMove;
270
271
  private _onSparklineMouseLeave;
@@ -279,6 +280,7 @@ export declare class NileKpiChart extends NileElement {
279
280
  private _onDescEnter;
280
281
  private _onLabelEnter;
281
282
  private _onGaugeEnter;
283
+ private _onTrendEnter;
282
284
  private _onTipLeave;
283
285
  private renderTrend;
284
286
  render(): TemplateResult;